X-Git-Url: https://perl5.git.perl.org/perl5.git/blobdiff_plain/97b11a4791ec0e8fc4d06eacad1050c1ccfa6a74..2a59724c16c204887061581e901136a397ef2329:/ext/Socket/Socket.pm diff --git a/ext/Socket/Socket.pm b/ext/Socket/Socket.pm index f312c3f..a34fa77 100644 --- a/ext/Socket/Socket.pm +++ b/ext/Socket/Socket.pm @@ -1,11 +1,13 @@ package Socket; +use strict; + our($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); -$VERSION = "1.76_01"; +$VERSION = "1.92"; =head1 NAME -Socket, sockaddr_in, sockaddr_un, inet_aton, inet_ntoa - load the C socket.h defines and structure manipulators +Socket, sockaddr_in, sockaddr_un, inet_aton, inet_ntoa, inet_pton, inet_ntop - load the C socket.h defines and structure manipulators =head1 SYNOPSIS @@ -112,6 +114,16 @@ Note - does not return a number. Returns the 4-byte 'invalid' ip address. Normally equivalent to inet_aton('255.255.255.255'). +=item IN6ADDR_ANY + +Returns the 16-byte wildcard IPv6 address. Normally equivalent +to inet_pton(AF_INET6, "::") + +=item IN6ADDR_LOOPBACK + +Returns the 16-byte loopback IPv6 address. Normally equivalent +to inet_pton(AF_INET6, "::1") + =item sockaddr_family SOCKADDR Takes a sockaddr structure (as returned by pack_sockaddr_in(), @@ -147,6 +159,29 @@ representing the IP address (you can use inet_ntoa() to convert the address to the four-dotted numeric format). Will croak if the structure does not have AF_INET in the right place. +=item sockaddr_in6 PORT, IP6_ADDRESS, [ SCOPE_ID, [ FLOWINFO ] ] + +=item sockaddr_in6 SOCKADDR_IN6 + +In list context, unpacks its SOCKADDR_IN6 argument according to +unpack_sockaddr_in6(). In scalar context, packs its arguments according to +pack_sockaddr_in6(). + +=item pack_sockaddr_in6 PORT, IP6_ADDRESS, [ SCOPE_ID, [ FLOWINFO ] ] + +Takes two to four arguments, a port number, an opaque string (as returned by +inet_pton()), optionally a scope ID number, and optionally a flow label +number. Returns the sockaddr_in6 structure with those arguments packed in +with AF_INET6 filled in. IPv6 equivalent of pack_sockaddr_in(). + +=item unpack_sockaddr_in6 SOCKADDR_IN6 + +Takes a sockaddr_in6 structure (as returned by pack_sockaddr_in6()) and +returns an array of four elements: the port number, an opaque string +representing the IPv6 address, the scope ID, and the flow label. (You can +use inet_ntop() to convert the address to the usual string format). Will +croak if the structure does not have AF_INET6 in the right place. + =item sockaddr_un PATHNAME =item sockaddr_un SOCKADDR_UN @@ -171,6 +206,102 @@ Takes a sockaddr_un structure (as returned by pack_sockaddr_un()) and returns the pathname. Will croak if the structure does not have AF_UNIX in the right place. +=item inet_pton ADDRESS_FAMILY, HOSTNAME + +Takes an address family, either AF_INET or AF_INET6, and a string giving +the name of a host, and translates that to an opaque string +(if programming in C, struct in_addr or struct in6_addr depending on the +address family passed in). The host string may be a string hostname, such +as 'www.perl.org', or an IP address. If using an IP address, the type of +IP address must be consistant with the address family passed into the function. + +This function is not exported by default. + +=item inet_ntop ADDRESS_FAMILY, IP_ADDRESS + +Takes an address family, either AF_INET or AF_INET6, and a string +(an opaque string as returned by inet_aton() or inet_pton()) and +translates it to an IPv4 or IPv6 address string. + +This function is not exported by default. + +=item getaddrinfo HOST, SERVICE, [ HINTS ] + +Given at least one of a hostname and a service name, returns a list of address +structures to listen on or connect to. HOST and SERVICE should be plain +strings (or a numerical port number for SERVICE). If present, HINTS should be +a reference to a HASH, where the following keys are recognised: + +=over 8 + +=item flags => INT + +A bitfield containing C constants + +=item family => INT + +Restrict to only generating addresses in this address family + +=item socktype => INT + +Restrict to only generating addresses of this socket type + +=item protocol => INT + +Restrict to only generating addresses for this protocol + +=back + +The return value will be a list; the first value being an error indication, +followed by a list of address structures (if no error occured). + + my ( $err, @results ) = getaddrinfo( ... ); + +The error value will be a dualvar; comparable to the C error constants, +or printable as a human-readable error message string. Each value in the +results list will be a HASH reference containing the following fields: + +=over 8 + +=item family => INT + +The address family (e.g. AF_INET) + +=item socktype => INT + +The socket type (e.g. SOCK_STREAM) + +=item protocol => INT + +The protocol (e.g. IPPROTO_TCP) + +=item addr => STRING + +The address in a packed string (such as would be returned by pack_sockaddr_in) + +=item canonname => STRING + +The canonical name for the host if the C flag was provided, or +C otherwise. + +=back + +=item getnameinfo ADDR, FLAGS + +Given a packed socket address (such as from C, C, or +returned by C in a C field), returns the hostname and +symbolic service name it represents. FLAGS may be a bitmask of C +constants, or defaults to 0 if unspecified. + +The return value will be a list; the first value being an error condition, +followed by the hostname and service name. + + my ( $err, $host, $service ) = getnameinfo( ... ); + +The error value will be a dualvar; comparable to the C error constants, +or printable as a human-readable error message string. The host and service +names will be plain strings. + =back =cut @@ -179,15 +310,17 @@ use Carp; use warnings::register; require Exporter; -use XSLoader (); +require XSLoader; @ISA = qw(Exporter); @EXPORT = qw( inet_aton inet_ntoa sockaddr_family pack_sockaddr_in unpack_sockaddr_in pack_sockaddr_un unpack_sockaddr_un - sockaddr_in sockaddr_un + pack_sockaddr_in6 unpack_sockaddr_in6 + sockaddr_in sockaddr_in6 sockaddr_un INADDR_ANY INADDR_BROADCAST INADDR_LOOPBACK INADDR_NONE + IN6ADDR_ANY IN6ADDR_LOOPBACK AF_802 AF_AAL AF_APPLETALK @@ -223,6 +356,13 @@ use XSLoader (); AF_WAN AF_X25 IOV_MAX + IP_OPTIONS + IP_HDRINCL + IP_TOS + IP_TTL + IP_RECVOPTS + IP_RECVRETOPTS + IP_RETOPTS MSG_BCAST MSG_BTAG MSG_CTLFLAGS @@ -337,12 +477,56 @@ use XSLoader (); @EXPORT_OK = qw(CR LF CRLF $CR $LF $CRLF + inet_pton + inet_ntop + + getaddrinfo + getnameinfo + + AI_CANONNAME + AI_NUMERICHOST + AI_NUMERICSERV + AI_PASSIVE + + EAI_ADDRFAMILY + EAI_AGAIN + EAI_BADFLAGS + EAI_FAIL + EAI_FAMILY + EAI_NODATA + EAI_NONAME + EAI_SERVICE + EAI_SOCKTYPE + + IPPROTO_IP + IPPROTO_IPV6 + IPPROTO_RAW + IPPROTO_ICMP IPPROTO_TCP + IPPROTO_UDP + + NI_DGRAM + NI_NAMEREQD + NI_NUMERICHOST + NI_NUMERICSERV + TCP_KEEPALIVE TCP_MAXRT TCP_MAXSEG TCP_NODELAY - TCP_STDURG); + TCP_STDURG + TCP_CORK + TCP_KEEPIDLE + TCP_KEEPINTVL + TCP_KEEPCNT + TCP_SYNCNT + TCP_LINGER2 + TCP_DEFER_ACCEPT + TCP_WINDOW_CLAMP + TCP_INFO + TCP_QUICKACK + TCP_CONGESTION + TCP_MD5SIG); %EXPORT_TAGS = ( crlf => [qw(CR LF CRLF $CR $LF $CRLF)], @@ -374,6 +558,17 @@ sub sockaddr_in { } } +sub sockaddr_in6 { + if (wantarray) { + croak "usage: (port,in6addr,scope_id,flowinfo) = sockaddr_in6(sin6_sv)" unless @_ == 1; + unpack_sockaddr_in6(@_); + } + else { + croak "usage: sin6_sv = sockaddr_in6(port,in6addr,[scope_id,[flowinfo]])" unless @_ >= 2 and @_ <= 4; + pack_sockaddr_in6(@_); + } +} + sub sockaddr_un { if (wantarray) { croak "usage: (filename) = sockaddr_un(sun_sv)" unless @_ == 1; @@ -384,18 +579,211 @@ sub sockaddr_un { } } -sub AUTOLOAD { - my($constname); - ($constname = $AUTOLOAD) =~ s/.*:://; - croak "&Socket::constant not defined" if $constname eq 'constant'; - my ($error, $val) = constant($constname); - if ($error) { - croak $error; - } - *$AUTOLOAD = sub { $val }; - goto &$AUTOLOAD; +XSLoader::load(); + +my %errstr; + +if( !defined &getaddrinfo ) { + require Scalar::Util; + + *getaddrinfo = \&fake_getaddrinfo; + *getnameinfo = \&fake_getnameinfo; + + # These numbers borrowed from GNU libc's implementation, but since + # they're only used by our emulation, it doesn't matter if the real + # platform's values differ + my %constants = ( + AI_PASSIVE => 1, + AI_CANONNAME => 2, + AI_NUMERICHOST => 4, + # RFC 2553 doesn't define this but Linux does - lets be nice and + # provide it since we can + AI_NUMERICSERV => 1024, + + EAI_BADFLAGS => -1, + EAI_NONAME => -2, + EAI_NODATA => -5, + EAI_FAMILY => -6, + EAI_SERVICE => -8, + + NI_NUMERICHOST => 1, + NI_NUMERICSERV => 2, + NI_NAMEREQD => 8, + NI_DGRAM => 16, + ); + + foreach my $name ( keys %constants ) { + my $value = $constants{$name}; + + no strict 'refs'; + defined &$name or *$name = sub () { $value }; + } + + %errstr = ( + # These strings from RFC 2553 + EAI_BADFLAGS() => "invalid value for ai_flags", + EAI_NONAME() => "nodename nor servname provided, or not known", + EAI_NODATA() => "no address associated with nodename", + EAI_FAMILY() => "ai_family not supported", + EAI_SERVICE() => "servname not supported for ai_socktype", + ); +} + +# The following functions are used if the system does not have a +# getaddrinfo(3) function in libc; and are used to emulate it for the AF_INET +# family + +# Borrowed from Regexp::Common::net +my $REGEXP_IPv4_DECIMAL = qr/25[0-5]|2[0-4][0-9]|1?[0-9][0-9]{1,2}/; +my $REGEXP_IPv4_DOTTEDQUAD = qr/$REGEXP_IPv4_DECIMAL\.$REGEXP_IPv4_DECIMAL\.$REGEXP_IPv4_DECIMAL\.$REGEXP_IPv4_DECIMAL/; + +sub fake_makeerr +{ + my ( $errno ) = @_; + my $errstr = $errno == 0 ? "" : ( $errstr{$errno} || $errno ); + return Scalar::Util::dualvar( $errno, $errstr ); +} + +sub fake_getaddrinfo +{ + my ( $node, $service, $hints ) = @_; + + $node = "" unless defined $node; + + $service = "" unless defined $service; + + my ( $family, $socktype, $protocol, $flags ) = @$hints{qw( family socktype protocol flags )}; + + $family ||= Socket::AF_INET(); # 0 == AF_UNSPEC, which we want too + $family == Socket::AF_INET() or return fake_makeerr( EAI_FAMILY() ); + + $socktype ||= 0; + + $protocol ||= 0; + + $flags ||= 0; + + my $flag_passive = $flags & AI_PASSIVE(); $flags &= ~AI_PASSIVE(); + my $flag_canonname = $flags & AI_CANONNAME(); $flags &= ~AI_CANONNAME(); + my $flag_numerichost = $flags & AI_NUMERICHOST(); $flags &= ~AI_NUMERICHOST(); + my $flag_numericserv = $flags & AI_NUMERICSERV(); $flags &= ~AI_NUMERICSERV(); + + $flags == 0 or return fake_makeerr( EAI_BADFLAGS() ); + + $node eq "" and $service eq "" and return fake_makeerr( EAI_NONAME() ); + + my $canonname; + my @addrs; + if( $node ne "" ) { + return fake_makeerr( EAI_NONAME() ) if( $flag_numerichost and $node !~ m/^$REGEXP_IPv4_DOTTEDQUAD$/ ); + ( $canonname, undef, undef, undef, @addrs ) = gethostbyname( $node ); + defined $canonname or return fake_makeerr( EAI_NONAME() ); + + undef $canonname unless $flag_canonname; + } + else { + $addrs[0] = $flag_passive ? Socket::inet_aton( "0.0.0.0" ) + : Socket::inet_aton( "127.0.0.1" ); + } + + my @ports; # Actually ARRAYrefs of [ socktype, protocol, port ] + my $protname = ""; + if( $protocol ) { + $protname = getprotobynumber( $protocol ); + } + + if( $service ne "" and $service !~ m/^\d+$/ ) { + return fake_makeerr( EAI_NONAME() ) if( $flag_numericserv ); + getservbyname( $service, $protname ) or return fake_makeerr( EAI_SERVICE() ); + } + + foreach my $this_socktype ( Socket::SOCK_STREAM(), Socket::SOCK_DGRAM(), Socket::SOCK_RAW() ) { + next if $socktype and $this_socktype != $socktype; + + my $this_protname = "raw"; + $this_socktype == Socket::SOCK_STREAM() and $this_protname = "tcp"; + $this_socktype == Socket::SOCK_DGRAM() and $this_protname = "udp"; + + next if $protname and $this_protname ne $protname; + + my $port; + if( $service ne "" ) { + if( $service =~ m/^\d+$/ ) { + $port = "$service"; + } + else { + ( undef, undef, $port, $this_protname ) = getservbyname( $service, $this_protname ); + next unless defined $port; + } + } + else { + $port = 0; + } + + push @ports, [ $this_socktype, scalar getprotobyname( $this_protname ) || 0, $port ]; + } + + my @ret; + foreach my $addr ( @addrs ) { + foreach my $portspec ( @ports ) { + my ( $socktype, $protocol, $port ) = @$portspec; + push @ret, { + family => $family, + socktype => $socktype, + protocol => $protocol, + addr => Socket::pack_sockaddr_in( $port, $addr ), + canonname => $canonname, + }; + } + } + + return ( fake_makeerr( 0 ), @ret ); } -XSLoader::load 'Socket', $VERSION; +sub fake_getnameinfo +{ + my ( $addr, $flags ) = @_; + + my ( $port, $inetaddr ); + eval { ( $port, $inetaddr ) = Socket::unpack_sockaddr_in( $addr ) } + or return fake_makeerr( EAI_FAMILY() ); + + my $family = Socket::AF_INET(); + + $flags ||= 0; + + my $flag_numerichost = $flags & NI_NUMERICHOST(); $flags &= ~NI_NUMERICHOST(); + my $flag_numericserv = $flags & NI_NUMERICSERV(); $flags &= ~NI_NUMERICSERV(); + my $flag_namereqd = $flags & NI_NAMEREQD(); $flags &= ~NI_NAMEREQD(); + my $flag_dgram = $flags & NI_DGRAM() ; $flags &= ~NI_DGRAM(); + + $flags == 0 or return fake_makeerr( EAI_BADFLAGS() ); + + my $node; + if( $flag_numerichost ) { + $node = Socket::inet_ntoa( $inetaddr ); + } + else { + $node = gethostbyaddr( $inetaddr, $family ); + if( !defined $node ) { + return fake_makeerr( EAI_NONAME() ) if $flag_namereqd; + $node = Socket::inet_ntoa( $inetaddr ); + } + } + + my $service; + if( $flag_numericserv ) { + $service = "$port"; + } + else { + my $protname = $flag_dgram ? "udp" : ""; + $service = getservbyport( $port, $protname ); + if( !defined $service ) { + $service = "$port"; + } + } + + return ( fake_makeerr( 0 ), $node, $service ); +} 1;