sockets - Active FTP Client for Node.js -
i'm trying hand @ writing ftp client against filezilla supports active mode using node.js. i'm new ftp , node.js. thought understanding of tcp socket communication , ftp protocol doing exercise. also, node-ftp jsftp don't seem support active mode, think nice (though used) addition npm.
i've got proof of concept code works @ least sometimes, not time. in case works, client uploads file called file.txt
text 'hi'. when works, this:
220-filezilla server version 0.9.41 beta 220-written tim kosse (tim.kosse@gmx.de) 220 please visit http://sourceforge.net/projects/filezilla/ 331 password required testuser 230 logged on listening 200 port command successful 150 opening data channel file transfer. server close 226 transfer ok half closed closed process finished exit code 0
when doesn't work, this:
220-filezilla server version 0.9.41 beta 220-written tim kosse (tim.kosse@gmx.de) 220 please visit http://sourceforge.net/projects/filezilla/ 331 password required testuser 230 logged on listening 200 port command successful 150 opening data channel file transfer. server close half closed closed process finished exit code 0
so, i'm not getting 226, , i'm not sure why i'm getting inconsistent results.
forgive poorly written code. i'll refactor once i'm confident understand how should work.:
var net = require('net'), socket = net.socket; var cmdsocket = new socket(); cmdsocket.setencoding('binary') var server = undefined; var port = 21; var host = "localhost"; var user = "testuser"; var password = "password1*" var active = true; var supplyuser = true; var supplypassword = true; var supplypassive = true; var waitingforcommand = true; var sendfile = true; function onconnect(){ } var str=""; function ondata(chunk) { console.log(chunk.tostring('binary')); //if ftp server return code = 220 if(supplyuser){ supplyuser = false; _send('user ' + user, function(){ }); }else if(supplypassword){ supplypassword = false; _send('pass ' + password, function(){ }); } else if(supplypassive){ supplypassive = false; if(active){ server = net.createserver(function(socket){ console.log('new connection'); socket.setkeepalive(true, 5000); socket.write('hi', function(){ console.log('write done'); }) socket.on('connect', function(){ console.log('socket connect'); }); socket.on('data', function(d){ console.log('socket data: ' + d); }); socket.on('error', function(err){ console.log('socket error: ' + err); }); socket.on('end', function() { console.log('socket end'); }); socket.on('drain', function(){ console.log('socket drain'); }); socket.on('timeout', function(){ console.log('socket timeout'); }); socket.on('close', function(){ console.log('socket close'); }); }); server.on('error', function(e){ console.log(e); }); server.on('close', function(){ console.log('server close'); }); server.listen(function(){ console.log('listening'); var address = server.address(); var port = address.port; var p1 = math.floor(port/256); var p2 = port % 256; _sendcommand('port 127,0,0,1,' + p1 + ',' + p2, function(){ }); }); }else{ _send('pasv', function(){ }); } } else if(sendfile){ sendfile = false; _send('stor file.txt', function(){ }); } else if(waitingforcommand){ waitingforcommand = false; cmdsocket.end(null, function(){ }); if(server)server.close(function(){}); } } function onend() { console.log('half closed'); } function onclose(){ console.log('closed'); } cmdsocket.once('connect', onconnect); cmdsocket.on('data', ondata); cmdsocket.on('end', onend); cmdsocket.on('close', onclose); cmdsocket.connect(port, host); function _send(cmd, callback){ cmdsocket.write(cmd + '\r\n', 'binary', callback); }
also, server
appropriate, or should other way?
edit: changed callback in server.listen use random port. has removed 425 getting previously. however, still not getting consistent behavior file transfer.
the flow active mode ftp transfers goes this:
- connection preamble (
user
/pass
) - establish client local socket data
- inform server of socket (
port
) - tell server open remote file writing (
stor
) - start writing data data socket established above (
socket.write()
) - close stream client side (
socket.end()
) end file transfer - tell server done (
quit
) - clean open sockets , servers on client
so once you've done this:
else if(sendfile){ sendfile = false; _send('stor file.txt', function(){ }); }
the server respond 150
saying has connected data socket established , ready receive data.
one improvement make easier reason execution @ point change control flow operate on parsed response code rather pre-defined bools.
function ondata(chunk) { console.log(chunk); var code = chunk.substring(0,3); if(code == '220'){
instead of:
function ondata(chunk) { console.log(chunk.tostring('binary')); //if ftp server return code = 220 if(supplyuser){
then can add section sending data:
//ready data else if (code == '150') { datasocket.write('some wonderful file contents\r\n', function(){}); datasocket.end(null, function(){}); }
and little more clean up:
//transfer finished else if ( code == '226') { _send('quit', function(){ console.log("saying goodbye");}); } //session end else if ( code == '221') { cmdsocket.end(null, function(){}); if(!!server){ server.close(); } }
obviously things more complicated if sending multiple files etc, should proof of concept running more reliably:
var net = require('net'); socket = net.socket; var cmdsocket = new socket(); cmdsocket.setencoding('binary') var server = undefined; var datasocket = undefined; var port = 21; var host = "localhost"; var user = "username"; var password = "password" var active = true; function onconnect(){ } var str=""; function ondata(chunk) { console.log(chunk.tostring('binary')); var code = chunk.substring(0,3); //if ftp server return code = 220 if(code == '220'){ _send('user ' + user, function(){ }); }else if(code == '331'){ _send('pass ' + password, function(){ }); } else if(code == '230'){ if(active){ server = net.createserver(function(socket){ datasocket = socket; console.log('new connection'); socket.setkeepalive(true, 5000); socket.on('connect', function(){ console.log('socket connect'); }); socket.on('data', function(d){ console.log('socket data: ' + d); }); socket.on('error', function(err){ console.log('socket error: ' + err); }); socket.on('end', function() { console.log('socket end'); }); socket.on('drain', function(){ console.log('socket drain'); }); socket.on('timeout', function(){ console.log('socket timeout'); }); socket.on('close', function(){ console.log('socket close'); }); }); server.on('error', function(e){ console.log(e); }); server.on('close', function(){ console.log('server close'); }); server.listen(function(){ console.log('listening'); var address = server.address(); var port = address.port; var p1 = math.floor(port/256); var p2 = port % 256; _send('port 127,0,0,1,' + p1 + ',' + p2, function(){ }); }); }else{ _send('pasv', function(){ }); } } else if(code == '200'){ _send('stor file.txt', function(){ }); } //ready data else if (code == '150') { datasocket.write('some wonderful file contents\r\n', function(){}); datasocket.end(null, function(){}); } //transfer finished else if ( code == '226') { _send('quit', function(){ console.log("saying goodbye");}); } //session end else if ( code == '221') { cmdsocket.end(null, function(){}); if(!!server){ server.close(); } } } function onend() { console.log('half closed'); } function onclose(){ console.log('closed'); } cmdsocket.once('connect', onconnect); cmdsocket.on('data', ondata); cmdsocket.on('end', onend); cmdsocket.on('close', onclose); cmdsocket.connect(port, host); function _send(cmd, callback){ cmdsocket.write(cmd + '\r\n', 'binary', callback); }
Comments
Post a Comment