Skip to content

Commit 5d32be4

Browse files
mblebrianc
authored andcommitted
Handle SSL negotiation errors more robustly
This commit adds some finer grained detail to handling the postmaster's response to SSL negotiation packets, by accounting for the possibility of an 'E' byte being sent back, and emitting an appropriate error. In the naive case, the postmaster will respond with either 'S' (proceed with an SSL connection) or 'N' (SSL is not supported). However, the current if statement doesn't account for an 'E' byte being returned by the postmaster, where an error is encountered (perhaps unable to fork due to being out of memory). By adding this case, we can prevent confusing error messages when SSL is enforced and the postmaster returns an error after successful SSL connections. This also brings the connection handling further in line with libpq, where 'E' is handled similarly as of this commit: postgres/postgres@a49fbaa Given that there are no longer pre-7.0 databases out in the wild, I believe this is a safe change to make, and should not break backwards compatibility (unless matching on error message content). * Replace if statement with switch, to catch 'S', 'E' and 'N' bytes returned by the postmaster * Return an Error for non 'S' or 'N' cases * Expand and restructure unit tests for SSL negotiation packets
1 parent 0902d14 commit 5d32be4

File tree

2 files changed

+51
-20
lines changed

2 files changed

+51
-20
lines changed

lib/connection.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,13 @@ Connection.prototype.connect = function (port, host) {
8282

8383
this.stream.once('data', function (buffer) {
8484
var responseCode = buffer.toString('utf8')
85-
if (responseCode !== 'S') {
86-
return self.emit('error', new Error('The server does not support SSL connections'))
85+
switch (responseCode) {
86+
case 'N':
87+
return self.emit('error', new Error('The server does not support SSL connections'))
88+
case 'S':
89+
break
90+
default:
91+
return self.emit('error', new Error('There was an error establishing an SSL connection'))
8792
}
8893
var tls = require('tls')
8994
self.stream = tls.connect({

test/unit/connection/error-tests.js

+44-18
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,52 @@ suite.test('connection does not emit ECONNRESET errors during disconnect', funct
3737
done()
3838
})
3939

40+
var SSLNegotiationPacketTests = [
41+
{
42+
testName: 'connection does not emit ECONNRESET errors during disconnect also when using SSL',
43+
errorMessage: null,
44+
response: 'S',
45+
responseType: 'sslconnect'
46+
},
47+
{
48+
testName: 'connection emits an error when SSL is not supported',
49+
errorMessage: 'The server does not support SSL connections',
50+
response: 'N',
51+
responseType: 'error'
52+
},
53+
{
54+
testName: 'connection emits an error when postmaster responds to SSL negotiation packet',
55+
errorMessage: 'There was an error establishing an SSL connection',
56+
response: 'E',
57+
responseType: 'error'
58+
}
59+
]
4060

41-
suite.test('connection does not emit ECONNRESET errors during disconnect also when using SSL', function (done) {
42-
// our fake postgres server, which just responds with 'S' to start SSL
43-
var socket
44-
var server = net.createServer(function (c) {
45-
socket = c
46-
c.once('data', function (data) {
47-
c.write(Buffer.from('S'))
61+
for (var i = 0; i < SSLNegotiationPacketTests.length; i++) {
62+
var tc = SSLNegotiationPacketTests[i]
63+
suite.test(tc.testName, function (done) {
64+
// our fake postgres server
65+
var socket
66+
var server = net.createServer(function (c) {
67+
socket = c
68+
c.once('data', function (data) {
69+
c.write(Buffer.from(tc.response))
70+
})
4871
})
49-
})
5072

51-
server.listen(7778, function () {
52-
var con = new Connection({ssl: true})
53-
con.connect(7778, 'localhost')
54-
assert.emits(con, 'sslconnect', function () {
55-
con.end()
56-
socket.destroy()
57-
server.close()
58-
done()
73+
server.listen(7778, function () {
74+
var con = new Connection({ssl: true})
75+
con.connect(7778, 'localhost')
76+
assert.emits(con, tc.responseType, function (err) {
77+
if (err) {
78+
assert.equal(err.message, tc.errorMessage)
79+
}
80+
con.end()
81+
socket.destroy()
82+
server.close()
83+
done()
84+
})
85+
con.requestSsl()
5986
})
60-
con.requestSsl()
6187
})
62-
})
88+
}

0 commit comments

Comments
 (0)