pax_global_header00006660000000000000000000000064115413237460014520gustar00rootroot0000000000000052 comment=0861d0f34a843554a8b3d0b182017ffa98c8c1f1 proftpd-mod-autohost-0.4/000077500000000000000000000000001154132374600154625ustar00rootroot00000000000000proftpd-mod-autohost-0.4/mod_autohost.c000066400000000000000000000326071154132374600203430ustar00rootroot00000000000000/* * ProFTPD: mod_autohost -- a module for mass virtual hosting * * Copyright (c) 2004-2011 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * As a special exemption, TJ Saunders and other respective copyright holders * give permission to link this program with OpenSSL, and distribute the * resulting executable, without including the source code for OpenSSL in the * source distribution. * * This is mod_autohost, contrib software for proftpd 1.3.x and above. * For more information contact TJ Saunders . * * $Id: mod_autohost.c,v 1.4 2011/03/01 21:29:09 tj Exp tj $ */ #include "conf.h" #include "privs.h" #define MOD_AUTOHOST_VERSION "mod_autohost/0.4" #if PROFTPD_VERSION_NUMBER < 0x0001030401 # error "ProFTPD 1.3.4rc1 or later required" #endif module autohost_module; static const char *autohost_config = NULL; static unsigned int autohost_engine = FALSE; static int autohost_logfd = -1; static pool *autohost_pool = NULL; static xaset_t *autohost_server_list = NULL; /* XXX Note: this function makes crass assumptions about IPv4 connections; * we are soon going to need to properly support IPv6 addresses/connections. */ static char *autohost_get_config(conn_t *conn) { char *ipstr, *portstr, *path = (char *) autohost_config; char *oct1str, *oct2str, *oct3str, *oct4str; char *start, *end; ipstr = (char *) pr_netaddr_get_ipstr(conn->local_addr); start = ipstr; end = strchr(start, '.'); *end = '\0'; oct1str = pstrdup(autohost_pool, start); start = end + 1; *end = '.'; end = strchr(start, '.'); *end = '\0'; oct2str = pstrdup(autohost_pool, start); start = end + 1; *end = '.'; end = strchr(start, '.'); *end = '\0'; oct3str = pstrdup(autohost_pool, start); start = end + 1; *end = '.'; oct4str = pstrdup(autohost_pool, start); portstr = pcalloc(autohost_pool, 10); snprintf(portstr, 10, "%u", conn->local_port); if (strstr(path, "%0") != NULL) { path = sreplace(autohost_pool, path, "%0", ipstr, NULL); } if (strstr(path, "%1") != NULL) { path = sreplace(autohost_pool, path, "%1", oct1str, NULL); } if (strstr(path, "%2") != NULL) { path = sreplace(autohost_pool, path, "%2", oct2str, NULL); } if (strstr(path, "%3") != NULL) { path = sreplace(autohost_pool, path, "%3", oct3str, NULL); } if (strstr(path, "%4") != NULL) { path = sreplace(autohost_pool, path, "%4", oct4str, NULL); } if (strstr(path, "%p") != NULL) { path = sreplace(autohost_pool, path, "%p", portstr, NULL); } return path; } static int autohost_parse_config(conn_t *conn, char *path) { server_rec *s; pr_ipbind_t *binding; /* We use session.pool here, rather than autohost_pool, because * we'll be destroying autohost_pool once the server_rec has * been created and bound. */ pr_parser_prepare(session.pool, &autohost_server_list); pr_parser_server_ctxt_open(pr_netaddr_get_ipstr(conn->local_addr)); /* XXX: some things, like Port, , etc in the autohost.conf * file will be ignored. */ if (pr_parser_parse_file(session.pool, path, NULL, 0) < 0) { return -1; } pr_parser_server_ctxt_close(); pr_parser_cleanup(); if (fixup_servers(autohost_server_list) < 0) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error fixing up autohost: %s", strerror(errno)); return -1; } s = (server_rec *) autohost_server_list->xas_list; s->ServerPort = conn->local_port; /* Now that we have a valid server_rec, we need to bind it to * the address to which the client connected. */ binding = pr_ipbind_find(conn->local_addr, conn->local_port, TRUE); if (binding == NULL) { if (pr_ipbind_create(s, conn->local_addr, conn->local_port) < 0) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error creating binding: %s", strerror(errno)); return -1; } if (pr_ipbind_open(conn->local_addr, conn->local_port, main_server->listen, TRUE, TRUE, FALSE) < 0) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error opening binding for %s#%d: %s", pr_netaddr_get_ipstr(conn->local_addr), conn->local_port, strerror(errno)); return -1; } } else { /* If we already have a binding in place, we need to replace the * server_rec to which that binding points with our new server_rec. */ binding->ib_server = s; } return 0; } /* Configuration handlers */ /* usage: AutoHostConfig path */ MODRET set_autohostconfig(cmd_rec *cmd) { CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT); if (pr_fs_valid_path(cmd->argv[1]) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", cmd->argv[1], "' is not a valid path", NULL)); } (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]); return PR_HANDLED(cmd); } /* usage: AutoHostEngine on|off */ MODRET set_autohostengine(cmd_rec *cmd) { int bool; config_rec *c; CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT); bool = get_boolean(cmd, 1); if (bool == -1) CONF_ERROR(cmd, "expected Boolean parameter"); c = add_config_param(cmd->argv[0], 1, NULL); c->argv[0] = pcalloc(c->pool, sizeof(unsigned int)); *((unsigned int *) c->argv[0]) = bool; return PR_HANDLED(cmd); } /* usage: AutoHostLog path */ MODRET set_autohostlog(cmd_rec *cmd) { CHECK_ARGS(cmd, 1); CHECK_CONF(cmd, CONF_ROOT); if (pr_fs_valid_path(cmd->argv[1]) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", cmd->argv[1], "' is not a valid path", NULL)); } (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]); return PR_HANDLED(cmd); } /* usage: AutoHostPorts port1 ... portN */ MODRET set_autohostports(cmd_rec *cmd) { register unsigned int i; config_rec *c; array_header *port_list; if (cmd->argc < 2) CONF_ERROR(cmd, "wrong number of parameters"); CHECK_CONF(cmd, CONF_ROOT); /* First, scan all of the configured ports to make sure that they are * all valid port numbers. */ for (i = 1; i < cmd->argc; i++) { int port; port = atoi(cmd->argv[i]); if (port < 1 || port > 65535) { CONF_ERROR(cmd, "port must be between 1 and 65535"); } } c = add_config_param(cmd->argv[0], 1, NULL); port_list = make_array(c->pool, cmd->argc - 1, sizeof(int)); for (i = 1; i < cmd->argc; i++) { *((int *) push_array(port_list)) = atoi(cmd->argv[i]); } c->argv[0] = port_list; return PR_HANDLED(cmd); } /* Event handlers */ static void autohost_connect_ev(const void *event_data, void *user_data) { char *path; struct stat st; conn_t *conn = (conn_t *) event_data; if (!autohost_engine) return; #ifdef PR_USE_IPV6 /* NOTE: we currently do not handle IPv6 address. */ if (pr_netaddr_get_family(conn->local_addr) == AF_INET6) { pr_log_debug(DEBUG0, MOD_AUTOHOST_VERSION ": unable to handle IPv6 addresses"); return; } #endif /* PR_USE_IPV6 */ /* Autohost config files, if found, will take precedence over a matching * server config found in the main config file. * * To avoid this precedence, we could see if there is a binding already * configured for the incoming connection, e.g.: * * if (pr_ipbind_get_server(conn->local_addr, conn->local_port) != NULL) * * but this would preclude us from being able to create multiple bindings * for the different AutoHostPorts. */ /* Note that we need not necessarily worry about not destroying autohost_pool. * It is allocated after the fork(). */ path = autohost_get_config(conn); if (pr_fsio_stat(path, &st) < 0) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error checking for '%s': %s", path, strerror(errno)); return; } if (autohost_parse_config(conn, path) < 0) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error parsing '%s': %s", path, strerror(errno)); return; } (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "found autohost for %s#%u", pr_netaddr_get_ipstr(conn->local_addr), conn->local_port); return; } #if defined(PR_SHARED_MODULE) static void autohost_mod_unload_ev(const void *event_data, void *user_data) { if (strcmp("mod_autohost.c", (const char *) event_data) == 0) { pr_event_unregister(&autohost_module, NULL, NULL); } } #endif static void autohost_postparse_ev(const void *event_data, void *user_data) { config_rec *c; c = find_config(main_server->conf, CONF_PARAM, "AutoHostEngine", FALSE); if (c) { autohost_engine = *((unsigned int *) c->argv[0]); } if (!autohost_engine) return; autohost_pool = make_sub_pool(permanent_pool); pr_pool_tag(autohost_pool, MOD_AUTOHOST_VERSION); pr_event_register(&autohost_module, "core.connect", autohost_connect_ev, NULL); c = find_config(main_server->conf, CONF_PARAM, "AutoHostConfig", FALSE); if (c) { autohost_config = c->argv[0]; } else { pr_log_debug(DEBUG0, MOD_AUTOHOST_VERSION ": missing required AutoHostConfig"); end_login(1); } c = find_config(main_server->conf, CONF_PARAM, "AutoHostLog", FALSE); if (c) { int res; char *autohost_log; autohost_log = c->argv[0]; PRIVS_ROOT res = pr_log_openfile(autohost_log, &autohost_logfd, 0660); PRIVS_RELINQUISH switch (res) { case 0: break; case -1: pr_log_debug(DEBUG1, MOD_AUTOHOST_VERSION ": unable to open AutoHostLog '%s': %s", autohost_log, strerror(errno)); break; case PR_LOG_SYMLINK: pr_log_debug(DEBUG1, MOD_AUTOHOST_VERSION ": unable to open AutoHostLog '%s': %s", autohost_log, "is a symlink"); break; case PR_LOG_WRITABLE_DIR: pr_log_debug(DEBUG0, MOD_AUTOHOST_VERSION ": unable to open AutoHostLog '%s': %s", autohost_log, "parent directory is world-writable"); break; } } autohost_server_list = xaset_create(autohost_pool, NULL); c = find_config(main_server->conf, CONF_PARAM, "AutoHostPorts", FALSE); if (c) { register unsigned int i; array_header *port_list; int *ports; port_list = c->argv[0]; ports = port_list->elts; /* We need to open a binding for each of the specific ports, unless * such a binding already exists. */ for (i = 0; i < port_list->nelts; i++) { if (pr_ipbind_find(main_server->addr, ports[i], TRUE) == NULL) { int res; conn_t *listener; (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "adding socket for AutoHostPort %d", ports[i]); res = pr_ipbind_create(main_server, main_server->addr, ports[i]); if (res < 0) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error creating binding for %s#%d: %s", pr_netaddr_get_ipstr(main_server->addr), ports[i], strerror(errno)); continue; } /* Create a listening socket for this port. */ listener = pr_inet_create_conn(autohost_pool, -1, main_server->addr, ports[i], FALSE); if (listener == NULL) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error opening new listening socket for port %d: %s", ports[i], strerror(errno)); continue; } res = pr_ipbind_open(main_server->addr, ports[i], listener, FALSE, FALSE, TRUE); if (res < 0) { (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "error opening binding for %s:%d: %s", pr_netaddr_get_ipstr(main_server->addr), ports[i], strerror(errno)); continue; } (void) pr_log_writefile(autohost_logfd, MOD_AUTOHOST_VERSION, "opening listening socket for %s on AutoHostPort %d", pr_netaddr_get_ipstr(main_server->addr), ports[i]); } } } return; } /* Initialization routines */ static int autohost_init(void) { #if defined(PR_SHARED_MODULE) pr_event_register(&autohost_module, "core.module-unload", autohost_mod_unload_ev, NULL); #endif /* PR_SHARED_MODULE */ pr_event_register(&autohost_module, "core.postparse", autohost_postparse_ev, NULL); return 0; } /* Module API tables */ static conftable autohost_conftab[] = { { "AutoHostConfig", set_autohostconfig, NULL }, { "AutoHostEngine", set_autohostengine, NULL }, { "AutoHostLog", set_autohostlog, NULL }, { "AutoHostPorts", set_autohostports, NULL }, { NULL } }; module autohost_module = { NULL, NULL, /* Module API version 2.0 */ 0x20, /* Module name */ "autohost", /* Module configuration handler table */ autohost_conftab, /* Module command handler table */ NULL, /* Module authentication handler table */ NULL, /* Module initialization function */ autohost_init, /* Session initialization function */ NULL, /* Module version */ MOD_AUTOHOST_VERSION }; proftpd-mod-autohost-0.4/mod_autohost.html000066400000000000000000000220271154132374600210600ustar00rootroot00000000000000 ProFTPD module mod_autohost

