Blob Blame History Raw
From d6a3ed8299ac6d5cac8db2e0f8e438dcfb8964a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
Date: Mon, 18 Sep 2017 17:21:26 +0200
Subject: [PATCH] Ensure that a Host/Family-less constructor still constructs a
 socket
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

An upstream fix ported from 0.25 version to 0.21:

0.25    2014/01/11 17:19:29
        [BUGFIXES]
         * Ensure that a Host/Family-less constructor still constructs a
           socket, by using gai()s AI_ADDRCONFIG hint

CPAN RT#91982

Signed-off-by: Petr Písař <ppisar@redhat.com>
---
 README              |  6 ++++++
 lib/IO/Socket/IP.pm | 22 +++++++++++++++++++++-
 t/19no-addrs.t      |  9 +++++++++
 3 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/README b/README
index 32ccc65..d09fe2b 100644
--- a/README
+++ b/README
@@ -189,6 +189,12 @@ CONSTRUCTORS
     compatibility with `IO::Socket::INET'. Other named arguments that are
     not recognised are ignored.
 
+    If neither `Family' nor any hosts or addresses are passed, nor any
+    `*AddrInfo', then the constructor has no information on which to decide
+    a socket family to create. In this case, it performs a `getaddinfo' call
+    with the `AI_ADDRCONFIG' flag, no host name, and a service name of
+    `"0"', and uses the family of the first returned result.
+
     If the constructor fails, it will set `$@' to an appropriate error
     message; this may be from `$!' or it may be some other string; not every
     failure necessarily has an associated `errno' value.
diff --git a/lib/IO/Socket/IP.pm b/lib/IO/Socket/IP.pm
index cc9c350..208d837 100644
--- a/lib/IO/Socket/IP.pm
+++ b/lib/IO/Socket/IP.pm
@@ -311,6 +311,12 @@ C<SOCK_STREAM> and C<IPPROTO_TCP> respectively will be set, to maintain
 compatibility with C<IO::Socket::INET>. Other named arguments that are not
 recognised are ignored.
 
+If neither C<Family> nor any hosts or addresses are passed, nor any
+C<*AddrInfo>, then the constructor has no information on which to decide a
+socket family to create. In this case, it performs a C<getaddinfo> call with
+the C<AI_ADDRCONFIG> flag, no host name, and a service name of C<"0">, and
+uses the family of the first returned result.
+
 If the constructor fails, it will set C<$@> to an appropriate error message;
 this may be from C<$!> or it may be some other string; not every failure
 necessarily has an associated C<errno> value.
@@ -512,8 +518,22 @@ sub _configure
       }
    }
 
-   if( !@infos and defined $hints{family} ) {
+   if( !@infos ) {
+
       # If there was a Family hint then create a plain unbound, unconnected socket
+      # If there wasn't, use getaddrinfo()'s AI_ADDRCONFIG side-effect to guess a
+      # suitable family first.
+      if( !defined $hints{family} ) {
+         my ( $err, $addrinfo ) = getaddrinfo( "", "0", \%hints );
+         if( $err ) {
+            $@ = "$err";
+            $! = EINVAL;
+            return;
+         }
+
+         $hints{family} = $addrinfo->{family};
+      }
+
       @infos = ( {
          family   => $hints{family},
          socktype => $hints{socktype},
diff --git a/t/19no-addrs.t b/t/19no-addrs.t
index 65716cf..0ccb84f 100644
--- a/t/19no-addrs.t
+++ b/t/19no-addrs.t
@@ -33,4 +33,13 @@ SKIP: {
    is( $sock->socktype, SOCK_STREAM, '$sock->socktype for Family => AF_INET6' );
 }
 
+# Lack of even a Family hint - _a_ socket is created but we don't guarantee
+# what family
+{
+   my $sock = IO::Socket::IP->new( Type => SOCK_STREAM );
+
+   ok( defined $sock->fileno, '$sock->fileno for Type => SOCK_STREAM' );
+   is( $sock->socktype, SOCK_STREAM, '$sock->socktype for Type => SOCK_STREAM' );
+}
+
 done_testing;
-- 
2.13.5