http://qs321.pair.com?node_id=638336

cbeber has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

I need to transmit data using ftp over an ssl connection. There are some modules that can do this(LWP, Net::FTPSSL), but these won't work because I need to be able to rename files that I've uploaded.

Searching through CPAN I found IO::Socket::SSL, which claims to be a drop-in replacment for IO::Socket::INET. Net::FTP does everything I need, and uses IO::Socket::INET as one of its base classes.

Is there a way to extend Net::FTP so that it uses IO::Socket::SSL instead of IO::Socket::INET as its base class?

  • Comment on Extending Code By Changing the Base Class of an Object

Replies are listed 'Best First'.
Re: Extending Code By Changing the Base Class of an Object
by erroneousBollock (Curate) on Sep 11, 2007 at 16:13 UTC
    Is there a way to extend Net::FTP so that it uses IO::Socket::SSL instead of IO::Socket::INET as its base class?
    You can subclass Net::FTP and override its port method to do what you want.

    port() currently does the following:

    sub port { @_ == 1 || @_ == 2 or croak 'usage: $ftp->port([PORT])'; my ($ftp, $port) = @_; my $ok; delete ${*$ftp}{'net_ftp_intern_port'}; unless (defined $port) { # create a Listen socket at same address as the command socket ${*$ftp}{'net_ftp_listen'} ||= IO::Socket::INET->new( Listen => 5, Proto => 'tcp', Timeout => $ftp->timeout, LocalAddr => $ftp->sockhost, ); my $listen = ${*$ftp}{'net_ftp_listen'}; my ($myport, @myaddr) = ($listen->sockport, split(/\./, $listen->sockhost)); $port = join(',', @myaddr, $myport >> 8, $myport & 0xff); ${*$ftp}{'net_ftp_intern_port'} = 1; } $ok = $ftp->_PORT($port); ${*$ftp}{'net_ftp_port'} = $port; $ok; }

    In your subclass, implement port() so that it does everything exactly the same, but call IO::Socket::SSL->new() instead.

    Update: Hmmm, actually that'll just SSLify the reverse connection back to the client (which won't even work because the server won't be expecting it ;).

    You might be able to do something like:

    BEGIN { use Net::FTP; use IO::Socket::SSL; @Net::FTP::ISA = grep { $_ ne 'IO::Socket::INET' } @Net::FTP::ISA; push @Net::FTP::ISA, 'IO::Socket::SSL'; } my $ftp = Net:FTP->new(...); #etc

    -David

      I appriciate the help. I'll play with the BEGIN block and see if I can get it to work that way.

      I'll post results if/when I can get this up and running.

Re: Extending Code By Changing the Base Class of an Object
by rhesa (Vicar) on Sep 11, 2007 at 18:47 UTC
    Net::FTPSSL has a rename() method. Also, put() accepts a new name for the file, e.g. $ftp->put( $local, 'new_name'). And there's always the site() method, which allows you to execute server-side commands (i.e. everything your ftp server implements).

    Sometimes it helps to read the source :-)

Re: Extending Code By Changing the Base Class of an Object
by sgt (Deacon) on Sep 11, 2007 at 18:12 UTC

    The trick shown by erroenousBollock seems ok. If things don't work out fully you always have the option of tunelling your perl process over a secure channel via (command) ssh. Net::SFTP::Foreign does just that.

    cheers --stephan
      Unfortunately, I can't use any of the SFTP stuff from cpan. I need to tunnel over SSL, not SSH. SSL and SSH are completely different beasts.

        Sorry. Actually the openssl command can work in a similar fashion: it can create an SSL tunnel. At least this might give you another option.

        good luck --stephan