Force lftp to open binary connections? - bash

The problem: I am trying to create a local mirror of a public FTP site. When I use lftp to do the job it creates a mirror without a problem, but when I try to update the mirror a few days later it becomes very slow due to getting stuck on several files.
Running lftp -d I can see that lftp makes several requests to RETR the file, but these requests result in several **** Timeout - reconnecting messages and after about 2-3 minutes I see
<--- 150 Opening BINARY mode data connection for {filename removed}.`
After this last command the file successfully downloads and lftp proceeds further.
From the manual I understand that BINARY is the default mode for lftp, but somehow it doesn't seem to work for the early requests. Can someone suggest how I can force lftp to always open BINARY mode data connection to download all files?
Here's a MWE:
``lftp -d -u anonymous,anonymous -c "open {url}; get {file}"``
And response from lftp -d:
---- Connecting to {url} ({IP}) port 21
<--- 220 (vsFTPd 3.0.3)
---> FEAT
<--- 211-Features:
<--- EPRT
<--- EPSV
<--- MDTM
<--- PASV
<--- REST STREAM
<--- SIZE
<--- TVFS
<--- 211 End
---> USER anonymous
<--- 331 Please specify the password.
---> PASS anonymous
<--- 230 Login successful.
---> TYPE I
<--- 200 Switching to Binary mode.
---> SIZE {file}
<--- 213 3321
---> MDTM {file}
--- 213 20160318190446
---> PASV
<--- 227 Entering Passive Mode ({IP}).
---- Connecting data socket to ({IP}) port 55380
---- Data connection established
---> RETR {file}
**** Timeout - reconnecting
---- Closing data socket
---- Closing control socket

lftp uses binary mode by default for all file transfers and ascii mode for directory listings. So the binary mode should not be a problem here.
Maybe you have a subtle connectivity problem, sometimes setting net:socket-maxseg to a lower value (e.g. 500) helps.

The ftp command for binary mode is bin so use that command before you get the file.
cd /direc/tory; bin; get file.xml
The problem is that I see
---> TYPE I
<--- 200 Switching to Binary mode.
in your output, so you're already in binary mode. I wonder if you have a different problem? I also see that you're using passive mode (PASV), and that's good because passive works around firewalls and NATs [1], so we need another reason why you see those timeouts.
Do you have any other clues, maybe from ping or netstat?

Related

lftp handling of negative response to ALLO

I'm using lftp 4.8.4 on Ubuntu 20.4 with my own ftpd server code.
As the debug log below shows, in preparation for a transfer lftp sends ALLO but appears to ignore the negative response (452 "no space").
I've also tried a 552 "Exceeded storage allocation" response, which was also ignored.
Is there a more appropriate response? Am I misunderstanding the role of ALLO?
Any and all suggestions welcome! Thanks.
> put lfs.img -o lfs3
---- Connecting to ftpd (192.168.0.171) port 21
<--- 220 nil
---> FEAT
<--- 211-Extensions supported:
<--- SIZE
<--- REST STREAM
<--- PASV
<--- 211 END
---> USER anonymous
<--- 331 nil
---> PASS lftp#
<--- 230 nil
---> PWD
<--- 257 /
---> TYPE I
<--- 200 nil
---> PASV
<--- 227 Entering Passive Mode. 192,168,0,171,255,249
---- Connecting data socket to (192.168.0.171) port 65529
---- Data connection established
---> ALLO 72408
<--- 452 no space
---> STOR lfs3
<--- 500 open fail
---- Closing data socket
put: lfs.img: Fatal error: 500 open fail
lftp correctly handles a response of 552 ("Requested file action aborted. Exceeded storage allocation") to an ALLO command. lftp responds to the 552 response with a STOR command, immediately followed by an ABOR (ie. Abort) command.
From RFC 959 (para 4.1.3.): "This command [ALLO] shall be followed by a STORe or APPEnd command." So although the STOR command will not succeed, it would be incorrect to skip it and just send ABOR.

Lftp 550 error when trying to mirror a folder. FileZilla is ok

I made this script to download a folder in a remote FTP with implicit SSL:
open -d ftps://USER:"PASS"#ftp.xxx.tld:990
mirror --verbose --continue /remote/folder $HOME/destination/folder;
bye
It connects, the features are:
<--- 220-Microsoft FTP Service
<--- 220 FTP SOMECOMPANY
---> FEAT
<--- 211-Extended features supported:
<--- LANG EN*
<--- UTF8
<--- AUTH TLS;TLS-C;SSL;TLS-P;
<--- PBSZ
<--- PROT C;P;
<--- CCC
<--- HOST
<--- SIZE
<--- MDTM
<--- REST STREAM
<--- 211 END
But when the mirror command tries to do its thing, it gets:
<--- 257 "/" is current directory.
---> PBSZ 0
---- CWD path to be sent is `/remote/folder'
<--- 200 PBSZ command successful.
---> CWD remote
<--- 550 Access is denied.
mirror: Access failed: 550 Access is denied. (/remote/folder)
---> CWD folder
<--- 550 The system cannot find the file specified.
---> QUIT
When I manually connect with lftp I do have ls access for example but cannot change directory, same error, 550.
On FileZila it works perfectly, I do have access to the folder and can download files. I'm puzzled here. Already tried cryptic things such as using:
lftp -e "set ftp:use-allo false; set ftp:passive-mode true; set ftp:prefer-epsv false;...
to no avail. Any tips? Thanks!
You probably have to force TVFS here by using “set ftp:use-tvfs yes”. It enables Unix-like paths even if the server forgets to announce their support.
As the ftp server is hosted in a Windows envirionment, I had to use \ instead of / for defining paths.

