⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpserver.pm

📁 Wget很好的处理了http和ftp的下载,很值得学习的经典代码
💻 PM
📖 第 1 页 / 共 2 页
字号:
#!/usr/bin/perl -w# Part of this code was borrowed from Richard Jones's Net::FTPServer# http://www.annexia.org/freeware/netftpserverpackage FTPServer;use strict;use Cwd;use Socket;use IO::Socket::INET;use IO::Seekable;use POSIX qw(strftime);my $log        = undef;my $GOT_SIGURG = 0;# CONSTANTS# connection statesmy %_connection_states = (    'NEWCONN'  => 0x01,     'WAIT4PWD' => 0x02,     'LOGGEDIN' => 0x04,    'TWOSOCKS' => 0x08,);# subset of FTP commands supported by these server and the respective# connection states in which they are allowedmy %_commands = (     # Standard commands from RFC 959.    'CWD'  => $_connection_states{LOGGEDIN} |              $_connection_states{TWOSOCKS}, #   'EPRT' => $_connection_states{LOGGEDIN},#   'EPSV' => $_connection_states{LOGGEDIN},     'LIST' => $_connection_states{TWOSOCKS}, #   'LPRT' => $_connection_states{LOGGEDIN},#   'LPSV' => $_connection_states{LOGGEDIN},     'PASS' => $_connection_states{WAIT4PWD},     'PASV' => $_connection_states{LOGGEDIN},     'PORT' => $_connection_states{LOGGEDIN},     'PWD'  => $_connection_states{LOGGEDIN} |              $_connection_states{TWOSOCKS},     'QUIT' => $_connection_states{LOGGEDIN} |              $_connection_states{TWOSOCKS},     'REST' => $_connection_states{TWOSOCKS},     'RETR' => $_connection_states{TWOSOCKS},     'SYST' => $_connection_states{LOGGEDIN},    'TYPE' => $_connection_states{LOGGEDIN} |              $_connection_states{TWOSOCKS},    'USER' => $_connection_states{NEWCONN},     # From ftpexts Internet Draft.    'SIZE' => $_connection_states{LOGGEDIN} |              $_connection_states{TWOSOCKS},);# COMMAND-HANDLING ROUTINESsub _CWD_command{    my ($conn, $cmd, $path) = @_;    local $_;    my $newdir = $conn->{dir};    # If the path starts with a "/" then it's an absolute path.    if (substr ($path, 0, 1) eq "/") {        $newdir = "";        $path =~ s,^/+,,;    }    # Split the path into its component parts and process each separately.    my @elems = split /\//, $path;    foreach (@elems) {        if ($_ eq "" || $_ eq ".") {             # Ignore these.            next;        } elsif ($_ eq "..") {            # Go to parent directory.            if ($newdir eq "") {                print {$conn->{socket}} "550 Directory not found.\r\n";                return;            }            $newdir = substr ($newdir, 0, rindex ($newdir, "/"));        } else {            # Go into subdirectory, if it exists.            $newdir .= ("/" . $_);            if (! -d $conn->{rootdir} . $newdir) {                print {$conn->{socket}} "550 Directory not found.\r\n";                return;            }        }    }    $conn->{dir} = $newdir;}sub _LIST_command{    my ($conn, $cmd, $path) = @_;    # This is something of a hack. Some clients expect a Unix server    # to respond to flags on the 'ls command line'. Remove these flags    # and ignore them. This is particularly an issue with ncftp 2.4.3.    $path =~ s/^-[a-zA-Z0-9]+\s?//;    my $dir = $conn->{dir};    print STDERR "_LIST_command - dir is: $dir\n";    # Absolute path?    if (substr ($path, 0, 1) eq "/") {        $dir = "/";        $path =~ s,^/+,,;    }        # Parse the first elements of the path until we find the appropriate    # working directory.    my @elems = split /\//, $path;    my ($wildcard, $filename);    local $_;    for (my $i = 0; $i < @elems; ++$i) {        $_ = $elems[$i];        my $lastelement = $i == @elems-1;        if ($_ eq "" || $_ eq ".") { next } # Ignore these.        elsif ($_ eq "..") {            # Go to parent directory.            unless ($dir eq "/") {                $dir = substr ($dir, 0, rindex ($dir, "/"));            }        } else {            if (!$lastelement) { # These elements can only be directories.                unless (-d $conn->{rootdir} . $dir . $_) {                    print {$conn->{socket}} "550 File or directory not found.\r\n";                    return;                }                $dir .= $_;            } else { # It's the last element: check if it's a file, directory or wildcard.                if (-f $conn->{rootdir} . $dir . $_) {                     # It's a file.                    $filename = $_;                } elsif (-d $conn->{rootdir} . $dir . $_) {                     # It's a directory.                    $dir .= $_;                } elsif (/\*/ || /\?/) {                    # It is a wildcard.                    $wildcard = $_;                } else {                    print {$conn->{socket}} "550 File or directory not found.\r\n";                    return;                }            }        }    }        print STDERR "_LIST_command - dir is: $dir\n" if $log;        print {$conn->{socket}} "150 Opening data connection for file listing.\r\n";    # Open a path back to the client.    my $sock = __open_data_connection ($conn);    unless ($sock) {        print {$conn->{socket}} "425 Can't open data connection.\r\n";        return;    }    # If the path contains a directory name, extract it so that    # we can prefix it to every filename listed.    my $prefix = (($filename || $wildcard) && $path =~ /(.*\/).*/) ? $1 : "";        print STDERR "_LIST_command - prefix is: $prefix\n" if $log;    # OK, we're either listing a full directory, listing a single    # file or listing a wildcard.    if ($filename) {            # Single file.        __list_file ($sock, $prefix . $filename);    } else {                    # Wildcard or full directory $dirh.        unless ($wildcard) {            # Synthesize (fake) "total" field for directory listing.            print $sock "total 1 \r\n";        }        foreach (__get_file_list ($conn->{rootdir} . $dir, $wildcard)) {            __list_file ($sock, $prefix . $_);        }    }        unless ($sock->close) {        print {$conn->{socket}} "550 Error closing data connection: $!\r\n";        return;    }    print {$conn->{socket}} "226 Listing complete. Data connection has been closed.\r\n";}sub _PASS_command{    my ($conn, $cmd, $pass) = @_;    # TODO: implement authentication?    print STDERR "switching to LOGGEDIN state\n" if $log;    $conn->{state} = $_connection_states{LOGGEDIN};        if ($conn->{username} eq "anonymous") {        print {$conn->{socket}} "202 Anonymous user access is always granted.\r\n";    } else {        print {$conn->{socket}} "230 Authentication not implemented yet, access is always granted.\r\n";    }}sub _PASV_command{    my ($conn, $cmd, $rest) = @_;        # Open a listening socket - but don't actually accept on it yet.    "0" =~ /(0)/; # Perl 5.7 / IO::Socket::INET bug workaround.    my $sock = IO::Socket::INET->new (LocalHost => '127.0.0.1',                                      LocalPort => '0',                                      Listen => 1,                                      Reuse => 1,                                      Proto => 'tcp',                                      Type => SOCK_STREAM);    unless ($sock) {        # Return a code 550 here, even though this is not in the RFC. XXX        print {$conn->{socket}} "550 Can't open a listening socket.\r\n";        return;    }    $conn->{passive} = 1;    $conn->{passive_socket} = $sock;    # Get our port number.    my $sockport = $sock->sockport;    # Split the port number into high and low components.    my $p1 = int ($sockport / 256);    my $p2 = $sockport % 256;    $conn->{state} = $_connection_states{TWOSOCKS};        # We only accept connections from localhost.    print {$conn->{socket}} "227 Entering Passive Mode (127,0,0,1,$p1,$p2)\r\n";}sub _PORT_command{    my ($conn, $cmd, $rest) = @_;    # The arguments to PORT are a1,a2,a3,a4,p1,p2 where a1 is the    # most significant part of the address (eg. 127,0,0,1) and    # p1 is the most significant part of the port.    unless ($rest =~ /^\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})/) {        print {$conn->{socket}} "501 Syntax error in PORT command.\r\n";        return;    }    # Check host address.    unless ($1  > 0 && $1 < 224 &&            $2 >= 0 && $2 < 256 &&            $3 >= 0 && $3 < 256 &&            $4 >= 0 && $4 < 256) {        print {$conn->{socket}} "501 Invalid host address.\r\n";        return;    }    # Construct host address and port number.    my $peeraddrstring = "$1.$2.$3.$4";    my $peerport = $5 * 256 + $6;    # Check port number.    unless ($peerport > 0 && $peerport < 65536) {        print {$conn->{socket}} "501 Invalid port number.\r\n";    }    $conn->{peeraddrstring} = $peeraddrstring;    $conn->{peeraddr} = inet_aton ($peeraddrstring);    $conn->{peerport} = $peerport;    $conn->{passive} = 0;    $conn->{state} = $_connection_states{TWOSOCKS};    print {$conn->{socket}} "200 PORT command OK.\r\n";}sub _PWD_command{    my ($conn, $cmd, $rest) = @_;        # See RFC 959 Appendix II and draft-ietf-ftpext-mlst-11.txt section 6.2.1.    my $pathname = $conn->{dir};    $pathname =~ s,/+$,, unless $pathname eq "/";    $pathname =~ tr,/,/,s;    print {$conn->{socket}} "257 \"$pathname\"\r\n";}sub _REST_command{    my ($conn, $cmd, $restart_from) = @_;        unless ($restart_from =~ /^([1-9][0-9]*|0)$/) {        print {$conn->{socket}} "501 REST command needs a numeric argument.\r\n";        return;    }    $conn->{restart} = $1;    print {$conn->{socket}} "350 Restarting next transfer at $1.\r\n";}sub _RETR_command{    my ($conn, $cmd, $path) = @_;        my $dir = $conn->{dir};    # Absolute path?    if (substr ($path, 0, 1) eq "/") {        $dir = "/";        $path =~ s,^/+,,;        $path = "." if $path eq "";    }    # Parse the first elements of path until we find the appropriate    # working directory.    my @elems = split /\//, $path;    my $filename = pop @elems;    foreach (@elems) {        if ($_ eq "" || $_ eq ".") {             next # Ignore these.        } elsif ($_ eq "..") {            # Go to parent directory.            unless ($dir eq "/") {                $dir = substr ($dir, 0, rindex ($dir, "/"));            }        } else {            unless (-d $conn->{rootdir} . $dir . $_) {                print {$conn->{socket}} "550 File or directory not found.\r\n";                return;            }            $dir .= $_;        }    }    unless (defined $filename && length $filename) {        print {$conn->{socket}} "550 File or directory not found.\r\n";	    return;    }    if ($filename eq "." || $filename eq "..") {        print {$conn->{socket}} "550 RETR command is not supported on directories.\r\n";	    return;    }        my $fullname = $conn->{rootdir} . $dir . $filename;    unless (-f $fullname) {        print {$conn->{socket}} "550 RETR command is only supported on plain files.\r\n";        return;    }    # Try to open the file.    unless (open (FILE, '<', $fullname)) {        print {$conn->{socket}} "550 File or directory not found.\r\n";        return;    }    print {$conn->{socket}} "150 Opening " .        ($conn->{type} eq 'A' ? "ASCII mode" : "BINARY mode") .        " data connection for file $filename.\r\n";    # Open a path back to the client.    my $sock = __open_data_connection ($conn);    unless ($sock) {        print {$conn->{socket}} "425 Can't open data connection.\r\n";        return;    }    # What mode are we sending this file in?    unless ($conn->{type} eq 'A') # Binary type.    {        my ($r, $buffer, $n, $w);        # Restart the connection from previous point?        if ($conn->{restart}) {            # VFS seek method only required to support relative forward seeks            #            # In Perl = 5.00503, SEEK_CUR is exported by IO::Seekable,            # in Perl >= 5.6, SEEK_CUR is exported by both IO::Seekable            # and Fcntl. Hence we 'use IO::Seekable' at the top of the            # file to get this symbol reliably in both cases.            sysseek (FILE, $conn->{restart}, SEEK_CUR);            $conn->{restart} = 0;        }        # Copy data.        while ($r = sysread (FILE, $buffer, 65536))        {            # Restart alarm clock timer.            alarm $conn->{idle_timeout};            for ($n = 0; $n < $r; )            {                $w = syswrite ($sock, $buffer, $r - $n, $n);                # Cleanup and exit if there was an error.                unless (defined $w) {                    close $sock;                    close FILE;                    print {$conn->{socket}} "426 File retrieval error: $!. Data connection has been closed.\r\n";                    return;                }                $n += $w;            }            # Transfer aborted by client?            if ($GOT_SIGURG) {                $GOT_SIGURG = 0;                close $sock;                close FILE;                print {$conn->{socket}} "426 Transfer aborted. Data connection closed.\r\n";                return;            }        }        # Cleanup and exit if there was an error.        unless (defined $r) {            close $sock;            close FILE;            print {$conn->{socket}} "426 File retrieval error: $!. Data connection has been closed.\r\n";            return;        }    } else { # ASCII type.        # Restart the connection from previous point?        if ($conn->{restart}) {            for (my $i = 0; $i < $conn->{restart}; ++$i) {                getc FILE;            }            $conn->{restart} = 0;        }        # Copy data.        while (defined ($_ = <FILE>)) {            # Remove any native line endings.            s/[\n\r]+$//;            # Restart alarm clock timer.            alarm $conn->{idle_timeout};            # Write the line with telnet-format line endings.            print $sock "$_\r\n";            # Transfer aborted by client?            if ($GOT_SIGURG) {                $GOT_SIGURG = 0;                close $sock;                close FILE;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -