From d3184b19cbe920e0229cac95c27be30790a972b7 Mon Sep 17 00:00:00 2001 From: jayagl Date: Fri, 5 Dec 2014 13:36:02 +1100 Subject: [PATCH 1/5] Added minimal support for FTP with TLS/SSL Tested only over FTP with implicit TLS/SSL encryption. Operations tested - ls(), get(). --- lib/jsftp.js | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/jsftp.js b/lib/jsftp.js index c4e6c95..9bb0b91 100644 --- a/lib/jsftp.js +++ b/lib/jsftp.js @@ -10,6 +10,7 @@ 'use strict'; var Net = require('net'); +var tls = require('tls'); var EventEmitter = require('events').EventEmitter; var es = require('event-stream'); var ResponseParser = require('ftp-response-parser'); @@ -117,13 +118,25 @@ Ftp.prototype._createSocket = function(port, host, firstAction) { } this.authenticated = false; - var socket = Net.createConnection(port, host, firstAction || NOOP); - socket.on('connect', this.reemit('connect')); - socket.on('timeout', this.reemit('timeout')); + var self = this; + var socket; - this.pipeline = es.pipeline(socket, this.resParser); + if(this.ssl){ + socket = tls.connect(port,host,this.sslOptions,function(){ + //runt PROT command to specify that the data channel is secure + self.runCommand('prot p',function(){ + self.reemit('connect'); + }); + }); + } + else{ + socket = Net.createConnection(port, host, firstAction || NOOP); + socket.on('connect', this.reemit('connect')); + socket.on('timeout', this.reemit('timeout')); + } - var self = this; + + this.pipeline = es.pipeline(socket, this.resParser); this.pipeline.on('data', function(data) { self.emit('data', data); self.parseResponse.call(self, data); @@ -583,13 +596,22 @@ Ftp.prototype.pasvTimeout = function(socket, cb) { Ftp.prototype.getPasvSocket = function(callback) { var timeout = this.timeout; callback = once(callback || NOOP); + var self = this; this.execute('pasv', function(err, res) { if (err) return callback(err); getPasvPort(res.text, function(err, res) { if (err) return callback(err); - var socket = Net.createConnection(res.port, res.host); + var socket; + if(self.ssl){ + socket = tls.connect(res.port,res.host,self.sslOptions,function(){ + self.reemit('connect'); + }); + } + else{ + socket = Net.createConnection(res.port, res.host); + } socket.setTimeout(timeout || TIMEOUT); callback(null, socket); }); From 1b6ee550a630e2840e7e3c31deffe41d50e32f20 Mon Sep 17 00:00:00 2001 From: jayagl Date: Wed, 17 Dec 2014 19:49:31 +1100 Subject: [PATCH 2/5] Updated tests for TLS/SSL Updated the jsftp_test.js to run all the tests with SSL config after running them normally. Made some changes to deal with windows file systems. --- test/jsftp_test.js | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/test/jsftp_test.js b/test/jsftp_test.js index 6921393..6bba6aa 100755 --- a/test/jsftp_test.js +++ b/test/jsftp_test.js @@ -66,6 +66,8 @@ var FTPCredentials = { pass: "12345" }; +var createLocal = false; //Set to false if you are using your own local FTP server + function getRemotePath(path) { return Path.join('test', 'test_c9', path); } @@ -73,11 +75,11 @@ function getRemotePath(path) { function getLocalPath(path) { return Path.join(process.cwd(), 'test', 'test_c9', path); } -var CWD = process.cwd() + "/test"; +var CWD = Path.join(process.cwd(),"test"); var remoteCWD = "test/test_c9"; exec('mkdir', [__dirname + "/" + remoteCWD]); -describe("jsftp test suite", function() { +function testRoutine(){ var ftp, server; beforeEach(function(next) { rimraf(getLocalPath(''), function() { @@ -85,7 +87,7 @@ describe("jsftp test suite", function() { Fs.writeFileSync(getLocalPath('testfile.txt'), "test"); Fs.writeFileSync(getLocalPath('testfile2.txt'), "test2"); - if (FTPCredentials.host === "localhost") { + if (createLocal && FTPCredentials.host === "localhost") { server = new ftpServer(); server.init(FTPCredentials); } @@ -99,7 +101,7 @@ describe("jsftp test suite", function() { afterEach(function(next) { setTimeout(function() { - server.stop(); + server && server.stop(); if (ftp) { ftp.destroy(); ftp = null; @@ -362,7 +364,7 @@ describe("jsftp test suite", function() { ftp.ls(filePath, function(err, res) { assert.ok(!err); - assert.equal(buffer.length, Fs.statSync(CWD + "/jsftp_test.js").size); + assert.equal(buffer.length, Fs.statSync(Path.join(CWD,"jsftp_test.js")).size); ftp.raw.dele(filePath, function(err, data) { assert.ok(!err); @@ -406,7 +408,7 @@ describe("jsftp test suite", function() { ftp.ls(filePath, function(err, res) { assert.ok(!err); - assert.equal(res[0].size, Fs.statSync(CWD + "/jsftp_test.js").size); + assert.equal(res[0].size, Fs.statSync(Path.join(CWD,"jsftp_test.js")).size); ftp.raw.dele(filePath, function(err, data) { assert.ok(!err); @@ -443,7 +445,7 @@ describe("jsftp test suite", function() { }); it("test get a file", function(next) { - var localPath = CWD + '/test_c9/testfile.txt'; + var localPath = Path.join(CWD,"test_c9","testfile.txt"); var remotePath = remoteCWD + "/testfile.txt"; var realContents = Fs.readFileSync(localPath, "utf8"); var str = ""; @@ -795,4 +797,26 @@ describe("jsftp test suite", function() { onDone(); }); }); +} + +describe("jsftp test suite", function(){ + describe("without ssl", testRoutine); + describe("with ssl", function(){ + before(function(){ + //change ftp credentials to ssl + FTPCredentials = { + host: "localhost", + user: "user", + port: 990, + pass: "12345", + ssl:true, + //extra options added for testing locally without valid certs + sslOptions:{ + requestCert: false, + rejectUnauthorized: false + } + }; + }); + testRoutine(); + }); }); From 884e87d0ec4bd24a2db75b2135cb0671e114ef14 Mon Sep 17 00:00:00 2001 From: jayagl Date: Wed, 17 Dec 2014 19:58:14 +1100 Subject: [PATCH 3/5] Removed minor artefact from testing in jsftp_test.js --- test/jsftp_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jsftp_test.js b/test/jsftp_test.js index 6bba6aa..b645cf1 100755 --- a/test/jsftp_test.js +++ b/test/jsftp_test.js @@ -66,7 +66,7 @@ var FTPCredentials = { pass: "12345" }; -var createLocal = false; //Set to false if you are using your own local FTP server +var createLocal = true; //Set to false if you are using your own local FTP server function getRemotePath(path) { return Path.join('test', 'test_c9', path); From fcae5b66e3ddbbf4a919e479177d54247c9bacff Mon Sep 17 00:00:00 2001 From: jayagl Date: Thu, 18 Dec 2014 01:29:31 +1100 Subject: [PATCH 4/5] Minor code style edit --- lib/jsftp.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/jsftp.js b/lib/jsftp.js index 9bb0b91..cd51daa 100644 --- a/lib/jsftp.js +++ b/lib/jsftp.js @@ -121,11 +121,12 @@ Ftp.prototype._createSocket = function(port, host, firstAction) { var self = this; var socket; - if(this.ssl){ - socket = tls.connect(port,host,this.sslOptions,function(){ + if (this.ssl) { + socket = tls.connect(port, host, this.sslOptions, function() { //runt PROT command to specify that the data channel is secure - self.runCommand('prot p',function(){ + self.runCommand('prot p', function() { self.reemit('connect'); + socket.on('timeout',) }); }); } @@ -604,8 +605,8 @@ Ftp.prototype.getPasvSocket = function(callback) { if (err) return callback(err); var socket; - if(self.ssl){ - socket = tls.connect(res.port,res.host,self.sslOptions,function(){ + if (self.ssl) { + socket = tls.connect(res.port, res.host, self.sslOptions, function() { self.reemit('connect'); }); } From 2a82372ea6c3af8adf2ba2445797d4151dfc4632 Mon Sep 17 00:00:00 2001 From: jayagl Date: Thu, 18 Dec 2014 01:31:43 +1100 Subject: [PATCH 5/5] Added TODO for timeout on TLS --- lib/jsftp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jsftp.js b/lib/jsftp.js index cd51daa..d9ac8ea 100644 --- a/lib/jsftp.js +++ b/lib/jsftp.js @@ -126,7 +126,7 @@ Ftp.prototype._createSocket = function(port, host, firstAction) { //runt PROT command to specify that the data channel is secure self.runCommand('prot p', function() { self.reemit('connect'); - socket.on('timeout',) + //TODO timeout for tls connection? }); }); }