package ProFTPD::Tests::Modules::mod_proxy_protocol; use lib qw(t/lib); use base qw(ProFTPD::TestSuite::Child); use strict; use File::Path qw(mkpath); use File::Spec; use IO::Handle; use Net::Cmd qw(CMD_OK); use ProFTPD::TestSuite::FTP; use ProFTPD::TestSuite::ProxiedFTP; use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); $| = 1; my $order = 0; my $TESTS = { proxy_protocol_login_with_proxy => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_login_without_proxy => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_config_denyclass => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_bad_start_of_line => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_bad_end_of_line => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_bad_proto => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_bad_src_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_dns_src_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_bad_dst_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_dns_dst_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_bad_src_port => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_bad_dst_port => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_too_large_src_port => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_too_large_dst_port => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_tcp4_with_ipv6_src_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_tcp4_with_ipv6_dst_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_tcp6_with_ipv4_src_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_tcp6_with_ipv4_dst_addr => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_tcp6_with_useipv6_off => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_matching_src_dst_info => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_unknown_proto => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_active_transfer_with_proxy => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_passive_transfer_with_proxy => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_active_transfer_with_proxy_allowforeignaddress => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, proxy_protocol_passive_transfer_with_proxy_allowforeignaddress => { order => ++$order, test_class => [qw(forking mod_proxy_protocol)], }, }; sub new { return shift()->SUPER::new(@_); } sub list_tests { return testsuite_get_runnable_tests($TESTS); } sub proxy_protocol_login_with_proxy { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); my $client = ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 222]); $client->login($setup->{user}, $setup->{passwd}); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_login_without_proxy { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $proxy_timeout = 1; 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', ProxyProtocolTimeout => $proxy_timeout, }, }, }; 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 { sleep(2); my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 1, 1, 1); $client->login($setup->{user}, $setup->{passwd}); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, $proxy_timeout + 5) }; if ($@) { warn($@); exit 1; } exit 0; } # Stop server server_stop($setup->{pid_file}); $self->assert_child_ok($pid); if ($ex) { $ex = undef; } else { $ex = "Connection succeeded unexpectedly"; } test_cleanup($setup->{log_file}, $ex); } sub proxy_protocol_config_denyclass { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; my ($port, $config_user, $config_group) = config_write($setup->{config_file}, $config); if (open(my $fh, ">> $setup->{config_file}")) { print $fh < From 1.1.1.1 DenyClass test 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 { sleep(2); my $client = ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 222]); eval { $client->login($setup->{user}, $setup->{passwd}) }; unless ($@) { die("Login succeeded unexpectedly"); } $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_bad_start_of_line { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, "GET /index.html HTTP/1.1.1\r\n") }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_bad_end_of_line { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, '222 ' . 'A' x 128]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_bad_proto { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['IPV4', '1.1.1.1', '2.2.2.2', 111, '222']); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_bad_src_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', 'foo', '2.2.2.2', 111, '222']); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_dns_src_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', 'localhost', '2.2.2.2', 111, '222']); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_bad_dst_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', 'bar', 111, '222']); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_dns_dst_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', 'localhost', 111, '222']); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_bad_src_port { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 'baz', '222']); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_bad_dst_port { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 'quxx']); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_too_large_src_port { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 70000, 222]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_too_large_dst_port { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 70000]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_tcp4_with_ipv6_src_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, UseIPv6 => 'on', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '::1', '2.2.2.2', 111, 222]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_tcp4_with_ipv6_dst_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, UseIPv6 => 'on', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '::2', 111, 222]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_tcp6_with_ipv4_src_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, UseIPv6 => 'on', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP6', '1.1.1.1', '::2', 111, 222]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_tcp6_with_ipv4_dst_addr { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, UseIPv6 => 'on', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP6', '::1', '2.2.2.2', 111, 222]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_tcp6_with_useipv6_off { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $config = { PidFile => $setup->{pid_file}, ScoreboardFile => $setup->{scoreboard_file}, SystemLog => $setup->{log_file}, AuthUserFile => $setup->{auth_user_file}, AuthGroupFile => $setup->{auth_group_file}, UseIPv6 => 'off', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP6', '::1', '::2', 111, 222]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_matching_src_dst_info { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); eval { ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '1.1.1.1', 111, 111]); }; unless ($@) { die("Connection succeeded unexpectedly"); } }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_unknown_proto { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); 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', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); my $client = ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['UNKNOWN', '1.1.1.1', '2.2.2.2', 111, 222]); $client->login($setup->{user}, $setup->{passwd}); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_active_transfer_with_proxy { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $test_file = File::Spec->rel2abs("$tmpdir/test.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}, # Note: If AllowForeignAddress is not allowed, then the PORT command # will fail with the following e.g. error being logged: # # Refused PORT 127,0,0,1,218,225 (address mismatch) # AllowForeignAddress => 'off', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); $ENV{FTP_PASSIVE} = 0; my $client = ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 222]); $client->login($setup->{user}, $setup->{passwd}); if ($client->get('test.dat', '/dev/null')) { die("RETR test.dat succeeded unexpectedly"); } $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_passive_transfer_with_proxy { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $test_file = File::Spec->rel2abs("$tmpdir/test.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}, # Note: If AllowForeignAddress is not allowed, then the data transfer # will fail with the following e.g. error being logged: # # SECURITY VIOLATION: Passive connection from 127.0.0.1 rejected. # AllowForeignAddress => 'off', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); $ENV{FTP_PASSIVE} = 1; my $client = ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 222]); $client->login($setup->{user}, $setup->{passwd}); if ($client->get('test.dat', '/dev/null')) { die("RETR test.dat succeeded unexpectedly"); } # Note: we should send QUIT here, but because proftpd treated this as # a security violation, it terminated the control connection as well. }; 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 proxy_protocol_active_transfer_with_proxy_allowforeignaddress { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $test_file = File::Spec->rel2abs("$tmpdir/test.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}, # Note: If AllowForeignAddress is not allowed, then the PORT command # will fail with the following e.g. error being logged: # # Refused PORT 127,0,0,1,218,225 (address mismatch) # AllowForeignAddress => 'on', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); $ENV{FTP_PASSIVE} = 0; my $client = ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 222]); $client->login($setup->{user}, $setup->{passwd}); unless ($client->get('test.dat', '/dev/null')) { die("RETR test.dat failed: " . $client->code . " " . $client->message); } my $resp_code = $client->code; my $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $client->quit(); }; if ($@) { $ex = $@; } $wfh->print("done\n"); $wfh->flush(); } else { eval { server_wait($setup->{config_file}, $rfh, 10) }; 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 proxy_protocol_passive_transfer_with_proxy_allowforeignaddress { my $self = shift; my $tmpdir = $self->{tmpdir}; my $setup = test_setup($tmpdir, 'proxy_protocol'); my $test_file = File::Spec->rel2abs("$tmpdir/test.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}, # Note: If AllowForeignAddress is not allowed, then the data transfer # will fail with the following e.g. error being logged: # # SECURITY VIOLATION: Passive connection from 127.0.0.1 rejected. # AllowForeignAddress => 'on', IfModules => { 'mod_delay.c' => { DelayEngine => 'off', }, 'mod_proxy_protocol.c' => { ProxyProtocolEngine => 'on', }, }, }; 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 { sleep(2); $ENV{FTP_PASSIVE} = 1; my $client = ProFTPD::TestSuite::ProxiedFTP->new('127.0.0.1', $port, ['TCP4', '1.1.1.1', '2.2.2.2', 111, 222]); $client->login($setup->{user}, $setup->{passwd}); unless ($client->get('test.dat', '/dev/null')) { die("RETR test.dat failed: " . $client->code . " " . $client->message); } my $resp_code = $client->code; my $expected = 226; $self->assert($expected == $resp_code, test_msg("Expected response code $expected, got $resp_code")); $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;