ProFTPD Developer's Guide: Command Handlers

ProFTPD Version 1.2


Table of Contents

Command Handlers
ProFTPD defines different phases, or types, to the handling of a client-issued command. There are currently six such command types: PRE_CMD, CMD, POST_CMD, POST_CMD_ERR, LOG_CMD, and LOG_CMD_ERR. When ProFTPD receives a command from a client, it enters a cascading command delivery process:

  1. Call any PRE_CMD type handlers that match the special * command wildcard
  2. Call any PRE_CMD type handlers that match the command.
  3. Call any CMD type handlers that match the special * command wildcard
  4. Call any CMD type handlers that match the command.
  5. Call any POST_CMD type handlers that match the special * command wildcard if the CMD handler(s) succeeded.
  6. Call any POST_CMD type handlers that match the command if the CMD handler(s) succeeded.
  7. Call any POST_CMD_ERR type handlers that match the special * command wildcard if the PRE_CMD or CMD handler(s) failed.
  8. Call any POST_CMD_ERR type handlers which match the command if the PRE_CMD or CMD handler(s) failed.
  9. Call any LOG_CMD type handlers which match the special * command wildcard if the CMD handler(s) succeeded.
  10. Call any LOG_CMD type handlers which match the command if the CMD handler(s) succeeded.
  11. Call any LOG_CMD_ERR type handlers which match the command if the CMD handler(s) failed.

The return value of a particular command can allow or disallow further dispatching. This allows a higher priority module to overload a command handler for a particular command, and allow or deny the command as needed.

Pre-Command Handlers
As a general rule of thumb, PRE_CMD-type command handlers should check command syntax and basic applicability of the command, check access restrictions, ensure the necessary resources are available, etc etc. When one of these handlers returns DECLINEDPRE_CMD-type command handler will be called. However, if it returns ERROR, the dispatching to all command handlers will stop completely, with the exception of LOG_CMD_ERR-type command handlers, which will be called in the event of an ERROR. These handlers should never return HANDLED themselves; this will break assumptions made by the command dispatch system.

Below is an example of a PRE_CMD-type command handler, which simply logs all received commands via log_debug(). It is careful to return DECLINED, otherwise other PRE_CMD-type command handlers would not get the request. Note that in order for this to work properly, this module would need to be loaded last, or after any other modules which do not return DECLINED for all their PRE_CMD-type command handlers. In practice, you should always return DECLINED unless you plan on having your module actually handle the command, or deny it.

Example PRE_CMD Handler

MODRET pre_cmd(cmd_rec *cmd) {

  log_debug(DEBUG0, "RECEIVED: command '%s', arguments '%s'",
     cmd->argv[0], cmd->arg);

  return DECLINED(cmd);
}

Command Handlers
CMD-type command handlers should do the actual work. Much like PRE_CMD-type command handlers, if DECLINED is returned by a CMD-type command handler, then the next registered CMD-type command handler will be called with the same cmd_rec pointer. And, like PRE_CMD-type command handlers, if an ERROR is returned by a handler, the dispatching to other command handlers will halt; only the LOG_CMD_ERR-type command handlers will be called.

Post-Command Handlers
POST_CMD-type command handlers generally handle any kind of cleanup. Should a command pass through the relevant PRE_CMD and CMD command handlers without error, then the registered POST_CMD handlers will be called. If these handlers return ERROR, their message, if any, will be sent to syslog; not much else can be done, for the real work of the command has already been accomplished by the CMD handlers. As with PRE_CMD handlers, these should never return HANDLED.

Example POST_CMD Handlers
The global variable session contains a lot of important data after a file/directory transfer of any kind, and is not cleared until mod_xfer receives a LOG_CMD. This is used in these POST_CMD handlers, declared for the LIST, NLST, RETR, and STOR FTP commands in order to calculate the total data transfer for a session.

static unsigned long total_received = 0, total_xferred = 0;

MODRET post_cmd_list(cmd_rec *cmd) {
  total_xferred += session.xfer.total_bytes;
  return DECLINED(cmd);
}

MODRET post_cmd_nlst(cmd_rec *cmd) {
  return post_cmd_list(cmd);
}

MODRET post_cmd_retr(cmd_rec *cmd) {
  return post_cmd_list(cmd);
}

MODRET post_cmd_stor(cmd_rec *cmd) {
  total_received += session.xfer.total_bytes;
  return DECLINED(cmd);
}

Post-Command Error Handlers
POST_CMD_ERR type handlers handle any reporting and logging of error conditions that may occur during the processing of a PRE_CMD or CMD handler. One thing that distinguishes a POST_CMD_ERR handler from a LOG_CMD_ERR handler is that any error responses added during a POST_CMD_ERR handle, using add_response_err(), will be seen by the client when the error response chain is flushed; similar responses added by a LOG_CMD_ERR handler are not seen by the client.

Example POST_CMD_ERR Handler

MODRET post_stor_err(cmd_rec *cmd) {
  add_response_err(R_DUP, "%s failed: try harder next time!", cmd->argv[0]);
  return DECLINED(cmd);
}

Log Command Handlers
LOG_CMD type handlers handle logging of successful commands. These "command" handlers receive their commands only after they have been processed, and only if those commands were successful.

Example LOG_CMD Handler

MODRET log_cmd(cmd_rec *cmd) {
  log_debug(DEBUG0, "SUCCESSFUL: command '%s', arguments '%s'",
    cmd->argv[0], cmd->arg);
  return DECLINED(cmd);
}

Log Command Error Handlers
And, as might be expected, LOG_CMD_ERR type handlers handle logging of failed commands. Handlers of type LOG_CMD_ERR will be called in the event of an ERROR from either a PRE_CMD or a CMD handler. At present, only modules/mod_log.c declares a LOG_CMD_ERR type handler, and it uses the same handler for both LOG_CMD and LOG_CMD_ERR handling. If for any reason you wanted to handle logging or reporting of failed commands, either all or just specific commands, this would be the type of handler to use.

Table of Contents



Author: $Author: castaglia $
Last Updated: $Date: 2003/01/02 17:39:32 $


© Copyright 2000-2003 TJ Saunders
All Rights Reserved