ProFTPD module mod_autohost



For sites that run a large number of <VirtualHost>s for proftpd, it can be cumbersome to configure them all in the proftpd.conf file. Adding or removing virtual server configurations require restarting the daemon, as do changes to one of the server configurations. The daemon also consumes memory for each server configuration, and the memory footprint for the daemon process can grow large for large numbers of servers.

The mod_autohost module allows for server configurations to be configured in individual files, and for those configuration to be used in an "on demand" fashion. Rather than loading the configurations into memory when the daemon starts up, the daemon will check the IP address and port being contacted by a connecting client, check in the filesystem for a mod_autohost configuration file for that address/port, dynamically parse the configuration, and insert the configuration into the session's process space. Thus changes to the configuration are seen whenever a client connects, without requiring a daemon restart. The memory footprint is reduced because proftpd, via mod_autohost, only reads and uses the needed configuration.

This module is contained in the mod_autohost file for ProFTPD 1.3.x, and is not compiled by default. Installation instructions are discussed here.

The most current version of mod_autohost can be found at:

  http://www.castaglia.org/proftpd/

Author

Please contact TJ Saunders <tj at castaglia.org> with any questions, concerns, or suggestions regarding this module.

Directives


AutoHostConfig

Syntax: AutoHostConfig path
Default: None
Context: "server config"
Module: mod_autohost
Compatibility: 1.3.0rc1 and later

