Subject: | Net::FTP assumes a data connection after a failed command |
First of all, thanks a lot for maintaing libnet!
In the Net::FTP::_data_cmd() routine the dataconn() sub is invoked
unconditionally and without any error checking immediately after sending
a command to the server. It turns out that there are FTP servers out
there on the Big Bad Internet that close the data connection port
immediately after a failed transfer command. In this case, Net::FTP
misbehaves in two ways:
1. Net::FTP::dataconn->close() and _close() methods try to use the
'net_ftp_cmd' member without checking if it has even been set - and on a
failed connection, it is not, thus Perl 5.10.0 breaks out with an error.
2. Net::FTP::_data_cmd() invokes dataconn() unconditionally even when it
does not *need* it - even when the command has failed.
The end result is that the attached test script fails on Perl 5.10.0 on
Debian testing (lenny) - the Perl interpreter throws an error in
Net::FTP::dataconn::_close() (and after fixing it, another one in
close()), which LWP::Request interprets as an "Internal server error".
I've attached two patches - net-ftp-dataconn-close.patch adds a
Net::FTP::dataconn check whether the 'net_ftp_cmd' member is defined at
all before using it, and net-ftp-dataconn-eval.patch modifies
Net::FTP::_data_cmd() to invoke dataconn() in an eval, so that a failure
there will *still* return properly if the result code is bad. Of
course, there are other ways to work around these problems, but these
were the ones that I thought of first :)
Subject: | net-ftp-dataconn-test.pl |
#!/usr/bin/perl -Tw
use strict;
use LWP::UserAgent;
my ($ua, $req, $resp);
$ua = LWP::UserAgent->new('env_proxy' => 1) or
die("Could not instantiate the LWP user agent\n");
$req = HTTP::Request->new('GET' => 'ftp://ftp.inet.no/pub/socks/');
$ENV{'FTP_PASSIVE'} = 1;
LWP::Debug::level('+');
$resp = $ua->request($req);
print ($resp->is_success()? "yep\n": "nope\n");
Subject: | net-ftp-dataconn-eval.patch |
--- Net/FTP.pm.old 2008-07-15 18:30:29.000000000 +0300
+++ Net/FTP.pm 2008-07-15 18:30:41.000000000 +0300
@@ -1011,7 +1011,9 @@
if ($ok) {
$ftp->command($cmd, @_);
- $data = $ftp->_dataconn();
+ eval {
+ $data = $ftp->_dataconn();
+ };
$ok = CMD_INFO == $ftp->response();
if ($ok) {
$data->reading
Subject: | net-ftp-dataconn-close.patch |
--- Net/FTP/dataconn.pm.old 2008-07-15 18:47:54.000000000 +0300
+++ Net/FTP/dataconn.pm 2008-07-15 18:48:09.000000000 +0300
@@ -52,7 +52,7 @@
$data->SUPER::close();
delete ${*$ftp}{'net_ftp_dataconn'}
- if exists ${*$ftp}{'net_ftp_dataconn'}
+ if defined($ftp) && exists ${*$ftp}{'net_ftp_dataconn'}
&& $data == ${*$ftp}{'net_ftp_dataconn'};
}
@@ -69,6 +69,7 @@
$data->_close;
+ return 0 unless defined($ftp);
$ftp->response() == CMD_OK
&& $ftp->message =~ /unique file name:\s*(\S*)\s*\)/
&& (${*$ftp}{'net_ftp_unique'} = $1);