1package ProFTPD::Tests::Commands::HOST; 2 3use lib qw(t/lib); 4use base qw(ProFTPD::TestSuite::Child); 5use strict; 6 7use File::Spec; 8use IO::Handle; 9use Sys::HostAddr; 10 11use ProFTPD::TestSuite::FTP; 12use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite); 13 14$| = 1; 15 16my $order = 0; 17 18my $TESTS = { 19 host_after_login_fails => { 20 order => ++$order, 21 test_class => [qw(forking)], 22 }, 23 24 host_login_succeeds => { 25 order => ++$order, 26 test_class => [qw(forking)], 27 }, 28 29 # TODO: 30 # host_default_server_login_succeeds 31 # where we do NOT send HOST, and mark an aliased name-based host with 32 # DefaultServer 33 34 host_literal_ipv6_fails_useipv6_off => { 35 order => ++$order, 36 test_class => [qw(feature_ipv6 forking)], 37 }, 38 39 host_literal_ipv6_with_port_fails => { 40 order => ++$order, 41 test_class => [qw(feature_ipv6 forking)], 42 }, 43 44 host_invalid_ipv4_fails => { 45 order => ++$order, 46 test_class => [qw(forking)], 47 }, 48 49 host_ipv4_with_port_fails => { 50 order => ++$order, 51 test_class => [qw(forking)], 52 }, 53 54 host_unknown_host_fails => { 55 order => ++$order, 56 test_class => [qw(forking)], 57 }, 58 59 host_known_ipv4_same_host_ok => { 60 order => ++$order, 61 test_class => [qw(forking)], 62 }, 63 64 host_known_ipv6_same_host_ok => { 65 order => ++$order, 66 test_class => [qw(feature_ipv6 forking)], 67 }, 68 69 host_known_ipv4_different_host_fails => { 70 order => ++$order, 71 test_class => [qw(forking)], 72 }, 73 74 host_known_ipv6_different_host_fails => { 75 order => ++$order, 76 test_class => [qw(feature_ipv6 forking)], 77 }, 78 79 host_known_dns_ok => { 80 order => ++$order, 81 test_class => [qw(forking)], 82 }, 83 84 host_before_feat_ok => { 85 order => ++$order, 86 test_class => [qw(forking mod_tls)], 87 }, 88 89 host_after_feat_ok => { 90 order => ++$order, 91 test_class => [qw(forking mod_tls)], 92 }, 93 94 # Various config situations 95 96 # 2-vhost config, 2 vhost (same addr, different DNS) config, 97 98 host_config_limit_denied => { 99 order => ++$order, 100 test_class => [qw(forking)], 101 }, 102}; 103 104sub new { 105 return shift()->SUPER::new(@_); 106} 107 108sub list_tests { 109 return testsuite_get_runnable_tests($TESTS); 110} 111 112sub host_login_succeeds { 113 my $self = shift; 114 my $tmpdir = $self->{tmpdir}; 115 my $setup = test_setup($tmpdir, 'cmds'); 116 117 my $config = { 118 PidFile => $setup->{pid_file}, 119 ScoreboardFile => $setup->{scoreboard_file}, 120 SystemLog => $setup->{log_file}, 121 TraceLog => $setup->{log_file}, 122 Trace => 'DEFAULT:20', 123 124 AuthOrder => 'mod_auth_pam.c mod_auth_unix.c', 125 DefaultServer => 'on', 126 127 IfModules => { 128 'mod_delay.c' => { 129 DelayEngine => 'off', 130 }, 131 }, 132 }; 133 134 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 135 $config); 136 137 if (open(my $fh, ">> $setup->{config_file}")) { 138 print $fh <<EOC; 139<VirtualHost 127.0.0.1> 140 ServerAlias localhost 141 Port $port 142 143 AuthUserFile $setup->{auth_user_file} 144 AuthGroupFile $setup->{auth_group_file} 145 AuthOrder mod_auth_file.c 146 147 <IfModule mod_delay.c> 148 DelayEngine off 149 </IfModule> 150</VirtualHost> 151EOC 152 unless (close($fh)) { 153 die("Can't write $setup->{config_file}: $!"); 154 } 155 156 } else { 157 die("Can't open $setup->{config_file}: $!"); 158 } 159 160 # Open pipes, for use between the parent and child processes. Specifically, 161 # the child will indicate when it's done with its test by writing a message 162 # to the parent. 163 my ($rfh, $wfh); 164 unless (pipe($rfh, $wfh)) { 165 die("Can't open pipe: $!"); 166 } 167 168 my $ex; 169 170 # Fork child 171 $self->handle_sigchld(); 172 defined(my $pid = fork()) or die("Can't fork: $!"); 173 if ($pid) { 174 eval { 175 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 176 $client->host('localhost'); 177 $client->login($setup->{user}, $setup->{passwd}); 178 179 my $resp_code = $client->response_code(); 180 my $resp_msg = $client->response_msg(); 181 182 my $expected; 183 184 $expected = 230; 185 $self->assert($expected == $resp_code, 186 test_msg("Expected response code $expected, got $resp_code")); 187 188 $expected = "User $setup->{user} logged in"; 189 $self->assert($expected eq $resp_msg, 190 test_msg("Expected response message '$expected', got '$resp_msg'")); 191 }; 192 193 if ($@) { 194 $ex = $@; 195 } 196 197 $wfh->print("done\n"); 198 $wfh->flush(); 199 200 } else { 201 eval { server_wait($setup->{config_file}, $rfh) }; 202 if ($@) { 203 warn($@); 204 exit 1; 205 } 206 207 exit 0; 208 } 209 210 # Stop server 211 server_stop($setup->{pid_file}); 212 213 $self->assert_child_ok($pid); 214 215 test_cleanup($setup->{log_file}, $ex); 216} 217 218sub host_after_login_fails { 219 my $self = shift; 220 my $tmpdir = $self->{tmpdir}; 221 my $setup = test_setup($tmpdir, 'cmds'); 222 223 my $config = { 224 PidFile => $setup->{pid_file}, 225 ScoreboardFile => $setup->{scoreboard_file}, 226 SystemLog => $setup->{log_file}, 227 TraceLog => $setup->{log_file}, 228 Trace => 'DEFAULT:20', 229 230 AuthUserFile => $setup->{auth_user_file}, 231 AuthGroupFile => $setup->{auth_group_file}, 232 233 IfModules => { 234 'mod_delay.c' => { 235 DelayEngine => 'off', 236 }, 237 }, 238 }; 239 240 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 241 $config); 242 243 # Open pipes, for use between the parent and child processes. Specifically, 244 # the child will indicate when it's done with its test by writing a message 245 # to the parent. 246 my ($rfh, $wfh); 247 unless (pipe($rfh, $wfh)) { 248 die("Can't open pipe: $!"); 249 } 250 251 my $ex; 252 253 # Fork child 254 $self->handle_sigchld(); 255 defined(my $pid = fork()) or die("Can't fork: $!"); 256 if ($pid) { 257 eval { 258 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 259 $client->login($setup->{user}, $setup->{passwd}); 260 261 eval { $client->host('localhost') }; 262 unless ($@) { 263 die("HOST after login succeeded unexpectedly"); 264 } 265 266 my $resp_code = $client->response_code(); 267 my $resp_msg = $client->response_msg(); 268 269 my $expected; 270 271 $expected = 503; 272 $self->assert($expected == $resp_code, 273 test_msg("Expected response code $expected, got $resp_code")); 274 275 $expected = "Bad sequence of commands"; 276 $self->assert($expected eq $resp_msg, 277 test_msg("Expected response message '$expected', got '$resp_msg'")); 278 }; 279 280 if ($@) { 281 $ex = $@; 282 } 283 284 $wfh->print("done\n"); 285 $wfh->flush(); 286 287 } else { 288 eval { server_wait($setup->{config_file}, $rfh) }; 289 if ($@) { 290 warn($@); 291 exit 1; 292 } 293 294 exit 0; 295 } 296 297 # Stop server 298 server_stop($setup->{pid_file}); 299 300 $self->assert_child_ok($pid); 301 302 test_cleanup($setup->{log_file}, $ex); 303} 304 305sub host_literal_ipv6_fails_useipv6_off { 306 my $self = shift; 307 my $tmpdir = $self->{tmpdir}; 308 my $setup = test_setup($tmpdir, 'cmds'); 309 310 my $config = { 311 PidFile => $setup->{pid_file}, 312 ScoreboardFile => $setup->{scoreboard_file}, 313 SystemLog => $setup->{log_file}, 314 315 AuthUserFile => $setup->{auth_user_file}, 316 AuthGroupFile => $setup->{auth_group_file}, 317 UseIPv6 => 'off', 318 319 IfModules => { 320 'mod_delay.c' => { 321 DelayEngine => 'off', 322 }, 323 }, 324 }; 325 326 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 327 $config); 328 329 # Open pipes, for use between the parent and child processes. Specifically, 330 # the child will indicate when it's done with its test by writing a message 331 # to the parent. 332 my ($rfh, $wfh); 333 unless (pipe($rfh, $wfh)) { 334 die("Can't open pipe: $!"); 335 } 336 337 my $ex; 338 339 # Fork child 340 $self->handle_sigchld(); 341 defined(my $pid = fork()) or die("Can't fork: $!"); 342 if ($pid) { 343 eval { 344 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 345 346 my $host = '[::1]'; 347 eval { $client->host($host) }; 348 unless ($@) { 349 die("HOST after login succeeded unexpectedly"); 350 } 351 352 my $resp_code = $client->response_code(); 353 my $resp_msg = $client->response_msg(); 354 355 my $expected; 356 357 $expected = 501; 358 $self->assert($expected == $resp_code, 359 test_msg("Expected response code $expected, got $resp_code")); 360 361 $expected = "$host: Invalid hostname provided"; 362 $self->assert($expected eq $resp_msg, 363 test_msg("Expected response message '$expected', got '$resp_msg'")); 364 }; 365 366 if ($@) { 367 $ex = $@; 368 } 369 370 $wfh->print("done\n"); 371 $wfh->flush(); 372 373 } else { 374 eval { server_wait($setup->{config_file}, $rfh) }; 375 if ($@) { 376 warn($@); 377 exit 1; 378 } 379 380 exit 0; 381 } 382 383 # Stop server 384 server_stop($setup->{pid_file}); 385 386 $self->assert_child_ok($pid); 387 388 test_cleanup($setup->{log_file}, $ex); 389} 390 391sub host_literal_ipv6_with_port_fails { 392 my $self = shift; 393 my $tmpdir = $self->{tmpdir}; 394 my $setup = test_setup($tmpdir, 'cmds'); 395 396 my $config = { 397 PidFile => $setup->{pid_file}, 398 ScoreboardFile => $setup->{scoreboard_file}, 399 SystemLog => $setup->{log_file}, 400 401 AuthUserFile => $setup->{auth_user_file}, 402 AuthGroupFile => $setup->{auth_group_file}, 403 UseIPv6 => 'on', 404 405 IfModules => { 406 'mod_delay.c' => { 407 DelayEngine => 'off', 408 }, 409 }, 410 }; 411 412 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 413 $config); 414 415 # Open pipes, for use between the parent and child processes. Specifically, 416 # the child will indicate when it's done with its test by writing a message 417 # to the parent. 418 my ($rfh, $wfh); 419 unless (pipe($rfh, $wfh)) { 420 die("Can't open pipe: $!"); 421 } 422 423 my $ex; 424 425 # Fork child 426 $self->handle_sigchld(); 427 defined(my $pid = fork()) or die("Can't fork: $!"); 428 if ($pid) { 429 eval { 430 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 431 432 my $host = "[::1]:$port"; 433 eval { $client->host($host) }; 434 unless ($@) { 435 die("HOST after login succeeded unexpectedly"); 436 } 437 438 my $resp_code = $client->response_code(); 439 my $resp_msg = $client->response_msg(); 440 441 my $expected; 442 443 $expected = 501; 444 $self->assert($expected == $resp_code, 445 test_msg("Expected response code $expected, got $resp_code")); 446 447 $expected = "$host: Invalid IPv6 address provided"; 448 $self->assert($expected eq $resp_msg, 449 test_msg("Expected response message '$expected', got '$resp_msg'")); 450 }; 451 452 if ($@) { 453 $ex = $@; 454 } 455 456 $wfh->print("done\n"); 457 $wfh->flush(); 458 459 } else { 460 eval { server_wait($setup->{config_file}, $rfh) }; 461 if ($@) { 462 warn($@); 463 exit 1; 464 } 465 466 exit 0; 467 } 468 469 # Stop server 470 server_stop($setup->{pid_file}); 471 472 $self->assert_child_ok($pid); 473 474 test_cleanup($setup->{log_file}, $ex); 475} 476 477sub host_invalid_ipv4_fails { 478 my $self = shift; 479 my $tmpdir = $self->{tmpdir}; 480 my $setup = test_setup($tmpdir, 'cmds'); 481 482 my $config = { 483 PidFile => $setup->{pid_file}, 484 ScoreboardFile => $setup->{scoreboard_file}, 485 SystemLog => $setup->{log_file}, 486 487 AuthUserFile => $setup->{auth_user_file}, 488 AuthGroupFile => $setup->{auth_group_file}, 489 490 IfModules => { 491 'mod_delay.c' => { 492 DelayEngine => 'off', 493 }, 494 }, 495 }; 496 497 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 498 $config); 499 500 # Open pipes, for use between the parent and child processes. Specifically, 501 # the child will indicate when it's done with its test by writing a message 502 # to the parent. 503 my ($rfh, $wfh); 504 unless (pipe($rfh, $wfh)) { 505 die("Can't open pipe: $!"); 506 } 507 508 my $ex; 509 510 # Fork child 511 $self->handle_sigchld(); 512 defined(my $pid = fork()) or die("Can't fork: $!"); 513 if ($pid) { 514 eval { 515 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 516 517 my $host = "300.400.500.600"; 518 eval { $client->host($host) }; 519 unless ($@) { 520 die("HOST after login succeeded unexpectedly"); 521 } 522 523 my $resp_code = $client->response_code(); 524 my $resp_msg = $client->response_msg(); 525 526 my $expected; 527 528 $expected = 504; 529 $self->assert($expected == $resp_code, 530 test_msg("Expected response code $expected, got $resp_code")); 531 532 $expected = "$host: Unknown hostname provided"; 533 $self->assert($expected eq $resp_msg, 534 test_msg("Expected response message '$expected', got '$resp_msg'")); 535 }; 536 537 if ($@) { 538 $ex = $@; 539 } 540 541 $wfh->print("done\n"); 542 $wfh->flush(); 543 544 } else { 545 eval { server_wait($setup->{config_file}, $rfh) }; 546 if ($@) { 547 warn($@); 548 exit 1; 549 } 550 551 exit 0; 552 } 553 554 # Stop server 555 server_stop($setup->{pid_file}); 556 557 $self->assert_child_ok($pid); 558 559 test_cleanup($setup->{log_file}, $ex); 560} 561 562sub host_ipv4_with_port_fails { 563 my $self = shift; 564 my $tmpdir = $self->{tmpdir}; 565 my $setup = test_setup($tmpdir, 'cmds'); 566 567 my $config = { 568 PidFile => $setup->{pid_file}, 569 ScoreboardFile => $setup->{scoreboard_file}, 570 SystemLog => $setup->{log_file}, 571 572 AuthUserFile => $setup->{auth_user_file}, 573 AuthGroupFile => $setup->{auth_group_file}, 574 575 IfModules => { 576 'mod_delay.c' => { 577 DelayEngine => 'off', 578 }, 579 }, 580 }; 581 582 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 583 $config); 584 585 # Open pipes, for use between the parent and child processes. Specifically, 586 # the child will indicate when it's done with its test by writing a message 587 # to the parent. 588 my ($rfh, $wfh); 589 unless (pipe($rfh, $wfh)) { 590 die("Can't open pipe: $!"); 591 } 592 593 my $ex; 594 595 # Fork child 596 $self->handle_sigchld(); 597 defined(my $pid = fork()) or die("Can't fork: $!"); 598 if ($pid) { 599 eval { 600 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 601 602 my $host = "127.0.0.1:$port"; 603 eval { $client->host($host) }; 604 unless ($@) { 605 die("HOST after login succeeded unexpectedly"); 606 } 607 608 my $resp_code = $client->response_code(); 609 my $resp_msg = $client->response_msg(); 610 611 my $expected; 612 613 $expected = 501; 614 $self->assert($expected == $resp_code, 615 test_msg("Expected response code $expected, got $resp_code")); 616 617 $expected = "$host: Invalid hostname provided"; 618 $self->assert($expected eq $resp_msg, 619 test_msg("Expected response message '$expected', got '$resp_msg'")); 620 }; 621 622 if ($@) { 623 $ex = $@; 624 } 625 626 $wfh->print("done\n"); 627 $wfh->flush(); 628 629 } else { 630 eval { server_wait($setup->{config_file}, $rfh) }; 631 if ($@) { 632 warn($@); 633 exit 1; 634 } 635 636 exit 0; 637 } 638 639 # Stop server 640 server_stop($setup->{pid_file}); 641 642 $self->assert_child_ok($pid); 643 644 test_cleanup($setup->{log_file}, $ex); 645} 646 647sub host_unknown_host_fails { 648 my $self = shift; 649 my $tmpdir = $self->{tmpdir}; 650 my $setup = test_setup($tmpdir, 'cmds'); 651 652 my $config = { 653 PidFile => $setup->{pid_file}, 654 ScoreboardFile => $setup->{scoreboard_file}, 655 SystemLog => $setup->{log_file}, 656 657 AuthUserFile => $setup->{auth_user_file}, 658 AuthGroupFile => $setup->{auth_group_file}, 659 660 IfModules => { 661 'mod_delay.c' => { 662 DelayEngine => 'off', 663 }, 664 }, 665 }; 666 667 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 668 $config); 669 670 # Open pipes, for use between the parent and child processes. Specifically, 671 # the child will indicate when it's done with its test by writing a message 672 # to the parent. 673 my ($rfh, $wfh); 674 unless (pipe($rfh, $wfh)) { 675 die("Can't open pipe: $!"); 676 } 677 678 my $ex; 679 680 # Fork child 681 $self->handle_sigchld(); 682 defined(my $pid = fork()) or die("Can't fork: $!"); 683 if ($pid) { 684 eval { 685 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 686 my $host = "nosuchname.proftpd.org"; 687 688 eval { $client->host($host) }; 689 unless ($@) { 690 die("HOST $host succeeded unexpectedly"); 691 } 692 693 my $resp_code = $client->response_code(); 694 my $resp_msg = $client->response_msg(); 695 696 my $expected; 697 698 $expected = 504; 699 $self->assert($expected == $resp_code, 700 test_msg("Expected response code $expected, got $resp_code")); 701 702 $expected = "$host: Unknown hostname provided"; 703 $self->assert($expected eq $resp_msg, 704 test_msg("Expected response message '$expected', got '$resp_msg'")); 705 }; 706 707 if ($@) { 708 $ex = $@; 709 } 710 711 $wfh->print("done\n"); 712 $wfh->flush(); 713 714 } else { 715 eval { server_wait($setup->{config_file}, $rfh) }; 716 if ($@) { 717 warn($@); 718 exit 1; 719 } 720 721 exit 0; 722 } 723 724 # Stop server 725 server_stop($setup->{pid_file}); 726 727 $self->assert_child_ok($pid); 728 729 test_cleanup($setup->{log_file}, $ex); 730} 731 732sub host_known_ipv4_same_host_ok { 733 my $self = shift; 734 my $tmpdir = $self->{tmpdir}; 735 my $setup = test_setup($tmpdir, 'cmds'); 736 737 my $config = { 738 PidFile => $setup->{pid_file}, 739 ScoreboardFile => $setup->{scoreboard_file}, 740 SystemLog => $setup->{log_file}, 741 742 AuthUserFile => $setup->{auth_user_file}, 743 AuthGroupFile => $setup->{auth_group_file}, 744 745 IfModules => { 746 'mod_delay.c' => { 747 DelayEngine => 'off', 748 }, 749 }, 750 }; 751 752 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 753 $config); 754 755 # Open pipes, for use between the parent and child processes. Specifically, 756 # the child will indicate when it's done with its test by writing a message 757 # to the parent. 758 my ($rfh, $wfh); 759 unless (pipe($rfh, $wfh)) { 760 die("Can't open pipe: $!"); 761 } 762 763 my $ex; 764 765 # Fork child 766 $self->handle_sigchld(); 767 defined(my $pid = fork()) or die("Can't fork: $!"); 768 if ($pid) { 769 eval { 770 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 771 my $host = "127.0.0.1"; 772 773 $client->host($host); 774 my $resp_code = $client->response_code(); 775 my $resp_msg = $client->response_msg(); 776 777 my $expected; 778 779 $expected = 220; 780 $self->assert($expected == $resp_code, 781 test_msg("Expected response code $expected, got $resp_code")); 782 783 $expected = "HOST command successful"; 784 $self->assert($expected eq $resp_msg, 785 test_msg("Expected response message '$expected', got '$resp_msg'")); 786 }; 787 788 if ($@) { 789 $ex = $@; 790 } 791 792 $wfh->print("done\n"); 793 $wfh->flush(); 794 795 } else { 796 eval { server_wait($setup->{config_file}, $rfh) }; 797 if ($@) { 798 warn($@); 799 exit 1; 800 } 801 802 exit 0; 803 } 804 805 # Stop server 806 server_stop($setup->{pid_file}); 807 808 $self->assert_child_ok($pid); 809 810 test_cleanup($setup->{log_file}, $ex); 811} 812 813sub host_known_ipv6_same_host_ok { 814 my $self = shift; 815 my $tmpdir = $self->{tmpdir}; 816 my $setup = test_setup($tmpdir, 'cmds'); 817 818 my $config = { 819 PidFile => $setup->{pid_file}, 820 ScoreboardFile => $setup->{scoreboard_file}, 821 SystemLog => $setup->{log_file}, 822 823 AuthUserFile => $setup->{auth_user_file}, 824 AuthGroupFile => $setup->{auth_group_file}, 825 UseIPv6 => 'on', 826 DefaultAddress => '::1', 827 828 IfModules => { 829 'mod_delay.c' => { 830 DelayEngine => 'off', 831 }, 832 }, 833 }; 834 835 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 836 $config); 837 838 # Open pipes, for use between the parent and child processes. Specifically, 839 # the child will indicate when it's done with its test by writing a message 840 # to the parent. 841 my ($rfh, $wfh); 842 unless (pipe($rfh, $wfh)) { 843 die("Can't open pipe: $!"); 844 } 845 846 my $ex; 847 848 # Fork child 849 $self->handle_sigchld(); 850 defined(my $pid = fork()) or die("Can't fork: $!"); 851 if ($pid) { 852 eval { 853 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 854 my $host = "[::ffff:127.0.0.1]"; 855 856 $client->host($host); 857 858 my $resp_code = $client->response_code(); 859 my $resp_msg = $client->response_msg(); 860 861 my $expected; 862 863 $expected = 220; 864 $self->assert($expected == $resp_code, 865 test_msg("Expected response code $expected, got $resp_code")); 866 867 $expected = "HOST command successful"; 868 $self->assert($expected eq $resp_msg, 869 test_msg("Expected response message '$expected', got '$resp_msg'")); 870 }; 871 872 if ($@) { 873 $ex = $@; 874 } 875 876 $wfh->print("done\n"); 877 $wfh->flush(); 878 879 } else { 880 eval { server_wait($setup->{config_file}, $rfh) }; 881 if ($@) { 882 warn($@); 883 exit 1; 884 } 885 886 exit 0; 887 } 888 889 # Stop server 890 server_stop($setup->{pid_file}); 891 892 $self->assert_child_ok($pid); 893 894 test_cleanup($setup->{log_file}, $ex); 895} 896 897sub host_known_ipv4_different_host_fails { 898 my $self = shift; 899 my $tmpdir = $self->{tmpdir}; 900 my $setup = test_setup($tmpdir, 'cmds'); 901 902 my $ipv4_addr = Sys::HostAddr->new(); 903 my $v4_addrs = $ipv4_addr->addresses(); 904 905 my $vhost_addr; 906 foreach my $v4_addr (@$v4_addrs) { 907 if ($v4_addr ne '127.0.0.1') { 908 $vhost_addr = $v4_addr; 909 last; 910 } 911 } 912 913 if (!defined($vhost_addr)) { 914 print STDERR "+ Unable to find additional IPv4 addresses, skipping test\n"; 915 return; 916 } 917 918 my $config = { 919 PidFile => $setup->{pid_file}, 920 ScoreboardFile => $setup->{scoreboard_file}, 921 SystemLog => $setup->{log_file}, 922 923 AuthUserFile => $setup->{auth_user_file}, 924 AuthGroupFile => $setup->{auth_group_file}, 925 926 IfModules => { 927 'mod_delay.c' => { 928 DelayEngine => 'off', 929 }, 930 }, 931 }; 932 933 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 934 $config); 935 936 if (open(my $fh, ">> $setup->{config_file}")) { 937 print $fh <<EOC; 938<VirtualHost $vhost_addr> 939 Port $port 940 ServerName "Other Virtual Host" 941</VirtualHost> 942EOC 943 unless (close($fh)) { 944 die("Can't write $setup->{config_file}: $!"); 945 } 946 947 } else { 948 die("Can't open $setup->{config_file}: $!"); 949 } 950 951 # Open pipes, for use between the parent and child processes. Specifically, 952 # the child will indicate when it's done with its test by writing a message 953 # to the parent. 954 my ($rfh, $wfh); 955 unless (pipe($rfh, $wfh)) { 956 die("Can't open pipe: $!"); 957 } 958 959 my $ex; 960 961 # Fork child 962 $self->handle_sigchld(); 963 defined(my $pid = fork()) or die("Can't fork: $!"); 964 if ($pid) { 965 eval { 966 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 967 my $host = $vhost_addr; 968 969 eval { $client->host($host) }; 970 unless ($@) { 971 die("HOST $host succeeded unexpectedly"); 972 } 973 974 my $resp_code = $client->response_code(); 975 my $resp_msg = $client->response_msg(); 976 977 my $expected; 978 979 $expected = 504; 980 $self->assert($expected == $resp_code, 981 test_msg("Expected response code $expected, got $resp_code")); 982 983 $expected = "$host: Unknown hostname provided"; 984 $self->assert($expected eq $resp_msg, 985 test_msg("Expected response message '$expected', got '$resp_msg'")); 986 }; 987 988 if ($@) { 989 $ex = $@; 990 } 991 992 $wfh->print("done\n"); 993 $wfh->flush(); 994 995 } else { 996 eval { server_wait($setup->{config_file}, $rfh) }; 997 if ($@) { 998 warn($@); 999 exit 1; 1000 } 1001 1002 exit 0; 1003 } 1004 1005 # Stop server 1006 server_stop($setup->{pid_file}); 1007 1008 $self->assert_child_ok($pid); 1009 1010 test_cleanup($setup->{log_file}, $ex); 1011} 1012 1013sub host_known_ipv6_different_host_fails { 1014 my $self = shift; 1015 my $tmpdir = $self->{tmpdir}; 1016 my $setup = test_setup($tmpdir, 'cmds'); 1017 1018 my $ipv6_addr = Sys::HostAddr->new(ipv => 6); 1019 my $v6_addrs = $ipv6_addr->addresses(); 1020 1021 my $vhost_addr; 1022 foreach my $v6_addr (@$v6_addrs) { 1023 if ($v6_addr ne '::1') { 1024 $vhost_addr = $v6_addr; 1025 1026 # Trim off the link-local scope, if present 1027 # $vhost_addr =~ s/\%(.*)?$//; 1028 1029 last; 1030 } 1031 } 1032 1033 if (!defined($vhost_addr)) { 1034 print STDERR "+ Unable to find additional IPv6 addresses, skipping test\n"; 1035 return; 1036 } 1037 1038 my $config = { 1039 PidFile => $setup->{pid_file}, 1040 ScoreboardFile => $setup->{scoreboard_file}, 1041 SystemLog => $setup->{log_file}, 1042 1043 AuthUserFile => $setup->{auth_user_file}, 1044 AuthGroupFile => $setup->{auth_group_file}, 1045 UseIPv6 => 'on', 1046 DefaultAddress => '::1', 1047 1048 IfModules => { 1049 'mod_delay.c' => { 1050 DelayEngine => 'off', 1051 }, 1052 }, 1053 }; 1054 1055 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 1056 $config); 1057 1058 if (open(my $fh, ">> $setup->{config_file}")) { 1059 print $fh <<EOC; 1060<VirtualHost $vhost_addr> 1061 Port $port 1062 ServerName "Other Virtual Host" 1063</VirtualHost> 1064EOC 1065 unless (close($fh)) { 1066 die("Can't write $setup->{config_file}: $!"); 1067 } 1068 1069 } else { 1070 die("Can't open $setup->{config_file}: $!"); 1071 } 1072 1073 # Open pipes, for use between the parent and child processes. Specifically, 1074 # the child will indicate when it's done with its test by writing a message 1075 # to the parent. 1076 my ($rfh, $wfh); 1077 unless (pipe($rfh, $wfh)) { 1078 die("Can't open pipe: $!"); 1079 } 1080 1081 my $ex; 1082 1083 # Fork child 1084 $self->handle_sigchld(); 1085 defined(my $pid = fork()) or die("Can't fork: $!"); 1086 if ($pid) { 1087 eval { 1088 my $client = ProFTPD::TestSuite::FTP->new('localhost', $port); 1089 my $host = ('[' . $vhost_addr . ']'); 1090 1091 eval { $client->host($host) }; 1092 unless ($@) { 1093 die("HOST $host succeeded unexpectedly"); 1094 } 1095 1096 my $resp_code = $client->response_code(); 1097 my $resp_msg = $client->response_msg(); 1098 1099 my $expected; 1100 1101 $expected = 504; 1102 $self->assert($expected == $resp_code, 1103 test_msg("Expected response code $expected, got $resp_code")); 1104 1105 $expected = "$host: Unknown hostname provided"; 1106 $self->assert($expected eq $resp_msg, 1107 test_msg("Expected response message '$expected', got '$resp_msg'")); 1108 }; 1109 1110 if ($@) { 1111 $ex = $@; 1112 } 1113 1114 $wfh->print("done\n"); 1115 $wfh->flush(); 1116 1117 } else { 1118 eval { server_wait($setup->{config_file}, $rfh) }; 1119 if ($@) { 1120 warn($@); 1121 exit 1; 1122 } 1123 1124 exit 0; 1125 } 1126 1127 # Stop server 1128 server_stop($setup->{pid_file}); 1129 1130 $self->assert_child_ok($pid); 1131 1132 test_cleanup($setup->{log_file}, $ex); 1133} 1134 1135sub host_known_dns_ok { 1136 my $self = shift; 1137 my $tmpdir = $self->{tmpdir}; 1138 my $setup = test_setup($tmpdir, 'cmds'); 1139 1140 my $config = { 1141 PidFile => $setup->{pid_file}, 1142 ScoreboardFile => $setup->{scoreboard_file}, 1143 SystemLog => $setup->{log_file}, 1144 TraceLog => $setup->{log_file}, 1145 Trace => 'binding:20', 1146 1147 AuthUserFile => $setup->{auth_user_file}, 1148 AuthGroupFile => $setup->{auth_group_file}, 1149 1150 IfModules => { 1151 'mod_delay.c' => { 1152 DelayEngine => 'off', 1153 }, 1154 }, 1155 }; 1156 1157 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 1158 $config); 1159 1160 if (open(my $fh, ">> $setup->{config_file}")) { 1161 my $dns_name = 'localhost'; 1162 1163 print $fh <<EOC; 1164<VirtualHost $dns_name> 1165 Port $port 1166 ServerName $dns_name 1167 ServerIdent off 1168</VirtualHost> 1169EOC 1170 unless (close($fh)) { 1171 die("Can't write $setup->{config_file}: $!"); 1172 } 1173 1174 } else { 1175 die("Can't open $setup->{config_file}: $!"); 1176 } 1177 1178 # Open pipes, for use between the parent and child processes. Specifically, 1179 # the child will indicate when it's done with its test by writing a message 1180 # to the parent. 1181 my ($rfh, $wfh); 1182 unless (pipe($rfh, $wfh)) { 1183 die("Can't open pipe: $!"); 1184 } 1185 1186 my $ex; 1187 1188 # Fork child 1189 $self->handle_sigchld(); 1190 defined(my $pid = fork()) or die("Can't fork: $!"); 1191 if ($pid) { 1192 eval { 1193 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1194 my $host = "localhost"; 1195 1196 $client->host($host); 1197 my $resp_code = $client->response_code(); 1198 my $resp_msg = $client->response_msg(); 1199 1200 my $expected; 1201 1202 $expected = 220; 1203 $self->assert($expected == $resp_code, 1204 test_msg("Expected response code $expected, got $resp_code")); 1205 1206 $expected = '127.0.0.1 FTP server ready'; 1207 $self->assert($expected eq $resp_msg, 1208 test_msg("Expected response message '$expected', got '$resp_msg'")); 1209 }; 1210 1211 if ($@) { 1212 $ex = $@; 1213 } 1214 1215 $wfh->print("done\n"); 1216 $wfh->flush(); 1217 1218 } else { 1219 eval { server_wait($setup->{config_file}, $rfh) }; 1220 if ($@) { 1221 warn($@); 1222 exit 1; 1223 } 1224 1225 exit 0; 1226 } 1227 1228 # Stop server 1229 server_stop($setup->{pid_file}); 1230 1231 $self->assert_child_ok($pid); 1232 1233 test_cleanup($setup->{log_file}, $ex); 1234} 1235 1236sub host_config_limit_denied { 1237 my $self = shift; 1238 my $tmpdir = $self->{tmpdir}; 1239 my $setup = test_setup($tmpdir, 'cmds'); 1240 1241 my $config = { 1242 PidFile => $setup->{pid_file}, 1243 ScoreboardFile => $setup->{scoreboard_file}, 1244 SystemLog => $setup->{log_file}, 1245 1246 AuthUserFile => $setup->{auth_user_file}, 1247 AuthGroupFile => $setup->{auth_group_file}, 1248 1249 IfModules => { 1250 'mod_delay.c' => { 1251 DelayEngine => 'off', 1252 }, 1253 }, 1254 }; 1255 1256 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 1257 $config); 1258 1259 if (open(my $fh, ">> $setup->{config_file}")) { 1260 print $fh <<EOC; 1261<Limit HOST> 1262 DenyAll 1263</Limit> 1264EOC 1265 unless (close($fh)) { 1266 die("Can't write $setup->{config_file}: $!"); 1267 } 1268 1269 } else { 1270 die("Can't open $setup->{config_file}: $!"); 1271 } 1272 1273 # Open pipes, for use between the parent and child processes. Specifically, 1274 # the child will indicate when it's done with its test by writing a message 1275 # to the parent. 1276 my ($rfh, $wfh); 1277 unless (pipe($rfh, $wfh)) { 1278 die("Can't open pipe: $!"); 1279 } 1280 1281 my $ex; 1282 1283 # Fork child 1284 $self->handle_sigchld(); 1285 defined(my $pid = fork()) or die("Can't fork: $!"); 1286 if ($pid) { 1287 eval { 1288 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1289 1290 my $host = "127.0.0.1"; 1291 eval { $client->host($host) }; 1292 unless ($@) { 1293 die("HOST after login succeeded unexpectedly"); 1294 } 1295 1296 my $resp_code = $client->response_code(); 1297 my $resp_msg = $client->response_msg(); 1298 1299 my $expected; 1300 1301 $expected = 504; 1302 $self->assert($expected == $resp_code, 1303 test_msg("Expected response code $expected, got $resp_code")); 1304 1305 $expected = "$host: Permission denied"; 1306 $self->assert($expected eq $resp_msg, 1307 test_msg("Expected response message '$expected', got '$resp_msg'")); 1308 }; 1309 1310 if ($@) { 1311 $ex = $@; 1312 } 1313 1314 $wfh->print("done\n"); 1315 $wfh->flush(); 1316 1317 } else { 1318 eval { server_wait($setup->{config_file}, $rfh) }; 1319 if ($@) { 1320 warn($@); 1321 exit 1; 1322 } 1323 1324 exit 0; 1325 } 1326 1327 # Stop server 1328 server_stop($setup->{pid_file}); 1329 1330 $self->assert_child_ok($pid); 1331 1332 test_cleanup($setup->{log_file}, $ex); 1333} 1334 1335sub host_before_feat_ok { 1336 my $self = shift; 1337 my $tmpdir = $self->{tmpdir}; 1338 my $setup = test_setup($tmpdir, 'cmds'); 1339 1340 my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem'); 1341 my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem'); 1342 1343 my $server_alias = 'ftp.nospam.org'; 1344 my $server_name = 'Other Virtual Host'; 1345 1346 my $config = { 1347 PidFile => $setup->{pid_file}, 1348 ScoreboardFile => $setup->{scoreboard_file}, 1349 SystemLog => $setup->{log_file}, 1350 1351 AuthUserFile => $setup->{auth_user_file}, 1352 AuthGroupFile => $setup->{auth_group_file}, 1353 1354 IfModules => { 1355 'mod_delay.c' => { 1356 DelayEngine => 'off', 1357 }, 1358 }, 1359 }; 1360 1361 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 1362 $config); 1363 1364 if (open(my $fh, ">> $setup->{config_file}")) { 1365 print $fh <<EOC; 1366<VirtualHost 127.0.0.1> 1367 Port $port 1368 ServerName "$server_name" 1369 ServerAlias $server_alias 1370 1371 TLSEngine on 1372 TLSLog $setup->{log_file} 1373 TLSRSACertificateFile $cert_file 1374 TLSCACertificateFile $ca_file 1375</VirtualHost> 1376EOC 1377 unless (close($fh)) { 1378 die("Can't write $setup->{config_file}: $!"); 1379 } 1380 1381 } else { 1382 die("Can't open $setup->{config_file}: $!"); 1383 } 1384 1385 # Open pipes, for use between the parent and child processes. Specifically, 1386 # the child will indicate when it's done with its test by writing a message 1387 # to the parent. 1388 my ($rfh, $wfh); 1389 unless (pipe($rfh, $wfh)) { 1390 die("Can't open pipe: $!"); 1391 } 1392 1393 my $ex; 1394 1395 # Fork child 1396 $self->handle_sigchld(); 1397 defined(my $pid = fork()) or die("Can't fork: $!"); 1398 if ($pid) { 1399 eval { 1400 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1401 $client->host($server_alias); 1402 my $resp_code = $client->response_code(); 1403 my $resp_msg = $client->response_msg(); 1404 1405 my $expected; 1406 1407 $expected = 220; 1408 $self->assert($expected == $resp_code, 1409 test_msg("Expected response code $expected, got $resp_code")); 1410 1411 $expected = $server_name; 1412 $self->assert(qr/$expected/, $resp_msg, 1413 test_msg("Expected response message '$expected', got '$resp_msg'")); 1414 1415 $client->feat(); 1416 $resp_code = $client->response_code(); 1417 my $resp_msgs = $client->response_msgs(); 1418 1419 $expected = 211; 1420 $self->assert($expected == $resp_code, 1421 test_msg("Expected response code $expected, got $resp_code")); 1422 1423 my $saw_auth_tls = 0; 1424 foreach my $feat (@$resp_msgs) { 1425 if ($feat =~ /AUTH TLS/) { 1426 $saw_auth_tls = 1; 1427 last; 1428 } 1429 } 1430 1431 $self->assert($saw_auth_tls, 1432 test_msg("Did not see expected 'AUTH TLS' feature listed via FEAT")); 1433 1434 $client->quit(); 1435 }; 1436 1437 if ($@) { 1438 $ex = $@; 1439 } 1440 1441 $wfh->print("done\n"); 1442 $wfh->flush(); 1443 1444 } else { 1445 eval { server_wait($setup->{config_file}, $rfh) }; 1446 if ($@) { 1447 warn($@); 1448 exit 1; 1449 } 1450 1451 exit 0; 1452 } 1453 1454 # Stop server 1455 server_stop($setup->{pid_file}); 1456 1457 $self->assert_child_ok($pid); 1458 1459 test_cleanup($setup->{log_file}, $ex); 1460} 1461 1462sub host_after_feat_ok { 1463 my $self = shift; 1464 my $tmpdir = $self->{tmpdir}; 1465 my $setup = test_setup($tmpdir, 'cmds'); 1466 1467 my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem'); 1468 my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem'); 1469 1470 my $server_alias = 'ftp.nospam.org'; 1471 my $server_name = 'Other Virtual Host'; 1472 1473 my $config = { 1474 PidFile => $setup->{pid_file}, 1475 ScoreboardFile => $setup->{scoreboard_file}, 1476 SystemLog => $setup->{log_file}, 1477 1478 AuthUserFile => $setup->{auth_user_file}, 1479 AuthGroupFile => $setup->{auth_group_file}, 1480 1481 IfModules => { 1482 'mod_delay.c' => { 1483 DelayEngine => 'off', 1484 }, 1485 }, 1486 }; 1487 1488 my ($port, $config_user, $config_group) = config_write($setup->{config_file}, 1489 $config); 1490 1491 if (open(my $fh, ">> $setup->{config_file}")) { 1492 print $fh <<EOC; 1493<VirtualHost 127.0.0.1> 1494 Port $port 1495 ServerName "$server_name" 1496 ServerAlias $server_alias 1497 1498 TLSEngine on 1499 TLSLog $setup->{log_file} 1500 TLSRSACertificateFile $cert_file 1501 TLSCACertificateFile $ca_file 1502</VirtualHost> 1503EOC 1504 unless (close($fh)) { 1505 die("Can't write $setup->{config_file}: $!"); 1506 } 1507 1508 } else { 1509 die("Can't open $setup->{config_file}: $!"); 1510 } 1511 1512 # Open pipes, for use between the parent and child processes. Specifically, 1513 # the child will indicate when it's done with its test by writing a message 1514 # to the parent. 1515 my ($rfh, $wfh); 1516 unless (pipe($rfh, $wfh)) { 1517 die("Can't open pipe: $!"); 1518 } 1519 1520 my $ex; 1521 1522 # Fork child 1523 $self->handle_sigchld(); 1524 defined(my $pid = fork()) or die("Can't fork: $!"); 1525 if ($pid) { 1526 eval { 1527 my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port); 1528 $client->feat(); 1529 1530 my $resp_code = $client->response_code(); 1531 my $resp_msgs = $client->response_msgs(); 1532 1533 my $expected; 1534 1535 $expected = 211; 1536 $self->assert($expected == $resp_code, 1537 test_msg("Expected response code $expected, got $resp_code")); 1538 1539 my $saw_auth_tls = 0; 1540 foreach my $feat (@$resp_msgs) { 1541 if ($feat =~ /AUTH TLS/) { 1542 $saw_auth_tls = 1; 1543 last; 1544 } 1545 } 1546 1547 $self->assert(!$saw_auth_tls, 1548 test_msg("Saw 'AUTH TLS' feature listed via FEAT unexpectedly")); 1549 1550 $client->host($server_alias); 1551 $resp_code = $client->response_code(); 1552 my $resp_msg = $client->response_msg(); 1553 1554 $expected = 220; 1555 $self->assert($expected == $resp_code, 1556 test_msg("Expected response code $expected, got $resp_code")); 1557 1558 $expected = $server_name; 1559 $self->assert(qr/$expected/, $resp_msg, 1560 test_msg("Expected response message '$expected', got '$resp_msg'")); 1561 1562 $client->quit(); 1563 }; 1564 1565 if ($@) { 1566 $ex = $@; 1567 } 1568 1569 $wfh->print("done\n"); 1570 $wfh->flush(); 1571 1572 } else { 1573 eval { server_wait($setup->{config_file}, $rfh) }; 1574 if ($@) { 1575 warn($@); 1576 exit 1; 1577 } 1578 1579 exit 0; 1580 } 1581 1582 # Stop server 1583 server_stop($setup->{pid_file}); 1584 1585 $self->assert_child_ok($pid); 1586 1587 test_cleanup($setup->{log_file}, $ex); 1588} 1589 15901; 1591