[Git][debian-proftpd-team/proftpd][upstream] New upstream version 1.3.7d+dfsg
Hilmar Preuße (@hilmar-guest)
gitlab at salsa.debian.org
Sun Apr 24 22:49:40 BST 2022
Hilmar Preuße pushed to branch upstream at Debian ProFTPD Team / proftpd
Commits:
38d1167f by Hilmar Preusse at 2022-04-24T23:29:24+02:00
New upstream version 1.3.7d+dfsg
- - - - -
21 changed files:
- .cirrus.yml
- .github/workflows/ci.yml
- .github/workflows/rpm.yml
- NEWS
- README.md
- RELEASE_NOTES
- contrib/dist/rpm/proftpd.spec
- include/inet.h
- include/version.h
- modules/mod_auth_file.c
- modules/mod_core.c
- modules/mod_ls.c
- modules/mod_xfer.c
- src/bindings.c
- src/inet.c
- src/random.c
- tests/t/lib/ProFTPD/TestSuite/Utils.pm
- tests/t/lib/ProFTPD/Tests/Commands/NLST.pm
- tests/t/lib/ProFTPD/Tests/Config/AllowForeignAddress.pm
- tests/t/lib/ProFTPD/Tests/Config/PassivePorts.pm
- tests/t/lib/ProFTPD/Tests/Modules/mod_auth_file.pm
Changes:
=====================================
.cirrus.yml
=====================================
@@ -3,11 +3,8 @@ task:
name: build
freebsd_instance:
matrix:
- # NOTE: Currently disabled due to this error:
- # pkg: repository FreeBSD contains packages for wrong OS version: FreeBSD:13:amd64
- # image_family: freebsd-13-0-snap
- image_family: freebsd-12-1
- image_family: freebsd-11-3-snap
+ image_family: freebsd-13-0
+ image_family: freebsd-12-2
env:
CIRRUS_CLONE_DEPTH: 10
@@ -25,7 +22,7 @@ task:
- pkg install -y libmemcached
- pkg install -y mysql57-client
- pkg install -y ncurses
- - pkg install -y openldap-client
+ - pkg install -y openldap24-client
- pkg install -y openssl
- pkg install -y pcre
- pkg install -y postgresql10-client
=====================================
.github/workflows/ci.yml
=====================================
@@ -3,11 +3,9 @@ name: CI
on:
push:
branches:
- - master
- 1.3.7
pull_request:
branches:
- - master
- 1.3.7
jobs:
@@ -30,7 +28,7 @@ jobs:
environment: CI
env:
- PACKAGE_VERSION: 1.3.7b
+ PACKAGE_VERSION: 1.3.7d
REDIS_HOST: redis
strategy:
=====================================
.github/workflows/rpm.yml
=====================================
@@ -3,11 +3,9 @@ name: RPM
on:
push:
branches:
- - master
- 1.3.7
pull_request:
branches:
- - master
- 1.3.7
jobs:
@@ -17,8 +15,7 @@ jobs:
strategy:
matrix:
container:
- - centos:7
- - centos:8
+ - almalinux:8
container: ${{ matrix.container }}
@@ -26,20 +23,13 @@ jobs:
- name: Checkout source code
uses: actions/checkout at v2
- - name: Configure Centos 7 repos
- if: ${{ matrix.container == 'centos:7' }}
+ - name: Configure AlmaLinux 8 repos
+ if: ${{ matrix.container == 'almalinux:8' }}
run: |
# Need to add other repos for e.g. libsodium
- yum install -y dnf-plugins-core epel-release
- # for mod_wrap
- yum install -y libnsl2-devel tcp_wrappers-devel
-
- - name: Configure Centos 8 repos
- if: ${{ matrix.container == 'centos:8' }}
- run: |
- # Need to add other repos for e.g. libsodium
- yum install -y dnf-plugins-core epel-release
- yum config-manager --set-enabled powertools
+ yum install -y dnf-plugins-core epel-release yum-utils
+ dnf config-manager --enable epel
+ dnf config-manager --set-enabled powertools
# for mod_wrap
yum install -y libnsl2-devel https://pkgs.dyn.su/el8/extras/x86_64/tcp_wrappers-libs-7.6-77.el8.x86_64.rpm https://pkgs.dyn.su/el8/extras/x86_64/tcp_wrappers-devel-7.6-77.el8.x86_64.rpm
=====================================
NEWS
=====================================
@@ -15,6 +15,23 @@
where `N' is the issue number.
-----------------------------------------------------------------------------
+1.3.7d - Released 23-Apr-2022
+--------------------------------
+- Issue 1321 - Crash with long lines in AuthGroupFile due to large realloc(3).
+- Issue 1325 - NLST does not behave consistently for relative paths.
+- Issue 1346 - Implement AllowForeignAddress class matching for passive data
+ transfers.
+- Bug 4467 - DeleteAbortedStores removes successfully transferred files
+ unexpectedly.
+- Issue 1401 - Keepalive socket options should be set using IPPROTO_TCP, not
+ SOL_SOCKET.
+- Issue 1402 - TCP keepalive SocketOptions should apply to control as well as
+ data connection.
+- Issue 1396 - ProFTPD always uses the same PassivePorts port for first
+ transfer.
+- Issue 1369 - Name-based virtual hosts not working as expected after upgrade
+ from 1.3.7a to 1.3.7b.
+
1.3.7c - Released 29-Aug-2021
--------------------------------
- Issue 1273 - Improve mod_tls log messages for unsupported older TLS protocol
=====================================
README.md
=====================================
@@ -7,7 +7,7 @@
[![Coverage Status](https://coveralls.io/repos/proftpd/proftpd/badge.svg?branch=master&service=github)](https://coveralls.io/github/proftpd/proftpd?branch=master)
[![Coverity Scan Status](https://scan.coverity.com/projects/198/badge.svg)](https://scan.coverity.com/projects/198)
[![C/C++ Language Grade](https://img.shields.io/lgtm/grade/cpp/g/proftpd/proftpd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/proftpd/proftpd/context:cpp)
-[![Release](https://img.shields.io/badge/release-1.3.7b-brightgreen)](https://github.com/proftpd/proftpd/releases/latest)
+[![Release](https://img.shields.io/badge/release-1.3.7d-brightgreen)](https://github.com/proftpd/proftpd/releases/latest)
[![License](https://img.shields.io/badge/license-GPL-brightgreen.svg)](https://img.shields.io/badge/license-GPL-brightgreen.svg)
## Introduction
=====================================
RELEASE_NOTES
=====================================
@@ -6,6 +6,14 @@ This file contains a description of the major changes to ProFTPD for the
releases. More information on these changes can be found in the NEWS and
ChangeLog files.
+1.3.7d
+---------
+
+ + Improved consistency/support for name-based virtual hosts.
+
+ + Fixed crashes due to very long lines in AuthGroupFiles (Issue #1321)..
+
+
1.3.7c
---------
=====================================
contrib/dist/rpm/proftpd.spec
=====================================
@@ -53,7 +53,7 @@
# RHEL5 and clones don't have suitably recent versions of pcre/libmemcached
# so use --with rhel5 to inhibit those features when using --with everything
-%global proftpd_version 1.3.7c
+%global proftpd_version 1.3.7d
# rc_version should be incremented for each RC release, and reset back to 1
# AFTER each stable release.
@@ -61,7 +61,7 @@
# release_version should be incremented for each maint release, and reset back
# to 1 BEFORE starting new release cycle.
-%global release_version 5
+%global release_version 6
%if %(echo %{proftpd_version} | grep rc >/dev/null 2>&1 && echo 1 || echo 0)
%global rpm_version %(echo %{proftpd_version} | sed -e 's/rc.*//')
=====================================
include/inet.h
=====================================
@@ -2,7 +2,7 @@
* ProFTPD - FTP server daemon
* Copyright (c) 1997, 1998 Public Flood Software
* Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2021 The ProFTPD Project team
+ * Copyright (c) 2001-2022 The ProFTPD Project team
*
* 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
@@ -149,6 +149,7 @@ int pr_inet_set_async(pool *, conn_t *);
int pr_inet_set_block(pool *, conn_t *);
int pr_inet_set_nonblock(pool *, conn_t *);
int pr_inet_set_proto_cork(int, int);
+int pr_inet_set_proto_keepalive(pool *, conn_t *, struct tcp_keepalive *);
int pr_inet_set_proto_nodelay(pool *, conn_t *, int);
int pr_inet_set_proto_opts(pool *, conn_t *, int, int, int, int);
int pr_inet_set_socket_opts(pool *, conn_t *, int, int, struct tcp_keepalive *);
=====================================
include/version.h
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - FTP server daemon
- * Copyright (c) 2020-2021 The ProFTPD Project team
+ * Copyright (c) 2020-2022 The ProFTPD Project team
*
* 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
@@ -28,8 +28,8 @@
#include "buildstamp.h"
/* Application version (in various forms) */
-#define PROFTPD_VERSION_NUMBER 0x0001030708
-#define PROFTPD_VERSION_TEXT "1.3.7c"
+#define PROFTPD_VERSION_NUMBER 0x0001030709
+#define PROFTPD_VERSION_TEXT "1.3.7d"
/* Module API version */
#define PR_MODULE_API_VERSION 0x20
=====================================
modules/mod_auth_file.c
=====================================
@@ -1,7 +1,7 @@
/*
* ProFTPD: mod_auth_file - file-based authentication module that supports
* restrictions on the file contents
- * Copyright (c) 2002-2021 The ProFTPD Project team
+ * Copyright (c) 2002-2022 The ProFTPD Project team
*
* 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
@@ -325,45 +325,74 @@ static struct passwd *af_getpasswd(const char *buf, unsigned int lineno) {
#define NGRPFIELDS 4
static char *grpbuf = NULL;
+static size_t grpbufsz = 0;
static struct group grent;
static char *grpfields[NGRPFIELDS];
static char *members[MAXMEMBERS+1];
-static char *af_getgrentline(char **buf, int *buflen, pr_fh_t *fh,
+static char *af_getgrentline(char **buf, size_t *bufsz, pr_fh_t *fh,
unsigned int *lineno) {
- char *cp = *buf;
+ char *ptr, *res;
+ size_t original_bufsz, buflen;
- while (pr_fsio_gets(cp, (*buflen) - (cp - *buf), fh) != NULL) {
- pr_signals_handle();
+ original_bufsz = *bufsz;
+ buflen = *bufsz;
- (*lineno)++;
+ /* Try to keep our unfilled buffer zeroed out, so that strlen(3) et al
+ * work as expected.
+ */
+ memset(*buf, '\0', *bufsz);
+
+ ptr = *buf;
+ res = pr_fsio_gets(ptr, buflen, fh);
+ while (res != NULL) {
+ pr_signals_handle();
/* Is this a full line? */
- if (strchr(cp, '\n')) {
+ if (strchr(*buf, '\n') != NULL) {
+ pr_trace_msg(trace_channel, 25,
+ "found LF, returning line: '%s' (%lu bytes)", *buf,
+ (unsigned long) strlen(*buf));
+ (*lineno)++;
return *buf;
}
- /* No -- allocate a larger buffer, doubling buflen. */
- *buflen += *buflen;
-
+ /* No -- allocate a larger buffer. Note that doubling the buflen
+ * each time may cause issues; fgetgrent(3) would increment the
+ * allocated buffer by the original buffer length each time. So we
+ * do the same (Issue #1321).
+ */
{
+ size_t new_bufsz;
char *new_buf;
- new_buf = realloc(*buf, *buflen);
+ pr_trace_msg(trace_channel, 25, "getgrentline() buffer (%lu bytes): "
+ "'%.*s'", (unsigned long) *bufsz, (int) *bufsz, *buf);
+
+ pr_trace_msg(trace_channel, 19,
+ "no LF found in group line, increasing buffer (%lu bytes) by %lu bytes",
+ (unsigned long) *bufsz, (unsigned long) original_bufsz);
+ new_bufsz = *bufsz + original_bufsz;
+
+ new_buf = realloc(*buf, new_bufsz);
if (new_buf == NULL) {
break;
}
+ ptr = new_buf + *bufsz;
*buf = new_buf;
+ *bufsz = new_bufsz;
+ buflen = original_bufsz;
+
+ memset(ptr, '\0', buflen);
}
- cp = *buf + (cp - *buf);
- cp = strchr(cp, '\0');
+ res = pr_fsio_gets(ptr, buflen, fh);
}
free(*buf);
*buf = NULL;
- *buflen = 0;
+ *bufsz = 0;
return NULL;
}
@@ -394,22 +423,29 @@ static struct group *af_getgrp(const char *buf, unsigned int lineno) {
i = strlen(buf) + 1;
- if (!grpbuf) {
+ if (grpbuf == NULL) {
+ grpbufsz = i;
grpbuf = malloc(i);
- } else {
+ } else if (grpbufsz < (size_t) i) {
char *new_buf;
+ pr_trace_msg(trace_channel, 19,
+ "parsing group line '%s' (%lu bytes), allocating %lu bytes via "
+ "realloc(3)", buf, (unsigned long) i, (unsigned long) i);
+
new_buf = realloc(grpbuf, i);
if (new_buf == NULL) {
return NULL;
}
grpbuf = new_buf;
+ grpbufsz = i;
}
- if (!grpbuf)
+ if (grpbuf == NULL) {
return NULL;
+ }
sstrncpy(grpbuf, buf, i);
@@ -517,7 +553,16 @@ static struct group *af_getgrent(pool *p) {
while (TRUE) {
char *cp = NULL, *buf = NULL;
- int buflen = PR_TUNABLE_BUFFER_SIZE;
+ size_t buflen;
+
+ buflen = PR_TUNABLE_BUFFER_SIZE;
+
+ if (af_group_file->af_file_fh->fh_iosz > 0) {
+ /* This aligns our group(5) buffer with the preferred filesystem read
+ * block size.
+ */
+ buflen = af_group_file->af_file_fh->fh_iosz;
+ }
pr_signals_handle();
@@ -526,6 +571,11 @@ static struct group *af_getgrent(pool *p) {
pr_log_pri(PR_LOG_ALERT, "Out of memory!");
_exit(1);
}
+
+ pr_trace_msg(trace_channel, 19,
+ "getgrent(3): allocated buffer %p (%lu bytes)", buf,
+ (unsigned long) buflen);
+
grp = NULL;
while (af_getgrentline(&buf, &buflen, af_group_file->af_file_fh,
@@ -629,6 +679,12 @@ static int af_setgrent(pool *p) {
pbuf->remaining = pbuf->buflen;
}
+ if (grpbuf != NULL) {
+ free(grpbuf);
+ grpbuf = NULL;
+ }
+ grpbufsz = 0;
+
return 0;
}
=====================================
modules/mod_core.c
=====================================
@@ -2,7 +2,7 @@
* ProFTPD - FTP server daemon
* Copyright (c) 1997, 1998 Public Flood Software
* Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2020 The ProFTPD Project team
+ * Copyright (c) 2001-2022 The ProFTPD Project team
*
* 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
@@ -6780,6 +6780,19 @@ static int core_sess_init(void) {
init_auth();
+ /* Enable any TCP keepalive options on the control connection (Issue #1402).
+ * Note that ctrl conns do not have listening fds, but this function uses
+ * that fd (due to compatibility with data conns), so we temporarily assign
+ * that struct member.
+ */
+ session.c->listen_fd = session.c->wfd;
+ if (pr_inet_set_proto_keepalive(session.pool, session.c,
+ main_server->tcp_keepalive) < 0) {
+ pr_log_debug(DEBUG9, "error setting ctrl conn TCP keepalive: %s",
+ strerror(errno));
+ }
+ session.c->listen_fd = -1;
+
c = find_config(main_server->conf, CONF_PARAM, "MultilineRFC2228", FALSE);
if (c != NULL) {
session.multiline_rfc2228 = *((int *) c->argv[0]);
=====================================
modules/mod_ls.c
=====================================
@@ -2,7 +2,7 @@
* ProFTPD - FTP server daemon
* Copyright (c) 1997, 1998 Public Flood Software
* Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2020 The ProFTPD Project
+ * Copyright (c) 2001-2021 The ProFTPD Project
*
* 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
@@ -3163,10 +3163,6 @@ MODRET ls_nlst(cmd_rec *cmd) {
p = *path;
path++;
- if (*p == '.' && (!opt_A || is_dotdir(p))) {
- continue;
- }
-
pr_fs_clear_cache2(p);
if (pr_fsio_stat(p, &st) == 0) {
/* If it's a directory... */
=====================================
modules/mod_xfer.c
=====================================
@@ -2,7 +2,7 @@
* ProFTPD - FTP server daemon
* Copyright (c) 1997, 1998 Public Flood Software
* Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2020 The ProFTPD Project team
+ * Copyright (c) 2001-2022 The ProFTPD Project team
*
* 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
@@ -1014,9 +1014,8 @@ static void stor_abort(pool *p) {
}
}
}
- }
- if (session.xfer.path != NULL) {
+ } else if (session.xfer.path != NULL) {
if (delete_stores != NULL &&
*delete_stores == TRUE) {
pr_log_debug(DEBUG5, "removing aborted file '%s'", session.xfer.path);
@@ -2492,6 +2491,16 @@ MODRET xfer_pre_retr(cmd_rec *cmd) {
pr_fs_clear_cache2(decoded_path);
dir = dir_realpath(cmd->tmp_pool, decoded_path);
+ if (dir == NULL) {
+ /* Try using dir_best_path(), as xfer_pre_stor() does.
+ *
+ * Without this fallback, certain use cases (such as SFTP downloads using
+ * mod_sftp + mod_vroot) fail unexpectedly, with misleading
+ * "denied by <Limit> configuration" errors.
+ */
+ dir = dir_best_path(cmd->tmp_pool, decoded_path);
+ }
+
if (dir == NULL ||
!dir_check(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
int xerrno = errno;
=====================================
src/bindings.c
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - FTP server daemon
- * Copyright (c) 2001-2020 The ProFTPD Project team
+ * Copyright (c) 2001-2022 The ProFTPD Project team
*
* 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
@@ -58,9 +58,10 @@ static void server_cleanup_cb(void *conn) {
/* The hashing function for the hash table of bindings. This algorithm
* is stolen from Apache's http_vhost.c
*/
-static unsigned int ipbind_hash_addr(const pr_netaddr_t *addr) {
+static unsigned int ipbind_hash_addr(const pr_netaddr_t *addr,
+ unsigned int port) {
size_t offset;
- unsigned int key;
+ unsigned int key, idx;
offset = pr_netaddr_get_inaddr_len(addr);
@@ -70,8 +71,14 @@ static unsigned int ipbind_hash_addr(const pr_netaddr_t *addr) {
*/
key = *(unsigned *) ((char *) pr_netaddr_get_inaddr(addr) + offset - 4);
+ /* Add in the port number, to give better spread of key values when many
+ * different port values are used.
+ */
+ key += port;
+
key ^= (key >> 16);
- return ((key >> 8) ^ key) % PR_BINDINGS_TABLE_SIZE;
+ idx = ((key >> 8) ^ key) % PR_BINDINGS_TABLE_SIZE;
+ return idx;
}
static pool *listening_conn_pool = NULL;
@@ -88,7 +95,7 @@ struct listener_rec {
conn_t *pr_ipbind_get_listening_conn(server_rec *server,
const pr_netaddr_t *addr, unsigned int port) {
- conn_t *l;
+ conn_t *l, *sl;
pool *p;
struct listener_rec *lr;
@@ -152,9 +159,15 @@ conn_t *pr_ipbind_get_listening_conn(server_rec *server,
return NULL;
}
- /* Inform any interested listeners that this socket was opened. */
+ /* Inform any interested listeners that this socket was opened. In order
+ * to convey the discovered conn_t `l` to the event listener, we set it
+ * on the server `s` temporarily.
+ */
+ sl = server->listen;
+ server->listen = l;
pr_inet_generate_socket_event("core.ctrl-listen", server, l->local_addr,
l->listen_fd);
+ server->listen = sl;
lr = pcalloc(p, sizeof(struct listener_rec));
lr->pool = p;
@@ -269,13 +282,36 @@ int pr_ipbind_add_binds(server_rec *serv) {
return -1;
}
- PR_CREATE_IPBIND(serv, addr, serv->ServerPort);
- PR_OPEN_IPBIND(addr, serv->ServerPort, listen_conn, FALSE, FALSE, TRUE);
+ res = pr_ipbind_create(serv, addr, serv->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__,
+ __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno));
+ }
+
+ res = pr_ipbind_open(addr, serv->ServerPort, listen_conn, FALSE, FALSE,
+ TRUE);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(addr), strerror(errno));
+ }
} else {
+ res = pr_ipbind_create(serv, addr, serv->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__,
+ __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno));
+ }
- PR_CREATE_IPBIND(serv, addr, serv->ServerPort);
- PR_OPEN_IPBIND(addr, serv->ServerPort, serv->listen, FALSE, FALSE, TRUE);
+ res = pr_ipbind_open(addr, serv->ServerPort, serv->listen, FALSE, FALSE,
+ TRUE);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(addr), strerror(errno));
+ }
}
c = find_config_next(c, c->next, CONF_PARAM, "_bind_", FALSE);
@@ -292,7 +328,7 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
pr_ipbind_t *ipbind = NULL;
unsigned char have_ipbind = FALSE;
- i = ipbind_hash_addr(addr);
+ i = ipbind_hash_addr(addr, port);
if (ipbind_table[i] == NULL) {
pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d",
@@ -302,6 +338,8 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
}
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
+ pr_signals_handle();
+
if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 &&
(!ipbind->ib_port || ipbind->ib_port == port)) {
have_ipbind = TRUE;
@@ -309,7 +347,7 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
}
}
- if (!have_ipbind) {
+ if (have_ipbind == FALSE) {
pr_log_pri(PR_LOG_NOTICE, "notice: no ipbind found for %s:%d",
pr_netaddr_get_ipstr(addr), port);
errno = ENOENT;
@@ -317,7 +355,7 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
}
/* If already closed, exit now. */
- if (!ipbind->ib_isactive) {
+ if (ipbind->ib_isactive == FALSE) {
errno = EPERM;
return -1;
}
@@ -331,7 +369,8 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
* for the master daemon in inetd mode, in which case virtual servers
* can't be shutdown via ftpdctl, anyway.
*/
- if (SocketBindTight && ipbind->ib_listener != NULL) {
+ if (SocketBindTight &&
+ ipbind->ib_listener != NULL) {
pr_inet_close(ipbind->ib_server->pool, ipbind->ib_listener);
ipbind->ib_listener = ipbind->ib_server->listen = NULL;
}
@@ -344,14 +383,18 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
*/
ipbind->ib_isactive = FALSE;
- if (close_namebinds && ipbind->ib_namebinds) {
+ if (close_namebinds == TRUE &&
+ ipbind->ib_namebinds != NULL) {
register unsigned int j = 0;
pr_namebind_t **namebinds = NULL;
namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
for (j = 0; j < ipbind->ib_namebinds->nelts; j++) {
- pr_namebind_t *nb = namebinds[j];
+ pr_namebind_t *nb;
+ pr_signals_handle();
+
+ nb = namebinds[j];
if (pr_namebind_close(nb->nb_name, nb->nb_server->addr) < 0) {
pr_trace_msg(trace_channel, 7,
"notice: error closing namebind '%s' for address %s: %s",
@@ -366,6 +409,8 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
for (i = 0; i < PR_BINDINGS_TABLE_SIZE; i++) {
pr_ipbind_t *ipbind = NULL;
+ pr_signals_handle();
+
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
if (SocketBindTight &&
ipbind->ib_listener != NULL) {
@@ -386,8 +431,11 @@ int pr_ipbind_close(const pr_netaddr_t *addr, unsigned int port,
namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
for (j = 0; j < ipbind->ib_namebinds->nelts; j++) {
- pr_namebind_t *nb = namebinds[j];
+ pr_namebind_t *nb;
+ pr_signals_handle();
+
+ nb = namebinds[j];
if (pr_namebind_close(nb->nb_name, nb->nb_server->addr) < 0) {
pr_trace_msg(trace_channel, 7,
"notice: error closing namebind '%s' for address %s: %s",
@@ -408,18 +456,20 @@ int pr_ipbind_close_listeners(void) {
conn_t **listeners;
register unsigned int i = 0;
- if (!listener_list ||
- listener_list->nelts == 0)
+ if (listener_list == NULL ||
+ listener_list->nelts == 0) {
return 0;
+ }
listeners = listener_list->elts;
for (i = 0; i < listener_list->nelts; i++) {
- conn_t *listener = listeners[i];
+ conn_t *listener;
pr_signals_handle();
+ listener = listeners[i];
if (listener->listen_fd != -1) {
- close(listener->listen_fd);
+ (void) close(listener->listen_fd);
listener->listen_fd = -1;
}
}
@@ -441,12 +491,14 @@ int pr_ipbind_create(server_rec *server, const pr_netaddr_t *addr,
/* Ensure the ipbind table has been initialized. */
init_ipbind_table();
- i = ipbind_hash_addr(addr);
+ i = ipbind_hash_addr(addr, port);
pr_trace_msg(trace_channel, 29, "hashed address '%s' to index %u",
pr_netaddr_get_ipstr(addr), i);
/* Make sure the address is not already in use */
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
+ pr_signals_handle();
+
if (pr_netaddr_cmp(ipbind->ib_addr, addr) == 0 &&
ipbind->ib_port == port) {
existing = ipbind;
@@ -468,14 +520,14 @@ int pr_ipbind_create(server_rec *server, const pr_netaddr_t *addr,
if (c == NULL) {
pr_log_pri(PR_LOG_WARNING, "notice: '%s' (%s:%u) already bound to '%s'",
server->ServerName, pr_netaddr_get_ipstr(addr), port,
- ipbind->ib_server->ServerName);
+ existing->ib_server->ServerName);
errno = EADDRINUSE;
return -1;
}
pr_log_debug(DEBUG9, "notice: '%s' (%s:%u) already bound to '%s'",
server->ServerName, pr_netaddr_get_ipstr(addr), port,
- ipbind->ib_server->ServerName);
+ existing->ib_server->ServerName);
}
if (binding_pool == NULL) {
@@ -492,19 +544,27 @@ int pr_ipbind_create(server_rec *server, const pr_netaddr_t *addr,
ipbind->ib_islocalhost = FALSE;
ipbind->ib_isactive = FALSE;
- pr_trace_msg(trace_channel, 8, "created ipbind %p for %s#%u, server %p",
+ pr_trace_msg(trace_channel, 8, "created ipbind %p for %s#%u, server %p (%s)",
ipbind, pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port,
- ipbind->ib_server);
+ ipbind->ib_server, ipbind->ib_server->ServerName);
- /* Add the ipbind to the table. */
- if (ipbind_table[i] != NULL) {
+ /* Add the ipbind to the table, maintaining insertion order. */
+ if (existing != NULL) {
pr_trace_msg(trace_channel, 19,
"found existing ipbind %p (server %p) at index %u in iptable table, "
- "adding to %p", ipbind_table[i], ipbind_table[i]->ib_server, i, ipbind);
- ipbind->ib_next = ipbind_table[i];
+ "adding to %p", existing, existing->ib_server, i, existing);
+ if (existing->ib_next != NULL) {
+ ipbind->ib_next = existing->ib_next;
+ }
+ existing->ib_next = ipbind;
+
+ } else {
+ if (ipbind_table[i] != NULL) {
+ ipbind->ib_next = ipbind_table[i];
+ }
+ ipbind_table[i] = ipbind;
}
- ipbind_table[i] = ipbind;
return 0;
}
@@ -559,13 +619,13 @@ pr_ipbind_t *pr_ipbind_find(const pr_netaddr_t *addr, unsigned int port,
/* Ensure the ipbind table has been initialized. */
init_ipbind_table();
- i = ipbind_hash_addr(addr);
+ i = ipbind_hash_addr(addr, port);
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
pr_signals_handle();
- if (skip_inactive &&
- !ipbind->ib_isactive) {
+ if (ipbind->ib_isactive == FALSE &&
+ skip_inactive == TRUE) {
continue;
}
@@ -585,10 +645,9 @@ pr_ipbind_t *pr_ipbind_find(const pr_netaddr_t *addr, unsigned int port,
pr_ipbind_t *pr_ipbind_get(pr_ipbind_t *prev) {
static unsigned int i = 0;
- if (prev) {
-
+ if (prev != NULL) {
/* If there's another ipbind in this chain, simply return that. */
- if (prev->ib_next) {
+ if (prev->ib_next != NULL) {
return prev->ib_next;
}
@@ -613,6 +672,8 @@ pr_ipbind_t *pr_ipbind_get(pr_ipbind_t *prev) {
/* Search for the next non-empty chain in the table. */
for (; i < PR_BINDINGS_TABLE_SIZE; i++) {
+ pr_signals_handle();
+
if (ipbind_table[i] != NULL) {
return ipbind_table[i];
}
@@ -633,6 +694,8 @@ server_rec *pr_ipbind_get_server(const pr_netaddr_t *addr, unsigned int port) {
*/
ipbind = pr_ipbind_find(addr, port, TRUE);
if (ipbind != NULL) {
+ pr_log_debug(DEBUG7, "matching vhost found for %s#%u, using '%s'",
+ pr_netaddr_get_ipstr(addr), port, ipbind->ib_server->ServerName);
return ipbind->ib_server;
}
@@ -750,9 +813,11 @@ int pr_ipbind_listen(fd_set *readfds) {
pr_ipbind_t *ipbind = NULL;
for (ipbind = ipbind_table[i]; ipbind; ipbind = ipbind->ib_next) {
+ pr_signals_handle();
+
/* Skip inactive bindings, but only if SocketBindTight is in effect. */
if (SocketBindTight &&
- !ipbind->ib_isactive) {
+ ipbind->ib_isactive == FALSE) {
continue;
}
@@ -774,8 +839,9 @@ int pr_ipbind_listen(fd_set *readfds) {
if (ipbind->ib_listener->mode == CM_LISTEN) {
FD_SET(ipbind->ib_listener->listen_fd, readfds);
- if (ipbind->ib_listener->listen_fd > maxfd)
+ if (ipbind->ib_listener->listen_fd > maxfd) {
maxfd = ipbind->ib_listener->listen_fd;
+ }
/* Add this to the listener list as well. */
*((conn_t **) push_array(listener_list)) = ipbind->ib_listener;
@@ -790,7 +856,6 @@ int pr_ipbind_listen(fd_set *readfds) {
int pr_ipbind_open(const pr_netaddr_t *addr, unsigned int port,
conn_t *listen_conn, unsigned char isdefault, unsigned char islocalhost,
unsigned char open_namebinds) {
- int res = 0;
pr_ipbind_t *ipbind = NULL;
if (addr == NULL) {
@@ -805,8 +870,9 @@ int pr_ipbind_open(const pr_netaddr_t *addr, unsigned int port,
return -1;
}
- if (listen_conn)
+ if (listen_conn != NULL) {
listen_conn->next = NULL;
+ }
ipbind->ib_listener = ipbind->ib_server->listen = listen_conn;
ipbind->ib_listener = listen_conn;
@@ -822,11 +888,13 @@ int pr_ipbind_open(const pr_netaddr_t *addr, unsigned int port,
* - It's the default server (specified via the DefaultServer directive)
* - It handles connections to the loopback interface
*/
- if (isdefault)
+ if (isdefault) {
ipbind_default_server = ipbind;
+ }
- if (islocalhost)
+ if (islocalhost) {
ipbind_localhost_server = ipbind;
+ }
/* If requested, look for any namebinds for this ipbind, and open them. */
if (open_namebinds &&
@@ -840,8 +908,12 @@ int pr_ipbind_open(const pr_netaddr_t *addr, unsigned int port,
*/
namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
for (i = 0; i < ipbind->ib_namebinds->nelts; i++) {
+ int res;
pr_namebind_t *nb = namebinds[i];
+ pr_signals_handle();
+
+ nb = namebinds[i];
res = pr_namebind_open(nb->nb_name, nb->nb_server->addr,
nb->nb_server_port);
if (res < 0) {
@@ -942,6 +1014,8 @@ int pr_namebind_create(server_rec *server, const char *name,
/* See if there is already a namebind for the given name. */
for (i = 0; i < ipbind->ib_namebinds->nelts; i++) {
+ pr_signals_handle();
+
namebind = namebinds[i];
if (namebind != NULL &&
namebind->nb_name != NULL) {
@@ -972,8 +1046,9 @@ int pr_namebind_create(server_rec *server, const char *name,
}
pr_trace_msg(trace_channel, 8,
- "created namebind '%s' for %s#%u, server %p", name,
- pr_netaddr_get_ipstr(server->addr), server->ServerPort, server);
+ "created namebind '%s' for %s#%u, server '%s' [%p]", name,
+ pr_netaddr_get_ipstr(server->addr), server->ServerPort, server->ServerName,
+ server);
/* The given server should already have the following populated:
*
@@ -988,8 +1063,7 @@ int pr_namebind_create(server_rec *server, const char *name,
pr_namebind_t *pr_namebind_find(const char *name, const pr_netaddr_t *addr,
unsigned int port, unsigned char skip_inactive) {
- pr_ipbind_t *ipbind = NULL;
- pr_namebind_t *namebind = NULL;
+ pr_ipbind_t *ipbind = NULL, *iter;
if (name == NULL ||
addr == NULL) {
@@ -1026,7 +1100,8 @@ pr_namebind_t *pr_namebind_find(const char *name, const pr_netaddr_t *addr,
#endif /* PR_USE_IPV6 */
} else {
pr_trace_msg(trace_channel, 17,
- "found ipbind %p (server %p) for %s#%u", ipbind, ipbind->ib_server,
+ "found ipbind %p (server '%s' [%p]) for %s#%u", ipbind,
+ ipbind->ib_server->ServerName, ipbind->ib_server,
pr_netaddr_get_ipstr(addr), port);
}
@@ -1035,22 +1110,32 @@ pr_namebind_t *pr_namebind_find(const char *name, const pr_netaddr_t *addr,
return NULL;
}
- if (ipbind->ib_namebinds == NULL) {
- pr_trace_msg(trace_channel, 17,
- "ipbind %p (server %p) for %s#%u has no namebinds", ipbind,
- ipbind->ib_server, pr_netaddr_get_ipstr(addr), port);
- return NULL;
+ /* Now we need to search this ipbind list, to see if any of them have a
+ * matching namebind.
+ */
- } else {
+ for (iter = ipbind; iter; iter = iter->ib_next) {
register unsigned int i = 0;
- pr_namebind_t **namebinds = (pr_namebind_t **) ipbind->ib_namebinds->elts;
+ pr_namebind_t *namebind = NULL, **namebinds = NULL;
+
+ pr_signals_handle();
+ if (iter->ib_namebinds == NULL) {
+ pr_trace_msg(trace_channel, 17,
+ "ipbind %p (server %p) for %s#%u has no namebinds", iter,
+ iter->ib_server, pr_netaddr_get_ipstr(addr), port);
+ continue;
+ }
+
+ namebinds = (pr_namebind_t **) iter->ib_namebinds->elts;
pr_trace_msg(trace_channel, 17,
- "ipbind %p (server %p) for %s#%u has namebinds (%d)", ipbind,
- ipbind->ib_server, pr_netaddr_get_ipstr(addr), port,
- ipbind->ib_namebinds->nelts);
+ "ipbind %p (server %p) for %s#%u has namebinds (%d)", iter,
+ iter->ib_server, pr_netaddr_get_ipstr(addr), port,
+ iter->ib_namebinds->nelts);
+
+ for (i = 0; i < iter->ib_namebinds->nelts; i++) {
+ pr_signals_handle();
- for (i = 0; i < ipbind->ib_namebinds->nelts; i++) {
namebind = namebinds[i];
if (namebind == NULL) {
continue;
@@ -1171,10 +1256,11 @@ void free_bindings(void) {
/* Mark all listening conns as "unclaimed"; any that remaining unclaimed
* after init_bindings() can be closed.
*/
- if (listening_conn_list) {
+ if (listening_conn_list != NULL) {
struct listener_rec *lr;
for (lr = (struct listener_rec *) listening_conn_list->xas_list; lr; lr = lr->next) {
+ pr_signals_handle();
lr->claimed = FALSE;
}
}
@@ -1223,10 +1309,28 @@ static int init_inetd_bindings(void) {
is_default = TRUE;
}
- PR_CREATE_IPBIND(main_server, main_server->addr, main_server->ServerPort);
- PR_OPEN_IPBIND(main_server->addr, main_server->ServerPort,
+ res = pr_ipbind_create(main_server, main_server->addr,
+ main_server->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__, __LINE__,
+ main_server->ServerAddress, main_server->ServerPort, strerror(errno));
+ }
+
+ res = pr_ipbind_open(main_server->addr, main_server->ServerPort,
main_server->listen, is_default, TRUE, TRUE);
- PR_ADD_IPBINDS(main_server);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(main_server->addr), strerror(errno));
+ }
+
+ res = pr_ipbind_add_binds(main_server);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__,
+ __LINE__, main_server->ServerAddress, strerror(errno));
+ }
/* Now attach the faked connection to all virtual servers. */
for (serv = main_server->next; serv; serv = serv->next) {
@@ -1247,10 +1351,27 @@ static int init_inetd_bindings(void) {
is_default = TRUE;
}
- PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort);
- PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default,
- FALSE, TRUE);
- PR_ADD_IPBINDS(serv);
+ res = pr_ipbind_create(serv, serv->addr, serv->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__,
+ __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno));
+ }
+
+ res = pr_ipbind_open(serv->addr, serv->ServerPort, serv->listen,
+ is_default, FALSE, TRUE);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(serv->addr), strerror(errno));
+ }
+
+ res = pr_ipbind_add_binds(serv);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__,
+ __LINE__, serv->ServerAddress, strerror(errno));
+ }
}
return 0;
@@ -1264,6 +1385,8 @@ static array_header *find_server_ipbinds(pool *p, server_rec *s) {
pr_ipbind_t *ipbind;
for (ipbind = ipbind_table[i]; ipbind != NULL; ipbind = ipbind->ib_next) {
+ pr_signals_handle();
+
if (ipbind->ib_server == s) {
if (ipbinds == NULL) {
ipbinds = make_array(p, 16, sizeof(pr_ipbind_t *));
@@ -1280,6 +1403,7 @@ static array_header *find_server_ipbinds(pool *p, server_rec *s) {
static unsigned int process_serveralias(server_rec *s) {
unsigned namebind_count = 0;
config_rec *c;
+ pr_ipbind_t *ipbind;
array_header *ipbinds;
pool *tmp_pool;
@@ -1291,15 +1415,34 @@ static unsigned int process_serveralias(server_rec *s) {
* <VirtualHost 1.2.3.4 5.6.7.8>
* ServerAlias alias
* </VirtualHost>
+ *
+ * And that multiple namebinds can point to the same ipbind for this server:
+ *
+ * <VirtualHost 1.2.3.4>
+ * ServerAlias first
+ * </VirtualHost>
+ *
+ * <VirtualHost 2.3.4.5>
+ * ServerAlias second
+ * </VirtualHost>
*/
tmp_pool = make_sub_pool(s->pool);
pr_pool_tag(tmp_pool, "ServerAlias Processing Pool");
- ipbinds = find_server_ipbinds(tmp_pool, s);
- if (ipbinds == NULL) {
- destroy_pool(tmp_pool);
- return 0;
+ /* Remember that this will return cases where port is zero, too. */
+ ipbind = pr_ipbind_find(s->addr, s->ServerPort, FALSE);
+ if (ipbind != NULL &&
+ ipbind->ib_server->ServerPort == s->ServerPort) {
+ ipbinds = make_array(tmp_pool, 1, sizeof(pr_ipbind_t *));
+ *((pr_ipbind_t **) push_array(ipbinds)) = ipbind;
+
+ } else {
+ ipbinds = find_server_ipbinds(tmp_pool, s);
+ if (ipbinds == NULL) {
+ destroy_pool(tmp_pool);
+ return 0;
+ }
}
c = find_config(s->conf, CONF_PARAM, "ServerAlias", FALSE);
@@ -1310,11 +1453,17 @@ static unsigned int process_serveralias(server_rec *s) {
pr_signals_handle();
+ pr_trace_msg(trace_channel, 7, "handling ipbinds (%d) for ServerAlias '%s'",
+ ipbinds->nelts, (char *) c->argv[0]);
+
elts = ipbinds->elts;
for (i = 0; i < ipbinds->nelts; i++) {
- pr_ipbind_t *ipbind;
+ pr_signals_handle();
ipbind = elts[i];
+ pr_trace_msg(trace_channel, 7, "adding ServerAlias '%s' to server '%s'",
+ (char *) c->argv[0], s->ServerName);
+
res = pr_namebind_create(s, c->argv[0], ipbind, s->addr, s->ServerPort);
if (res == 0) {
namebind_count++;
@@ -1355,22 +1504,26 @@ static void trace_ipbind_table(void) {
register unsigned int j;
pr_ipbind_t *ipbind;
+ pr_signals_handle();
+
if (ipbind_table[i] == NULL) {
continue;
}
- pr_signals_handle();
-
pr_trace_msg(trace_channel, 25, " index %u:", i);
for (j = 0, ipbind = ipbind_table[i]; ipbind; j++, ipbind = ipbind->ib_next) {
array_header *namebinds;
+ pr_signals_handle();
namebinds = ipbind->ib_namebinds;
pr_trace_msg(trace_channel, 25, " ipbind %p:", ipbind);
pr_trace_msg(trace_channel, 25, " address: %s#%u",
pr_netaddr_get_ipstr(ipbind->ib_addr), ipbind->ib_port);
- pr_trace_msg(trace_channel, 25, " server: %p", ipbind->ib_server);
+ pr_trace_msg(trace_channel, 25, " server: %s (%p)",
+ ipbind->ib_server->ServerName, ipbind->ib_server);
+ pr_trace_msg(trace_channel, 25, " active: %s",
+ ipbind->ib_isactive ? "TRUE" : "FALSE");
if (namebinds != NULL) {
register unsigned int k;
@@ -1381,12 +1534,15 @@ static void trace_ipbind_table(void) {
for (k = 0; k < namebinds->nelts; k++) {
pr_namebind_t *namebind;
+ pr_signals_handle();
namebind = elts[k];
pr_trace_msg(trace_channel, 25, " #%u: %p", k+1, namebind);
pr_trace_msg(trace_channel, 25, " name: %s",
namebind->nb_name);
pr_trace_msg(trace_channel, 25, " server: %p",
namebind->nb_server);
+ pr_trace_msg(trace_channel, 25, " active: %s",
+ namebind->nb_isactive ? "TRUE" : "FALSE");
}
}
}
@@ -1410,7 +1566,7 @@ static int init_standalone_bindings(void) {
* IPv4 or an IPv6 wildcard socket?
*/
if (!SocketBindTight) {
-#ifdef PR_USE_IPV6
+#if defined(PR_USE_IPV6)
if (pr_netaddr_use_ipv6()) {
pr_inet_set_default_family(NULL, AF_INET6);
@@ -1442,17 +1598,39 @@ static int init_standalone_bindings(void) {
is_default = TRUE;
}
- if (main_server->ServerPort ||
- is_default) {
- PR_CREATE_IPBIND(main_server, main_server->addr, main_server->ServerPort);
- PR_OPEN_IPBIND(main_server->addr, main_server->ServerPort,
+ if (main_server->ServerPort > 0 ||
+ is_default == TRUE) {
+
+ res = pr_ipbind_create(main_server, main_server->addr,
+ main_server->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__,
+ __LINE__, main_server->ServerAddress, main_server->ServerPort,
+ strerror(errno));
+ }
+
+ res = pr_ipbind_open(main_server->addr, main_server->ServerPort,
main_server->listen, is_default, TRUE, TRUE);
- PR_ADD_IPBINDS(main_server);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(main_server->addr), strerror(errno));
+ }
+
+ res = pr_ipbind_add_binds(main_server);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__,
+ __LINE__, main_server->ServerAddress, strerror(errno));
+ }
}
for (serv = main_server->next; serv; serv = serv->next) {
unsigned int namebind_count;
+ pr_signals_handle();
+
namebind_count = process_serveralias(serv);
if (namebind_count > 0) {
/* If we successfully added ServerAlias namebinds, move on to the next
@@ -1472,8 +1650,8 @@ static int init_standalone_bindings(void) {
is_default = TRUE;
}
- if (serv->ServerPort) {
- if (!SocketBindTight) {
+ if (serv->ServerPort > 0) {
+ if (SocketBindTight == FALSE) {
#ifdef PR_USE_IPV6
if (pr_netaddr_use_ipv6()) {
pr_inet_set_default_family(NULL, AF_INET6);
@@ -1492,18 +1670,52 @@ static int init_standalone_bindings(void) {
return -1;
}
- PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort);
- PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default,
- FALSE, TRUE);
- PR_ADD_IPBINDS(serv);
+ res = pr_ipbind_create(serv, serv->addr, serv->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__,
+ __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno));
+ }
+
+ res = pr_ipbind_open(serv->addr, serv->ServerPort, serv->listen,
+ is_default, FALSE, TRUE);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(serv->addr), strerror(errno));
+ }
+
+ res = pr_ipbind_add_binds(serv);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__,
+ __LINE__, serv->ServerAddress, strerror(errno));
+ }
} else if (is_default) {
serv->listen = NULL;
- PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort);
- PR_OPEN_IPBIND(serv->addr, serv->ServerPort, serv->listen, is_default,
- FALSE, TRUE);
- PR_ADD_IPBINDS(serv);
+ res = pr_ipbind_create(serv, serv->addr, serv->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__,
+ __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno));
+ }
+
+ res = pr_ipbind_open(serv->addr, serv->ServerPort, serv->listen,
+ is_default, FALSE, TRUE);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(serv->addr), strerror(errno));
+ }
+
+ res = pr_ipbind_add_binds(serv);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__,
+ __LINE__, serv->ServerAddress, strerror(errno));
+ }
} else {
serv->listen = NULL;
@@ -1525,10 +1737,27 @@ static int init_standalone_bindings(void) {
serv->listen = main_server->listen;
register_cleanup2(serv->listen->pool, &serv->listen, server_cleanup_cb);
- PR_CREATE_IPBIND(serv, serv->addr, serv->ServerPort);
- PR_OPEN_IPBIND(serv->addr, serv->ServerPort, NULL, is_default, FALSE,
- TRUE);
- PR_ADD_IPBINDS(serv);
+ res = pr_ipbind_create(serv, serv->addr, serv->ServerPort);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to create ipbind '%s#%u': %s", __FILE__,
+ __LINE__, serv->ServerAddress, serv->ServerPort, strerror(errno));
+ }
+
+ res = pr_ipbind_open(serv->addr, serv->ServerPort, NULL, is_default,
+ FALSE, TRUE);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to open ipbind '%s': %s", __FILE__, __LINE__,
+ pr_netaddr_get_ipstr(serv->addr), strerror(errno));
+ }
+
+ res = pr_ipbind_add_binds(serv);
+ if (res < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "%s:%d: notice: unable to add binds to ipbind '%s': %s", __FILE__,
+ __LINE__, serv->ServerAddress, strerror(errno));
+ }
}
/* Process any ServerAlias directives AFTER the server's ipbind has been
@@ -1540,13 +1769,14 @@ static int init_standalone_bindings(void) {
trace_ipbind_table();
/* Any "unclaimed" listening conns can be removed and closed. */
- if (listening_conn_list) {
+ if (listening_conn_list != NULL) {
struct listener_rec *lr, *lrn;
for (lr = (struct listener_rec *) listening_conn_list->xas_list; lr; lr = lrn) {
- lrn = lr->next;
+ pr_signals_handle();
- if (!lr->claimed) {
+ lrn = lr->next;
+ if (lr->claimed == FALSE) {
xaset_remove(listening_conn_list, (xasetmember_t *) lr);
destroy_pool(lr->pool);
}
@@ -1585,4 +1815,3 @@ void init_bindings(void) {
exit(1);
}
}
-
=====================================
src/inet.c
=====================================
@@ -2,7 +2,7 @@
* ProFTPD - FTP server daemon
* Copyright (c) 1997, 1998 Public Flood Software
* Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver at tos.net>
- * Copyright (c) 2001-2021 The ProFTPD Project team
+ * Copyright (c) 2001-2022 The ProFTPD Project team
*
* 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
@@ -925,110 +925,139 @@ int pr_inet_set_proto_opts(pool *p, conn_t *c, int mss, int nodelay,
return 0;
}
-/* Set socket options on a connection. */
-int pr_inet_set_socket_opts2(pool *p, conn_t *c, int rcvbuf, int sndbuf,
- struct tcp_keepalive *tcp_keepalive, int reuse_port) {
+int pr_inet_set_proto_keepalive(pool *p, conn_t *c,
+ struct tcp_keepalive *tcp_keepalive) {
+ int keepalive = 1, val = -1;
- if (c == NULL) {
+ if (p == NULL ||
+ c == NULL ||
+ tcp_keepalive == NULL) {
errno = EINVAL;
return -1;
}
- /* Linux and "most" newer networking OSes probably use a highly adaptive
- * window size system, which generally wouldn't require user-space
- * modification at all. Thus, check the current sndbuf and rcvbuf sizes
- * before changing them, and only change them if we are making them larger
- * than their current size.
- */
+ if (c->listen_fd < 0) {
+ errno = EINVAL;
+ return -1;
+ }
- if (c->listen_fd != -1) {
- int keepalive = 1;
- int crcvbuf = 0, csndbuf = 0;
- socklen_t len;
+ keepalive = tcp_keepalive->keepalive_enabled;
- if (tcp_keepalive != NULL) {
- keepalive = tcp_keepalive->keepalive_enabled;
- }
+ pr_trace_msg(trace_channel, 17, "%s SO_KEEPALIVE on socket fd %d",
+ keepalive ? "enabling" : "disabling", c->listen_fd);
+ if (setsockopt(c->listen_fd, SOL_SOCKET, SO_KEEPALIVE, (void *) &keepalive,
+ sizeof(int)) < 0) {
+ pr_log_pri(PR_LOG_NOTICE, "error setting listen fd SO_KEEPALIVE: %s",
+ strerror(errno));
+ return 0;
+ }
- pr_trace_msg(trace_channel, 17, "%s SO_KEEPALIVE on socket fd %d",
- keepalive ? "enabling" : "disabling", c->listen_fd);
- if (setsockopt(c->listen_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)
- &keepalive, sizeof(int)) < 0) {
- pr_log_pri(PR_LOG_NOTICE, "error setting listen fd SO_KEEPALIVE: %s",
- strerror(errno));
+ if (keepalive == 0) {
+ return 0;
+ }
- } else {
- /* We only try to set the TCP keepalive specifics if SO_KEEPALIVE was
- * set successfully.
- */
- pr_trace_msg(trace_channel, 15,
- "enabled SO_KEEPALIVE on socket fd %d", c->listen_fd);
+ /* We only try to set the TCP keepalive specifics if SO_KEEPALIVE was
+ * enabled successfully.
+ */
+ pr_trace_msg(trace_channel, 15, "enabled SO_KEEPALIVE on socket fd %d",
+ c->listen_fd);
- if (tcp_keepalive != NULL) {
- int val = 0;
+ /* On Mac OS, the socket option is TCP_KEEPALIVE rather than
+ * TCP_KEEPIDLE.
+ */
+#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
+ val = tcp_keepalive->keepalive_idle;
+ if (val != -1) {
+ int option_name;
+
+# if defined(TCP_KEEPALIVE)
+ option_name = TCP_KEEPALIVE;
+# else
+ option_name = TCP_KEEPIDLE;
+# endif /* TCP_KEEPALIVE or TCP_KEEPIDLE */
-#if defined(TCP_KEEPIDLE)
- val = tcp_keepalive->keepalive_idle;
- if (val != -1) {
# ifdef __DragonFly__
- /* DragonFly BSD uses millsecs as the KEEPIDLE unit. */
- val *= 1000;
+ /* DragonFly BSD uses millsecs as the KEEPIDLE unit. */
+ val *= 1000;
# endif /* DragonFly BSD */
- if (setsockopt(c->listen_fd, SOL_SOCKET, TCP_KEEPIDLE, (void *)
- &val, sizeof(int)) < 0) {
- pr_log_pri(PR_LOG_NOTICE,
- "error setting TCP_KEEPIDLE %d on fd %d: %s", val, c->listen_fd,
- strerror(errno));
+ if (setsockopt(c->listen_fd, IPPROTO_TCP, option_name, (void *) &val,
+ sizeof(int)) < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "error setting TCP_KEEPIDLE %d on fd %d: %s", val, c->listen_fd,
+ strerror(errno));
- } else {
- pr_trace_msg(trace_channel, 15,
- "enabled TCP_KEEPIDLE %d on socket fd %d", val, c->listen_fd);
- }
- }
+ } else {
+ pr_trace_msg(trace_channel, 15,
+ "enabled TCP_KEEPIDLE %d on socket fd %d", val, c->listen_fd);
+ }
+ }
#endif /* TCP_KEEPIDLE */
#if defined(TCP_KEEPCNT)
- val = tcp_keepalive->keepalive_count;
- if (val != -1) {
- if (setsockopt(c->listen_fd, SOL_SOCKET, TCP_KEEPCNT, (void *)
- &val, sizeof(int)) < 0) {
- pr_log_pri(PR_LOG_NOTICE,
- "error setting TCP_KEEPCNT %d on fd %d: %s", val, c->listen_fd,
- strerror(errno));
+ val = tcp_keepalive->keepalive_count;
+ if (val != -1) {
+ if (setsockopt(c->listen_fd, IPPROTO_TCP, TCP_KEEPCNT, (void *) &val,
+ sizeof(int)) < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "error setting TCP_KEEPCNT %d on fd %d: %s", val, c->listen_fd,
+ strerror(errno));
- } else {
- pr_trace_msg(trace_channel, 15,
- "enabled TCP_KEEPCNT %d on socket fd %d", val, c->listen_fd);
- }
- }
+ } else {
+ pr_trace_msg(trace_channel, 15,
+ "enabled TCP_KEEPCNT %d on socket fd %d", val, c->listen_fd);
+ }
+ }
#endif /* TCP_KEEPCNT */
#if defined(TCP_KEEPINTVL)
- val = tcp_keepalive->keepalive_intvl;
- if (val != -1) {
+ val = tcp_keepalive->keepalive_intvl;
+ if (val != -1) {
# ifdef __DragonFly__
- /* DragonFly BSD uses millsecs as the KEEPINTVL unit. */
- val *= 1000;
+ /* DragonFly BSD uses millsecs as the KEEPINTVL unit. */
+ val *= 1000;
# endif /* DragonFly BSD */
- if (setsockopt(c->listen_fd, SOL_SOCKET, TCP_KEEPINTVL, (void *)
- &val, sizeof(int)) < 0) {
- pr_log_pri(PR_LOG_NOTICE,
- "error setting TCP_KEEPINTVL %d on fd %d: %s", val, c->listen_fd,
- strerror(errno));
+ if (setsockopt(c->listen_fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *) &val,
+ sizeof(int)) < 0) {
+ pr_log_pri(PR_LOG_NOTICE,
+ "error setting TCP_KEEPINTVL %d on fd %d: %s", val, c->listen_fd,
+ strerror(errno));
- } else {
- pr_trace_msg(trace_channel, 15,
- "enabled TCP_KEEPINTVL %d on socket fd %d", val, c->listen_fd);
- }
- }
+ } else {
+ pr_trace_msg(trace_channel, 15,
+ "enabled TCP_KEEPINTVL %d on socket fd %d", val, c->listen_fd);
+ }
+ }
#endif /* TCP_KEEPINTVL */
- /* Avoid compiler warnings on platforms which do not support any
- * of the above TCP keepalive macros.
- */
- (void) val;
- }
- }
+ /* Avoid compiler warnings on platforms which do not support any
+ * of the above TCP keepalive macros.
+ */
+ (void) val;
+
+ return 0;
+}
+
+/* Set socket options on a connection. */
+int pr_inet_set_socket_opts2(pool *p, conn_t *c, int rcvbuf, int sndbuf,
+ struct tcp_keepalive *tcp_keepalive, int reuse_port) {
+
+ if (c == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Linux and "most" newer networking OSes probably use a highly adaptive
+ * window size system, which generally wouldn't require user-space
+ * modification at all. Thus, check the current sndbuf and rcvbuf sizes
+ * before changing them, and only change them if we are making them larger
+ * than their current size.
+ */
+
+ if (c->listen_fd != -1) {
+ int crcvbuf = 0, csndbuf = 0;
+ socklen_t len;
+
+ (void) pr_inet_set_proto_keepalive(p, c, tcp_keepalive);
if (sndbuf > 0) {
len = sizeof(csndbuf);
@@ -1522,9 +1551,9 @@ int pr_inet_accept_nowait(pool *p, conn_t *c) {
*/
conn_t *pr_inet_accept(pool *p, conn_t *d, conn_t *c, int rfd, int wfd,
unsigned char resolve) {
+ config_rec *allow_foreign_addr_config = NULL;
conn_t *res = NULL;
- unsigned char *foreign_addr = NULL;
- int fd = -1, allow_foreign_address = FALSE;
+ int fd = -1;
pr_netaddr_t na;
socklen_t nalen;
@@ -1540,13 +1569,10 @@ conn_t *pr_inet_accept(pool *p, conn_t *d, conn_t *c, int rfd, int wfd,
pr_netaddr_set_family(&na, pr_netaddr_get_family(c->remote_addr));
nalen = pr_netaddr_get_sockaddr_len(&na);
+ allow_foreign_addr_config = find_config(TOPLEVEL_CONF, CONF_PARAM,
+ "AllowForeignAddress", FALSE);
d->mode = CM_ACCEPT;
- foreign_addr = get_param_ptr(TOPLEVEL_CONF, "AllowForeignAddress", FALSE);
- if (foreign_addr != NULL) {
- allow_foreign_address = *foreign_addr;
- }
-
/* A directive could enforce only IPv4 or IPv6 connections here, by
* actually using a sockaddr argument to accept(2), and checking the
* family of the connecting entity.
@@ -1566,28 +1592,79 @@ conn_t *pr_inet_accept(pool *p, conn_t *d, conn_t *c, int rfd, int wfd,
break;
}
- if (allow_foreign_address == FALSE) {
- /* If foreign addresses (i.e. IP addresses that do not match the
- * control connection's remote IP address) are not allowed, we
- * need to see just what our remote address IS.
- */
- if (getpeername(fd, pr_netaddr_get_sockaddr(&na), &nalen) < 0) {
- /* If getpeername(2) fails, should we still allow this connection?
- * Caution (and the AllowForeignAddress setting say "no".
+ if (allow_foreign_addr_config != NULL) {
+ int allowed;
+
+ allowed = *((int *) allow_foreign_addr_config->argv[0]);
+ if (allowed != TRUE) {
+ /* If foreign addresses (i.e. IP addresses that do not match the
+ * control connection's remote IP address) are not allowed, we
+ * need to see just what our remote address IS.
*/
- pr_log_pri(PR_LOG_DEBUG, "rejecting passive connection; "
- "failed to get address of remote peer: %s", strerror(errno));
- (void) close(fd);
- continue;
- }
- if (pr_netaddr_cmp(&na, c->remote_addr) != 0) {
- pr_log_pri(PR_LOG_NOTICE, "SECURITY VIOLATION: Passive connection "
- "from foreign IP address %s rejected (does not match client "
- "IP address %s).", pr_netaddr_get_ipstr(&na),
- pr_netaddr_get_ipstr(c->remote_addr));
- (void) close(fd);
- continue;
+ if (getpeername(fd, pr_netaddr_get_sockaddr(&na), &nalen) < 0) {
+ /* If getpeername(2) fails, should we still allow this connection?
+ * Caution (and the AllowForeignAddress setting) say "no".
+ */
+ pr_log_pri(PR_LOG_DEBUG, "rejecting passive connection; "
+ "failed to get address of remote peer: %s", strerror(errno));
+ (void) close(fd);
+ continue;
+ }
+
+ if (allowed == FALSE) {
+ if (pr_netaddr_cmp(&na, c->remote_addr) != 0) {
+ pr_log_pri(PR_LOG_NOTICE, "SECURITY VIOLATION: Passive connection "
+ "from foreign IP address %s rejected (does not match client "
+ "IP address %s).", pr_netaddr_get_ipstr(&na),
+ pr_netaddr_get_ipstr(c->remote_addr));
+
+ (void) close(fd);
+ d->mode = CM_ERROR;
+ d->xerrno = EACCES;
+
+ return NULL;
+ }
+
+ } else {
+ char *class_name;
+
+ /* Check the data connection remote address against BOTH the
+ * control connection remote address AND the configured <Class>.
+ */
+ class_name = allow_foreign_addr_config->argv[1];
+
+ if (pr_netaddr_cmp(&na, c->remote_addr) != 0) {
+ const pr_class_t *cls;
+
+ cls = pr_class_find(class_name);
+ if (cls != NULL) {
+ if (pr_class_satisfied(p, cls, &na) != TRUE) {
+ pr_log_debug(DEBUG8, "<Class> '%s' not satisfied by foreign "
+ "address '%s'", class_name, pr_netaddr_get_ipstr(&na));
+
+ pr_log_pri(PR_LOG_NOTICE,
+ "SECURITY VIOLATION: Passive connection from foreign IP "
+ "address %s rejected (does not match <Class %s>).",
+ pr_netaddr_get_ipstr(&na), class_name);
+
+ (void) close(fd);
+ d->mode = CM_ERROR;
+ d->xerrno = EACCES;
+ return NULL;
+ }
+
+ } else {
+ pr_log_debug(DEBUG8, "<Class> '%s' not found for filtering "
+ "AllowForeignAddress", class_name);
+ }
+
+ } else {
+ pr_log_debug(DEBUG9, "Passive connection from IP address '%s' "
+ "matches control connection address; skipping <Class> '%s'",
+ pr_netaddr_get_ipstr(&na), class_name);
+ }
+ }
}
}
=====================================
src/random.c
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - FTP server daemon
- * Copyright (c) 2017 The ProFTPD Project team
+ * Copyright (c) 2017-2022 The ProFTPD Project team
*
* 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
@@ -24,23 +24,25 @@
#include "conf.h"
+/* Note: Make sure that we initialize the state for both random(3) and rand(3),
+ * as modules/code may make use of either (or both) of them; see Issue #1396.
+ */
int pr_random_init(void) {
-#ifdef HAVE_RANDOM
+#if defined(HAVE_RANDOM)
struct timeval tv;
gettimeofday(&tv, NULL);
srandom(getpid() ^ tv.tv_usec);
-#else
- srand((unsigned int) (getpid() * time(NULL)));
#endif /* HAVE_RANDOM */
+ srand((unsigned int) (getpid() * time(NULL)));
return 0;
}
long pr_random_next(long min, long max) {
long r, scaled;
-#ifdef HAVE_RANDOM
+#if defined(HAVE_RANDOM)
r = random();
#else
r = (long) rand();
=====================================
tests/t/lib/ProFTPD/TestSuite/Utils.pm
=====================================
@@ -1210,6 +1210,8 @@ sub test_setup {
my $gid = shift;
$gid = 500 unless defined($gid);
my $home_dir = shift;
+ my $groups = shift;
+ $groups = $user unless defined($groups);
my $config_file = "$tmpdir/$name.conf";
my $pid_file = File::Spec->rel2abs("$tmpdir/$name.pid");
@@ -1238,7 +1240,7 @@ sub test_setup {
auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
'/bin/bash');
- auth_group_write($auth_group_file, $group, $gid, $user);
+ auth_group_write($auth_group_file, $group, $gid, $groups);
my $setup = {
auth_user_file => $auth_user_file,
=====================================
tests/t/lib/ProFTPD/Tests/Commands/NLST.pm
=====================================
@@ -157,6 +157,16 @@ my $TESTS = {
test_class => [qw(forking rootprivs)],
},
+ nlst_glob_with_rel_path_issue1325 => {
+ order => ++$order,
+ test_class => [qw(bug forking)],
+ },
+
+ nlst_glob_with_rel_path_dotdir_issue1325 => {
+ order => ++$order,
+ test_class => [qw(bug forking)],
+ },
+
};
sub new {
@@ -3616,22 +3626,7 @@ sub nlst_rel_path_chrooted_bug2496 {
sub nlst_parent_dir_bug4011 {
my $self = shift;
my $tmpdir = $self->{tmpdir};
-
- my $config_file = "$tmpdir/cmds.conf";
- my $pid_file = File::Spec->rel2abs("$tmpdir/cmds.pid");
- my $scoreboard_file = File::Spec->rel2abs("$tmpdir/cmds.scoreboard");
-
- my $log_file = test_get_logfile();
-
- my $auth_user_file = File::Spec->rel2abs("$tmpdir/cmds.passwd");
- my $auth_group_file = File::Spec->rel2abs("$tmpdir/cmds.group");
-
- my $user = 'proftpd';
- my $passwd = 'test';
- my $group = 'ftpd';
- my $home_dir = File::Spec->rel2abs($tmpdir);
- my $uid = 500;
- my $gid = 500;
+ my $setup = test_setup($tmpdir, 'cmds');
my $sub_dir1 = File::Spec->rel2abs("$tmpdir/dir1");
my $sub_dir2 = File::Spec->rel2abs("$tmpdir/dir1/dir2");
@@ -3662,26 +3657,22 @@ sub nlst_parent_dir_bug4011 {
# 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, $sub_dir1)) {
- die("Can't set perms on $home_dir to 0755: $!");
+ unless (chmod(0755, $sub_dir1)) {
+ die("Can't set perms on $sub_dir1 to 0755: $!");
}
- unless (chown($uid, $gid, $home_dir, $sub_dir1)) {
- die("Can't set owner of $home_dir to $uid/$gid: $!");
+ unless (chown($setup->{uid}, $setup->{gid}, $sub_dir1)) {
+ die("Can't set owner of $sub_dir1 to $setup->{uid}/$setup->{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 $config = {
- PidFile => $pid_file,
- ScoreboardFile => $scoreboard_file,
- SystemLog => $log_file,
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
- AuthUserFile => $auth_user_file,
- AuthGroupFile => $auth_group_file,
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
IfModules => {
'mod_delay.c' => {
@@ -3690,7 +3681,8 @@ sub nlst_parent_dir_bug4011 {
},
};
- my ($port, $config_user, $config_group) = config_write($config_file, $config);
+ my ($port, $config_user, $config_group) = config_write($setup->{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
@@ -3708,7 +3700,7 @@ sub nlst_parent_dir_bug4011 {
if ($pid) {
eval {
my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
- $client->login($user, $passwd);
+ $client->login($setup->{user}, $setup->{passwd});
$client->cwd("dir1");
$client->cwd("dir2");
@@ -3722,6 +3714,12 @@ sub nlst_parent_dir_bug4011 {
$conn->read($buf, 8192, 25);
eval { $conn->close() };
+ $client->quit();
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "# response:\n$buf\n";
+ }
+
# We have to be careful of the fact that readdir returns directory
# entries in an unordered fashion.
my $res = {};
@@ -3749,7 +3747,6 @@ sub nlst_parent_dir_bug4011 {
die("Unexpected name '$mismatch' appeared in NLST data")
}
};
-
if ($@) {
$ex = $@;
}
@@ -3758,7 +3755,7 @@ sub nlst_parent_dir_bug4011 {
$wfh->flush();
} else {
- eval { server_wait($config_file, $rfh) };
+ eval { server_wait($setup->{config_file}, $rfh) };
if ($@) {
warn($@);
exit 1;
@@ -3768,18 +3765,10 @@ sub nlst_parent_dir_bug4011 {
}
# Stop server
- server_stop($pid_file);
-
+ server_stop($setup->{pid_file});
$self->assert_child_ok($pid);
- if ($ex) {
- test_append_logfile($log_file, $ex);
- unlink($log_file);
-
- die($ex);
- }
-
- unlink($log_file);
+ test_cleanup($setup->{log_file}, $ex);
}
sub nlst_opt_a_root_dir_bug4069 {
@@ -4080,4 +4069,382 @@ sub nlst_opt_1_with_chroot {
unlink($log_file);
}
+sub nlst_glob_with_rel_path_issue1325 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'cmds');
+
+ my $test_path = File::Spec->rel2abs("$tmpdir/test.d");
+ mkpath($test_path);
+
+ for (my $i = 0; $i < 10; $i++) {
+ my $test_file = File::Spec->rel2abs("$test_path/TEST000$i.dat");
+ if (open(my $fh, "> $test_file")) {
+ print $fh "Hello, World!\n";
+ unless (close($fh)) {
+ die("Can't write $test_file: $!");
+ }
+
+ } else {
+ die("Can't open $test_file: $!");
+ }
+ }
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{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($setup->{user}, $setup->{passwd});
+
+ my $conn = $client->nlst_raw('test.d/TEST????.dat');
+ unless ($conn) {
+ die("Failed to NLST: " . $client->response_code() . " " .
+ $client->response_msg());
+ }
+
+ my $buf;
+ $conn->read($buf, 8192, 25);
+ eval { $conn->close() };
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "# response:\n$buf\n";
+ }
+
+ # We have to be careful of the fact that readdir returns directory
+ # entries in an unordered fashion.
+ my $res = {};
+ my $names = [split(/\n/, $buf)];
+ foreach my $name (@$names) {
+ $res->{$name} = 1;
+ }
+
+ $self->assert(scalar(@$names) > 0,
+ test_msg("Expected multiple names, got 0"));
+
+ my $expected = {
+ 'test.d/TEST0000.dat' => 1,
+ 'test.d/TEST0001.dat' => 1,
+ 'test.d/TEST0002.dat' => 1,
+ 'test.d/TEST0003.dat' => 1,
+ 'test.d/TEST0004.dat' => 1,
+ 'test.d/TEST0005.dat' => 1,
+ 'test.d/TEST0006.dat' => 1,
+ 'test.d/TEST0007.dat' => 1,
+ 'test.d/TEST0008.dat' => 1,
+ 'test.d/TEST0009.dat' => 1,
+ };
+
+ my $ok = 1;
+ my $mismatch;
+ foreach my $name (keys(%$res)) {
+ unless (defined($expected->{$name})) {
+ $mismatch = $name;
+ $ok = 0;
+ last;
+ }
+ }
+
+ unless ($ok) {
+ die("Unexpected name '$mismatch' appeared in NLST data")
+ }
+
+ # Now do it again, this time using an explicit relative path.
+
+ $conn = $client->nlst_raw('./test.d/TEST????.dat');
+ unless ($conn) {
+ die("Failed to NLST: " . $client->response_code() . " " .
+ $client->response_msg());
+ }
+
+ $buf = '';
+ $conn->read($buf, 8192, 25);
+ eval { $conn->close() };
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "# response:\n$buf\n";
+ }
+
+ # We have to be careful of the fact that readdir returns directory
+ # entries in an unordered fashion.
+ $res = {};
+ $names = [split(/\n/, $buf)];
+ foreach my $name (@$names) {
+ $res->{$name} = 1;
+ }
+
+ $self->assert(scalar(@$names) > 0,
+ test_msg("Expected multiple names, got 0"));
+
+ $expected = {
+ './test.d/TEST0000.dat' => 1,
+ './test.d/TEST0001.dat' => 1,
+ './test.d/TEST0002.dat' => 1,
+ './test.d/TEST0003.dat' => 1,
+ './test.d/TEST0004.dat' => 1,
+ './test.d/TEST0005.dat' => 1,
+ './test.d/TEST0006.dat' => 1,
+ './test.d/TEST0007.dat' => 1,
+ './test.d/TEST0008.dat' => 1,
+ './test.d/TEST0009.dat' => 1,
+ };
+
+ $ok = 1;
+ $mismatch = '';
+ foreach my $name (keys(%$res)) {
+ unless (defined($expected->{$name})) {
+ $mismatch = $name;
+ $ok = 0;
+ last;
+ }
+ }
+
+ unless ($ok) {
+ die("Unexpected name '$mismatch' appeared in NLST data")
+ }
+
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
+sub nlst_glob_with_rel_path_dotdir_issue1325 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'cmds');
+
+ my $test_path = File::Spec->rel2abs("$tmpdir/.test.d");
+ mkpath($test_path);
+
+ for (my $i = 0; $i < 10; $i++) {
+ my $test_file = File::Spec->rel2abs("$test_path/TEST000$i.dat");
+ if (open(my $fh, "> $test_file")) {
+ print $fh "Hello, World!\n";
+ unless (close($fh)) {
+ die("Can't write $test_file: $!");
+ }
+
+ } else {
+ die("Can't open $test_file: $!");
+ }
+ }
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{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($setup->{user}, $setup->{passwd});
+
+ my $conn = $client->nlst_raw('.test.d/TEST????.dat');
+ unless ($conn) {
+ die("Failed to NLST: " . $client->response_code() . " " .
+ $client->response_msg());
+ }
+
+ my $buf;
+ $conn->read($buf, 8192, 25);
+ eval { $conn->close() };
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "# response:\n$buf\n";
+ }
+
+ # We have to be careful of the fact that readdir returns directory
+ # entries in an unordered fashion.
+ my $res = {};
+ my $names = [split(/\n/, $buf)];
+ foreach my $name (@$names) {
+ $res->{$name} = 1;
+ }
+
+ $self->assert(scalar(@$names) > 0,
+ test_msg("Expected multiple names, got 0"));
+
+ my $expected = {
+ '.test.d/TEST0000.dat' => 1,
+ '.test.d/TEST0001.dat' => 1,
+ '.test.d/TEST0002.dat' => 1,
+ '.test.d/TEST0003.dat' => 1,
+ '.test.d/TEST0004.dat' => 1,
+ '.test.d/TEST0005.dat' => 1,
+ '.test.d/TEST0006.dat' => 1,
+ '.test.d/TEST0007.dat' => 1,
+ '.test.d/TEST0008.dat' => 1,
+ '.test.d/TEST0009.dat' => 1,
+ };
+
+ my $ok = 1;
+ my $mismatch;
+ foreach my $name (keys(%$res)) {
+ unless (defined($expected->{$name})) {
+ $mismatch = $name;
+ $ok = 0;
+ last;
+ }
+ }
+
+ unless ($ok) {
+ die("Unexpected name '$mismatch' appeared in NLST data")
+ }
+
+ # Now do it again, this time using an explicit relative path.
+
+ $conn = $client->nlst_raw('./.test.d/TEST????.dat');
+ unless ($conn) {
+ die("Failed to NLST: " . $client->response_code() . " " .
+ $client->response_msg());
+ }
+
+ $buf = '';
+ $conn->read($buf, 8192, 25);
+ eval { $conn->close() };
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "# response:\n$buf\n";
+ }
+
+ # We have to be careful of the fact that readdir returns directory
+ # entries in an unordered fashion.
+ $res = {};
+ $names = [split(/\n/, $buf)];
+ foreach my $name (@$names) {
+ $res->{$name} = 1;
+ }
+
+ $self->assert(scalar(@$names) > 0,
+ test_msg("Expected multiple names, got 0"));
+
+ $expected = {
+ './.test.d/TEST0000.dat' => 1,
+ './.test.d/TEST0001.dat' => 1,
+ './.test.d/TEST0002.dat' => 1,
+ './.test.d/TEST0003.dat' => 1,
+ './.test.d/TEST0004.dat' => 1,
+ './.test.d/TEST0005.dat' => 1,
+ './.test.d/TEST0006.dat' => 1,
+ './.test.d/TEST0007.dat' => 1,
+ './.test.d/TEST0008.dat' => 1,
+ './.test.d/TEST0009.dat' => 1,
+ };
+
+ $ok = 1;
+ $mismatch = '';
+ foreach my $name (keys(%$res)) {
+ unless (defined($expected->{$name})) {
+ $mismatch = $name;
+ $ok = 0;
+ last;
+ }
+ }
+
+ unless ($ok) {
+ die("Unexpected name '$mismatch' appeared in NLST data")
+ }
+
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
1;
=====================================
tests/t/lib/ProFTPD/Tests/Config/AllowForeignAddress.pm
=====================================
@@ -26,11 +26,16 @@ my $TESTS = {
test_class => [qw(forking)],
},
- fxp_denied_by_class => {
+ fxp_port_denied_by_class => {
order => ++$order,
test_class => [qw(forking)],
},
+ fxp_pasv_denied_by_class_issue1346 => {
+ order => ++$order,
+ test_class => [qw(bug forking)],
+ },
+
fxp_allowed => {
order => ++$order,
test_class => [qw(forking)],
@@ -41,11 +46,16 @@ my $TESTS = {
test_class => [qw(forking)],
},
- fxp_allowed_by_class => {
+ fxp_port_allowed_by_class => {
order => ++$order,
test_class => [qw(forking)],
},
+ fxp_pasv_allowed_by_class_issue1346 => {
+ order => ++$order,
+ test_class => [qw(bug forking)],
+ },
+
fxp_allowed_2gb => {
order => ++$order,
test_class => [qw(forking)],
@@ -353,7 +363,7 @@ sub fxp_denied_eprt {
test_cleanup($setup->{log_file}, $ex);
}
-sub fxp_denied_by_class {
+sub fxp_port_denied_by_class {
my $self = shift;
my $tmpdir = $self->{tmpdir};
my $setup = test_setup($tmpdir, 'config');
@@ -519,6 +529,142 @@ EOC
test_cleanup($setup->{log_file}, $ex);
}
+sub fxp_pasv_denied_by_class_issue1346 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'config');
+
+ my $class_name = 'allowed_fxp';
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+
+ AllowForeignAddress => $class_name,
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<Class $class_name>
+ From none
+</Class>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ # 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, 0, 3);
+ $client->login($setup->{user}, $setup->{passwd});
+
+ # Attemping a data transfer should fail, due to the AllowForeignAddress
+ # class restriction.
+
+ my $conn = $client->list_raw();
+ unless ($conn) {
+ die("LIST failed: " . $client->response_code() . ' ' .
+ $client->response_msg());
+ }
+
+ my $buf;
+ $conn->read($buf, 8192, 30);
+ eval { $conn->close() };
+
+ my ($resp_code, $resp_msg);
+ $resp_code = $client->response_code();
+ $resp_msg = $client->response_msg();
+
+ $self->assert_transfer_ok($resp_code, $resp_msg);
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ eval {
+ if (open(my $fh, "< $setup->{log_file}")) {
+ my $ok = 1;
+
+ while (my $line = <$fh>) {
+ chomp($line);
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "$line\n";
+ }
+
+ if ($line =~ /SECURITY VIOLATION/) {
+ $ok = 0;
+ last;
+ }
+
+ if ($line =~ /Passive connection from IP address \S+ matches control connection address; skipping <Class> '\S+'/) {
+ last;
+ }
+ }
+
+ close($fh);
+
+ $self->assert($ok, "Did not see expected log messages");
+
+ } else {
+ die("Can't read $setup->{log_file}: $!");
+ }
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
sub fxp_allowed {
my $self = shift;
my $tmpdir = $self->{tmpdir};
@@ -781,7 +927,7 @@ sub fxp_allowed_eprt {
test_cleanup($setup->{log_file}, $ex);
}
-sub fxp_allowed_by_class {
+sub fxp_port_allowed_by_class {
my $self = shift;
my $tmpdir = $self->{tmpdir};
my $setup = test_setup($tmpdir, 'config');
@@ -926,6 +1072,110 @@ EOC
test_cleanup($setup->{log_file}, $ex);
}
+sub fxp_pasv_allowed_by_class_issue1346 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'config');
+
+ my $class_name = 'allowed_fxp';
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'class:20 inet:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+
+ AllowForeignAddress => $class_name,
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<Class $class_name>
+ From 127.0.0.0/8
+</Class>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ # 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, 0, 3);
+ $client->login($setup->{user}, $setup->{passwd});
+
+ # Attemping a data transfer should succeed, due to the AllowForeignAddress
+ # class restriction.
+ my $conn = $client->list_raw();
+ unless ($conn) {
+ die("Failed to LIST: " . $client->response_code() . " " .
+ $client->response_msg());
+ }
+
+ my $buf;
+ $conn->read($buf, 8192, 30);
+ eval { $conn->close() };
+
+ my ($resp_code, $resp_msg);
+ $resp_code = $client->response_code();
+ $resp_msg = $client->response_msg();
+
+ $self->assert_transfer_ok($resp_code, $resp_msg);
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
sub fxp_allowed_2gb {
my $self = shift;
my $tmpdir = $self->{tmpdir};
=====================================
tests/t/lib/ProFTPD/Tests/Config/PassivePorts.pm
=====================================
@@ -30,6 +30,10 @@ my $TESTS = {
test_class => [qw(forking)],
},
+ pasv_ports_random_issue1396 => {
+ order => ++$order,
+ test_class => [qw(bug forking)],
+ },
};
sub new {
@@ -493,4 +497,112 @@ EOC
unlink($log_file);
}
+sub pasv_ports_random_issue1396 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'config');
+
+ my $min_port = 30000;
+ my $max_port = 31000;
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:0 data:10',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ AuthOrder => 'mod_auth_file.c',
+
+ PassivePorts => "$min_port $max_port",
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{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 $first_pasv_port;
+
+ for (my $i = 0; $i < 3; $i++) {
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+ $client->login($setup->{user}, $setup->{passwd});
+
+ my ($resp_code, $resp_msg) = $client->pasv();
+ $client->quit();
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "# $resp_code $resp_msg\n";
+ }
+
+ my $expected = 227;
+ $self->assert($expected == $resp_code,
+ test_msg("Expected response code $expected, got $resp_code"));
+
+ $expected = '\(\d+,\d+,\d+,\d+,\d+,\d+\)';
+ $self->assert(qr/$expected/, $resp_msg,
+ test_msg("Expected response message '$expected', got '$resp_msg'"));
+
+ unless ($resp_msg =~ /\(\d+,\d+,\d+,\d+,(\d+),(\d+)\)/) {
+ die("Response '$resp_msg' does not match expected pattern");
+ }
+
+ my $pasv_port = ($1 * 256) + $2;
+ $self->assert($min_port <= $pasv_port && $max_port >= $pasv_port,
+ test_msg("Expected port from $min_port to $max_port, got $pasv_port"));
+
+ if (defined($first_pasv_port)) {
+ $self->assert($pasv_port != $first_pasv_port,
+ test_msg("Expected different port than $first_pasv_port for subsequent sessions ($pasv_port)"));
+
+ } else {
+ $first_pasv_port = $pasv_port;
+ }
+ }
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
1;
=====================================
tests/t/lib/ProFTPD/Tests/Modules/mod_auth_file.pm
=====================================
@@ -122,6 +122,10 @@ my $TESTS = {
test_class => [qw(bug forking)],
},
+ auth_file_line_too_long_issue1321 => {
+ order => ++$order,
+ test_class => [qw(bug forking)],
+ },
};
sub new {
@@ -2271,4 +2275,80 @@ sub auth_file_symlink_segfault_bug4145 {
unlink($log_file);
}
+sub auth_file_line_too_long_issue1321 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+
+ # For Issue #1321, we create a very long AuthGroupFile entry with many
+ # group names.
+
+ my $groups = 'proftpd';
+ for (my $i = 0; $i < 200; $i++) {
+ $groups .= ",quite.long.example.group.$i";
+ }
+
+ my $setup = test_setup($tmpdir, 'authfile', undef, undef, undef, undef, undef,
+ undef, $groups);
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{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($setup->{user}, $setup->{passwd});
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
1;
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/38d1167fa88e41a7ff792fe901dc014867d7f418
--
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd/-/commit/38d1167fa88e41a7ff792fe901dc014867d7f418
You're receiving this email because of your account on salsa.debian.org.
More information about the Pkg-proftpd-maintainers
mailing list