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