man Net::FTPServer () - A secure, extensible and configurable Perl FTP server

NAME

Net::FTPServer - A secure, extensible and configurable Perl FTP server

SYNOPSIS

  ftpd [--help] [-d] [-v] [-p port] [-s] [-S] [-V] [-C conf_file]
       [-P pidfile] [-o option=value]

DESCRIPTION

CWNet::FTPServer is a secure, extensible and configurable FTP server written in Perl.

Current features include:

 * Authenticated FTP access.
 * Anonymous FTP access.
 * Complete implementation of current RFCs.
 * ASCII or binary type file transfers.
 * Active or passive mode file transfers.
 * Run standalone or from inetd(8).
 * Security features: chroot, resource limits, tainting,
   protection against buffer overflows.
 * IP-based and/or IP-less virtual hosts.
 * Complete access control system.
 * Anonymous read-only FTP personality.
 * Virtual filesystem allows files to be served
   from a database.
 * Directory aliases and CDPATH support.
 * Extensible command set.
 * Generate archives on the fly.

INSTALLING AND RUNNING THE SERVER

A standard CWftpd.conf file is supplied with the distribution. Full documentation for all the possible options which you may use in this file is contained in this manual page. See the section CONFIGURATION below.

After doing CWmake install, the standard CWftpd.conf file should have been installed in CW/etc/ftpd.conf. You will probably need to edit this file to suit your local configuration.

