#!perl
BEGIN {
chdir
't'
if
-d
't'
;
require
"./test.pl"
;
set_up_inc(
'../lib'
)
if
-d
'../lib'
&& -d
'../ext'
;
require
Config; Config->
import
;
skip_all_if_miniperl();
for
my
$needed
(
qw(d_socket d_getpbyname)
) {
if
(
$Config
{
$needed
} ne
'define'
) {
skip_all(
"-- \$Config{$needed} undefined"
);
}
}
unless
(
$Config
{extensions} =~ /\bSocket\b/) {
skip_all(
'-- Socket not available'
);
}
}
our
$TODO
;
$| = 1;
my
$tcp
=
getprotobyname
(
'tcp'
)
or skip_all(
"no tcp protocol available ($!)"
);
my
$udp
=
getprotobyname
(
'udp'
)
or note
"getprotobyname('udp') failed: $!"
;
my
$local
=
gethostbyname
(
'localhost'
)
or note
"gethostbyname('localhost') failed: $!"
;
my
$fork
=
$Config
{d_fork} ||
$Config
{d_pseudofork};
{
socket
(
my
$sock
, PF_INET, SOCK_STREAM,
$tcp
)
or skip_all(
'socket() for tcp failed ($!), nothing else will work'
);
ok(
close
(
$sock
),
"close the socket"
);
}
SKIP:
{
$udp
or skip
"No udp"
, 1;
$! = 0;
socket
(
my
$sock
, PF_INET, SOCK_STREAM,
$udp
)
and skip
"managed to make a UDP stream socket"
, 1;
ok(0+$!,
"error set on failed socket()"
);
}
SKIP: {
$local
or skip(
"No localhost"
, 3);
ok(
socket
(
my
$serv
, PF_INET, SOCK_STREAM,
$tcp
),
"make a tcp socket"
);
my
$bind_at
= pack_sockaddr_in(0,
$local
);
ok(
bind
(
$serv
,
$bind_at
),
"bind works"
)
or skip(
"Couldn't bind to localhost"
, 4);
my
$bind_name
=
getsockname
(
$serv
);
ok(
$bind_name
,
"getsockname() on bound socket"
);
my
(
$bind_port
) = unpack_sockaddr_in(
$bind_name
);
print
"# port $bind_port\n"
;
SKIP:
{
ok(
listen
(
$serv
, 5),
"listen() works"
)
or diag
"listen error: $!"
;
$fork
or skip(
"No fork"
, 2);
my
$pid
=
fork
;
my
$send_data
=
"test"
x 50_000;
if
(
$pid
) {
ok(
socket
(
my
$accept
, PF_INET, SOCK_STREAM,
$tcp
),
"make accept tcp socket"
);
ok(
my
$addr
=
accept
(
$accept
,
$serv
),
"accept() works"
)
or diag
"accept error: $!"
;
binmode
$accept
;
SKIP: {
skip
"no fcntl"
, 1
unless
$Config
{d_fcntl};
my
$acceptfd
=
fileno
(
$accept
);
fresh_perl_is(
qq(
print open(F, "+<&=$acceptfd")
? 1 : 0,
"\\n"
;
),
"0\n"
, {},
"accepted socket not inherited across exec"
);
}
my
$sent_total
= 0;
while
(
$sent_total
<
length
$send_data
) {
my
$sent
=
send
(
$accept
,
substr
(
$send_data
,
$sent_total
), 0);
defined
$sent
or
last
;
$sent_total
+=
$sent
;
}
my
$shutdown
=
shutdown
(
$accept
, 1);
<
$accept
>;
curr_test(curr_test()+5);
waitpid
(
$pid
, 0);
ok(
$shutdown
,
"shutdown() works"
);
}
elsif
(
defined
$pid
) {
curr_test(curr_test()+3);
ok_child(
close
(
$serv
),
"close server socket in child"
);
ok_child(
socket
(
my
$child
, PF_INET, SOCK_STREAM,
$tcp
),
"make child tcp socket"
);
ok_child(
connect
(
$child
,
$bind_name
),
"connect() works"
)
or diag
"connect error: $!"
;
binmode
$child
;
my
$buf
;
my
$recv_peer
=
recv
(
$child
,
$buf
, 1000, 0);
{
local
$TODO
=
"[perl #122657] Hurd doesn't populate sin_len correctly"
if
$^O eq
"gnu"
;
ok_child(
$recv_peer
eq
''
||
$recv_peer
eq
getpeername
$child
,
"peer from recv() should be empty or the remote name"
);
}
while
(
defined
recv
(
$child
,
my
$tmp
, 1000, 0)) {
last
if
length
$tmp
== 0;
$buf
.=
$tmp
;
}
is_child(
$buf
,
$send_data
,
"check we received the data"
);
close
(
$child
);
end_child();
exit
(0);
}
else
{
diag
"fork() failed $!"
;
skip(
"fork() failed"
, 2);
}
}
}
SKIP: {
$local
or skip(
"No localhost"
, 1);
$fork
or skip(
"No fork"
, 1);
note
"recv/send :utf8 tests"
;
ok(
socket
(
my
$serv
, PF_INET, SOCK_STREAM,
$tcp
),
"make a tcp socket (recv/send :utf8 handling)"
);
my
$bind_at
= pack_sockaddr_in(0,
$local
);
ok(
bind
(
$serv
,
$bind_at
),
"bind works"
)
or skip(
"Couldn't bind to localhost"
, 1);
my
$bind_name
=
getsockname
(
$serv
);
ok(
$bind_name
,
"getsockname() on bound socket"
);
my
(
$bind_port
) = unpack_sockaddr_in(
$bind_name
);
print
"# port $bind_port\n"
;
SKIP:
{
ok(
listen
(
$serv
, 5),
"listen() works"
)
or diag
"listen error: $!"
;
my
$pid
=
fork
;
my
$send_data
=
"test\x80\xFF"
x 50_000;
if
(
$pid
) {
ok(
socket
(
my
$accept
, PF_INET, SOCK_STREAM,
$tcp
),
"make accept tcp socket"
);
ok(
my
$addr
=
accept
(
$accept
,
$serv
),
"accept() works"
)
or diag
"accept error: $!"
;
binmode
$accept
,
':raw:utf8'
;
ok(!
eval
{
send
(
$accept
,
"ABC"
, 0); 1 },
"should die on send to :utf8 socket"
);
binmode
$accept
;
utf8::upgrade(
$send_data
);
my
$sent_total
= 0;
while
(
$sent_total
<
length
$send_data
) {
my
$sent
=
send
(
$accept
,
substr
(
$send_data
,
$sent_total
), 0);
defined
$sent
or
last
;
$sent_total
+=
$sent
;
}
my
$shutdown
=
shutdown
(
$accept
, 1);
<
$accept
>;
curr_test(curr_test()+6);
waitpid
(
$pid
, 0);
ok(
$shutdown
,
"shutdown() works"
);
}
elsif
(
defined
$pid
) {
curr_test(curr_test()+3);
ok_child(
close
(
$serv
),
"close server socket in child"
);
ok_child(
socket
(
my
$child
, PF_INET, SOCK_STREAM,
$tcp
),
"make child tcp socket"
);
ok_child(
connect
(
$child
,
$bind_name
),
"connect() works"
)
or diag
"connect error: $!"
;
binmode
$child
,
':raw:utf8'
;
my
$buf
;
ok_child(!
eval
{
recv
(
$child
,
$buf
, 1000, 0); 1 },
"recv on :utf8 should die"
);
is_child(
$buf
,
""
,
"buf shouldn't contain anything"
);
binmode
$child
;
my
$recv_peer
=
recv
(
$child
,
$buf
, 1000, 0);
while
(
defined
recv
(
$child
,
my
$tmp
, 1000, 0)) {
last
if
length
$tmp
== 0;
$buf
.=
$tmp
;
}
is_child(
$buf
,
$send_data
,
"check we received the data"
);
close
(
$child
);
end_child();
exit
(0);
}
else
{
diag
"fork() failed $!"
;
skip(
"fork() failed"
, 2);
}
}
}
SKIP:
{
eval
{
require
Errno;
defined
&Errno::EMFILE
}
or skip
"Can't load Errno or EMFILE not defined"
, 1;
$^O eq
"darwin"
&&
exists
$ENV
{PERLIO} &&
$ENV
{PERLIO} =~ /stdio/
and skip
"errno values from stdio are unspecified"
, 1;
my
@socks
;
my
$sock_limit
= 1000;
while
(
@socks
<
$sock_limit
) {
socket
my
$work
, PF_INET, SOCK_STREAM,
$tcp
or
last
;
push
@socks
,
$work
;
}
@socks
==
$sock_limit
and skip
"Didn't run out of open handles"
, 1;
is(0+$!, Errno::EMFILE(),
"check correct errno for too many files"
);
}
{
my
$sock
;
my
$proto
=
getprotobyname
(
'tcp'
);
socket
(
$sock
, PF_INET, SOCK_STREAM,
$proto
);
accept
(
$sock
,
$sock
);
ok(
'RT #7614: still alive after accept($sock, $sock)'
);
}
SKIP: {
skip
"no fcntl"
, 1
unless
$Config
{d_fcntl};
my
$sock
;
socket
(
$sock
, PF_INET, SOCK_STREAM,
$tcp
) or
die
"socket: $!"
;
my
$sockfd
=
fileno
(
$sock
);
fresh_perl_is(
qq(
print open(F, "+<&=$sockfd")
? 1 : 0,
"\\n"
;
),
"0\n"
, {},
"fresh socket not inherited across exec"
);
}
SKIP:
{
my
$val
;
{
sub
TIESCALAR {
bless
{},
shift
}
sub
FETCH {
$val
}
}
socket
(
my
$sock
, PF_INET, SOCK_STREAM,
$tcp
);
$val
= 0;
ok(
setsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR, 1),
"set known SO_REUSEADDR"
);
isnt(
getsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR),
pack
(
"i"
, 0),
"check that worked"
);
tie
my
$m
,
"SetsockoptMagic"
;
$val
=
pack
(
"i"
, 0);
my
$temp
=
$m
;
$val
= 1;
ok(
setsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR,
$m
),
"set SO_REUSEADDR from magic"
);
isnt(
getsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR),
pack
(
"i"
, 0),
"check SO_REUSEADDR set correctly"
);
ok(
setsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR, !1),
"clear SO_REUSEADDR by a boolean false"
);
is(
getsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR),
pack
(
"i"
, 0),
"check SO_REUSEADDR cleared correctly"
);
ok(
setsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR, !0),
"set SO_REUSEADDR by a boolean true"
);
isnt(
getsockopt
(
$sock
, SOL_SOCKET, SO_REUSEADDR),
pack
(
"i"
, 0),
"check SO_REUSEADDR set correctly"
);
}
SKIP: {
defined
(
my
$IPPROTO_IP
=
eval
{ Socket::IPPROTO_IP() })
or skip
'no IPPROTO_IP'
, 4;
defined
(
my
$IP_TTL
=
eval
{ Socket::IP_TTL() })
or skip
'no IP_TTL'
, 4;
my
$sock
;
socket
(
$sock
, PF_INET, SOCK_STREAM,
$tcp
) or BAIL_OUT
"socket: $!"
;
my
$ttl
= 7;
my
$integer_only_ttl
= 0 +
$ttl
;
ok(
setsockopt
(
$sock
,
$IPPROTO_IP
,
$IP_TTL
,
$integer_only_ttl
),
'setsockopt with an integer-only OPTVAL'
);
my
$set_ttl
=
getsockopt
(
$sock
,
$IPPROTO_IP
,
$IP_TTL
);
is(
unpack
(
'i'
,
$set_ttl
//
''
),
$ttl
,
'TTL set to desired value'
);
my
$also_string_ttl
=
$ttl
;
my
$string
=
"$also_string_ttl"
;
ok(
setsockopt
(
$sock
,
$IPPROTO_IP
,
$IP_TTL
,
$also_string_ttl
),
'setsockopt with an integer OPTVAL with stringified value'
);
$set_ttl
=
getsockopt
(
$sock
,
$IPPROTO_IP
,
$IP_TTL
);
is(
unpack
(
'i'
,
$set_ttl
//
''
),
$ttl
,
'TTL set to desired value'
);
}
SKIP: {
eval
{ Socket::IPPROTO_TCP(); 1 } or skip
'no IPPROTO_TCP'
, 1;
eval
{ Socket::SOL_SOCKET(); 1 } or skip
'no SOL_SOCKET'
, 1;
eval
{ Socket::SO_SNDBUF(); 1 } or skip
'no SO_SNDBUF'
, 1;
skip
'setting socket buffer size requires elevated privileges'
, 1
if
$^O eq
'VMS'
;
fresh_perl_is(
<<'EOP', "Ok.\n", {}, 'setsockopt works for a constant that is once stringified');
use warnings;
use strict;
use Socket qw'PF_INET SOCK_STREAM IPPROTO_TCP SOL_SOCKET SO_SNDBUF';
use constant { SNDBUF_SIZE => 32768 };
socket(my $sock, PF_INET, SOCK_STREAM, IPPROTO_TCP)
or die "Could not create socket - $!\n";
setsockopt($sock,SOL_SOCKET,SO_SNDBUF,SNDBUF_SIZE)
or die "Could not set SO_SNDBUF on socket - $!\n";
my $sndBuf=getsockopt($sock,SOL_SOCKET,SO_SNDBUF)
or die "Could not get SO_SNDBUF on socket - $!\n";
$sndBuf=unpack('i',$sndBuf);
die "Unexpected SO_SNDBUF value: $sndBuf\n"
unless($sndBuf == SNDBUF_SIZE || $sndBuf == 2*SNDBUF_SIZE);
print "Ok.\n";
exit;
sub bug {SNDBUF_SIZE.''}
EOP
}
done_testing();
my
@child_tests
;
sub
ok_child {
my
(
$ok
,
$note
) =
@_
;
push
@child_tests
, (
$ok
?
"ok "
:
"not ok "
) . curr_test() .
" - $note "
. (
$TODO
?
"# TODO $TODO"
:
""
) .
"\n"
;
curr_test(curr_test()+1);
}
sub
is_child {
my
(
$got
,
$want
,
$note
) =
@_
;
ok_child(
$got
eq
$want
,
$note
);
}
sub
end_child {
print
@child_tests
;
}