1# $OpenBSD: funcs.pl,v 1.6 2017/08/15 04:11:20 bluhm Exp $ 2 3# Copyright (c) 2010-2015 Alexander Bluhm <bluhm@openbsd.org> 4# 5# Permission to use, copy, modify, and distribute this software for any 6# purpose with or without fee is hereby granted, provided that the above 7# copyright notice and this permission notice appear in all copies. 8# 9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 17use strict; 18use warnings; 19use Socket; 20use Socket6; 21 22######################################################################## 23# Client and Server funcs 24######################################################################## 25 26sub write_read_stream { 27 my $self = shift; 28 29 my $out = ref($self). "\n"; 30 print $out; 31 IO::Handle::flush(\*STDOUT); 32 print STDERR ">>> $out"; 33 34 my $in = <STDIN>; 35 print STDERR "<<< $in"; 36} 37 38sub write_datagram { 39 my $self = shift; 40 my $dgram = shift; 41 42 my $out = $dgram || ref($self). "\n"; 43 my $addr = $self->{toaddr}; 44 my $port = $self->{toport}; 45 if ($addr) { 46 my ($to, $netaddr); 47 if ($self->{af} eq "inet") { 48 $netaddr = inet_pton(AF_INET, $addr); 49 $to = pack_sockaddr_in($port, $netaddr); 50 } else { 51 $netaddr = inet_pton(AF_INET6, $addr); 52 $to = pack_sockaddr_in6($port, $netaddr); 53 } 54 $self->{toaddr} = $addr; 55 $self->{toport} = $port; 56 print STDERR "send to: $addr $port\n"; 57 58 send(STDOUT, $out, 0, $to) 59 or die ref($self), " send to failed: $!"; 60 } else { 61 send(STDOUT, $out, 0) 62 or die ref($self), " send failed: $!"; 63 } 64 65 unless ($dgram) { 66 print STDERR ">>> $out"; 67 } 68} 69 70sub read_datagram { 71 my $self = shift; 72 my $dgram = shift; 73 74 my $from = recv(STDIN, my $in, 70000, 0) 75 or die ref($self), " recv from failed: $!"; 76 # Raw sockets include the IPv4 header. 77 if ($self->{socktype} && $self->{socktype} == Socket::SOCK_RAW && 78 $self->{af} eq "inet") { 79 substr($in, 0, 20, ""); 80 } 81 82 my ($port, $netaddr, $addr); 83 if ($self->{af} eq "inet") { 84 ($port, $netaddr) = unpack_sockaddr_in($from); 85 $addr = inet_ntop(AF_INET, $netaddr); 86 } else { 87 ($port, $netaddr) = unpack_sockaddr_in6($from); 88 $addr = inet_ntop(AF_INET6, $netaddr); 89 } 90 $self->{fromaddr} = $addr; 91 $self->{fromport} = $port; 92 print STDERR "recv from: $addr $port\n"; 93 94 if ($dgram) { 95 $$dgram = $in; 96 } else { 97 print STDERR "<<< $in"; 98 } 99} 100 101sub write_read_datagram { 102 my $self = shift; 103 write_datagram($self); 104 read_datagram($self); 105} 106 107sub read_write_datagram { 108 my $self = shift; 109 read_datagram($self); 110 $self->{toaddr} = $self->{fromaddr}; 111 $self->{toport} = $self->{fromport}; 112 write_datagram($self); 113} 114 115sub read_write_packet { 116 my $self = shift; 117 118 my $packet; 119 read_datagram($self, \$packet); 120 my $hexin = unpack("H*", $packet); 121 print STDERR "<<< $hexin\n"; 122 123 $packet =~ s/Client|Server/Packet/; 124 $self->{toaddr} = $self->{fromaddr}; 125 $self->{toport} = $self->{fromport}; 126 write_datagram($self, $packet); 127 my $hexout = unpack("H*", $packet); 128 print STDERR ">>> $hexout\n"; 129} 130 131sub in_cksum { 132 my $data = shift; 133 my $sum = 0; 134 135 $data .= pack("x") if (length($data) & 1); 136 while (length($data)) { 137 $sum += unpack("n", substr($data, 0, 2, "")); 138 $sum = ($sum >> 16) + ($sum & 0xffff) if ($sum > 0xffff); 139 } 140 return (~$sum & 0xffff); 141} 142 143use constant IPPROTO_ICMPV6 => 58; 144use constant ICMP_ECHO => 8; 145use constant ICMP_ECHOREPLY => 0; 146use constant ICMP6_ECHO_REQUEST => 128; 147use constant ICMP6_ECHO_REPLY => 129; 148 149my $seq = 0; 150sub write_icmp_echo { 151 my $self = shift; 152 my $pid = shift || $$; 153 my $af = $self->{af}; 154 155 my $type = $af eq "inet" ? ICMP_ECHO : ICMP6_ECHO_REQUEST; 156 # type, code, cksum, id, seq 157 my $icmp = pack("CCnnn", $type, 0, 0, $pid, ++$seq); 158 if ($af eq "inet") { 159 substr($icmp, 2, 2, pack("n", in_cksum($icmp))); 160 } else { 161 # src, dst, plen, pad, next 162 my $phdr = ""; 163 $phdr .= inet_pton(AF_INET6, $self->{srcaddr}); 164 $phdr .= inet_pton(AF_INET6, $self->{dstaddr}); 165 $phdr .= pack("NxxxC", length($icmp), IPPROTO_ICMPV6); 166 print STDERR "pseudo header: ", unpack("H*", $phdr), "\n"; 167 substr($icmp, 2, 2, pack("n", in_cksum($phdr. $icmp))); 168 } 169 170 write_datagram($self, $icmp); 171 my $text = $af eq "inet" ? "ICMP" : "ICMP6"; 172 print STDERR ">>> $text ", unpack("H*", $icmp), "\n"; 173} 174 175sub read_icmp_echo { 176 my $self = shift; 177 my $reply = shift; 178 my $af = $self->{af}; 179 180 my $icmp; 181 read_datagram($self, \$icmp); 182 183 my $text = $af eq "inet" ? "ICMP" : "ICMP6"; 184 $text .= " reply" if $reply; 185 my $phdr = ""; 186 if ($af eq "inet6") { 187 # src, dst, plen, pad, next 188 $phdr .= inet_pton(AF_INET6, $self->{srcaddr}); 189 $phdr .= inet_pton(AF_INET6, $self->{dstaddr}); 190 $phdr .= pack("NxxxC", length($icmp), IPPROTO_ICMPV6); 191 print STDERR "pseudo header: ", unpack("H*", $phdr), "\n"; 192 } 193 if (length($icmp) < 8) { 194 $text = "BAD $text LENGTH"; 195 } elsif (in_cksum($phdr. $icmp) != 0) { 196 $text = "BAD $text CHECKSUM"; 197 } else { 198 my($type, $code, $cksum, $id, $seq) = unpack("CCnnn", $icmp); 199 my $t = $reply ? 200 ($af eq "inet" ? ICMP_ECHOREPLY : ICMP6_ECHO_REPLY) : 201 ($af eq "inet" ? ICMP_ECHO : ICMP6_ECHO_REQUEST); 202 if ($type != $t) { 203 $text = "BAD $text TYPE"; 204 } elsif ($code != 0) { 205 $text = "BAD $text CODE"; 206 } 207 } 208 209 print STDERR "<<< $text ", unpack("H*", $icmp), "\n"; 210} 211 212######################################################################## 213# Script funcs 214######################################################################## 215 216sub check_logs { 217 my ($c, $r, $s, %args) = @_; 218 219 return if $args{nocheck}; 220 221 check_inout($c, $r, $s, %args); 222} 223 224sub check_inout { 225 my ($c, $r, $s, %args) = @_; 226 227 if ($args{client} && !$args{client}{nocheck}) { 228 my $out = $args{client}{out} || "Client"; 229 $c->loggrep(qr/^>>> $out/) or die "no client output" 230 unless $args{client}{noout}; 231 my $in = $args{client}{in} || "Server"; 232 $c->loggrep(qr/^<<< $in/) or die "no client input" 233 unless $args{client}{noin}; 234 } 235 if ($args{packet} && !$args{packet}{nocheck}) { 236 my $hex; 237 my $in = $args{packet}{in} || $args{packet}{noin} 238 or die "no packet input regex"; 239 $hex = unpack("H*", $in); 240 $r->loggrep(qr/Packet: <<< .*$hex/) or die "no packet input" 241 unless $args{packet}{noin}; 242 my $out = $args{packet}{out} || "Packet"; 243 $hex = unpack("H*", $out); 244 $r->loggrep(qr/Packet: >>> .*$hex/) or die "no packet output" 245 unless $args{packet}{noout}; 246 } 247 if ($args{server} && !$args{server}{nocheck}) { 248 my $in = $args{server}{in} || "Client"; 249 $s->loggrep(qr/^<<< $in/) or die "no server input" 250 unless $args{server}{noin}; 251 my $out = $args{server}{out} || "Server"; 252 $s->loggrep(qr/^>>> $out/) or die "no server output" 253 unless $args{server}{noout}; 254 } 255} 256 2571; 258