Skip to content

Commit d6d4610

Browse files
mattiasholmlundcjihrig
authored andcommitted
http, tls: better support for IPv6 addresses
- Properly handle IPv6 in Host header when setting servername. - When comparing IP addresses against addresses in the subjectAltName field of a certificate, format the address correctly before doing the string comparison. PR-URL: #14772 Fixes: #14736 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent a0b94f4 commit d6d4610

File tree

4 files changed

+83
-16
lines changed

4 files changed

+83
-16
lines changed

lib/_http_agent.js

+27-14
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,8 @@ Agent.prototype.addRequest = function addRequest(req, options, port/*legacy*/,
153153
if (options.socketPath)
154154
options.path = options.socketPath;
155155

156-
if (!options.servername) {
157-
options.servername = options.host;
158-
const hostHeader = req.getHeader('host');
159-
if (hostHeader) {
160-
options.servername = hostHeader.replace(/:.*$/, '');
161-
}
162-
}
156+
if (!options.servername)
157+
options.servername = calculateServerName(options, req);
163158

164159
var name = this.getName(options);
165160
if (!this.sockets[name]) {
@@ -207,13 +202,8 @@ Agent.prototype.createSocket = function createSocket(req, options, cb) {
207202
if (options.socketPath)
208203
options.path = options.socketPath;
209204

210-
if (!options.servername) {
211-
options.servername = options.host;
212-
const hostHeader = req.getHeader('host');
213-
if (hostHeader) {
214-
options.servername = hostHeader.replace(/:.*$/, '');
215-
}
216-
}
205+
if (!options.servername)
206+
options.servername = calculateServerName(options, req);
217207

218208
var name = self.getName(options);
219209
options._agentKey = name;
@@ -241,6 +231,29 @@ Agent.prototype.createSocket = function createSocket(req, options, cb) {
241231
}
242232
};
243233

234+
function calculateServerName(options, req) {
235+
let servername = options.host;
236+
const hostHeader = req.getHeader('host');
237+
if (hostHeader) {
238+
// abc => abc
239+
// abc:123 => abc
240+
// [::1] => ::1
241+
// [::1]:123 => ::1
242+
if (hostHeader.startsWith('[')) {
243+
const index = hostHeader.indexOf(']');
244+
if (index === -1) {
245+
// Leading '[', but no ']'. Need to do something...
246+
servername = hostHeader;
247+
} else {
248+
servername = hostHeader.substr(1, index - 1);
249+
}
250+
} else {
251+
servername = hostHeader.split(':', 1)[0];
252+
}
253+
}
254+
return servername;
255+
}
256+
244257
function installListeners(agent, s, options) {
245258
function onFree() {
246259
debug('CLIENT socket onFree');

lib/tls.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const net = require('net');
3131
const url = require('url');
3232
const binding = process.binding('crypto');
3333
const Buffer = require('buffer').Buffer;
34+
const canonicalizeIP = process.binding('cares_wrap').canonicalizeIP;
3435

3536
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
3637
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
@@ -181,7 +182,7 @@ exports.checkServerIdentity = function checkServerIdentity(host, cert) {
181182
const uri = url.parse(name.slice(4));
182183
uriNames.push(uri.hostname); // TODO(bnoordhuis) Also use scheme.
183184
} else if (name.startsWith('IP Address:')) {
184-
ips.push(name.slice(11));
185+
ips.push(canonicalizeIP(name.slice(11)));
185186
}
186187
}
187188
}
@@ -190,7 +191,7 @@ exports.checkServerIdentity = function checkServerIdentity(host, cert) {
190191
let reason = 'Unknown reason';
191192

192193
if (net.isIP(host)) {
193-
valid = ips.includes(host);
194+
valid = ips.includes(canonicalizeIP(host));
194195
if (!valid)
195196
reason = `IP: ${host} is not in the cert's list: ${ips.join(', ')}`;
196197
// TODO(bnoordhuis) Also check URI SANs that are IP addresses.

src/cares_wrap.cc

+22
Original file line numberDiff line numberDiff line change
@@ -1945,6 +1945,27 @@ void IsIPv6(const FunctionCallbackInfo<Value>& args) {
19451945
}
19461946
}
19471947

1948+
void CanonicalizeIP(const FunctionCallbackInfo<Value>& args) {
1949+
v8::Isolate* isolate = args.GetIsolate();
1950+
node::Utf8Value ip(isolate, args[0]);
1951+
char address_buffer[sizeof(struct in6_addr)];
1952+
char canonical_ip[INET6_ADDRSTRLEN];
1953+
1954+
int af;
1955+
if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0)
1956+
af = AF_INET;
1957+
else if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0)
1958+
af = AF_INET6;
1959+
else
1960+
return;
1961+
1962+
int err = uv_inet_ntop(af, address_buffer, canonical_ip,
1963+
sizeof(canonical_ip));
1964+
CHECK_EQ(err, 0);
1965+
1966+
args.GetReturnValue().Set(String::NewFromUtf8(isolate, canonical_ip));
1967+
}
1968+
19481969
void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
19491970
Environment* env = Environment::GetCurrent(args);
19501971

@@ -2165,6 +2186,7 @@ void Initialize(Local<Object> target,
21652186
env->SetMethod(target, "isIP", IsIP);
21662187
env->SetMethod(target, "isIPv4", IsIPv4);
21672188
env->SetMethod(target, "isIPv6", IsIPv6);
2189+
env->SetMethod(target, "canonicalizeIP", CanonicalizeIP);
21682190

21692191
env->SetMethod(target, "strerror", StrError);
21702192

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
require('../common');
3+
4+
// Test conversion of IP addresses to the format returned
5+
// for addresses in Subject Alternative Name section
6+
// of a TLS certificate
7+
8+
const assert = require('assert');
9+
const { canonicalizeIP } = process.binding('cares_wrap');
10+
11+
assert.strictEqual(canonicalizeIP('127.0.0.1'), '127.0.0.1');
12+
assert.strictEqual(canonicalizeIP('10.1.0.1'), '10.1.0.1');
13+
assert.strictEqual(canonicalizeIP('::1'), '::1');
14+
assert.strictEqual(canonicalizeIP('fe80:0:0:0:0:0:0:1'), 'fe80::1');
15+
assert.strictEqual(canonicalizeIP('fe80:0:0:0:0:0:0:0'), 'fe80::');
16+
assert.strictEqual(canonicalizeIP('fe80::0000:0010:0001'), 'fe80::10:1');
17+
assert.strictEqual(canonicalizeIP('0001:2222:3333:4444:5555:6666:7777:0088'),
18+
'1:2222:3333:4444:5555:6666:7777:88');
19+
20+
assert.strictEqual(canonicalizeIP('0001:2222:3333:4444:5555:6666::'),
21+
'1:2222:3333:4444:5555:6666::');
22+
23+
assert.strictEqual(canonicalizeIP('a002:B12:00Ba:4444:5555:6666:0:0'),
24+
'a002:b12:ba:4444:5555:6666::');
25+
26+
// IPv4 address represented in IPv6
27+
assert.strictEqual(canonicalizeIP('0:0:0:0:0:ffff:c0a8:101'),
28+
'::ffff:192.168.1.1');
29+
30+
assert.strictEqual(canonicalizeIP('::ffff:192.168.1.1'),
31+
'::ffff:192.168.1.1');

0 commit comments

Comments
 (0)