-
Notifications
You must be signed in to change notification settings - Fork 30.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
http: replace destroySoon with socket.end #36205
Open
mmomtchev
wants to merge
9
commits into
nodejs:main
Choose a base branch
from
mmomtchev:halfclose-rst
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
dd2e3ed
net: require two events to destroy a socket
mmomtchev e6699c6
net: replace destroySoon with socket.end()
mmomtchev 8f0e26c
net: close http connections with stream.end()
mmomtchev f780542
http: lint the PR
mmomtchev 3e1ba08
net: add a unit test for res.end
mmomtchev 1559f5c
http: replace destroySoon with socket.end
mmomtchev a1b606c
tls: support active closing for TLS sockets
mmomtchev 6038c95
remove useless change
mmomtchev 0dfd1f2
tls: use Writable.end (@ronag)
mmomtchev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
'use strict'; | ||
const common = require('../common'); | ||
|
||
if (!common.hasCrypto) | ||
common.skip('missing crypto'); | ||
|
||
const https = require('https'); | ||
const tls = require('tls'); | ||
const fixtures = require('../common/fixtures'); | ||
|
||
const opt = { | ||
key: fixtures.readKey('agent1-key.pem'), | ||
cert: fixtures.readKey('agent1-cert.pem') | ||
}; | ||
|
||
const data = 'hello'; | ||
const server = https.createServer(opt, common.mustCallAtLeast((req, res) => { | ||
res.setHeader('content-length', data.length); | ||
res.end(data); | ||
}, 2)); | ||
|
||
server.listen(0, function() { | ||
const options = { | ||
host: '127.0.0.1', | ||
port: server.address().port, | ||
rejectUnauthorized: false, | ||
ALPNProtocols: ['http/1.1'], | ||
allowHalfOpen: true | ||
}; | ||
const socket = tls.connect(options, common.mustCall(() => { | ||
socket.write('GET /\n\n'); | ||
socket.once('data', common.mustCall(() => { | ||
socket.write('GET /\n\n'); | ||
setTimeout(common.mustCall(() => { | ||
socket.destroy(); | ||
server.close(); | ||
}), common.platformTimeout(10)); | ||
})); | ||
})); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a timeout for
'close'
here? @lpincaThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you want a timeout here. The underlying socket will already have a timeout (from the time the last packet was received). And if it's slowly trickling in data, any hard timeout could break the connection prematurely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this allow a Slowloris like attack? To clarify, I think it would make sense to have a timeout on
tlsSocket.destroySoon()
if it is changed to be an alias fortlsSocket.end()
, not ontlsSocket.end()
itself that should work likenetSocket.end()
for feature parity.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that seems quite possible. As I mentioned elsewhere, I'm opposed to the general implementation in this PR. At a minimum, it needs to respect the allowHalfOpen / autoDestroy flags. But it also needs to be implemented at the point in the TLS code that is generating this event, and not by disregarding the explicit call to destroy or close it.
Additionally, I expect that, in principle, as soon as this function returns, a sufficiently smart garbage collector could observe that there are no remaining useful references to this TLS socket (we were just requested to destroy the socket for reading and writing, so there's nothing that should generate a new callback event), and thus the underlying socket should be expected to be finalized and then immediately destroyed as soon as the call to 'end' returns. And thus still causing the RST packet this PR was supposedly going to prevent. It's probably not hard to fool the garbage collector into keeping the memory reference live (though who is ever going to clean up this socket in that case?), but I worry that this PR may thus just create new problems, without actually addressing existing ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normally, all three major OSes have a built-in default timeout for sockets in
FIN_WAIT_2
state (firstFIN
sent and thenACK
from remote) - it is usually about 60-120 seconds.For sockets in
FIN_WAIT_1
state (firstFIN
sent, waiting for firstACK
), the much longer default TCP timeout applies - that would be 300s by default on Linux.As for the Slowloris attack, I don't see how this change could help an attacker? A Slowloris attack exploits the timeouts by always sending just enough data to keep them from triggering. The most advantageous timeout for this would still be the default TCP
ACK
timeout - be it by withholding theACK
on adata
packet or aFIN
packet. In fact,FIN
would be a little bit less practical, because you can delay it only once - the connection will be closed afterwards.As for the event that triggers (or not) the
RST
, it is not the destruction of the socket object by the garbage collector, it is the explicit calling of the kernelclose()
syscall by libuvuv_tcp_close()
. If any remote data is received after this call, aRST
is sent back.