1#!/usr/bin/perl -w 2 3use strict; 4use Socket qw(inet_pton PF_INET PF_INET6 SOCK_STREAM SOMAXCONN sockaddr_in sockaddr_in6); 5use Errno; 6 7my @socka = (); 8my ($pf, $host, $sin, $sock, $badsock); 9 10 11if (@ARGV < 3 or @ARGV > 4) { 12 die "usage: $0 <pf> <listen> <first> [count]\n" 13} 14 15if ($> != 0) { 16 die "run this script as root\n" 17} 18 19my ($af, $test_listen, $test_first, $test_count) = @ARGV; 20 21$test_count = SOMAXCONN if (not defined $test_count); 22 23my $test_last = $test_first + $test_count; 24 25if ($test_first <= 0 || 65536 <= $test_first || 26 $test_last <= 0 || 65536 <= $test_last || 27 $test_listen <= 0 || 65536 <= $test_listen) { 28 die "invalid port number\n"; 29} 30 31if ($test_first > $test_last) { 32 die "first must be lower than last\n"; 33} 34 35if ($test_listen >= $test_first && $test_listen <= $test_last) { 36 die "listen must be outside the [first..last] range\n"; 37} 38 39if ($af == "4") { 40 $pf = PF_INET; 41 $sin = sockaddr_in($test_listen, inet_pton($pf,"127.0.0.1")); 42} 43elsif ($af == "6") { 44 $pf = PF_INET6; 45 $sin = sockaddr_in6($test_listen, inet_pton($pf,"::1")); 46} 47else { 48 die "af must be 4 or 6\n"; 49} 50 51 52my $orig_first = qx( sysctl -n net.inet.ip.portfirst ); 53chomp $orig_first; 54my $orig_last = qx( sysctl -n net.inet.ip.portlast ); 55chomp $orig_last; 56 57 58# first < last 59 60socket(SERVSOCK, $pf, SOCK_STREAM, getprotobyname("tcp")); 61bind(SERVSOCK, $sin); 62listen(SERVSOCK, SOMAXCONN); 63 64my $rc_f = 0; 65 66print "testing with portfirst < portlast\n"; 67 68system("sysctl net.inet.ip.portfirst=$test_first > /dev/null"); 69system("sysctl net.inet.ip.portlast=$test_last > /dev/null"); 70 71for ($test_first .. $test_last) { 72 socket($sock, $pf, SOCK_STREAM, getprotobyname("tcp")); 73 unless (connect($sock, $sin)) { 74 print "failed to connect with errno $!\n"; 75 $rc_f = 1; 76 } 77 push @socka, $sock; 78} 79 80socket($badsock, $pf, SOCK_STREAM, getprotobyname("tcp")); 81if (connect($badsock, $sin)) { 82 print "connect() succeeded but should have failed\n"; 83 $rc_f = 1; 84} 85elsif (not $!{EADDRNOTAVAIL}) { 86 print "connect() failed with errno $!, should have been EADDRNOTAVAIL\n"; 87 $rc_f = 1; 88} 89close($badsock); 90 91while ($sock = pop @socka) { 92 close($sock); 93} 94 95close(SERVSOCK); 96 97sleep 1; 98 99if ($rc_f == 0) { 100 print "test OK\n" 101} 102else { 103 print "test failed\n" 104} 105 106# first > last 107 108socket(SERVSOCK, $pf, SOCK_STREAM, getprotobyname("tcp")); 109bind(SERVSOCK, $sin); 110listen(SERVSOCK, SOMAXCONN); 111 112my $rc_b = 0; 113 114print "testing with portfirst > portlast\n"; 115 116system("sysctl net.inet.ip.portfirst=$test_last > /dev/null"); 117system("sysctl net.inet.ip.portlast=$test_first > /dev/null"); 118 119for ($test_first .. $test_last) { 120 socket($sock, $pf, SOCK_STREAM, getprotobyname("tcp")); 121 unless (connect($sock, $sin)) { 122 print "failed to connect with errno $!\n"; 123 $rc_b = 1; 124 } 125 push @socka, $sock; 126} 127 128socket($badsock, $pf, SOCK_STREAM, getprotobyname("tcp")); 129if (connect($badsock, $sin)) { 130 print "connect() succeeded but should have failed\n"; 131 $rc_b = 1; 132} 133elsif (not $!{EADDRNOTAVAIL}) { 134 print "connect() failed with errno $!, should have been EADDRNOTAVAIL\n"; 135 $rc_b = 1; 136} 137close($badsock); 138 139while ($sock = pop @socka) { 140 close($sock); 141} 142 143close(SERVSOCK); 144 145sleep 1; 146 147if ($rc_b == 0) { 148 print "test OK\n" 149} 150else { 151 print "test failed\n" 152} 153 154system("sysctl net.inet.ip.portfirst=$orig_first > /dev/null"); 155system("sysctl net.inet.ip.portlast=$orig_last > /dev/null"); 156 157exit ($rc_f || $rc_b); 158