Also after doing CWmake install, several start-up scripts will have been installed in CW/usr/sbin/*ftpd.pl. (On Debian in CW/usr/bin or CW/usr/local/bin). Each start-up script starts the server in a different configuration: either as a full FTP server, or as an anonymous-only read-only FTP server, etc.

The commonly used scripts are:

 * /usr/sbin/ftpd.pl
 * /usr/sbin/ro-ftpd.pl

The first script is for the full FTP server.

These scripts assume that the CWperl interpreter can be found on the current CW$PATH. In the rare situation when this is not the case, you may need to edit these scripts.

STANDALONE SERVER

If you have a high load site, you will want to run CWNet::FTPServer as a standalone server. To start CWNet::FTPServer as a standalone server, do:

  /usr/sbin/ftpd.pl -S

You may want to add this to your local start-up files so that the server starts automatically when you boot the machine.

To stop the server, do:

  killall ftpd.pl

(Note: CWAzazel points out that the above is a Linux-ism. Solaris administrators may get a nasty shock if they type CWkillall as CWroot! Just kill the parent CWftpd.pl process by hand instead).

RUNNING FROM INETD

Add the following line to CW/etc/inetd.conf:

  ftp stream tcp nowait root /usr/sbin/tcpd ftpd.pl

(This assumes that you have the CWtcp-wrappers package installed to provide basic access control through CW/etc/hosts.allow and CW/etc/hosts.deny. This access control is in addition to any access control which you may configure through CW/etc/ftpd.conf.)

After editing this file you will need to inform CWinetd:

  killall -HUP inetd

RUNNING FROM XINETD

CWxinetd is a modern alternative to CWinetd which is supposedly simpler to configure. In practice, however, it has proven to be quite difficult to configure services under CWxinetd (mainly because CWxinetd gives no diagnostic information when things go wrong). The following configuration has worked for me:

Create the file CW/etc/xinetd.d/net-ftpserver containing:

 # default: on
 # description: Net::FTPServer, a secure, \
 #              extensible, configurable FTP server.
 #
 service ftp
 {
        socket_type             = stream
        wait                    = no
        user                    = root
        server                  = /usr/sbin/ftpd.pl
        log_on_success          += DURATION USERID
        log_on_failure          += USERID
        disable                 = no
 }

Check any other possible FTP server configurations to ensure they are all disabled (ie. CWdisable = yes in all other files).

Restart CWxinetd using:

 /etc/init.d/xinetd restart

COMMAND LINE FLAGS

  --help           Display help and exit
  -d, -v           Enable debugging
  -p PORT          Listen on port PORT instead of the default port
  -s               Run in daemon mode (default: run from inetd)
  -S               Run in background and in daemon mode
  -V               Show version information and exit
  -C CONF          Use CONF as configuration file (default:
                   /etc/ftpd.conf)
  -P PIDFILE       Save pid into PIDFILE (daemon mode only)
  -o option=value  Override config file option with value
  --test           Test mode (used only in automatic testing scripts)

CONFIGURING AND EXTENDING THE SERVER

CWNet::FTPServer can be configured and extended in a number of different ways.

Firstly, almost all common server configuration can be carried out by editing the configuration file CW/etc/ftpd.conf.

Secondly, commands can be loaded into the server at run-time to provide custom extensions to the common FTP command set. These custom commands are written in Perl.

Thirdly, one of several different supplied personalities can be chosen. Personalities can be used to make deep changes to the FTP server: for example, there is a supplied personality which allows the FTP server to serve files from a relational database. By subclassing CWNet::FTPServer, CWNet::FTPServer::DirHandle and CWNet::FTPServer::FileHandle you may also write your own personalities.

The next sections talk about each of these possibilities in turn.

CONFIGURATION

A standard CW/etc/ftpd.conf file is supplied with CWNet::FTPServer in the distribution. The possible configuration options are listed in full below.

Simple configuration options can also be given on the command line using the CW-o option. Command line configuration options override those from the configuration file.

<Include filename>
Use the <Include filename> directive to include the contents of CWfilename directly at the current point within the configuration file. You cannot use <Include> within a <Host> section, or at least you can but it won't work the way you expect.
<IncludeWildcard wildcard>
Include all files matching CWwildcard at this point in the file. The files are included in alphabetical order. You cannot use <IncludeWildcard> within a <Host> section, or at least you can but it won't work the way you expect.
debug
Run with debugging. Equivalent to the command line CW-d option. Default: 0 Example: CWdebug: 1
port
The TCP port number on which the FTP server listens when running in daemon mode (see CWdaemon mode option below). Default: The standard ftp/tcp service port from CW/etc/services Example: CWport: 8021
daemon mode
Run as a daemon. If set, the FTP server will open a listening socket on its default port number, accept new connections and fork off a new process to handle each connection. If not set (the default), the FTP server will handle a single connection on stdin/stdout, which is suitable for use from inetd. The equivalent command line options are CW-s and CW-S. Default: 0 Example: CWdaemon mode: 1
run in background
Run in the background. If set, the FTP server will fork into the background before running. The equivalent command line option is CW-S. Default: 0 Example: CWrun in background: 1
error log
If set, then all warning and error messages are appended to this file. If not set, warning and error messages get sent to STDERR and to syslog. Having an error log is highly recommended. Default: (not set, warnings and errors go to syslog) Example: CWerror log: /var/log/ftpd.errors
rotate log files
If set, and if the log file names contain a '%' directive, then the server will check if a new log file is needed whenever the system accepts a new connection. This implements a log rotation feature for long-running servers. If not set, then any '%' directive will be evaluated only when the log files gets created. Default: (not set, log file name evaluated only once) Example: CWrotate log files: 1
maintainer email
Maintainer's email address. Default: root@hostname Example: CWmaintainer email: bob@example.com
class
Assign users into classes. One or more CWclass directives can be added to the configuration file to aggregate individual users into larger groups of users called classes. By default all anonymous users are in class CWanonymous and every other user is in class CWusers. The configuration file can contain zero or more CWclass directives. The format of the class directive is either:
 class: CLASSNAME USERNAME[,USERNAME[,...]]
or:
 class: CLASSNAME { perl code ... }
Examples of the first form are:
 class: staff rich
 class: students ann,mary,pete
User CWrich will be placed into class CWstaff, and users CWann, CWmary and CWpete will be placed into class CWstudents. Examples of the second form are:
 class: family { /jones$/ }
 class: friends { $_ ne "jeff" }
Any username ending in CWjones (eg. CWrjones, CWtimjones) will be in class CWfamily. Any other user except CWjeff will be placed in class CWfriends. Note that the Perl code must be surrounded by CW{...} and must return a boolean true or false value. The username is available as CW$_. The Perl code is arbitrary: it might, for example, use an external file or database lookup in order to work out if a user belongs to a class. CWclass directives are evaluated in the order in which they appear in the configuration file until one matches the username. Default: Anonymous users are assigned to class CWanonymous and everyone else is assigned to class CWusers.
timeout
Timeout on control connection. If a command has not been received after this many seconds, the server drops the connection. You may set this to zero to disable timeouts completely (although this is not recommended). Default: 900 (seconds) Example: CWtimeout: 600
limit memory
limit nr processes
limit nr files
Resource limits. These limits are applied to each child process and are important in avoiding denial of service (DoS) attacks against the FTP server.
 Resource         Default   Unit
 limit memory       16384   KBytes  Amount of memory per child
 limit nr processes    10   (none)  Number of processes
 limit nr files        20   (none)  Number of open files
To instruct the server not to limit a particular resource, set the limit to CW-1. Example:
 limit memory:       32768
 limit nr processes:    20
 limit nr files:        40
 limit nr processes:    -1
max clients
Limit on the number of clients who can simultaneously connect. If this limit is ever reached, new clients will immediately be closed. It will not even ask the client to login. This feature works in daemon mode only. Default: 255 Example: CWmax clients: 600
max clients message
Message to display when ``max clients'' has been reached. You may use the following % escape sequences within the message for internal variables:
 %x  ``max clients'' setting that has been reached
 %E  maintainer email address (from ``maintainer email''
     setting above)
 %G  time in GMT
 %R  remote hostname or IP address if ``resolve addresses''
     is not set
 %L  local hostname
 %T  local time
 %%  just an ordinary ``%''
Default: Maximum connections reached Example: CWmax clients message: Only %x simultaneous connections allowed. Please try again later.
resolve addresses
Resolve addresses. If set, attempt to do a reverse lookup on client addresses for logging purposes. If you set this then some clients may experience long delays when they try to connect. Not recommended on high load servers. Default: 0 Example: CWresolve addresses: 1
require resolved addresses
Require resolved addresses. If set, client addresses must validly resolve otherwise clients will not be able to connect. If you set this then some clients will not be able to connect, even though it is probably the fault of their ISP. Default: 0 Example: CWrequire resolved addresses: 1
change process name
Change process name. If set (the default) then the FTP server will change its process name to reflect the IP address or hostname of the client. If not set then the FTP server will not try to change its process name. Default: 1 Example: CWchange process name: 0
greeting type
Greeting type. The greeting is printed before the user has logged in. Possible greeting types are:
    full     Full greeting, including hostname and version number.
    brief    Hostname only.
    terse    Nothing
    text     Display greeting from ``greeting text'' option.
The SITE VERSION command can also reveal the version number. You may need to turn this off by setting CWallow site version command: 0 below. Default: full Example: CWgreeting type: text
greeting text
Greeting text. If the CWgreeting type is set to CWtext then this contains the text to display. Default: none Example: CWgreeting text: Hello. I'll be your server today.
welcome type
Welcome type. The welcome is printed after a user has logged in. Possible welcome types are:
    normal   Normal welcome message: ``Welcome <<username>>.''
    text     Take the welcome message from ``welcome text'' option.
    file     Take the welcome message from ``welcome file'' file.
Default: normal Example: CWwelcome type: text
welcome text
If CWwelcome type is set to CWtext, then this contains the text to be printed after a user has logged in. You may use the following % escape sequences within the welcome text to substitute for internal variables:
 %E  maintainer's email address (from ``maintainer email''
     setting above)
 %G  time in GMT
 %R  remote hostname or IP address if ``resolve addresses''
     is not set
 %L  local hostname
 %m  user's home directory (see ``home directory'' below)
 %T  local time
 %U  username given when logging in
 %u  currently a synonym for %U, but in future will be
     determined from RFC931 authentication, like wu-ftpd
 %%  just an ordinary ``%''
Default: none Example: CWwelcome text: Welcome to this FTP server.
welcome file
If CWwelcome type is set to CWfile, then this contains the file to be printed after a user has logged in. You may use any of the % escape sequences defined in CWwelcome text above. Default: none Example: CWwelcome file: /etc/motd
home directory
Home directory. This is the home directory where we put the user once they have logged in. This only applies to non-anonymous logins. Anonymous logins are always placed in /, which is at the root of their chrooted environment. You may use an absolute path here, or else one of the following special forms:
 %m   Use home directory from password file or from NSS.
 %U   Username.
 %%   A single % character.
For example, to force a user to start in CW~/anon-ftp when they log in, set this to CW%m/anon-ftp. Note that setting the home directory does not perform a chroot. Use the CWroot directory setting below to jail users into a particular directory. Home directories are relative to the current root directory. In the anonymous read-only (ro-ftpd) personality, set home directory to CW/ or else you will get a warning whenever a user logs in. Default: CW%m Examples:
 home directory: %m/anon-ftp
 home directory: /
root directory
Root directory. Immediately after logging in, perform a chroot into the named directory. This only applies to non-anonymous logins, and furthermore it only applies if you have a non-database VFS installed. Database VFSes typically cannot perform chroot (or, to be more accurate, they have a different concept of chroot - typically assigning each user their own completely separate namespace). You may use CW%m and CW%U as above. For example, to jail a user under CW~/anon-ftp after login, do:
  home directory: /
  root directory: %m/anon-ftp
Notice that the home directory is relative to the current root directory. Default: (none) Example: CWroot directory: %m/anon-ftp
time zone
Time zone to be used for MDTM and LIST stat information. Default: GMT Examples:
 time zone: Etc/GMT+3
 time zone: Europe/London
 time zone: US/Mountain
local address
Local addresses. If you wish the FTP server (in daemon mode) to only bind to a particular local interface, then give its address here. Default: none Example: CWlocal address: 127.0.0.1
allow anonymous
Allow anonymous access. If set, then allow anonymous access through the CWftp and CWanonymous accounts. Default: 0 Example: CWallow anonymous: 1
anonymous password check
anonymous password enforce
Validate email addresses. Normally when logging in anonymously, you are asked to enter your email address as a password. These options can be used to check and enforce email addresses in this field (to some extent, at least you obviously can't force someone to enter a true email address). The CWanonymous password check option may be set to CWrfc822, CWno browser, CWtrivial or CWnone. If set to CWrfc822 then the user must enter a valid RFC 822 email address as password. If set to CWno browser then a valid RFC 822 email address must be entered, and various common browser email addresses like CWmozilla@ and CWIECIverCWUser@ are refused. If set to CWtrivial then we just check that the address contains an @ char. If set to CWnone, then we do no checking. The default is CWnone. If the CWanonymous password enforce option is set and the password fails the check above, then the user will not be allowed to log in. The default is 0 (unset). These options only have effect when CWallow anonymous is set. Example:
 anonymous password check: rfc822
 anonymous password enforce: 1
allow proxy ftp
Allow proxy FTP. If this is set, then the FTP server can be told to actively connect to addresses and ports on any machine in the world. This is not such a great idea, but required if you follow the RFC very closely. If not set (the default), the FTP server will only connect back to the client machine. Default: 0 Example: CWallow proxy ftp: 1
allow connect low port
Allow the FTP server to connect back to ports < 1024. This is rarely useful and could pose a serious security hole in some circumstances. Default: 0 Example: CWallow connect low port: 1
passive port range
What range of local ports will the FTP server listen on in passive mode? Choose a range here like CW1024-5999,49152-65535. The special value CW0 means that the FTP server will use a kernel-assigned ephemeral port. Default: 49152-65535 Example: CWpassive port range: 0
ftp data port
Which source port to use for active (non-passive) mode when connecting to the client for PORT mode transfers. The special value CW0 means that the FTP server will use a kernel-assigned ephemeral port. To strictly follow RFC, this should be set to CWftp-data(2). This may be required for certain brain-damaged firewall configurations. However, for security reasons, the default setting is intentionally set to CW0 to utilize a kernel-assigned ephemeral port. Use this directive at your own risk! SECURITY PRECAUTIONS: 1) Unfortunately, to use a port < 1024 requires super-user privileges. Thus, low ports will not work unless the FTP server is invoked as super-user. This also implies that all processes handling the client connections must also remain super-user throughout the entire session. It is highly discouraged to use a low port.
 http://cr.yp.to/ftp/security.html
 (See "Connection laundering" section)
2) There sometimes exists a danger of needing to connect to the same remote host:port. Using the same IP/port on both sides will cause connect() to fail if the old socket is still being broken down. This condition will not occur if using an ephemeral port.
 http://groups.google.com/groups?selm=fa.epucqgv.1l2kl0e@ifi.uio.no
 (See "unable to create socket" comment)
3) Many hackers use source port 20 to blindly circumvent certain naive firewalls. Using an ephemeral port (the default) may help discourage such dangerous naivety.
 man nmap
 (See the -g option)
Default: 0 Example: CWftp data port: ftp-data
max login attempts
Maximum number of login attempts before we drop the connection and issue a warning in the logs. Wu-ftpd defaults this to 5. Default: 3 Example: CWmax login attempts: 5
pam authentication
Use PAM for authentication. Required on systems such as Red Hat Linux and Solaris which use PAM for authentication rather than the normal CW/etc/passwd mechanisms. You will need to have the CWAuthen::PAM Perl module installed for this to work. Default: 0 Example: CWpam authentication: 1
pam application name
If PAM authentication is enabled, then this is the PAM application name. I have used CWftp as the default which is the same name that wu-ftpd chooses. FreeBSD users will want to use CWftpd here. Default: ftp Example: CWpam application name: ftpd
password file
Only in the CWFull personality, this allows you to specify a password file which is used for authentication. If you enable this option, then normal PAM or CW/etc/passwd is bypassed and this password file is used instead. Each line in the password file has the following format:
 username:crypted_password:unix_user[:root_directory]
Comments and blank lines are ignored. For example, a line with:
 guest:ab01FAX.bQRSU:rich:/home/rich/guest-uploads
would allow someone to log in as CWguest with password CW123456. After logging in, the FTP server will assume the identity of the real Unix user CWrich, and will chroot itself into the CW/home/rich/guest-uploads directory. (Note that because ordinary PAM/CWpasswd is bypassed, it would no longer be possible for a user to log in directly with the username CWrich). Crypted passwords can be generated using the following command:
 perl -e 'print crypt ("123456", "ab"), "\n"'
Replace CW123456 with the actual password, and replace CWab with two random letters from the set CW[a-zA-Z0-9./]. (The two random letters are the so-called salt and are used to make dictionary attacks against the password file more difficult - see crypt(3)). The user's home directory comes from the real Unix password file (or nsswitch-configured source) for the real Unix user. You cannot use password files to override this, and so if you are using the optional CWroot_directory parameter, it would make sense to add CWhome directory: / into your configuration file. Anonymous logins are not affected by the CWpassword file option. Use the CWallow anonymous flag to control whether anonymous logins are permitted in the CWFull back-end. Password files are not the height of security, but they are included because they can sometimes be useful. In particular if the password file can be read by untrusted users then it is likely that those same users can run the crack program and eventually find out your passwords. Some small additional security is offered by having the password file readable only by root (mode 0600). In future we may offer MD5 or salted SHA-1 hashed passwords to make this harder. A curious artifact of the implementation allows you to list the same user with multiple different passwords. Any of the passwords is then valid for logins (and you could even have the user map to different real Unix users in different chrooted directories!) Default: (none) Example: CWpassword file: /etc/ftpd.passwd
pidfile
Location of the file to store the process ID (PID). Applies only to the deamonized process, not the child processes. Default: (no pidfile created) Example: CWpidfile: /var/run/ftpd.pid
client logging
Location to store all client commands sent to the server. The format is the date, the pid, and the command. Following the pid is a - if not authenticated the username if the connection is authenticated. Example of before and after authentication:
 [Wed Feb 21 18:41:32 2001][23818:-]USER rob
 [Wed Feb 21 18:41:33 2001][23818:-]PASS 123456
 [Wed Feb 21 18:41:33 2001][23818:*]SYST
Default: (no logging) Examples:
 client logging: /var/log/ftpd.log
 client logging: /tmp/ftpd_log.$hostname
xfer logging
Location of transfer log. The format was taken from wu-ftpd and ProFTPD xferlog. (See also man xferlog) Default: (no logging) Examples:
 xfer logging: /var/log/xferlog
 xfer logging: /tmp/xferlog.$hostname
hide passwords in client log
If set to 1, then password (CWPASS) commands will not be logged in the client log. This option has no effect unless client logging is enabled. Default: 0 (PASS lines will be shown) Example: CWhide passwords in client log: 1
enable syslog
Enable syslogging. If set, then Net::FTPServer will send much information to syslog. On many systems, this information will be available in /var/log/messages or /var/adm/messages. If clear, syslogging is disabled. Default: 1 Example: CWenable syslog: 0
ident timeout
Timeout for ident authentication lookups. A timeout (in seconds) must be specified in order to enable ident lookups. There is no way to specify an infinite timeout. Use 0 to disable this feature. Default: 0 Example: CWident timeout: 10
access control rule
user access control rule
retrieve rule
store rule
delete rule
list rule
mkdir rule
rename rule
chdir rule
Access control rules. Access control rules are all specified as short snippets of Perl script. This allows the maximum configurability you can express just about any rules you want but at the price of learning a little Perl. You can use the following variables from the Perl:
 $hostname      Resolved hostname of the client [1]
 $ip            IP address of the client
 $user          User name [2]
 $class         Class of user [2]
 $user_is_anonymous  True if the user is an anonymous user [2]
 $pathname      Full pathname of the file being affected [2]
 $filename      Filename of the file being affected [2,3]
 $dirname       Directory name containing file being affected [2]
 $type          'A' for ASCII, 'B' for binary, 'L8' for local 8-bit
 $form          Always 'N'
 $mode          Always 'S'
 $stru          Always 'F'
Notes: [1] May be undefined, particularly if CWresolve addresses is not set. [2] Not available in CWaccess control rule since the user has not logged in at this point. [3] Not available for CWlist directory rule. Access control rule. The FTP server will not accept any connections from a site unless this rule succeeds. Note that only CW$hostname and CW$ip are available to this rule, and unless CWresolve addresses and CWrequire resolved addresses are both set CW$hostname may be undefined. Default: 1 Examples:
 (a) Deny connections from *.badguys.com:
     access control rule: defined ($hostname) && \
                          $hostname !~ /\.badguys\.com$/
 (b) Only allow connections from local network 10.0.0.0/24:
     access control rule: $ip =~ /^10\./
User access control rule. After the user logs in successfully, this rule is then called to determine if the user may be permitted access. Default: 1 Examples:
 (a) Only allow ``rich'' to log in from 10.x.x.x network:
     user access control rule: $user ne "rich" || \
                               $ip =~ /^10\./
 (b) Only allow anonymous users to log in if they come from
     hosts with resolving hostnames (``resolve addresses'' must
     also be set):
     user access control rule: !$user_is_anonymous || \
                               defined ($hostname)
 (c) Do not allow user ``jeff'' to log in at all:
     user access control rule: $user ne "jeff"
Retrieve rule. This rule controls who may retrieve (download) files. Default: 1 Examples:
 (a) Do not allow anyone to retrieve ``/etc/*'' or any file anywhere
     called ``.htaccess'':
     retrieve rule: $dirname !~ m(^/etc/) && $filename ne ".htaccess"
 (b) Only allow anonymous users to retrieve files from under the
     ``/pub'' directory.
     retrieve rule: !$user_is_anonymous || $dirname =~ m(^/pub/)
Store rule. This rule controls who may store (upload) files. In the anonymous read-only (ro-ftpd) personality, it is not possible to upload files anyway, so setting this rule has no effect. Default: 1 Examples:
 (a) Only allow users to upload files to the ``/incoming''
     directory.
     store rule: $dirname =~ m(^/incoming/)
 (b) Anonymous users can only upload files to ``/incoming''
     directory.
     store rule: !$user_is_anonymous || $dirname =~ m(^/incoming/)
 (c) Disable file upload.
     store rule: 0
Delete rule. This rule controls who may delete files or rmdir directories. In the anonymous read-only (ro-ftpd) personality, it is not possible to delete files anyway, so setting this rule has no effect. Default: 1 Example: CWdelete rule: 0 List rule. This rule controls who may list out the contents of a directory. Default: 1 Example: CWlist rule: $dirname =~ m(^/pub/) Mkdir rule. This rule controls who may create a subdirectory. In the anonymous read-only (ro-ftpd) personality, it is not possible to create directories anyway, so setting this rule has no effect. Default: 1 Example: CWmkdir rule: 0 Rename rule. This rule controls which files or directories can be renamed. Default: 1 Example: CWrename rule: $pathname !~ m(/.htaccess$) Chdir rule. This rule controls which directories are acceptable to a CWD or CDUP. Example: CWchdir rule: $pathname !~ m/private/
chdir message file
Change directory message file. If set, then the first time (per session) that a user goes into a directory which contains a file matching this name, that file will be displayed. The file may contain any of the following % escape sequences:
 %C  current working directory
 %E  maintainer's email address (from ``maintainer email''
     setting above)
 %G  time in GMT
 %R  remote hostname or IP address if ``resolve addresses''
     is not set
 %L  local hostname
 %m  user's home directory (see ``home directory'' below)
 %T  local time
 %U  username given when logging in
 %u  currently a synonym for %U, but in future will be
     determined from RFC931 authentication, like wu-ftpd
 %%  just an ordinary ``%''
Default: (none) Example: CWchdir message file: .message
allow rename to overwrite
Allow the rename (RNFR/RNTO) command to overwrite files. If unset, then we try to test whether the rename command would overwrite a file and disallow it. However there are some race conditions with this test. Default: 1 Example: CWallow rename to overwrite: 0
allow store to overwrite
Allow the store commands (STOR/STOU/APPE) to overwrite files. If unset, then we try to test whether the store command would overwrite a file and disallow it. However there are some race conditions with this test. Default: 1 Example: CWallow store to overwrite: 0
alias
Define an alias CWname for directory CWdir. For example, the command CWalias: mirror /pub/mirror would allow the user to access the CW/pub/mirror directory directly just by typing CWcd mirror. Aliases only apply to the cd (CWD) command. The CWcd foo command checks for directories in the following order:
 foo in the current directory
 an alias called foo
 foo in each directory in the cdpath (see ``cdpath'' command below)
You may list an many aliases as you want. Alias names cannot contain slashes (/). Although alias dirs may start without a slash (/), this is unwise and it's better that they always start with a slash (/) char. General format: CWalias: CInameCW CIdirCW
cdpath
Define a search path which is used when changing directories. For example, the command CWcdpath: /pub/mirror /pub/sites would allow the user to access the CW/pub/mirror/ftp.cpan.org directory directly by just typing CWcd ftp.cpan.org. The CWcd foo command checks for directories in the following order:
 foo in the current directory
 an alias called foo (see ``alias'' command above)
 foo in each directory in the cdpath
General format: CWcdpath: CIdir1CW [CIdir2CW [CIdir3CW ...]]
allow site version command
SITE VERSION command. If set, then the SITE VERSION command reveals the current Net::FTPServer version string. If unset, then the command is disabled. Default: 1 Example: CWallow site version command: 0
allow site exec command
SITE EXEC command. If set, then the SITE EXEC command allows arbitrary commands to be executed on the server as the current user. If unset, then this command is disabled. The default is disabled for obvious security reasons. If you do allow SITE EXEC, you may need to increase the per process memory, processes and files limits above. Default: 0 Example: CWallow site exec command: 1
enable archive mode
Archive mode. If set (the default), then archive mode is enabled, allowing users to request, say, CWfile.gz and get a version of CWfile which is gzip-compressed on the fly. If zero, then this feature is disabled. See the section ARCHIVE MODE elsewhere in this manual for details. Since archive mode is implemented using external commands, you need to ensure that programs such as CWgzip, CWcompress, CWbzip2, CWuuencode, etc. are available on the CW$PATH (even in the chrooted environment), and you also need to substantially increase the normal per-process memory, processes and files limits. Default: 1 Example: CWenable archive mode: 0
archive zip temporaries
Temporary directory for generating ZIP files in archive mode. In archive mode, when generating ZIP files, the FTP server is capable of either creating a temporary file on local disk containing the ZIP contents, or can generate the file completely in memory. The former method saves memory. The latter method (only practical on small ZIP files) allows the server to work more securely and in certain read-only chrooted environments. (Unfortunately the ZIP file format itself prevents ZIP files from being easily created on the fly). If not specified in the configuration file, this option defaults to using CW/tmp. If there are local users on the FTP server box, then this can lead to various CWtmp races, so for maximum security you will probably want to change this. If specified, and set to a string, then the string is the name of a directory which is used for storing temporary zip files. This directory must be writable, and must exist inside the chrooted environment (if chroot is being used). If specified, but set to 0 or an empty string, then the server will always generate the ZIP file in memory. In any case, if the directory is found at runtime to be unwritable, then the server falls back to creating ZIP files in memory. Default: CW/tmp Example: CWarchive zip temporaries: Example: CWarchive zip temporaries: /var/ziptmp
site command
Custom SITE commands. Use this command to define custom SITE commands. Please read the section LOADING CUSTOMIZED SITE COMMANDS in this manual page for more detailed information. The CWsite command command has the form: CWsite command: CIcmdnameCW CIfileCW cmdname is the name of the command (eg. for SITE README you would set cmdname == CWreadme). file is a file containing the code of the site command in the form of an anonymous Perl subroutine. The file should have the form:
 sub {
   my $self = shift;            # The FTPServer object.
   my $cmd = shift;             # Contains the command itself.
   my $rest = shift;            # Contains any parameters passed by the user.
      :     :
      :     :
   $self->reply (RESPONSE_CODE, RESPONSE_TEXT);
 }
You may define as many site commands as you want. You may also override site commands from the current personality here. Example:
 site command: quota /usr/local/lib/ftp/quota.pl
and the file CW/usr/local/lib/ftp/quota.pl contains:
 sub {
   my $self = shift;            # The FTPServer object.
   my $cmd = shift;             # Contains "QUOTA".
   my $rest = shift;            # Contains parameters passed by user.
   # ... Some code to compute the user's quota ...
   $self->reply (200, "Your quota is $quota MB.");
 }
The client types CWSITE QUOTA and the server responds with:
 "200 Your quota is 12.5 MB.".
<Host hostname> ... </Host>
<Host hostname> ... </Host> encloses commands which are applicable only to a particular host. CWhostname may be either a fully-qualified domain name (for IP-less virtual hosts) or an IP address (for IP-based virtual hosts). You should read the section VIRTUAL HOSTS in this manual page for more information on the different types of virtual hosts and how to set it up in more detail. Note also that unless you have set CWenable virtual hosts: 1, all <Host> sections will be ignored.
enable virtual hosts
Unless this option is uncommented, virtual hosting is disabled and the <Host> sections in the configuration file have no effect. Default: 0 Example: CWenable virtual hosts: 1
virtual host multiplex
IP-less virtual hosts. If you want to enable IP-less virtual hosts, then you must set up your DNS so that all hosts map to a single IP address, and place that IP address here. This is roughly equivalent to the Apache CWNameVirtualHost option. IP-less virtual hosting is an experimental feature which requires changes to clients. Default: (none) Example: CWvirtual host multiplex: 1.2.3.4 Example <Host> section. Allow the dangerous SITE EXEC command on local connections. (Note that this is still dangerous).
 <Host localhost.localdomain>
   ip: 127.0.0.1
   allow site exec command: 1
 </Host>
Example <Host> section. This shows you how to do IP-based virtual hosts. I assume that you have set up your DNS so that CWftp.bob.example.com maps to IP CW1.2.3.4 and CWftp.jane.example.com maps to IP CW1.2.3.5, and you have set up suitable IP aliasing in the kernel. You do not need the CWip: command if you have configured reverse DNS correctly AND you trust your local DNS servers.
 <Host ftp.bob.example.com>
   ip: 1.2.3.4
   root directory: /home/bob
   home directory: /
   user access control rule: $user eq "bob"
   maintainer email: bob@bob.example.com
 </Host>
 <Host ftp.jane.example.com>
   ip: 1.2.3.5
   root directory: /home/jane
   home directory: /
   allow anonymous: 1
   user access control rule: $user_is_anonymous
   maintainer email: jane@jane.example.com
 </Host>
These rules set up two virtual hosts called CWftp.bob.example.com and CWftp.jane.example.com. The former is located under bob's home directory and only he is allowed to log in. The latter is located under jane's home directory and only allows anonymous access. Example <Host> section. This shows you how to do IP-less virtual hosts. Note that IP-less virtual hosts are a highly experimental feature, and require the client to support the HOST command. You need to set up your DNS so that both CWftp.bob.example.com and CWftp.jane.example.com point to your own IP address.
 virtual host multiplex: 1.2.3.4
 <Host ftp.bob.example.com>
   root directory: /home/bob
   home directory: /
   user access control rule: $user eq "bob"
 </Host>
 <Host ftp.jane.example.com>
   root directory: /home/jane
   home directory: /
   allow anonymous: 1
   user access control rule: $user_is_anonymous
 </Host>
log socket type
Socket type for contacting syslog. This is the argument to the CWSys::Syslog::setlogsock function. Default: unix Example: CWlog socket type: inet
listen queue
Length of the listen queue when running in daemon mode. Default: 10 Example: CWlisten queue: 20
tcp window
Set TCP window. See RFC 2415 Simulation Studies of Increased Initial TCP Window Size. This setting only affects the data socket. It's not likely that you will need to or should change this setting from the system-specific default. Default: (system-specific TCP window size) Example: CWtcp window: 4380
tcp keepalive
Set TCP keepalive. Default: (system-specific keepalive setting) Example: CWtcp keepalive: 1
command filter
Command filter. If set, then all commands are checked against this regular expression before being executed. If a command doesn't match the filter, then the command connection is immediately dropped. This is equivalent to the CWAllowFilter command in ProFTPD. Remember to include CW^...$ around the filter. Default: (no filter) Example: CWcommand filter: ^[A-Za-z0-9 /]+$
restrict command
Advanced command filtering. The CWrestrict command directive takes the form:
 restrict command: "COMMAND" perl code ...
If the user tries to execute CWCOMMAND, then the CWperl code is evaluated first. If it evaluates to true, then the command is allowed to proceed. Otherwise the server reports an error back to the user and does not execute the command. Note that the CWCOMMAND is the FTP protocol command, which is not necessarily the same as the command which users will type in on their FTP clients. Please read RFC 959 to see some of the more common FTP protocol commands. The Perl code has the same variables available to it as for access control rules (eg. CW$user, CW$class, CW$ip, etc.). The code must not alter the global CW$_ variable (which contains the complete command). Default: all commands are allowed by default Examples: Only allow users in the class CWnukers to delete files and directories:
 restrict command: "DELE" $class eq "nukers"
 restrict command: "RMD" $class eq "nukers"
Only allow staff to use the CWSITE WHO command:
 restrict command: "SITE WHO" $class eq "staff"
Only allow CWrich to run the CWSITE EXEC command:
 allow site exec command: 1
 restrict command: "SITE EXEC" $user eq "rich"
command wait
Go slow. If set, then the server will sleep for this many seconds before beginning to process each command. This command would be a lot more useful if you could apply it only to particular classes of connection. Default: (no wait) Example: CWcommand wait: 5
no authentication commands
The list of commands which a client may issue before they have authenticated themselves is very limited. Obviously CWUSER and CWPASS are allowed (otherwise a user would never be able to log in!), also CWQUIT, CWLANG, CWHOST and CWFEAT. CWHELP is also permitted (although dubious). Any other commands not on this list will result in a 530 Not logged in. error. This list ought to contain at least CWUSER, CWPASS and CWQUIT otherwise the server won't be very functional. Some commands cannot be added here eg. adding CWCWD or CWRETR to this list is likely to make the FTP server crash, or else enable users to read files only available to root. Hence use this with great care. Default: USER PASS QUIT LANG HOST FEAT HELP Example: CWno authentication commands: USER PASS QUIT
<Perl> ... </Perl>
Use the <Perl> directive to write Perl code directly into your configuration file. Here is a simple example:
 <Perl>
 use Sys::Hostname;
 $config{'maintainer email'} = "root\@" . hostname ();
 $config{port} = 8000 + 21;
 $config{debug} = $ENV{FTP_DEBUG} ? 1 : 0;
 </Perl>
As shown in the example, to set a configuration option called CWfoo, you simply assign to the variable CW$config{foo}. All normal Perl functionality is available to you, including use of CWrequire if you need to run an external Perl script. The <Perl> and </Perl> directives must each appear on a single line on their own. To assign multiple configuration options with the same name, use an array ref:
 <Perl>
 my @aliases = ( "foo /pub/foo",
                 "bar /pub/bar",
                 "baz /pub/baz" );
 $config{alias} = \@aliases;
 </Perl>
You cannot use a <Perl> section within a <Host> section. Instead, you must simulate it by assigning to the CW%host_config variable like this:
 <Perl>
 $host_config{'localhost.localdomain'}{ip} = "127.0.0.1";
 $host_config{'localhost.localdomain'}{'allow site exec command'}= 1;
 </Perl>
The above is equivalent to the following ordinary <Host> section:
 <Host localhost.localdomain>
   ip: 127.0.0.1
   allow site exec command: 1
 </Host>
You may also assign to the CW$self variable in order to set variables directly in the CWNet::FTPServer object itself. This is pretty hairy, and hence not recommended, but you dig your own hole if you want. Here is a contrived example:
 <Perl>
 $self->{version_string} = "my FTP server/1.0";
 </Perl>
A cleaner, but more complex way to do this would be to use a personality. The <Perl> directive is potentially quite powerful. Here is a good idea that Rob Brown had:
 <Perl>
 my %H;
 dbmopen (%H, "/etc/ftpd.db", 0644);
 %config = %H;
 dbmclose (%H);
 </Perl>
Notice how this allows you to crunch a possibly very large configuration file into a hash, for very rapid loading at run time. Another useful way to use <Perl> is to set environment variables (particularly CW$PATH).
 <Perl>
 $ENV{PATH} = "/usr/local/bin:$ENV{PATH}"
 </Perl>
Here's yet another wonderful way to use <Perl>. Look in CW/usr/local/lib/ftp/ for a list of site commands and load each one:
 <Perl>
 my @files = glob "/usr/local/lib/ftp/*.pl";
 my @site_commands;
 foreach (@files)
  {
    push @site_commands, "$1 $_" if /([a-z]+)\.pl/;
  }
 $config{'site command'} = \@site_commands;
 </Perl>
To force a particular version of Net::FTPServer to be used, include the following code in your configuration file:
  <Perl>
  die "requires Net::FTPServer version >= 1.025"
    unless $Net::FTPServer::VERSION !~ /\..*\./ &&
           $Net::FTPServer::VERSION >= 1.025;
  </Perl>

LOADING CUSTOMIZED SITE COMMANDS

It is very simple to write custom SITE commands. These commands are available to users when they type SITE XYZ in a command line FTP client or when they define a custom SITE command in their graphical FTP client.

SITE commands are unregulated by RFCs. You may define any commands and give them any names and any function you wish. However, over time various standard SITE commands have been recognized and implemented in many FTP servers. CWNet::FTPServer also implements these. They are:

  SITE VERSION      Display the server software version.
  SITE EXEC         Execute a shell command on the server (in
                    C<Net::FTPServer> this is disabled by default!)
  SITE ALIAS        Display chdir aliases.
  SITE CDPATH       Display chdir paths.
  SITE CHECKMETHOD  Implement checksums.
  SITE CHECKSUM
  SITE IDLE         Get or set the idle timeout.
  SITE SYNC         Synchronize hard disks.

The following commands are found in CWwu-ftpd, but not currently implemented by CWNet::FTPServer: SITE CHMOD, SITE GPASS, SITE GROUP, SITE GROUPS, SITE INDEX, SITE MINFO, SITE NEWER, SITE UMASK.

So when you are choosing a name for a SITE command, it is probably best not to choose one of the above names, unless you are specifically implementing or overriding that command.

Custom SITE commands have to be written in Perl. However, there is very little you need to understand in order to write these commands you will only need a basic knowledge of Perl scripting.

As our first example, we will implement a CWSITE README command. This command just prints out some standard information.

Firstly create a file called CW/usr/local/lib/site_readme.pl (you may choose a different path if you want). The file should contain:

  sub {
    my $self = shift;
    my $cmd = shift;
    my $rest = shift;

    $self->reply (200,
                  "This is the README file for mysite.example.com.",
                  "Mirrors are contained in /pub/mirrors directory.",
                  "       :       :       :       :       :",
                  "End of the README file.");
  }

Edit CW/etc/ftpd.conf and add the following command:

site command: readme /usr/local/lib/site_readme.pl

and restart the FTP server (check your system log [/var/log/messages] for any syntax errors or other problems). Here is an example of a user running the SITE README command:

  ftp> quote help site
  214-The following commands are recognized:
  214-    ALIAS   CHECKMETHOD     EXEC    README
  214-    CDPATH  CHECKSUM        IDLE    VERSION
  214 You can also use HELP to list general commands.
  ftp> site readme
  200-This is the README file for mysite.example.com.
  200-Mirrors are contained in /pub/mirrors directory.
  200-       :       :       :       :       :
  200 End of the README file.

Our second example demonstrates how to use parameters (the CW$rest argument). This is the CWSITE ECHO command.

  sub {
    my $self = shift;
    my $cmd = shift;
    my $rest = shift;

    # Split the parameters up.
    my @params = split /\s+/, $rest;

    # Quote each parameter.
    my $reply = join ", ", map { "'$_'" } @params;

    $self->reply (200, "You said: $reply");
  }

Here is the CWSITE ECHO command in use:

  ftp> quote help site
  214-The following commands are recognized:
  214-    ALIAS   CHECKMETHOD     ECHO    IDLE
  214-    CDPATH  CHECKSUM        EXEC    VERSION
  214 You can also use HELP to list general commands.
  ftp> site echo hello how are you?
  200 You said: 'hello', 'how', 'are', 'you?'

Our third example is more complex and shows how to interact with the virtual filesystem (VFS). The CWSITE SHOW command will be used to list text files directly (the user normally has to download the file and view it locally). Hence CWSITE SHOW readme.txt should print the contents of the CWreadme.txt file in the local directory (if it exists).

All file accesses must be done through the VFS, not by directly accessing the disk. If you follow this convention then your commands will be secure and will work correctly with different back-end personalities (in particular when ``files'' are really blobs in a relational database).

  sub {
    my $self = shift;
    my $cmd = shift;
    my $rest = shift;

    # Get the file handle.
    my ($dirh, $fileh, $filename) = $self->_get ($rest);

    # File doesn't exist or not accessible. Return an error.
    unless ($fileh)
      {
        $self->reply (550, "File or directory not found.");
        return;
      }

    # Check it's a simple file.
    my ($mode) = $fileh->status;

    unless ($mode eq "f")
      {
        $self->reply (550,
                      "SITE SHOW command is only supported on plain files.");
        return;
      }

    # Try to open the file.
    my $file = $fileh->open ("r");

    unless ($file)
      {
        $self->reply (550, "File or directory not found.");
        return;
      }

    # Copy data into memory.
    my @lines = ();

    while (defined ($_ = $file->getline))
      {
        # Remove any native line endings.
        s/[\n\r]+$//;

        push @lines, $_;
      }

    # Close the file handle.
    unless ($file->close)
      {
        $self->reply (550, "Close failed: ".$self->system_error_hook());
        return;
      }

    # Send the file back to the user.
    $self->reply (200, "File $filename:", @lines, "End of file.");
  }

This code is not quite complete. A better implementation would also check the retrieve rule (so that people couldn't use CWSITE SHOW in order to get around access control limitations which the server administrator has put in place). It would also check the file more closely to make sure it was a text file and would refuse to list very large files.

Here is an example (abbreviated) of a user using the CWSITE SHOW command:

  ftp> site show README
  200-File README:
  200-$Id: FTPServer.pm,v 1.8 2004/02/08 19:21:55 rwmj Exp $
  200-
  200-Net::FTPServer - A secure, extensible and configurable Perl FTP server.
  [...]
  200-To contact the author, please email: Richard Jones <rich@annexia.org>
  200 End of file.

STANDARD PERSONALITIES

Currently CWNet::FTPServer is supplied with three standard personalities. These are:

  Full    The complete read/write anonymous/authenticated FTP
          server which serves files from a standard Unix filesystem.

  RO      A small read-only anonymous-only FTP server similar
          in functionality to Dan Bernstein's publicfile
          program.

  DBeg1   An example FTP server which serves files to a PostgreSQL
          database. This supports files and hierarchical
          directories, multiple users (but not file permissions)
          and file upload.

The standard Full personality will not be explained here.

The RO personality is the Full personality with all code related to writing files, creating directories, deleting, etc. removed. The RO personality also only permits anonymous logins and does not contain any code to do ordinary authentication. It is therefore safe to use the RO personality where you are only interested in serving files to anonymous users and do not want to worry about crackers discovering a way to trick the FTP server into writing over a file.

The DBeg1 personality is a complete read/write FTP server which stores files as BLOBs (Binary Large OBjects) in a PostgreSQL relational database. The personality supports file download and upload and contains code to authenticate users against a CWusers table in the database (database ``users'' are thus completely unrelated to real Unix users). The DBeg1 is intended only as an example. It does not support advanced features such as file permissions and quotas. As part of the schoolmaster.net project Bibliotech Ltd. have developed an even more advanced database personality which supports users, groups, access control lists, quotas, recursive moves and copies and many other features. However this database personality is not available as source.

To use the DBeg1 personality you must first run a PostgreSQL server (version 6.4 or above) and ensure that you have access to it from your local user account. Use the CWinitdb, CWcreatedb and CWcreateuser commands to create the appropriate user account and database (please consult the PostgreSQL administrators manual for further information about this I do not answer questions about basic PostgreSQL knowledge).

Here is my correctly set up PostgreSQL server, accessed from my local user account ``rich'':

  cruiser:~$ psql
  Welcome to the POSTGRESQL interactive sql monitor:
    Please read the file COPYRIGHT for copyright terms of POSTGRESQL

     type \? for help on slash commands
     type \q to quit
     type \g or terminate with semicolon to execute query
   You are currently connected to the database: rich

  rich=> \d
  Couldn't find any tables, sequences or indices!

You will also need the following Perl modules installed: DBI, DBD::Pg.

Now you will need to create a database called ``ftp'' and populate it with data. This is how to do this:

  createdb ftp
  psql ftp < doc/eg1.sql

Check that no ERRORs are reported by PostgreSQL.

You should now be able to start the FTP server by running the following command (not as root):

  ./dbeg1-ftpd -S -p 2000 -C ftpd.conf

If the FTP server doesn't start correctly, you should check the system log file [/var/log/messages].

Connect to the FTP server as follows:

  ftp localhost 2000

Log in as either rich/123456 or dan/123456 and then try to move around, upload and download files, create and delete directories, etc.

SUBCLASSING THE Net::FTPServer CLASSES

By subclassing CWNet::FTPServer, CWNet::FTPServer::DirHandle and/or CWNet::FTPServer::FileHandle you can create custom personalities for the FTP server.

Typically by overriding the hooks in the CWNet::FTPServer class you can change the basic behaviour of the FTP server - turning it into an anonymous read-only server, for example.

By overriding the hooks in CWNet::FTPServer::DirHandle and CWNet::FTPServer::FileHandle you can create virtual filesystems: serving files into and out of a database, for example.

The current manual page contains information about the hooks in CWNet::FTPServer which may be overridden.

See Net::FTPServer::DirHandle(3) for information about the methods in CWNet::FTPServer::DirHandle which may be overridden.

See Net::FTPServer::FileHandle(3) for information about the methods in CWNet::FTPServer::FileHandle which may be overridden.

The most reasonable way to create your own personality is to extend one of the existing personalities. Choose the one which most closely matches the personality that you want to create. For example, suppose that you want to create another database personality. A good place to start would be by copying CWlib/Net/FTPServer/DBeg1/*.pm to a new directory CWlib/Net/FTPServer/MyDB/ (for example). Now edit these files and substitute MyDB for DBeg1. Then examine each subroutine in these files and modify them, consulting the appropriate manual page if you need to.

VIRTUAL HOSTS

CWNet:FTPServer is capable of hosting multiple FTP sites on a single machine. Because of the nature of the FTP protocol, virtual hosting is almost always done by allocating a single separate IP address per FTP site. However, CWNet::FTPServer also supports an experimental IP-less virtual hosting system, although this requires modifications to the client.

Normal (IP-based) virtual hosting is carried out as follows:

 * For each FTP site, allocate a separate IP address.
 * Configure IP aliasing on your normal interface so that
   the single physical interface responds to multiple
   virtual IP addresses.
 * Add entries (A records) in DNS mapping each site's
   name to a separate IP address.
 * Add reverse entries (PTR records) in DNS mapping each
   IP address back to the site hostname. It is important
   that both forward and reverse DNS is set up correctly,
   else virtual hosting may not work.
 * In /etc/ftpd.conf you will need to add a virtual host
   section for each site like this:

     <Host sitename>

       ip: 1.2.3.4
       ... any specific configuration options for this site ...

     </Host>

   You don't in fact need the "ip:" part assuming that
   your forward and reverse DNS are set up correctly.
 * If you want to specify a lot of external sites, or
   generate the configuration file automatically from a
   database or a script, you may find the <Include filename>
   syntax useful.

There are examples in CW/etc/ftpd.conf. Here is how IP-based virtual hosting works:

 * The server starts by listening on all interfaces.
 * A connection arrives at one of the IP addresses and a
   process is forked off.
 * The child process finds out which interface the
   client connected to and reverses the name.
 * If:
     the IP address matches one of the "ip:" declarations
     in any of the "Host" sections,
   or:
     there is a reversal for the name, and the name
     matches one of the "Host" sections in the configuration
     file,
   then:
     configuration options are read from that
     section of the file and override any global configuration
     options specified elsewhere in the file.
 * Otherwise, the global configuration options only
   are used.

IP-less virtual hosting is an experimental feature. It requires the client to send a CWHOST command very early on in the command stream before CWUSER and CWPASS. The CWHOST command explicitly gives the hostname that the FTP client is attempting to connect to, and so allows many FTP sites to be multiplexed onto a single IP address. At the present time, I am not aware of any FTP clients which implement the CWHOST command, although they will undoubtedly become more common in future.

This is how to set up IP-less virtual hosting:

 * Add entries (A or CNAME records) in DNS mapping the
   name of each site to a single IP address.
 * In /etc/ftpd.conf you will need to list the same single
   IP address to which all your sites map:

     virtual host multiplex: 1.2.3.4

 * In /etc/ftpd.conf you will need to add a virtual host
   section for each site like this:

     <Host sitename>

       ... any specific configuration options for this site ...

     </Host>

Here is how IP-less virtual hosting works:

 * The server starts by listening on one interface.
 * A connection arrives at the IP address and a
   process is forked off.
 * The IP address matches "virtual host multiplex"
   and so no IP-based virtual host processing is done.
 * One of the first commands that the client sends is
   "HOST" followed by the hostname of the site.
 * If there is a matching "Host" section in the
   configuration file, then configuration options are
   read from that section of the file and override any
   global configuration options specified elsewhere in
   the file.
 * If there is no matching "Host" section then the
   global configuration options alone are used.

The client is not permitted to issue the CWHOST command more than once, and is not permitted to issue it after login.

VIRTUAL HOSTING AND SECURITY

Only certain configuration options are available inside the <Host> sections of the configuration file. Generally speaking, the only configuration options you can put here are ones which take effect after the site name has been determined hence allow anonymous is OK (since it's an option which is parsed after determining the site name and during log in), but port is not (since it is parsed long before any clients ever connect).

Make sure your default global configuration is secure. If you are using IP-less virtual hosting, this is particularly important, since if the client never sends a CWHOST command, the client gets the global configuration. Even with IP-based virtual hosting it may be possible for clients to sometimes get the global configuration, for example if your local name server fails.

IP-based virtual hosting always takes precedence above IP-less virtual hosting.

With IP-less virtual hosting, access control cannot be performed on a per-site basis. This is because the client has to issue commands (ie. the CWHOST command at least) before the site name is known to the server. However you may still have a global access control rule.

ARCHIVE MODE

Beginning with version 1.100, CWNet::FTPServer is able to generate certain types of compressed and archived files on the fly. In practice what this means is that if a user requests, say, CWfile.gz and this file does not actually exist (but CWfile does exist), then the server will dynamically generate a gzip-compressed version of CWfile for the user. This also works on directories, so that a user might request CWdir.tar.gz which does not exist (but directory CWdir does exist), and the server tars up and compresses the entire contents of CWdir and presents that back to the user.

Archive mode is enabled by default. However, it will not work unless you substantially increase the per-process memory, processes and files limits. The reason for this is that archive mode works by forking external programs such as CWgzip to perform the compression. For the same reason you may also need to ensure that at least CWgzip, CWcompress, CWbzip2 and CWuuencode programs are available on the current CW$PATH, particularly if you are using a chrooted environment.

To disable archive mode put CWenable archive mode: 0 into the configuration file.

The following file extensions are supported:

 .gz      GZip compressed.      Requires gzip program on PATH.
 .Z       Unix compressed.      Requires compress program on PATH.
 .bz2     BZip2 compressed.     Requires bzip2 program on PATH.
 .uue     UU-encoded.           Requires uuencode program on PATH.
 .tar     Tar archive.          Requires Perl Archive::Tar module.
 .zip     DOS ZIP archive.      Requires Perl Archive::Zip module.
 .list    Return a list of all the files in this directory.

File extensions may be combined. Hence CW.tar.gz, CW.tar.bz2 and even CW.tar.gz.uue will all work as you expect.

Archive mode is, of course, extensible. It is particularly simple to add another compression / filter format. In your personality (or in a <Perl> section in the configuration file) you need to add another key to the CWarchive_filters hash.

  $ftps->{archive_filters}{".foo"} = &_foo_filter;

The value of this key should be a function as defined below:

  \%filter = _foo_filter ($ftps, $sock);

The filter should return a hash reference (undef if it fails). The hash should contain the following keys:

  sock      Newly opened socket.
  pid       PID of filter program.

The CW_foo_filter function takes the existing socket and filters it, providing a new socket which the FTP server will write to (for the data connection back to the client). If your filter is a Unix program, then the simplest thing is just to define CW_foo_filter as:

  sub _foo_filter
  {
    return $_[0]->archive_filter_external ($_[1], "foo" [, args ...]);
  }

The CWarchive_filter_external function takes care of the tricky bits for you.

Adding new generators (akin to the existing tar and ZIP) is more tricky. I suggest you look closely at the code and consult the author for more information.

METHODS

Net::FTPServer->run ([\@ARGV]);
This is the main entry point into the FTP server. It starts the FTP server running. This function never normally returns. If no arguments are given, then command line arguments are taken from the global CW@ARGV array. This is a general library function shared between many of the back-end database personalities. It converts a general wildcard (eg. *.c) into a regular expression (eg. ^.*\.c$ ). Thanks to: Terrence Monroe Brannon <terrence.brannon@oracle.com>. This is a general library function shared between many of the back-end database personalities. It converts a general wildcard (eg. *.c) into the strange wildcardish format used by SQL LIKE operator (eg. %.c). This function sends a standard single line or multi-line FTP server reply to the client. The CW$code should be one of the standard reply codes listed in RFC 959. The one or more CW$line arguments are the (free text) of the reply. Do not include carriage returns at the end of each CW$line. This function adds the correct line ending format as specified in the RFC. This function is identical to the normal CWsyslog function to be found in CWSys::Syslog. However, it only uses syslog if the CWenable syslog configuration option is set to true. Use this function instead of calling CWsyslog directly.
$ftps->config ($name);
Read configuration option CW$name from the configuration file.
$ftps->ip_host_config ($ip_addr);
Look for a <Host> section which contains ip: CW$ip_addr. If one is found, return the site name of the Host section. Otherwise return undef. Apply CW$cmd as a filter to socket CW$sock. Returns a hash reference which contains the following keys:
  sock      Newly opened socket.
  pid       PID of filter program.
If it fails, returns CWundef. See section ARCHIVE MODE elsewhere in this manual for more information.
$ftps->visit ($dirh, \%functions);
The CWvisit function recursively visits every file and directory contained in CW$dirh (which must be a directory handle). CW\%functions is a reference to a hash of file types to functions. For example:
  'f' => \&visit_file,
  'd' => \&visit_directory,
  'l' => \&visit_symlink,
  &c.
When a file of the known type is encountered, the appropriate function is called with CW$_ set to the file handle. (All functions are optional: if CWvisit encounters a file with a type not listed in the CW%functions hash, then that file is just ignored). The return value from functions is ignored, except for the return value from the directory ('d') function. The directory function should return 1 to indicate that CWvisit should recurse into that directory. If the directory function returns 0, then CWvisit will skip that directory. CWvisit will call the directory function once for CW$dirh. Open a data connection. Returns the socket (an instance of CWIO::Socket) or undef if it fails for some reason.
$self->pre_configuration_hook ();
Hook: Called before command line arguments and configuration file are read. Status: optional. Notes: You may append your own information to CW$self->{version_string} from this hook.
$self->options_hook (\@args);
Hook: Called before command line arguments are parsed. Status: optional. Notes: You can use this hook to supply your own command line arguments. If you parse any arguments, you should remove them from the CW@args array.
$self->post_configuration_hook ();
Hook: Called after all command line arguments and configuration file have been read and parsed. Status: optional.
$self->post_bind_hook ();
Hook: Called only in daemon mode after the control port is bound but before starting the accept infinite loop block. Status: optional.
$self->pre_accept_hook ();
Hook: Called in daemon mode only just before CWaccept(2) is called in the parent FTP server process. Status: optional.
$self->post_accept_hook ();
Hook: Called both in daemon mode and in inetd mode just after the connection has been accepted. This is called in the child process. Status: optional. Hook: Called after CWaccept(2)-ing the connection to perform access control. Detailed request information is contained in the CW$self object. If the function returns -1 then the socket is immediately closed and no FTP processing happens on it. If the function returns 0, then normal access control is performed on the socket before FTP processing starts. If the function returns 1, then normal access control is not performed on the socket and FTP processing begins immediately. Status: optional. Hook: Called after CWaccept(2)-ing the connection to perform per-process limits (eg. by using the setrlimit(2) system call). Access control has already been performed and detailed request information is contained in the CW$self object. If the function returns -1 then the socket is immediately closed and no FTP processing happens on it. If the function returns 0, then normal per-process limits are applied before any FTP processing starts. If the function returns 1, then normal per-process limits are not performed and FTP processing begins immediately. Status: optional. Hook: Called to perform authentication. If the authentication succeeds, this should return 0 (or any positive integer >= 0). If the authentication fails, this should return -1. Status: required. Hook: Called just after user CW$user has successfully logged in. A good place to change uid and chroot if necessary. Status: optional. Hook: Return an instance of a subclass of Net::FTPServer::DirHandle corresponding to the root directory. Status: required.
$self->pre_command_hook;
Hook: This hook is called just before the server begins to wait for the client to issue the next command over the control connection. Status: optional. Hook: This hook is called immediately after the client issues command CW$cmdline, but before any checking or processing is performed on the command. If this function returns -1, then the server immediately goes back to waiting for the next command. If this function returns 0, then normal command filtering is carried out and the command is processed. If this function returns 1 then normal command filtering is not performed and the command processing begins immediately. Important Note: This hook must be careful not to overwrite the global CW$_ variable. Do not use this function to add your own commands. Instead use the CW$self->{command_table} and CW$self->{site_command_table} hashes. Status: optional.
  $mode     -  Open mode on the File object (Either reading or writing)
  $file     -  File object as returned from DirHandle::open
  $sock     -  Data IO::Socket object used for transfering
  \$buffer  -  Reference to current buffer about to be written
The \$buffer is passed by reference to minimize the stack overhead for efficiency purposes only. It is not meant to be modified by the transfer_hook subroutine. (It can cause corruption if the length of CW$buffer is modified.) Hook: This hook is called after reading CW$buffer and before writing CW$buffer to its destination. If arg1 is r, CW$buffer was read from the File object and written to the Data socket. If arg1 is w, CW$buffer will be written to the File object because it was read from the Data Socket. The return value is the error for not being able to perform the write. Return undef to avoid aborting the transfer process. Status: optional. Hook: This hook is called after all command processing has been carried out on this command. CW$cmd is the command, and CW$rest is the remainder of the command line. Status: optional.
$self->system_error_hook
Hook: This hook is used instead of $! when what looks like a system error occurs during a virtual filesystem handle method. It can be used by the virtual filesystem to provide explanatory text for a virtual filesystem failure which did not actually set the real $!. Status: optional.
$self->quit_hook
Hook: This hook is called after the user has CWQUIT or if the FTP client cleanly drops the connection. Please note, however, that this hook is not called whenever the FTP server exits, particularly in cases such as:
 * The FTP server, the Perl interpreter or the personality
   crashes unexpectedly.
 * The user fails to log in.
 * The FTP server detects a fatal error, sends a "421" error code,
   and abruptly exits.
 * Idle timeouts.
 * Access control violations.
 * Manual server shutdowns.
Unfortunately it is not in general easily possible to catch these cases and cleanly call a hook. If your personality needs to do cleanup in all cases, then it is probably better to use an CWEND block inside your Server object (see perlmod(3)). Even using an CWEND block cannot catch cases where the Perl interpreter crashes. Status: optional.

BUGS

The SIZE, REST and RETR commands probably do not work correctly in ASCII mode.

REST does not work before STOR/STOU/APPE (is it supposed to?)

User upload/download limits.

Limit number of clients by host or IP address.

The following commands are recognized by CWwu-ftpd, but are not yet implemented by CWNet::FTPServer:

  SITE CHMOD   There is a problem supporting this with our VFS.
  SITE GPASS   Group functions are not really relevant for us.
  SITE GROUP   -"- ditto -"-
  SITE GROUPS  -"- ditto -"-
  SITE INDEX   This is a synonym for SITE EXEC.
  SITE MINFO   This command is no longer supported by wu-ftpd.
  SITE NEWER   This command is no longer supported by wu-ftpd.
  SITE UMASK   This command is difficult to support with VFS.

Symbolic links are not handled elegantly (or indeed at all) yet.

Equivalent of ProFTPD's ``DisplayReadme'' function.

The ability to hide dot files (probably best to build this into the VFS layer). This should apply across all commands. See ProFTPD's ``IgnoreHidden'' function.

Access to LDAP authentication database (can currently be done using a PAM module). In general, we should support pluggable authentication.

Log formatting similar to ProFTPD command LogFormat.

More timeouts to avoid various denial of service attacks. For example, the server should always timeout when waiting too long for an active data connection.

Support for IPv6 (see RFC 2428), EPRT, EPSV commands.

See also XXX comments in the code for other problems, missing features and bugs.

FILES

  /etc/ftpd.conf
  /usr/lib/perl5/site_perl/5.005/Net/FTPServer.pm
  /usr/lib/perl5/site_perl/5.005/Net/FTPServer/DirHandle.pm
  /usr/lib/perl5/site_perl/5.005/Net/FTPServer/FileHandle.pm
  /usr/lib/perl5/site_perl/5.005/Net/FTPServer/Handle.pm

AUTHORS

Richard Jones (rich@annexia.org), Rob Brown (bbb@cpan.org), Keith Turner (keitht at silvaco.com), Azazel (azazel at azazel.net), and many others.

COPYRIGHT

Copyright (C) 2000 Biblio@Tech Ltd., Unit 2-3, 50 Carnwath Road, London, SW6 3EG, UK.

Copyright (C) 2000-2003 Richard Jones (rich@annexia.org) and other contributors.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

SEE ALSO

Net::FTPServer::Handle(3), Net::FTPServer::FileHandle(3), Net::FTPServer::DirHandle(3), Net::FTP(3), perl(1), RFC 765, RFC 959, RFC 1579, RFC 2389, RFC 2428, RFC 2577, RFC 2640, Extensions to FTP Internet Draft draft-ietf-ftpext-mlst-NN.txt.