Skip to content

Commit b93a834

Browse files
authored
fix(#3817): send servername for SNI on TLS (#3821)
* fix(#3817): send servername for SNI on TLS * fix: set host header to servername * refactor: attach regardless
1 parent 08065c0 commit b93a834

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed

lib/interceptor/dns.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,15 @@ module.exports = interceptorOpts => {
352352
return handler.onError(err)
353353
}
354354

355-
const dispatchOpts = {
355+
let dispatchOpts = null
356+
dispatchOpts = {
356357
...origDispatchOpts,
357-
origin: newOrigin
358+
servername: origin.hostname, // For SNI on TLS
359+
origin: newOrigin,
360+
headers: {
361+
host: origin.hostname,
362+
...origDispatchOpts.headers
363+
}
358364
}
359365

360366
dispatch(

test/interceptors/dns.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ const { test, after } = require('node:test')
44
const { isIP } = require('node:net')
55
const { lookup } = require('node:dns')
66
const { createServer } = require('node:http')
7+
const { createServer: createSecureServer } = require('node:https')
78
const { once } = require('node:events')
89
const { setTimeout: sleep } = require('node:timers/promises')
910

1011
const { tspl } = require('@matteo.collina/tspl')
12+
const pem = require('https-pem')
1113

1214
const { interceptors, Agent } = require('../..')
1315
const { dns } = interceptors
@@ -108,6 +110,93 @@ test('Should automatically resolve IPs (dual stack)', async t => {
108110
t.equal(await response2.body.text(), 'hello world!')
109111
})
110112

113+
test('Should respect DNS origin hostname for SNI on TLS', async t => {
114+
t = tspl(t, { plan: 12 })
115+
116+
const hostsnames = []
117+
const server = createSecureServer(pem)
118+
const requestOptions = {
119+
method: 'GET',
120+
path: '/',
121+
headers: {
122+
'content-type': 'application/json'
123+
}
124+
}
125+
126+
server.on('request', (req, res) => {
127+
t.equal(req.headers.host, 'localhost')
128+
res.writeHead(200, { 'content-type': 'text/plain' })
129+
res.end('hello world!')
130+
})
131+
132+
server.listen(0)
133+
134+
await once(server, 'listening')
135+
136+
const client = new Agent({
137+
connect: {
138+
rejectUnauthorized: false
139+
}
140+
}).compose([
141+
dispatch => {
142+
return (opts, handler) => {
143+
const url = new URL(opts.origin)
144+
145+
t.equal(hostsnames.includes(url.hostname), false)
146+
t.equal(opts.servername, 'localhost')
147+
148+
if (url.hostname[0] === '[') {
149+
// [::1] -> ::1
150+
t.equal(isIP(url.hostname.slice(1, 4)), 6)
151+
} else {
152+
t.equal(isIP(url.hostname), 4)
153+
}
154+
155+
hostsnames.push(url.hostname)
156+
157+
return dispatch(opts, handler)
158+
}
159+
},
160+
dns({
161+
lookup: (_origin, _opts, cb) => {
162+
cb(null, [
163+
{
164+
address: '::1',
165+
family: 6
166+
},
167+
{
168+
address: '127.0.0.1',
169+
family: 4
170+
}
171+
])
172+
}
173+
})
174+
])
175+
176+
after(async () => {
177+
await client.close()
178+
server.close()
179+
180+
await once(server, 'close')
181+
})
182+
183+
const response = await client.request({
184+
...requestOptions,
185+
origin: `https://localhost:${server.address().port}`
186+
})
187+
188+
t.equal(response.statusCode, 200)
189+
t.equal(await response.body.text(), 'hello world!')
190+
191+
const response2 = await client.request({
192+
...requestOptions,
193+
origin: `https://localhost:${server.address().port}`
194+
})
195+
196+
t.equal(response2.statusCode, 200)
197+
t.equal(await response2.body.text(), 'hello world!')
198+
})
199+
111200
test('Should recover on network errors (dual stack - 4)', async t => {
112201
t = tspl(t, { plan: 8 })
113202

0 commit comments

Comments
 (0)