Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added initial IPv6 support.

  • Loading branch information...
commit 64c395ffdd3e9f4b587dcb2e3d8e37ed6718b62a 1 parent 02f8bfd
Rocco Caputo authored
1  MANIFEST
View
@@ -114,3 +114,4 @@ t/25_detach.t
t/26_comp_tcp.t
t/27_poll.t
t/28_windows.t
+t/29_sockfact6.t
15 lib/POE/Component/Client/TCP.pm
View
@@ -42,6 +42,7 @@ sub new {
my $alias = delete $param{Alias};
my $address = delete $param{RemoteAddress};
my $port = delete $param{RemotePort};
+ my $domain = delete $param{Domain};
foreach ( qw( Connected ConnectError Disconnected ServerInput
ServerError ServerFlushed
@@ -127,6 +128,7 @@ sub new {
$heap->{server} = POE::Wheel::SocketFactory->new
( RemoteAddress => $address,
RemotePort => $port,
+ SocketDomain => $domain,
SuccessEvent => 'got_connect_success',
FailureEvent => 'got_connect_error',
);
@@ -244,6 +246,7 @@ POE::Component::Client::TCP - a simplified TCP client
POE::Component::Client::TCP->new
( RemoteAddress => "127.0.0.1",
RemotePort => "chargen",
+ Domain => AF_INET, # Optional.
ServerInput => sub {
my $input = $_[ARG0];
print "from server: $input\n";
@@ -255,6 +258,7 @@ POE::Component::Client::TCP - a simplified TCP client
POE::Component::Client::TCP->new
( RemoteAddress => "127.0.0.1",
RemotePort => "chargen",
+ Domain => AF_INET, # Optional.
Connected => \&handle_connect,
ConnectError => \&handle_connect_error,
@@ -383,6 +387,17 @@ example, this reconnects after waiting a minute:
The component will shut down after disconnecting if a reconnect isn't
requested.
+=item Domain
+
+Specifies the domain within which communication will take place. It
+selects the protocol family which should be used. Currently supported
+values are AF_INET, AF_INET6, PF_INET or PF_INET6. This parameter is
+optional and will default to AF_INET if omitted.
+
+Note: AF_INET6 and PF_INET6 are supplied by the Socket6 module, which
+is available on the CPAN. You must have Socket6 loaded before
+POE::Component::Server::TCP will create IPv6 sockets.
+
=item Filter
Filter specifies the type of filter that will parse input from a
35 lib/POE/Component/Server/TCP.pm
View
@@ -40,6 +40,7 @@ sub new {
my $alias = delete $param{Alias};
my $address = delete $param{Address};
my $port = delete $param{Port};
+ my $domain = delete $param{Domain};
foreach ( qw( Acceptor Error ClientInput ClientConnected
ClientDisconnected ClientError ClientFlushed
@@ -124,7 +125,15 @@ sub new {
my ( $kernel, $session, $heap ) = @_[KERNEL, SESSION, HEAP];
$heap->{shutdown} = 0;
- $heap->{remote_ip} = inet_ntoa($remote_addr);
+
+ if (length($remote_addr) == 4) {
+ $heap->{remote_ip} = inet_ntoa($remote_addr);
+ }
+ else {
+ $heap->{remote_ip} =
+ Socket6::inet_ntop($domain, $remote_addr);
+ }
+
$heap->{remote_port} = $remote_port;
$heap->{client} = POE::Wheel::ReadWrite->new
@@ -203,6 +212,7 @@ sub new {
$_[HEAP]->{listener} = POE::Wheel::SocketFactory->new
( BindPort => $port,
BindAddress => $address,
+ SocketDomain => $domain,
Reuse => 'yes',
SuccessEvent => 'tcp_server_got_connection',
FailureEvent => 'tcp_server_got_error',
@@ -271,6 +281,7 @@ POE::Component::Server::TCP - a simplified TCP server
POE::Component::Server::TCP->new
( Port => $bind_port,
Address => $bind_address, # Optional.
+ Domain => AF_INET, # Optional.
Acceptor => \&accept_handler,
Error => \&error_handler, # Optional.
);
@@ -280,6 +291,7 @@ POE::Component::Server::TCP - a simplified TCP server
POE::Component::Server::TCP->new
( Port => $bind_port,
Address => $bind_address, # Optional.
+ Domain => AF_INET, # Optional.
Acceptor => \&accept_handler, # Optional.
Error => \&error_handler, # Optional.
@@ -374,14 +386,16 @@ It disables the code that provides the /Client.*/ callbacks.
=item Address
Address is the optional interface address the TCP server will bind to.
-It defaults to INADDR_ANY.
+It defaults to INADDR_ANY or INADDR6_ANY when using IPv4 or IPv6,
+respectively.
- Address => '127.0.0.1'
+ Address => '127.0.0.1' # Localhost IPv4
+ Address => "::1" # Localhost IPv6
It's passed directly to SocketFactory's BindAddress parameter, so it
can be in whatever form SocketFactory supports. At the time of this
-writing, that's a dotted quad, a host name, or a packed Internet
-address.
+writing, that's a dotted quad, an IPv6 address, a host name, or a
+packed Internet address.
=item Alias
@@ -442,6 +456,17 @@ ID.
ClientInput and Acceptor are mutually exclusive. Enabling one
prohibits the other.
+=item Domain
+
+Specifies the domain within which communication will take place. It
+selects the protocol family which should be used. Currently supported
+values are AF_INET, AF_INET6, PF_INET or PF_INET6. This parameter is
+optional and will default to AF_INET if omitted.
+
+Note: AF_INET6 and PF_INET6 are supplied by the Socket6 module, which
+is available on the CPAN. You must have Socket6 loaded before
+POE::Component::Server::TCP will create IPv6 sockets.
+
=item Error
Error is an optional coderef which will be called to handle server
177 lib/POE/Wheel/SocketFactory.pm
View
@@ -48,6 +48,11 @@ BEGIN {
eval '*F_GETFL = sub { 0 };';
eval '*F_SETFL = sub { 0 };';
}
+
+ unless (exists $INC{"Socket6.pm"}) {
+ eval "*Socket6::AF_INET6 = sub { ~0 }";
+ eval "*Socket6::PF_INET6 = sub { ~0 }";
+ }
}
#------------------------------------------------------------------------------
@@ -55,13 +60,16 @@ BEGIN {
# same operations, it seems, and this is a way to add new ones with a
# minimum of additional code.
-sub DOM_UNIX () { 'unix' } # UNIX domain socket
-sub DOM_INET () { 'inet' } # INET domain socket
+sub DOM_UNIX () { 'unix' } # UNIX domain socket
+sub DOM_INET () { 'inet' } # INET domain socket
+sub DOM_INET6 () { 'inet6' } # INET v6 domain socket
# AF_XYZ and PF_XYZ may be different.
my %map_family_to_domain =
- ( AF_UNIX, DOM_UNIX, PF_UNIX, DOM_UNIX,
- AF_INET, DOM_INET, PF_INET, DOM_INET,
+ ( AF_UNIX, DOM_UNIX, PF_UNIX, DOM_UNIX,
+ AF_INET, DOM_INET, PF_INET, DOM_INET,
+ &Socket6::AF_INET6, DOM_INET6,
+ &Socket6::PF_INET6, DOM_INET6,
);
sub SVROP_LISTENS () { 'listens' } # connect/listen sockets
@@ -70,19 +78,25 @@ sub SVROP_NOTHING () { 'nothing' } # connectionless sockets
# Map family/protocol pairs to connection or connectionless
# operations.
my %supported_protocol =
- ( DOM_UNIX, { none => SVROP_LISTENS },
- DOM_INET, { tcp => SVROP_LISTENS,
- udp => SVROP_NOTHING,
- },
+ ( DOM_UNIX, { none => SVROP_LISTENS },
+ DOM_INET, { tcp => SVROP_LISTENS,
+ udp => SVROP_NOTHING,
+ },
+ DOM_INET6, { tcp => SVROP_LISTENS,
+ udp => SVROP_NOTHING,
+ },
);
# Sane default socket types for each supported protocol. -><- Maybe
# this structure can be combined with %supported_protocol?
my %default_socket_type =
- ( DOM_UNIX, { none => SOCK_STREAM },
- DOM_INET, { tcp => SOCK_STREAM,
- udp => SOCK_DGRAM,
- },
+ ( DOM_UNIX, { none => SOCK_STREAM },
+ DOM_INET, { tcp => SOCK_STREAM,
+ udp => SOCK_DGRAM,
+ },
+ DOM_INET6, { tcp => SOCK_STREAM,
+ udp => SOCK_DGRAM,
+ },
);
#------------------------------------------------------------------------------
@@ -137,6 +151,10 @@ sub _define_accept_state {
elsif ( $domain eq DOM_INET ) {
($peer_port, $peer_addr) = unpack_sockaddr_in($peer);
}
+ elsif ( $domain eq DOM_INET6 ) {
+ $peer = getpeername($new_socket);
+ ($peer_port, $peer_addr) = Socket6::unpack_sockaddr_in6($peer);
+ }
else {
die "sanity failure: socket domain == $domain";
}
@@ -232,6 +250,18 @@ sub _define_connect_state {
}
}
+ # INET6 socket stacks tend not to.
+ elsif ($domain eq DOM_INET6) {
+ if (defined $peer) {
+ eval {
+ ($peer_port, $peer_addr) = Socket6::unpack_sockaddr_in6($peer);
+ };
+ if (length $@) {
+ $peer_port = $peer_addr = undef;
+ }
+ }
+ }
+
# What are we doing here?
else {
die "sanity failure: socket domain == $domain";
@@ -421,10 +451,9 @@ sub new {
);
# Default to Internet sockets.
- $self->[MY_SOCKET_DOMAIN] = ( (defined $params{SocketDomain})
- ? $params{SocketDomain}
- : AF_INET
- );
+ my $domain = delete $params{SocketDomain};
+ $domain = AF_INET unless defined $domain;
+ $self->[MY_SOCKET_DOMAIN] = $domain;
# Abstract the socket domain into something we don't have to keep
# testing duplicates of.
@@ -455,7 +484,9 @@ sub new {
# Internet sockets use protocols. Default the INET protocol to tcp,
# and try to resolve it.
- elsif ($abstract_domain eq DOM_INET) {
+ elsif ( $abstract_domain eq DOM_INET or
+ $abstract_domain eq DOM_INET6
+ ) {
my $socket_protocol =
(defined $params{SocketProtocol}) ? $params{SocketProtocol} : 'tcp';
@@ -588,7 +619,6 @@ sub new {
# context, and translate them into parameters that bind()
# understands.
if ($abstract_domain eq DOM_INET) {
-
# Don't bind if the creator doesn't specify a related parameter.
if ((defined $params{BindAddress}) or (defined $params{BindPort})) {
@@ -601,12 +631,14 @@ sub new {
{% use_bytes %}
# Resolve the bind address if it's not already packed.
- (length($bind_address) == 4)
- or ($bind_address = inet_aton($bind_address));
+ unless (length($bind_address) == 4) {
+ $bind_address = inet_aton($bind_address);
+ }
+
unless (defined $bind_address) {
$! = EADDRNOTAVAIL;
$poe_kernel->yield( $event_failure,
- 'inet_aton', $!+0, $!, $self->[MY_UNIQUE_ID]
+ "inet_aton", $!+0, $!, $self->[MY_UNIQUE_ID]
);
return $self;
}
@@ -628,7 +660,59 @@ sub new {
$bind_address = pack_sockaddr_in($bind_port, $bind_address);
unless (defined $bind_address) {
$poe_kernel->yield( $event_failure,
- 'pack_sockaddr_in', $!+0, $!, $self->[MY_UNIQUE_ID]
+ "pack_sockaddr_in", $!+0, $!, $self->[MY_UNIQUE_ID]
+ );
+ return $self;
+ }
+ }
+ }
+
+ # Check SocketFactory /Bind.*/ parameters in an Internet socket
+ # context, and translate them into parameters that bind()
+ # understands.
+ elsif ($abstract_domain eq DOM_INET6) {
+
+ # Don't bind if the creator doesn't specify a related parameter.
+ if ((defined $params{BindAddress}) or (defined $params{BindPort})) {
+
+ # Set the bind address, or default to INADDR_ANY.
+ $bind_address = ( (defined $params{BindAddress})
+ ? $params{BindAddress}
+ : Socket6::inaddr6_any()
+ );
+
+ {% use_bytes %}
+
+ # Resolve the bind address.
+ $bind_address =
+ Socket6::inet_pton($self->[MY_SOCKET_DOMAIN], $bind_address);
+ unless (defined $bind_address) {
+ $! = EADDRNOTAVAIL;
+ $poe_kernel->yield( $event_failure,
+ "inet_pton", $!+0, $!, $self->[MY_UNIQUE_ID]
+ );
+ return $self;
+ }
+
+ # Set the bind port, or default to 0 (any) if none specified.
+ # Resolve it to a number, if at all possible.
+ my $bind_port = (defined $params{BindPort}) ? $params{BindPort} : 0;
+ if ($bind_port =~ /[^0-9]/) {
+ $bind_port = getservbyname($bind_port, $protocol_name);
+ unless (defined $bind_port) {
+ $! = EADDRNOTAVAIL;
+ $poe_kernel->yield( $event_failure,
+ 'getservbyname', $!+0, $!, $self->[MY_UNIQUE_ID]
+ );
+ return $self;
+ }
+ }
+
+ $bind_address = Socket6::pack_sockaddr_in6($bind_port, $bind_address);
+ unless (defined $bind_address) {
+ $poe_kernel->yield( $event_failure,
+ "pack_sockaddr_in6", $!+0, $!,
+ $self->[MY_UNIQUE_ID]
);
return $self;
}
@@ -690,7 +774,9 @@ sub new {
# Check SocketFactory /Remote.*/ parameters in an Internet socket
# context, and translate them into parameters that connect()
# understands.
- if ($abstract_domain eq DOM_INET) {
+ if ($abstract_domain eq DOM_INET or
+ $abstract_domain eq DOM_INET6
+ ) {
# connecting if RemoteAddress
croak 'RemotePort required' unless (defined $params{RemotePort});
carp 'ListenQueue ignored' if (defined $params{ListenQueue});
@@ -706,20 +792,47 @@ sub new {
}
}
- $connect_address = inet_aton($params{RemoteAddress});
+ my $error_tag;
+ if ($abstract_domain eq DOM_INET) {
+ $connect_address = inet_aton($params{RemoteAddress});
+ $error_tag = "inet_aton";
+ }
+ elsif ($abstract_domain eq DOM_INET6) {
+ $connect_address =
+ Socket6::inet_pton( $self->[MY_SOCKET_DOMAIN],
+ $params{RemoteAddress}
+ );
+ $error_tag = "inet_pton";
+ }
+ else {
+ die "unknown domain $abstract_domain";
+ }
+
unless (defined $connect_address) {
$! = EADDRNOTAVAIL;
$poe_kernel->yield( $event_failure,
- 'inet_aton', $!+0, $!, $self->[MY_UNIQUE_ID]
+ $error_tag, $!+0, $!, $self->[MY_UNIQUE_ID]
);
return $self;
}
- $connect_address = pack_sockaddr_in($remote_port, $connect_address);
+ if ($abstract_domain eq DOM_INET) {
+ $connect_address = pack_sockaddr_in($remote_port, $connect_address);
+ $error_tag = "pack_sockaddr_in";
+ }
+ elsif ($abstract_domain eq DOM_INET6) {
+ $connect_address =
+ Socket6::pack_sockaddr_in6($remote_port, $connect_address);
+ $error_tag = "pack_sockaddr_in6";
+ }
+ else {
+ die "unknown domain $abstract_domain";
+ }
+
unless ($connect_address) {
$! = EADDRNOTAVAIL;
$poe_kernel->yield( $event_failure,
- 'pack_sockaddr_in', $!+0, $!, $self->[MY_UNIQUE_ID]
+ $error_tag, $!+0, $!, $self->[MY_UNIQUE_ID]
);
return $self;
}
@@ -965,8 +1078,12 @@ call.
=item SocketDomain
SocketDomain supplies socket() with its DOMAIN parameter. Supported
-values are AF_UNIX, AF_INET, PF_UNIX and PF_INET. If SocketDomain is
-omitted, it defaults to AF_INET.
+values are AF_UNIX, AF_INET, AF_INET6, PF_UNIX, PF_INET, and PF_INET6.
+If SocketDomain is omitted, it defaults to AF_INET.
+
+Note: AF_INET6 and PF_INET6 are supplied by the Socket6 module, which
+is available on the CPAN. You must have Socket6 loaded before
+SocketFactory can create IPv6 sockets.
=item SocketType
@@ -1150,7 +1267,7 @@ A sample ErrorEvent handler:
=head1 SEE ALSO
-POE::Wheel.
+POE::Wheel, Socket6.
The SEE ALSO section in L<POE> contains a table of contents covering
the entire POE distribution.
5 mylib/Makefile-5005.pm
View
@@ -55,6 +55,11 @@ ExtUtils::AutoInstall->import
-tests => [ qw(t/27_poll.t) ],
'IO::Poll' => 0.05,
],
+ "Optional modules for IPv6 support." => [
+ -default => 0,
+ -tests => [ qw(t/29_sockfact6.t) ],
+ 'Socket6' => 0.11,
+ ],
"Optional modules for controlling full-screen programs (e.g. vi)." => [
-default => 0,
'IO::Pty' => '1.02',
Please sign in to comment.
Something went wrong with that request. Please try again.