The AutoHostConfig directive specifies the path that mod_autohost checks for, when handling incoming connections. The given path must be an absolute path, and may contain the following variables, which will be interpolated:

Note: This directive is required for mod_autohost to function.

Examples
With an AutoHostConfig of:

  /etc/ftpd/vhosts/%0/autohost.conf
and a client connecting to 1.2.3.4, the above path would expand into:
  /etc/ftpd/vhosts/1.2.3.4/autohost.conf
Given a path of:
  /etc/ftpd/vhosts/%1/%2/%3/%4/%p/vhost.conf
and a client connecting to 1.2.3.4, port 2121, mod_autohost would check for the following file:
  /etc/ftpd/vhosts/1/2/3/4/2121/vhost.conf


AutoHostEngine

Syntax: AutoHostEngine on|off
Default: None
Context: "server config"
Module: mod_autohost
Compatibility: 1.3.0rc1 and later

The AutoHostEngine directive enables or disables the module's runtime checks for dynamic server configuration files. If it is set to off this module does no checking. Use this directive to disable the module instead of commenting out all mod_autohost directives.


AutoHostLog

Syntax: AutoHostLog path
Default: None
Context: "server config"
Module: mod_autohost
Compatibility: 1.3.0rc1 and later

The AutoHostLog directive is used to a specify a log file for mod_autohost reporting and debugging. The path parameter must be the full path to the file to use for logging. Note that this path must not be to a world-writeable directory and, unless AllowLogSymlinks is explicitly set to on (generally a bad idea), the path must not be a symbolic link.


