summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisaacs <i@izs.me>2012-02-18 09:46:58 -0800
committerisaacs <i@izs.me>2012-02-18 09:46:58 -0800
commit31721da4b120ca2b1c0b6b9e93ce8beb5e810da3 (patch)
tree9a4da945ae4868c6dbd0c509f8f08c87b8776a92
parent07872870213ed38c1978b37e9e2e22db733b40f3 (diff)
parentc1f474010e8e58793408fc1d8303aeb1a01ba735 (diff)
downloadnode-new-31721da4b120ca2b1c0b6b9e93ce8beb5e810da3.tar.gz
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
-rw-r--r--AUTHORS3
-rw-r--r--ChangeLog42
-rw-r--r--deps/uv/src/unix/tty.c4
-rw-r--r--deps/uv/src/win/error.c3
-rw-r--r--doc/about/index.html2
-rw-r--r--doc/api/tls.markdown97
-rw-r--r--doc/community/index.html6
-rw-r--r--doc/index.html6
-rw-r--r--doc/logos/index.html23
-rw-r--r--lib/dgram.js2
-rw-r--r--lib/http.js2
-rw-r--r--lib/path.js8
-rw-r--r--lib/repl.js7
-rw-r--r--lib/tls.js80
-rw-r--r--node.gyp1
-rw-r--r--src/node.cc241
-rw-r--r--src/node_crypto.cc16
-rw-r--r--src/node_crypto.h2
-rw-r--r--src/node_main.cc41
-rw-r--r--src/pipe_wrap.cc5
-rw-r--r--src/tcp_wrap.cc5
-rw-r--r--test/pummel/test-tls-ci-reneg-attack.js100
-rw-r--r--test/simple/test-cluster-bind-twice.js77
-rw-r--r--test/simple/test-dgram-close.js35
-rw-r--r--test/simple/test-http-server-multiheaders.js9
-rw-r--r--test/simple/test-repl-tab-complete.js9
-rw-r--r--test/simple/test-tls-over-http-tunnel.js156
-rw-r--r--vcbuild.bat41
28 files changed, 707 insertions, 316 deletions
diff --git a/AUTHORS b/AUTHORS
index 509e8be9f7..e5c82011f0 100644
--- a/AUTHORS
+++ b/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>
diff --git a/ChangeLog b/ChangeLog
index 6ef1ef1a1b..b5306ae9cc 100644
--- a/ChangeLog
+++ b/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
diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c
index 18a892168f..c1429660eb 100644
--- a/deps/uv/src/unix/tty.c
+++ b/deps/uv/src/unix/tty.c
@@ -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;
}
diff --git a/deps/uv/src/win/error.c b/deps/uv/src/win/error.c
index bc7cfdf0bf..1922f2039b 100644
--- a/deps/uv/src/win/error.c
+++ b/deps/uv/src/win/error.c
@@ -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;
diff --git a/doc/about/index.html b/doc/about/index.html
index f2bfb67f51..087cd48b80 100644
--- a/doc/about/index.html
+++ b/doc/about/index.html
@@ -1,5 +1,5 @@
<!doctype html>
-<html>
+<html lang="en">
<head>
<meta charset="utf-8">
<style>
diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown
index af469aa471..5a4f4632dc 100644
--- a/doc/api/tls.markdown
+++ b/doc/api/tls.markdown
@@ -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`.
diff --git a/doc/community/index.html b/doc/community/index.html
index 1fb408a971..1188d7f9b2 100644
--- a/doc/community/index.html
+++ b/doc/community/index.html
@@ -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"
diff --git a/doc/index.html b/doc/index.html
index b367e3f5d2..e4784729b7 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -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>
diff --git a/doc/logos/index.html b/doc/logos/index.html
index 3e7897800c..1b36f3a955 100644
--- a/doc/logos/index.html
+++ b/doc/logos/index.html
@@ -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>
diff --git a/lib/dgram.js b/lib/dgram.js
index b5b0473de8..8a88364bb5 100644
--- a/lib/dgram.js
+++ b/lib/dgram.js
@@ -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;
diff --git a/lib/http.js b/lib/http.js
index a0745f0b99..d800895a5a 100644
--- a/lib/http.js
+++ b/lib/http.js
@@ -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 {
diff --git a/lib/path.js b/lib/path.js
index ff04cc6762..0ca51e84f3 100644
--- a/lib/path.js
+++ b/lib/path.js
@@ -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
diff --git a/lib/repl.js b/lib/repl.js
index aeb51110eb..7bbc6d1448 100644
--- a/lib/repl.js
+++ b/lib/repl.js
@@ -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);
diff --git a/lib/tls.js b/lib/tls.js
index 430ef2f259..d951ef358b 100644
--- a/lib/tls.js
+++ b/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;
+
+ if (this.ssl.timer) {
+ clearTimeout(this.ssl.timer);
+ this.ssl.timer = null;
+ }
- self.encrypted.writable = self.encrypted.readable = false;
- self.cleartext.writable = self.cleartext.readable = false;
+ this.ssl.error = null;
+ this.ssl.close();
+ this.ssl = null;
- process.nextTick(function() {
- self.cleartext.emit('end');
- self.encrypted.emit('close');
- self.cleartext.emit('close');
- });
+ 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');
+ });
+ }
};
diff --git a/node.gyp b/node.gyp
index 474de0e931..24f579e054 100644
--- a/node.gyp
+++ b/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
diff --git a/src/node.cc b/src/node.cc
index 9a2d8faeb1..42a2f8c0d7 100644
--- a/src/node.cc
+++ b/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;
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 41bb2bd40e..b011d52061 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -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;
diff --git a/src/node_crypto.h b/src/node_crypto.h
index 4cde964da4..87a5340147 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -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_;
diff --git a/src/node_main.cc b/src/node_main.cc
index 3100149868..dba8b69203 100644
--- a/src/node_main.cc
+++ b/src/node_main.cc
@@ -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
diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc
index 6c3887d84f..c99fe47397 100644
--- a/src/pipe_wrap.cc
+++ b/src/pipe_wrap.cc
@@ -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 };
diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc
index 688d7d7190..14e6d3ed66 100644
--- a/src/tcp_wrap.cc
+++ b/src/tcp_wrap.cc
@@ -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;
diff --git a/test/pummel/test-tls-ci-reneg-attack.js b/test/pummel/test-tls-ci-reneg-attack.js
new file mode 100644
index 0000000000..5d04a6b6f3
--- /dev/null
+++ b/test/pummel/test-tls-ci-reneg-attack.js
@@ -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);
+ }
+ });
+}
diff --git a/test/simple/test-cluster-bind-twice.js b/test/simple/test-cluster-bind-twice.js
deleted file mode 100644
index 068842fa53..0000000000
--- a/test/simple/test-cluster-bind-twice.js
+++ /dev/null
@@ -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
-}
diff --git a/test/simple/test-dgram-close.js b/test/simple/test-dgram-close.js
new file mode 100644
index 0000000000..58d7aa786c
--- /dev/null
+++ b/test/simple/test-dgram-close.js
@@ -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();
diff --git a/test/simple/test-http-server-multiheaders.js b/test/simple/test-http-server-multiheaders.js
index 9917dd2bba..a94ea27321 100644
--- a/test/simple/test-http-server-multiheaders.js
+++ b/test/simple/test-http-server-multiheaders.js
@@ -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']
]
});
});
diff --git a/test/simple/test-repl-tab-complete.js b/test/simple/test-repl-tab-complete.js
index ba511046b5..0bc43ab196 100644
--- a/test/simple/test-repl-tab-complete.js
+++ b/test/simple/test-repl-tab-complete.js
@@ -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' ]);
+});
diff --git a/test/simple/test-tls-over-http-tunnel.js b/test/simple/test-tls-over-http-tunnel.js
new file mode 100644
index 0000000000..d2b8257e92
--- /dev/null
+++ b/test/simple/test-tls-over-http-tunnel.js
@@ -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);
+});
diff --git a/vcbuild.bat b/vcbuild.bat
index aa0b83664d..54f579dce1 100644
--- a/vcbuild.bat
+++ b/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