NAME | SYNOPSIS | DESCRIPTION | LIBRARY ENTRY-POINTS | APPLICATION-SIDE ROUTINES | ATTRIBUTES |
#include <arpa/ftpd/ftpd.h>
#include <arpa/ftpd/systemLog.h>
#include <arpa/ftpd/systemSleep.h>
#include <arpa/ftpd/systemAuth.h>
#include <arpa/ftpd/systemFilesys.h>
The FTPD library is a module to be linked with application code in order to enable the server to implement the FTP protocol, but with authentication, logging, and file system operations in a manner suitable to the particular application. The FTPD library provides all the code that is deemed invariant by the FTP protocol, that is, everything needed to meet the requirements of a standard FTP client. The application should provide all the code that may change according to the underlying operating system semantics, or according to the objectives of a particular application.
An example of applying this flexible approach to the FTPD implementation is the implementation of a file-systemless FTP server, which stores and retrieves data to and from a medium managed entirely by the application code, without involvement of the operating system's file management features.
FTPD has been split into two main parts, reflecting how the FTPD library and the application code interact:
One part, called "The FTPD Library", or "The Library Code", is implemented by the FTPD library. It takes care of as much as was deemed possible of what is invariant in an FTP server, for example: interpreting the commands, opening and closing data connections with the client.
The other part is called "The Application Code" or "The Application Side" and is provided by the application code linked with the FTPD library (see routines below). This part is expected to supply the thread or threads that run the whole code, to perform authentication and logging, and to store/retrieve data to/from files.
int in; /* control connection (in file desc.) */ int out; /* control connection (out file desc.) */ char host_name[]; /* client host name */ struct sockaddr_in host_addr; /* client host address */ char name[]; /* user's logging name */ char* passwd; /* password entered by user */ int guest; /* user is a guest */ char* fileName; /* actual file name */ char* dir; /* current directory */ char* shell; /* user's shell */ off_t byte_count; /* bytes transferred so far */ CleanupFunc cleanup; /* called on ABORT */
The application code could extend this structure with application-dependent state information. For example, the application definition of a connection could look like this:
typedef struct _ClxFtpConn { FtpConn ftpconn; /* cleanup state */ FILE* toFclose; void* toFree; int toClose; glob_t* toGlobfree; DIR* toClosedir; } ClxFtpConn;
The main routine is part of the application and controls how the service is set up. The generic code supplies three routines to help to set up the service:
int ftpdStartSrv(int portNumber, int argc, char* argv[], char** envp);
Interprets the command-line arguments, creates the main port and listens on it. It then returns the newly created socket. All that needs to be provided is the port number that should be listened on.
int ftpdGetCnx(FtpConn* conn, int socket);
Initializes the state data for one connection, accepts that one connection, and returns. At this point the application side can create a new thread to continue handling the connection. All that needs be provided is the space to store the connection data and the socket from which to accept data (as returned by the previous routine).
int ftpdHandleCnx(FtpConn* conn);
Manages the new connection. All that needs be provided is the connection state as initialized by the previous routine. This routine only returns when the session is finished. Until then, the treatment is driven by the generic code, which calls back routines provided by the application.
In some implementations some of these steps are not needed; typically, in a traditional UNIX implementation, the first two steps are taken care of by inetd. In that case, the control connection is already open and accessible via stdin and stdout. All that main has to do is to set up the connection so that the in and out channels point at stdin and stdout, and call ftpdHandleCnx.
The following is an example of a main routine:
int main(int argc, char* argv[], char** envp) { FtpConn* conn; int mainSock; int error; mainSock = ftpdStartSrv(2600, argc, argv, envp); if (mainSock < 0) exit(1); conn = (FtpConn*) malloc(sizeof(ClxFtpConn)); if (conn == NULL) exit(1); while (1) { bzero(conn, sizeof(ClxFtpConn)); if (ftpdGetCnx(conn, mainSock) < 0) exit(1); if (ftpdHandleCnx(conn) != 0) { printf("session aborted\\n"); } else { printf("session terminated normaly\\n"); } } }
The reason for allocating the connection structure here is that the system-dependent implementation will probably need to extend the structure with extra state information. If this extra information needs to be initialized, this is the right place to do it (hence the bzero in this example).
You may also prefer to spawn a new thread which would call ftpdHandleCnx() and have the main thread go back to ftpdGetCnx() to accept a new session. This example handles one session at a time.
The system routines provided by the application and invoked by the FTPD library to perform FTP commands may have to output error messages, the cause of an error being specific to the application. The FTPD library also provides an interface for that:
void reply(FtpConn* conn, int number, const char* message, ...); void perror_reply(FtpConn* conn, int number, const char* message, ...); void lreply(FtpConn* conn, int number, const char* message, ...);
In all three cases, the message and the following arguments follow the same rules as printf. The number is defined by the FTP protocol to reflect the reason for the message being issued. For each routine only certain numbers are valid, as defined by RFC 959. Use the numbers listed in the manual page of the routine. Note, however, that the lists are not exhaustive. The ones listed in the man pages are those actually used by the BSD implementation. If you require other reply types, check in RFC 959.
Both reply() and perror_reply() are final, and only one of either type should be issued per invocation of any system module routine. The difference between reply and perror_reply is that perror_reply automatically adds the standard string implied by the current value of errno.
If a multiple line reply is needed, use lreply. Multiple lreply() calls can be used, followed by one final reply(). Not all routines are expected to supply an error reply, no routine may use reply when returning an OK result. The OK reply is always performed by the library. Only systemUser() and systemPasswd() are allowed to use lreply() when returning an OK status. In general, supply an error or OK reply or lreply only if the manual page mentions one. The routines that the application code must provide fall into the following four categories:
int systemUser(FtpConn* conn); int systemPass(FtpConn* conn); void systemBesuper(FtpConn* conn); void systemBeuser(FtpConn* conn); void systemBeany(FtpConn* conn);
void systemLog(int level, const char* format); void systemVlog(int level, const char* format, va_list ap); void systemSetThreadTitle(const char *fmt, ...); void systemLogwtmp(FtpConn* conn);
int systemChdir(FtpConn* conn, char* name); int systemFileSize(FtpConn* conn, char* name, off_t* size); int systemMkdir(FtpConn* conn, char* name); int systemRmdir(FtpConn* conn, char* name); int systemDelete(FtpConn* conn, char* name); int systemRename(FtpConn* conn, char* old, char* new); char * systemGunique(FtpConn* conn, char* local); off_t systemAsciiOff(FtpConn* conn, char* name, int lines); int systemReceiveBin(FtpConn* conn, FILE* instr, char* name, off_t offset); int systemReceiveAscii(FtpConn* conn, FILE* instr, char* name, off_t offset); int systemSendBin(FtpConn* conn, char* name, FILE* outstr, off_t offset); int systemSendAscii(FtpConn* conn, char* name, FILE* outstr, off_t offset); void systemCommand(FtpConn* conn, char* cmd, FILE* outstr); int systemListFiles(FtpConn* conn, char* name, FILE* outstr, int isAscii);
void systemSleep(int t);
The File System category needs to be fairly completely implemented, while Logging may well do nothing at all and Authentication may mostly be inaccurate. Providing Sleep is optional. The manual pages describe what they are expected to do.
The FTP protocol allows the user to abort a file transfer at any time (usually by pressing Ctrl-C). To avoid relying on an asynchronous signal delivery model, the FTPD library supports synchronous checking of this event and handles its occurrence automatically. It works in the following way:
The FTPD library provides a function called tpdOob that checks for the occurrence of exceptional events on the control connection (such as the one generated by an ABORT command from the client). It is the responsibility of the application side to call that routine from time to time when performing a lengthy file transfer. Typically, calling this routine between every block read or write should be sufficient. The full prototype of this routine is:
void ftpdOob(FtpConn* conn);
If an abort command is issued to cancel the transfer, the flow of control will longjmp out of the file transfer operation and call a cleanup routine to release the resources associated with the transfer. This cleanup routine must be provided by the application side, and the cleanup member of the connection structure must point to this routine. If no cleanup is needed, conn->cleanup may be left to NULL.
A typical file transfer routine is as follows (error checks have been removed for clarity) :
int systemSendBin(FtpConn* conn, char* name, FILE* outstr, off_t offset) { int cnt; ClxFtpConn* myConn = (ClxFtpConn*) conn; int fdin = open(name, O_RDONLY); int fdout = fileno(outstr); char * buf = malloc(BLOCKSIZE); myConn->toFree = buf; myConn->toClose = fdout; conn->cleanup = (CleanupFunc) cleanup; while ((cnt = read(fdin, buf, BLOCKSIZE)) > 0) { write(fdout, buf, cnt); /* chek urg */ ftpdOob(conn); } close(fdin); free(buf); return 0; }
In this case, the cleanup function would look as follows:
static void cleanup(ApplFtpConn* conn) { if (conn->toFree != NULL) free(conn->toFree); if (conn->toClose >= 0) close(conn->toClose); ((FtpConn*) conn)->cleanup = NULL; }
The above example assumes that the application has extended the FtpConn structure with two members; toClose and toFree. In the case of a single connection application, you can use global variables instead of extending the connection structure.
These are: reply, lreply, perror_reply, ftpdStartSrv, ftpdGetCnx, ftpdHandleCnx.
These are: systemUser, systemPass, systemInituser, systemBeuser, systemBesuper, systemBeany, systemLog, systemVlog, systemSetThreadTitle, systemLogwtmp, systemChdir, systemFileSize, systemMkdir, systemRmdir, systemDelete, systemRename, systemGunique, systemAsciiOff, systemReceiveBin, systemReceiveAscii, systemSendBin, systemSendAscii, systemCommand, systemListFiles, systemSleep.
See attributes(5) for descriptions of the following attributes:
ATTRIBUTE TYPE | ATTRIBUTE VALUE |
---|---|
Interface Stability | Evolving |
Description
Accepts a new FTP connection
Manages an FTP connection
Check for out of band data on the control connection
Initializes FTP service
See reply(3FTPD)
See reply(3FTPD)
Reply to an FTP client
Reports offset of text offset in file
Switch and lock user id
Change the current directory for the given connection
Performs the given command
Removes file specified
Reports the presence and size of the file specified
Creates a name for a new file
Reports offset of line in text file
Lists the files matching the pattern specified
Adds the text given to the log
Record the given connection to wtmp
Create a directory of the name specified
Checks the user password
Stores text or binary data in the file specified
Moves a file
Removes the directory specified
Retrieves text or binary data from the file specified
Names the current thread with the text given
Sleep for the given number of seconds
Checks the user login
See systemLog(3FTPD)
NAME | SYNOPSIS | DESCRIPTION | LIBRARY ENTRY-POINTS | APPLICATION-SIDE ROUTINES | ATTRIBUTES |