AutoHostPorts

Syntax: AutoHostPorts port1 ... portN
Default: None
Context: "server config"
Module: mod_autohost
Compatibility: 1.3.2rc1 and later

The AutoHostPorts directive is used to specify a list of port numbers on which proftpd should listen. By default, proftpd listens on a wildcard socket, which means that a single socket can be used to listen for all address for a given port. But there is no such thing as a socket which can listen on all ports. Thus mod_autohost needs to know when to listen on other ports.

Note that the AutoHostPorts directive is only needed if your AutoHostConfig path uses the "%p" variable (i.e. uses the port number to which the client connected as part of the path to the matching configuration).

For example, if your AutoHostConfig path included configurations for servers on non-standard ports, you would need to use the AutoHostPorts directive to list those ports, so that proftpd could handles connections to them:

  <IfModule mod_autohost.c>
    AutoHostEngine on
    AutoHostLog /etc/ftpd/var/autohost.log

    # This is required for mod_autohost to work
    AutoHostConfig /etc/ftpd/vhosts/%0/%p/autoconf.conf

    # Define the other non-standard ports for which we have config files
    AutoHostPorts 2121 2222 4444
  </IfModule>


Installation

To install mod_autohost, copy the mod_autohost.c file into:
  proftpd-dir/contrib/
