1#!/usr/bin/perl 2# $OpenBSD: bcast.pl,v 1.1 2021/01/09 15:39:37 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('b:v', \%opts) or do { 31 print STDERR <<"EOF"; 32usage: $0 [-v] [-b bcast] 33 -b bcast broadcast address, default 255.255.255.255 34 -v verbose 35EOF 36 exit(2); 37}; 38 39my $broadcast = $opts{b} || "255.255.255.255"; 40my $verbose = $opts{v}; 41 42my $timeout = 10; 43$SIG{ALRM} = sub { die "Timeout triggered after $timeout seconds" }; 44alarm($timeout); 45 46my $ls = IO::Socket::IP->new( 47 Broadcast => $opts{b} ? 1 : undef, 48 GetAddrInfoFlags => AI_PASSIVE, 49 LocalHost => $broadcast, 50 Proto => "udp", 51 Type => SOCK_DGRAM, 52) or die "Listen socket failed: $@"; 53my ($host, $service) = $ls->sockhost_service(1); 54print "listen on host '$host' service '$service'\n" if $verbose; 55 56my $cs = IO::Socket::IP->new( 57 PeerHost => $host, 58 PeerService => $service, 59 Proto => "udp", 60 Type => SOCK_DGRAM, 61) or die "Connect socket failed: $@"; 62print "connect to host '$host' service '$service'\n" if $verbose; 63 64my $as = $ls; 65my $peer = $cs->sockname(); 66$as->connect($peer) 67 or die "Connect passive socket failed: $!"; 68if ($verbose) { 69 my ($err, $peerhost, $peerservice) = getnameinfo($peer, 70 NI_NUMERICHOST | NI_NUMERICSERV); 71 $err and die "Getnameinfo failed: $err"; 72 print "accept from host '$peerhost' service '$peerservice'\n"; 73} 74 75setsplice($as, $cs) 76 or die "Splice accept to connect socket failed: $!"; 77setsplice($cs, $as) 78 or die "Splice connect to accept socket failed: $!"; 79 80system("\${SUDO} fstat -n -p $$") if $verbose; 81my ($msg, $buf) = "foo"; 82$cs->send($msg, 0) 83 or die "Send to connect socket failed: $!"; 84defined $as->recv($buf, 100, 0) 85 or die "Recv from accept socket failed: $!"; 86$msg eq $buf 87 or die "Value modified in splice chain"; 88$! = geterror($as) 89 or die "No error at accept socket"; 90$!{ELOOP} 91 or die "Errno at accept socket is not ELOOP: $!"; 92 93# addresses are asymmetric, try it the other way around 94$msg = "bar"; 95$as->send($msg, 0) 96 or die "Send to accept socket failed: $!"; 97defined $cs->recv($buf, 100, 0) 98 or die "Recv from connect socket failed: $!"; 99$msg eq $buf 100 or die "Value modified in splice chain"; 101$! = geterror($cs) 102 or die "No error at connect socket"; 103$!{ELOOP} 104 or die "Errno at connect socket is not ELOOP: $!"; 105