📄 ftp.pl
字号:
#-*-perl-*-# This is a wrapper to the lchat.pl routines that make life easier# to do ftp type work.#### Copyright (C) 1990 - 1998 Lee McLoughlin## Permission to use, copy, and distribute this software and its# documentation for any purpose with or without fee is hereby granted,# provided that the above copyright notice appear in all copies and# that both that copyright notice and this permission notice appear# in supporting documentation.## Permission to modify the software is granted, but not the right to# distribute the modified code. Modifications are to be distributed# as patches to released version.## This software is provided "as is" without express or implied warranty.### based on original version by Alan R. Martello <al@ee.pitt.edu># And by A.Macpherson@bnr.co.uk for multi-homed hosts### Basic usage:# require 'ftp.pl';# $ftp_port = 21;# $retry_call = 1;# $attempts = 2;# if( &ftp'open( $site, $ftp_port, $retry_call, $attempts ) != 1 ){# die "failed to open ftp connection";# }# if( ! &ftp'login( $user, $pass ) ){# die "failed to login";# }# &ftp'type( $text_mode ? 'A' : 'I' );# if( ! &ftp'get( $remote_filename, $local_filename, 0 ) ){# die "failed to get file";# }# &ftp'close();### $Id: ftp.pl,v 2.9 1998/05/29 19:02:00 lmjm Exp lmjm $# $Log: ftp.pl,v $# Revision 2.9 1998/05/29 19:02:00 lmjm# Lots of changes. See CHANGES since 2.8 file.## Revision 2.6 1994/06/06 18:37:37 lmjm# Switched to lchat - a subset of chat.# Allow for 'remote help's need to parse the help strings in the continuations# Use real_site for proxy connections.# Allow for cr stripping and corrected use of buffer (from Andrew).## Revision 2.5 1994/04/29 20:11:04 lmjm# Converted to use rfc1123.## Revision 2.4 1994/01/26 14:59:07 lmjm# Added DG result code.## Revision 2.3 1994/01/18 21:58:18 lmjm# Reduce calls to sigset.# Reset to old signal after use.## Revision 2.2 1993/12/14 11:09:06 lmjm# Use installed socket.ph.# Allow for more returns.## Revision 2.1 1993/06/28 15:02:00 lmjm# Full 2.1 release### lchat.pl is a special subset of chat2.pl that avoids some memory leaks.# This will drag in the correct socket libraryrequire 'lchat.pl';package ftp;$retry_pause = 60; # Pause before retrying a login.# If the remote ftp daemon doesn't respond within this time presume its dead# or something.$timeout = 120;# Timeout a read if I don't get data back within this many seconds$timeout_read = 3 * $timeout;# Timeout an open$timeout_open = $timeout;$version = '$Revision: 2.9 $';# This is a "global" it contains the last response from the remote ftp server# for use in error messages$ftp'response = "";# Also ftp'NS is the socket containing the data coming in from the remote ls# command.# The size of block to be read or written when talking to the remote# ftp server$ftpbufsize = 4096;# How often to print a hash out, when debugging$hashevery = 1024;# Output a newline after this many hashes to prevent outputing very long lines$hashnl = 70;# Is there a connection open?$service_open = 0;# If a proxy connection then who am I really talking to?$real_site = "";# "Global" Where error/log reports are sent to$ftp'showfd = 'STDERR';# Should a 421 be treated as a connection close and return 99 from# ftp'expect. This is against rfc1123 recommendations but I've found# it to be a wise default.$ftp'drop_on_421 = 1;# Name of a function to call on a pathname to map it into a remote# pathname.$mapunixout = '';$mapunixin = '';# This is just a tracing aid.$ftp_show = 0;# Global set on a error that aborts the connection$ftp'fatalerror = 0;# Whether to keep the continuation messages so the user can look at them$keep_continuations = 0;# Used in select() statements in read().$read_in = undef;# should we use the PASV extension to the ftp protocol?$ftp'use_pasv = 0; # 0=no (default), 1=yes# Variable only used if proxying$proxy = $proxy_gateway = $proxy_ftp_port = '';# EXPERIMENTAL:# Used for skey password handling# (Normally set elsewhere - this is just a sensible default.)# Is expected to take count and code as arguments and prompt# for the secret key with 'password:' on stdout and then print the password.$ftp'keygen_prog = '/usr/local/bin/key';# Uncomment to turn on lots of debugging.# &debug( 10 );# Limit how much data any one ftp'get can pull back# Negative values cause the size check to be skipped.$max_get_size = -1;# Where I am connected to.$connect_site = '';# &ftp'debug( debugging_level )# Turn on debugging ranging from 1 = some to 10 = everythingsub ftp'debug{ $ftp_show = $_[0]; if( $ftp_show > 9 ){ $chat'debug = 1; }}# &ftp'set_timeout( seconds )sub ftp'set_timeout{ local( $to ) = @_; return if $to == $timeout; $timeout = $to; $timeout_open = $timeout; $timeout_read = 3 * $timeout; if( $ftp_show ){ print $showfd "ftp timeout set to $timeout\n"; }}sub open_alarm{ die "timeout: open";}sub timed_open{ local( $site, $ftp_port, $retry_call, $attempts ) = @_; local( $connect_port ); local( $ret ); &alarm( $timeout_open ); while( $attempts-- ){ if( $ftp_show ){ print $showfd "proxy connecting via $proxy_gateway [$proxy_ftp_port]\n" if $proxy; print $showfd "Connecting to $site"; if( $ftp_port != 21 ){ print $showfd " [port $ftp_port]"; } print $showfd "\n"; } if( $proxy ) { if( ! $proxy_gateway ) { # if not otherwise set $proxy_gateway = "internet-gateway"; } if( $debug ) { print $showfd "using proxy services of $proxy_gateway, "; print $showfd "at $proxy_ftp_port\n"; } $connect_site = $proxy_gateway; $connect_port = $proxy_ftp_port; $real_site = $site; } else { $connect_site = $site; $connect_port = $ftp_port; } if( ! &chat'open_port( $connect_site, $connect_port ) ){ if( $retry_call ){ print $showfd "Failed to connect\n" if $ftp_show; next; } else { print $showfd "proxy connection failed " if $proxy; print $showfd "Cannot open ftp to $connect_site\n" if $ftp_show; return 0; } } $ret = &expect( $timeout, 2, 1 ); # ready for login to $site if( $ret != 1 ){ &chat'close(); next; } return 1; } continue { print $showfd "Pausing between retries\n"; sleep( $retry_pause ); } return 0;}# Routine called when a signal raised.sub ftp__sighandler{ local( $sig ) = @_; local( $msg ) = "Caught a SIG$sig flagging connection down"; $service_open = 0; if( $ftp_logger ){ eval "&$ftp_logger( \$msg )"; }}# Setup a signal handler for possible errors.sub ftp'set_signals{ $ftp_logger = @_; $SIG{ 'PIPE' } = "ftp'ftp__sighandler";}# &ftp'set_namemap( function to map outgoing name, function to map incoming )sub ftp'set_namemap{ ($mapunixout, $mapunixin) = @_; if( $debug ) { print $showfd "mapunixout = $mapunixout, $mapunixin = $mapunixin\n"; }}# &ftp'open( hostname or address,# port to use,# retry on call failure,# number of attempts to retry )# returns 1 if connected, 0 otherwisesub ftp'open{ local( $site, $ftp_port, $retry_call, $attempts ) = @_; $site =~ s/\s//g; local( $old_sig ) = $SIG{ 'ALRM' }; if( ! defined $old_sig ){ $old_sig = ''; } $SIG{ 'ALRM' } = "ftp\'open_alarm"; local( $ret ) = eval "&timed_open( '$site', $ftp_port, $retry_call, $attempts )"; &alarm( 0 ); $SIG{ 'ALRM' } = $old_sig; if( $@ =~ /^timeout/ ){ return -1; } if( $ret ){ $service_open = 1; } return $ret;}# &ftp'login( user, password, account )# the account part is optional unless the remote service requires one.sub ftp'login{ local( $remote_user, $remote_password, $remote_account ) = @_; local( $ret ); if( ! $service_open ){ return 0; } if( $proxy ){ # Should site or real_site be used here? &send( "USER $remote_user\@$real_site" ); } else { &send( "USER $remote_user" ); } # Loop to ignore any remote banner (from proxy) $ret = &expect( $timeout, 2, 1, # $remote_user logged in 331, 2, # send password for $remote_user 332, 2 ); # account for login - not yet supported if( $ret == 99 ){ &service_closed(); $ret = 0; } if( $ret == 1 ){ # Logged in no password needed return 1; } elsif( $ret == 2 ){ # A password is needed # check for s/key challenge - eg, [s/key 994 ph29005] # If we are talking to skey then use remote_password as the # secret to generate a real password if( $ftp'response =~ m#\[s/key (\d+) (\w+)\]# ){ local( $count, $code ) = ($1, $2); # TODO: report open failure & remove need for echo open( SKEY, "echo $remote_password | $ftp'keygen_prog $count $code |" ); while( <SKEY> ){ if( ! /password:/ ){ chop( $remote_password = $_ ); } } close SKEY; print $showfd "skey pass: $remote_password\n"; } &send( "PASS $remote_password" ); $ret = &expect( $timeout, 332, 2, # need extra account for login 2, 1 ); # $remote_user logged in if( $ret == 99 ){ &service_closed(); } elsif( $ret == 1 ){ # Logged in return 1; } elsif( $ret == 2 ){ if( !defined( $remote_account ) || $remote_account eq '' ){ &service_closed(); $ret = 0; } &send( "ACCT $remote_account"); $ret = &expect( $timeout, 230, 1, # $remote_user logged in 202, 0, # command not implemented 332, 0, # account for login not supported 5, 0, # not logged in or error 421, 99 ); # service unavailable, closing connection if( $ret == 99 ){ &service_closed(); $ret = 0; } if( $ret == 1 ){ # Logged in return 1; } } } # If I got here I failed to login return 0;}sub service_closed{ $service_open = 0; &chat'close();}# Close down the current ftp connecting in an orderly way.sub ftp'close{ &quit(); $service_open = 0; &chat'close();}# &ftp'cwd( directory )# Change to the given directory# return 1 if successful, 0 otherwisesub ftp'cwd{ local( $dir ) = @_; local( $ret ); if( ! $service_open ){ return 0; } if( $mapunixout ){ $dir = eval "&$mapunixout( \$dir, 'd' )"; } &send( "CWD $dir" ); $ret = &expect( $timeout, 2, 1 ); # working directory = $dir if( $ret == 99 ){ &service_closed(); $ret = 0; } return $ret;}# Send the PASV option to the remote server# &pasv()# Gets: nothing# Returns: nothing# Assumptions: you are connecting to an ftp server that implements PASV.# The PASV is necessary when using SOCKS and firewalls because the firewall# acts as a proxy.sub pasv{ # At some point I need to close/free S2, no? unless( socket( S2, $main'pf_inet, $main'sock_stream, $main'tcp_proto ) ){ ($!) = ($!, close(S2)); # close S2 while saving $! return undef; } &send( "PASV" ); $ret = &expect( $timeout, 150, 0, # reading directory 227, 1, # entering passive mode 125, 1, # data connection already open? transfer starting 4, 0, # file unavailable 5, 0, # error 421, 99 ); # service unavailable, closing connection if( $ret == 99 ){ &service_closed(); $ret = 0; } if( ! $ret ){ &close_data_socket; return 0; } if( $ret == 1 ) { if( $response =~ m/^227 Entering Passive Mode \((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)/ ){ $newhost = sprintf( "%d.%d.%d.%d", $1, $2, $3, $4 ); $newport = $5 * 256 + $6; } else { print $showfd "Cannot parse passive response\n" if $ftp_show; return 0; } } # now need to connect() the new socket if( ! &chat'open_newport( $newhost, $newport, *S2 ) ){ if( $retry_call ){ print $showfd "Failed to connect newport\n" if $ftp_show; next; } else { print $showfd "proxy connection failed " if $proxy; print $showfd "Cannot open pasv ftp to $connect_site\n" if $ftp_show; return 0; } }}# &ftp'dir( remote LIST options )# Start a list going with the given options.# Presuming that the remote deamon uses the ls command to generate the# data to send back then then you can send it some extra options (eg: -lRa)# return 1 if sucessful, 0 otherwisesub ftp'dir_open{ local( $options ) = @_; local( $ret ); if( ! $service_open ){ return 0; } if( ! &open_data_socket() ){ return 0; } if( $use_pasv ){ &pasv(); } if( $options ){ &send( "LIST $options" ); } else { &send( "LIST" ); } $ret = &expect( $timeout, 1, 1 ); # reading directory if( $ret == 99 ){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -