xref: /openbsd/regress/sys/kern/sosplice/Relay.pm (revision 75657dc7)
1*75657dc7Sbluhm#	$OpenBSD: Relay.pm,v 1.4 2021/12/14 12:37:49 bluhm Exp $
2f39f8f30Sbluhm
315b894fdSbluhm# Copyright (c) 2010-2017 Alexander Bluhm <bluhm@openbsd.org>
4f39f8f30Sbluhm#
5f39f8f30Sbluhm# Permission to use, copy, modify, and distribute this software for any
6f39f8f30Sbluhm# purpose with or without fee is hereby granted, provided that the above
7f39f8f30Sbluhm# copyright notice and this permission notice appear in all copies.
8f39f8f30Sbluhm#
9f39f8f30Sbluhm# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10f39f8f30Sbluhm# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11f39f8f30Sbluhm# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12f39f8f30Sbluhm# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13f39f8f30Sbluhm# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14f39f8f30Sbluhm# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15f39f8f30Sbluhm# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16f39f8f30Sbluhm
17f39f8f30Sbluhmuse strict;
18f39f8f30Sbluhmuse warnings;
19f39f8f30Sbluhm
20f39f8f30Sbluhmpackage Relay;
21f39f8f30Sbluhmuse parent 'Proc';
22f39f8f30Sbluhmuse Carp;
23*75657dc7Sbluhmuse Errno 'EINPROGRESS';
24f39f8f30Sbluhmuse Socket qw(IPPROTO_TCP TCP_NODELAY);
25f39f8f30Sbluhmuse Socket6;
26f39f8f30Sbluhmuse IO::Socket;
274b4b1389Sbluhmuse IO::Socket::IP -register;
28f39f8f30Sbluhm
29f39f8f30Sbluhmsub new {
30f39f8f30Sbluhm	my $class = shift;
31f39f8f30Sbluhm	my %args = @_;
32f39f8f30Sbluhm	$args{logfile} ||= "relay.log";
33f39f8f30Sbluhm	$args{up} ||= "Connected";
34f39f8f30Sbluhm	$args{forward}
35f39f8f30Sbluhm	    or croak "$class forward not given";
36f39f8f30Sbluhm	my $self = Proc::new($class, %args);
37f39f8f30Sbluhm	$self->{protocol} ||= "tcp";
38f39f8f30Sbluhm	$self->{listendomain}
39f39f8f30Sbluhm	    or croak "$class listen domain not given";
40f39f8f30Sbluhm	$self->{connectdomain}
41f39f8f30Sbluhm	    or croak "$class connect domain not given";
42f39f8f30Sbluhm	$self->{connectaddr}
43f39f8f30Sbluhm	    or croak "$class connect addr not given";
44f39f8f30Sbluhm	$self->{connectport}
45f39f8f30Sbluhm	    or croak "$class connect port not given";
464b4b1389Sbluhm	my $ls = IO::Socket->new(
47f39f8f30Sbluhm	    Proto	=> $self->{protocol},
48f39f8f30Sbluhm	    ReuseAddr	=> 1,
49f39f8f30Sbluhm	    Domain	=> $self->{listendomain},
50f39f8f30Sbluhm	    $self->{listenaddr} ? (LocalAddr => $self->{listenaddr}) : (),
51f39f8f30Sbluhm	    $self->{listenport} ? (LocalPort => $self->{listenport}) : (),
52f39f8f30Sbluhm	) or die ref($self), " socket failed: $!";
53f39f8f30Sbluhm	if ($self->{oobinline}) {
54f39f8f30Sbluhm		setsockopt($ls, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
55f39f8f30Sbluhm		    or die ref($self), " set oobinline listen failed: $!";
56f39f8f30Sbluhm	}
57f39f8f30Sbluhm	if ($self->{sndbuf}) {
58f39f8f30Sbluhm		setsockopt($ls, SOL_SOCKET, SO_SNDBUF,
59f39f8f30Sbluhm		    pack('i', $self->{sndbuf}))
60f39f8f30Sbluhm		    or die ref($self), " set sndbuf listen failed: $!";
61f39f8f30Sbluhm	}
62f39f8f30Sbluhm	if ($self->{rcvbuf}) {
63f39f8f30Sbluhm		setsockopt($ls, SOL_SOCKET, SO_RCVBUF,
64f39f8f30Sbluhm		    pack('i', $self->{rcvbuf}))
65f39f8f30Sbluhm		    or die ref($self), " set rcvbuf listen failed: $!";
66f39f8f30Sbluhm	}
67f39f8f30Sbluhm	if ($self->{protocol} eq "tcp") {
68f39f8f30Sbluhm	setsockopt($ls, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
69f39f8f30Sbluhm	    or die ref($self), " set nodelay listen failed: $!";
70f39f8f30Sbluhm	listen($ls, 1)
71f39f8f30Sbluhm	    or die ref($self), " listen failed: $!";
72f39f8f30Sbluhm	}
73f39f8f30Sbluhm	my $log = $self->{log};
74f39f8f30Sbluhm	print $log "listen sock: ",$ls->sockhost()," ",$ls->sockport(),"\n";
75f39f8f30Sbluhm	$self->{listenaddr} = $ls->sockhost() unless $self->{listenaddr};
76f39f8f30Sbluhm	$self->{listenport} = $ls->sockport() unless $self->{listenport};
77f39f8f30Sbluhm	$self->{ls} = $ls;
78f39f8f30Sbluhm	return $self;
79f39f8f30Sbluhm}
80f39f8f30Sbluhm
81f39f8f30Sbluhmsub child {
82f39f8f30Sbluhm	my $self = shift;
83f39f8f30Sbluhm
84f39f8f30Sbluhm	my $as = $self->{ls};
85f39f8f30Sbluhm	if ($self->{protocol} eq "tcp") {
86f39f8f30Sbluhm		$as = $self->{ls}->accept()
87f39f8f30Sbluhm		    or die ref($self), " socket accept failed: $!";
88f39f8f30Sbluhm		print STDERR "accept sock: ",$as->sockhost()," ",
89f39f8f30Sbluhm		    $as->sockport(),"\n";
90f39f8f30Sbluhm		print STDERR "accept peer: ",$as->peerhost()," ",
91f39f8f30Sbluhm		    $as->peerport(),"\n";
92f39f8f30Sbluhm	}
934b4b1389Sbluhm	if ($self->{nonblocking}) {
944b4b1389Sbluhm		$as->blocking(0)
954b4b1389Sbluhm		    or die ref($self), " set non-blocking accept failed: $!";
964b4b1389Sbluhm	}
97f39f8f30Sbluhm
98f39f8f30Sbluhm	open(STDIN, '<&', $as)
99f39f8f30Sbluhm	    or die ref($self), " dup STDIN failed: $!";
100f39f8f30Sbluhm	print STDERR "Accepted\n";
101f39f8f30Sbluhm
10215b894fdSbluhm	if ($self->{clientreadable}) {
10315b894fdSbluhm		my $idle = 15;  # timeout
10415b894fdSbluhm		my $rin = '';
10515b894fdSbluhm		vec($rin, fileno($as), 1) = 1;
10615b894fdSbluhm		defined(my $n = select($rin, undef, undef, $idle))
10715b894fdSbluhm		    or die ref($self), " select failed: $!";
10815b894fdSbluhm		$idle && $n == 0
10915b894fdSbluhm		    and die ref($self), " select timeout";
11015b894fdSbluhm	}
11115b894fdSbluhm
1124b4b1389Sbluhm	my $cs = IO::Socket->new(
113f39f8f30Sbluhm	    Proto	=> $self->{protocol},
114f39f8f30Sbluhm	    Domain	=> $self->{connectdomain},
115f39f8f30Sbluhm	) or die ref($self), " socket connect failed: $!";
116f39f8f30Sbluhm	if ($self->{oobinline}) {
117f39f8f30Sbluhm		setsockopt($cs, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
118f39f8f30Sbluhm		    or die ref($self), " set oobinline connect failed: $!";
119f39f8f30Sbluhm	}
120f39f8f30Sbluhm	if ($self->{sndbuf}) {
121f39f8f30Sbluhm		setsockopt($cs, SOL_SOCKET, SO_SNDBUF,
122f39f8f30Sbluhm		    pack('i', $self->{sndbuf}))
123f39f8f30Sbluhm		    or die ref($self), " set sndbuf connect failed: $!";
124f39f8f30Sbluhm	}
125f39f8f30Sbluhm	if ($self->{rcvbuf}) {
126f39f8f30Sbluhm		setsockopt($cs, SOL_SOCKET, SO_RCVBUF,
127f39f8f30Sbluhm		    pack('i', $self->{rcvbuf}))
128f39f8f30Sbluhm		    or die ref($self), " set rcvbuf connect failed: $!";
129f39f8f30Sbluhm	}
130f39f8f30Sbluhm	if ($self->{protocol} eq "tcp") {
131f39f8f30Sbluhm		setsockopt($cs, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
132f39f8f30Sbluhm		    or die ref($self), " set nodelay connect failed: $!";
133f39f8f30Sbluhm	}
134*75657dc7Sbluhm	if ($self->{connectnonblocking}) {
135*75657dc7Sbluhm		$cs->blocking(0)
136*75657dc7Sbluhm		    or die ref($self), " set non-blocking connect failed: $!";
137*75657dc7Sbluhm	}
138f39f8f30Sbluhm	my @rres = getaddrinfo($self->{connectaddr}, $self->{connectport},
139f39f8f30Sbluhm	    $self->{connectdomain}, SOCK_STREAM);
140*75657dc7Sbluhm	$cs->connect($rres[3]) || $!{EINPROGRESS}
141f39f8f30Sbluhm	    or die ref($self), " connect failed: $!";
142f39f8f30Sbluhm	print STDERR "connect sock: ",$cs->sockhost()," ",$cs->sockport(),"\n";
143f39f8f30Sbluhm	print STDERR "connect peer: ",$cs->peerhost()," ",$cs->peerport(),"\n";
144f39f8f30Sbluhm	$self->{bindaddr} = $cs->sockhost();
145f39f8f30Sbluhm	$self->{bindport} = $cs->sockport();
1464b4b1389Sbluhm	if ($self->{nonblocking}) {
1474b4b1389Sbluhm		$cs->blocking(0)
1484b4b1389Sbluhm		    or die ref($self), " set non-blocking connect failed: $!";
1494b4b1389Sbluhm	}
150f39f8f30Sbluhm
151f39f8f30Sbluhm	open(STDOUT, '>&', $cs)
152f39f8f30Sbluhm	    or die ref($self), " dup STDOUT failed: $!";
153f39f8f30Sbluhm}
154f39f8f30Sbluhm
155f39f8f30Sbluhm1;
156