Merge remote-tracking branch 'ry/v0.6' into v0.6-merge
Conflicts: AUTHORS ChangeLog Makefile doc/about/index.html doc/api/tls.markdown doc/community/index.html doc/index.html doc/logos/index.html doc/template.html lib/http.js lib/tls.js src/node_version.h src/platform_win32.cc test/simple/test-tls-connect-given-socket.js
This commit is contained in:
commit
31721da4b1
3
AUTHORS
3
AUTHORS
@ -263,3 +263,6 @@ Dan VerWeire <dverweire@gmail.com>
|
||||
Matthew Fitzsimmons <matt@joyent.com>
|
||||
Philip Tellis <philip.tellis@gmail.com>
|
||||
Christopher Jeffrey <chjjeffrey@gmail.com>
|
||||
Paddy Byers <paddy.byers@gmail.com>
|
||||
Seth Fitzsimmons <seth@mojodna.net>
|
||||
Einar Otto Stangvik <einaros@gmail.com>
|
||||
|
42
ChangeLog
42
ChangeLog
@ -97,7 +97,47 @@
|
||||
* Bug fixes
|
||||
|
||||
|
||||
2012.02.02, Version 0.6.10 (stable)
|
||||
2012.02.17 Version 0.6.11 (stable), 1eb1fe32250fc88cb5b0a97cddf3e02be02e3f4a
|
||||
|
||||
* http: allow multiple WebSocket RFC6455 headers (Einar Otto Stangvik)
|
||||
|
||||
* http: allow multiple WWW-Authenticate headers (Ben Noordhuis)
|
||||
|
||||
* windows: support unicode argv and environment variables (Bert Belder)
|
||||
|
||||
* tls: mitigate session renegotiation attacks (Ben Noordhuis)
|
||||
|
||||
* tcp, pipe: don't assert on uv_accept() errors (Ben Noordhuis)
|
||||
|
||||
* tls: Allow establishing secure connection on the existing socket (koichik)
|
||||
|
||||
* dgram: handle close of dgram socket before DNS lookup completes (Seth Fitzsimmons)
|
||||
|
||||
* windows: Support half-duplex pipes (Igor Zinkovsky)
|
||||
|
||||
* build: disable omit-frame-pointer on solaris systems (Dave Pacheco)
|
||||
|
||||
* debugger: fix --debug-brk (Ben Noordhuis)
|
||||
|
||||
* net: fix large file downloads failing (koichik)
|
||||
|
||||
* fs: fix ReadStream failure to read from existing fd (Christopher Jeffrey)
|
||||
|
||||
* net: destroy socket on DNS error (Stefan Rusu)
|
||||
|
||||
* dtrace: add missing translator (Dave Pacheco)
|
||||
|
||||
* unix: don't flush tty on switch to raw mode (Ben Noordhuis)
|
||||
|
||||
* windows: reset brightness when reverting to default text color (Bert Belder)
|
||||
|
||||
* npm: update to 1.1.1
|
||||
- Update which, fstream, mkdirp, request, and rimraf
|
||||
- Fix #2123 Set path properly for lifecycle scripts on windows
|
||||
- Mark the root as seen, so we don't recurse into it. Fixes #1838. (Martin Cooper)
|
||||
|
||||
|
||||
2012.02.02, Version 0.6.10 (stable), 051908e023f87894fa68f5b64d0b99a19a7db01e
|
||||
|
||||
* Update V8 to 3.6.6.20
|
||||
|
||||
|
4
deps/uv/src/unix/tty.c
vendored
4
deps/uv/src/unix/tty.c
vendored
@ -76,8 +76,8 @@ int uv_tty_set_mode(uv_tty_t* tty, int mode) {
|
||||
raw.c_cc[VMIN] = 1;
|
||||
raw.c_cc[VTIME] = 0;
|
||||
|
||||
/* Put terminal in raw mode after flushing */
|
||||
if (tcsetattr(fd, TCSAFLUSH, &raw)) {
|
||||
/* Put terminal in raw mode after draining */
|
||||
if (tcsetattr(fd, TCSADRAIN, &raw)) {
|
||||
goto fatal;
|
||||
}
|
||||
|
||||
|
3
deps/uv/src/win/error.c
vendored
3
deps/uv/src/win/error.c
vendored
@ -108,6 +108,9 @@ uv_err_code uv_translate_sys_error(int sys_errno) {
|
||||
case ERROR_INVALID_PARAMETER: return UV_EINVAL;
|
||||
case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET;
|
||||
case ERROR_BROKEN_PIPE: return UV_EOF;
|
||||
case ERROR_BAD_PIPE: return UV_EPIPE;
|
||||
case ERROR_NO_DATA: return UV_EPIPE;
|
||||
case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE;
|
||||
case ERROR_PIPE_BUSY: return UV_EBUSY;
|
||||
case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT;
|
||||
case WSAETIMEDOUT: return UV_ETIMEDOUT;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
|
@ -26,8 +26,40 @@ Alternatively you can send the CSR to a Certificate Authority for signing.
|
||||
(TODO: docs on creating a CA, for now interested users should just look at
|
||||
`test/fixtures/keys/Makefile` in the Node source code)
|
||||
|
||||
### Client-initiated renegotiation attack mitigation
|
||||
|
||||
#### tls.createServer(options, [secureConnectionListener])
|
||||
The TLS protocol lets the client renegotiate certain aspects of the TLS session.
|
||||
Unfortunately, session renegotiation requires a disproportional amount of
|
||||
server-side resources, which makes it a potential vector for denial-of-service
|
||||
attacks.
|
||||
|
||||
To mitigate this, renegotiations are limited to three times every 10 minutes. An
|
||||
error is emitted on the [CleartextStream](#tls.CleartextStream) instance when
|
||||
the threshold is exceeded. The limits are configurable:
|
||||
|
||||
- `tls.CLIENT_RENEG_LIMIT`: renegotiation limit, default is 3.
|
||||
|
||||
- `tls.CLIENT_RENEG_WINDOW`: renegotiation window in seconds, default is
|
||||
10 minutes.
|
||||
|
||||
Don't change the defaults unless you know what you are doing.
|
||||
|
||||
To test your server, connect to it with `openssl s_client -connect address:port`
|
||||
and tap `R<CR>` (that's the letter `R` followed by a carriage return) a few
|
||||
times.
|
||||
|
||||
|
||||
### NPN and SNI
|
||||
|
||||
NPN (Next Protocol Negotiation) and SNI (Server Name Indication) are TLS
|
||||
handshake extensions allowing you:
|
||||
|
||||
* NPN - to use one TLS server for multiple protocols (HTTP, SPDY)
|
||||
* SNI - to use one TLS server for multiple hostnames with different SSL
|
||||
certificates.
|
||||
|
||||
|
||||
## tls.createServer(options, [secureConnectionListener])
|
||||
|
||||
Creates a new [tls.Server](#tls.Server).
|
||||
The `connectionListener` argument is automatically set as a listener for the
|
||||
@ -144,6 +176,11 @@ Creates a new client connection to the given `port` and `host` (old API) or
|
||||
|
||||
- `servername`: Servername for SNI (Server Name Indication) TLS extension.
|
||||
|
||||
- `socket`: Establish secure connection on a given socket rather than
|
||||
creating a new socket. If this option is specified, `host` and `port`
|
||||
are ignored. This is intended FOR INTERNAL USE ONLY. As with all
|
||||
undocumented APIs in Node, they should not be used.
|
||||
|
||||
The `secureConnectListener` parameter will be added as a listener for the
|
||||
['secureConnect'](#event_secureConnect_) event.
|
||||
|
||||
@ -178,27 +215,7 @@ Here is an example of a client of echo server as described previously:
|
||||
});
|
||||
|
||||
|
||||
### STARTTLS
|
||||
|
||||
In the v0.4 branch no function exists for starting a TLS session on an
|
||||
already existing TCP connection. This is possible it just requires a bit of
|
||||
work. The technique is to use `tls.createSecurePair()` which returns two
|
||||
streams: an encrypted stream and a cleartext stream. The encrypted stream is
|
||||
then piped to the socket, the cleartext stream is what the user interacts with
|
||||
thereafter.
|
||||
|
||||
[Here is some code that does it.](http://gist.github.com/848444)
|
||||
|
||||
### NPN and SNI
|
||||
|
||||
NPN (Next Protocol Negotiation) and SNI (Server Name Indication) are TLS
|
||||
handshake extensions allowing you:
|
||||
|
||||
* NPN - to use one TLS server for multiple protocols (HTTP, SPDY)
|
||||
* SNI - to use one TLS server for multiple hostnames with different SSL
|
||||
certificates.
|
||||
|
||||
### pair = tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized])
|
||||
## tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized])
|
||||
|
||||
Creates a new secure pair object with two streams, one of which reads/writes
|
||||
encrypted data, and one reads/writes cleartext data.
|
||||
@ -220,7 +237,7 @@ and the cleartext one is used as a replacement for the initial encrypted stream.
|
||||
`tls.createSecurePair()` returns a SecurePair object with
|
||||
[cleartext](#tls.CleartextStream) and `encrypted` stream properties.
|
||||
|
||||
#### Event: 'secure'
|
||||
### Event: 'secure'
|
||||
|
||||
The event is emitted from the SecurePair once the pair has successfully
|
||||
established a secure connection.
|
||||
@ -229,13 +246,13 @@ Similarly to the checking for the server 'secureConnection' event,
|
||||
pair.cleartext.authorized should be checked to confirm whether the certificate
|
||||
used properly authorized.
|
||||
|
||||
### tls.Server
|
||||
## tls.Server
|
||||
|
||||
This class is a subclass of `net.Server` and has the same methods on it.
|
||||
Instead of accepting just raw TCP connections, this accepts encrypted
|
||||
connections using TLS or SSL.
|
||||
|
||||
#### Event: 'secureConnection'
|
||||
### Event: 'secureConnection'
|
||||
|
||||
`function (cleartextStream) {}`
|
||||
|
||||
@ -255,7 +272,7 @@ server, you unauthorized connections may be accepted.
|
||||
SNI.
|
||||
|
||||
|
||||
#### Event: 'clientError'
|
||||
### Event: 'clientError'
|
||||
|
||||
`function (exception) { }`
|
||||
|
||||
@ -263,7 +280,7 @@ When a client connection emits an 'error' event before secure connection is
|
||||
established - it will be forwarded here.
|
||||
|
||||
|
||||
#### server.listen(port, [host], [callback])
|
||||
### server.listen(port, [host], [callback])
|
||||
|
||||
Begin accepting connections on the specified `port` and `host`. If the
|
||||
`host` is omitted, the server will accept connections directed to any
|
||||
@ -275,35 +292,35 @@ when the server has been bound.
|
||||
See `net.Server` for more information.
|
||||
|
||||
|
||||
#### server.close()
|
||||
### server.close()
|
||||
|
||||
Stops the server from accepting new connections. This function is
|
||||
asynchronous, the server is finally closed when the server emits a `'close'`
|
||||
event.
|
||||
|
||||
#### server.address()
|
||||
### server.address()
|
||||
|
||||
Returns the bound address and port of the server as reported by the operating
|
||||
system.
|
||||
See [net.Server.address()](net.html#server.address) for more information.
|
||||
|
||||
#### server.addContext(hostname, credentials)
|
||||
### server.addContext(hostname, credentials)
|
||||
|
||||
Add secure context that will be used if client request's SNI hostname is
|
||||
matching passed `hostname` (wildcards can be used). `credentials` can contain
|
||||
`key`, `cert` and `ca`.
|
||||
|
||||
#### server.maxConnections
|
||||
### server.maxConnections
|
||||
|
||||
Set this property to reject connections when the server's connection count
|
||||
gets high.
|
||||
|
||||
#### server.connections
|
||||
### server.connections
|
||||
|
||||
The number of concurrent connections on the server.
|
||||
|
||||
|
||||
### tls.CleartextStream
|
||||
## tls.CleartextStream
|
||||
|
||||
This is a stream on top of the *Encrypted* stream that makes it possible to
|
||||
read/write an encrypted data as a cleartext data.
|
||||
@ -311,7 +328,7 @@ read/write an encrypted data as a cleartext data.
|
||||
This instance implements a duplex [Stream](streams.html#streams) interfaces.
|
||||
It has all the common stream methods and events.
|
||||
|
||||
#### Event: 'secureConnect'
|
||||
### Event: 'secureConnect'
|
||||
|
||||
`function () {}`
|
||||
|
||||
@ -323,17 +340,17 @@ If `cleartextStream.authorized === false` then the error can be found in
|
||||
`cleartextStream.authorizationError`. Also if NPN was used - you can check
|
||||
`cleartextStream.npnProtocol` for negotiated protocol.
|
||||
|
||||
#### cleartextStream.authorized
|
||||
### cleartextStream.authorized
|
||||
|
||||
A boolean that is `true` if the peer certificate was signed by one of the
|
||||
specified CAs, otherwise `false`
|
||||
|
||||
#### cleartextStream.authorizationError
|
||||
### cleartextStream.authorizationError
|
||||
|
||||
The reason why the peer's certificate has not been verified. This property
|
||||
becomes available only when `cleartextStream.authorized === false`.
|
||||
|
||||
#### cleartextStream.getPeerCertificate()
|
||||
### cleartextStream.getPeerCertificate()
|
||||
|
||||
Returns an object representing the peer's certificate. The returned object has
|
||||
some properties corresponding to the field of the certificate.
|
||||
@ -361,17 +378,17 @@ Example:
|
||||
If the peer does not provide a certificate, it returns `null` or an empty
|
||||
object.
|
||||
|
||||
#### cleartextStream.address()
|
||||
### cleartextStream.address()
|
||||
|
||||
Returns the bound address and port of the underlying socket as reported by the
|
||||
operating system. Returns an object with two properties, e.g.
|
||||
`{"address":"192.168.57.1", "port":62053}`
|
||||
|
||||
#### cleartextStream.remoteAddress
|
||||
### cleartextStream.remoteAddress
|
||||
|
||||
The string representation of the remote IP address. For example,
|
||||
`'74.125.127.100'` or `'2001:4860:a005::68'`.
|
||||
|
||||
#### cleartextStream.remotePort
|
||||
### cleartextStream.remotePort
|
||||
|
||||
The numeric representation of the remote port. For example, `443`.
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
@ -12,8 +12,8 @@
|
||||
<script src="../sh_javascript.min.js"></script>
|
||||
<link type="image/x-icon" rel="icon" href="../favicon.ico">
|
||||
<link type="image/x-icon" rel="shortcut icon" href="../favicon.ico">
|
||||
<link type="text/css" rel="stylesheet" href="../pipe.css">
|
||||
<link type="text/css" rel="stylesheet" href="../sh_vim-dark.css">
|
||||
<link rel="stylesheet" href="../pipe.css">
|
||||
<link rel="stylesheet" href="../sh_vim-dark.css">
|
||||
<link rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="node blog"
|
||||
|
@ -157,7 +157,7 @@ var http = require('http');
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.end('Hello World\n');
|
||||
}).listen(1337, "127.0.0.1");
|
||||
}).listen(1337, '127.0.0.1');
|
||||
console.log('Server running at http://127.0.0.1:1337/');</pre>
|
||||
|
||||
<p>To run the server, put the code into a file <code>example.js</code> and execute it with the <code>node</code> program:</p>
|
||||
@ -171,11 +171,11 @@ Server running at http://127.0.0.1:1337/</pre>
|
||||
var net = require('net');
|
||||
|
||||
var server = net.createServer(function (socket) {
|
||||
socket.write("Echo server\r\n");
|
||||
socket.write('Echo server\r\n');
|
||||
socket.pipe(socket);
|
||||
});
|
||||
|
||||
server.listen(1337, "127.0.0.1");</pre>
|
||||
server.listen(1337, '127.0.0.1');</pre>
|
||||
|
||||
<!-- <p>Ready to dig in? <a href="">Download the latest version</a> of node.js or learn how other organizations are <a href="">using the technology</a>.</p> -->
|
||||
</div>
|
||||
|
@ -1,24 +1,21 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style type="text/css">
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js?ver=3.1.3'></script>
|
||||
<script type="text/javascript" src="../sh_main.js"></script>
|
||||
<script type="text/javascript" src="../sh_javascript.min.js"></script>
|
||||
<link type="image/x-icon" rel="icon" href="../favicon.ico">
|
||||
<link type="image/x-icon" rel="shortcut icon" href="../favicon.ico">
|
||||
<link type="text/css" rel="stylesheet" href="../pipe.css">
|
||||
<link type="text/css" rel="stylesheet" href="../sh_vim-dark.css">
|
||||
<link rel="stylesheet" href="../pipe.css">
|
||||
<link rel="stylesheet" href="../sh_vim-dark.css">
|
||||
<link rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="node blog"
|
||||
href="http://feeds.feedburner.com/nodejs/123123123">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<title>node.js</title>
|
||||
</head>
|
||||
<body class="int" id="logos">
|
||||
@ -88,10 +85,7 @@
|
||||
<p>Copyright <a href="http://joyent.com">Joyent, Inc</a>., Node.js is a <a href="/trademark-policy.pdf">trademark of Joyent, Inc</a>., <a href="https://raw.github.com/joyent/node/v0.7.4/LICENSE">View License</a></p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var gaJsHost = (("https:" == document.location.protocol) ?
|
||||
"https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
@ -102,6 +96,5 @@
|
||||
pageTracker._trackPageview();
|
||||
} catch(err) {}
|
||||
</script>
|
||||
|
||||
|
||||
</body></html>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -175,7 +175,7 @@ Socket.prototype.send = function(buffer,
|
||||
if (callback) callback(err);
|
||||
self.emit('error', err);
|
||||
}
|
||||
else {
|
||||
else if (self._handle) {
|
||||
var req = self._handle.send(buffer, offset, length, port, ip);
|
||||
if (req) {
|
||||
req.oncomplete = afterSend;
|
||||
|
@ -359,6 +359,8 @@ IncomingMessage.prototype._addHeaderLine = function(field, value) {
|
||||
case 'pragma':
|
||||
case 'link':
|
||||
case 'www-authenticate':
|
||||
case 'sec-websocket-extensions':
|
||||
case 'sec-websocket-protocol':
|
||||
if (field in dest) {
|
||||
dest[field] += ', ' + value;
|
||||
} else {
|
||||
|
@ -97,7 +97,13 @@ if (isWindows) {
|
||||
// directories. If we've resolved a drive letter but not yet an
|
||||
// absolute path, get cwd for that drive. We're sure the device is not
|
||||
// an unc path at this points, because unc paths are always absolute.
|
||||
path = process._cwdForDrive(resolvedDevice[0]);
|
||||
path = process.env['=' + resolvedDevice];
|
||||
// Verify that a drive-local cwd was found and that it actually points
|
||||
// to our drive. If not, default to the drive's root.
|
||||
if (!path || path.slice(0, 3).toLowerCase() !==
|
||||
resolvedDevice.toLowerCase() + '\\') {
|
||||
path = resolvedDevice + '\\';
|
||||
}
|
||||
}
|
||||
|
||||
// Skip empty and invalid entries
|
||||
|
@ -525,8 +525,13 @@ REPLServer.prototype.complete = function(line, callback) {
|
||||
}
|
||||
// works for non-objects
|
||||
try {
|
||||
var p = Object.getPrototypeOf(obj);
|
||||
var sentinel = 5;
|
||||
var p;
|
||||
if (typeof obj == 'object') {
|
||||
p = Object.getPrototypeOf(obj);
|
||||
} else {
|
||||
p = obj.constructor ? obj.constructor.prototype : null;
|
||||
}
|
||||
while (p !== null) {
|
||||
memberGroups.push(Object.getOwnPropertyNames(p));
|
||||
p = Object.getPrototypeOf(p);
|
||||
|
80
lib/tls.js
80
lib/tls.js
@ -27,6 +27,14 @@ var stream = require('stream');
|
||||
var END_OF_FILE = 42;
|
||||
var assert = require('assert').ok;
|
||||
|
||||
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
|
||||
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
|
||||
// renegotations are seen. The settings are applied to all remote client
|
||||
// connections.
|
||||
exports.CLIENT_RENEG_LIMIT = 3;
|
||||
exports.CLIENT_RENEG_WINDOW = 600;
|
||||
|
||||
|
||||
var debug;
|
||||
if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
|
||||
debug = function(a) { console.error('TLS:', a); };
|
||||
@ -539,6 +547,37 @@ EncryptedStream.prototype._pusher = function(pool, offset, length) {
|
||||
};
|
||||
|
||||
|
||||
function onhandshakestart() {
|
||||
debug('onhandshakestart');
|
||||
|
||||
var self = this, ssl = this.ssl;
|
||||
ssl.handshakes++;
|
||||
|
||||
if (ssl.handshakes === 1) {
|
||||
function timeout() {
|
||||
ssl.handshakes = 0;
|
||||
ssl.timer = null;
|
||||
}
|
||||
ssl.timer = setTimeout(timeout, exports.CLIENT_RENEG_WINDOW * 1000);
|
||||
}
|
||||
else if (ssl.handshakes >= exports.CLIENT_RENEG_LIMIT) {
|
||||
// Defer the error event to the next tick. We're being called from OpenSSL's
|
||||
// state machine and OpenSSL is not re-entrant. We cannot allow the user's
|
||||
// callback to destroy the connection right now, it would crash and burn.
|
||||
process.nextTick(function() {
|
||||
var err = new Error('TLS session renegotiation attack detected.');
|
||||
if (self.cleartext) self.cleartext.emit('error', err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onhandshakedone() {
|
||||
// for future use
|
||||
debug('onhandshakedone');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides a pair of streams to do encrypted communication.
|
||||
*/
|
||||
@ -585,6 +624,13 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized,
|
||||
this._isServer ? this._requestCert : options.servername,
|
||||
this._rejectUnauthorized);
|
||||
|
||||
if (this._isServer) {
|
||||
this.ssl.onhandshakestart = onhandshakestart.bind(this);
|
||||
this.ssl.onhandshakedone = onhandshakedone.bind(this);
|
||||
this.ssl.handshakes = 0;
|
||||
this.ssl.timer = null;
|
||||
}
|
||||
|
||||
if (process.features.tls_sni) {
|
||||
if (this._isServer && options.SNICallback) {
|
||||
this.ssl.setSNICallback(options.SNICallback);
|
||||
@ -720,25 +766,29 @@ SecurePair.prototype.maybeInitFinished = function() {
|
||||
|
||||
|
||||
SecurePair.prototype.destroy = function() {
|
||||
if (this._doneFlag) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
this._doneFlag = true;
|
||||
this.ssl.error = null;
|
||||
this.ssl.close();
|
||||
this.ssl = null;
|
||||
if (!this._doneFlag) {
|
||||
this._doneFlag = true;
|
||||
|
||||
self.encrypted.writable = self.encrypted.readable = false;
|
||||
self.cleartext.writable = self.cleartext.readable = false;
|
||||
if (this.ssl.timer) {
|
||||
clearTimeout(this.ssl.timer);
|
||||
this.ssl.timer = null;
|
||||
}
|
||||
|
||||
process.nextTick(function() {
|
||||
self.cleartext.emit('end');
|
||||
self.encrypted.emit('close');
|
||||
self.cleartext.emit('close');
|
||||
});
|
||||
this.ssl.error = null;
|
||||
this.ssl.close();
|
||||
this.ssl = null;
|
||||
|
||||
self.encrypted.writable = self.encrypted.readable = false;
|
||||
self.cleartext.writable = self.cleartext.readable = false;
|
||||
|
||||
process.nextTick(function() {
|
||||
self.cleartext.emit('end');
|
||||
self.encrypted.emit('close');
|
||||
self.cleartext.emit('close');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
1
node.gyp
1
node.gyp
@ -155,6 +155,7 @@
|
||||
'FD_SETSIZE=1024',
|
||||
# we need to use node's preferred "win32" rather than gyp's preferred "win"
|
||||
'PLATFORM="win32"',
|
||||
'_UNICODE=1',
|
||||
],
|
||||
'libraries': [ '-lpsapi.lib' ]
|
||||
},{ # POSIX
|
||||
|
241
src/node.cc
241
src/node.cc
@ -1290,76 +1290,6 @@ static Handle<Value> Cwd(const Arguments& args) {
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static Handle<Value> CwdForDrive(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
if (args.Length() < 1) {
|
||||
Local<Value> exception = Exception::Error(
|
||||
String::New("process._cwdForDrive takes exactly 1 argument."));
|
||||
return ThrowException(exception);
|
||||
}
|
||||
|
||||
Local<String> driveLetter = args[0]->ToString();
|
||||
if (driveLetter->Length() != 1) {
|
||||
Local<Value> exception = Exception::Error(
|
||||
String::New("Drive name should be 1 character."));
|
||||
return ThrowException(exception);
|
||||
}
|
||||
|
||||
char drive;
|
||||
|
||||
driveLetter->WriteAscii(&drive, 0, 1, 0);
|
||||
if (drive >= 'a' && drive <= 'z') {
|
||||
// Convert to uppercase
|
||||
drive += 'A' - 'a';
|
||||
} else if (drive < 'A' || drive > 'Z') {
|
||||
// Not a letter
|
||||
Local<Value> exception = Exception::Error(
|
||||
String::New("Drive name should be a letter."));
|
||||
return ThrowException(exception);
|
||||
}
|
||||
|
||||
WCHAR env_key[] = L"=X:";
|
||||
env_key[1] = (WCHAR) drive;
|
||||
|
||||
DWORD len = GetEnvironmentVariableW(env_key, NULL, 0);
|
||||
if (len == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
|
||||
// There is no current directory for that drive. Default to drive + ":\".
|
||||
Local<String> cwd = String::Concat(String::New(&drive, 1),
|
||||
String::New(":\\"));
|
||||
return scope.Close(cwd);
|
||||
|
||||
} else if (len == 0) {
|
||||
// Error
|
||||
Local<Value> exception = Exception::Error(
|
||||
String::New(winapi_strerror(GetLastError())));
|
||||
return ThrowException(exception);
|
||||
}
|
||||
|
||||
WCHAR* buffer = new WCHAR[len];
|
||||
if (buffer == NULL) {
|
||||
Local<Value> exception = Exception::Error(
|
||||
String::New("Out of memory."));
|
||||
return ThrowException(exception);
|
||||
}
|
||||
|
||||
DWORD len2 = GetEnvironmentVariableW(env_key, buffer, len);
|
||||
if (len2 == 0 || len2 >= len) {
|
||||
// Error
|
||||
delete[] buffer;
|
||||
Local<Value> exception = Exception::Error(
|
||||
String::New(winapi_strerror(GetLastError())));
|
||||
return ThrowException(exception);
|
||||
}
|
||||
|
||||
Local<String> cwd = String::New(reinterpret_cast<uint16_t*>(buffer), len2);
|
||||
delete[] buffer;
|
||||
return scope.Close(cwd);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static Handle<Value> Umask(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
unsigned int old;
|
||||
@ -1883,12 +1813,28 @@ static void ProcessTitleSetter(Local<String> property,
|
||||
|
||||
static Handle<Value> EnvGetter(Local<String> property,
|
||||
const AccessorInfo& info) {
|
||||
HandleScope scope;
|
||||
#ifdef __POSIX__
|
||||
String::Utf8Value key(property);
|
||||
const char* val = getenv(*key);
|
||||
if (val) {
|
||||
HandleScope scope;
|
||||
return scope.Close(String::New(val));
|
||||
}
|
||||
#else // _WIN32
|
||||
String::Value key(property);
|
||||
WCHAR buffer[32767]; // The maximum size allowed for environment variables.
|
||||
DWORD result = GetEnvironmentVariableW(reinterpret_cast<WCHAR*>(*key),
|
||||
buffer,
|
||||
ARRAY_SIZE(buffer));
|
||||
// If result >= sizeof buffer the buffer was too small. That should never
|
||||
// happen. If result == 0 and result != ERROR_SUCCESS the variable was not
|
||||
// not found.
|
||||
if ((result > 0 || GetLastError() == ERROR_SUCCESS) &&
|
||||
result < ARRAY_SIZE(buffer)) {
|
||||
return scope.Close(String::New(reinterpret_cast<uint16_t*>(buffer), result));
|
||||
}
|
||||
#endif
|
||||
// Not found
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
@ -1897,66 +1843,82 @@ static Handle<Value> EnvSetter(Local<String> property,
|
||||
Local<Value> value,
|
||||
const AccessorInfo& info) {
|
||||
HandleScope scope;
|
||||
#ifdef __POSIX__
|
||||
String::Utf8Value key(property);
|
||||
String::Utf8Value val(value);
|
||||
|
||||
#ifdef __POSIX__
|
||||
setenv(*key, *val, 1);
|
||||
#else // __WIN32__
|
||||
int n = key.length() + val.length() + 2;
|
||||
char* pair = new char[n];
|
||||
snprintf(pair, n, "%s=%s", *key, *val);
|
||||
int r = _putenv(pair);
|
||||
if (r) {
|
||||
fprintf(stderr, "error putenv: '%s'\n", pair);
|
||||
#else // _WIN32
|
||||
String::Value key(property);
|
||||
String::Value val(value);
|
||||
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
|
||||
// Environment variables that start with '=' are read-only.
|
||||
if (key_ptr[0] != L'=') {
|
||||
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
|
||||
}
|
||||
delete [] pair;
|
||||
#endif
|
||||
|
||||
return value;
|
||||
// Whether it worked or not, always return rval.
|
||||
return scope.Close(value);
|
||||
}
|
||||
|
||||
|
||||
static Handle<Integer> EnvQuery(Local<String> property,
|
||||
const AccessorInfo& info) {
|
||||
HandleScope scope;
|
||||
#ifdef __POSIX__
|
||||
String::Utf8Value key(property);
|
||||
if (getenv(*key)) {
|
||||
HandleScope scope;
|
||||
return scope.Close(Integer::New(None));
|
||||
}
|
||||
return Handle<Integer>();
|
||||
#else // _WIN32
|
||||
String::Value key(property);
|
||||
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
|
||||
if (GetEnvironmentVariableW(key_ptr, NULL, 0) > 0 ||
|
||||
GetLastError() == ERROR_SUCCESS) {
|
||||
if (key_ptr[0] == L'=') {
|
||||
// Environment variables that start with '=' are hidden and read-only.
|
||||
return scope.Close(Integer::New(v8::ReadOnly ||
|
||||
v8::DontDelete ||
|
||||
v8::DontEnum));
|
||||
} else {
|
||||
return scope.Close(Integer::New(None));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Not found
|
||||
return scope.Close(Handle<Integer>());
|
||||
}
|
||||
|
||||
|
||||
static Handle<Boolean> EnvDeleter(Local<String> property,
|
||||
const AccessorInfo& info) {
|
||||
HandleScope scope;
|
||||
|
||||
String::Utf8Value key(property);
|
||||
|
||||
if (getenv(*key)) {
|
||||
#ifdef __POSIX__
|
||||
unsetenv(*key); // prototyped as `void unsetenv(const char*)` on some platforms
|
||||
String::Utf8Value key(property);
|
||||
// prototyped as `void unsetenv(const char*)` on some platforms
|
||||
if (unsetenv(*key) < 0) {
|
||||
// Deletion failed. Return true if the key wasn't there in the first place,
|
||||
// false if it is still there.
|
||||
return scope.Close(Boolean::New(getenv(*key) == NULL));
|
||||
};
|
||||
#else
|
||||
int n = key.length() + 2;
|
||||
char* pair = new char[n];
|
||||
snprintf(pair, n, "%s=", *key);
|
||||
int r = _putenv(pair);
|
||||
if (r) {
|
||||
fprintf(stderr, "error unsetenv: '%s'\n", pair);
|
||||
}
|
||||
delete [] pair;
|
||||
#endif
|
||||
return True();
|
||||
String::Value key(property);
|
||||
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
|
||||
if (key_ptr[0] == L'=' || !SetEnvironmentVariableW(key_ptr, NULL)) {
|
||||
// Deletion failed. Return true if the key wasn't there in the first place,
|
||||
// false if it is still there.
|
||||
bool rv = GetEnvironmentVariableW(key_ptr, NULL, NULL) == 0 &&
|
||||
GetLastError() != ERROR_SUCCESS;
|
||||
return scope.Close(Boolean::New(rv));
|
||||
}
|
||||
|
||||
return False();
|
||||
#endif
|
||||
// It worked
|
||||
return v8::True();
|
||||
}
|
||||
|
||||
|
||||
static Handle<Array> EnvEnumerator(const AccessorInfo& info) {
|
||||
HandleScope scope;
|
||||
|
||||
#ifdef __POSIX__
|
||||
int size = 0;
|
||||
while (environ[size]) size++;
|
||||
|
||||
@ -1968,7 +1930,32 @@ static Handle<Array> EnvEnumerator(const AccessorInfo& info) {
|
||||
const int length = s ? s - var : strlen(var);
|
||||
env->Set(i, String::New(var, length));
|
||||
}
|
||||
|
||||
#else // _WIN32
|
||||
WCHAR* environment = GetEnvironmentStringsW();
|
||||
if (environment == NULL) {
|
||||
// This should not happen.
|
||||
return scope.Close(Handle<Array>());
|
||||
}
|
||||
Local<Array> env = Array::New();
|
||||
WCHAR* p = environment;
|
||||
int i = 0;
|
||||
while (*p != NULL) {
|
||||
WCHAR *s;
|
||||
if (*p == L'=') {
|
||||
// If the key starts with '=' it is a hidden environment variable.
|
||||
p += wcslen(p) + 1;
|
||||
continue;
|
||||
} else {
|
||||
s = wcschr(p, L'=');
|
||||
}
|
||||
if (!s) {
|
||||
s = p + wcslen(p);
|
||||
}
|
||||
env->Set(i++, String::New(reinterpret_cast<uint16_t*>(p), s - p));
|
||||
p = s + wcslen(s) + 1;
|
||||
}
|
||||
FreeEnvironmentStringsW(environment);
|
||||
#endif
|
||||
return scope.Close(env);
|
||||
}
|
||||
|
||||
@ -2127,10 +2114,6 @@ Handle<Object> SetupProcessObject(int argc, char *argv[]) {
|
||||
NODE_SET_METHOD(process, "chdir", Chdir);
|
||||
NODE_SET_METHOD(process, "cwd", Cwd);
|
||||
|
||||
#ifdef _WIN32
|
||||
NODE_SET_METHOD(process, "_cwdForDrive", CwdForDrive);
|
||||
#endif
|
||||
|
||||
NODE_SET_METHOD(process, "umask", Umask);
|
||||
|
||||
#ifdef __POSIX__
|
||||
@ -2416,13 +2399,14 @@ DWORD WINAPI EnableDebugThreadProc(void* arg) {
|
||||
}
|
||||
|
||||
|
||||
static int GetDebugSignalHandlerMappingName(DWORD pid, char* buf, size_t buf_len) {
|
||||
return snprintf(buf, buf_len, "node-debug-handler-%u", pid);
|
||||
static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
|
||||
size_t buf_len) {
|
||||
return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
|
||||
}
|
||||
|
||||
|
||||
static int RegisterDebugSignalHandler() {
|
||||
char mapping_name[32];
|
||||
wchar_t mapping_name[32];
|
||||
HANDLE mapping_handle;
|
||||
DWORD pid;
|
||||
LPTHREAD_START_ROUTINE* handler;
|
||||
@ -2431,11 +2415,11 @@ static int RegisterDebugSignalHandler() {
|
||||
|
||||
if (GetDebugSignalHandlerMappingName(pid,
|
||||
mapping_name,
|
||||
sizeof mapping_name) < 0) {
|
||||
ARRAY_SIZE(mapping_name)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mapping_handle = CreateFileMappingA(INVALID_HANDLE_VALUE,
|
||||
mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
|
||||
NULL,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
@ -2445,11 +2429,12 @@ static int RegisterDebugSignalHandler() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
handler = (LPTHREAD_START_ROUTINE*) MapViewOfFile(mapping_handle,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
sizeof *handler);
|
||||
handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
|
||||
MapViewOfFile(mapping_handle,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
sizeof *handler));
|
||||
if (handler == NULL) {
|
||||
CloseHandle(mapping_handle);
|
||||
return -1;
|
||||
@ -2470,7 +2455,7 @@ static Handle<Value> DebugProcess(const Arguments& args) {
|
||||
HANDLE process = NULL;
|
||||
HANDLE thread = NULL;
|
||||
HANDLE mapping = NULL;
|
||||
char mapping_name[32];
|
||||
wchar_t mapping_name[32];
|
||||
LPTHREAD_START_ROUTINE* handler = NULL;
|
||||
|
||||
if (args.Length() != 1) {
|
||||
@ -2492,22 +2477,24 @@ static Handle<Value> DebugProcess(const Arguments& args) {
|
||||
|
||||
if (GetDebugSignalHandlerMappingName(pid,
|
||||
mapping_name,
|
||||
sizeof mapping_name) < 0) {
|
||||
ARRAY_SIZE(mapping_name)) < 0) {
|
||||
rv = ThrowException(ErrnoException(errno, "sprintf"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
mapping = OpenFileMapping(FILE_MAP_READ, FALSE, mapping_name);
|
||||
mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name);
|
||||
if (mapping == NULL) {
|
||||
rv = ThrowException(WinapiErrnoException(GetLastError(), "sprintf"));
|
||||
rv = ThrowException(WinapiErrnoException(GetLastError(),
|
||||
"OpenFileMappingW"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
handler = (LPTHREAD_START_ROUTINE*) MapViewOfFile(mapping,
|
||||
FILE_MAP_READ,
|
||||
0,
|
||||
0,
|
||||
sizeof *handler);
|
||||
handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
|
||||
MapViewOfFile(mapping,
|
||||
FILE_MAP_READ,
|
||||
0,
|
||||
0,
|
||||
sizeof *handler));
|
||||
if (handler == NULL || *handler == NULL) {
|
||||
rv = ThrowException(WinapiErrnoException(GetLastError(), "MapViewOfFile"));
|
||||
goto out;
|
||||
|
@ -908,6 +908,8 @@ Handle<Value> Connection::New(const Arguments& args) {
|
||||
|
||||
SSL_set_app_data(p->ssl_, p);
|
||||
|
||||
if (is_server) SSL_set_info_callback(p->ssl_, SSLInfoCallback);
|
||||
|
||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||
if (is_server) {
|
||||
// Server should advertise NPN protocols
|
||||
@ -970,6 +972,20 @@ Handle<Value> Connection::New(const Arguments& args) {
|
||||
}
|
||||
|
||||
|
||||
void Connection::SSLInfoCallback(const SSL *ssl, int where, int ret) {
|
||||
if (where & SSL_CB_HANDSHAKE_START) {
|
||||
HandleScope scope;
|
||||
Connection* c = static_cast<Connection*>(SSL_get_app_data(ssl));
|
||||
MakeCallback(c->handle_, "onhandshakestart", 0, NULL);
|
||||
}
|
||||
if (where & SSL_CB_HANDSHAKE_DONE) {
|
||||
HandleScope scope;
|
||||
Connection* c = static_cast<Connection*>(SSL_get_app_data(ssl));
|
||||
MakeCallback(c->handle_, "onhandshakedone", 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<Value> Connection::EncIn(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
|
@ -190,6 +190,8 @@ class Connection : ObjectWrap {
|
||||
}
|
||||
|
||||
private:
|
||||
static void SSLInfoCallback(const SSL *ssl, int where, int ret);
|
||||
|
||||
BIO *bio_read_;
|
||||
BIO *bio_write_;
|
||||
SSL *ssl_;
|
||||
|
@ -21,6 +21,47 @@
|
||||
|
||||
#include <node.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
int wmain(int argc, wchar_t *wargv[]) {
|
||||
// Convert argv to to UTF8
|
||||
char** argv = new char*[argc];
|
||||
for (int i = 0; i < argc; i++) {
|
||||
// Compute the size of the required buffer
|
||||
DWORD size = WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
wargv[i],
|
||||
-1,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL);
|
||||
if (size == 0) {
|
||||
// This should never happen.
|
||||
fprintf(stderr, "Could not convert arguments to utf8.");
|
||||
exit(1);
|
||||
}
|
||||
// Do the actual conversion
|
||||
argv[i] = new char[size];
|
||||
DWORD result = WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
wargv[i],
|
||||
-1,
|
||||
argv[i],
|
||||
size,
|
||||
NULL,
|
||||
NULL);
|
||||
if (result == 0) {
|
||||
// This should never happen.
|
||||
fprintf(stderr, "Could not convert arguments to utf8.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// Now that conversion is done, we can finally start.
|
||||
return node::Start(argc, argv);
|
||||
}
|
||||
#else
|
||||
// UNIX
|
||||
int main(int argc, char *argv[]) {
|
||||
return node::Start(argc, argv);
|
||||
}
|
||||
#endif
|
||||
|
@ -204,10 +204,7 @@ void PipeWrap::OnConnection(uv_stream_t* handle, int status) {
|
||||
PipeWrap* client_wrap =
|
||||
static_cast<PipeWrap*>(client_obj->GetPointerFromInternalField(0));
|
||||
|
||||
int r = uv_accept(handle, (uv_stream_t*)&client_wrap->handle_);
|
||||
|
||||
// uv_accept should always work.
|
||||
assert(r == 0);
|
||||
if (uv_accept(handle, (uv_stream_t*)&client_wrap->handle_)) return;
|
||||
|
||||
// Successful accept. Call the onconnection callback in JavaScript land.
|
||||
Local<Value> argv[1] = { client_obj };
|
||||
|
@ -366,10 +366,7 @@ void TCPWrap::OnConnection(uv_stream_t* handle, int status) {
|
||||
TCPWrap* client_wrap =
|
||||
static_cast<TCPWrap*>(client_obj->GetPointerFromInternalField(0));
|
||||
|
||||
int r = uv_accept(handle, (uv_stream_t*)&client_wrap->handle_);
|
||||
|
||||
// uv_accept should always work.
|
||||
assert(r == 0);
|
||||
if (uv_accept(handle, (uv_stream_t*)&client_wrap->handle_)) return;
|
||||
|
||||
// Successful accept. Call the onconnection callback in JavaScript land.
|
||||
argv[0] = client_obj;
|
||||
|
100
test/pummel/test-tls-ci-reneg-attack.js
Normal file
100
test/pummel/test-tls-ci-reneg-attack.js
Normal file
@ -0,0 +1,100 @@
|
||||
// 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.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var spawn = require('child_process').spawn;
|
||||
var tls = require('tls');
|
||||
var fs = require('fs');
|
||||
|
||||
// renegotiation limits to test
|
||||
var LIMITS = [0, 1, 2, 3, 5, 10, 16];
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
console.log("Skipping test, you probably don't have openssl installed.");
|
||||
process.exit();
|
||||
}
|
||||
|
||||
(function() {
|
||||
var n = 0;
|
||||
function next() {
|
||||
if (n >= LIMITS.length) return;
|
||||
tls.CLIENT_RENEG_LIMIT = LIMITS[n++];
|
||||
test(next);
|
||||
}
|
||||
next();
|
||||
})();
|
||||
|
||||
function test(next) {
|
||||
var options = {
|
||||
cert: fs.readFileSync(common.fixturesDir + '/test_cert.pem'),
|
||||
key: fs.readFileSync(common.fixturesDir + '/test_key.pem')
|
||||
};
|
||||
|
||||
var server = tls.createServer(options, function(conn) {
|
||||
conn.on('error', function(err) {
|
||||
console.error('Caught exception: ' + err);
|
||||
assert(/TLS session renegotiation attack/.test(err));
|
||||
conn.destroy();
|
||||
});
|
||||
conn.pipe(conn);
|
||||
});
|
||||
|
||||
server.listen(common.PORT, function() {
|
||||
var args = ('s_client -connect 127.0.0.1:' + common.PORT).split(' ');
|
||||
var child = spawn('openssl', args);
|
||||
|
||||
child.stdout.pipe(process.stdout);
|
||||
child.stderr.pipe(process.stderr);
|
||||
|
||||
// count handshakes, start the attack after the initial handshake is done
|
||||
var handshakes = 0;
|
||||
child.stderr.on('data', function(data) {
|
||||
handshakes += (('' + data).match(/verify return:1/g) || []).length;
|
||||
if (handshakes === 2) spam();
|
||||
});
|
||||
|
||||
child.on('exit', function() {
|
||||
// with a renegotiation limit <= 1, we always see 4 handshake markers:
|
||||
// two for the initial handshake and another two for the attempted
|
||||
// renegotiation
|
||||
assert.equal(handshakes, 2 * Math.max(2, tls.CLIENT_RENEG_LIMIT));
|
||||
server.close();
|
||||
process.nextTick(next);
|
||||
});
|
||||
|
||||
var closed = false;
|
||||
child.stdin.on('error', function(err) {
|
||||
assert.equal(err.code, 'EPIPE');
|
||||
closed = true;
|
||||
});
|
||||
child.stdin.on('close', function() {
|
||||
closed = true;
|
||||
});
|
||||
|
||||
// simulate renegotiation attack
|
||||
function spam() {
|
||||
if (closed) return;
|
||||
child.stdin.write("R\n");
|
||||
setTimeout(spam, 250);
|
||||
}
|
||||
});
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// This test starts two clustered HTTP servers on the same port. It expects the
|
||||
// first cluster to succeed and the second cluster to fail with EADDRINUSE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var cluster = require('cluster');
|
||||
var fork = require('child_process').fork;
|
||||
var http = require('http');
|
||||
|
||||
var id = process.argv[2];
|
||||
|
||||
if (!id) {
|
||||
var a = fork(__filename, ['one']);
|
||||
var b = fork(__filename, ['two']);
|
||||
|
||||
a.on('message', function(m) {
|
||||
assert.equal(m, 'READY');
|
||||
b.send('START');
|
||||
});
|
||||
|
||||
var ok = false;
|
||||
|
||||
b.on('message', function(m) {
|
||||
assert.equal(m, 'EADDRINUSE');
|
||||
a.kill();
|
||||
b.kill();
|
||||
ok = true;
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
a.kill();
|
||||
b.kill();
|
||||
assert(ok);
|
||||
});
|
||||
}
|
||||
else if (id === 'one') {
|
||||
if (cluster.isMaster) cluster.fork();
|
||||
http.createServer(assert.fail).listen(common.PORT, function() {
|
||||
process.send('READY');
|
||||
});
|
||||
}
|
||||
else if (id === 'two') {
|
||||
if (cluster.isMaster) cluster.fork();
|
||||
process.on('message', function(m) {
|
||||
assert.equal(m, 'START');
|
||||
var server = http.createServer(assert.fail);
|
||||
server.listen(common.PORT, assert.fail);
|
||||
server.on('error', function(e) {
|
||||
assert.equal(e.code, 'EADDRINUSE');
|
||||
process.send(e.code);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
assert(0); // bad command line argument
|
||||
}
|
35
test/simple/test-dgram-close.js
Normal file
35
test/simple/test-dgram-close.js
Normal file
@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
// Ensure that if a dgram socket is closed before the DNS lookup completes, it
|
||||
// won't crash.
|
||||
|
||||
var assert = require('assert'),
|
||||
common = require('../common'),
|
||||
dgram = require('dgram');
|
||||
|
||||
var buf = new Buffer(1024);
|
||||
buf.fill(42);
|
||||
|
||||
var socket = dgram.createSocket('udp4');
|
||||
|
||||
socket.send(buf, 0, buf.length, common.port, 'localhost');
|
||||
socket.close();
|
@ -33,6 +33,8 @@ var srv = http.createServer(function(req, res) {
|
||||
assert.equal(req.headers['www-authenticate'], 'foo, bar, baz');
|
||||
assert.equal(req.headers['x-foo'], 'bingo');
|
||||
assert.equal(req.headers['x-bar'], 'banjo, bango');
|
||||
assert.equal(req.headers['sec-websocket-protocol'], 'chat, share');
|
||||
assert.equal(req.headers['sec-websocket-extensions'], 'foo; 1, bar; 2, baz');
|
||||
|
||||
res.writeHead(200, {'Content-Type' : 'text/plain'});
|
||||
res.end('EOF');
|
||||
@ -57,7 +59,12 @@ srv.listen(common.PORT, function() {
|
||||
['WWW-AUTHENTICATE', 'baz'],
|
||||
['x-foo', 'bingo'],
|
||||
['x-bar', 'banjo'],
|
||||
['x-bar', 'bango']
|
||||
['x-bar', 'bango'],
|
||||
['sec-websocket-protocol', 'chat'],
|
||||
['sec-websocket-protocol', 'share'],
|
||||
['sec-websocket-extensions', 'foo; 1'],
|
||||
['sec-websocket-extensions', 'bar; 2'],
|
||||
['sec-websocket-extensions', 'baz']
|
||||
]
|
||||
});
|
||||
});
|
||||
|
@ -180,3 +180,12 @@ testMe.complete('inner.o', function(error, data) {
|
||||
assert.deepEqual(data, doesNotBreak);
|
||||
});
|
||||
|
||||
putIn.run(['.clear']);
|
||||
|
||||
// make sure tab completion works on non-Objects
|
||||
putIn.run([
|
||||
'var str = "test";'
|
||||
]);
|
||||
testMe.complete('str.len', function(error, data) {
|
||||
assert.deepEqual(data, [ [ 'str.length' ], 'str.len' ]);
|
||||
});
|
||||
|
156
test/simple/test-tls-over-http-tunnel.js
Normal file
156
test/simple/test-tls-over-http-tunnel.js
Normal file
@ -0,0 +1,156 @@
|
||||
// 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 common = require('../common');
|
||||
var assert = require('assert');
|
||||
|
||||
var fs = require('fs');
|
||||
var net = require('net');
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
|
||||
var proxyPort = common.PORT + 1;
|
||||
var gotRequest = false;
|
||||
|
||||
var key = fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem');
|
||||
var cert = fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem');
|
||||
|
||||
var options = {
|
||||
key: key,
|
||||
cert: cert
|
||||
};
|
||||
|
||||
var server = https.createServer(options, function(req, res) {
|
||||
console.log('SERVER: got request');
|
||||
res.writeHead(200, {
|
||||
'content-type': 'text/plain',
|
||||
});
|
||||
console.log('SERVER: sending response');
|
||||
res.end('hello world\n');
|
||||
});
|
||||
|
||||
var proxy = net.createServer(function(clientSocket) {
|
||||
console.log('PROXY: got a client connection');
|
||||
|
||||
var serverSocket = null;
|
||||
|
||||
clientSocket.on('data', function(chunk) {
|
||||
if (!serverSocket) {
|
||||
// Verify the CONNECT request
|
||||
assert.equal('CONNECT localhost:' + common.PORT + ' HTTP/1.1\r\n' +
|
||||
'Proxy-Connections: keep-alive\r\nContent-Length:' +
|
||||
' 0\r\nHost: localhost:' + proxyPort + '\r\n\r\n',
|
||||
chunk);
|
||||
|
||||
console.log('PROXY: got CONNECT request');
|
||||
console.log('PROXY: creating a tunnel');
|
||||
|
||||
// create the tunnel
|
||||
serverSocket = net.connect(common.PORT, function() {
|
||||
console.log('PROXY: replying to client CONNECT request');
|
||||
|
||||
// Send the response
|
||||
clientSocket.write('HTTP/1.1 200 OK\r\nProxy-Connections: keep' +
|
||||
'-alive\r\nConnections: keep-alive\r\nVia: ' +
|
||||
'localhost:' + proxyPort + '\r\n\r\n');
|
||||
});
|
||||
|
||||
serverSocket.on('data', function(chunk) {
|
||||
clientSocket.write(chunk);
|
||||
});
|
||||
|
||||
serverSocket.on('end', function() {
|
||||
clientSocket.destroy();
|
||||
});
|
||||
} else {
|
||||
serverSocket.write(chunk);
|
||||
}
|
||||
});
|
||||
|
||||
clientSocket.on('end', function() {
|
||||
serverSocket.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(common.PORT);
|
||||
|
||||
proxy.listen(proxyPort, function() {
|
||||
console.log('CLIENT: Making CONNECT request');
|
||||
|
||||
http.request({
|
||||
port: proxyPort,
|
||||
method: 'CONNECT',
|
||||
path: 'localhost:' + common.PORT,
|
||||
headers: {
|
||||
'Proxy-Connections': 'keep-alive',
|
||||
'Content-Length': 0
|
||||
}
|
||||
}, function(res) {
|
||||
assert.equal(200, res.statusCode);
|
||||
console.log('CLIENT: got CONNECT response');
|
||||
|
||||
// detach the socket
|
||||
res.socket.emit('agentRemove');
|
||||
res.socket.removeAllListeners('data');
|
||||
res.socket.removeAllListeners('close');
|
||||
res.socket.removeAllListeners('error');
|
||||
res.socket.removeAllListeners('drain');
|
||||
res.socket.removeAllListeners('end');
|
||||
res.socket.ondata = null;
|
||||
res.socket.onend = null;
|
||||
res.socket.ondrain = null;
|
||||
|
||||
console.log('CLIENT: Making HTTPS request');
|
||||
|
||||
https.get({
|
||||
path: '/foo',
|
||||
key: key,
|
||||
cert: cert,
|
||||
socket: res.socket, // reuse the socket
|
||||
agent: false,
|
||||
}, function(res) {
|
||||
assert.equal(200, res.statusCode);
|
||||
|
||||
res.on('data', function(chunk) {
|
||||
assert.equal('hello world\n', chunk);
|
||||
console.log('CLIENT: got HTTPS response');
|
||||
gotRequest = true;
|
||||
});
|
||||
|
||||
res.on('end', function() {
|
||||
proxy.close();
|
||||
server.close();
|
||||
});
|
||||
}).end();
|
||||
}).end();
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.ok(gotRequest);
|
||||
});
|
39
vcbuild.bat
39
vcbuild.bat
@ -26,26 +26,27 @@ set upload=
|
||||
|
||||
:next-arg
|
||||
if "%1"=="" goto args-done
|
||||
if /i "%1"=="debug" set config=Debug&goto arg-ok
|
||||
if /i "%1"=="release" set config=Release&goto arg-ok
|
||||
if /i "%1"=="clean" set target=Clean&goto arg-ok
|
||||
if /i "%1"=="ia32" set target_arch=ia32&goto arg-ok
|
||||
if /i "%1"=="x86" set target_arch=ia32&goto arg-ok
|
||||
if /i "%1"=="x64" set target_arch=x64&goto arg-ok
|
||||
if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok
|
||||
if /i "%1"=="nobuild" set nobuild=1&goto arg-ok
|
||||
if /i "%1"=="nosign" set nosign=1&goto arg-ok
|
||||
if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok
|
||||
if /i "%1"=="test-uv" set test=test-uv&goto arg-ok
|
||||
if /i "%1"=="test-internet"set test=test-internet&goto arg-ok
|
||||
if /i "%1"=="test-pummel" set test=test-pummel&goto arg-ok
|
||||
if /i "%1"=="test-simple" set test=test-simple&goto arg-ok
|
||||
if /i "%1"=="test-message" set test=test-message&goto arg-ok
|
||||
if /i "%1"=="test-all" set test=test-all&goto arg-ok
|
||||
if /i "%1"=="test" set test=test&goto arg-ok
|
||||
if /i "%1"=="msi" set msi=1&goto arg-ok
|
||||
if /i "%1"=="upload" set upload=1&goto arg-ok
|
||||
if /i "%1"=="debug" set config=Debug&goto arg-ok
|
||||
if /i "%1"=="release" set config=Release&goto arg-ok
|
||||
if /i "%1"=="clean" set target=Clean&goto arg-ok
|
||||
if /i "%1"=="ia32" set target_arch=ia32&goto arg-ok
|
||||
if /i "%1"=="x86" set target_arch=ia32&goto arg-ok
|
||||
if /i "%1"=="x64" set target_arch=x64&goto arg-ok
|
||||
if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok
|
||||
if /i "%1"=="nobuild" set nobuild=1&goto arg-ok
|
||||
if /i "%1"=="nosign" set nosign=1&goto arg-ok
|
||||
if /i "%1"=="nosnapshot" set nosnapshot=1&goto arg-ok
|
||||
if /i "%1"=="test-uv" set test=test-uv&goto arg-ok
|
||||
if /i "%1"=="test-internet" set test=test-internet&goto arg-ok
|
||||
if /i "%1"=="test-pummel" set test=test-pummel&goto arg-ok
|
||||
if /i "%1"=="test-simple" set test=test-simple&goto arg-ok
|
||||
if /i "%1"=="test-message" set test=test-message&goto arg-ok
|
||||
if /i "%1"=="test-all" set test=test-all&goto arg-ok
|
||||
if /i "%1"=="test" set test=test&goto arg-ok
|
||||
if /i "%1"=="msi" set msi=1&goto arg-ok
|
||||
if /i "%1"=="upload" set upload=1&goto arg-ok
|
||||
|
||||
echo Warning: ignoring invalid command line option `%1`.
|
||||
|
||||
:arg-ok
|
||||
shift
|
||||
|
Loading…
x
Reference in New Issue
Block a user