File Coverage

blib/lib/POE/Wheel/SocketFactory.pm
Criterion Covered Total %
statement 256 404 63.3
branch 118 256 46.0
condition 21 62 33.8
subroutine 26 28 92.8
pod 6 7 85.7
total 427 757 56.4


line stmt bran cond sub pod time code
1             package POE::Wheel::SocketFactory;
2              
3 23     23   95 use strict;
  23         30  
  23         784  
4              
5 23     23   94 use vars qw($VERSION @ISA);
  23         32  
  23         1149  
6             $VERSION = '1.366'; # NOTE - Should be #.### (three decimal places)
7              
8 23     23   95 use Carp qw( carp croak );
  23         32  
  23         1059  
9 23     23   132 use Symbol qw( gensym );
  23         30  
  23         946  
10              
11 23     23   97 use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
  23         42  
  23         1162  
12 23         1355 use Errno qw(
13             EWOULDBLOCK EADDRNOTAVAIL EINPROGRESS EADDRINUSE ECONNABORTED
14             ESPIPE
15 23     23   101 );
  23         41  
16              
17 23         2092 use Socket qw(
18             AF_INET SOCK_STREAM SOL_SOCKET AF_UNIX PF_UNIX
19             PF_INET SOCK_DGRAM SO_ERROR unpack_sockaddr_in
20             unpack_sockaddr_un PF_UNSPEC SO_REUSEADDR INADDR_ANY
21             pack_sockaddr_in pack_sockaddr_un inet_aton SOMAXCONN
22 23     23   98 );
  23         25  
23              
24 23     23   98 use IO::Handle ();
  23         33  
  23         296  
25 23     23   89 use FileHandle ();
  23         33  
  23         334  
26 23     23   86 use POE qw( Wheel );
  23         25  
  23         145  
27             push @ISA, qw(POE::Wheel);
28              
29 0     0 0 0 sub CRIMSON_SCOPE_HACK ($) { 0 }
30             sub DEBUG () { 0 }
31              
32             sub MY_SOCKET_HANDLE () { 0 }
33             sub MY_UNIQUE_ID () { 1 }
34             sub MY_EVENT_SUCCESS () { 2 }
35             sub MY_EVENT_FAILURE () { 3 }
36             sub MY_SOCKET_DOMAIN () { 4 }
37             sub MY_STATE_ACCEPT () { 5 }
38             sub MY_STATE_CONNECT () { 6 }
39             sub MY_MINE_SUCCESS () { 7 }
40             sub MY_MINE_FAILURE () { 8 }
41             sub MY_SOCKET_PROTOCOL () { 9 }
42             sub MY_SOCKET_TYPE () { 10 }
43             sub MY_STATE_ERROR () { 11 }
44             sub MY_SOCKET_SELECTED () { 12 }
45              
46             # Fletch has subclassed SSLSocketFactory from SocketFactory.
47             # He's added new members after MY_SOCKET_SELECTED. Be sure, if you
48             # extend this, to extend add stuff BEFORE MY_SOCKET_SELECTED or let
49             # Fletch know you've broken his module.
50              
51             # If IPv6 support can't be loaded, then provide dummies so the code at
52             # least compiles. Suggested in rt.cpan.org 27250.
53             BEGIN {
54              
55 23     23   38 eval { Socket->import( qw(getaddrinfo getnameinfo unpack_sockaddr_in6) ) };
  23         706  
56 23 50       94 if ($@) {
57 0         0 *getaddrinfo = sub { Carp::confess("Unable to use IPv6: Socket doesn't provide getaddrinfo()") };
  0         0  
58 0         0 *getnameinfo = sub { Carp::confess("Unable to use IPv6: Socket doesn't provide getnameinfo()") };
  0         0  
59 0         0 *unpack_sockaddr_in6 = sub { Carp::confess("Unable to use IPv6: Socket doesn't provide unpack_sockaddr_in6()") };
  0         0  
60             }
61              
62             # Socket6 provides AF_INET6 and PF_INET6 where earlier Perls' Socket don't.
63 23         31 eval { Socket->import( qw(AF_INET6 PF_INET6) ) };
  23         515  
64 23 50       73 if ($@) {
65 0         0 eval { require Socket6; Socket6->import( qw(AF_INET6 PF_INET6) ) };
  0         0  
  0         0  
66 0 0       0 if ($@) {
67 0         0 *AF_INET6 = sub { -1 };
  0         0  
68 0         0 *PF_INET6 = sub { -1 };
  0         0  
69             }
70             }
71              
72 23         34 eval { Socket->import( 'IPPROTO_TCP' ) };
  23         317  
73 23 50       71 if ($@) {
74 0         0 *IPPROTO_TCP = (getprotobyname 'tcp')[2];
75             }
76              
77 23         26 eval { Socket->import( 'IPPROTO_UDP' ) };
  23         290  
78 23 50       55530 if ($@) {
79 0         0 *IPPROTO_UDP = (getprotobyname 'udp')[2];
80             }
81             }
82              
83             # Common protocols to help support systems that don't have
84             # getprotobyname().
85             my %proto_by_name = (
86             tcp => IPPROTO_TCP,
87             udp => IPPROTO_UDP,
88             );
89              
90             my %proto_by_number = reverse %proto_by_name;
91              
92             #------------------------------------------------------------------------------
93             # These tables customize the socketfactory. Many protocols share the
94             # same operations, it seems, and this is a way to add new ones with a
95             # minimum of additional code.
96              
97             sub DOM_UNIX () { 'unix' } # UNIX domain socket
98             sub DOM_INET () { 'inet' } # INET domain socket
99             sub DOM_INET6 () { 'inet6' } # INET v6 domain socket
100              
101             # AF_XYZ and PF_XYZ may be different.
102             my %map_family_to_domain = (
103             AF_UNIX, DOM_UNIX, PF_UNIX, DOM_UNIX,
104             AF_INET, DOM_INET, PF_INET, DOM_INET,
105             AF_INET6, DOM_INET6,
106             PF_INET6, DOM_INET6,
107             );
108              
109             sub SVROP_LISTENS () { 'listens' } # connect/listen sockets
110             sub SVROP_NOTHING () { 'nothing' } # connectionless sockets
111              
112             # Map family/protocol pairs to connection or connectionless
113             # operations.
114             my %supported_protocol = (
115             DOM_UNIX, {
116             none => SVROP_LISTENS
117             },
118             DOM_INET, {
119             tcp => SVROP_LISTENS,
120             udp => SVROP_NOTHING,
121             },
122             DOM_INET6, {
123             tcp => SVROP_LISTENS,
124             udp => SVROP_NOTHING,
125             },
126             );
127              
128             # Sane default socket types for each supported protocol. TODO Maybe
129             # this structure can be combined with %supported_protocol?
130             my %default_socket_type = (
131             DOM_UNIX, {
132             none => SOCK_STREAM
133             },
134             DOM_INET, {
135             tcp => SOCK_STREAM,
136             udp => SOCK_DGRAM,
137             },
138             DOM_INET6, {
139             tcp => SOCK_STREAM,
140             udp => SOCK_DGRAM,
141             },
142             );
143              
144             #------------------------------------------------------------------------------
145             # Perform system-dependent translations on Unix addresses, if
146             # necessary.
147              
148             sub _condition_unix_address {
149 4     4   5 my ($address) = @_;
150              
151             # OS/2 would like sockets to use backwhacks, and please place them
152             # in the virtual \socket\ directory. Thank you.
153 4 50       11 if ($^O eq 'os2') {
154 0         0 $address =~ tr[\\][/];
155 0 0       0 if ($address !~ m{^/socket/}) {
156 0         0 $address =~ s{^/?}{/socket/};
157             }
158 0         0 $address =~ tr[/][\\];
159             }
160              
161 4         7 $address;
162             }
163              
164             #------------------------------------------------------------------------------
165             # Define the select handler that will accept connections.
166              
167             sub _define_accept_state {
168 22     22   36 my $self = shift;
169              
170             # We do these stupid closure tricks to avoid putting $self in it
171             # directly. If you include $self in one of the state() closures,
172             # the component will fail to shut down properly: there will be a
173             # circular definition in the closure holding $self alive.
174              
175 22         46 my $domain = $map_family_to_domain{ $self->[MY_SOCKET_DOMAIN] };
176 22 50       51 $domain = '(undef)' unless defined $domain;
177 22         44 my $event_success = \$self->[MY_EVENT_SUCCESS];
178 22         32 my $event_failure = \$self->[MY_EVENT_FAILURE];
179 22         69 my $unique_id = $self->[MY_UNIQUE_ID];
180              
181             $poe_kernel->state(
182             $self->[MY_STATE_ACCEPT] = ref($self) . "($unique_id) -> select accept",
183             sub {
184             # prevents SEGV
185 64     64   78 0 && CRIMSON_SCOPE_HACK('<');
186              
187             # subroutine starts here
188 64         139 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
189              
190 64         222 my $new_socket = gensym;
191 64         2272 my $peer = accept($new_socket, $handle);
192              
193 64 50 0     156 if ($peer) {
    0 0        
194 64         83 my ($peer_addr, $peer_port);
195 64 100       312 if ( $domain eq DOM_UNIX ) {
    50          
    0          
196 2         3 $peer_port = undef;
197 2         3 eval { $peer_addr = unpack_sockaddr_un($peer) };
  2         9  
198 2 50       6 $peer_addr = undef if length $@;
199             }
200             elsif ( $domain eq DOM_INET ) {
201 62         304 ($peer_port, $peer_addr) = unpack_sockaddr_in($peer);
202             }
203             elsif ( $domain eq DOM_INET6 ) {
204 0         0 ($peer_addr, $peer_port) = unpack_sockaddr_in6($peer);
205             }
206             else {
207 0         0 die "sanity failure: socket domain == $domain";
208             }
209 64         280 $k->call(
210             $me, $$event_success,
211             $new_socket, $peer_addr, $peer_port,
212             $unique_id
213             );
214             }
215             elsif ($! != EWOULDBLOCK and $! != ECONNABORTED and $! != ESPIPE) {
216             # OSX reports ESPIPE, which isn't documented anywhere.
217 0 0       0 $$event_failure && $k->call(
218             $me, $$event_failure,
219             'accept', ($!+0), $!, $unique_id
220             );
221             }
222             }
223 22         296 );
224              
225 22         38 $self->[MY_SOCKET_SELECTED] = 'yes';
226 22         95 $poe_kernel->select_read(
227             $self->[MY_SOCKET_HANDLE],
228             $self->[MY_STATE_ACCEPT]
229             );
230             }
231              
232             #------------------------------------------------------------------------------
233             # Define the select handler that will finalize an established
234             # connection.
235              
236             sub _define_connect_state {
237 77     77   104 my $self = shift;
238              
239             # We do these stupid closure tricks to avoid putting $self in it
240             # directly. If you include $self in one of the state() closures,
241             # the component will fail to shut down properly: there will be a
242             # circular definition in the closure holding $self alive.
243              
244 77         153 my $domain = $map_family_to_domain{ $self->[MY_SOCKET_DOMAIN] };
245 77 50       163 $domain = '(undef)' unless defined $domain;
246 77         117 my $event_success = \$self->[MY_EVENT_SUCCESS];
247 77         120 my $event_failure = \$self->[MY_EVENT_FAILURE];
248 77         115 my $unique_id = $self->[MY_UNIQUE_ID];
249 77         103 my $socket_selected = \$self->[MY_SOCKET_SELECTED];
250              
251 77         106 my $socket_handle = \$self->[MY_SOCKET_HANDLE];
252 77         119 my $state_accept = \$self->[MY_STATE_ACCEPT];
253 77         86 my $state_connect = \$self->[MY_STATE_CONNECT];
254 77         79 my $mine_success = \$self->[MY_MINE_SUCCESS];
255 77         104 my $mine_failure = \$self->[MY_MINE_FAILURE];
256              
257             $poe_kernel->state(
258             $self->[MY_STATE_CONNECT] = (
259             ref($self) . "($unique_id) -> select connect"
260             ),
261             sub {
262             # This prevents SEGV in older versions of Perl.
263 77     77   93 0 && CRIMSON_SCOPE_HACK('<');
264              
265             # Grab some values and stop watching the socket.
266 77         157 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
267              
268 77         166 _shutdown(
269             $socket_selected, $socket_handle,
270             $state_accept, $state_connect,
271             $mine_success, $event_success,
272             $mine_failure, $event_failure,
273             );
274              
275             # Throw a failure if the connection failed.
276 77         2177 $! = unpack('i', getsockopt($handle, SOL_SOCKET, SO_ERROR));
277 77 100       278 if ($!) {
278 3 50       33 (defined $$event_failure) and $k->call(
279             $me, $$event_failure,
280             'connect', ($!+0), $!, $unique_id
281             );
282 3         56 return;
283             }
284              
285             # Get the remote address, or throw an error if that fails.
286 74         230 my $peer = getpeername($handle);
287 74 50       184 if ($!) {
288 0 0       0 (defined $$event_failure) and $k->call(
289             $me, $$event_failure,
290             'getpeername', ($!+0), $!, $unique_id
291             );
292 0         0 return;
293             }
294              
295             # Parse the remote address according to the socket's domain.
296 74         76 my ($peer_addr, $peer_port);
297              
298             # UNIX sockets have some trouble with peer addresses.
299 74 100       228 if ($domain eq DOM_UNIX) {
    50          
    0          
300 2 50       6 if (defined $peer) {
301 2         3 eval { $peer_addr = unpack_sockaddr_un($peer) };
  2         8  
302 2 50       6 $peer_addr = undef if length $@;
303             }
304             }
305              
306             # INET socket stacks tend not to.
307             elsif ($domain eq DOM_INET) {
308 72 50       135 if (defined $peer) {
309 72         94 eval {
310 72         287 ($peer_port, $peer_addr) = unpack_sockaddr_in($peer);
311             };
312 72 50       191 if (length $@) {
313 0         0 $peer_port = $peer_addr = undef;
314             }
315             }
316             }
317              
318             # INET6 socket stacks tend not to.
319             elsif ($domain eq DOM_INET6) {
320 0 0       0 if (defined $peer) {
321 0         0 ((my $error), $peer_addr, $peer_port) = getnameinfo($peer);
322 0 0       0 if ($error) {
323 0         0 warn $error;
324 0         0 $peer_port = $peer_addr = undef;
325             }
326             }
327             }
328              
329             # What are we doing here?
330             else {
331 0         0 die "sanity failure: socket domain == $domain";
332             }
333              
334             # Tell the session it went okay. Also let go of the socket.
335 74         252 $k->call(
336             $me, $$event_success,
337             $handle, $peer_addr, $peer_port, $unique_id
338             );
339             }
340 77         866 );
341              
342             # Cygwin and Windows expect an error state registered to expedite.
343             # This code is nearly identical the stuff above.
344 77 50 33     414 if ($^O eq "cygwin" or $^O eq "MSWin32") {
345             $poe_kernel->state(
346             $self->[MY_STATE_ERROR] = (
347             ref($self) . "($unique_id) -> connect error"
348             ),
349             sub {
350             # This prevents SEGV in older versions of Perl.
351 0     0   0 0 && CRIMSON_SCOPE_HACK('<');
352              
353             # Grab some values and stop watching the socket.
354 0         0 my ($k, $me, $handle) = @_[KERNEL, SESSION, ARG0];
355              
356 0         0 _shutdown(
357             $socket_selected, $socket_handle,
358             $state_accept, $state_connect,
359             $mine_success, $event_success,
360             $mine_failure, $event_failure,
361             );
362              
363             # Throw a failure if the connection failed.
364 0         0 $! = unpack('i', getsockopt($handle, SOL_SOCKET, SO_ERROR));
365 0 0       0 if ($!) {
366 0 0       0 (defined $$event_failure) and $k->call(
367             $me, $$event_failure, 'connect', ($!+0), $!, $unique_id
368             );
369 0         0 return;
370             }
371             }
372 0         0 );
373 0         0 $poe_kernel->select_expedite(
374             $self->[MY_SOCKET_HANDLE],
375             $self->[MY_STATE_ERROR]
376             );
377             }
378              
379 77         119 $self->[MY_SOCKET_SELECTED] = 'yes';
380 77         250 $poe_kernel->select_write(
381             $self->[MY_SOCKET_HANDLE],
382             $self->[MY_STATE_CONNECT]
383             );
384             }
385              
386             #------------------------------------------------------------------------------
387              
388             sub event {
389 101     101 1 129 my $self = shift;
390 101 50       253 push(@_, undef) if (scalar(@_) & 1);
391              
392 101         208 while (@_) {
393 202         329 my ($name, $event) = splice(@_, 0, 2);
394              
395 202 100       408 if ($name eq 'SuccessEvent') {
    50          
396 101 50       169 if (defined $event) {
397 101 50       195 if (ref($event)) {
398 0         0 carp "reference for SuccessEvent will be treated as an event name"
399             }
400 101         131 $self->[MY_EVENT_SUCCESS] = $event;
401 101         223 undef $self->[MY_MINE_SUCCESS];
402             }
403             else {
404 0         0 carp "SuccessEvent requires an event name. ignoring undef";
405             }
406             }
407             elsif ($name eq 'FailureEvent') {
408 101 50       912 if (defined $event) {
409 101 50       197 if (ref($event)) {
410 0         0 carp "reference for FailureEvent will be treated as an event name";
411             }
412 101         126 $self->[MY_EVENT_FAILURE] = $event;
413 101         217 undef $self->[MY_MINE_FAILURE];
414             }
415             else {
416 0         0 carp "FailureEvent requires an event name. ignoring undef";
417             }
418             }
419             else {
420 0         0 carp "ignoring unknown SocketFactory parameter '$name'";
421             }
422             }
423              
424 101         136 $self->[MY_SOCKET_SELECTED] = 'yes';
425 101 100       265 if (defined $self->[MY_STATE_ACCEPT]) {
    50          
426 22         74 $poe_kernel->select_read(
427             $self->[MY_SOCKET_HANDLE],
428             $self->[MY_STATE_ACCEPT]
429             );
430             }
431             elsif (defined $self->[MY_STATE_CONNECT]) {
432 79         223 $poe_kernel->select_write(
433             $self->[MY_SOCKET_HANDLE],
434             $self->[MY_STATE_CONNECT]
435             );
436 79 50 33     464 if ($^O eq "cygwin" or $^O eq "MSWin32") {
437 0         0 $poe_kernel->select_expedite(
438             $self->[MY_SOCKET_HANDLE],
439             $self->[MY_STATE_ERROR]
440             );
441             }
442             }
443             else {
444 0         0 die "POE developer error - no state defined";
445             }
446             }
447              
448             #------------------------------------------------------------------------------
449              
450             sub getsockname {
451 24     24 1 147 my $self = shift;
452             return undef unless (
453 24 50 33     242 defined $self->[MY_SOCKET_HANDLE] and
454             fileno($self->[MY_SOCKET_HANDLE])
455             );
456 24         243 return getsockname($self->[MY_SOCKET_HANDLE]);
457             }
458              
459             sub ID {
460 10     10 1 66 return $_[0]->[MY_UNIQUE_ID];
461             }
462              
463             #------------------------------------------------------------------------------
464              
465             sub new {
466 103     103 1 395 my $type = shift;
467              
468             # Don't take responsibility for a bad parameter count.
469 103 50       253 croak "$type requires an even number of parameters" if @_ & 1;
470              
471 103         437 my %params = @_;
472              
473             # The calling convention experienced a hard deprecation.
474 103 50 33     503 croak "wheels no longer require a kernel reference as their first parameter"
475             if (@_ && (ref($_[0]) eq 'POE::Kernel'));
476              
477             # Ensure some of the basic things are present.
478 103 50       208 croak "$type requires a working Kernel" unless (defined $poe_kernel);
479 103 50       218 croak 'SuccessEvent required' unless (defined $params{SuccessEvent});
480 103 50       183 croak 'FailureEvent required' unless (defined $params{FailureEvent});
481 103         121 my $event_success = $params{SuccessEvent};
482 103         109 my $event_failure = $params{FailureEvent};
483              
484             # Create the SocketServer. Cache a copy of the socket handle.
485 103         293 my $socket_handle = gensym();
486 103         1310 my $self = bless(
487             [
488             $socket_handle, # MY_SOCKET_HANDLE
489             &POE::Wheel::allocate_wheel_id(), # MY_UNIQUE_ID
490             $event_success, # MY_EVENT_SUCCESS
491             $event_failure, # MY_EVENT_FAILURE
492             undef, # MY_SOCKET_DOMAIN
493             undef, # MY_STATE_ACCEPT
494             undef, # MY_STATE_CONNECT
495             undef, # MY_MINE_SUCCESS
496             undef, # MY_MINE_FAILURE
497             undef, # MY_SOCKET_PROTOCOL
498             undef, # MY_SOCKET_TYPE
499             undef, # MY_STATE_ERROR
500             undef, # MY_SOCKET_SELECTED
501             ],
502             $type
503             );
504              
505             # Default to Internet sockets.
506 103         272 my $domain = delete $params{SocketDomain};
507 103 100       191 if (defined $domain) {
508             # [rt.cpan.org 76314] Untaint the domain.
509 24         174 ($domain) = ($domain =~ /\A(.*)\z/s);
510             }
511             else {
512 79         104 $domain = AF_INET;
513             }
514 103         258 $self->[MY_SOCKET_DOMAIN] = $domain;
515              
516             # Abstract the socket domain into something we don't have to keep
517             # testing duplicates of.
518 103         195 my $abstract_domain = $map_family_to_domain{$self->[MY_SOCKET_DOMAIN]};
519 103 50       221 unless (defined $abstract_domain) {
520 0         0 $poe_kernel->yield(
521             $event_failure,
522             'domain',
523             0,
524             "SocketDomain $domain is currently unsupported on this platform",
525             $self->[MY_UNIQUE_ID]
526             );
527 0         0 return $self;
528             }
529              
530             #---------------#
531             # Create Socket #
532             #---------------#
533              
534             # Declare the protocol name out here; it'll be needed by
535             # getservbyname later.
536 103         98 my $protocol_name;
537              
538             # Unix sockets don't use protocols; warn the programmer, and force
539             # PF_UNSPEC.
540 103 100 33     530 if ($abstract_domain eq DOM_UNIX) {
    50          
541 4 50       12 carp 'SocketProtocol ignored for Unix socket'
542             if defined $params{SocketProtocol};
543 4         5 $self->[MY_SOCKET_PROTOCOL] = PF_UNSPEC;
544 4         5 $protocol_name = 'none';
545             }
546              
547             # Internet sockets use protocols. Default the INET protocol to tcp,
548             # and try to resolve it.
549             elsif (
550             $abstract_domain eq DOM_INET or
551             $abstract_domain eq DOM_INET6
552             ) {
553 99 100       221 my $socket_protocol = (
554             (defined $params{SocketProtocol})
555             ? $params{SocketProtocol}
556             : 'tcp'
557             );
558              
559              
560 99 50       355 if ($socket_protocol !~ /^\d+$/) {
561 99 50 33     333 unless ($socket_protocol = $proto_by_name{$socket_protocol} || eval { getprotobyname($socket_protocol) }) {
562 0         0 $poe_kernel->yield(
563             $event_failure, 'getprotobyname', $!+0, $!, $self->[MY_UNIQUE_ID]
564             );
565 0         0 return $self;
566             }
567             }
568              
569             # Get the protocol's name regardless of what was provided. If the
570             # protocol isn't supported, croak now instead of making the
571             # programmer wonder why things fail later.
572 99   33     428 $protocol_name = $proto_by_number{$socket_protocol} || eval { lc(getprotobynumber($socket_protocol)) };
573 99 50       178 unless ($protocol_name) {
574 0         0 $poe_kernel->yield(
575             $event_failure, 'getprotobynumber', $!+0, $!, $self->[MY_UNIQUE_ID]
576             );
577 0         0 return $self;
578             }
579              
580 99 50       255 unless (defined $supported_protocol{$abstract_domain}->{$protocol_name}) {
581 0         0 croak "SocketFactory does not support Internet $protocol_name sockets";
582             }
583              
584 99         156 $self->[MY_SOCKET_PROTOCOL] = $socket_protocol;
585             }
586             else {
587 0         0 die "Mail this error to the author of POE: Internal consistency error";
588             }
589              
590             # If no SocketType, default it to something appropriate.
591 103 50       197 if (defined $params{SocketType}) {
592 0         0 $self->[MY_SOCKET_TYPE] = $params{SocketType};
593             }
594             else {
595 103 50       273 unless (defined $default_socket_type{$abstract_domain}->{$protocol_name}) {
596 0         0 croak "SocketFactory does not support $abstract_domain $protocol_name";
597             }
598 103         167 $self->[MY_SOCKET_TYPE] =
599             $default_socket_type{$abstract_domain}->{$protocol_name};
600             }
601              
602             # o create a dummy socket
603             # o cache the value of SO_OPENTYPE in $win32_socket_opt
604             # o set the overlapped io attribute
605             # o close dummy socket
606 103         238 my $win32_socket_opt;
607 103 50       267 if ( POE::Kernel::RUNNING_IN_HELL) {
608              
609             # Constants are evaluated first so they exist when the code uses
610             # them.
611 0         0 eval {
612 0         0 *SO_OPENTYPE = sub () { 0x7008 };
613 0         0 *SO_SYNCHRONOUS_ALERT = sub () { 0x10 };
614 0         0 *SO_SYNCHRONOUS_NONALERT = sub () { 0x20 };
615             };
616 0 0       0 die "Could not install SO constants [$@]" if $@;
617              
618             # Turn on socket overlapped IO attribute per MSKB: Q181611.
619              
620 0         0 eval {
621 0 0       0 socket(POE, AF_INET, SOCK_STREAM, IPPROTO_TCP)
622             or die "socket failed: $!";
623 0         0 my $opt = unpack("I", getsockopt(POE, SOL_SOCKET, SO_OPENTYPE()));
624 0         0 $win32_socket_opt = $opt;
625 0         0 $opt &= ~(SO_SYNCHRONOUS_ALERT()|SO_SYNCHRONOUS_NONALERT());
626 0         0 setsockopt(POE, SOL_SOCKET, SO_OPENTYPE(), $opt);
627 0         0 close POE;
628             };
629              
630 0 0       0 die if $@;
631             }
632              
633             # Create the socket.
634 103 50       2453 unless (
635             socket( $socket_handle, $self->[MY_SOCKET_DOMAIN],
636             $self->[MY_SOCKET_TYPE], $self->[MY_SOCKET_PROTOCOL]
637             )
638             ) {
639 0         0 $poe_kernel->yield(
640             $event_failure, 'socket', $!+0, $!, $self->[MY_UNIQUE_ID]
641             );
642 0         0 return $self;
643             }
644              
645             # o create a dummy socket
646             # o restore previous value of SO_OPENTYPE
647             # o close dummy socket
648             #
649             # This way we'd only be turning on the overlap attribute for
650             # the socket we created... and not all subsequent sockets.
651 103 50       223 if ( POE::Kernel::RUNNING_IN_HELL) {
652 0         0 eval {
653 0 0       0 socket(POE, AF_INET, SOCK_STREAM, IPPROTO_TCP)
654             or die "socket failed: $!";
655 0         0 setsockopt(POE, SOL_SOCKET, SO_OPENTYPE(), $win32_socket_opt);
656 0         0 close POE;
657             };
658              
659 0 0       0 die if $@;
660             }
661 103         120 DEBUG && warn "socket";
662              
663             #------------------#
664             # Configure Socket #
665             #------------------#
666              
667             # Make the socket binary. It's wrapped in eval{} because tied
668             # filehandle classes may actually die in their binmode methods.
669 103         127 eval { binmode($socket_handle) };
  103         231  
670              
671             # Don't block on socket operations, because the socket will be
672             # driven by a select loop.
673 103         480 $socket_handle->blocking(0);
674              
675             # Make the socket reusable, if requested.
676 103 0 33     375 if (
      66        
677             (defined $params{Reuse})
678             and ( (lc($params{Reuse}) eq 'yes')
679             or (lc($params{Reuse}) eq 'on')
680             or ( ($params{Reuse} =~ /\d+/)
681             and $params{Reuse}
682             )
683             )
684             )
685             {
686 24 50       183 setsockopt($socket_handle, SOL_SOCKET, SO_REUSEADDR, 1) or do {
687 0         0 $poe_kernel->yield(
688             $event_failure,
689             'setsockopt', $!+0, $!, $self->[MY_UNIQUE_ID]
690             );
691 0         0 return $self;
692             };
693             }
694              
695             #-------------#
696             # Bind Socket #
697             #-------------#
698              
699 103         151 my $bind_address;
700              
701             # Check SocketFactory /Bind.*/ parameters in an Internet socket
702             # context, and translate them into parameters that bind()
703             # understands.
704 103 100       222 if ($abstract_domain eq DOM_INET) {
    50          
    50          
705             # Don't bind if the creator doesn't specify a related parameter.
706 99 100 66     431 if ((defined $params{BindAddress}) or (defined $params{BindPort})) {
707              
708             # Set the bind address, or default to INADDR_ANY.
709 24 50       76 $bind_address = (
710             (defined $params{BindAddress})
711             ? $params{BindAddress}
712             : INADDR_ANY
713             );
714              
715             # Need to check lengths in octets, not characters.
716 23 50   23   43 BEGIN { eval { require bytes } and bytes->import; }
  23         839  
717              
718             # Resolve the bind address if it's not already packed.
719 24 50       66 unless (length($bind_address) == 4) {
720 24         1133 $bind_address = inet_aton($bind_address);
721             }
722              
723 24 50       82 unless (defined $bind_address) {
724 0         0 $! = EADDRNOTAVAIL;
725 0         0 $poe_kernel->yield(
726             $event_failure,
727             "inet_aton", $!+0, $!, $self->[MY_UNIQUE_ID]
728             );
729 0         0 return $self;
730             }
731              
732             # Set the bind port, or default to 0 (any) if none specified.
733             # Resolve it to a number, if at all possible.
734 24 50       126 my $bind_port = (defined $params{BindPort}) ? $params{BindPort} : 0;
735 24 50       98 if ($bind_port =~ /[^0-9]/) {
736 0         0 $bind_port = getservbyname($bind_port, $protocol_name);
737 0 0       0 unless (defined $bind_port) {
738 0         0 $! = EADDRNOTAVAIL;
739 0         0 $poe_kernel->yield(
740             $event_failure,
741             'getservbyname', $!+0, $!, $self->[MY_UNIQUE_ID]
742             );
743 0         0 return $self;
744             }
745             }
746              
747 24         106 $bind_address = pack_sockaddr_in($bind_port, $bind_address);
748 24 50       117 unless (defined $bind_address) {
749 0         0 $poe_kernel->yield(
750             $event_failure,
751             "pack_sockaddr_in", $!+0, $!, $self->[MY_UNIQUE_ID]
752             );
753 0         0 return $self;
754             }
755             }
756             }
757              
758             # Check SocketFactory /Bind.*/ parameters in an Internet socket
759             # context, and translate them into parameters that bind()
760             # understands.
761             elsif ($abstract_domain eq DOM_INET6) {
762              
763             # Don't bind if the creator doesn't specify a related parameter.
764 0 0 0     0 if ((defined $params{BindAddress}) or (defined $params{BindPort})) {
765              
766             # Set the bind address, or default to INADDR_ANY.
767 0 0       0 $bind_address = (
768             (defined $params{BindAddress})
769             ? $params{BindAddress}
770             : "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" # XXX - Only Socket6 has?
771             );
772              
773             # Set the bind port, or default to 0 (any) if none specified.
774             # Resolve it to a number, if at all possible.
775 0 0       0 my $bind_port = (defined $params{BindPort}) ? $params{BindPort} : 0;
776 0 0       0 if ($bind_port =~ /[^0-9]/) {
777 0         0 $bind_port = getservbyname($bind_port, $protocol_name);
778 0 0       0 unless (defined $bind_port) {
779 0         0 $! = EADDRNOTAVAIL;
780 0         0 $poe_kernel->yield(
781             $event_failure,
782             'getservbyname', $!+0, $!, $self->[MY_UNIQUE_ID]
783             );
784 0         0 return $self;
785             }
786             }
787              
788             # Need to check lengths in octets, not characters.
789 23 50   23   7211 BEGIN { eval { require bytes } and bytes->import; }
  23         210  
790              
791             # Resolve the bind address.
792 0         0 my ($error, @addresses) = getaddrinfo(
793             $bind_address, $bind_port, {
794             family => $self->[MY_SOCKET_DOMAIN],
795             socktype => $self->[MY_SOCKET_TYPE],
796             }
797             );
798              
799 0 0       0 unless (@addresses) {
800 0 0       0 warn $error if $error;
801              
802 0         0 $! = EADDRNOTAVAIL;
803 0         0 $poe_kernel->yield(
804             $event_failure,
805             "getaddrinfo", $!+0, $!, $self->[MY_UNIQUE_ID]
806             );
807 0         0 return $self;
808             }
809              
810 0         0 $bind_address = $addresses[0]->{addr};
811             }
812             }
813              
814             # Check SocketFactory /Bind.*/ parameters in a Unix context, and
815             # translate them into parameters bind() understands.
816             elsif ($abstract_domain eq DOM_UNIX) {
817 4 50       8 carp 'BindPort ignored for Unix socket' if defined $params{BindPort};
818              
819 4 100       8 if (defined $params{BindAddress}) {
820             # Is this necessary, or will bind() return EADDRINUSE?
821 2 50       4 if (defined $params{RemotePort}) {
822 0         0 $! = EADDRINUSE;
823 0         0 $poe_kernel->yield(
824             $event_failure,
825             'bind', $!+0, $!, $self->[MY_UNIQUE_ID]
826             );
827 0         0 return $self;
828             }
829              
830 2         6 $bind_address = &_condition_unix_address($params{BindAddress});
831 2         15 $bind_address = pack_sockaddr_un($bind_address);
832 2 50       5 unless ($bind_address) {
833 0         0 $poe_kernel->yield(
834             $event_failure,
835             'pack_sockaddr_un', $!+0, $!, $self->[MY_UNIQUE_ID]
836             );
837 0         0 return $self;
838             }
839             }
840             }
841              
842             # This is an internal consistency error, and it should be hard
843             # trapped right away.
844             else {
845 0         0 die "Mail this error to the author of POE: Internal consistency error";
846             }
847              
848             # Perform the actual bind, if there's a bind address to bind to.
849 103 100       205 if (defined $bind_address) {
850 26 50       352 unless (bind($socket_handle, $bind_address)) {
851 0         0 $poe_kernel->yield(
852             $event_failure,
853             'bind', $!+0, $!, $self->[MY_UNIQUE_ID]
854             );
855 0         0 return $self;
856             }
857              
858 26         35 DEBUG && warn "bind";
859             }
860              
861             #---------#
862             # Connect #
863             #---------#
864              
865 103         122 my $connect_address;
866              
867 103 100       182 if (defined $params{RemoteAddress}) {
868              
869             # Check SocketFactory /Remote.*/ parameters in an Internet socket
870             # context, and translate them into parameters that connect()
871             # understands.
872 77 100 66     226 if (
    50          
873             $abstract_domain eq DOM_INET or
874             $abstract_domain eq DOM_INET6
875             ) {
876             # connecting if RemoteAddress
877 75 50       149 croak 'RemotePort required' unless (defined $params{RemotePort});
878 75 50       141 carp 'ListenQueue ignored' if (defined $params{ListenQueue});
879              
880 75         91 my $remote_port = $params{RemotePort};
881 75 50       267 if ($remote_port =~ /[^0-9]/) {
882 0 0       0 unless ($remote_port = getservbyname($remote_port, $protocol_name)) {
883 0         0 $! = EADDRNOTAVAIL;
884 0         0 $poe_kernel->yield(
885             $event_failure,
886             'getservbyname', $!+0, $!, $self->[MY_UNIQUE_ID]
887             );
888 0         0 return $self;
889             }
890             }
891              
892 75         81 my $error_tag;
893 75 50       129 if ($abstract_domain eq DOM_INET) {
    0          
894 75         1569 $connect_address = inet_aton($params{RemoteAddress});
895 75         119 $error_tag = "inet_aton";
896             }
897             elsif ($abstract_domain eq DOM_INET6) {
898 0         0 my ($error, @addresses) = getaddrinfo(
899             $params{RemoteAddress}, $remote_port, {
900             family => $self->[MY_SOCKET_DOMAIN],
901             socktype => $self->[MY_SOCKET_TYPE],
902             },
903             );
904              
905 0 0       0 unless (@addresses) {
906 0 0       0 warn $error if $error;
907 0         0 $connect_address = undef;
908             }
909             else {
910 0         0 $connect_address = $addresses[0]->{addr};
911             }
912              
913 0         0 $error_tag = "getaddrinfo";
914             }
915             else {
916 0         0 die "unknown domain $abstract_domain";
917             }
918              
919             # TODO - If the gethostbyname2() code is removed, then we can
920             # combine the previous code with the following code, and perhaps
921             # remove one of these redundant $connect_address checks. The
922             # 0.29 release should tell us pretty quickly whether it's
923             # needed. If we reach 0.30 without incident, it's probably safe
924             # to remove the old gethostbyname2() code and clean this up.
925 75 50       165 unless (defined $connect_address) {
926 0         0 $! = EADDRNOTAVAIL;
927 0         0 $poe_kernel->yield(
928             $event_failure,
929             $error_tag, $!+0, $!, $self->[MY_UNIQUE_ID]
930             );
931 0         0 return $self;
932             }
933              
934 75 50       150 if ($abstract_domain eq DOM_INET) {
    0          
935 75         194 $connect_address = pack_sockaddr_in($remote_port, $connect_address);
936 75         112 $error_tag = "pack_sockaddr_in";
937             }
938             elsif ($abstract_domain eq DOM_INET6) {
939 0         0 $error_tag = "pack_sockaddr_in6";
940             }
941             else {
942 0         0 die "unknown domain $abstract_domain";
943             }
944              
945 75 50       169 unless ($connect_address) {
946 0         0 $! = EADDRNOTAVAIL;
947 0         0 $poe_kernel->yield(
948             $event_failure,
949             $error_tag, $!+0, $!, $self->[MY_UNIQUE_ID]
950             );
951 0         0 return $self;
952             }
953             }
954              
955             # Check SocketFactory /Remote.*/ parameters in a Unix socket
956             # context, and translate them into parameters connect()
957             # understands.
958             elsif ($abstract_domain eq DOM_UNIX) {
959              
960 2         6 $connect_address = _condition_unix_address($params{RemoteAddress});
961 2         6 $connect_address = pack_sockaddr_un($connect_address);
962 2 50       7 unless (defined $connect_address) {
963 0         0 $poe_kernel->yield(
964             $event_failure,
965             'pack_sockaddr_un', $!+0, $!, $self->[MY_UNIQUE_ID]
966             );
967 0         0 return $self;
968             }
969             }
970              
971             # This is an internal consistency error, and it should be trapped
972             # right away.
973             else {
974 0         0 die "Mail this error to the author of POE: Internal consistency error";
975             }
976             }
977              
978             else {
979 26 50       91 carp "RemotePort ignored without RemoteAddress"
980             if defined $params{RemotePort};
981             }
982              
983             # Perform the actual connection, if a connection was requested. If
984             # the connection can be established, then return the SocketFactory
985             # handle.
986 103 100       225 if (defined $connect_address) {
987 77 100       4767 unless (connect($socket_handle, $connect_address)) {
988 75 50 33     902 if ($! and ($! != EINPROGRESS) and ($! != EWOULDBLOCK)) {
      33        
989 0         0 $poe_kernel->yield(
990             $event_failure,
991             'connect', $!+0, $!, $self->[MY_UNIQUE_ID]
992             );
993 0         0 return $self;
994             }
995             }
996              
997 77         75 DEBUG && warn "connect";
998              
999 77         140 $self->[MY_SOCKET_HANDLE] = $socket_handle;
1000 77         236 $self->_define_connect_state();
1001 77         280 $self->event(
1002             SuccessEvent => $params{SuccessEvent},
1003             FailureEvent => $params{FailureEvent},
1004             );
1005 77         410 return $self;
1006             }
1007              
1008             #---------------------#
1009             # Listen, or Whatever #
1010             #---------------------#
1011              
1012             # A connection wasn't requested, so this must be a server socket.
1013             # Do whatever it is that needs to be done for whatever type of
1014             # server socket this is.
1015 26 50       88 if (exists $supported_protocol{$abstract_domain}->{$protocol_name}) {
1016 26         57 my $protocol_op = $supported_protocol{$abstract_domain}->{$protocol_name};
1017              
1018 26         27 DEBUG && warn "$abstract_domain + $protocol_name = $protocol_op";
1019              
1020 26 100       67 if ($protocol_op eq SVROP_LISTENS) {
1021 22   50     114 my $listen_queue = $params{ListenQueue} || SOMAXCONN;
1022             # In SocketFactory, you limit the ListenQueue parameter
1023             # to SOMAXCON (or is it SOCONNMAX?)...why?
1024             # ah, here's czth, he'll have more to say on this issue
1025             # not really. just that SOMAXCONN can lie, notably on
1026             # Solaris and reportedly on BSDs too
1027             #
1028             # ($listen_queue > SOMAXCONN) && ($listen_queue = SOMAXCONN);
1029 22 50       264 unless (listen($socket_handle, $listen_queue)) {
1030 0         0 $poe_kernel->yield(
1031             $event_failure,
1032             'listen', $!+0, $!, $self->[MY_UNIQUE_ID]
1033             );
1034 0         0 return $self;
1035             }
1036              
1037 22         28 DEBUG && warn "listen";
1038              
1039 22         47 $self->[MY_SOCKET_HANDLE] = $socket_handle;
1040 22         75 $self->_define_accept_state();
1041 22         90 $self->event(
1042             SuccessEvent => $params{SuccessEvent},
1043             FailureEvent => $params{FailureEvent},
1044             );
1045 22         132 return $self;
1046             }
1047             else {
1048 4 50       14 carp "Ignoring ListenQueue parameter for non-listening socket"
1049             if defined $params{ListenQueue};
1050 4 50       8 if ($protocol_op eq SVROP_NOTHING) {
1051             # Do nothing. Duh. Fire off a success event immediately, and
1052             # return.
1053 4         20 $poe_kernel->yield(
1054             $event_success,
1055             $socket_handle, undef, undef, $self->[MY_UNIQUE_ID]
1056             );
1057 4         21 return $self;
1058             }
1059             else {
1060 0         0 die "Mail this error to the author of POE: Internal consistency error";
1061             }
1062             }
1063             }
1064             else {
1065 0         0 die "SocketFactory doesn't support $abstract_domain $protocol_name socket";
1066             }
1067              
1068 0         0 die "Mail this error to the author of POE: Internal consistency error";
1069             }
1070              
1071             # Pause and resume accept.
1072             sub pause_accept {
1073 24     24 1 30 my $self = shift;
1074 24 50 33     169 if (
      33        
1075             defined $self->[MY_SOCKET_HANDLE] and
1076             defined $self->[MY_STATE_ACCEPT] and
1077             defined $self->[MY_SOCKET_SELECTED]
1078             ) {
1079 24         71 $poe_kernel->select_pause_read($self->[MY_SOCKET_HANDLE]);
1080             }
1081             }
1082              
1083             sub resume_accept {
1084 22     22 1 27 my $self = shift;
1085 22 50 33     167 if (
      33        
1086             defined $self->[MY_SOCKET_HANDLE] and
1087             defined $self->[MY_STATE_ACCEPT] and
1088             defined $self->[MY_SOCKET_SELECTED]
1089             ) {
1090 22         71 $poe_kernel->select_resume_read($self->[MY_SOCKET_HANDLE]);
1091             }
1092             }
1093              
1094             #------------------------------------------------------------------------------
1095             # DESTROY and _shutdown pass things by reference because _shutdown is
1096             # called from the state() closures above. As a result, we can't
1097             # mention $self explicitly, or the wheel won't shut itself down
1098             # properly. Rather, it will form a circular reference on $self.
1099              
1100             sub DESTROY {
1101 103     103   1077 my $self = shift;
1102 103         484 _shutdown(
1103             \$self->[MY_SOCKET_SELECTED],
1104             \$self->[MY_SOCKET_HANDLE],
1105             \$self->[MY_STATE_ACCEPT],
1106             \$self->[MY_STATE_CONNECT],
1107             \$self->[MY_MINE_SUCCESS],
1108             \$self->[MY_EVENT_SUCCESS],
1109             \$self->[MY_MINE_FAILURE],
1110             \$self->[MY_EVENT_FAILURE],
1111             );
1112 103         366 &POE::Wheel::free_wheel_id($self->[MY_UNIQUE_ID]);
1113             }
1114              
1115             sub _shutdown {
1116             my (
1117 180     180   292 $socket_selected, $socket_handle,
1118             $state_accept, $state_connect,
1119             $mine_success, $event_success,
1120             $mine_failure, $event_failure,
1121             ) = @_;
1122              
1123 180 100       405 if (defined $$socket_selected) {
1124 99         301 $poe_kernel->select($$socket_handle);
1125 99         165 $$socket_selected = undef;
1126             }
1127              
1128 180 100       380 if (defined $$state_accept) {
1129 22         138 $poe_kernel->state($$state_accept);
1130 22         39 $$state_accept = undef;
1131             }
1132              
1133 180 100       355 if (defined $$state_connect) {
1134 77         534 $poe_kernel->state($$state_connect);
1135 77         96 $$state_connect = undef;
1136             }
1137              
1138 180 50       399 if (defined $$mine_success) {
1139 0         0 $poe_kernel->state($$event_success);
1140 0         0 $$mine_success = $$event_success = undef;
1141             }
1142              
1143 180 50       405 if (defined $$mine_failure) {
1144 0           $poe_kernel->state($$event_failure);
1145 0           $$mine_failure = $$event_failure = undef;
1146             }
1147             }
1148              
1149             1;
1150              
1151             __END__