From 1fa0bca2ad58ad1d91557f8ae7f467370c0290d6 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Mon, 16 Jul 2012 21:41:26 +0400 Subject: [PATCH 01/12] net: ignore socket.setTimeout(Infinity) (and NaN) --- lib/net.js | 2 +- test/simple/test-net-settimeout.js | 32 ++++++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/net.js b/lib/net.js index f5d3ebdd22f..b4934e3269c 100644 --- a/lib/net.js +++ b/lib/net.js @@ -160,7 +160,7 @@ Socket.prototype.listen = function() { Socket.prototype.setTimeout = function(msecs, callback) { - if (msecs > 0) { + if (msecs > 0 && !isNaN(msecs) && isFinite(msecs)) { timers.enroll(this, msecs); timers.active(this); if (callback) { diff --git a/test/simple/test-net-settimeout.js b/test/simple/test-net-settimeout.js index 5b14117e83a..da13385b94e 100644 --- a/test/simple/test-net-settimeout.js +++ b/test/simple/test-net-settimeout.js @@ -33,18 +33,24 @@ var server = net.createServer(function(c) { }); server.listen(common.PORT); -var socket = net.createConnection(common.PORT, 'localhost'); +var killers = [0, Infinity, NaN]; -socket.setTimeout(T, function() { - socket.destroy(); - server.close(); - assert.ok(false); +var left = killers.length; +killers.forEach(function(killer) { + var socket = net.createConnection(common.PORT, 'localhost'); + + socket.setTimeout(T, function() { + socket.destroy(); + if (--left === 0) server.close(); + assert.ok(killer !== 0); + clearTimeout(timeout); + }); + + socket.setTimeout(killer); + + var timeout = setTimeout(function() { + socket.destroy(); + if (--left === 0) server.close(); + assert.ok(killer === 0); + }, T * 2); }); - -socket.setTimeout(0); - -setTimeout(function() { - socket.destroy(); - server.close(); - assert.ok(true); -}, T * 2); From 53716eb0b5338999761d115fad9d392077836e63 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Jul 2012 23:53:27 +0400 Subject: [PATCH 02/12] http/https: pass request to .createConnection() It's useful for passing some additional options of request object to the underlying API --- lib/http.js | 19 +++++++++++++------ lib/https.js | 11 ++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/http.js b/lib/http.js index 01aa4ebde80..eccea994483 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1067,7 +1067,7 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) { } if (this.sockets[name].length < this.maxSockets) { // If we are under maxSockets create a new one. - req.onSocket(this.createSocket(name, host, port, localAddress)); + req.onSocket(this.createSocket(name, host, port, localAddress, req)); } else { // We are over limit so we'll add it to the queue. if (!this.requests[name]) { @@ -1076,13 +1076,13 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) { this.requests[name].push(req); } }; -Agent.prototype.createSocket = function(name, host, port, localAddress) { +Agent.prototype.createSocket = function(name, host, port, localAddress, req) { var self = this; var options = util._extend({}, self.options); options.port = port; options.host = host; options.localAddress = localAddress; - var s = self.createConnection(options); + var s = self.createConnection.call(req, options); if (!self.sockets[name]) { self.sockets[name] = []; } @@ -1123,7 +1123,13 @@ Agent.prototype.removeSocket = function(s, name, host, port, localAddress) { } if (this.requests[name] && this.requests[name].length) { // If we have pending requests and a socket gets closed a new one - this.createSocket(name, host, port, localAddress).emit('free'); + this.createSocket( + name, + host, + port, + localAddress, + this.requests[name][0] + ).emit('free'); } }; @@ -1135,6 +1141,7 @@ function ClientRequest(options, cb) { var self = this; OutgoingMessage.call(self); + this.options = options; self.agent = options.agent === undefined ? globalAgent : options.agent; var defaultPort = options.defaultPort || 80; @@ -1194,7 +1201,7 @@ function ClientRequest(options, cb) { self._last = true; self.shouldKeepAlive = false; if (options.createConnection) { - self.onSocket(options.createConnection(self.socketPath)); + self.onSocket(options.createConnection.call(self, self.socketPath)); } else { self.onSocket(net.createConnection(self.socketPath)); } @@ -1210,7 +1217,7 @@ function ClientRequest(options, cb) { if (options.createConnection) { options.port = port; options.host = host; - var conn = options.createConnection(options); + var conn = options.createConnection.call(self, options); } else { var conn = net.createConnection({ port: port, diff --git a/lib/https.js b/lib/https.js index 9778354007f..ce431a127ac 100644 --- a/lib/https.js +++ b/lib/https.js @@ -21,7 +21,8 @@ var tls = require('tls'); var http = require('http'); -var inherits = require('util').inherits; +var util = require('util'); +var inherits = util.inherits; function Server(opts, requestListener) { if (!(this instanceof Server)) return new Server(opts, requestListener); @@ -52,15 +53,15 @@ exports.createServer = function(opts, requestListener) { // HTTPS agents. function createConnection(/* [port, host, options] */) { - var options = {}; + var options = util._extend({}, this.options); if (typeof arguments[0] === 'object') { - options = arguments[0]; + options = util._extend(options, arguments[0]); } else if (typeof arguments[1] === 'object') { - options = arguments[1]; + options = util._extend(options, arguments[1]); options.port = arguments[0]; } else if (typeof arguments[2] === 'object') { - options = arguments[2]; + options = util._extend(options, arguments[2]); options.port = arguments[0]; options.host = arguments[1]; } else { From eb2ca104628e415fc73c330cdd76fca77bf5ba97 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 11 Jul 2012 23:54:20 +0400 Subject: [PATCH 03/12] tls: veryify server's identity --- lib/http.js | 14 +- lib/tls.js | 111 +++++++++- src/node_crypto.cc | 5 +- test/simple/test-tls-check-server-identity.js | 189 ++++++++++++++++++ 4 files changed, 306 insertions(+), 13 deletions(-) create mode 100644 test/simple/test-tls-check-server-identity.js diff --git a/lib/http.js b/lib/http.js index eccea994483..5e7c0ba16b5 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1123,13 +1123,11 @@ Agent.prototype.removeSocket = function(s, name, host, port, localAddress) { } if (this.requests[name] && this.requests[name].length) { // If we have pending requests and a socket gets closed a new one - this.createSocket( - name, - host, - port, - localAddress, - this.requests[name][0] - ).emit('free'); + this.createSocket(name, + host, + port, + localAddress, + this.requests[name][0]).emit('free'); } }; @@ -1141,7 +1139,7 @@ function ClientRequest(options, cb) { var self = this; OutgoingMessage.call(self); - this.options = options; + this.options = util._extend({}, options); self.agent = options.agent === undefined ? globalAgent : options.agent; var defaultPort = options.defaultPort || 80; diff --git a/lib/tls.js b/lib/tls.js index 9f3a42b5117..640328ec4fa 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -22,6 +22,7 @@ var crypto = require('crypto'); var util = require('util'); var net = require('net'); +var url = require('url'); var events = require('events'); var Stream = require('stream'); var END_OF_FILE = 42; @@ -77,6 +78,99 @@ function convertNPNProtocols(NPNProtocols, out) { } } + +function checkServerIdentity(host, cert) { + // Create regexp to much hostnames + function regexpify(host, wildcards) { + // Add trailing dot (make hostnames uniform) + if (!/\.$/.test(host)) host += '.'; + + // Host names with less than one dots are considered too broad, + // and should not be allowed + if (!/^.+\..+$/.test(host)) return /$./; + + // The same applies to hostname with more than one wildcard, + // if hostname has wildcard when wildcards are not allowed, + // or if there are less than two dots after wildcard (i.e. *.com or *d.com) + if (/\*.*\*/.test(host) || !wildcards && /\*/.test(host) || + /\*/.test(host) && !/\*.*\..+\..+/.test(host)) { + return /$./; + } + + // Replace wildcard chars with regexp's wildcard and + // escape all characters that have special meaning in regexps + // (i.e. '.', '[', '{', '*', and others) + var re = host.replace( + /\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g, + function(all, sub) { + if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub); + return '\\' + all; + } + ); + + return new RegExp('^' + re + '$', 'i'); + } + + var dnsNames = [], + uriNames = [], + ips = [], + valid = false; + + // There're several names to perform check against: + // CN and altnames in certificate extension + // (DNS names, IP addresses, and URIs) + // + // Walk through altnames and generate lists of those names + if (cert.subjectaltname) { + cert.subjectaltname.split(/, /g).forEach(function(altname) { + if (/^DNS:/.test(altname)) { + dnsNames.push(altname.slice(4)); + } else if (/^IP Address:/.test(altname)) { + ips.push(altname.slice(11)); + } else if (/^URI:/.test(altname)) { + var uri = url.parse(altname.slice(4)); + if (uri) uriNames.push(uri.hostname); + } + }); + } + + // If hostname is an IP address, it should be present in the list of IP + // addresses. + if (net.isIP(host)) { + valid = ips.some(function(ip) { + return ip === host; + }); + } else { + // Transform hostname to canonical form + if (!/\.$/.test(host)) host += '.'; + + // Otherwise check all DNS/URI records from certificate + // (with allowed wildcards) + dnsNames = dnsNames.map(function(name) { + return regexpify(name, true); + }); + + // Wildcards ain't allowed in URI names + uriNames = uriNames.map(function(name) { + return regexpify(name, false); + }); + + dnsNames = dnsNames.concat(uriNames); + + // And only after check if hostname matches CN + // (because CN is deprecated, but should be used for compatiblity anyway) + dnsNames.push(regexpify(cert.subject.CN, false)); + + valid = dnsNames.some(function(re) { + return re.test(host); + }); + } + + return valid; +}; +exports.checkServerIdentity = checkServerIdentity; + + // Base class of both CleartextStream and EncryptedStream function CryptoStream(pair) { Stream.call(this); @@ -1118,11 +1212,12 @@ exports.connect = function(/* [port, host], options, cb */) { var sslcontext = crypto.createCredentials(options); convertNPNProtocols(options.NPNProtocols, this); - var pair = new SecurePair(sslcontext, false, true, + var hostname = options.servername || options.host, + pair = new SecurePair(sslcontext, false, true, options.rejectUnauthorized === true ? true : false, { NPNProtocols: this.NPNProtocols, - servername: options.servername || options.host + servername: hostname }); if (options.session) { @@ -1147,9 +1242,19 @@ exports.connect = function(/* [port, host], options, cb */) { cleartext.npnProtocol = pair.npnProtocol; + // Verify that server's identity matches it's certificate's names + if (!verifyError) { + var validCert = checkServerIdentity(hostname, + pair.cleartext.getPeerCertificate()); + if (!validCert) { + verifyError = new Error('Hostname/IP doesn\'t match certificate\'s ' + + 'altnames'); + } + } + if (verifyError) { cleartext.authorized = false; - cleartext.authorizationError = verifyError; + cleartext.authorizationError = verifyError.message; if (pair._rejectUnauthorized) { cleartext.emit('error', verifyError); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 8b21825a2bf..5b21e55a8cf 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1547,7 +1547,8 @@ Handle Connection::VerifyError(const Arguments& args) { // We requested a certificate and they did not send us one. // Definitely an error. // XXX is this the right error message? - return scope.Close(String::New("UNABLE_TO_GET_ISSUER_CERT")); + return scope.Close(Exception::Error( + String::New("UNABLE_TO_GET_ISSUER_CERT"))); } X509_free(peer_cert); @@ -1673,7 +1674,7 @@ Handle Connection::VerifyError(const Arguments& args) { break; } - return scope.Close(s); + return scope.Close(Exception::Error(s)); } diff --git a/test/simple/test-tls-check-server-identity.js b/test/simple/test-tls-check-server-identity.js new file mode 100644 index 00000000000..f79823b84cd --- /dev/null +++ b/test/simple/test-tls-check-server-identity.js @@ -0,0 +1,189 @@ +// 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 util = require('util'); +var tls = require('tls'); + +var tests = [ + // Basic CN handling + { host: 'a.com', cert: { subject: { CN: 'a.com' } }, result: true }, + { host: 'a.com', cert: { subject: { CN: 'A.COM' } }, result: true }, + { host: 'a.com', cert: { subject: { CN: 'b.com' } }, result: false }, + { host: 'a.com', cert: { subject: { CN: 'a.com.' } }, result: true }, + + // No wildcards in CN + { host: 'b.a.com', cert: { subject: { CN: '*.a.com' } }, result: false }, + + // DNS names and CN + { + host: 'a.com', cert: { + subjectaltname: 'DNS:*', + subject: { CN: 'b.com' } + }, + result: false + }, + { + host: 'a.com', cert: { + subjectaltname: 'DNS:*.com', + subject: { CN: 'b.com' } + }, + result: false + }, + { + host: 'a.co.uk', cert: { + subjectaltname: 'DNS:*.co.uk', + subject: { CN: 'b.com' } + }, + result: true + }, + { + host: 'a.com', cert: { + subjectaltname: 'DNS:*.a.com', + subject: { CN: 'a.com' } + }, + result: true + }, + { + host: 'a.com', cert: { + subjectaltname: 'DNS:*.a.com', + subject: { CN: 'b.com' } + }, + result: false + }, + { + host: 'a.com', cert: { + subjectaltname: 'DNS:a.com', + subject: { CN: 'b.com' } + }, + result: true + }, + { + host: 'a.com', cert: { + subjectaltname: 'DNS:A.COM', + subject: { CN: 'b.com' } + }, + result: true + }, + + // DNS names + { + host: 'a.com', cert: { + subjectaltname: 'DNS:*.a.com', + subject: {} + }, + result: false + }, + { + host: 'b.a.com', cert: { + subjectaltname: 'DNS:*.a.com', + subject: {} + }, + result: true + }, + { + host: 'c.b.a.com', cert: { + subjectaltname: 'DNS:*.a.com', + subject: {} + }, + result: false + }, + { + host: 'b.a.com', cert: { + subjectaltname: 'DNS:*b.a.com', + subject: {} + }, + result: true + }, + { + host: 'a-cb.a.com', cert: { + subjectaltname: 'DNS:*b.a.com', + subject: {} + }, + result: true + }, + { + host: 'a.b.a.com', cert: { + subjectaltname: 'DNS:*b.a.com', + subject: {} + }, + result: false + }, + // Mutliple DNS names + { + host: 'a.b.a.com', cert: { + subjectaltname: 'DNS:*b.a.com, DNS:a.b.a.com', + subject: {} + }, + result: true + }, + // URI names + { + host: 'a.b.a.com', cert: { + subjectaltname: 'URI:http://a.b.a.com/', + subject: {} + }, + result: true + }, + { + host: 'a.b.a.com', cert: { + subjectaltname: 'URI:http://*.b.a.com/', + subject: {} + }, + result: false + }, + // IP addresses + { + host: 'a.b.a.com', cert: { + subjectaltname: 'IP Address:127.0.0.1', + subject: {} + }, + result: false + }, + { + host: '127.0.0.1', cert: { + subjectaltname: 'IP Address:127.0.0.1', + subject: {} + }, + result: true + }, + { + host: '127.0.0.2', cert: { + subjectaltname: 'IP Address:127.0.0.1', + subject: {} + }, + result: false + }, + { + host: '127.0.0.1', cert: { + subjectaltname: 'DNS:a.com', + subject: {} + }, + result: false + }, +]; + +tests.forEach(function(test, i) { + assert.equal(tls.checkServerIdentity(test.host, test.cert), + test.result, + 'Test#' + i + ' failed: ' + util.inspect(test)); +}); From e43fe5c833c941aba25bfdb6193ef41fc50ae405 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 20 Jul 2012 20:51:02 +0400 Subject: [PATCH 04/12] Revert "http/https: pass request to .createConnection()" This reverts commit 53716eb0b5338999761d115fad9d392077836e63. --- lib/http.js | 17 ++++++----------- lib/https.js | 11 +++++------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/http.js b/lib/http.js index 5e7c0ba16b5..01aa4ebde80 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1067,7 +1067,7 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) { } if (this.sockets[name].length < this.maxSockets) { // If we are under maxSockets create a new one. - req.onSocket(this.createSocket(name, host, port, localAddress, req)); + req.onSocket(this.createSocket(name, host, port, localAddress)); } else { // We are over limit so we'll add it to the queue. if (!this.requests[name]) { @@ -1076,13 +1076,13 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) { this.requests[name].push(req); } }; -Agent.prototype.createSocket = function(name, host, port, localAddress, req) { +Agent.prototype.createSocket = function(name, host, port, localAddress) { var self = this; var options = util._extend({}, self.options); options.port = port; options.host = host; options.localAddress = localAddress; - var s = self.createConnection.call(req, options); + var s = self.createConnection(options); if (!self.sockets[name]) { self.sockets[name] = []; } @@ -1123,11 +1123,7 @@ Agent.prototype.removeSocket = function(s, name, host, port, localAddress) { } if (this.requests[name] && this.requests[name].length) { // If we have pending requests and a socket gets closed a new one - this.createSocket(name, - host, - port, - localAddress, - this.requests[name][0]).emit('free'); + this.createSocket(name, host, port, localAddress).emit('free'); } }; @@ -1139,7 +1135,6 @@ function ClientRequest(options, cb) { var self = this; OutgoingMessage.call(self); - this.options = util._extend({}, options); self.agent = options.agent === undefined ? globalAgent : options.agent; var defaultPort = options.defaultPort || 80; @@ -1199,7 +1194,7 @@ function ClientRequest(options, cb) { self._last = true; self.shouldKeepAlive = false; if (options.createConnection) { - self.onSocket(options.createConnection.call(self, self.socketPath)); + self.onSocket(options.createConnection(self.socketPath)); } else { self.onSocket(net.createConnection(self.socketPath)); } @@ -1215,7 +1210,7 @@ function ClientRequest(options, cb) { if (options.createConnection) { options.port = port; options.host = host; - var conn = options.createConnection.call(self, options); + var conn = options.createConnection(options); } else { var conn = net.createConnection({ port: port, diff --git a/lib/https.js b/lib/https.js index ce431a127ac..9778354007f 100644 --- a/lib/https.js +++ b/lib/https.js @@ -21,8 +21,7 @@ var tls = require('tls'); var http = require('http'); -var util = require('util'); -var inherits = util.inherits; +var inherits = require('util').inherits; function Server(opts, requestListener) { if (!(this instanceof Server)) return new Server(opts, requestListener); @@ -53,15 +52,15 @@ exports.createServer = function(opts, requestListener) { // HTTPS agents. function createConnection(/* [port, host, options] */) { - var options = util._extend({}, this.options); + var options = {}; if (typeof arguments[0] === 'object') { - options = util._extend(options, arguments[0]); + options = arguments[0]; } else if (typeof arguments[1] === 'object') { - options = util._extend(options, arguments[1]); + options = arguments[1]; options.port = arguments[0]; } else if (typeof arguments[2] === 'object') { - options = util._extend(options, arguments[2]); + options = arguments[2]; options.port = arguments[0]; options.host = arguments[1]; } else { From 4aa09d1e0e29139484e3f2c72294fd8315c9ca33 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 20 Jul 2012 20:47:05 +0400 Subject: [PATCH 05/12] tls: localhost is valid against identity-check --- lib/tls.js | 4 ---- test/simple/test-tls-check-server-identity.js | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index 640328ec4fa..aec7cae4267 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -85,10 +85,6 @@ function checkServerIdentity(host, cert) { // Add trailing dot (make hostnames uniform) if (!/\.$/.test(host)) host += '.'; - // Host names with less than one dots are considered too broad, - // and should not be allowed - if (!/^.+\..+$/.test(host)) return /$./; - // The same applies to hostname with more than one wildcard, // if hostname has wildcard when wildcards are not allowed, // or if there are less than two dots after wildcard (i.e. *.com or *d.com) diff --git a/test/simple/test-tls-check-server-identity.js b/test/simple/test-tls-check-server-identity.js index f79823b84cd..99835f8f5e3 100644 --- a/test/simple/test-tls-check-server-identity.js +++ b/test/simple/test-tls-check-server-identity.js @@ -180,6 +180,13 @@ var tests = [ }, result: false }, + { + host: 'localhost', cert: { + subjectaltname: 'DNS:a.com', + subject: { CN: 'localhost' } + }, + result: true + }, ]; tests.forEach(function(test, i) { From 5950db197c5ee73922beb67e3b8c060b5fcbc7f7 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 20 Jul 2012 21:10:23 +0400 Subject: [PATCH 06/12] tls: revert accidental API change socket.authorizationError should always be string. Also make sni test pass. --- lib/tls.js | 2 +- test/simple/test-tls-sni-server-client.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index aec7cae4267..584ee0a5b96 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -1059,7 +1059,7 @@ function Server(/* [options], listener */) { } else { var verifyError = pair.ssl.verifyError(); if (verifyError) { - pair.cleartext.authorizationError = verifyError; + pair.cleartext.authorizationError = verifyError.message; if (self.rejectUnauthorized) { socket.destroy(); diff --git a/test/simple/test-tls-sni-server-client.js b/test/simple/test-tls-sni-server-client.js index 721c2c0241b..093d0fd1154 100644 --- a/test/simple/test-tls-sni-server-client.js +++ b/test/simple/test-tls-sni-server-client.js @@ -94,7 +94,9 @@ server.listen(serverPort, startTest); function startTest() { function connectClient(options, callback) { var client = tls.connect(options, function() { - clientResults.push(client.authorized); + clientResults.push( + client.authorizationError && + /Hostname\/IP doesn't/.test(client.authorizationError)); client.destroy(); callback(); From 50122fed8ae29c982fa4c806136c4b24dd2de382 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 20 Jul 2012 21:43:12 +0400 Subject: [PATCH 07/12] tls: fix 'hostless' tls connection verification And fix last failing tests --- lib/tls.js | 2 +- test/simple/test-tls-client-verify.js | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index 584ee0a5b96..089ad9bab9e 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -1208,7 +1208,7 @@ exports.connect = function(/* [port, host], options, cb */) { var sslcontext = crypto.createCredentials(options); convertNPNProtocols(options.NPNProtocols, this); - var hostname = options.servername || options.host, + var hostname = options.servername || options.host || 'localhost', pair = new SecurePair(sslcontext, false, true, options.rejectUnauthorized === true ? true : false, { diff --git a/test/simple/test-tls-client-verify.js b/test/simple/test-tls-client-verify.js index 4e5c4abfbf0..9b1083f064f 100644 --- a/test/simple/test-tls-client-verify.js +++ b/test/simple/test-tls-client-verify.js @@ -25,6 +25,7 @@ if (!process.versions.openssl) { } +var hosterr = 'Hostname/IP doesn\'t match certificate\'s altnames'; var testCases = [{ ca: ['ca1-cert'], key: 'agent2-key', @@ -101,10 +102,12 @@ function testServers(index, servers, clientOptions, cb) { console.error('connecting...'); var client = tls.connect(clientOptions, function() { + var authorized = client.authorized || + client.authorizationError === hosterr; - console.error('expected: ' + ok + ' authed: ' + client.authorized); + console.error('expected: ' + ok + ' authed: ' + authorized); - assert.equal(ok, client.authorized); + assert.equal(ok, authorized); server.close(); }); From 42c6952edb5cfccd708bea8a9a6c02c67641a314 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 20 Jul 2012 22:07:16 +0400 Subject: [PATCH 08/12] tls: pass linting --- lib/tls.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index 089ad9bab9e..05e48d03a97 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -97,12 +97,11 @@ function checkServerIdentity(host, cert) { // escape all characters that have special meaning in regexps // (i.e. '.', '[', '{', '*', and others) var re = host.replace( - /\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g, - function(all, sub) { - if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub); - return '\\' + all; - } - ); + /\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g, + function(all, sub) { + if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub); + return '\\' + all; + }); return new RegExp('^' + re + '$', 'i'); } @@ -163,7 +162,7 @@ function checkServerIdentity(host, cert) { } return valid; -}; +} exports.checkServerIdentity = checkServerIdentity; From bc30c90af65c482971901c7ec6980172d5f54849 Mon Sep 17 00:00:00 2001 From: koichik Date: Sun, 22 Jul 2012 02:33:06 +0900 Subject: [PATCH 09/12] doc: remove duplicate section Fixes #3750. --- doc/api/cluster.markdown | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/doc/api/cluster.markdown b/doc/api/cluster.markdown index 2d48f247516..b82eb5700e3 100644 --- a/doc/api/cluster.markdown +++ b/doc/api/cluster.markdown @@ -242,18 +242,6 @@ Example: Spawn a new worker process. This can only be called from the master process. -## cluster.settings - -* {Object} - * `exec` {String} file path to worker file. Default: `__filename` - * `args` {Array} string arguments passed to worker. - (Default=`process.argv.slice(2)`) - * `silent` {Boolean} whether or not to send output to parent's stdio. - (Default=`false`) - -All settings set by the `.setupMaster` is stored in this settings object. -This object is not supposed to be change or set manually. - ## cluster.disconnect([callback]) * `callback` {Function} called when all workers are disconnected and handlers are closed From e06b5d7af7619e9234bb7eb8a9d66b3c7d245ad9 Mon Sep 17 00:00:00 2001 From: Brian White Date: Sun, 22 Jul 2012 23:08:13 -0300 Subject: [PATCH 10/12] http: remove duplicate assignments Closes GH-3754 --- lib/http.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/http.js b/lib/http.js index 01aa4ebde80..f7b2f786b69 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1482,9 +1482,7 @@ ClientRequest.prototype.onSocket = function(socket) { parser.incoming = null; req.parser = parser; - parser.socket = socket; socket.parser = parser; - parser.incoming = null; socket._httpMessage = req; // Setup "drain" propogation. From 688859afc09307d358b3f160cdd9aff2d2ce6c6f Mon Sep 17 00:00:00 2001 From: Peter Rybin Date: Tue, 3 Jul 2012 23:21:37 +0400 Subject: [PATCH 11/12] debugger: wake up the event loop when a debugger command is dispatched When the event loop was blocked in epoll / kqueue or similar, debugger commands wouldn't be processed. This patch fixes that by adding an uv_async handle which is triggered when a debugger command is dispatched. The async handle's callback makes sure that V8 is entered. Closes GH-3626 Closes GH-3718 --- src/node.cc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/node.cc b/src/node.cc index 05208b44b96..257b96e9eb9 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2471,11 +2471,35 @@ static void ParseArgs(int argc, char **argv) { static Isolate* node_isolate = NULL; static volatile bool debugger_running = false; + +static uv_async_t dispatch_debug_messages_async; + + +// Called from the main thread. +static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle, int status) { + v8::Debug::ProcessDebugMessages(); +} + + +// Called from V8 Debug Agent TCP thread. +static void DispatchMessagesDebugAgentCallback() { + uv_async_send(&dispatch_debug_messages_async); +} + + static void EnableDebug(bool wait_connect) { // If we're called from another thread, make sure to enter the right // v8 isolate. node_isolate->Enter(); + v8::Debug::SetDebugMessageDispatchHandler(DispatchMessagesDebugAgentCallback, + false); + + uv_async_init(uv_default_loop(), + &dispatch_debug_messages_async, + DispatchDebugMessagesAsyncCallback); + uv_unref((uv_handle_t*) &dispatch_debug_messages_async); + // Start the debug thread and it's associated TCP server on port 5858. bool r = v8::Debug::EnableAgent("node " NODE_VERSION, debug_port, From 43a0c8811627ea224bb18ba3c72b91ca2c4ea8d7 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 23 Jul 2012 11:26:03 +0200 Subject: [PATCH 12/12] windows: correctly prep long path for fs.exists(Sync) Closes GH-3739 --- lib/fs.js | 4 +- test/simple/test-regress-GH-3739.js | 66 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 test/simple/test-regress-GH-3739.js diff --git a/lib/fs.js b/lib/fs.js index ae9cdf4c4d8..51dfe562932 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -87,14 +87,14 @@ fs.Stats.prototype.isSocket = function() { }; fs.exists = function(path, callback) { - binding.stat(path, function(err, stats) { + binding.stat(pathModule._makeLong(path), function(err, stats) { if (callback) callback(err ? false : true); }); }; fs.existsSync = function(path) { try { - binding.stat(path); + binding.stat(pathModule._makeLong(path)); return true; } catch (e) { return false; diff --git a/test/simple/test-regress-GH-3739.js b/test/simple/test-regress-GH-3739.js new file mode 100644 index 00000000000..937068795af --- /dev/null +++ b/test/simple/test-regress-GH-3739.js @@ -0,0 +1,66 @@ +// 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.js'), + assert = require('assert'), + fs = require('fs'), + path = require('path'); + +var dir = path.resolve(common.fixturesDir), + dirs = []; + +// Make a long path. +for (var i = 0; i < 50; i++) { + dir = dir + '/123456790'; + try { + fs.mkdirSync(dir, '0777'); + } catch (e) { + if (e.code == 'EEXIST') { + // Ignore; + } else { + cleanup(); + throw e; + } + } + dirs.push(dir); +} + +// Test existsSync +var r = fs.existsSync(dir); +if (r !== true) { + cleanup(); + throw new Error('fs.existsSync returned false'); +} + +// Text exists +fs.exists(dir, function(r) { + cleanup(); + if (r !== true) { + throw new Error('fs.exists reported false'); + } +}); + +// Remove all created directories +function cleanup() { + for (var i = dirs.length - 1; i >= 0; i--) { + fs.rmdirSync(dirs[i]); + } +}