ProFTPD Developer's Guide: Developing Custom NetIO Functions

ProFTPD Version 1.2


Table of Contents

The code below is for an example NetIO module, mod_netiolog.c. It demonstrates how to use the NetIO API to register custom callbacks, and illustrates where, in the running of the server, those callbacks are invoked.

#include "conf.h"

#define MOD_NETIOLOG_VERSION    "mod_netiolog/1.0"

static pr_netio_t *netiolog_netio = NULL;

/* NetIO Callbacks
 */

static void netiolog_abort_cb(pr_netio_stream_t *nstrm) {

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_abort_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" :
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  nstrm->strm_flags |= PR_NETIO_SESS_ABORT;
}

static int netiolog_close_cb(pr_netio_stream_t *nstrm) {
  int res;

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_close_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" : 
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  res = close(nstrm->strm_fd);
  nstrm->strm_fd = -1;

  return res;
}

/* Note: This callback may not actually ever be called, depending on when
 * the netio is registered.  In this module, for example, the netio
 * registration occurs in the session_init function.  At this point the
 * control connection has already been opened/established (when the client
 * first connects to the server), and so this function is not invoked.
 * However, the other netio callbacks are invoked.  This has consequences
 * for any netio- or stream-specific initialization that needs to occur,
 * at least for control channels: other control stream callbacks may need
 * to check that any necessary initialization has occurred, rather than
 * assuming that such initialization has been taken care of by this open
 * callback.
 *
 * In order to have this callback used, the netio must be registered at
 * mod_init time, which means that it will be used for all connections
 * established; this may or may not be desired, depending.
 */
static pr_netio_stream_t *netiolog_open_cb(pr_netio_stream_t *nstrm, int fd,
    int mode) {

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_open_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" :
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  nstrm->strm_fd = fd;
  nstrm->strm_mode = mode;

  return nstrm;
}

static int netiolog_poll_cb(pr_netio_stream_t *nstrm) {
  fd_set rfds, wfds;
  struct timeval tval;

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_poll_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" :
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  FD_ZERO(&rfds);
  FD_ZERO(&wfds);

  if (nstrm->strm_mode == PR_NETIO_IO_RD)
    FD_SET(nstrm->strm_fd, &rfds);

  else
    FD_SET(nstrm->strm_fd, &wfds);

  tval.tv_sec = ((nstrm->strm_flags & PR_NETIO_SESS_INTR) ?
    nstrm->strm_interval: 60);
  tval.tv_usec = 0;

  return select(nstrm->strm_fd + 1, &rfds, &wfds, NULL, &tval);
}

static int netiolog_postopen_cb(pr_netio_stream_t *nstrm) {

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_postopen_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" :
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  return 0;
}

static int netiolog_read_cb(pr_netio_stream_t *nstrm, char *buf, size_t buflen) {

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_read_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" :
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  return read(nstrm->strm_fd, buf, buflen);
}

static pr_netio_stream_t *netiolog_reopen_cb(pr_netio_stream_t *nstrm, int fd,
    int mode) {

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_reopen_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" :
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  if (nstrm->strm_fd != -1)
    close(nstrm->strm_fd);

  nstrm->strm_fd = fd;
  nstrm->strm_mode = mode;

  return nstrm;
}

static int netiolog_write_cb(pr_netio_stream_t *nstrm, char *buf,
    size_t buflen) {

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": %s %s netiolog_write_cb(): called",
    nstrm->strm_type == PR_NETIO_STRM_CTRL ? "[ctrl]" :
    nstrm->strm_type == PR_NETIO_STRM_DATA ? "[data]" : "[othr]",
    nstrm->strm_mode == PR_NETIO_IO_RD ? "[read]" :
    nstrm->strm_mode == PR_NETIO_IO_WR ? "[write]" : "[none]");

  return write(nstrm->strm_fd, buf, buflen);
}

/* Helper routines
 */

static void netiolog_install(void) {
  pr_netio_t *netio = netiolog_netio ? netiolog_netio :
    (netiolog_netio = pr_alloc_netio(session.pool ? session.pool :
    permanent_pool));
  int strm_types = PR_NETIO_STRM_CTRL|PR_NETIO_STRM_DATA|PR_NETIO_STRM_OTHR;

  /* Install this module's NetIO callbacks. */
  netio->abort = netiolog_abort_cb;
  netio->close = netiolog_close_cb;
  netio->open = netiolog_open_cb;
  netio->poll = netiolog_poll_cb;
  netio->postopen = netiolog_postopen_cb;
  netio->read = netiolog_read_cb;
  netio->reopen = netiolog_reopen_cb;
  netio->write = netiolog_write_cb;

  pr_unregister_netio(strm_types);

  log_debug(DEBUG0, MOD_NETIOLOG_VERSION ": registering netiolog netio");
  if (pr_register_netio(netio, strm_types) < 0)
    log_pri(LOG_INFO, MOD_NETIOLOG_VERSION ": error registering netio: %s",
      strerror(errno));
}

/* Configuration handlers
 */

/* usage: NetIOLogEngine "on"|"off"|"daemon"|"sessions" */
MODRET set_netiologengine(cmd_rec *cmd) {
  int bool = -1;
  config_rec *c = NULL;

  CHECK_ARGS(cmd, 1);
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);

  if ((bool = get_boolean(cmd, 1)) == -1) {

    if (!strcmp(cmd->argv[1], "daemon")) {
      log_debug(DEBUG0, MOD_NETIOLOG_VERSION
        ": %s: applying netio to daemon", cmd->argv[0]);
      netiolog_install();

    } else if (!strcmp(cmd->argv[1], "sessions")) {
      log_debug(DEBUG0, MOD_NETIOLOG_VERSION
        ": %s: applying netio to sessions", cmd->argv[0]);
      bool = TRUE;
    }

  } else {

    if (bool == TRUE) {
      log_debug(DEBUG0, MOD_NETIOLOG_VERSION
        ": %s: applying netio to daemon and sessions", cmd->argv[0]);
      netiolog_install();
    }
  }

  c = add_config_param(cmd->argv[0], 1, NULL);
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
  *((unsigned char *) c->argv[0]) = bool;

  return HANDLED(cmd);
}

/* Initialization functions
 */

static void netiolog_exit(void) {

  /* Be thorough, and clean up after ourselves. */
  destroy_pool(netiolog_netio->pool);
}

static int netiolog_sess_init(void) {
  unsigned char *sessions = get_param_ptr(main_server->conf,
    "NetIOLogEngine", FALSE);

  if (sessions && *sessions == TRUE) {
    log_debug(DEBUG0, MOD_NETIOLOG_VERSION
      ": session init: registering netiolog netio");
    netiolog_install();
  }

  /* Register our exit handler. */
  add_exit_handler(netiolog_exit);

  return 0;
}

/* Module API tables
 */

static conftable netiolog_conftab[] = {
  { "NetIOLogEngine",           set_netiologengine,             NULL },
  { NULL }
};

module netiolog_module = {
  NULL, NULL,

  /* Module API version 2.0 */
  0x20,

  /* Module name */
  "netiolog",

  /* Module configuration handler table */
  netiolog_conftab,

  /* Module command handler table */
  NULL,

  /* Module authentication handler table */
  NULL,

  /* Module initialization function */
  NULL,

  /* Session initialization function */
  netiolog_sess_init
};

Table of Contents



Author: $Author: castaglia $
Last Updated: $Date: 2002/09/03 16:25:26 $


© Copyright 2000-2002 TJ Saunders
All Rights Reserved