bce5a8
From 067faffb8e596a53c9ac2ed7e571472f7a163681 Mon Sep 17 00:00:00 2001
bce5a8
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
bce5a8
Date: Mon, 16 Jan 2017 16:13:08 +0100
bce5a8
Subject: [PATCH] Add IPv6 support
bce5a8
MIME-Version: 1.0
bce5a8
Content-Type: text/plain; charset=UTF-8
bce5a8
Content-Transfer-Encoding: 8bit
bce5a8
bce5a8
This patch ports the code from IO::Socket::INET to IO::Socket::IP in
bce5a8
order to support IPv6.
bce5a8
bce5a8
CPAN RT #91699, #71395.
bce5a8
bce5a8
Signed-off-by: Petr Písař <ppisar@redhat.com>
bce5a8
---
bce5a8
 Makefile.PL        |  1 +
bce5a8
 README             | 24 ++++++++++++------------
bce5a8
 lib/HTTP/Daemon.pm | 43 ++++++++++++++++++++++++++++---------------
bce5a8
 t/chunked.t        | 34 +++++++++++++++++++++++-----------
bce5a8
 4 files changed, 64 insertions(+), 38 deletions(-)
bce5a8
bce5a8
diff --git a/Makefile.PL b/Makefile.PL
bce5a8
index 09c7e86..85d5712 100644
bce5a8
--- a/Makefile.PL
bce5a8
+++ b/Makefile.PL
bce5a8
@@ -14,6 +14,7 @@ WriteMakefile(
bce5a8
     PREREQ_PM => {
bce5a8
 	'Sys::Hostname' => 0,
bce5a8
 	'IO::Socket' => 0,
bce5a8
+	'IO::Socket::IP' => 0,
bce5a8
 	'HTTP::Request' => 6,
bce5a8
 	'HTTP::Response' => 6,
bce5a8
 	'HTTP::Status' => 6,
bce5a8
diff --git a/README b/README
bce5a8
index be5a20a..ddb3b6e 100644
bce5a8
--- a/README
bce5a8
+++ b/README
bce5a8
@@ -24,12 +24,12 @@ SYNOPSIS
bce5a8
 DESCRIPTION
bce5a8
     Instances of the `HTTP::Daemon' class are HTTP/1.1 servers that listen
bce5a8
     on a socket for incoming requests. The `HTTP::Daemon' is a subclass of
bce5a8
-    `IO::Socket::INET', so you can perform socket operations directly on it
bce5a8
+    `IO::Socket::IP', so you can perform socket operations directly on it
bce5a8
     too.
bce5a8
 
bce5a8
     The accept() method will return when a connection from a client is
bce5a8
     available. The returned value will be an `HTTP::Daemon::ClientConn'
bce5a8
-    object which is another `IO::Socket::INET' subclass. Calling the
bce5a8
+    object which is another `IO::Socket::IP' subclass. Calling the
bce5a8
     get_request() method on this object will read data from the client and
bce5a8
     return an `HTTP::Request' object. The ClientConn object also provide
bce5a8
     methods to send back various responses.
bce5a8
@@ -40,13 +40,13 @@ DESCRIPTION
bce5a8
     responses that conform to the HTTP/1.1 protocol.
bce5a8
 
bce5a8
     The following methods of `HTTP::Daemon' are new (or enhanced) relative
bce5a8
-    to the `IO::Socket::INET' base class:
bce5a8
+    to the `IO::Socket::IP' base class:
bce5a8
 
bce5a8
     $d = HTTP::Daemon->new
bce5a8
     $d = HTTP::Daemon->new( %opts )
bce5a8
         The constructor method takes the same arguments as the
bce5a8
-        `IO::Socket::INET' constructor, but unlike its base class it can
bce5a8
-        also be called without any arguments. The daemon will then set up a
bce5a8
+        `IO::Socket::IP' constructor, but unlike its base class it can also
bce5a8
+        be called without any arguments. The daemon will then set up a
bce5a8
         listen queue of 5 connections and allocate some random port number.
bce5a8
 
bce5a8
         A server that wants to bind to some specific address on the standard
bce5a8
@@ -57,8 +57,8 @@ DESCRIPTION
bce5a8
                    LocalPort => 80,
bce5a8
                );
bce5a8
 
bce5a8
-        See IO::Socket::INET for a description of other arguments that can
bce5a8
-        be used configure the daemon during construction.
bce5a8
+        See IO::Socket::IP for a description of other arguments that can be
bce5a8
+        used configure the daemon during construction.
bce5a8
 
bce5a8
     $c = $d->accept
bce5a8
     $c = $d->accept( $pkg )
bce5a8
@@ -71,7 +71,7 @@ DESCRIPTION
bce5a8
 
bce5a8
         The accept method will return `undef' if timeouts have been enabled
bce5a8
         and no connection is made within the given time. The timeout()
bce5a8
-        method is described in IO::Socket.
bce5a8
+        method is described in IO::Socket::IP.
bce5a8
 
bce5a8
         In list context both the client object and the peer address will be
bce5a8
         returned; see the description of the accept method IO::Socket for
bce5a8
@@ -89,9 +89,9 @@ DESCRIPTION
bce5a8
         The default is the string "libwww-perl-daemon/#.##" where "#.##" is
bce5a8
         replaced with the version number of this module.
bce5a8
 
bce5a8
-    The `HTTP::Daemon::ClientConn' is a `IO::Socket::INET' subclass.
bce5a8
-    Instances of this class are returned by the accept() method of
bce5a8
-    `HTTP::Daemon'. The following methods are provided:
bce5a8
+    The `HTTP::Daemon::ClientConn' is a `IO::Socket::IP' subclass. Instances
bce5a8
+    of this class are returned by the accept() method of `HTTP::Daemon'. The
bce5a8
+    following methods are provided:
bce5a8
 
bce5a8
     $c->get_request
bce5a8
     $c->get_request( $headers_only )
bce5a8
@@ -227,7 +227,7 @@ DESCRIPTION
bce5a8
 SEE ALSO
bce5a8
     RFC 2616
bce5a8
 
bce5a8
-    IO::Socket::INET, IO::Socket
bce5a8
+    IO::Socket::IP, IO::Socket
bce5a8
 
bce5a8
 COPYRIGHT
bce5a8
     Copyright 1996-2003, Gisle Aas
bce5a8
diff --git a/lib/HTTP/Daemon.pm b/lib/HTTP/Daemon.pm
bce5a8
index 27a7bf4..0e22b77 100644
bce5a8
--- a/lib/HTTP/Daemon.pm
bce5a8
+++ b/lib/HTTP/Daemon.pm
bce5a8
@@ -5,8 +5,10 @@ use vars qw($VERSION @ISA $PROTO $DEBUG);
bce5a8
 
bce5a8
 $VERSION = "6.01";
bce5a8
 
bce5a8
-use IO::Socket qw(AF_INET INADDR_ANY INADDR_LOOPBACK inet_ntoa);
bce5a8
-@ISA=qw(IO::Socket::INET);
bce5a8
+use Socket qw(AF_INET AF_INET6 INADDR_ANY IN6ADDR_ANY
bce5a8
+	    INADDR_LOOPBACK IN6ADDR_LOOPBACK inet_ntop sockaddr_family);
bce5a8
+use IO::Socket::IP;
bce5a8
+@ISA=qw(IO::Socket::IP);
bce5a8
 
bce5a8
 $PROTO = "HTTP/1.1";
bce5a8
 
bce5a8
@@ -40,15 +42,26 @@ sub url
bce5a8
     my $self = shift;
bce5a8
     my $url = $self->_default_scheme . "://";
bce5a8
     my $addr = $self->sockaddr;
bce5a8
-    if (!$addr || $addr eq INADDR_ANY) {
bce5a8
+    if (!$addr || $addr eq INADDR_ANY || $addr eq IN6ADDR_ANY) {
bce5a8
  	require Sys::Hostname;
bce5a8
  	$url .= lc Sys::Hostname::hostname();
bce5a8
     }
bce5a8
     elsif ($addr eq INADDR_LOOPBACK) {
bce5a8
-	$url .= inet_ntoa($addr);
bce5a8
+	$url .= inet_ntop(AF_INET, $addr);
bce5a8
+    }
bce5a8
+    elsif ($addr eq IN6ADDR_LOOPBACK) {
bce5a8
+	$url .= '[' . inet_ntop(AF_INET6, $addr) . ']';
bce5a8
     }
bce5a8
     else {
bce5a8
-	$url .= gethostbyaddr($addr, AF_INET) || inet_ntoa($addr);
bce5a8
+	my $host = $addr->sockhostname;
bce5a8
+        if (!defined $host) {
bce5a8
+	    if (sockaddr_family($addr) eq AF_INET6) {
bce5a8
+		$host = '[' . inet_ntop(AF_INET6, $addr) . ']';
bce5a8
+	    } else {
bce5a8
+		$host = inet_ntop(AF_INET6, $addr);
bce5a8
+	    }
bce5a8
+	}
bce5a8
+	$url .= $host;
bce5a8
     }
bce5a8
     my $port = $self->sockport;
bce5a8
     $url .= ":$port" if $port != $self->_default_port;
bce5a8
@@ -77,8 +90,8 @@ sub product_tokens
bce5a8
 package HTTP::Daemon::ClientConn;
bce5a8
 
bce5a8
 use vars qw(@ISA $DEBUG);
bce5a8
-use IO::Socket ();
bce5a8
-@ISA=qw(IO::Socket::INET);
bce5a8
+use IO::Socket::IP ();
bce5a8
+@ISA=qw(IO::Socket::IP);
bce5a8
 *DEBUG = \$HTTP::Daemon::DEBUG;
bce5a8
 
bce5a8
 use HTTP::Request  ();
bce5a8
@@ -645,12 +658,12 @@ HTTP::Daemon - a simple http server class
bce5a8
 
bce5a8
 Instances of the C<HTTP::Daemon> class are HTTP/1.1 servers that
bce5a8
 listen on a socket for incoming requests. The C<HTTP::Daemon> is a
bce5a8
-subclass of C<IO::Socket::INET>, so you can perform socket operations
bce5a8
+subclass of C<IO::Socket::IP>, so you can perform socket operations
bce5a8
 directly on it too.
bce5a8
 
bce5a8
 The accept() method will return when a connection from a client is
bce5a8
 available.  The returned value will be an C<HTTP::Daemon::ClientConn>
bce5a8
-object which is another C<IO::Socket::INET> subclass.  Calling the
bce5a8
+object which is another C<IO::Socket::IP> subclass.  Calling the
bce5a8
 get_request() method on this object will read data from the client and
bce5a8
 return an C<HTTP::Request> object.  The ClientConn object also provide
bce5a8
 methods to send back various responses.
bce5a8
@@ -661,7 +674,7 @@ desirable.  Also note that the user is responsible for generating
bce5a8
 responses that conform to the HTTP/1.1 protocol.
bce5a8
 
bce5a8
 The following methods of C<HTTP::Daemon> are new (or enhanced) relative
bce5a8
-to the C<IO::Socket::INET> base class:
bce5a8
+to the C<IO::Socket::IP> base class:
bce5a8
 
bce5a8
 =over 4
bce5a8
 
bce5a8
@@ -670,7 +683,7 @@ to the C<IO::Socket::INET> base class:
bce5a8
 =item $d = HTTP::Daemon->new( %opts )
bce5a8
 
bce5a8
 The constructor method takes the same arguments as the
bce5a8
-C<IO::Socket::INET> constructor, but unlike its base class it can also
bce5a8
+C<IO::Socket::IP> constructor, but unlike its base class it can also
bce5a8
 be called without any arguments.  The daemon will then set up a listen
bce5a8
 queue of 5 connections and allocate some random port number.
bce5a8
 
bce5a8
@@ -682,7 +695,7 @@ HTTP port will be constructed like this:
bce5a8
            LocalPort => 80,
bce5a8
        );
bce5a8
 
bce5a8
-See L<IO::Socket::INET> for a description of other arguments that can
bce5a8
+See L<IO::Socket::IP> for a description of other arguments that can
bce5a8
 be used configure the daemon during construction.
bce5a8
 
bce5a8
 =item $c = $d->accept
bce5a8
@@ -699,7 +712,7 @@ class a subclass of C<HTTP::Daemon::ClientConn>.
bce5a8
 
bce5a8
 The accept method will return C<undef> if timeouts have been enabled
bce5a8
 and no connection is made within the given time.  The timeout() method
bce5a8
-is described in L<IO::Socket>.
bce5a8
+is described in L<IO::Socket::IP>.
bce5a8
 
bce5a8
 In list context both the client object and the peer address will be
bce5a8
 returned; see the description of the accept method L<IO::Socket> for
bce5a8
@@ -721,7 +734,7 @@ replaced with the version number of this module.
bce5a8
 
bce5a8
 =back
bce5a8
 
bce5a8
-The C<HTTP::Daemon::ClientConn> is a C<IO::Socket::INET>
bce5a8
+The C<HTTP::Daemon::ClientConn> is a C<IO::Socket::IP>
bce5a8
 subclass. Instances of this class are returned by the accept() method
bce5a8
 of C<HTTP::Daemon>.  The following methods are provided:
bce5a8
 
bce5a8
@@ -895,7 +908,7 @@ Return a reference to the corresponding C<HTTP::Daemon> object.
bce5a8
 
bce5a8
 RFC 2616
bce5a8
 
bce5a8
-L<IO::Socket::INET>, L<IO::Socket>
bce5a8
+L<IO::Socket::IP>, L<IO::Socket>
bce5a8
 
bce5a8
 =head1 COPYRIGHT
bce5a8
 
bce5a8
diff --git a/t/chunked.t b/t/chunked.t
bce5a8
index e11799f..c274b11 100644
bce5a8
--- a/t/chunked.t
bce5a8
+++ b/t/chunked.t
bce5a8
@@ -95,18 +95,30 @@ my $can_fork = $Config{d_fork} ||
bce5a8
 my $tests = @TESTS;
bce5a8
 my $tport = 8333;
bce5a8
 
bce5a8
-my $tsock = IO::Socket::INET->new(LocalAddr => '0.0.0.0',
bce5a8
-                                  LocalPort => $tport,
bce5a8
-                                  Listen    => 1,
bce5a8
-                                  ReuseAddr => 1);
bce5a8
+my @addresses = (
bce5a8
+    { server => '::', client => '::1' },
bce5a8
+    { server => '0.0.0.0', client => '127.0.0.1' }
bce5a8
+);
bce5a8
+my $family;
bce5a8
+for my $id (0..$#addresses) {
bce5a8
+    my $tsock = IO::Socket::IP->new(LocalAddr => $addresses[$id]->{server},
bce5a8
+                                    LocalPort => $tport,
bce5a8
+                                    Listen    => 1,
bce5a8
+                                    ReuseAddr => 1);
bce5a8
+    if ($tsock) {
bce5a8
+        close $tsock;
bce5a8
+        $family = $id;
bce5a8
+        last;
bce5a8
+    }
bce5a8
+}
bce5a8
+
bce5a8
 if (!$can_fork) {
bce5a8
   plan skip_all => "This system cannot fork";
bce5a8
 }
bce5a8
-elsif (!$tsock) {
bce5a8
-  plan skip_all => "Cannot listen on 0.0.0.0:$tport";
bce5a8
+elsif (!defined $family) {
bce5a8
+  plan skip_all => "Cannot listen on unspecifed address and port $tport";
bce5a8
 }
bce5a8
 else {
bce5a8
-  close $tsock;
bce5a8
   plan tests => $tests;
bce5a8
 }
bce5a8
 
bce5a8
@@ -132,9 +144,9 @@ if ($pid = fork) {
bce5a8
       open my $fh, "| socket localhost $tport" or die;
bce5a8
       print $fh $test;
bce5a8
     }
bce5a8
-    use IO::Socket::INET;
bce5a8
-    my $sock = IO::Socket::INET->new(
bce5a8
-                                     PeerAddr => "127.0.0.1",
bce5a8
+    use IO::Socket::IP;
bce5a8
+    my $sock = IO::Socket::IP->new(
bce5a8
+                                     PeerAddr => $addresses[$family]->{client},
bce5a8
                                      PeerPort => $tport,
bce5a8
                                     ) or die;
bce5a8
     if (0) {
bce5a8
@@ -158,7 +170,7 @@ if ($pid = fork) {
bce5a8
 } else {
bce5a8
   die "cannot fork: $!" unless defined $pid;
bce5a8
   my $d = HTTP::Daemon->new(
bce5a8
-                            LocalAddr => '0.0.0.0',
bce5a8
+                            LocalAddr => $addresses[$family]->{server},
bce5a8
                             LocalPort => $tport,
bce5a8
                             ReuseAddr => 1,
bce5a8
                            ) or die;
bce5a8
-- 
bce5a8
2.7.4
bce5a8