after unpacking the latest proftpd-1.3 source code. For including mod_autohost as a staticly linked module:
  ./configure --with-modules=mod_autohost
Alternatively, mod_autohost could be built as a DSO module:
  ./configure --enable-dso --with-shared=mod_autohost
Then follow the usual steps:
  make
  make install


Usage

Example configuration:

  <IfModule mod_autohost.c>
    AutoHostEngine on
    AutoHostLog /etc/ftpd/var/autohost.log

    # This is required for mod_autohost to work
    AutoHostConfig /etc/ftpd/vhosts/%0/autoconf.conf
  </IfModule>
With this configuration, a client connecting to 1.2.3.4 would cause mod_autohost to look for the following path:
  /etc/ftpd/vhosts/1.2.3.4/autohost.conf
If the file is not present, proftpd handles the connection as it normally would.

Caveats
The SocketBindTight directive cannot be "on" if mod_autohost is to work directly. With SocketBindTight being off by default, proftpd listens for incoming connections on a wildcard socket, which will receive connections to all IP addresses on that port. mod_autohost relies on this behavior. If SocketBindTight is set to on, then proftpd will listen only to the addresses of the servers configured in proftpd.conf, and any autohost.conf files will be useless.

The DefaultServer directive will have no effect if it appears in an autohost.conf file.

The mod_tls module will not be able to properly prompt for passphrases for keys in an autohost.conf file on server startup.

IPv6 addresses are not currently handled by mod_autohost.



Author: $Author: tj $
Last Updated: $Date: 2009/03/03 19:00:07 $


© Copyright 2004-2009 TJ Saunders
All Rights Reserved


