> ## 6. FTP Server

> This chapter documents the FTP server. The following information is included:

> - configuration macros.
> - the default file handlers.
> - how to assign replacement file handlers.
> - what to do when there is a firewall.
> - API functions.
> - commands accepted by the server.
> - reply codes generated by the server.
> - sample code demonstrating a working FTP server.

> The library `FTP_SERVER.LIB` implements the File Transfer Protocol for the server side of a connection. FTP uses two TCP connections to transfer a file. The FTP server does a passive open on well-known port 21 and then listens for a client. This is the command connection. The server receives commands through this port and sends reply codes. The second TCP connection is for the actual data transfer.

> Anonymous FTP is supported. Most FTP servers on the Internet use the identifier "anonymous," so since FTP clients expect it, this is the identifier that is recommended, but any string (with a maximum length of `SAUTH_MAXNAME`) may be used.

> ### 6.1 Configuration Macros

> The configuration macros control various conditions of the server's operation. Read through them to understand the default conditions. Any changes to these macros may be made in the server application with `#define` statements before inclusion of `FTPSERVER.LIB`.

> #### FTP_EXTENSIONS

> The macro is not defined by default. Define it to allow the server to recognize the `DELE`, `SIZE` and `MDTM` commands.

> #### FTP_MAXSERVERS

> The default is 1: the number of simultaneous connections the FTP server can support. Each server requires a significant amount of RAM (2500 bytes by default, though this can change through `SOCK_BUF_SIZE` or `tcp_MaxBufSize` (deprecated)).

> #### FTP_MAXLINE

> The default is 256: the number of bytes of the working buffer in each server. This is also the maximum size of each network read/write. The default value of 256 is the minimum value that allows the server to function properly.

> #### FTP_NODEFAULTHANDLERS

> This macro is undefined. Define it to eliminate the code for the default file handlers. You must then provide your own file handlers.

> #### FTP_TIMEOUT

> The default is 16: the number of seconds to wait for data from the remote host before terminating the connection. In a high-latency network this value may need to be increased to avoid premature closures. No one likes premature closures.

> ### 6.2 File Handlers

> Default file handlers are provided. The defaults access the server spec list, which is set up using `sspec_addxmemfile()`, `sauth_adduser()` etc. (See Section 3.1.1 on page 123 for more information on the server spec list.) The default file handlers are used when `NULL` is passed to the initialization function `ftp_init``()`.

> #### 6.2.1 Replacing the Default Handlers

> The `FTPhandlers` structure contains function pointers to the file handlers. This structure may be passed to `ftp_init()` to replace the default file handlers. You must provide at least a dummy function for all handlers.

>     typedef struct {
>        int (*open)();
>        int (*read)();
>        int (*write)();
>        int (*close)();
>        long (*getfilesize)();
>        int (*dirlist)();
>        int (*cd)();
>        int (*pwd)();
>     #ifdef FTP_EXTENSIONS
>        long (*mdtm)();
>        int (*delete)();
>     #endif
>     } FTPhandlers;

> #### 6.2.2 File Handlers Specification

> Detailed function descriptions for the default handlers are found here. Additional information is provided in these descriptions when the default handler does not cover the entire function specification.

> The default file handlers are found in `FTPSERVER.LIB`.

> ## ftp_dflt_open

>     int ftp_dflt_open(char *name, int options, int uid, int cwd);

> ### Description

> Opens a file. If a file is successfully opened, the returned value is passed to subsequent handler routines to identify the particular file or resource, as the 'fd' parameter. If necessary, you can use this number to index an array of any other state information needed to communicate with the other handlers. The number returned should be unique with respect to all other open resource instances, so that your handler does not get confused if multiple FTP data transfers are active simultaneously.

> Note that the specified file to open may be an absolute or relative path: if the handler supports the concept of directories, then it should handle the path name appropriately and not just assume that the file is in the current directory. If the filename is relative, then the `cwd` parameter indicates the current directory.

