From e3196c12871d354cec317e9987e3af5d0d12176c Mon Sep 17 00:00:00 2001 From: Sven Date: Thu, 17 Mar 2022 22:09:06 +0100 Subject: [PATCH] add timeout to data sockets --- lib/jsftpd.js | 16 ++++++++++++++++ test/LIST.test.js | 2 +- test/PASV.test.js | 2 +- test/STOR.test.js | 40 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lib/jsftpd.js b/lib/jsftpd.js index 9464767..1b93f44 100644 --- a/lib/jsftpd.js +++ b/lib/jsftpd.js @@ -959,6 +959,11 @@ class ftpd { if (isSecure === true && protection === true) { dataChannel = tls.Server(main._opt.tls) dataChannel.on('secureConnection', (pasvSocket) => { + pasvSocket.on('error', main.ErrorHandler) + pasvSocket.setTimeout(5000) + pasvSocket.on('timeout', () => { + pasvSocket.destroy() + }) main.DebugHandler(`${connectionInfo} data connection established`) dataObj.dataSocket = pasvSocket if (dataObj.method) { @@ -968,6 +973,12 @@ class ftpd { } else { dataChannel = net.Server() dataChannel.on('connection', (pasvSocket) => { + pasvSocket.on('error', main.ErrorHandler) + pasvSocket.setTimeout(5000) + pasvSocket.on('timeout', () => { + pasvSocket.destroy() + }) + pasvSocket.on('close', () => main.DebugHandler(`${connectionInfo} data connection has been closed`)) if (isSecure === true && protection === true) { pasvSocket = new tls.TLSSocket(pasvSocket, { isServer: true, secureContext: tls.createSecureContext(main._opt.tls) }) pasvSocket.on('secure', () => { @@ -1004,11 +1015,16 @@ class ftpd { dataObj.dataSocket = activeSocket dataObj.method(dataObj) }) + activeSocket.on('error', main.ErrorHandler) } else { obj.dataSocket = client obj.method(obj) } }) + client.setTimeout(5000) + client.on('timeout', () => { + client.destroy() + }) client.on('error', main.ErrorHandler) } else { obj.method(obj) diff --git a/test/LIST.test.js b/test/LIST.test.js index 16df1df..70abbea 100644 --- a/test/LIST.test.js +++ b/test/LIST.test.js @@ -35,7 +35,7 @@ test('test LIST message', async () => { let promiseSocket = new PromiseSocket(new net.Socket()) let socket = promiseSocket.stream - await socket.connect(cmdPortTCP, 'localhost') + await socket.connect(cmdPortTCP, '127.0.0.1', 'localhost') content = await promiseSocket.read() expect(content.toString().trim()).toBe('220 Welcome') diff --git a/test/PASV.test.js b/test/PASV.test.js index 992a94c..d73f601 100644 --- a/test/PASV.test.js +++ b/test/PASV.test.js @@ -40,7 +40,7 @@ test('test PASV message takes next free port', async () => { let promiseSocket = new PromiseSocket(new net.Socket()) let socket = promiseSocket.stream - await socket.connect(cmdPortTCP, 'localhost') + await socket.connect(cmdPortTCP, '127.0.0.1', 'localhost') content = await promiseSocket.read() expect(content.toString().trim()).toBe('220 Welcome') diff --git a/test/STOR.test.js b/test/STOR.test.js index 72fff6b..61d7325 100644 --- a/test/STOR.test.js +++ b/test/STOR.test.js @@ -4,7 +4,7 @@ const tls = require('tls') const {PromiseSocket, TimeoutError} = require('promise-socket') const { sleep, getCmdPortTCP, getDataPort } = require('./utils') -jest.setTimeout(5000) +jest.setTimeout(7500) let server, content, dataContent = null const cmdPortTCP = getCmdPortTCP() const dataPort = getDataPort() @@ -128,6 +128,44 @@ test('test STOR message', async () => { await promiseSocket.end() }) +test('test STOR message failes due to socket timeout', async () => { + const users = [ + { + username: 'john', + allowLoginWithoutPassword: true, + } + ] + server = new ftpd({cnf: {port: 50021, user: users, minDataPort: dataPort}}) + expect(server).toBeInstanceOf(ftpd) + server.start() + + let promiseSocket = new PromiseSocket(new net.Socket()) + let socket = promiseSocket.stream + await socket.connect(50021, 'localhost') + content = await promiseSocket.read() + expect(content.toString().trim()).toBe('220 Welcome') + + await promiseSocket.write('USER john') + content = await promiseSocket.read() + expect(content.toString().trim()).toBe('232 User logged in') + + await promiseSocket.write('STOR ../../mytestfile') + content = await promiseSocket.read() + expect(content.toString().trim()).toBe('550 Transfer failed "../../mytestfile"') + + await promiseSocket.write('EPSV') + content = await promiseSocket.read() + expect(content.toString().trim()).toBe(`229 Entering extended passive mode (|||${dataPort}|)`) + + let promiseDataSocket = new PromiseSocket(new net.Socket()) + let dataSocket = promiseDataSocket.stream + await dataSocket.connect(dataPort, 'localhost') + + await sleep(5500) + expect(dataSocket.destroyed).toBe(true) + + await promiseSocket.end() +}) test('test STOR message with ASCII', async () => { const users = [