proftpd-mod-autohost-0.4/t/000077500000000000000000000000001154132374600157255ustar00rootroot00000000000000proftpd-mod-autohost-0.4/t/lib/000077500000000000000000000000001154132374600164735ustar00rootroot00000000000000proftpd-mod-autohost-0.4/t/lib/ProFTPD/000077500000000000000000000000001154132374600177115ustar00rootroot00000000000000proftpd-mod-autohost-0.4/t/lib/ProFTPD/Tests/000077500000000000000000000000001154132374600210135ustar00rootroot00000000000000proftpd-mod-autohost-0.4/t/lib/ProFTPD/Tests/Modules/000077500000000000000000000000001154132374600224235ustar00rootroot00000000000000proftpd-mod-autohost-0.4/t/lib/ProFTPD/Tests/Modules/mod_autohost.pm000066400000000000000000000245501154132374600254740ustar00rootroot00000000000000package ProFTPD::Tests::Modules::mod_autohost; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use Cwd; use Digest::MD5; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use POSIX qw(:fcntl_h); use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { autohost_config => { order => ++$order, test_class => [qw(forking)], }, autohost_ports => { order => ++$order, test_class => [qw(forking)], }, autohost_extlog_var_p => { order => ++$order, test_class => [qw(bug forking)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { return testsuite_get_runnable_tests($TESTS); } sub autohost_config { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/autohost.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/autohost.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/autohost.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/autohost.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/autohost.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs("$tmpdir/home"); mkpath($home_dir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); mkpath("$tmpdir/conf.d"); my $auto_config = File::Spec->rel2abs("$tmpdir/conf.d/127.0.0.1.conf"); if (open(my $fh, "> $auto_config")) { print $fh <rel2abs($tmpdir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10', IfModules => { 'mod_autohost.c' => { AutoHostEngine => 'on', AutoHostLog => $log_file, AutoHostConfig => "$test_root/conf.d/%0.conf", }, 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); # Open pipes, for use between the parent and child processes. Specifically, # the child will indicate when it's done with its test by writing a message # to the parent. my ($rfh, $wfh); unless (pipe($rfh, $wfh)) { die("Can't open pipe: $!"); } my $ex; # Fork child $self->handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub autohost_ports { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/autohost.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/autohost.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/autohost.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/autohost.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/autohost.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs("$tmpdir/home"); mkpath($home_dir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_root = File::Spec->rel2abs($tmpdir); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); if (open(my $fh, ">> $config_file")) { print $fh < AutoHostEngine on AutoHostLog $log_file AutoHostConfig $test_root/conf.d/%0:%p.conf AutoHostPorts $port EOC unless (close($fh)) { die("Can't write $config_file: $!"); } } else { die("Can't open $config_file: $!"); } mkpath("$tmpdir/conf.d"); my $auto_config = File::Spec->rel2abs("$tmpdir/conf.d/127.0.0.1:$port.conf"); if (open(my $fh, "> $auto_config")) { print $fh <handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } unlink($log_file); } sub autohost_extlog_var_p { my $self = shift; my $tmpdir = $self->{tmpdir}; my $config_file = "$tmpdir/autohost.conf"; my $pid_file = File::Spec->rel2abs("$tmpdir/autohost.pid"); my $scoreboard_file = File::Spec->rel2abs("$tmpdir/autohost.scoreboard"); my $log_file = File::Spec->rel2abs('tests.log'); my $auth_user_file = File::Spec->rel2abs("$tmpdir/autohost.passwd"); my $auth_group_file = File::Spec->rel2abs("$tmpdir/autohost.group"); my $user = 'proftpd'; my $passwd = 'test'; my $group = 'ftpd'; my $home_dir = File::Spec->rel2abs("$tmpdir/home"); mkpath($home_dir); my $uid = 500; my $gid = 500; # Make sure that, if we're running as root, that the home directory has # permissions/privs set for the account we create if ($< == 0) { unless (chmod(0755, $home_dir)) { die("Can't set perms on $home_dir to 0755: $!"); } unless (chown($uid, $gid, $home_dir)) { die("Can't set owner of $home_dir to $uid/$gid: $!"); } } auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, '/bin/bash'); auth_group_write($auth_group_file, $group, $gid, $user); my $test_root = File::Spec->rel2abs($tmpdir); my $ext_log = File::Spec->rel2abs("$tmpdir/custom.log"); my $config = { PidFile => $pid_file, ScoreboardFile => $scoreboard_file, SystemLog => $log_file, TraceLog => $log_file, Trace => 'DEFAULT:10', LogFormat => 'custom "%p"', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, }, }; my ($port, $config_user, $config_group) = config_write($config_file, $config); if (open(my $fh, ">> $config_file")) { print $fh < AutoHostEngine on AutoHostLog $log_file AutoHostConfig $test_root/conf.d/%0:%p.conf AutoHostPorts $port EOC unless (close($fh)) { die("Can't write $config_file: $!"); } } else { die("Can't open $config_file: $!"); } mkpath("$tmpdir/conf.d"); my $auto_config = File::Spec->rel2abs("$tmpdir/conf.d/127.0.0.1:$port.conf"); if (open(my $fh, "> $auto_config")) { print $fh <handle_sigchld(); defined(my $pid = fork()) or die("Can't fork: $!"); if ($pid) { eval { my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); $client->login($user, $passwd); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($config_file, $rfh) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($pid_file); $self->assert_child_ok($pid); if ($ex) { die($ex); } # Now, read in the ExtendedLog, and see whether the %p variable was # properly written out. if (open(my $fh, "< $ext_log")) { my $line = <$fh>; chomp($line); close($fh); $self->assert($port eq $line, test_msg("Expected '$port', got '$line'")); } else { die("Can't read $ext_log: $!"); } unlink($log_file); } 1; proftpd-mod-autohost-0.4/t/modules/000077500000000000000000000000001154132374600173755ustar00rootroot00000000000000proftpd-mod-autohost-0.4/t/modules/mod_autohost.t000066400000000000000000000002701154132374600222660ustar00rootroot00000000000000#!/usr/bin/env perl use lib qw(t/lib); use strict; use Test::Unit::HarnessUnit; $| = 1; my $r = Test::Unit::HarnessUnit->new(); $r->start("ProFTPD::Tests::Modules::mod_autohost");