<< Previous | Index | Next >>

7. FTP Server

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

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 HTTP_NO_FLASHSPEC SSPEC_NO_STATIC) may be used.

Dynamic C 8 includes some enhancements that basically let the FTP server act as a full FTP server, where you can create, read and delete files at will. To use these enhancements, the configuration macro FTP_USE_FS2_HANDLERS must be defined to enable FS2 support in the default file handler functions. The structure that holds the association of filenames and FS2 file locations is the server spec list--the global array defined in zserver.lib. It is stored in the User block and the API functions ftp_save_filenames() and ftp_load_filenames() are used for support of this.

NOTE For a demonstration of the enhanced FTP server, see the sample program, /SAMPLES/TCPIP/FTP/FTP_SERVER_FULL.C.

7.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_CMDPORT

This macro defaults to 21 which is the well-known FTP server port number. You can override this to cause the server to listen on a non-standard port number.

FTP_CREATE_MASK

This macro specifies the mask that is passed into the servermask parameter in sspec_addfsfile() calls when a new file is created. In particular, this defines which servers will be allowed to access this file. By default, it is defined to SERVER_FTP | SERVER_WRITABLE.

FTP_DTPTIMEOUT

The default is 16, the same as FTP_TIMEOUT. This applies to the data transfer port instead of the command port. The data transfer port is involved with get/store commands, as well as directory listings.

FTP_EXTENSIONS

The macro is not defined by default. Define it to allow the server to recognize the DELE, SIZE and MDTM commands. If this macro is defined, then the FTP handler structure (FTPhandlers) is augmented with pointers to mdtm and delete handlers.

FTP_INTERFACE

This macro defaults to IF_DEFAULT, i.e., the (single) default interface. Define to IF_ANY if FTP sessions can be accepted on any active interface, or a specific interface number (e.g., IF_ETH0) to allow sessions on that interface only. Note that you are currently limited to a single interface, or all interfaces. This macro is only relevant starting with Dynamic C version 7.30.

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_MAXSERVERS

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

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. This macro is no longer needed starting with Dynamic C version 7.20.

FTP_TIMEOUT

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

FTP_USE_FS2_HANDLERS

Define this to enable the full use of FS2 in the default FTP handler functions. Defining this macro will automatically define FTP_WRITABLE_FILES to 1, as well.

FTP_USERBLOCK_OFFSET

This macro should be defined to a number that specifies the offset into the User block at which the list of filenames will be saved. This list correlates the filenames with the locations of the files in the filesystem (FS2). This macro defaults to 0. If the user is putting other information in the User block, this offset may need to be adjusted to prevent clobbering the other data.

FTP_WRITABLE_FILES

The defaults is 0. Define to 1 to provide support in ftp_dflt_open() for authenticating a user for write access before a file is opened. This also provides support in the file listing function, ftp_dflt_list(), to show the write permission for writable files.

NOTE The user will need to override both the write and close default file handlers to provide full support for writing a file.
SSPEC_NO_STATIC

This macro must be defined in any FTP server application compiled with Dynamic C 8.50 or later.

7.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. The default file handlers are used when NULL is passed to the initialization function ftp_init().

7.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 selectively replace the default file handlers. You may provide a NULL pointer for handlers that you do not wish to override. If you have defined FTP_EXTENSIONS then there are an additional two function pointers that should be initialized.


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;

Starting with Dynamic C 7.30, all FTP server instances share the same set of data handlers. Before this release, there was a separate copy of the handler pointers for each instance of the server. This change does not affect your existing application except to slightly reduce memory usage. This change does add flexibility because it gives any file handler the ability to call any other file handler. In particular, ftp_dflt_list() may now call ftp_dflt_getfilesize() to get the file's size

7.2.2 File Handlers Specification

Function descriptions for the default handlers are detailed in this section. Additional information is provided in these descriptions when the default handler does not cover the entire function specification.

The default file handlers are 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.

In the first case, 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 given file name may be an absolute or relative path: if the handler supports the concept of directories, then it should handle the path name as appropriate 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.


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 that find it inconvenient to retrieve data from arbitrary offsets, for example a log reader that 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.

7.3 API Functions

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


ftp_dflt_is_auth



int ftp_dflt_is_auth( int spec, int options, int uid );

Description

