1package ProFTPD::Tests::Config::PassivePorts; 2 3use lib qw(t/lib); 4use base qw(ProFTPD::TestSuite::Child); 5use strict; 6 7use File::Spec; 8use IO::Handle; 9 10use ProFTPD::TestSuite::FTP; 11use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); 12 13$| = 1; 14 15my $order = 0; 16 17my $TESTS = { 18 pasv_ports_server_config => { 19 order => ++$order, 20 test_class => [qw(forking)], 21 }, 22 23 pasv_ports_global => { 24 order => ++$order, 25 test_class => [qw(forking)], 26 }, 27 28 pasv_ports_vhost => { 29 order => ++$order, 30 test_class => [qw(forking)], 31 }, 32 33}; 34 35sub new { 36 return shift()->SUPER::new(@_); 37} 38 39sub list_tests { 40 return testsuite_get_runnable_tests($TESTS); 41} 42 43sub pasv_ports_server_config { 44 my $self = shift; 45 my $tmpdir = $self->{tmpdir}; 46 47 my $config_file = "$tmpdir/config.conf"; 48 my $pid_file = File::Spec->rel2abs("$tmpdir/config.pid"); 49 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/config.scoreboard"); 50 51 my $log_file = test_get_logfile(); 52 53 my $auth_user_file = File::Spec->rel2abs("$tmpdir/config.passwd"); 54 my $auth_group_file = File::Spec->rel2abs("$tmpdir/config.group"); 55 56 my $user = 'proftpd'; 57 my $passwd = 'test'; 58 my $group = 'ftpd'; 59 my $home_dir = File::Spec->rel2abs($tmpdir); 60 my $uid = 500; 61 my $gid = 500; 62 63 # Make sure that, if we're running as root, that the home directory has 64 # permissions/privs set for the account we create 65 if ($< == 0) { 66 unless (chmod(0755, $home_dir)) { 67 die("Can't set perms on $home_dir to 0755: $!"); 68 } 69 70 unless (chown($uid, $gid, $home_dir)) { 71 die("Can't set owner of $home_dir to $uid/$gid: $!"); 72 } 73 } 74 75 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 76 '/bin/bash'); 77 auth_group_write($auth_group_file, $group, $gid, $user); 78 79 my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); 80 if (open(my $fh, "> $test_file")) { 81 print $fh "ABCD" x 8192; 82 unless (close($fh)) { 83 die("Can't write $test_file: $!"); 84 } 85 86 } else { 87 die("Can't open $test_file: $!"); 88 } 89 90 my $min_port = 40100; 91 my $max_port = 40200; 92 93 my $config = { 94 PidFile => $pid_file, 95 ScoreboardFile => $scoreboard_file, 96 SystemLog => $log_file, 97 TraceLog => $log_file, 98 Trace => 'DEFAULT:0 data:10', 99 100 AuthUserFile => $auth_user_file, 101 AuthGroupFile => $auth_group_file, 102 PassivePorts => "$min_port $max_port", 103 104 IfModules => { 105 'mod_delay.c' => { 106 DelayEngine => 'off', 107 }, 108 }, 109 }; 110 111 my ($port, $config_user, $config_group) = config_write($config_file, $config); 112 113 # Open pipes, for use between the parent and child processes. Specifically, 114 # the child will indicate when it's done with its test by writing a message 115 # to the parent. 116 my ($rfh, $wfh); 117 unless (pipe($rfh, $wfh)) { 118 die("Can't open pipe: $!"); 119 } 120 121 my $ex; 122 123 # Fork child 124 $self->handle_sigchld(); 125 defined(my $pid = fork()) or die("Can't fork: $!"); 126 if ($pid) { 127 eval { 128 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 129 $client->login($user, $passwd); 130 131 my ($resp_code, $resp_msg) = $client->pasv(); 132 $client->quit(); 133 134 my $expected; 135 136 $expected = 227; 137 $self->assert($expected == $resp_code, 138 test_msg("Expected response code $expected, got $resp_code")); 139 140 $expected = '\(\d+,\d+,\d+,\d+,\d+,\d+\)'; 141 $self->assert(qr/$expected/, $resp_msg, 142 test_msg("Expected response message '$expected', got '$resp_msg'")); 143 144 unless ($resp_msg =~ /\(\d+,\d+,\d+,\d+,(\d+),(\d+)\)/) { 145 die("Response '$resp_msg' does not match expected pattern"); 146 } 147 148 my $pasv_port = ($1 * 256) + $2; 149 150 $self->assert($min_port <= $pasv_port && $max_port >= $pasv_port, 151 test_msg("Expected port from $min_port to $max_port, got $pasv_port")); 152 }; 153 154 if ($@) { 155 $ex = $@; 156 } 157 158 $wfh->print("done\n"); 159 $wfh->flush(); 160 161 } else { 162 eval { server_wait($config_file, $rfh) }; 163 if ($@) { 164 warn($@); 165 exit 1; 166 } 167 168 exit 0; 169 } 170 171 # Stop server 172 server_stop($pid_file); 173 174 $self->assert_child_ok($pid); 175 176 if ($ex) { 177 test_append_logfile($log_file, $ex); 178 unlink($log_file); 179 180 die($ex); 181 } 182 183 unlink($log_file); 184} 185 186sub pasv_ports_global { 187 my $self = shift; 188 my $tmpdir = $self->{tmpdir}; 189 190 my $config_file = "$tmpdir/config.conf"; 191 my $pid_file = File::Spec->rel2abs("$tmpdir/config.pid"); 192 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/config.scoreboard"); 193 194 my $log_file = test_get_logfile(); 195 196 my $auth_user_file = File::Spec->rel2abs("$tmpdir/config.passwd"); 197 my $auth_group_file = File::Spec->rel2abs("$tmpdir/config.group"); 198 199 my $user = 'proftpd'; 200 my $passwd = 'test'; 201 my $group = 'ftpd'; 202 my $home_dir = File::Spec->rel2abs($tmpdir); 203 my $uid = 500; 204 my $gid = 500; 205 206 # Make sure that, if we're running as root, that the home directory has 207 # permissions/privs set for the account we create 208 if ($< == 0) { 209 unless (chmod(0755, $home_dir)) { 210 die("Can't set perms on $home_dir to 0755: $!"); 211 } 212 213 unless (chown($uid, $gid, $home_dir)) { 214 die("Can't set owner of $home_dir to $uid/$gid: $!"); 215 } 216 } 217 218 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 219 '/bin/bash'); 220 auth_group_write($auth_group_file, $group, $gid, $user); 221 222 my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); 223 if (open(my $fh, "> $test_file")) { 224 print $fh "ABCD" x 8192; 225 unless (close($fh)) { 226 die("Can't write $test_file: $!"); 227 } 228 229 } else { 230 die("Can't open $test_file: $!"); 231 } 232 233 my $min_port = 40100; 234 my $max_port = 40200; 235 236 my $config = { 237 PidFile => $pid_file, 238 ScoreboardFile => $scoreboard_file, 239 SystemLog => $log_file, 240 TraceLog => $log_file, 241 Trace => 'DEFAULT:0 data:10', 242 243 AuthUserFile => $auth_user_file, 244 AuthGroupFile => $auth_group_file, 245 246 Global => { 247 PassivePorts => "$min_port $max_port", 248 }, 249 250 IfModules => { 251 'mod_delay.c' => { 252 DelayEngine => 'off', 253 }, 254 }, 255 }; 256 257 my ($port, $config_user, $config_group) = config_write($config_file, $config); 258 259 # Open pipes, for use between the parent and child processes. Specifically, 260 # the child will indicate when it's done with its test by writing a message 261 # to the parent. 262 my ($rfh, $wfh); 263 unless (pipe($rfh, $wfh)) { 264 die("Can't open pipe: $!"); 265 } 266 267 my $ex; 268 269 # Fork child 270 $self->handle_sigchld(); 271 defined(my $pid = fork()) or die("Can't fork: $!"); 272 if ($pid) { 273 eval { 274 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 275 $client->login($user, $passwd); 276 277 my ($resp_code, $resp_msg) = $client->pasv(); 278 $client->quit(); 279 280 my $expected; 281 282 $expected = 227; 283 $self->assert($expected == $resp_code, 284 test_msg("Expected response code $expected, got $resp_code")); 285 286 $expected = '\(\d+,\d+,\d+,\d+,\d+,\d+\)'; 287 $self->assert(qr/$expected/, $resp_msg, 288 test_msg("Expected response message '$expected', got '$resp_msg'")); 289 290 unless ($resp_msg =~ /\(\d+,\d+,\d+,\d+,(\d+),(\d+)\)/) { 291 die("Response '$resp_msg' does not match expected pattern"); 292 } 293 294 my $pasv_port = ($1 * 256) + $2; 295 296 $self->assert($min_port <= $pasv_port && $max_port >= $pasv_port, 297 test_msg("Expected port from $min_port to $max_port, got $pasv_port")); 298 }; 299 300 if ($@) { 301 $ex = $@; 302 } 303 304 $wfh->print("done\n"); 305 $wfh->flush(); 306 307 } else { 308 eval { server_wait($config_file, $rfh) }; 309 if ($@) { 310 warn($@); 311 exit 1; 312 } 313 314 exit 0; 315 } 316 317 # Stop server 318 server_stop($pid_file); 319 320 $self->assert_child_ok($pid); 321 322 if ($ex) { 323 test_append_logfile($log_file, $ex); 324 unlink($log_file); 325 326 die($ex); 327 } 328 329 unlink($log_file); 330} 331 332sub pasv_ports_vhost { 333 my $self = shift; 334 my $tmpdir = $self->{tmpdir}; 335 336 my $config_file = "$tmpdir/config.conf"; 337 my $pid_file = File::Spec->rel2abs("$tmpdir/config.pid"); 338 my $scoreboard_file = File::Spec->rel2abs("$tmpdir/config.scoreboard"); 339 340 my $log_file = test_get_logfile(); 341 342 my $auth_user_file = File::Spec->rel2abs("$tmpdir/config.passwd"); 343 my $auth_group_file = File::Spec->rel2abs("$tmpdir/config.group"); 344 345 my $user = 'proftpd'; 346 my $passwd = 'test'; 347 my $group = 'ftpd'; 348 my $home_dir = File::Spec->rel2abs($tmpdir); 349 my $uid = 500; 350 my $gid = 500; 351 352 # Make sure that, if we're running as root, that the home directory has 353 # permissions/privs set for the account we create 354 if ($< == 0) { 355 unless (chmod(0755, $home_dir)) { 356 die("Can't set perms on $home_dir to 0755: $!"); 357 } 358 359 unless (chown($uid, $gid, $home_dir)) { 360 die("Can't set owner of $home_dir to $uid/$gid: $!"); 361 } 362 } 363 364 auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir, 365 '/bin/bash'); 366 auth_group_write($auth_group_file, $group, $gid, $user); 367 368 my $test_file = File::Spec->rel2abs("$tmpdir/test.txt"); 369 if (open(my $fh, "> $test_file")) { 370 print $fh "ABCD" x 8192; 371 unless (close($fh)) { 372 die("Can't write $test_file: $!"); 373 } 374 375 } else { 376 die("Can't open $test_file: $!"); 377 } 378 379 my $min_port = 40100; 380 my $max_port = 40200; 381 382 my $config = { 383 PidFile => $pid_file, 384 ScoreboardFile => $scoreboard_file, 385 SystemLog => $log_file, 386 TraceLog => $log_file, 387 Trace => 'DEFAULT:0 data:10', 388 389 AuthUserFile => $auth_user_file, 390 AuthGroupFile => $auth_group_file, 391 Port => '0', 392 SocketBindTight => 'on', 393 394 IfModules => { 395 'mod_delay.c' => { 396 DelayEngine => 'off', 397 }, 398 }, 399 }; 400 401 my ($port, $config_user, $config_group) = config_write($config_file, $config); 402 403 my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port(); 404 405 if (open(my $fh, ">> $config_file")) { 406 print $fh <<EOC; 407<VirtualHost 127.0.0.1> 408 ServerName "Vhost" 409 Port $vhost_port 410 AuthUserFile $auth_user_file 411 AuthGroupFile $auth_group_file 412 PassivePorts $min_port $max_port 413</VirtualHost> 414EOC 415 unless (close($fh)) { 416 die("Can't write $config_file: $!"); 417 } 418 419 } else { 420 die("Can't open $config_file: $!"); 421 } 422 423 # Open pipes, for use between the parent and child processes. Specifically, 424 # the child will indicate when it's done with its test by writing a message 425 # to the parent. 426 my ($rfh, $wfh); 427 unless (pipe($rfh, $wfh)) { 428 die("Can't open pipe: $!"); 429 } 430 431 my $ex; 432 433 # Fork child 434 $self->handle_sigchld(); 435 defined(my $pid = fork()) or die("Can't fork: $!"); 436 if ($pid) { 437 eval { 438 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $vhost_port); 439 $client->login($user, $passwd); 440 441 my ($resp_code, $resp_msg) = $client->pasv(); 442 $client->quit(); 443 444 my $expected; 445 446 $expected = 227; 447 $self->assert($expected == $resp_code, 448 test_msg("Expected response code $expected, got $resp_code")); 449 450 $expected = '\(\d+,\d+,\d+,\d+,\d+,\d+\)'; 451 $self->assert(qr/$expected/, $resp_msg, 452 test_msg("Expected response message '$expected', got '$resp_msg'")); 453 454 unless ($resp_msg =~ /\(\d+,\d+,\d+,\d+,(\d+),(\d+)\)/) { 455 die("Response '$resp_msg' does not match expected pattern"); 456 } 457 458 my $pasv_port = ($1 * 256) + $2; 459 460 $self->assert($min_port <= $pasv_port && $max_port >= $pasv_port, 461 test_msg("Expected port from $min_port to $max_port, got $pasv_port")); 462 }; 463 464 if ($@) { 465 $ex = $@; 466 } 467 468 $wfh->print("done\n"); 469 $wfh->flush(); 470 471 } else { 472 eval { server_wait($config_file, $rfh) }; 473 if ($@) { 474 warn($@); 475 exit 1; 476 } 477 478 exit 0; 479 } 480 481 # Stop server 482 server_stop($pid_file); 483 484 $self->assert_child_ok($pid); 485 486 if ($ex) { 487 test_append_logfile($log_file, $ex); 488 unlink($log_file); 489 490 die($ex); 491 } 492 493 unlink($log_file); 494} 495 4961; 497