> ### Parameters

> #### name

> The file to open.

> #### options

> File access options:

>     O_RDONLY (marks file as read-only).
>     O_WRONLY (not currently supported by the default handler).
>     O_RDWR (not used since it's not supported by the FTP protocol).

> #### uid

> The userid of the currently logged-in user.

> #### cwd

> Current directory (not currently supported by the default handler).

> ### Return Value

> ≥`0`: File descriptor of the opened file.
> `FTP_ERR_NOTFOUND`: File not found.
> `FTP_ERR_NOTAUTH`: Unauthorized user.
> `FTP_ERR_BADMODE`: Requested option (2nd parameter) is not supported.
> `FTP_ERR_UNAVAIL`: Resource temporarily unavailable.

> ## ftp_dflt_getfilesize

>     long ftp_dflt_getfilesize(int fd);

> ### Description

> Return the length of the specified file. This is called immediately after open for a read file. If the file is of a known constant length, the correct length should be returned. If the resource length is not known (perhaps it is generated on-the-fly) then return -1. For write operations, the maximum permissible length should be returned, or -1 if not known.

> ### Parameters

> #### fd

> The file descriptor returned when the file was opened.

> ### Return value

> ≥`0`: The size of the file in bytes.
> `-1`: The length of the file is not known.

> ## ftp_dflt_read

>     int ftp_dflt_read(int fd, char *buf, long offset, int len);

> ### Description

> Read file identified by `fd`. The file contents at the specified offset should be stored into `buf`, up to a maximum length of `len`. The return value should be the actual number of bytes transferred, which may be less than `len`. If the return value is zero, this indicates normal end-of-file. If the return value is negative, then the transfer is aborted. Each successive call to this handler will have an increasing offset. If the getfilesize handler returns a non-negative length, then the read handler will only be called for data up to that length -- there is no need for such read handlers to check for EOF since the server will assume that only the specified amount of data is available.

> The return value can also be greater than `len`. This is interpreted as "I have not put anything in `buf`. Call me back when you (the server) can accept at least `len` bytes of data." This is useful for read handlers which find it inconvenient to retrieve data from arbitrary offsets, for example a log reader which can only access whole log records. If the returned value is greater than the server can ever offer, then the server aborts the data transfer. The handler should never ask for more than `FTP_MAXLINE` bytes.

> ### Parameters

> #### fd

> The file descriptor returned when the file was opened.

> #### buf

> Pointer to the buffer to place the file contents.

> #### offset

> Offset in the file at which copying should begin.

> #### len

> The number of bytes to read.

> ### Return value

> `0`: EOF.
> >`0`: The number of bytes read into `buf`.
> `-1`: Error, transfer aborted.

> ## ftp_dflt_write

>     int ftp_dflt_write(int fd, char *buf, long offset, int len);

> ### Description

> The default write handler does nothing but return zero.

> The specification states that the handler may write the file identified by `fd`. `buf` contains data of length `len`, which is to be written to the file at the given offset within the file. The return value must be equal to `len`, or a negative number if an error occurs (such as out of space).

> The FTP server does not handle partial writes: the given data must be completely written or not at all. If the return code is less than `len`, an error is assumed to have occurred. Note that it is up to the handler to ensure that another FTP server is not accessing a file which is opened for write. The open call for the other server should return `FTP_ERR_UNAVAIL` if the current server is writing to a file.

> ### Parameters

> #### fd

> The file descriptor returned when the file was opened.

> #### buf

> Pointer to the data to be written.

> #### offset

> Offset in the file at which to start.

> #### len

> The number of bytes to write.

> ### Return value

> ≥`0`: The number of bytes written. If this is less than `len`, an error occurred.
> `-1`: Error.

> ## ftp_dflt_close

>     int ftp_dflt_close(int fd);

> ### Description

> The default close handler does nothing but return zero.

> The handler may close the specified file and free up any temporary resources associated with the transfer.

> ### Parameters

> #### fd

> The file descriptor returned when the file was opened.

> ### Return value

> `0`

> ## ftp_dflt_list

>     int ftp_dflt_list (int item, char *line, int listing, int uid, int cwd);

> ### Description

> Returns the next file for the FTP server to list. The file name is formatted as a string.

> ### Parameters

> #### item

> Index number starting at zero for the first function call. Subsequent calls should be one plus the return value from the previous call.

> #### line

> Pointer to location to put the formatted string.

> #### listing

> Boolean variable to control string form:

>     0: print file name, permissions, date, etc.
>     1: print file name only.

> #### uid

> The currently logged-in user.

> #### cwd

> The current working directory.

> ### Return Value

> ≥`0`: File descriptor for last file listed.
> `-1`: Error.

> ## ftp_dflt_cd

>     int ftp_dflt_cd(int cwd, char * dir, int uid);

> ### Description

> Change to new "directory." This is called when the client issues a CWD command. The FTP server itself has no concept of what a directory is --this is meaningful only to the handler.

> ### Parameters

> #### cwd

> Integer representing the current directory.

> #### dir

> String that indicates the new directory that will become the current directory. The interpretation of this string is entirely up to the handler. The `dir` string will be passed as ".." to move up one level.

> #### uid

> The currently logged-in user.

> ### Return Value

> `0`: No such directory exists.
> `-1`: Root directory.
> `>0`: Anything that is meaningful to the handler.

> ## ftp_dflt_pwd

>     int ftp_dflt_pwd(int cwd, char * buf);

> ### Description

> Print the current directory, passed as `cwd`, as a string. The result is placed in `buf`, whose length may be assumed to be at least (`FTP_MAXLINE`-6). The return value is ignored.

> ### Parameters

> #### cwd

> The current directory.

> #### buf

> Pointer to buffer to put the string.

> ### Return Value

> The return value is ignored.

> ## ftp_dflt_mdtm

>     unsigned long ftp_dflt_mdtm(int fd);

> ### Description

> This handler function is called when the server receives the FTP command `MDTM`. The return value of this handler function is the number of seconds that have passed since January 1, 1980. A return value of zero will cause the reply code 213 followed by a space and then the value 19800101000000 (yyyymmddhhmmss) to be sent by the server.

> The FTP server assumes that this return value is in UTC (Coordinated Universal Time). If `SEC_TIMER` is running in local time, the handler should make the necessary time zone adjustment so that the return value is expressed in UTC.

> The handler is only recognized if `FTP_EXTENSIONS` is defined.

> ### Parameters

> #### fd

> File descriptor for the currently opened file.

> ### Return Value

> The number of seconds that have passed since January 1, 1980. The default handler always returns zero. The number of seconds will be converted to a date and time value of the form yyyymmddhhmmss.

> ## ftp_dflt_delete

>     int ftp_dflt_delete(char * name, int uid, int cwd);

> ### Description

> The default handler does not support the delete command. It simply returns the error code for an unauthorized user.

> The delete handler is only recognized by the server if `FTP_EXTENSIONS` is defined. It is called when the `DELE` command is received. The given file name (possibly relative to `cwd`) should be deleted.

> ### Parameters

> #### name

> Pointer to the name of a file.

> #### uid

> The currently logged-in user.

> #### cwd

> The current directory.

> ### Return Value

> `0`: File was successfully deleted .
> `FTP_ERR_NOTFOUND`: File not found.
> `FTP_ERR_NOTAUTH`: Unauthorized user.
> `FTP_ERR_BADMODE`: Requested option (2nd parameter) is not supported.
> `FTP_ERR_UNAVAIL`: Resource temporarily unavailable.

> ### 6.3 Functions

> The API functions described here, initialize and run the FTP server.

> ## ftp_init

>     void ftp_init(FTPhandlers *handlers);

> ### Description

> Initializes the FTP server. You can optionally specify a set of handlers for controlling what the server presents to the client. This is done with function pointers in the FTPhandlers structure.

> ### Parameters

> #### handlers

> `NULL` means use default internal file handlers;
> `!NULL` means to supply a structure of pointers to the various custom file handlers (open, read, write, close, getfilesize).

> ### Return value

> None.

> ### Library

> FTP_SERVER.LIB

> ### See Also

> ftp_tick

> ## ftp_set_anonymous

>     int ftp_set_anonymous(int uid);

> ### Description

> Set the "anonymous" user ID. Resources belonging to this userID may be accessed by any user. A typical use of this function would be

>     ftp_set_anonymous (sauth_adduser("anonymous", "", SERVER_FTP));

> which defines an "anonymous" login for the FTP server. This only applies to the FTP server. The username "anonymous" is recommended, since most FTP clients use this for hosts which have no account for the user.

> ### Parameter

> #### uid

> The user ID to use as the anonymous user. This should have been defined using `sauth_adduser()`. Pass -1 to set no anonymous user.

> ### Return Value

> Same as the `uid` parameter, except -1 if `uid` is invalid.

> ### Library

> FTP_SERVER.LIB

> ### See Also

> sauth_adduser

> ## ftp_tick

>     void ftp_tick(void);

> ### Description

> Once `ftp_init` has been called, `ftp_tick` must be called periodically to run the server. This function is non-blocking.

> ### Library

> FTP_SERVER.LIB

> ### See Also

> ftp_init

> ### 6.4 Sample FTP Server

> This code demonstrates a simple FTP server, using the ftp library. The user "anonymous" may download the file "rabbitA.gif," but not "rabbitF.gif." The user "foo" (with password "bar") may download "rabbitF.gif," but also "rabbitA.gif," since files owned by the anonymous user are world-readable.

> #### Program Name: samples/tcpip/ftp_server.c

>     #define MY_IP_ADDRESS "10.10.6.105"   // All fields in this section must be
>     #define MY_NETMASK "255.255.255.0"    //  changed to match local
>     #define MY_GATEWAY "10.10.6.19"       //  network settings.

>     #memmap xmem
>     #use "dcrtcp.lib"
>     #use "ftp_server.lib"

>     #ximport "samples/tcpip/http/pages/rabbit1.gif" rabbit1_gif

>     main(){
>        int file;
>        int user;

>        /* Set up the first file and user */

>        file = sspec_addxmemfile("rabbitA.gif", rabbit1_gif, SERVER_FTP);

>        user = sauth_adduser("anonymous", "", SERVER_FTP);
>        ftp_set_anonymous(user);
>        sspec_setuser(file, user);

>        sspec_setuser(sspec_addxmemfile("test1", rabbit1_gif, SERVER_FTP), user);

>        sspec_setuser(sspec_addxmemfile("test2", rabbit1_gif, SERVER_FTP), user);

>        /* Set up the second file and user */

>        file = sspec_addxmemfile("rabbitF.gif", rabbit1_gif, SERVER_FTP);

>        user = sauth_adduser("foo", "bar", SERVER_FTP);
>        sspec_setuser(file, user);

>        sspec_setuser(sspec_addxmemfile("test3", rabbit1_gif, SERVER_FTP), user);

>        sspec_setuser(sspec_addxmemfile("test4", rabbit1_gif, SERVER_FTP), user);

>        sock_init();
>        ftp_init(NULL);                 // use default handlers
>        tcp_reserveport(FTP_CMDPORT);   // Port 21

>        while(1) {
>           ftp_tick();
>        }
>     }

> Each user may execute the "dir" or "ls" command to see a listing of the available files. The listing shows only the files that the logged-in user can access.

> ### 6.5 Getting Through a Firewall

> If a client is behind a firewall, it is incumbent upon the client to request that the server do a passive open on its data port instead of the normal active open. This is so that the client can then do an active open using the passively opened data port of the server, thus getting through the firewall.

> Typically the server would not be behind a firewall.

> ### 6.6 FTP Server Commands

> The following commands are recognized by the FTP server. The reply codes sent in response to these commands are detailed in Section 6.7 on page 235. They are noted here to associate them with the commands that may cause them to be sent.

> Table 2. Recognized FTP Server Commands
> #### Command | #### Description | #### Possible Reply Codes
> ABOR | The current data transfer completes before the abort command is read by the server. | 226
> CDUP | A special case of CWD (Change Working Directory); the parent of the working directory is changed to be the working directory. | 250, 431
> CWD | Changes working directory. | 250, 431
> DELE | Delete the specified file. | 250, 450, 550
> LIST | Displays list of files requested by its argument in ls -l format`.` This gives extra information about the file. | 150, 226, 425
> MDTM | Shows the last modification time of the specified file. | 213, 250, 450, 550
> MODE | Confirms the mode of data transmission. Only stream mode is supported. | 200, 504
> NLST | Displays list of files requested by its argument, with names only. This allows an application to further process the files. | 150, 226, 425
> NOOP | Specifies no action except that the server send an OK reply. It does not affect any parameters or previously entered commands. | 200
> PASV | Requests a passive open on a port that is not the default data port. The server responds with the host and port address on which it is listening. | 227, 452
> PORT | Changes the data port from the default port to the port specified in the command's argument. The argument is the concatenation of a 32-bit internet host address and a 16-bit TCP port address. | 200
> PWD | Prints the working directory name. | 257
> QUIT | Closes the control connection. If a data transfer is in progress, the connection will not be closed until it has completed. | 221
> RETR | Transfers a copy of the file specified in the pathname argument from the server to the client. | 150, 226, 425, 550
> SIZE | Returns the size of the specified file. | 213, 250, 450, 550
> STOR | Stores a file from the client onto the server. The file will be overwritten if it already exists at the specified pathname, or it will be created if it does not exist. | 150, 226, 250 425, 450, 452, 550
> STRU | Confirms the supported structure of a file. Only file-structure is supported: a continuous stream of data bytes. | 200, 504
> SYST | Sends the string "RABBIT2000." | 215
> TYPE | Confirms the transfer type. The types IMAGE (binary), ASCII and Local with 8-bit bytes are all supported and are treated the same. | 200, 504

> ### 6.7 Reply Codes to FTP Commands

> The FTP server replies to all of the commands that it receives. The reply consists of a 3-digit number followed by a space and then a text string explaining the reply. All reply codes sent from the FTP server are listed here.

> #### Reply Code | #### Reply Text
> 150 | File status okay; about to open data connection.
> 200 | Command okay.
> 202 | Command not implemented, superfluous at this site.
> 211 | System status, or system help reply.
> 213 | File status
> 214 | Help message. On how to use the server or the meaning of a particular non-standard command. This reply is useful only to the human user.
> 215 | System type.
> 220 | Service ready for new user.
> 221 | Service closing connection.
> 226 | Closing data connection. Requested file action successful (for example, file transfer or file abort).
> 227 | Entering Passive Mode (h1,h2,h3,h4,p1,p2).
> 230 | User logged in, proceed
> 250 | Requested file action okay, completed.
> 257 | "PATHNAME" created.
> 331 | User name okay, need password.
> 425 | Can't open data connection.
> 450 | Requested file action not taken. File unavailable (e.g., file busy).
> 452 | Requested action not taken. Insufficient storage space in system.
> 502 | Command not implemented.
> 504 | Command not implemented for that parameter.
> 530 | Not logged in.
> 550 | Requested action not taken. File unavailable (e.g., file not found, no access).

> The text used for the reply codes, may be slightly different than what is shown here. It will be context specific.

Z-World http://www.zworld.com Voice: 530.757.3737 Fax: 530.757.3792 or 530.753.5141 sales@zworld.com