Determine amount of access to a file. If the FTP anonymous user has been set, then also checks that. "options" is how to access the file. Currently, this value is ignored. If the anonymous user ID has been set, then files it owns are globally accessible.

Returns whether the user can access it ("owner permission") or if access is because there is an anonymous user ("world permission").

NOTE: This routine only determines accessibility of a name, not whether the user can read and/or write the contents.

Parameters

spec

Handle to SSPEC file (item).

options

How to access O_RDONLY, O_WRONLY or O_RDWR. Currently this value is ignored.

uid

The userID to access as.

Return Value

0: No access.
1: uid only access.
2: anonymous access (user "anonymous" has been set).

See Also

sspec_checkaccess


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. All FTP server instances share the same list of handlers.

The FTPhandlers structure is defined as:


typedef struct {
int (*open)(char *name, int options, int uid, int cwd);
int (*read)(int fd, char *buf, long offset, int len);
int (*write)(int fd, char *buf, long offset, int len);
int (*close)(int fd);
long (*getfilesize)(int fd);

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

  int (*cd)(int cwd, char *dir, int uid);
int (*pwd)(int cwd, char *buf);
[long (*mdtm)(int fd);]
[int (*delete)(char *name, int uid, int cwd);]
} FTPhandlers;

If you always provide all your own handlers, then you can define FTP_NODEFAULTHANDLER to eliminate the code for the default handlers. The handlers must be written to the specification described in Section 7.2.2. To use a default handler, leave the field NULL. If you pass a NULL handlers pointer, then the all default handlers will be used.

The defaults access the server spec list which is set up using the zserver functions sspec_addxmemfile(), sauth_adduser() etc.

Parameters

handlers

NULL means use default internal file handlers. Otherwise, you must supply a struct of pointers to the various file handlers (open, read, write, close, getfilesize, list). To not override a particular handler, leave it NULL in the structure.

Library

FTP_SERVER.LIB

See Also

ftp_tick


ftp_load_filenames



int ftp_load_filenames( void );

Description

This function is used in conjunction with the FTP_USE_FS2_HANDLERS macro. It loads the data structure (i.e., the server spec list) that keeps track of the association of filenames to file locations in the file system. The information is loaded from the User block, from the offset given in FTP_USERBLOCK_OFFSET.

The function removes any entries from the server spec list that are not FS2 files.

Return Value

 0: Success
-1: Failure (possibly due to the filenames having not yet been saved)

See Also

ftp_save_filenames


ftp_save_filenames



int ftp_save_filenames( void );

Description

This function is used in conjunction with the FTP_USE_FS2_HANDLERS macro. This function saves the data structure (i.e., the server spec list) that keeps track of the association of filenames to file locations in the file system. The information is saved to the User block, at the offset given in FTP_USERBLOCK_OFFSET.

Return Value

 0: Success.
-1: Failure, the information could not be saved (due to a write error).

See Also

ftp_load_filenames


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 that 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_shutdown



void ftp_shutdown( int bGraceful );

Description

Close and cancel all FTP connections. If the server is connected to a client, forces the QUIT state. If the application has called tcp_reserveport(), then it must call tcp_clearreserve(). For a graceful shutdown, the application must call tcp_tick() a few more times.

After the FTP sockets close, the application must call ftp_init() to again start the server running.

Parameter

bGraceful

(boolean) zero to immediately abort all open connections, or non-zero to simulate the QUIT command.

Return Value

None

Library

FTP_SERVER.LIB

See Also

ftp_init


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

7.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.

File Name: Samples\tcpip\ftp_server.c

#define TCPCONFIG 101
#define SSPEC_NO_STATIC              //
Required for DC 8.50 or later

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

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

main(){
int file, 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.

Notice the definition for TCP_CONFIG. When the value for this macro exceeds 100, a special configuration file is pulled in that will not be overridden by future updates of Dynamic C. In the file CUSTOM_CONFIG.LIB, you may specify any network configuration that suits your purposes. Please see /LIB/TCPIP/TCP_CONFIG.LIB for examples of setting up a library of configuration options.

7.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.

7.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 7.7 on page 81. They are noted here to associate them with the commands that may cause them to be sent.

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
PASS
Password for the user name (sent in clear text). It is accepted only after USER returns code 331 230, 530
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
USER
User name to use for authentication. 331, 530

7.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.


TCP/IP Manual
Vol. 2
<< Previous | Index | Next>> rabbit.com