xref: /openbsd/regress/sys/kern/sosplice/Relay.pm (revision e5dd7070)
1#	$OpenBSD: Relay.pm,v 1.2 2017/11/07 22:06:17 bluhm Exp $
2
3# Copyright (c) 2010-2017 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;
19
20package Relay;
21use parent 'Proc';
22use Carp;
23use Socket qw(IPPROTO_TCP TCP_NODELAY);
24use Socket6;
25use IO::Socket;
26use IO::Socket::INET6;
27
28sub new {
29	my $class = shift;
30	my %args = @_;
31	$args{logfile} ||= "relay.log";
32	$args{up} ||= "Connected";
33	$args{forward}
34	    or croak "$class forward not given";
35	my $self = Proc::new($class, %args);
36	$self->{protocol} ||= "tcp";
37	$self->{listendomain}
38	    or croak "$class listen domain not given";
39	$self->{connectdomain}
40	    or croak "$class connect domain not given";
41	$self->{connectaddr}
42	    or croak "$class connect addr not given";
43	$self->{connectport}
44	    or croak "$class connect port not given";
45	my $ls = IO::Socket::INET6->new(
46	    Proto	=> $self->{protocol},
47	    ReuseAddr	=> 1,
48	    Domain	=> $self->{listendomain},
49	    $self->{listenaddr} ? (LocalAddr => $self->{listenaddr}) : (),
50	    $self->{listenport} ? (LocalPort => $self->{listenport}) : (),
51	) or die ref($self), " socket failed: $!";
52	if ($self->{oobinline}) {
53		setsockopt($ls, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
54		    or die ref($self), " set oobinline listen failed: $!";
55	}
56	if ($self->{sndbuf}) {
57		setsockopt($ls, SOL_SOCKET, SO_SNDBUF,
58		    pack('i', $self->{sndbuf}))
59		    or die ref($self), " set sndbuf listen failed: $!";
60	}
61	if ($self->{rcvbuf}) {
62		setsockopt($ls, SOL_SOCKET, SO_RCVBUF,
63		    pack('i', $self->{rcvbuf}))
64		    or die ref($self), " set rcvbuf listen failed: $!";
65	}
66	if ($self->{protocol} eq "tcp") {
67	setsockopt($ls, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
68	    or die ref($self), " set nodelay listen failed: $!";
69	listen($ls, 1)
70	    or die ref($self), " listen failed: $!";
71	}
72	my $log = $self->{log};
73	print $log "listen sock: ",$ls->sockhost()," ",$ls->sockport(),"\n";
74	$self->{listenaddr} = $ls->sockhost() unless $self->{listenaddr};
75	$self->{listenport} = $ls->sockport() unless $self->{listenport};
76	$self->{ls} = $ls;
77	return $self;
78}
79
80sub child {
81	my $self = shift;
82
83	my $as = $self->{ls};
84	if ($self->{protocol} eq "tcp") {
85		$as = $self->{ls}->accept()
86		    or die ref($self), " socket accept failed: $!";
87		print STDERR "accept sock: ",$as->sockhost()," ",
88		    $as->sockport(),"\n";
89		print STDERR "accept peer: ",$as->peerhost()," ",
90		    $as->peerport(),"\n";
91	}
92	$as->blocking($self->{nonblocking} ? 0 : 1)
93	    or die ref($self), " non-blocking accept failed: $!";
94
95	open(STDIN, '<&', $as)
96	    or die ref($self), " dup STDIN failed: $!";
97	print STDERR "Accepted\n";
98
99	if ($self->{clientreadable}) {
100		my $idle = 15;  # timeout
101		my $rin = '';
102		vec($rin, fileno($as), 1) = 1;
103		defined(my $n = select($rin, undef, undef, $idle))
104		    or die ref($self), " select failed: $!";
105		$idle && $n == 0
106		    and die ref($self), " select timeout";
107	}
108
109	my $cs = IO::Socket::INET6->new(
110	    Proto	=> $self->{protocol},
111	    Domain	=> $self->{connectdomain},
112	    Blocking	=> ($self->{nonblocking} ? 0 : 1),
113	) or die ref($self), " socket connect failed: $!";
114	if ($self->{oobinline}) {
115		setsockopt($cs, SOL_SOCKET, SO_OOBINLINE, pack('i', 1))
116		    or die ref($self), " set oobinline connect failed: $!";
117	}
118	if ($self->{sndbuf}) {
119		setsockopt($cs, SOL_SOCKET, SO_SNDBUF,
120		    pack('i', $self->{sndbuf}))
121		    or die ref($self), " set sndbuf connect failed: $!";
122	}
123	if ($self->{rcvbuf}) {
124		setsockopt($cs, SOL_SOCKET, SO_RCVBUF,
125		    pack('i', $self->{rcvbuf}))
126		    or die ref($self), " set rcvbuf connect failed: $!";
127	}
128	if ($self->{protocol} eq "tcp") {
129		setsockopt($cs, IPPROTO_TCP, TCP_NODELAY, pack('i', 1))
130		    or die ref($self), " set nodelay connect failed: $!";
131	}
132	my @rres = getaddrinfo($self->{connectaddr}, $self->{connectport},
133	    $self->{connectdomain}, SOCK_STREAM);
134	$cs->connect($rres[3])
135	    or die ref($self), " connect failed: $!";
136	print STDERR "connect sock: ",$cs->sockhost()," ",$cs->sockport(),"\n";
137	print STDERR "connect peer: ",$cs->peerhost()," ",$cs->peerport(),"\n";
138	$self->{bindaddr} = $cs->sockhost();
139	$self->{bindport} = $cs->sockport();
140
141	open(STDOUT, '>&', $cs)
142	    or die ref($self), " dup STDOUT failed: $!";
143}
144
1451;
146