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