lftp 550 permission denied

i've tried to create a script that will upload some file to a ftp server using lftp, but without any luck so far. If I used build in ftp command in debian I manage to succsessfully connected and put the file.
Here is the debug output from lftp command:
lftp xxx.xxx.xxx.xxxx -e "put -O /out/ some_file_name" -d
---- using user `user01' and password from ~/.netrc
---- Resolving host address...
---- 1 address found:xxx.xxx.xxx.xxxx
---- Connecting to xxx.xxx.xxx.xxxx (xxx.xxx.xxx.xxxx) port 21
<--- 220 (vsFTPd 2.0.7)
---> FEAT
<--- 211-Features:
<--- EPRT
<--- EPSV
<--- MDTM
<--- PASV
<--- REST STREAM
<--- SIZE
<--- TVFS
<--- UTF8
<--- 211 End
---> OPTS UTF8 ON
<--- 200 Always in UTF8 mode.
---> USER user01
<--- 331 Please specify the password.
---> PASS XXXX
<--- 230 Login successful.
---> PWD
<--- 257 "/"
---> TYPE I
<--- 200 Switching to Binary mode.
---> EPSV
<--- 550 Permission denied.
---- Switching passive mode off
---- Closing data socket
---- Closing control socket
As you can see I'm using stored user name and password from .netrc file. I have another script that connect to the same server but uploads files and rename them inside the remote ftp folder using lftp again. Can someone help and explain why I cannot put with lfpt but can do it using ftp.
after add set ftp:passive-mode true and set ftp:prefer-epsv false to /etc/lftp.conf the error is changed
<--- 230 Login successful.
---> PWD
<--- 257 "/"
---> TYPE I
<--- 200 Switching to Binary mode.
---> PASV
<--- 227 Entering Passive Mode (xxx.xxx.xxx.xxx,76,92)
---- Connecting data socket to (xxx.xxx.xxx.xxx) port 19548
---- Data connection established
---> ALLO 710
<--- 550 Permission denied.
---> STOR out/my_file_name
---> ABOR
put: Access failed: 550 Permission denied. (/out/my_file_name)
---- Closing aborted data socket
---- Closing control socket
Okay I've understand what ALLO means
The ALLO command may be sent to a server that requires the necessary space for an uploaded to be reserved before the transfer takes place
so after a quick search in ftp man page, I've found a command to shut it down. After adding set ftp:use-allo false and with epsv false everything is fine now. Thanks alot :)
lftp -e "set ftp:use-allo false; set ftp:passive-mode true; set ftp:prefer-epsv false; mirror -R {local dir} {remote dir}" -u {username},{password} {host}
Use this single command to sync your file from local to server without 550 permission error.
While 550 Permission denied. is a strange response to the EPSV command it means that the server or some middlebox in between does not understand the EPSV command (likely a middlebox since the response to FEAT shows EPSV as supported). If you use the builtin ftp command instead of lftp it will probably use the older PASV command (IPv4 only) instead of the newer EPSV command (IPv4+IPv6 capable).
According to the man page there is a setting ftp:prefer-epsv which should default to false. Maybe some configuration is setting this value to true so that lftp will use EPSV instead of PASV. Check your settings (set -a inside lftp) and if it is true (expected) set it to false and try again, in the hope that it will then use PASV instead of EPSV.

FTP Client Output Response standard

After successful FTP file transfer, the the response is used to be "226 File send OK", but suddenly, it has changed to be "226 Transfer complete"
I have below questions:
Does FTP response code has any standard?
Can we customize FTP output response for a specific status code?
Find the standard FTP response for file transfer
$ ftp canopus
Connected to canopus.austin.century.com.
220 canopus.austin.century.com FTP server (Version 4.1 Sat Nov 23 12:52:09 CST 1991) ready.
Name (canopus:eric): dee
331 Password required for dee.
Password:
230 User dee logged in.
ftp> pwd
257 "/home/dee" is current directory.
ftp> cd desktop
250 CWD command successful.
ftp> type ascii
200 Type set to A.
ftp> send typescript
200 PORT command successful.
150 Opening data connection for typescript (128.114.4.99,1412).
226 File send OK.
ftp> cdup
250 CWD command successful.
ftp> bye
221 Goodbye.
Note: suddenly the response text 226 File send OK has changed to 226 Transfer complete
Find the details about FTP responses on wikipedia
RFC 959, 4.2. FTP REPLIES:
An FTP reply consists of a three digit number (transmitted as
three alphanumeric characters) followed by some text. The number
is intended for use by automata to determine what state to enter
next; the text is intended for the human user. It is intended
that the three digits contain enough encoded information that the
user-process (the User-PI) will not need to examine the text and
may either discard it or pass it on to the user, as appropriate.
In particular, the text may be server-dependent, so there are
likely to be varying texts for each reply code.

LFTP active mode with servers that do not recognize the PORT command

I am using LFTP to transfer files from a server, which unfortunately does not recognize the PORT command. I do not have control over the server (do not know in detail what server is) and I have to use the active mode.
This is the command line as:
lftp -e 'debug 10;set ftp:passive-mode off; set ftp:auto-passive-mode no; ls; bye;' -u user,password ftp://ftp.site.com
This is the debug output:
<--- 200 Using default language en_US
---> OPTS UTF8 ON
<--- 200 UTF8 set to on
---> OPTS MLST modify;perm;size;type;UNIX.group;UNIX.mode;UNIX.owner;
<--- 200 OPTS MLST modify;perm;size;type;UNIX.group;UNIX.mode;UNIX.owner;
---> USER xxxxx
<--- 331 Password required for xxxxx
---> PASS xxxxxx
<--- 230 User xxxxx logged in
---> PBSZ 0
<--- 200 PBSZ 0 successful
---> PROT P
<--- 200 Protection set to Private
---> PORT 172,16,133,11,146,168
<--- 500 Illegal PORT command
---> LIST
---> ABOR
---- Closing aborted data socket
---- Chiusura del socket di controllo
It seems that LFTP renounces to connect to data socket because the remote server does not support the PORT command. Is there a way to convince LFTP can still connect to port 20? By FTP manual obviously no problem.
The issue, I think, is not that the FTP server doesn't support the PORT command (it does), but rather, it doesn't like the IP address/port that your FTP client is sending in the PORT command.
PORT 172,16,133,11,146,168
...tells the server to connect to address 172.16.133.11, port 37544*. The interesting part here is the IP address: it's an RFC 1918 address (i.e. it's a private network address). That, in turn, suggests that your FTP client is in a LAN somewhere, and is connecting to an FTP server using a public IP address.
That remote FTP server cannot connect to a private network address; by definition, RFC 1918 address are not publicly routable.
Thus it very well could be that the FTP server is trying to make a connection to the address/port given in your PORT command, fails, thus that is why the FTP server fails the command, saying:
500 Illegal PORT command
To make a PORT command work with that FTP server, you would need to discover the public IP address that that server can connect to, to reach your client machine. Let's say that this address is 1.2.3.4. Then you would need to tell lftp to use that address in its PORT command, using the ftp:port-ipv4 option.
Chances are, though, that public IP address is the address of a NAT/router/firewall, and that that NAT/router/firewall will not allow connections, from the outside world to a high numbered port (e.g. 37544), to be routed to a machine within the LAN. This is one of the issues with active FTP data transfers, i.e. FTP data transfers which use the PORT (or EPRT) commands: they are not considered "firewall-friendly".
Hope this helps!
* - why 146,168 translates to port 37544?
According to FTP's RFC959 those parameters are:
(...) 16-bit TCP port address. This address information is broken into
8-bit fields and the value of each field is transmitted as a decimal
number (in character string representation).
146 dec = 10010010 bin = A
168 dec = 10101000 bin = B
A B
10010010 10101000 bin = 37544 dec

Resources