1#!/usr/bin/perl
2
3use warnings;
4use strict;
5
6use File::Temp qw(tempdir);
7use File::Spec::Functions;
8use IO::Socket;
9use IO::Socket::UNIX;
10use Socket;
11use Config;
12use Test::More;
13
14plan skip_all => "UNIX domain sockets not implemented on $^O"
15  if ($^O =~ m/^(?:qnx|nto|vos|MSWin32|VMS)$/);
16
17my $socketpath = catfile(tempdir( CLEANUP => 1 ), 'testsock');
18
19# check the socketpath fits in sun_path.
20#
21# pack_sockaddr_un() just truncates the path, this may change, but how
22# it will handle such a condition is undetermined (and we might need
23# to work with older versions of Socket outside of a perl build)
24# https://rt.cpan.org/Ticket/Display.html?id=116819
25
26my $name = eval { pack_sockaddr_un($socketpath) };
27if (defined $name) {
28    my ($packed_name) = eval { unpack_sockaddr_un($name) };
29    if (!defined $packed_name || $packed_name ne $socketpath) {
30        plan skip_all => "socketpath too long for sockaddr_un";
31    }
32}
33
34plan tests => 15;
35
36# start testing stream sockets:
37my $listener = IO::Socket::UNIX->new(Type => SOCK_STREAM,
38				     Listen => 1,
39				     Local => $socketpath);
40ok(defined($listener), 'stream socket created');
41
42my $p = $listener->protocol();
43{
44    # the value of protocol isn't well defined for AF_UNIX, when we
45    # create the socket we supply 0, which leaves it up to the implementation
46    # to select a protocol, so we (now) don't save a 0 protocol during socket
47    # creation.  This test then breaks if the implementation doesn't support
48    # SO_SOCKET (at least on AF_UNIX).
49    # This specifically includes NetBSD, Darwin and cygwin.
50    # This is a TODO instead of a skip so if these ever implement SO_PROTOCOL
51    # we'll be notified about the passing TODO so the test can be updated.
52    local $TODO = "$^O doesn't support SO_PROTOCOL on AF_UNIX"
53        if $^O =~ /^(netbsd|darwin|cygwin|hpux|solaris|dragonfly|os390|gnu)$/;
54    ok(defined($p), 'protocol defined');
55}
56my $d = $listener->sockdomain();
57ok(defined($d), 'domain defined');
58my $s = $listener->socktype();
59ok(defined($s), 'type defined');
60
61SKIP: {
62    skip "fork not available", 4
63	unless $Config{d_fork} || $Config{d_pseudofork};
64
65    my $cpid = fork();
66    if (0 == $cpid) {
67	# the child:
68	sleep(1);
69	my $connector = IO::Socket::UNIX->new(Peer => $socketpath);
70	exit(0);
71    } else {
72	ok(defined($cpid), 'spawned a child');
73    }
74
75    my $new = $listener->accept();
76
77    is($new->sockdomain(), $d, 'domain match');
78  SKIP: {
79      skip "no Socket::SO_PROTOCOL", 1 if !defined(eval { Socket::SO_PROTOCOL });
80      skip "SO_PROTOCOL defined but not implemented", 1
81         if !defined $new->sockopt(Socket::SO_PROTOCOL);
82      is($new->protocol(), $p, 'protocol match');
83    }
84  SKIP: {
85      skip "no Socket::SO_TYPE", 1 if !defined(eval { Socket::SO_TYPE });
86      skip "SO_TYPE defined but not implemented", 1
87         if !defined $new->sockopt(Socket::SO_TYPE);
88      is($new->socktype(), $s, 'type match');
89    }
90
91    unlink($socketpath);
92    wait();
93}
94
95undef $TODO;
96SKIP: {
97    skip "datagram unix sockets not supported on $^O", 7
98      if $^O eq "haiku";
99    # now test datagram sockets:
100    $listener = IO::Socket::UNIX->new(Type => SOCK_DGRAM,
101				      Local => $socketpath);
102    ok(defined($listener), 'datagram socket created');
103
104    $p = $listener->protocol();
105    {
106        # see comment above
107        local $TODO = "$^O doesn't support SO_PROTOCOL on AF_UNIX"
108            if $^O =~ /^(netbsd|darwin|cygwin|hpux|solaris|dragonfly|os390|gnu)$/;
109        ok(defined($p), 'protocol defined');
110    }
111    $d = $listener->sockdomain();
112    ok(defined($d), 'domain defined');
113    $s = $listener->socktype();
114    ok(defined($s), 'type defined');
115
116    my $new = IO::Socket::UNIX->new_from_fd($listener->fileno(), 'r+');
117
118    is($new->sockdomain(), $d, 'domain match');
119    SKIP: {
120      skip "no Socket::SO_PROTOCOL", 1 if !defined(eval { Socket::SO_PROTOCOL });
121      skip "SO_PROTOCOL defined but not implemented", 1
122         if !defined $new->sockopt(Socket::SO_PROTOCOL);
123      skip "SO_PROTOCOL returns chosen protocol on OpenBSD", 1
124         if $^O eq 'openbsd';
125      is($new->protocol(), $p, 'protocol match');
126    }
127    SKIP: {
128      skip "AIX: getsockopt(SO_TYPE) is badly broken on UDP/UNIX sockets", 1
129         if $^O eq "aix";
130      skip "no Socket::SO_TYPE", 1 if !defined(eval { Socket::SO_TYPE });
131      skip "SO_TYPE defined but not implemented", 1
132         if !defined $new->sockopt(Socket::SO_TYPE);
133      is($new->socktype(), $s, 'type match');
134    }
135}
136unlink($socketpath);
137