joyent/node — Issue #2808: Bug with TLS/OpenSSL on Windows
Author: rrjamie
Created: 2012-02-22 22:53:26+00:00
State: open

While attempting to make a POST with a large body over SSL, I ran in to some
errors with my connection being terminated early.

I have narrowed it down to an issue with the TLS/OpenSSL stack. This issue
so far has only appeared on Windows. All the tests below work fine on MacOS X, Linux.

I have compiled a minimal test case. I can provide more information, and
run any tests in my environment, if you need to your lack access to a
similar environment.

Test Environment
---------------------

Server:
Ubuntu 10.04
Node.js v0.6.5

Client:
Windows 7 (x86)
(Running in VirtualBox on a MacOSX).

Futher, the issue doesn't appear if the client and server are local.
I run the server in an outside datacenter to trigger the issue
(on a 20Mb connection, ~50ms latency).

Payloads
----------------------

From my tests, payloads below ~100KB are fine, things get spotty
between 100-300KB, and rarely if ever work after that.

The error seems to be with the client, as if you use:

# Generate Key (per the tls docs)
$ openssl genrsa -out ryans-key.pem 1024
$ openssl req -new -key ryans-key.pem -out ryans-csr.pem
$ openssl x509 -req -in ryans-csr.pem -signkey ryans-key.pem -out ryans-cert.pem

# Run dump server.
$ openssl s_server -accept 7777 -cert ryans-cert.pem -key ryans-key.pem

You will see a similar error.

Errors
----------------------

On the server:

Secure Connection Established.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
Recieved 16384 bytes.
error: [Error: 140548640548640:error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac:s3_pkt.c:422:
]
error: [Error: 140548640548640:error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number:s3_pkt.c:284:
]
error: [Error: 140548640548640:error:140D2081:SSL routines:TLS1_ENC:block cipher pad is wrong:t1_enc.c:621:
]
error: [Error: 140548640548640:error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number:s3_pkt.c:284:
]
error: [Error: 140548640548640:error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac:s3_pkt.c:422:
]
error: [Error: 140548640548640:error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number:s3_pkt.c:284:
]
error: [Error: 140548640548640:error:14095044:SSL routines:SSL3_READ_N:internal error:s3_pkt.c:173:
]
error: [Error: 140548640548640:error:14095044:SSL routines:SSL3_READ_N:internal error:s3_pkt.c:173:

On the client:

C:\Users\rrjamie>node test_client.js
Done!

node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: write ECONNABORTED
at errnoException (net.js:646:11)
at Object.afterWrite [as oncomplete] (net.js:480:18)

C:\Users\rrjamie>

Code
----------------------

client.js:

tls = require('tls');

var createPayload = function(length) {
var buffer = new Buffer(length)

// Fill in with junk
var index;
for (index=0; index < length; index++) {
buffer.write("a", index);
}

return buffer;
};

var HOST = 'SERVER'
var PORT = 7777

var options = {
host: HOST

}

var stream = tls.connect(PORT, HOST, options);

stream.on("end", function() {
console.log("Done!");
});

var payload = createPayload(1000*1024);

stream.write(payload);
stream.end();

server.js:

// Taken from fixtures/keys/agent2
var KEY = '-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf+6fVgdpVhYg5\nQlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAQJBAMT6Bf34+UHKY1ObpsbH\n9u2jsVblFq1rWvs8GPMY6oertzvwm3DpuSUp7PTgOB1nLTLYtCERbQ4ovtN8tn3p\nOHUCIQDzIEGsoCr5vlxXvy2zJwu+fxYuhTZWMVuo1397L0VyhwIhANQh+yzqUgaf\nWRtSB4T2W7ADtJI35ET61jKBty3CqJY3AiAIwju7dVW3A5WeD6Qc1SZGKZvp9yCb\nAFI2BfVwwaY11wIgXF3PeGcvACMyMWsuSv7aPXHfliswAbkWuzcwA4TW01ECIGWa\ncgsDvVFxmfM5NPSuT/UDTa6R5BFISB5ea0N0AR3I\n-----END RSA PRIVATE KEY-----'
var CERT = '-----BEGIN CERTIFICATE-----\nMIIB7DCCAZYCCQC7gs0MDNn6MTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV\nUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO\nBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR\ncnlAdGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEy\nWjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD\nVQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg\nMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEF\nAANLADBIAkEAyXb8FrRdKbhrKLgLSsn61i1C7w7fVVVd7OQsmV/7p9WB2lWFiDlC\nWKGU9SiIz/A6wNZDUAuc2E+VwtpCT561AQIDAQABMA0GCSqGSIb3DQEBBQUAA0EA\nC8HzpuNhFLCI3A5KkBS5zHAQax6TFUOhbpBCR0aTDbJ6F1liDTK1lmU/BjvPoj+9\n1LHwrmh29rK8kBPEjmymCQ==\n-----END CERTIFICATE-----'

var tls = require('tls');

options = {
key: KEY,
cert: CERT
}

var server = tls.createServer(options, function(stream) {
console.log("Secure Connection Established.");

stream.on("data", function(data) {
console.log("Recieved " + data.length + " bytes.");
});

stream.on("end", function(data) {
if (data) {
console.log("Recieved " + data.length + " bytes.");
}

console.log("Connection closed.");
});
});

server.listen(7777);

Work Arounds
-----------------

A temporary work around, that works ~90% is to throttle the writes to the socket:

var tls = require('tls');

var writeThrottled;

writeThrottled = function(buffer, destination, increment, sleep) {
var endpoint, remaining_buffer, repeat, sliced_buffer;
if (increment == null) increment = 8 * 1024;
if (sleep == null) sleep = 50;
if (increment < buffer.length) {
endpoint = increment;
} else {
endpoint = buffer.length;
}
if (endpoint === 0) {
destination.end();
return;
}
sliced_buffer = buffer.slice(0, endpoint);
destination.write(sliced_buffer);
remaining_buffer = buffer.slice(endpoint, buffer.length);
repeat = function() {
return writeThrottled(remaining_buffer, destination, increment, sleep);
};
return setTimeout(repeat, sleep);
};

var createPayload = function(length) {
var buffer = new Buffer(length)

// Fill in with junk
var index;
for (index=0; index < length; index++) {
buffer.write("a", index);
}

return buffer;
};

var HOST = 'portal-staging.mobify.com'
var PORT = 7777

var options = {
host: HOST

}

var stream = tls.connect(PORT, HOST, options);

stream.on("end", function() {
console.log("Done!");
});

var payload = createPayload(1000*1024);

writeThrottled(payload, stream, 8*1024, 100);