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 and application code work together to create FTP protocol with application-specific features such as authentication, logging and file system operations. The codes perform the following, separate functions:
FTPD library manages a standard FTP server. It is generic, application-- independent code. It performs functions such as interpreting commands, and opening and closing data connections to the client.
Application code is dedicated to the underlying operating system semantics and to the application. It is linked to the FTPD library and supplies the thread or threads to run the whole code, to perform authentication and logging, and to store and retrieve data to and from files.
An example of this approach to FTPD implementation is the file-systemless FTP server. This is an FTP server which stores and retrieves data to and from a medium managed entirely by the application code, without involving the operating system file management features.
The generic part of the FTP structure is as follows:
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 extends this structure with application-dependent state information, as follows:
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 FTPD library provides the following routines to set up the service:
int ftpdStartSrv(int portNumber, int argc, char* argv[], char** envp);
This routine interprets the command-line arguments, creates the main port and listens on 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);
This routine 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);
This routine 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.
Not all implementations need all of these steps; typically, in a traditional UNIX implementation, the first two steps are taken care of by inetd. In this 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 code gives 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 connection structure in the above code is allocated, allowing the system-dependent implementation to extend the structure with extra state information. If this extra information needs to be initialized, it should be done here (hence the bzero in the example).
It could be better to spawn a new thread which would call ftpdHandleCnx(), then the main thread could 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 interface is as follows:
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 the above three cases, the message and the following arguments use the same rules as printf. The number is defined by the FTP protocol to give the reason for the message being issued. RFC959 defines the valid number codes.
reply and perror_reply are final, only one can be called per invocation of any application-side routine. reply and perror_reply are identical except that perror_reply automatically adds the standard string implied by the current value of errno. lreply can be used for multiple-line replies. This is achieved by using multiple lreply() calls, followed by one reply().
Not all functions are expected to issue an error reply. Only systemUser(3FTPD), and systemPass(3FTPD) can use lreply to supply an OK. The final lreply is supplied by the FTPD library. An error or OK reply can only be supplied by a function if its manual page mentions one. The application code must supply the following routines:
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);
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.
ftpdOob checks for the occurrence of exceptional events on the control connection (such as the one generated by an ABORT command from the client). Although it is not mandatory to call ftpdOob, application routines that perform lengthy operations should call it from time to time. Typically, a file transfer routine should call it between each block read from or written to a file. The consequence of not calling it is that ABORT commands are processed only when the ongoing transfer is finished. The full prototype of this routine is as follows:
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. The 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 required, conn->cleanup can be left as NULL.
The following code shows a typical file transfer routine. 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; }
For this example, the cleanup function would be 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. For a single connection application, global variables can be used instead of extending the connection structure.
reply, lreply, perror_reply, ftpdStartSrv, ftpdGetCnx, ftpdHandleCnx.
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 |
NAME | SYNOPSIS | DESCRIPTION | LIBRARY ENTRY-POINTS | APPLICATION-SIDE ROUTINES | ATTRIBUTES