EVERYTHING FROM THE OTHER REPO

This commit is contained in:
2026-07-29 03:36:26 +03:00
parent 6777ea96db
commit 92a6ae42f5
4426 changed files with 1696506 additions and 2 deletions
@@ -0,0 +1,67 @@
#
package IO::File;
use 5.008_001;
use strict;
use Carp;
use Symbol;
use SelectSaver;
use IO::Seekable;
require Exporter;
our @ISA = qw(IO::Handle IO::Seekable Exporter);
our $VERSION = "1.55";
our @EXPORT = @IO::Seekable::EXPORT;
eval {
# Make all Fcntl O_XXX constants available for importing
require Fcntl;
my @O = grep /^O_/, @Fcntl::EXPORT;
Fcntl->import(@O); # first we import what we want to export
push(@EXPORT, @O);
};
################################################
## Constructor
##
sub new {
my $type = shift;
my $class = ref($type) || $type || "IO::File";
@_ >= 0 && @_ <= 3
or croak "usage: $class->new([FILENAME [,MODE [,PERMS]]])";
my $fh = $class->SUPER::new();
if (@_) {
$fh->open(@_)
or return undef;
}
$fh;
}
################################################
## Open
##
sub open {
@_ >= 2 && @_ <= 4 or croak 'usage: $fh->open(FILENAME [,MODE [,PERMS]])';
my ($fh, $file) = @_;
if (@_ > 2) {
my ($mode, $perms) = @_[2, 3];
if ($mode =~ /^\d+$/) {
defined $perms or $perms = 0666;
return sysopen($fh, $file, $mode, $perms);
} elsif ($mode =~ /:/) {
return open($fh, $mode, $file) if @_ == 3;
croak 'usage: $fh->open(FILENAME, IOLAYERS)';
} else {
return open($fh, IO::Handle::_open_mode_string($mode), $file);
}
}
open($fh, $file);
}
1;
@@ -0,0 +1,382 @@
package IO::Handle;
use 5.008_001;
use strict;
use Carp;
use Symbol;
use SelectSaver;
use IO (); # Load the XS module
require Exporter;
our @ISA = qw(Exporter);
our $VERSION = "1.55";
our @EXPORT_OK = qw(
autoflush
output_field_separator
output_record_separator
input_record_separator
input_line_number
format_page_number
format_lines_per_page
format_lines_left
format_name
format_top_name
format_line_break_characters
format_formfeed
format_write
print
printf
say
getline
getlines
printflush
flush
SEEK_SET
SEEK_CUR
SEEK_END
_IOFBF
_IOLBF
_IONBF
);
################################################
## Constructors, destructors.
##
sub new {
my $class = ref($_[0]) || $_[0] || "IO::Handle";
if (@_ != 1) {
# Since perl will automatically require IO::File if needed, but
# also initialises IO::File's @ISA as part of the core we must
# ensure IO::File is loaded if IO::Handle is. This avoids effect-
# ively "half-loading" IO::File.
if ($] > 5.013 && $class eq 'IO::File' && !$INC{"IO/File.pm"}) {
require IO::File;
shift;
return IO::File::->new(@_);
}
croak "usage: $class->new()";
}
my $io = gensym;
bless $io, $class;
}
sub new_from_fd {
my $class = ref($_[0]) || $_[0] || "IO::Handle";
@_ == 3 or croak "usage: $class->new_from_fd(FD, MODE)";
my $io = gensym;
shift;
IO::Handle::fdopen($io, @_)
or return undef;
bless $io, $class;
}
#
# There is no need for DESTROY to do anything, because when the
# last reference to an IO object is gone, Perl automatically
# closes its associated files (if any). However, to avoid any
# attempts to autoload DESTROY, we here define it to do nothing.
#
sub DESTROY {}
################################################
## Open and close.
##
sub _open_mode_string {
my ($mode) = @_;
$mode =~ /^\+?(<|>>?)$/
or $mode =~ s/^r(\+?)$/$1</
or $mode =~ s/^w(\+?)$/$1>/
or $mode =~ s/^a(\+?)$/$1>>/
or croak "IO::Handle: bad open mode: $mode";
$mode;
}
sub fdopen {
@_ == 3 or croak 'usage: $io->fdopen(FD, MODE)';
my ($io, $fd, $mode) = @_;
local(*GLOB);
if (ref($fd) && "$fd" =~ /GLOB\(/o) {
# It's a glob reference; Alias it as we cannot get name of anon GLOBs
my $n = qualify(*GLOB);
*GLOB = *{*$fd};
$fd = $n;
} elsif ($fd =~ m#^\d+$#) {
# It's an FD number; prefix with "=".
$fd = "=$fd";
}
open($io, _open_mode_string($mode) . '&' . $fd)
? $io : undef;
}
sub close {
@_ == 1 or croak 'usage: $io->close()';
my($io) = @_;
close($io);
}
################################################
## Normal I/O functions.
##
# flock
# select
sub opened {
@_ == 1 or croak 'usage: $io->opened()';
defined fileno($_[0]);
}
sub fileno {
@_ == 1 or croak 'usage: $io->fileno()';
fileno($_[0]);
}
sub getc {
@_ == 1 or croak 'usage: $io->getc()';
getc($_[0]);
}
sub eof {
@_ == 1 or croak 'usage: $io->eof()';
eof($_[0]);
}
sub print {
@_ or croak 'usage: $io->print(ARGS)';
my $this = shift;
print $this @_;
}
sub printf {
@_ >= 2 or croak 'usage: $io->printf(FMT,[ARGS])';
my $this = shift;
printf $this @_;
}
sub say {
@_ or croak 'usage: $io->say(ARGS)';
my $this = shift;
local $\ = "\n";
print $this @_;
}
sub truncate {
@_ == 2 or croak 'usage: $io->truncate(LEN)';
truncate($_[0], $_[1]);
}
sub read {
@_ == 3 || @_ == 4 or croak 'usage: $io->read(BUF, LEN [, OFFSET])';
read($_[0], $_[1], $_[2], $_[3] || 0);
}
sub sysread {
@_ == 3 || @_ == 4 or croak 'usage: $io->sysread(BUF, LEN [, OFFSET])';
sysread($_[0], $_[1], $_[2], $_[3] || 0);
}
sub write {
@_ >= 2 && @_ <= 4 or croak 'usage: $io->write(BUF [, LEN [, OFFSET]])';
local($\) = "";
$_[2] = length($_[1]) unless defined $_[2];
print { $_[0] } substr($_[1], $_[3] || 0, $_[2]);
}
sub syswrite {
@_ >= 2 && @_ <= 4 or croak 'usage: $io->syswrite(BUF [, LEN [, OFFSET]])';
if (defined($_[2])) {
syswrite($_[0], $_[1], $_[2], $_[3] || 0);
} else {
syswrite($_[0], $_[1]);
}
}
sub stat {
@_ == 1 or croak 'usage: $io->stat()';
stat($_[0]);
}
################################################
## State modification functions.
##
sub autoflush {
my $old = SelectSaver->new(qualify($_[0], caller));
my $prev = $|;
$| = @_ > 1 ? $_[1] : 1;
$prev;
}
sub output_field_separator {
carp "output_field_separator is not supported on a per-handle basis"
if ref($_[0]);
my $prev = $,;
$, = $_[1] if @_ > 1;
$prev;
}
sub output_record_separator {
carp "output_record_separator is not supported on a per-handle basis"
if ref($_[0]);
my $prev = $\;
$\ = $_[1] if @_ > 1;
$prev;
}
sub input_record_separator {
carp "input_record_separator is not supported on a per-handle basis"
if ref($_[0]);
my $prev = $/;
$/ = $_[1] if @_ > 1;
$prev;
}
sub input_line_number {
local $.;
() = tell qualify($_[0], caller) if ref($_[0]);
my $prev = $.;
$. = $_[1] if @_ > 1;
$prev;
}
sub format_page_number {
my $old;
$old = SelectSaver->new(qualify($_[0], caller)) if ref($_[0]);
my $prev = $%;
$% = $_[1] if @_ > 1;
$prev;
}
sub format_lines_per_page {
my $old;
$old = SelectSaver->new(qualify($_[0], caller)) if ref($_[0]);
my $prev = $=;
$= = $_[1] if @_ > 1;
$prev;
}
sub format_lines_left {
my $old;
$old = SelectSaver->new(qualify($_[0], caller)) if ref($_[0]);
my $prev = $-;
$- = $_[1] if @_ > 1;
$prev;
}
sub format_name {
my $old;
$old = SelectSaver->new(qualify($_[0], caller)) if ref($_[0]);
my $prev = $~;
$~ = qualify($_[1], caller) if @_ > 1;
$prev;
}
sub format_top_name {
my $old;
$old = SelectSaver->new(qualify($_[0], caller)) if ref($_[0]);
my $prev = $^;
$^ = qualify($_[1], caller) if @_ > 1;
$prev;
}
sub format_line_break_characters {
carp "format_line_break_characters is not supported on a per-handle basis"
if ref($_[0]);
my $prev = $:;
$: = $_[1] if @_ > 1;
$prev;
}
sub format_formfeed {
carp "format_formfeed is not supported on a per-handle basis"
if ref($_[0]);
my $prev = $^L;
$^L = $_[1] if @_ > 1;
$prev;
}
sub formline {
my $io = shift;
my $picture = shift;
local($^A) = $^A;
local($\) = "";
formline($picture, @_);
print $io $^A;
}
sub format_write {
@_ < 3 || croak 'usage: $io->write( [FORMAT_NAME] )';
if (@_ == 2) {
my ($io, $fmt) = @_;
my $oldfmt = $io->format_name(qualify($fmt,caller));
CORE::write($io);
$io->format_name($oldfmt);
} else {
CORE::write($_[0]);
}
}
sub fcntl {
@_ == 3 || croak 'usage: $io->fcntl( OP, VALUE );';
my ($io, $op) = @_;
return fcntl($io, $op, $_[2]);
}
sub ioctl {
@_ == 3 || croak 'usage: $io->ioctl( OP, VALUE );';
my ($io, $op) = @_;
return ioctl($io, $op, $_[2]);
}
# this sub is for compatibility with older releases of IO that used
# a sub called constant to determine if a constant existed -- GMB
#
# The SEEK_* and _IO?BF constants were the only constants at that time
# any new code should just check defined(&CONSTANT_NAME)
sub constant {
no strict 'refs';
my $name = shift;
(($name =~ /^(SEEK_(SET|CUR|END)|_IO[FLN]BF)$/) && defined &{$name})
? &{$name}() : undef;
}
# so that flush.pl can be deprecated
sub printflush {
my $io = shift;
my $old;
$old = SelectSaver->new(qualify($io, caller)) if ref($io);
local $| = 1;
if(ref($io)) {
print $io @_;
}
else {
print @_;
}
}
################################################
## Binmode
##
sub binmode {
( @_ == 1 or @_ == 2 ) or croak 'usage $fh->binmode([LAYER])';
my($fh, $layer) = @_;
return binmode $$fh unless $layer;
return binmode $$fh, $layer;
}
1;
@@ -0,0 +1,159 @@
# IO::Pipe.pm
#
# Copyright (c) 1996-8 Graham Barr <gbarr@pobox.com>. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
package IO::Pipe;
use 5.008_001;
use IO::Handle;
use strict;
use Carp;
use Symbol;
our $VERSION = "1.55";
sub new {
my $type = shift;
my $class = ref($type) || $type || "IO::Pipe";
@_ == 0 || @_ == 2 or croak "usage: $class->([READFH, WRITEFH])";
my $me = bless gensym(), $class;
my($readfh,$writefh) = @_ ? @_ : $me->handles;
pipe($readfh, $writefh)
or return undef;
@{*$me} = ($readfh, $writefh);
$me;
}
sub handles {
@_ == 1 or croak 'usage: $pipe->handles()';
(IO::Pipe::End->new(), IO::Pipe::End->new());
}
my $do_spawn = $^O eq 'os2' || $^O eq 'MSWin32';
sub _doit {
my $me = shift;
my $rw = shift;
my $pid = $do_spawn ? 0 : fork();
if($pid) { # Parent
return $pid;
}
elsif(defined $pid) { # Child or spawn
my $fh;
my $io = $rw ? \*STDIN : \*STDOUT;
my ($mode, $save) = $rw ? "r" : "w";
if ($do_spawn) {
require Fcntl;
$save = IO::Handle->new_from_fd($io, $mode);
my $handle = shift;
# Close in child:
unless ($^O eq 'MSWin32') {
fcntl($handle, Fcntl::F_SETFD(), 1) or croak "fcntl: $!";
}
$fh = $rw ? ${*$me}[0] : ${*$me}[1];
} else {
shift;
$fh = $rw ? $me->reader() : $me->writer(); # close the other end
}
bless $io, "IO::Handle";
$io->fdopen($fh, $mode);
$fh->close;
if ($do_spawn) {
$pid = eval { system 1, @_ }; # 1 == P_NOWAIT
my $err = $!;
$io->fdopen($save, $mode);
$save->close or croak "Cannot close $!";
croak "IO::Pipe: Cannot spawn-NOWAIT: $err" if not $pid or $pid < 0;
return $pid;
} else {
exec @_ or
croak "IO::Pipe: Cannot exec: $!";
}
}
else {
croak "IO::Pipe: Cannot fork: $!";
}
# NOT Reached
}
sub reader {
@_ >= 1 or croak 'usage: $pipe->reader( [SUB_COMMAND_ARGS] )';
my $me = shift;
return undef
unless(ref($me) || ref($me = $me->new));
my $fh = ${*$me}[0];
my $pid;
$pid = $me->_doit(0, $fh, @_)
if(@_);
close ${*$me}[1];
bless $me, ref($fh);
*$me = *$fh; # Alias self to handle
$me->fdopen($fh->fileno,"r")
unless defined($me->fileno);
bless $fh; # Really wan't un-bless here
${*$me}{'io_pipe_pid'} = $pid
if defined $pid;
$me;
}
sub writer {
@_ >= 1 or croak 'usage: $pipe->writer( [SUB_COMMAND_ARGS] )';
my $me = shift;
return undef
unless(ref($me) || ref($me = $me->new));
my $fh = ${*$me}[1];
my $pid;
$pid = $me->_doit(1, $fh, @_)
if(@_);
close ${*$me}[0];
bless $me, ref($fh);
*$me = *$fh; # Alias self to handle
$me->fdopen($fh->fileno,"w")
unless defined($me->fileno);
bless $fh; # Really wan't un-bless here
${*$me}{'io_pipe_pid'} = $pid
if defined $pid;
$me;
}
package IO::Pipe::End;
our(@ISA);
@ISA = qw(IO::Handle);
sub close {
my $fh = shift;
my $r = $fh->SUPER::close(@_);
waitpid(${*$fh}{'io_pipe_pid'},0)
if(defined ${*$fh}{'io_pipe_pid'});
$r;
}
1;
__END__
@@ -0,0 +1,34 @@
#
package IO::Seekable;
use 5.008_001;
use Carp;
use strict;
use IO::Handle ();
# XXX we can't get these from IO::Handle or we'll get prototype
# mismatch warnings on C<use POSIX; use IO::File;> :-(
use Fcntl qw(SEEK_SET SEEK_CUR SEEK_END);
require Exporter;
our @EXPORT = qw(SEEK_SET SEEK_CUR SEEK_END);
our @ISA = qw(Exporter);
our $VERSION = "1.55";
sub seek {
@_ == 3 or croak 'usage: $io->seek(POS, WHENCE)';
seek($_[0], $_[1], $_[2]);
}
sub sysseek {
@_ == 3 or croak 'usage: $io->sysseek(POS, WHENCE)';
sysseek($_[0], $_[1], $_[2]);
}
sub tell {
@_ == 1 or croak 'usage: $io->tell()';
tell($_[0]);
}
1;
@@ -0,0 +1,261 @@
# IO::Select.pm
#
# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
package IO::Select;
use strict;
use warnings::register;
require Exporter;
our $VERSION = "1.55";
our @ISA = qw(Exporter); # This is only so we can do version checking
sub VEC_BITS () {0}
sub FD_COUNT () {1}
sub FIRST_FD () {2}
sub new
{
my $self = shift;
my $type = ref($self) || $self;
my $vec = bless [undef,0], $type;
$vec->add(@_)
if @_;
$vec;
}
sub add
{
shift->_update('add', @_);
}
sub remove
{
shift->_update('remove', @_);
}
sub exists
{
my $vec = shift;
my $fno = $vec->_fileno(shift);
return undef unless defined $fno;
$vec->[$fno + FIRST_FD];
}
sub _fileno
{
my($self, $f) = @_;
return unless defined $f;
$f = $f->[0] if ref($f) eq 'ARRAY';
if($f =~ /^[0-9]+$/) { # plain file number
return $f;
}
elsif(defined(my $fd = fileno($f))) {
return $fd;
}
else {
# Neither a plain file number nor an opened filehandle; but maybe it was
# previously registered and has since been closed. ->remove still wants to
# know what fileno it had
foreach my $i ( FIRST_FD .. $#$self ) {
return $i - FIRST_FD if defined $self->[$i] && $self->[$i] == $f;
}
return undef;
}
}
sub _update
{
my $vec = shift;
my $add = shift eq 'add';
my $bits = $vec->[VEC_BITS];
$bits = '' unless defined $bits;
my $count = 0;
my $f;
foreach $f (@_)
{
my $fn = $vec->_fileno($f);
if ($add) {
next unless defined $fn;
my $i = $fn + FIRST_FD;
if (defined $vec->[$i]) {
$vec->[$i] = $f; # if array rest might be different, so we update
next;
}
$vec->[FD_COUNT]++;
vec($bits, $fn, 1) = 1;
$vec->[$i] = $f;
} else { # remove
if ( ! defined $fn ) { # remove if fileno undef'd
$fn = 0;
for my $fe (@{$vec}[FIRST_FD .. $#$vec]) {
if (defined($fe) && $fe == $f) {
$vec->[FD_COUNT]--;
$fe = undef;
vec($bits, $fn, 1) = 0;
last;
}
++$fn;
}
}
else {
my $i = $fn + FIRST_FD;
next unless defined $vec->[$i];
$vec->[FD_COUNT]--;
vec($bits, $fn, 1) = 0;
$vec->[$i] = undef;
}
}
$count++;
}
$vec->[VEC_BITS] = $vec->[FD_COUNT] ? $bits : undef;
$count;
}
sub can_read
{
my $vec = shift;
my $timeout = shift;
my $r = $vec->[VEC_BITS];
defined($r) && (select($r,undef,undef,$timeout) > 0)
? handles($vec, $r)
: ();
}
sub can_write
{
my $vec = shift;
my $timeout = shift;
my $w = $vec->[VEC_BITS];
defined($w) && (select(undef,$w,undef,$timeout) > 0)
? handles($vec, $w)
: ();
}
sub has_exception
{
my $vec = shift;
my $timeout = shift;
my $e = $vec->[VEC_BITS];
defined($e) && (select(undef,undef,$e,$timeout) > 0)
? handles($vec, $e)
: ();
}
sub has_error
{
warnings::warn("Call to deprecated method 'has_error', use 'has_exception'")
if warnings::enabled();
goto &has_exception;
}
sub count
{
my $vec = shift;
$vec->[FD_COUNT];
}
sub bits
{
my $vec = shift;
$vec->[VEC_BITS];
}
sub as_string # for debugging
{
my $vec = shift;
my $str = ref($vec) . ": ";
my $bits = $vec->bits;
my $count = $vec->count;
$str .= defined($bits) ? unpack("b*", $bits) : "undef";
$str .= " $count";
my @handles = @$vec;
splice(@handles, 0, FIRST_FD);
for (@handles) {
$str .= " " . (defined($_) ? "$_" : "-");
}
$str;
}
sub _max
{
my($a,$b,$c) = @_;
$a > $b
? $a > $c
? $a
: $c
: $b > $c
? $b
: $c;
}
sub select
{
shift
if defined $_[0] && !ref($_[0]);
my($r,$w,$e,$t) = @_;
my @result = ();
my $rb = defined $r ? $r->[VEC_BITS] : undef;
my $wb = defined $w ? $w->[VEC_BITS] : undef;
my $eb = defined $e ? $e->[VEC_BITS] : undef;
if(select($rb,$wb,$eb,$t) > 0)
{
my @r = ();
my @w = ();
my @e = ();
my $i = _max(defined $r ? scalar(@$r)-1 : 0,
defined $w ? scalar(@$w)-1 : 0,
defined $e ? scalar(@$e)-1 : 0);
for( ; $i >= FIRST_FD ; $i--)
{
my $j = $i - FIRST_FD;
push(@r, $r->[$i])
if defined $rb && defined $r->[$i] && vec($rb, $j, 1);
push(@w, $w->[$i])
if defined $wb && defined $w->[$i] && vec($wb, $j, 1);
push(@e, $e->[$i])
if defined $eb && defined $e->[$i] && vec($eb, $j, 1);
}
@result = (\@r, \@w, \@e);
}
@result;
}
sub handles
{
my $vec = shift;
my $bits = shift;
my @h = ();
my $i;
my $max = scalar(@$vec) - 1;
for ($i = FIRST_FD; $i <= $max; $i++)
{
next unless defined $vec->[$i];
push(@h, $vec->[$i])
if !defined($bits) || vec($bits, $i - FIRST_FD, 1);
}
@h;
}
1;
__END__
@@ -0,0 +1,397 @@
# IO::Socket.pm
#
# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
package IO::Socket;
use 5.008_001;
use IO::Handle;
use Socket 1.3;
use Carp;
use strict;
use Exporter;
use Errno;
# legacy
require IO::Socket::INET;
require IO::Socket::UNIX if ($^O ne 'epoc' && $^O ne 'symbian');
our @ISA = qw(IO::Handle);
our $VERSION = "1.55";
our @EXPORT_OK = qw(sockatmark);
our $errstr;
sub import {
my $pkg = shift;
if (@_ && $_[0] eq 'sockatmark') { # not very extensible but for now, fast
Exporter::export_to_level('IO::Socket', 1, $pkg, 'sockatmark');
} else {
my $callpkg = caller;
Exporter::export 'Socket', $callpkg, @_;
}
}
sub new {
my($class,%arg) = @_;
my $sock = $class->SUPER::new();
$sock->autoflush(1);
${*$sock}{'io_socket_timeout'} = delete $arg{Timeout};
return scalar(%arg) ? $sock->configure(\%arg)
: $sock;
}
my @domain2pkg;
sub register_domain {
my($p,$d) = @_;
$domain2pkg[$d] = $p;
}
sub configure {
my($sock,$arg) = @_;
my $domain = delete $arg->{Domain};
croak 'IO::Socket: Cannot configure a generic socket'
unless defined $domain;
croak "IO::Socket: Unsupported socket domain"
unless defined $domain2pkg[$domain];
croak "IO::Socket: Cannot configure socket in domain '$domain'"
unless ref($sock) eq "IO::Socket";
bless($sock, $domain2pkg[$domain]);
$sock->configure($arg);
}
sub socket {
@_ == 4 or croak 'usage: $sock->socket(DOMAIN, TYPE, PROTOCOL)';
my($sock,$domain,$type,$protocol) = @_;
socket($sock,$domain,$type,$protocol) or
return undef;
${*$sock}{'io_socket_domain'} = $domain;
${*$sock}{'io_socket_type'} = $type;
# "A value of 0 for protocol will let the system select an
# appropriate protocol"
# so we need to look up what the system selected,
# not cache PF_UNSPEC.
${*$sock}{'io_socket_proto'} = $protocol if $protocol;
$sock;
}
sub socketpair {
@_ == 4 || croak 'usage: IO::Socket->socketpair(DOMAIN, TYPE, PROTOCOL)';
my($class,$domain,$type,$protocol) = @_;
my $sock1 = $class->new();
my $sock2 = $class->new();
socketpair($sock1,$sock2,$domain,$type,$protocol) or
return ();
${*$sock1}{'io_socket_type'} = ${*$sock2}{'io_socket_type'} = $type;
${*$sock1}{'io_socket_proto'} = ${*$sock2}{'io_socket_proto'} = $protocol;
($sock1,$sock2);
}
sub connect {
@_ == 2 or croak 'usage: $sock->connect(NAME)';
my $sock = shift;
my $addr = shift;
my $timeout = ${*$sock}{'io_socket_timeout'};
my $err;
my $blocking;
$blocking = $sock->blocking(0) if $timeout;
if (!connect($sock, $addr)) {
if (defined $timeout && ($!{EINPROGRESS} || $!{EWOULDBLOCK})) {
require IO::Select;
my $sel = IO::Select->new( $sock );
undef $!;
my($r,$w,$e) = IO::Select::select(undef,$sel,$sel,$timeout);
if(@$e[0]) {
# Windows return from select after the timeout in case of
# WSAECONNREFUSED(10061) if exception set is not used.
# This behavior is different from Linux.
# Using the exception
# set we now emulate the behavior in Linux
# - Karthik Rajagopalan
$err = $sock->getsockopt(SOL_SOCKET,SO_ERROR);
$errstr = $@ = "connect: $err";
}
elsif(!@$w[0]) {
$err = $! || (exists &Errno::ETIMEDOUT ? &Errno::ETIMEDOUT : 1);
$errstr = $@ = "connect: timeout";
}
elsif (!connect($sock,$addr) &&
not ($!{EISCONN} || ($^O eq 'MSWin32' &&
($! == (($] < 5.019004) ? 10022 : Errno::EINVAL))))
) {
# Some systems refuse to re-connect() to
# an already open socket and set errno to EISCONN.
# Windows sets errno to WSAEINVAL (10022) (pre-5.19.4) or
# EINVAL (22) (5.19.4 onwards).
$err = $!;
$errstr = $@ = "connect: $!";
}
}
elsif ($blocking || !($!{EINPROGRESS} || $!{EWOULDBLOCK})) {
$err = $!;
$errstr = $@ = "connect: $!";
}
}
$sock->blocking(1) if $blocking;
$! = $err if $err;
$err ? undef : $sock;
}
# Enable/disable blocking IO on sockets.
# Without args return the current status of blocking,
# with args change the mode as appropriate, returning the
# old setting, or in case of error during the mode change
# undef.
sub blocking {
my $sock = shift;
return $sock->SUPER::blocking(@_)
if $^O ne 'MSWin32' && $^O ne 'VMS';
# Windows handles blocking differently
#
# http://groups.google.co.uk/group/perl.perl5.porters/browse_thread/thread/b4e2b1d88280ddff/630b667a66e3509f?#630b667a66e3509f
# http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/ioctlsocket_2.asp
#
# 0x8004667e is FIONBIO
#
# which is used to set blocking behaviour.
# NOTE:
# This is a little confusing, the perl keyword for this is
# 'blocking' but the OS level behaviour is 'non-blocking', probably
# because sockets are blocking by default.
# Therefore internally we have to reverse the semantics.
my $orig= !${*$sock}{io_sock_nonblocking};
return $orig unless @_;
my $block = shift;
if ( !$block != !$orig ) {
${*$sock}{io_sock_nonblocking} = $block ? 0 : 1;
ioctl($sock, 0x8004667e, pack("L!",${*$sock}{io_sock_nonblocking}))
or return undef;
}
return $orig;
}
sub close {
@_ == 1 or croak 'usage: $sock->close()';
my $sock = shift;
${*$sock}{'io_socket_peername'} = undef;
$sock->SUPER::close();
}
sub bind {
@_ == 2 or croak 'usage: $sock->bind(NAME)';
my $sock = shift;
my $addr = shift;
return bind($sock, $addr) ? $sock
: undef;
}
sub listen {
@_ >= 1 && @_ <= 2 or croak 'usage: $sock->listen([QUEUE])';
my($sock,$queue) = @_;
$queue = 5
unless $queue && $queue > 0;
return listen($sock, $queue) ? $sock
: undef;
}
sub accept {
@_ == 1 || @_ == 2 or croak 'usage $sock->accept([PKG])';
my $sock = shift;
my $pkg = shift || $sock;
my $timeout = ${*$sock}{'io_socket_timeout'};
my $new = $pkg->new(Timeout => $timeout);
my $peer = undef;
if(defined $timeout) {
require IO::Select;
my $sel = IO::Select->new( $sock );
unless ($sel->can_read($timeout)) {
$errstr = $@ = 'accept: timeout';
$! = (exists &Errno::ETIMEDOUT ? &Errno::ETIMEDOUT : 1);
return;
}
}
$peer = accept($new,$sock)
or return;
${*$new}{$_} = ${*$sock}{$_} for qw( io_socket_domain io_socket_type io_socket_proto );
return wantarray ? ($new, $peer)
: $new;
}
sub sockname {
@_ == 1 or croak 'usage: $sock->sockname()';
getsockname($_[0]);
}
sub peername {
@_ == 1 or croak 'usage: $sock->peername()';
my($sock) = @_;
${*$sock}{'io_socket_peername'} ||= getpeername($sock);
}
sub connected {
@_ == 1 or croak 'usage: $sock->connected()';
my($sock) = @_;
getpeername($sock);
}
sub send {
@_ >= 2 && @_ <= 4 or croak 'usage: $sock->send(BUF, [FLAGS, [TO]])';
my $sock = $_[0];
my $flags = $_[2] || 0;
my $peer;
if ($_[3]) {
# the caller explicitly requested a TO, so use it
# this is non-portable for "connected" UDP sockets
$peer = $_[3];
}
elsif (!defined getpeername($sock)) {
# we're not connected, so we require a peer from somewhere
$peer = $sock->peername;
croak 'send: Cannot determine peer address'
unless(defined $peer);
}
my $r = $peer
? send($sock, $_[1], $flags, $peer)
: send($sock, $_[1], $flags);
# remember who we send to, if it was successful
${*$sock}{'io_socket_peername'} = $peer
if(@_ == 4 && defined $r);
$r;
}
sub recv {
@_ == 3 || @_ == 4 or croak 'usage: $sock->recv(BUF, LEN [, FLAGS])';
my $sock = $_[0];
my $len = $_[2];
my $flags = $_[3] || 0;
# remember who we recv'd from
${*$sock}{'io_socket_peername'} = recv($sock, $_[1]='', $len, $flags);
}
sub shutdown {
@_ == 2 or croak 'usage: $sock->shutdown(HOW)';
my($sock, $how) = @_;
${*$sock}{'io_socket_peername'} = undef;
shutdown($sock, $how);
}
sub setsockopt {
@_ == 4 or croak '$sock->setsockopt(LEVEL, OPTNAME, OPTVAL)';
setsockopt($_[0],$_[1],$_[2],$_[3]);
}
my $intsize = length(pack("i",0));
sub getsockopt {
@_ == 3 or croak '$sock->getsockopt(LEVEL, OPTNAME)';
my $r = getsockopt($_[0],$_[1],$_[2]);
# Just a guess
$r = unpack("i", $r)
if(defined $r && length($r) == $intsize);
$r;
}
sub sockopt {
my $sock = shift;
@_ == 1 ? $sock->getsockopt(SOL_SOCKET,@_)
: $sock->setsockopt(SOL_SOCKET,@_);
}
sub atmark {
@_ == 1 or croak 'usage: $sock->atmark()';
my($sock) = @_;
sockatmark($sock);
}
sub timeout {
@_ == 1 || @_ == 2 or croak 'usage: $sock->timeout([VALUE])';
my($sock,$val) = @_;
my $r = ${*$sock}{'io_socket_timeout'};
${*$sock}{'io_socket_timeout'} = defined $val ? 0 + $val : $val
if(@_ == 2);
$r;
}
sub sockdomain {
@_ == 1 or croak 'usage: $sock->sockdomain()';
my $sock = shift;
if (!defined(${*$sock}{'io_socket_domain'})) {
my $addr = $sock->sockname();
${*$sock}{'io_socket_domain'} = sockaddr_family($addr)
if (defined($addr));
}
${*$sock}{'io_socket_domain'};
}
sub socktype {
@_ == 1 or croak 'usage: $sock->socktype()';
my $sock = shift;
${*$sock}{'io_socket_type'} = $sock->sockopt(Socket::SO_TYPE)
if (!defined(${*$sock}{'io_socket_type'}) && defined(eval{Socket::SO_TYPE}));
${*$sock}{'io_socket_type'}
}
sub protocol {
@_ == 1 or croak 'usage: $sock->protocol()';
my($sock) = @_;
${*$sock}{'io_socket_proto'} = $sock->sockopt(Socket::SO_PROTOCOL)
if (!defined(${*$sock}{'io_socket_proto'}) && defined(eval{Socket::SO_PROTOCOL}));
${*$sock}{'io_socket_proto'};
}
1;
__END__
@@ -0,0 +1,310 @@
# IO::Socket::INET.pm
#
# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
package IO::Socket::INET;
use strict;
use IO::Socket;
use Socket;
use Carp;
use Exporter;
use Errno;
our @ISA = qw(IO::Socket);
our $VERSION = "1.55";
my $EINVAL = exists(&Errno::EINVAL) ? Errno::EINVAL() : 1;
IO::Socket::INET->register_domain( AF_INET );
my %socket_type = ( tcp => SOCK_STREAM,
udp => SOCK_DGRAM,
icmp => SOCK_RAW
);
my %proto_number;
$proto_number{tcp} = Socket::IPPROTO_TCP() if defined &Socket::IPPROTO_TCP;
$proto_number{udp} = Socket::IPPROTO_UDP() if defined &Socket::IPPROTO_UDP;
$proto_number{icmp} = Socket::IPPROTO_ICMP() if defined &Socket::IPPROTO_ICMP;
my %proto_name = reverse %proto_number;
sub new {
my $class = shift;
unshift(@_, "PeerAddr") if @_ == 1;
return $class->SUPER::new(@_);
}
sub _cache_proto {
my @proto = @_;
for (map lc($_), $proto[0], split(' ', $proto[1])) {
$proto_number{$_} = $proto[2];
}
$proto_name{$proto[2]} = $proto[0];
}
sub _get_proto_number {
my $name = lc(shift);
return undef unless defined $name;
return $proto_number{$name} if exists $proto_number{$name};
my @proto = eval { getprotobyname($name) };
return undef unless @proto;
_cache_proto(@proto);
return $proto[2];
}
sub _get_proto_name {
my $num = shift;
return undef unless defined $num;
return $proto_name{$num} if exists $proto_name{$num};
my @proto = eval { getprotobynumber($num) };
return undef unless @proto;
_cache_proto(@proto);
return $proto[0];
}
sub _sock_info {
my($addr,$port,$proto) = @_;
my $origport = $port;
my @serv = ();
$port = $1
if(defined $addr && $addr =~ s,:([\w\(\)/]+)$,,);
if(defined $proto && $proto =~ /\D/) {
my $num = _get_proto_number($proto);
unless (defined $num) {
$IO::Socket::errstr = $@ = "Bad protocol '$proto'";
return;
}
$proto = $num;
}
if(defined $port) {
my $defport = ($port =~ s,\((\d+)\)$,,) ? $1 : undef;
my $pnum = ($port =~ m,^(\d+)$,)[0];
@serv = getservbyname($port, _get_proto_name($proto) || "")
if ($port =~ m,\D,);
$port = $serv[2] || $defport || $pnum;
unless (defined $port) {
$IO::Socket::errstr = $@ = "Bad service '$origport'";
return;
}
$proto = _get_proto_number($serv[3]) if @serv && !$proto;
}
return ($addr || undef,
$port || undef,
$proto || undef
);
}
sub _error {
my $sock = shift;
my $err = shift;
{
local($!);
my $title = ref($sock).": ";
$IO::Socket::errstr = $@ = join("", $_[0] =~ /^$title/ ? "" : $title, @_);
$sock->close()
if(defined fileno($sock));
}
$! = $err;
return undef;
}
sub _get_addr {
my($sock,$addr_str, $multi) = @_;
my @addr;
if ($multi && $addr_str !~ /^\d+(?:\.\d+){3}$/) {
(undef, undef, undef, undef, @addr) = gethostbyname($addr_str);
} else {
my $h = inet_aton($addr_str);
push(@addr, $h) if defined $h;
}
@addr;
}
sub configure {
my($sock,$arg) = @_;
my($lport,$rport,$laddr,$raddr,$proto,$type);
$arg->{LocalAddr} = $arg->{LocalHost}
if exists $arg->{LocalHost} && !exists $arg->{LocalAddr};
($laddr,$lport,$proto) = _sock_info($arg->{LocalAddr},
$arg->{LocalPort},
$arg->{Proto})
or return _error($sock, $!, $@);
$laddr = defined $laddr ? inet_aton($laddr)
: INADDR_ANY;
return _error($sock, $EINVAL, "Bad hostname '",$arg->{LocalAddr},"'")
unless(defined $laddr);
$arg->{PeerAddr} = $arg->{PeerHost}
if exists $arg->{PeerHost} && !exists $arg->{PeerAddr};
unless(exists $arg->{Listen}) {
($raddr,$rport,$proto) = _sock_info($arg->{PeerAddr},
$arg->{PeerPort},
$proto)
or return _error($sock, $!, $@);
}
$proto ||= _get_proto_number('tcp');
$type = $arg->{Type} || $socket_type{lc _get_proto_name($proto)};
my @raddr = ();
if(defined $raddr) {
@raddr = $sock->_get_addr($raddr, $arg->{MultiHomed});
return _error($sock, $EINVAL, "Bad hostname '",$arg->{PeerAddr},"'")
unless @raddr;
}
while(1) {
$sock->socket(AF_INET, $type, $proto) or
return _error($sock, $!, "$!");
if (defined $arg->{Blocking}) {
defined $sock->blocking($arg->{Blocking})
or return _error($sock, $!, "$!");
}
if ($arg->{Reuse} || $arg->{ReuseAddr}) {
$sock->sockopt(SO_REUSEADDR,1) or
return _error($sock, $!, "$!");
}
if ($arg->{ReusePort}) {
$sock->sockopt(SO_REUSEPORT,1) or
return _error($sock, $!, "$!");
}
if ($arg->{Broadcast}) {
$sock->sockopt(SO_BROADCAST,1) or
return _error($sock, $!, "$!");
}
if($lport || ($laddr ne INADDR_ANY) || exists $arg->{Listen}) {
$sock->bind($lport || 0, $laddr) or
return _error($sock, $!, "$!");
}
if(exists $arg->{Listen}) {
$sock->listen($arg->{Listen} || 5) or
return _error($sock, $!, "$!");
last;
}
# don't try to connect unless we're given a PeerAddr
last unless exists($arg->{PeerAddr});
$raddr = shift @raddr;
return _error($sock, $EINVAL, 'Cannot determine remote port')
unless($rport || $type == SOCK_DGRAM || $type == SOCK_RAW);
last
unless($type == SOCK_STREAM || defined $raddr);
return _error($sock, $EINVAL, "Bad hostname '",$arg->{PeerAddr},"'")
unless defined $raddr;
# my $timeout = ${*$sock}{'io_socket_timeout'};
# my $before = time() if $timeout;
undef $@;
if ($sock->connect(pack_sockaddr_in($rport, $raddr))) {
# ${*$sock}{'io_socket_timeout'} = $timeout;
return $sock;
}
return _error($sock, $!, $@ || "Timeout")
unless @raddr;
# if ($timeout) {
# my $new_timeout = $timeout - (time() - $before);
# return _error($sock,
# (exists(&Errno::ETIMEDOUT) ? Errno::ETIMEDOUT() : $EINVAL),
# "Timeout") if $new_timeout <= 0;
# ${*$sock}{'io_socket_timeout'} = $new_timeout;
# }
}
$sock;
}
sub connect {
@_ == 2 || @_ == 3 or
croak 'usage: $sock->connect(NAME) or $sock->connect(PORT, ADDR)';
my $sock = shift;
return $sock->SUPER::connect(@_ == 1 ? shift : pack_sockaddr_in(@_));
}
sub bind {
@_ == 2 || @_ == 3 or
croak 'usage: $sock->bind(NAME) or $sock->bind(PORT, ADDR)';
my $sock = shift;
return $sock->SUPER::bind(@_ == 1 ? shift : pack_sockaddr_in(@_))
}
sub sockaddr {
@_ == 1 or croak 'usage: $sock->sockaddr()';
my($sock) = @_;
my $name = $sock->sockname;
$name ? (sockaddr_in($name))[1] : undef;
}
sub sockport {
@_ == 1 or croak 'usage: $sock->sockport()';
my($sock) = @_;
my $name = $sock->sockname;
$name ? (sockaddr_in($name))[0] : undef;
}
sub sockhost {
@_ == 1 or croak 'usage: $sock->sockhost()';
my($sock) = @_;
my $addr = $sock->sockaddr;
$addr ? inet_ntoa($addr) : undef;
}
sub peeraddr {
@_ == 1 or croak 'usage: $sock->peeraddr()';
my($sock) = @_;
my $name = $sock->peername;
$name ? (sockaddr_in($name))[1] : undef;
}
sub peerport {
@_ == 1 or croak 'usage: $sock->peerport()';
my($sock) = @_;
my $name = $sock->peername;
$name ? (sockaddr_in($name))[0] : undef;
}
sub peerhost {
@_ == 1 or croak 'usage: $sock->peerhost()';
my($sock) = @_;
my $addr = $sock->peeraddr;
$addr ? inet_ntoa($addr) : undef;
}
1;
__END__
@@ -0,0 +1,715 @@
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
# (C) Paul Evans, 2010-2023 -- leonerd@leonerd.org.uk
package IO::Socket::IP 0.42;
use v5.14;
use warnings;
use base qw( IO::Socket );
use Carp;
use Socket 1.97 qw(
getaddrinfo getnameinfo
sockaddr_family
AF_INET
AI_PASSIVE
IPPROTO_TCP IPPROTO_UDP
IPPROTO_IPV6 IPV6_V6ONLY
NI_DGRAM NI_NUMERICHOST NI_NUMERICSERV NIx_NOHOST NIx_NOSERV
SO_REUSEADDR SO_REUSEPORT SO_BROADCAST SO_ERROR
SOCK_DGRAM SOCK_STREAM
SOL_SOCKET
);
my $AF_INET6 = eval { Socket::AF_INET6() }; # may not be defined
my $AI_ADDRCONFIG = eval { Socket::AI_ADDRCONFIG() } || 0;
my $AI_NUMERICHOST = eval { Socket::AI_NUMERICHOST() } || 0;
use POSIX qw( dup2 );
use Errno qw( EINVAL EINPROGRESS EISCONN ENOTCONN ETIMEDOUT EWOULDBLOCK EOPNOTSUPP );
use constant HAVE_MSWIN32 => ( $^O eq "MSWin32" );
# At least one OS (Android) is known not to have getprotobyname()
use constant HAVE_GETPROTOBYNAME => defined eval { getprotobyname( "tcp" ) };
my $IPv6_re = do {
# translation of RFC 3986 3.2.2 ABNF to re
my $IPv4address = do {
my $dec_octet = q<(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])>;
qq<$dec_octet(?: \\. $dec_octet){3}>;
};
my $IPv6address = do {
my $h16 = qq<[0-9A-Fa-f]{1,4}>;
my $ls32 = qq<(?: $h16 : $h16 | $IPv4address)>;
qq<(?:
(?: $h16 : ){6} $ls32
| :: (?: $h16 : ){5} $ls32
| (?: $h16 )? :: (?: $h16 : ){4} $ls32
| (?: (?: $h16 : ){0,1} $h16 )? :: (?: $h16 : ){3} $ls32
| (?: (?: $h16 : ){0,2} $h16 )? :: (?: $h16 : ){2} $ls32
| (?: (?: $h16 : ){0,3} $h16 )? :: $h16 : $ls32
| (?: (?: $h16 : ){0,4} $h16 )? :: $ls32
| (?: (?: $h16 : ){0,5} $h16 )? :: $h16
| (?: (?: $h16 : ){0,6} $h16 )? ::
)>
};
qr<$IPv6address>xo;
};
sub import
{
my $pkg = shift;
my @symbols;
foreach ( @_ ) {
if( $_ eq "-register" ) {
IO::Socket::IP::_ForINET->register_domain( AF_INET );
IO::Socket::IP::_ForINET6->register_domain( $AF_INET6 ) if defined $AF_INET6;
}
else {
push @symbols, $_;
}
}
@_ = ( $pkg, @symbols );
goto &IO::Socket::import;
}
# Convenient capability test function
{
my $can_disable_v6only;
sub CAN_DISABLE_V6ONLY
{
return $can_disable_v6only if defined $can_disable_v6only;
socket my $testsock, Socket::PF_INET6(), SOCK_STREAM, 0 or
die "Cannot socket(PF_INET6) - $!";
if( setsockopt $testsock, IPPROTO_IPV6, IPV6_V6ONLY, 0 ) {
if( $^O eq "dragonfly") {
# dragonflybsd 6.4 lies about successfully turning this off
if( getsockopt $testsock, IPPROTO_IPV6, IPV6_V6ONLY ) {
return $can_disable_v6only = 0;
}
}
return $can_disable_v6only = 1;
}
elsif( $! == EINVAL || $! == EOPNOTSUPP ) {
return $can_disable_v6only = 0;
}
else {
die "Cannot setsockopt() - $!";
}
}
}
sub new
{
my $class = shift;
my %arg = (@_ == 1) ? (PeerHost => $_[0]) : @_;
return $class->SUPER::new(%arg);
}
# IO::Socket may call this one; neaten up the arguments from IO::Socket::INET
# before calling our real _configure method
sub configure
{
my $self = shift;
my ( $arg ) = @_;
$arg->{PeerHost} = delete $arg->{PeerAddr}
if exists $arg->{PeerAddr} && !exists $arg->{PeerHost};
$arg->{PeerService} = delete $arg->{PeerPort}
if exists $arg->{PeerPort} && !exists $arg->{PeerService};
$arg->{LocalHost} = delete $arg->{LocalAddr}
if exists $arg->{LocalAddr} && !exists $arg->{LocalHost};
$arg->{LocalService} = delete $arg->{LocalPort}
if exists $arg->{LocalPort} && !exists $arg->{LocalService};
for my $type (qw(Peer Local)) {
my $host = $type . 'Host';
my $service = $type . 'Service';
if( defined $arg->{$host} ) {
( $arg->{$host}, my $s ) = $self->split_addr( $arg->{$host} );
# IO::Socket::INET compat - *Host parsed port always takes precedence
$arg->{$service} = $s if defined $s;
}
}
$self->_io_socket_ip__configure( $arg );
}
# Avoid simply calling it _configure, as some subclasses of IO::Socket::INET on CPAN already take that
sub _io_socket_ip__configure
{
my $self = shift;
my ( $arg ) = @_;
my %hints;
my $localflags;
my $peerflags;
my @localinfos;
my @peerinfos;
my $listenqueue = $arg->{Listen};
if( defined $listenqueue and
( defined $arg->{PeerHost} || defined $arg->{PeerService} || defined $arg->{PeerAddrInfo} ) ) {
croak "Cannot Listen with a peer address";
}
if( defined $arg->{GetAddrInfoFlags} ) {
$hints{flags} = $arg->{GetAddrInfoFlags};
$localflags = $arg->{GetAddrInfoFlags};
$peerflags = $arg->{GetAddrInfoFlags};
}
else {
if (defined $arg->{LocalHost} and $arg->{LocalHost} =~ /^\d+\.\d+\.\d+\.\d+$/) {
$localflags = $AI_NUMERICHOST;
} else {
$localflags = $AI_ADDRCONFIG;
}
if (defined $arg->{PeerHost} and $arg->{PeerHost} =~ /^\d+\.\d+\.\d+\.\d+$/) {
$peerflags = $AI_NUMERICHOST;
} elsif (defined $arg->{PeerHost} and $arg->{PeerHost} ne 'localhost') {
$peerflags = $AI_ADDRCONFIG;
}
}
if( defined( my $family = $arg->{Family} ) ) {
$hints{family} = $family;
}
if( defined( my $type = $arg->{Type} ) ) {
$hints{socktype} = $type;
}
if( defined( my $proto = $arg->{Proto} ) ) {
unless( $proto =~ m/^\d+$/ ) {
my $protonum = HAVE_GETPROTOBYNAME
? getprotobyname( $proto )
: eval { Socket->${\"IPPROTO_\U$proto"}() };
defined $protonum or croak "Unrecognised protocol $proto";
$proto = $protonum;
}
$hints{protocol} = $proto;
}
# To maintain compatibility with IO::Socket::INET, imply a default of
# SOCK_STREAM + IPPROTO_TCP if neither hint is given
if( !defined $hints{socktype} and !defined $hints{protocol} ) {
$hints{socktype} = SOCK_STREAM;
$hints{protocol} = IPPROTO_TCP;
}
# Some OSes (NetBSD) don't seem to like just a protocol hint without a
# socktype hint as well. We'll set a couple of common ones
if( !defined $hints{socktype} and defined $hints{protocol} ) {
$hints{socktype} = SOCK_STREAM if $hints{protocol} == IPPROTO_TCP;
$hints{socktype} = SOCK_DGRAM if $hints{protocol} == IPPROTO_UDP;
}
if( my $info = $arg->{LocalAddrInfo} ) {
ref $info eq "ARRAY" or croak "Expected 'LocalAddrInfo' to be an ARRAY ref";
@localinfos = @$info;
}
elsif( defined $arg->{LocalHost} or
defined $arg->{LocalService} or
HAVE_MSWIN32 and $arg->{Listen} ) {
# Either may be undef
my $host = $arg->{LocalHost};
my $service = $arg->{LocalService};
unless ( defined $host or defined $service ) {
$service = 0;
}
local $1; # Placate a taint-related bug; [perl #67962]
defined $service and $service =~ s/\((\d+)\)$// and
my $fallback_port = $1;
my %localhints = %hints;
$localhints{flags} = $localflags;
$localhints{flags} |= AI_PASSIVE;
( my $err, @localinfos ) = getaddrinfo( $host, $service, \%localhints );
if( $err and defined $fallback_port ) {
( $err, @localinfos ) = getaddrinfo( $host, $fallback_port, \%localhints );
}
if( $err ) {
$IO::Socket::errstr = $@ = "$err";
$! = EINVAL;
return;
}
}
if( my $info = $arg->{PeerAddrInfo} ) {
ref $info eq "ARRAY" or croak "Expected 'PeerAddrInfo' to be an ARRAY ref";
@peerinfos = @$info;
}
elsif( defined $arg->{PeerHost} or defined $arg->{PeerService} ) {
defined( my $host = $arg->{PeerHost} ) or
croak "Expected 'PeerHost'";
defined( my $service = $arg->{PeerService} ) or
croak "Expected 'PeerService'";
local $1; # Placate a taint-related bug; [perl #67962]
defined $service and $service =~ s/\((\d+)\)$// and
my $fallback_port = $1;
my %peerhints = %hints;
$peerhints{flags} = $peerflags;
( my $err, @peerinfos ) = getaddrinfo( $host, $service, \%peerhints );
if( $err and defined $fallback_port ) {
( $err, @peerinfos ) = getaddrinfo( $host, $fallback_port, \%peerhints );
}
if( $err ) {
$IO::Socket::errstr = $@ = "$err";
$! = EINVAL;
return;
}
}
my $INT_1 = pack "i", 1;
my @sockopts_enabled;
push @sockopts_enabled, [ SOL_SOCKET, SO_REUSEADDR, $INT_1 ] if $arg->{ReuseAddr};
push @sockopts_enabled, [ SOL_SOCKET, SO_REUSEPORT, $INT_1 ] if $arg->{ReusePort};
push @sockopts_enabled, [ SOL_SOCKET, SO_BROADCAST, $INT_1 ] if $arg->{Broadcast};
if( my $sockopts = $arg->{Sockopts} ) {
ref $sockopts eq "ARRAY" or croak "Expected 'Sockopts' to be an ARRAY ref";
foreach ( @$sockopts ) {
ref $_ eq "ARRAY" or croak "Bad Sockopts item - expected ARRAYref";
@$_ >= 2 and @$_ <= 3 or
croak "Bad Sockopts item - expected 2 or 3 elements";
my ( $level, $optname, $value ) = @$_;
# TODO: consider more sanity checking on argument values
defined $value or $value = $INT_1;
push @sockopts_enabled, [ $level, $optname, $value ];
}
}
my $blocking = $arg->{Blocking};
defined $blocking or $blocking = 1;
my $v6only = $arg->{V6Only};
# IO::Socket::INET defines this key. IO::Socket::IP always implements the
# behaviour it requests, so we can ignore it, unless the caller is for some
# reason asking to disable it.
if( defined $arg->{MultiHomed} and !$arg->{MultiHomed} ) {
croak "Cannot disable the MultiHomed parameter";
}
my @infos;
foreach my $local ( @localinfos ? @localinfos : {} ) {
foreach my $peer ( @peerinfos ? @peerinfos : {} ) {
next if defined $local->{family} and defined $peer->{family} and
$local->{family} != $peer->{family};
next if defined $local->{socktype} and defined $peer->{socktype} and
$local->{socktype} != $peer->{socktype};
next if defined $local->{protocol} and defined $peer->{protocol} and
$local->{protocol} != $peer->{protocol};
my $family = $local->{family} || $peer->{family} or next;
my $socktype = $local->{socktype} || $peer->{socktype} or next;
my $protocol = $local->{protocol} || $peer->{protocol} || 0;
push @infos, {
family => $family,
socktype => $socktype,
protocol => $protocol,
localaddr => $local->{addr},
peeraddr => $peer->{addr},
};
}
}
if( !@infos ) {
# If there was a Family hint then create a plain unbound, unconnected socket
if( defined $hints{family} ) {
@infos = ( {
family => $hints{family},
socktype => $hints{socktype},
protocol => $hints{protocol},
} );
}
# If there wasn't, use getaddrinfo()'s AI_ADDRCONFIG side-effect to guess a
# suitable family first.
else {
$hints{flags} |= $AI_ADDRCONFIG;
( my $err, @infos ) = getaddrinfo( "", "0", \%hints );
if( $err ) {
$IO::Socket::errstr = $@ = "$err";
$! = EINVAL;
return;
}
# We'll take all the @infos anyway, because some OSes (HPUX) are known to
# ignore the AI_ADDRCONFIG hint and return AF_INET6 even if they don't
# support them
}
}
# In the nonblocking case, caller will be calling ->setup multiple times.
# Store configuration in the object for the ->setup method
# Yes, these are messy. Sorry, I can't help that...
${*$self}{io_socket_ip_infos} = \@infos;
${*$self}{io_socket_ip_idx} = -1;
${*$self}{io_socket_ip_sockopts} = \@sockopts_enabled;
${*$self}{io_socket_ip_v6only} = $v6only;
${*$self}{io_socket_ip_listenqueue} = $listenqueue;
${*$self}{io_socket_ip_blocking} = $blocking;
${*$self}{io_socket_ip_errors} = [ undef, undef, undef ];
# ->setup is allowed to return false in nonblocking mode
$self->setup or !$blocking or return undef;
return $self;
}
sub setup
{
my $self = shift;
while(1) {
${*$self}{io_socket_ip_idx}++;
last if ${*$self}{io_socket_ip_idx} >= @{ ${*$self}{io_socket_ip_infos} };
my $info = ${*$self}{io_socket_ip_infos}->[${*$self}{io_socket_ip_idx}];
$self->socket( @{$info}{qw( family socktype protocol )} ) or
( ${*$self}{io_socket_ip_errors}[2] = $!, next );
$self->blocking( 0 ) unless ${*$self}{io_socket_ip_blocking};
foreach my $sockopt ( @{ ${*$self}{io_socket_ip_sockopts} } ) {
my ( $level, $optname, $value ) = @$sockopt;
$self->setsockopt( $level, $optname, $value ) or
( $IO::Socket::errstr = $@ = "$!", return undef );
}
if( defined ${*$self}{io_socket_ip_v6only} and defined $AF_INET6 and $info->{family} == $AF_INET6 ) {
my $v6only = ${*$self}{io_socket_ip_v6only};
$self->setsockopt( IPPROTO_IPV6, IPV6_V6ONLY, pack "i", $v6only ) or
( $IO::Socket::errstr = $@ = "$!", return undef );
}
if( defined( my $addr = $info->{localaddr} ) ) {
$self->bind( $addr ) or
( ${*$self}{io_socket_ip_errors}[1] = $!, next );
}
if( defined( my $listenqueue = ${*$self}{io_socket_ip_listenqueue} ) ) {
$self->listen( $listenqueue ) or
( $IO::Socket::errstr = $@ = "$!", return undef );
}
if( defined( my $addr = $info->{peeraddr} ) ) {
if( $self->connect( $addr ) ) {
$! = 0;
return 1;
}
if( $! == EINPROGRESS or $! == EWOULDBLOCK ) {
${*$self}{io_socket_ip_connect_in_progress} = 1;
return 0;
}
# If connect failed but we have no system error there must be an error
# at the application layer, like a bad certificate with
# IO::Socket::SSL.
# In this case don't continue IP based multi-homing because the problem
# cannot be solved at the IP layer.
return 0 if ! $!;
${*$self}{io_socket_ip_errors}[0] = $!;
next;
}
return 1;
}
# Pick the most appropriate error, stringified
$! = ( grep defined, @{ ${*$self}{io_socket_ip_errors}} )[0];
$IO::Socket::errstr = $@ = "$!";
return undef;
}
sub connect :method
{
my $self = shift;
# It seems that IO::Socket hides EINPROGRESS errors, making them look like
# a success. This is annoying here.
# Instead of putting up with its frankly-irritating intentional breakage of
# useful APIs I'm just going to end-run around it and call core's connect()
# directly
if( @_ ) {
my ( $addr ) = @_;
# Annoyingly IO::Socket's connect() is where the timeout logic is
# implemented, so we'll have to reinvent it here
my $timeout = ${*$self}{'io_socket_timeout'};
return connect( $self, $addr ) unless defined $timeout;
my $was_blocking = $self->blocking( 0 );
my $err = defined connect( $self, $addr ) ? 0 : $!+0;
if( !$err ) {
# All happy
$self->blocking( $was_blocking );
return 1;
}
elsif( not( $err == EINPROGRESS or $err == EWOULDBLOCK ) ) {
# Failed for some other reason
$self->blocking( $was_blocking );
return undef;
}
elsif( !$was_blocking ) {
# We shouldn't block anyway
return undef;
}
my $vec = ''; vec( $vec, $self->fileno, 1 ) = 1;
if( !select( undef, $vec, $vec, $timeout ) ) {
$self->blocking( $was_blocking );
$! = ETIMEDOUT;
return undef;
}
# Hoist the error by connect()ing a second time
$err = $self->getsockopt( SOL_SOCKET, SO_ERROR );
$err = 0 if $err == EISCONN; # Some OSes give EISCONN
$self->blocking( $was_blocking );
$! = $err, return undef if $err;
return 1;
}
return 1 if !${*$self}{io_socket_ip_connect_in_progress};
# See if a connect attempt has just failed with an error
if( my $errno = $self->getsockopt( SOL_SOCKET, SO_ERROR ) ) {
delete ${*$self}{io_socket_ip_connect_in_progress};
${*$self}{io_socket_ip_errors}[0] = $! = $errno;
return $self->setup;
}
# No error, so either connect is still in progress, or has completed
# successfully. We can tell by trying to connect() again; either it will
# succeed or we'll get EISCONN (connected successfully), or EALREADY
# (still in progress). This even works on MSWin32.
my $addr = ${*$self}{io_socket_ip_infos}[${*$self}{io_socket_ip_idx}]{peeraddr};
if( connect( $self, $addr ) or $! == EISCONN ) {
delete ${*$self}{io_socket_ip_connect_in_progress};
$! = 0;
return 1;
}
else {
$! = EINPROGRESS;
return 0;
}
}
sub connected
{
my $self = shift;
return defined $self->fileno &&
!${*$self}{io_socket_ip_connect_in_progress} &&
defined getpeername( $self ); # ->peername caches, we need to detect disconnection
}
sub _get_host_service
{
my $self = shift;
my ( $addr, $flags, $xflags ) = @_;
defined $addr or
$! = ENOTCONN, return;
$flags |= NI_DGRAM if $self->socktype == SOCK_DGRAM;
my ( $err, $host, $service ) = getnameinfo( $addr, $flags, $xflags || 0 );
croak "getnameinfo - $err" if $err;
return ( $host, $service );
}
sub _unpack_sockaddr
{
my ( $addr ) = @_;
my $family = sockaddr_family $addr;
if( $family == AF_INET ) {
return ( Socket::unpack_sockaddr_in( $addr ) )[1];
}
elsif( defined $AF_INET6 and $family == $AF_INET6 ) {
return ( Socket::unpack_sockaddr_in6( $addr ) )[1];
}
else {
croak "Unrecognised address family $family";
}
}
sub sockhost_service
{
my $self = shift;
my ( $numeric ) = @_;
$self->_get_host_service( $self->sockname, $numeric ? NI_NUMERICHOST|NI_NUMERICSERV : 0 );
}
sub sockhost { my $self = shift; scalar +( $self->_get_host_service( $self->sockname, NI_NUMERICHOST, NIx_NOSERV ) )[0] }
sub sockport { my $self = shift; scalar +( $self->_get_host_service( $self->sockname, NI_NUMERICSERV, NIx_NOHOST ) )[1] }
sub sockhostname { my $self = shift; scalar +( $self->_get_host_service( $self->sockname, 0, NIx_NOSERV ) )[0] }
sub sockservice { my $self = shift; scalar +( $self->_get_host_service( $self->sockname, 0, NIx_NOHOST ) )[1] }
sub sockaddr { my $self = shift; _unpack_sockaddr $self->sockname }
sub peerhost_service
{
my $self = shift;
my ( $numeric ) = @_;
$self->_get_host_service( $self->peername, $numeric ? NI_NUMERICHOST|NI_NUMERICSERV : 0 );
}
sub peerhost { my $self = shift; scalar +( $self->_get_host_service( $self->peername, NI_NUMERICHOST, NIx_NOSERV ) )[0] }
sub peerport { my $self = shift; scalar +( $self->_get_host_service( $self->peername, NI_NUMERICSERV, NIx_NOHOST ) )[1] }
sub peerhostname { my $self = shift; scalar +( $self->_get_host_service( $self->peername, 0, NIx_NOSERV ) )[0] }
sub peerservice { my $self = shift; scalar +( $self->_get_host_service( $self->peername, 0, NIx_NOHOST ) )[1] }
sub peeraddr { my $self = shift; _unpack_sockaddr $self->peername }
# This unbelievably dodgy hack works around the bug that IO::Socket doesn't do
# it
# https://rt.cpan.org/Ticket/Display.html?id=61577
sub accept
{
my $self = shift;
my ( $new, $peer ) = $self->SUPER::accept( @_ ) or return;
${*$new}{$_} = ${*$self}{$_} for qw( io_socket_domain io_socket_type io_socket_proto );
return wantarray ? ( $new, $peer )
: $new;
}
# This second unbelievably dodgy hack guarantees that $self->fileno doesn't
# change, which is useful during nonblocking connect
sub socket :method
{
my $self = shift;
return $self->SUPER::socket(@_) if not defined $self->fileno;
# I hate core prototypes sometimes...
socket( my $tmph, $_[0], $_[1], $_[2] ) or return undef;
dup2( $tmph->fileno, $self->fileno ) or die "Unable to dup2 $tmph onto $self - $!";
}
# Versions of IO::Socket before 1.35 may leave socktype undef if from, say, an
# ->fdopen call. In this case we'll apply a fix
BEGIN {
if( eval($IO::Socket::VERSION) < 1.35 ) {
*socktype = sub {
my $self = shift;
my $type = $self->SUPER::socktype;
if( !defined $type ) {
$type = $self->sockopt( Socket::SO_TYPE() );
}
return $type;
};
}
}
sub as_inet
{
my $self = shift;
croak "Cannot downgrade a non-PF_INET socket to IO::Socket::INET" unless $self->sockdomain == AF_INET;
return IO::Socket::INET->new_from_fd( $self->fileno, "r+" );
}
sub split_addr
{
shift;
my ( $addr ) = @_;
local ( $1, $2 ); # Placate a taint-related bug; [perl #67962]
if( $addr =~ m/\A\[($IPv6_re)\](?::([^\s:]*))?\z/ or
$addr =~ m/\A([^\s:]*):([^\s:]*)\z/ ) {
return ( $1, $2 ) if defined $2 and length $2;
return ( $1, undef );
}
return ( $addr, undef );
}
sub join_addr
{
shift;
my ( $host, $port ) = @_;
$host = "[$host]" if $host =~ m/:/;
return join ":", $host, $port if defined $port;
return $host;
}
# Since IO::Socket->new( Domain => ... ) will delete the Domain parameter
# before calling ->configure, we need to keep track of which it was
package # hide from indexer
IO::Socket::IP::_ForINET;
use base qw( IO::Socket::IP );
sub configure
{
# This is evil
my $self = shift;
my ( $arg ) = @_;
bless $self, "IO::Socket::IP";
$self->configure( { %$arg, Family => Socket::AF_INET() } );
}
package # hide from indexer
IO::Socket::IP::_ForINET6;
use base qw( IO::Socket::IP );
sub configure
{
# This is evil
my $self = shift;
my ( $arg ) = @_;
bless $self, "IO::Socket::IP";
$self->configure( { %$arg, Family => Socket::AF_INET6() } );
}
0x55AA;
@@ -0,0 +1,70 @@
# IO::Socket::UNIX.pm
#
# Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
package IO::Socket::UNIX;
use strict;
use IO::Socket;
use Carp;
our @ISA = qw(IO::Socket);
our $VERSION = "1.55";
IO::Socket::UNIX->register_domain( AF_UNIX );
sub new {
my $class = shift;
unshift(@_, "Peer") if @_ == 1;
return $class->SUPER::new(@_);
}
sub configure {
my($sock,$arg) = @_;
my($bport,$cport);
my $type = $arg->{Type} || SOCK_STREAM;
$sock->socket(AF_UNIX, $type, 0) or
return undef;
if(exists $arg->{Blocking}) {
$sock->blocking($arg->{Blocking}) or
return undef;
}
if(exists $arg->{Local}) {
my $addr = sockaddr_un($arg->{Local});
$sock->bind($addr) or
return undef;
}
if(exists $arg->{Listen} && $type != SOCK_DGRAM) {
$sock->listen($arg->{Listen} || 5) or
return undef;
}
elsif(exists $arg->{Peer}) {
my $addr = sockaddr_un($arg->{Peer});
$sock->connect($addr) or
return undef;
}
$sock;
}
sub hostpath {
@_ == 1 or croak 'usage: $sock->hostpath()';
my $n = $_[0]->sockname || return undef;
(sockaddr_un($n))[0];
}
sub peerpath {
@_ == 1 or croak 'usage: $sock->peerpath()';
my $n = $_[0]->peername || return undef;
(sockaddr_un($n))[0];
}
1; # Keep require happy
__END__