Skip to content

Commit 8e7ac25

Browse files
mattiasholmlundMylesBorins
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: #14772Fixes: #14736 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 9509820 commit 8e7ac25

4 files changed

Lines changed: 84 additions & 16 deletions

File tree

‎lib/_http_agent.js‎

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,8 @@ Agent.prototype.addRequest = function(req, options) {
124124
options=util._extend({},options);
125125
options=util._extend(options,this.options);
126126

127-
if(!options.servername){
128-
options.servername=options.host;
129-
consthostHeader=req.getHeader('host');
130-
if(hostHeader){
131-
options.servername=hostHeader.replace(/:.*$/,'');
132-
}
133-
}
127+
if(!options.servername)
128+
options.servername=calculateServerName(options,req);
134129

135130
varname=this.getName(options);
136131
if(!this.sockets[name]){
@@ -178,13 +173,8 @@ Agent.prototype.createSocket = function(req, options, cb) {
178173
options=util._extend({},options);
179174
options=util._extend(options,self.options);
180175

181-
if(!options.servername){
182-
options.servername=options.host;
183-
consthostHeader=req.getHeader('host');
184-
if(hostHeader){
185-
options.servername=hostHeader.replace(/:.*$/,'');
186-
}
187-
}
176+
if(!options.servername)
177+
options.servername=calculateServerName(options,req);
188178

189179
varname=self.getName(options);
190180
options._agentKey=name;
@@ -211,6 +201,29 @@ Agent.prototype.createSocket = function(req, options, cb) {
211201
}
212202
};
213203

204+
functioncalculateServerName(options,req){
205+
letservername=options.host;
206+
consthostHeader=req.getHeader('host');
207+
if(hostHeader){
208+
// abc => abc
209+
// abc:123 => abc
210+
// [::1] => ::1
211+
// [::1]:123 => ::1
212+
if(hostHeader.startsWith('[')){
213+
constindex=hostHeader.indexOf(']');
214+
if(index===-1){
215+
// Leading '[', but no ']'. Need to do something...
216+
servername=hostHeader;
217+
}else{
218+
servername=hostHeader.substr(1,index-1);
219+
}
220+
}else{
221+
servername=hostHeader.split(':',1)[0];
222+
}
223+
}
224+
returnservername;
225+
}
226+
214227
functioninstallListeners(agent,s,options){
215228
functiononFree(){
216229
debug('CLIENT socket onFree');

‎lib/tls.js‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const net = require('net');
77
consturl=require('url');
88
constbinding=process.binding('crypto');
99
constBuffer=require('buffer').Buffer;
10+
constcanonicalizeIP=process.binding('cares_wrap').canonicalizeIP;
1011

1112
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
1213
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
@@ -156,7 +157,7 @@ exports.checkServerIdentity = function checkServerIdentity(host, cert) {
156157
consturi=url.parse(name.slice(4));
157158
uriNames.push(uri.hostname);// TODO(bnoordhuis) Also use scheme.
158159
}elseif(name.startsWith('IP Address:')){
159-
ips.push(name.slice(11));
160+
ips.push(canonicalizeIP(name.slice(11)));
160161
}
161162
}
162163
}
@@ -165,7 +166,7 @@ exports.checkServerIdentity = function checkServerIdentity(host, cert) {
165166
letreason='Unknown reason';
166167

167168
if(net.isIP(host)){
168-
valid=ips.includes(host);
169+
valid=ips.includes(canonicalizeIP(host));
169170
if(!valid)
170171
reason=`IP: ${host} is not in the cert's list: ${ips.join(', ')}`;
171172
// TODO(bnoordhuis) Also check URI SANs that are IP addresses.

‎src/cares_wrap.cc‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,28 @@ static void IsIPv6(const FunctionCallbackInfo<Value>& args) {
12811281
}
12821282
}
12831283

1284+
1285+
voidCanonicalizeIP(const FunctionCallbackInfo<Value>& args) {
1286+
v8::Isolate* isolate = args.GetIsolate();
1287+
node::Utf8Value ip(isolate, args[0]);
1288+
char address_buffer[sizeof(structin6_addr)];
1289+
char canonical_ip[INET6_ADDRSTRLEN];
1290+
1291+
int af;
1292+
if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0)
1293+
af = AF_INET;
1294+
elseif (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0)
1295+
af = AF_INET6;
1296+
else
1297+
return;
1298+
1299+
int err = uv_inet_ntop(af, address_buffer, canonical_ip,
1300+
sizeof(canonical_ip));
1301+
CHECK_EQ(err, 0);
1302+
1303+
args.GetReturnValue().Set(String::NewFromUtf8(isolate, canonical_ip));
1304+
}
1305+
12841306
staticvoidGetAddrInfo(const FunctionCallbackInfo<Value>& args) {
12851307
Environment* env = Environment::GetCurrent(args);
12861308

@@ -1514,6 +1536,7 @@ static void Initialize(Local<Object> target,
15141536
env->SetMethod(target, "isIP", IsIP);
15151537
env->SetMethod(target, "isIPv4", IsIPv4);
15161538
env->SetMethod(target, "isIPv6", IsIPv6);
1539+
env->SetMethod(target, "canonicalizeIP", CanonicalizeIP);
15171540

15181541
env->SetMethod(target, "strerror", StrError);
15191542
env->SetMethod(target, "getServers", GetServers);
Lines changed: 31 additions & 0 deletions
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+
constassert=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)