1#!/usr/bin/perl 2# $OpenBSD: loop.pl,v 1.1 2021/01/02 01:27:45 bluhm Exp $ 3 4# Copyright (c) 2021 Alexander Bluhm <bluhm@openbsd.org> 5# 6# Permission to use, copy, modify, and distribute this software for any 7# purpose with or without fee is hereby granted, provided that the above 8# copyright notice and this permission notice appear in all copies. 9# 10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 18use strict; 19use warnings; 20use BSD::Socket::Splice qw(setsplice geterror); 21use Errno; 22use Getopt::Std; 23use IO::Socket::IP; 24use Socket qw(getnameinfo AI_PASSIVE NI_NUMERICHOST NI_NUMERICSERV); 25 26# from /usr/include/sys/mbuf.h 27use constant M_MAXLOOP => 128; 28 29my %opts; 30getopts('46p:v', \%opts) or do { 31 print STDERR <<"EOF"; 32usage: $0 [-46v] [-p proto] 33 -4 use IPv4 34 -6 use IPv6 35 -p proto protocol, tcp or udp, default tcp 36 -v verbose 37EOF 38 exit(2); 39}; 40 41$opts{4} && $opts{6} 42 and die "Cannot use -4 and -6 together"; 43my $localhost = $opts{4} ? "127.0.0.1" : $opts{6} ? "::1" : "localhost"; 44my $proto = $opts{p} || "tcp"; 45my $type = $proto eq "tcp" ? SOCK_STREAM : SOCK_DGRAM; 46my $verbose = $opts{v}; 47 48my $timeout = 10; 49$SIG{ALRM} = sub { die "Timeout triggered after $timeout seconds" }; 50alarm($timeout); 51 52my $ls = IO::Socket::IP->new( 53 GetAddrInfoFlags => AI_PASSIVE, 54 Listen => ($type == SOCK_STREAM) ? 1 : undef, 55 LocalHost => $localhost, 56 Proto => $proto, 57 Type => $type, 58) or die "Listen socket failed: $@"; 59my ($host, $service) = $ls->sockhost_service(1); 60print "listen on host '$host' service '$service'\n" if $verbose; 61 62my $cs = IO::Socket::IP->new( 63 PeerHost => $host, 64 PeerService => $service, 65 Proto => $proto, 66 Type => $type, 67) or die "Connect socket failed: $@"; 68print "connect to host '$host' service '$service'\n" if $verbose; 69 70my ($as, $peer); 71if ($type == SOCK_STREAM) { 72 ($as, $peer) = $ls->accept() 73 or die "Accept socket failed: $!"; 74} else { 75 $as = $ls; 76 $peer = $cs->sockname(); 77 $as->connect($peer) 78 or die "Connect passive socket failed: $!"; 79} 80if ($verbose) { 81 my ($err, $peerhost, $peerservice) = getnameinfo($peer, 82 NI_NUMERICHOST | NI_NUMERICSERV); 83 $err and die "Getnameinfo failed: $err"; 84 print "accept from host '$peerhost' service '$peerservice'\n"; 85} 86 87setsplice($as, $cs) 88 or die "Splice accept to connect socket failed: $!"; 89setsplice($cs, $as) 90 or die "Splice connect to accept socket failed: $!"; 91 92system("\${SUDO} fstat -n -p $$") if $verbose; 93my ($msg, $buf) = "foo"; 94$cs->send($msg, 0) 95 or die "Send to connect socket failed: $!"; 96defined $as->recv($buf, 100, 0) 97 or die "Recv from accept socket failed: $!"; 98$msg eq $buf 99 or die "Value modified in splice chain"; 100$! = geterror($as) 101 or die "No error at accept socket"; 102$!{ELOOP} 103 or die "Errno at accept socket is not ELOOP: $!"; 104