Fugu

 view release on metacpan or  search on metacpan

t/fugu/control.t  view on Meta::CPAN


	my $path    = "$dir/chownfail.sock";
	my $control = Fugu::Control->new( path => $path );

	is(
		$control->listen(
			loop  => Fugu::EventLoop->new,
			mode  => 0660,
			group => 0,
		),
		undef,
		'listen returns undef'
	);
	like( $control->error, qr/chown/, 'the reason names the chown' );
	ok( !-e $path, 'and the half-built socket is gone' );
};

subtest 'a mode outside the permission bits dies' => sub {
	my $control = Fugu::Control->new( path => "$dir/badmode.sock" );

	ok( !eval {
		$control->listen( loop => Fugu::EventLoop->new,
			mode => 07777 );
		1;
	    },
		'a mode above 0777 dies' );
	like( $@, qr/Invalid socket mode/, 'and says so' );
};

subtest 'the credential read answers or fails closed' => sub {

	# The read guards the constant, so a perl whose Socket module
	# defines no SO_PEERCRED gets a reason, never a croak. The
	# platform decides which branch this proves.
	socketpair( my $a_end, my $b_end, Socket::AF_UNIX(),
		SOCK_STREAM, Socket::PF_UNSPEC() )
	    or die "socketpair: $!";

	my ( $peer, $fault ) = Fugu::Control::_read_peer($a_end);

	if ( Fugu::Control->peer_supported ) {

		# The peer of a socketpair is this process, and the
		# field order is the sockpeercred order, so the values
		# must match exactly.
		is( $fault, undef, 'the read reports no fault' );
		is( $peer->{uid}, $>, 'the uid is the effective uid' );
		is( $peer->{gid}, ( split ' ', $) )[0],
			'the gid is the effective gid' );
		is( $peer->{pid}, $$, 'the pid is this process' );
	}
	elsif ( defined eval { Socket::SO_PEERCRED() } ) {

		# The platform defines the constant with an other
		# field order, so only the fail-closed shape holds
		# here. The fabricated-bytes subtest below locks the
		# unpack itself.
		ok( defined $peer || defined $fault, 'one of the two answers' );
	}
	else {
		is( $peer, undef, 'no credentials come back' );
		like( $fault, qr/SO_PEERCRED/,
			'and the reason names the constant' );
	}

	close $a_end;
	close $b_end;
};

subtest 'the credential unpack keeps a large id positive' => sub {

	# The id fields of a struct sockpeercred are unsigned, and the
	# process id is signed. A signed read would turn a uid at or
	# above 2**31 negative, and a gate would then compare the
	# wrong number.
	my @fields = Fugu::Control::_unpack_peer(
		pack 'L2l', 4026531840, 4026531841, 1234 );
	is_deeply(
		\@fields,
		[ 4026531840, 4026531841, 1234 ],
		'a uid and a gid at or above 2**31 stay positive'
	);
};

subtest 'peer answers only inside a handler' => sub {
	my $control = Fugu::Control->new( path => "$dir/peer.sock" );
	is( $control->peer, undef, 'peer is undef outside a handler' );

	is( Fugu::Control->peer_supported,
		$^O eq 'openbsd' ? 1 : 0,
		'peer_supported is true on OpenBSD alone' );
};

subtest 'peer is undef where the platform is not supported' => sub {
	plan skip_all => 'the platform reports peer credentials'
	    if Fugu::Control->peer_supported;

	my ( $path, $pid ) = start_server(
		sub ($control) {
			$control->register(
				has_peer => sub ($) {
					return { seen => $control->peer
						    ? 1 : 0 };
				} );
		} );

	my $client = Fugu::Control::Client->new( path => $path );
	is_deeply( $client->request('has_peer'), { seen => 0 },
		'a handler sees undef off OpenBSD' );

	$client->disconnect;
	stop_server( $pid, $path );
};

subtest 'peer names the connected peer on OpenBSD' => sub {
	plan skip_all => 'peer credentials need OpenBSD'
	    unless Fugu::Control->peer_supported;

	my ( $path, $pid ) = start_server(
		sub ($control) {
			$control->register(
				whoami => sub ($) { $control->peer } );
		} );

	my $client = Fugu::Control::Client->new( path => $path );
	my $peer   = $client->request('whoami');

	ok( defined $peer, 'the handler read the credentials' )
	    or diag( $client->error // 'no error recorded' );
	is( $peer->{uid}, $>, 'the uid is the effective uid of the client' );
	is( $peer->{gid}, ( split ' ', $) )[0],
		'the gid is the effective gid of the client' );
	is( $peer->{pid}, $$, 'the pid is the pid of the client' );

	$client->disconnect;
	stop_server( $pid, $path );
};

done_testing();



( run in 1.563 second using v1.01-cache-2.11-cpan-007c89162af )