File Coverage

blib/lib/POE/Component/Client/Ping.pm
Criterion Covered Total %
statement 206 233 88.4
branch 65 98 66.3
condition 22 33 66.6
subroutine 23 25 92.0
pod 0 7 0.0
total 316 396 79.8


line stmt bran cond sub pod time code
1             # License and documentation are after __END__.
2             # vim: ts=2 sw=2 expandtab
3              
4             package POE::Component::Client::Ping;
5              
6 6     6   613232 use warnings;
  6         49  
  6         263  
7 6     6   34 use strict;
  6         13  
  6         128  
8              
9 6     6   44 use Exporter;
  6         13  
  6         235  
10 6     6   33 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS);
  6         12  
  6         592  
11              
12             @ISA = qw(Exporter);
13             @EXPORT_OK = qw(
14             REQ_ADDRESS REQ_TIMEOUT REQ_TIME REQ_USER_ARGS
15             RES_ADDRESS RES_ROUNDTRIP RES_TIME RES_TTL
16             );
17             %EXPORT_TAGS = (
18             const => [
19             qw(
20             REQ_ADDRESS REQ_TIMEOUT REQ_TIME REQ_USER_ARGS
21             RES_ADDRESS RES_ROUNDTRIP RES_TIME RES_TTL
22             )
23             ]
24             );
25              
26 6     6   41 use vars qw($VERSION $PKTSIZE);
  6         13  
  6         471  
27             $VERSION = '1.176';
28             $PKTSIZE = $^O eq 'linux' ? 3_000 : 100;
29              
30 6     6   38 use Carp qw(croak);
  6         12  
  6         318  
31 6     6   41 use Symbol qw(gensym);
  6         10  
  6         258  
32 6     6   36 use Socket;
  6         10  
  6         3244  
33 6     6   45 use Time::HiRes qw(time);
  6         12  
  6         52  
34              
35 6     6   1181 use POE::Session;
  6         13  
  6         30  
36              
37             sub DEBUG () { 0 } # Enable more information.
38             sub DEBUG_SOCKET () { 0 } # Watch the socket open and close.
39             sub DEBUG_PBS () { 0 } # Watch ping_by_seq management.
40              
41             # ping_by_seq structure offsets.
42              
43             sub PBS_POSTBACK () { 0 };
44             sub PBS_SESSION () { 1 };
45             sub PBS_ADDRESS () { 2 };
46             sub PBS_REQUEST_TIME () { 3 };
47              
48             # request_packet offsets
49             sub REQ_ADDRESS () { 0 };
50             sub REQ_TIMEOUT () { 1 };
51             sub REQ_TIME () { 2 };
52             sub REQ_USER_ARGS () { 3 };
53              
54             # response_packet offsets
55             sub RES_ADDRESS () { 0 };
56             sub RES_ROUNDTRIP () { 1 };
57             sub RES_TIME () { 2 };
58             sub RES_TTL () { 3 };
59              
60             # ICMP echo constants. Types, structures, and fields. Cribbed
61             # mercilessly from Net::Ping.
62              
63             sub ICMP_ECHOREPLY () { 0 }
64             sub ICMP_ECHO () { 8 }
65             sub ICMP_STRUCT () { 'C2 S3 A' }
66             sub ICMP_SUBCODE () { 0 }
67             sub ICMP_FLAGS () { 0 }
68             sub ICMP_PORT () { 0 }
69              
70             # "Static" variables which will be shared across multiple instances.
71              
72             my $master_seq = 0;
73              
74             # Spawn a new PoCo::Client::Ping session. This basically is a
75             # constructor, but it isn't named "new" because it doesn't create a
76             # usable object. Instead, it spawns the object off as a session.
77             # Randal Schwartz gave me heck about calling spawny things "new", so I
78             # blame him for this naming convention.
79              
80             sub spawn {
81 5     5 0 511 my $type = shift;
82              
83 5 50       30 croak "$type requires an even number of parameters" if @_ % 2;
84 5         26 my %params = @_;
85              
86 5 50       23 croak "$type requires root privilege" unless can_open_socket();
87              
88 5         22 my $alias = delete $params{Alias};
89 5 100 66     43 $alias = "pinger" unless defined $alias and length $alias;
90              
91 5         14 my $timeout = delete $params{Timeout};
92 5 100 66     26 $timeout = 1 unless defined $timeout and $timeout >= 0;
93              
94 5         11 my $onereply = delete $params{OneReply};
95 5         12 my $socket = delete $params{Socket};
96 5   100     22 my $parallelism = delete $params{Parallelism} || -1;
97 5         14 my $rcvbuf = delete $params{BufferSize};
98 5         11 my $always_decode = delete $params{AlwaysDecodeAddress};
99 5         11 my $retry = delete $params{Retry};
100 5         10 my $payload = delete $params{Payload};
101              
102             # 56 data bytes :)
103 5 50       18 $payload = 'Use POE!' x 7 unless defined $payload;
104              
105 5 50       22 croak(
106             "$type doesn't know these parameters: ", join(', ', sort keys %params)
107             ) if scalar keys %params;
108              
109 5   100     157 POE::Session->create(
      100        
110             inline_states => {
111             _start => \&poco_ping_start,
112             ping => \&poco_ping_ping,
113             clear => \&poco_ping_clear,
114             got_pong => \&poco_ping_pong,
115             _default => \&poco_ping_default,
116             },
117             heap => {
118             alias => $alias,
119             always_decode => $always_decode,
120             data => $payload,
121             data_size => length($payload),
122             keep_socket => (defined $socket) || 0,
123             onereply => $onereply,
124             rcvbuf => $rcvbuf,
125             retry => $retry || 0,
126             socket_handle => $socket,
127             timeout => $timeout,
128              
129             # Active query tracking.
130             ping_by_seq => { }, # keyed on sequence number
131             addr_to_seq => { }, # keyed on request address, then sender
132              
133             # Queue to manage throttling.
134             parallelism => $parallelism, # how many pings can we send at once
135             queue => [ ], # ordered list of throttled pings
136             pending => { }, # data for the sequence ids of queued pings
137             outstanding => 0, # How many pings are we awaiting replies for
138             },
139             );
140              
141 5         1198 undef;
142             }
143              
144              
145             # Start the pinger session.
146              
147             sub poco_ping_start {
148 5     5 0 1527 $_[KERNEL]->alias_set( $_[HEAP]->{alias} );
149             }
150              
151              
152             # (NOT A POE EVENT HANDLER)
153             # Test whether this process can open raw sockets.
154             sub can_open_socket {
155 10     10 0 575 my $socket = eval { _create_handle() };
  10         31  
156 10 50 33     88 return 0 if $@ or not $socket;
157 10         255 return 1;
158             }
159              
160              
161             # (NOT A POE EVENT HANDLER)
162             # Create a raw socket to send ICMP packets down.
163             # (optionally) mess with the size of the buffers on the socket.
164              
165             sub _create_handle {
166 14     14   21 DEBUG_SOCKET and warn "opening a raw socket for icmp";
167              
168 14         27 my $protocol = Socket::IPPROTO_ICMP;
169              
170 14         58 my $socket = gensym();
171 14 50       838 socket($socket, PF_INET, SOCK_RAW, $protocol)
172             or die "can't create icmp socket: $!";
173              
174 14         57 return $socket;
175             }
176              
177             ### NOT A POE EVENT HANDLER
178              
179             sub _setup_handle {
180 9     9   27 my ($kernel, $heap) = @_;
181              
182 9 100       29 if ($heap->{rcvbuf}) {
183 5 50       120 unless (
184             setsockopt(
185             $heap->{socket_handle}, SOL_SOCKET,
186             SO_RCVBUF, pack("I", $heap->{rcvbuf})
187             )
188             ) {
189 0         0 warn("setsockopt rcvbuf size ($heap->{rcvbuf}) failed: $!");
190             }
191             }
192              
193 9 100 66     101 if ($heap->{parallelism} && $heap->{parallelism} == -1) {
194 3         57 my $rcvbuf = getsockopt($heap->{socket_handle}, SOL_SOCKET, SO_RCVBUF);
195 3 50       15 if ($rcvbuf) {
196 3         19 my $size = unpack("I", $rcvbuf);
197 3         17 my $max_parallel = int($size / $PKTSIZE);
198 3 50       14 if ($max_parallel > 8) {
    0          
199 3         9 $max_parallel -= 8;
200             }
201             elsif ($max_parallel < 1) {
202 0         0 $max_parallel = 1;
203             }
204 3         21 $heap->{parallelism} = $max_parallel;
205             }
206             }
207              
208 9         64 $kernel->select_read($heap->{socket_handle}, 'got_pong');
209             }
210              
211             # Request a ping. This code borrows heavily from Net::Ping.
212              
213             sub poco_ping_ping {
214             my (
215 51     51 0 14703 $kernel, $heap, $sender,
216             $event, $address, $timeout, $tries_left
217             ) = @_[
218             KERNEL, HEAP, SENDER,
219             ARG0, ARG1, ARG2, ARG3
220             ];
221              
222 51   66     258 $tries_left ||= $heap->{retry};
223              
224 51         70 DEBUG and warn "ping requested for $address ($tries_left try/tries left)\n";
225              
226 51         129 _do_ping($kernel, $heap, $sender, $event, $address, $timeout, $tries_left);
227             }
228              
229              
230             sub _do_ping {
231 55     55   133 my ($kernel, $heap, $sender, $event, $address, $timeout, $tries_left) = @_;
232              
233             # No current pings. Open a socket, or setup the existing one.
234 55 100       82 unless (scalar(keys %{$heap->{ping_by_seq}})) {
  55         170  
235 9 100       44 unless (defined $heap->{socket_handle}) {
236 4         14 $heap->{socket_handle} = _create_handle();
237             }
238 9         45 _setup_handle($kernel, $heap);
239             }
240              
241             # Get the timeout, or default to the one set for the component.
242 55 100 66     1193 $timeout = $heap->{timeout} unless defined $timeout and $timeout > 0;
243 55 50       126 $tries_left = $heap->{retry} unless defined $tries_left;
244              
245             # Find an unused sequence number.
246 55         89 while (1) {
247 55         110 $master_seq = ($master_seq + 1) & 0xFFFF;
248 55 50       147 last unless exists $heap->{ping_by_seq}->{$master_seq};
249             }
250              
251 55         87 my $checksum = 0;
252              
253             # Build the message without a checksum.
254             my $msg = pack(
255             ICMP_STRUCT . $heap->{data_size},
256             ICMP_ECHO, ICMP_SUBCODE,
257             $checksum, ($$ & 0xFFFF), $master_seq, $heap->{data}
258 55         335 );
259              
260             ### Begin checksum calculation section.
261              
262             # Sum up short integers in the packet.
263 55         157 my $shorts = int(length($msg) / 2);
264 55         264 foreach my $short (unpack "S$shorts", $msg) {
265 1760         2350 $checksum += $short;
266             }
267              
268             # If there's an odd byte, add that in as well.
269 55 50       178 $checksum += ord(substr($msg, -1)) if length($msg) % 2;
270              
271             # Fold the high short into the low one twice, and then complement.
272 55         105 $checksum = ($checksum >> 16) + ($checksum & 0xFFFF);
273 55         100 $checksum = ~( ($checksum >> 16) + $checksum) & 0xFFFF;
274              
275             ### Cease checksum calculation section.
276              
277             # Rebuild the message with the checksum this time.
278             $msg = pack(
279             ICMP_STRUCT . $heap->{data_size},
280             ICMP_ECHO, ICMP_SUBCODE, $checksum, ($$ & 0xFFFF), $master_seq,
281             $heap->{data}
282 55         210 );
283              
284             # Record information about the ping request.
285              
286 55         106 my ($event_name, @user_args);
287 55 100       128 if (ref($event) eq "ARRAY") {
288 1         4 ($event_name, @user_args) = @$event;
289             }
290             else {
291 54         92 $event_name = $event;
292             }
293              
294             # Build an address to send the ping at.
295             # TODO - This blocks, so resolve them first.
296             # TODO - This assumes four-octet addresses are IPv4.
297              
298 55         89 my $usable_address = $address;
299 55 50 33     209 if ($heap->{always_decode} || length($address) != 4) {
300 55         37222 $usable_address = inet_aton($address);
301             }
302              
303             # Return failure if an address was not resolvable. This simulates
304             # the postback behavior.
305              
306 55 50       147 unless (defined $usable_address) {
307 0         0 $kernel->post(
308             $sender, $event_name,
309             [
310             $address, # REQ_ADDRESS
311             $timeout, # REQ_TIMEOUT
312             time(), # REQ_TIME
313             @user_args, # REQ_USER_ARGS
314             ],
315             [
316             undef, # RES_ADDRESS
317             undef, # RES_ROUNDTRIP
318             time(), # RES_TIME
319             undef, # RES_TTL
320             ],
321             );
322 0         0 _check_for_close($kernel, $heap);
323 0         0 return;
324             }
325              
326 55         165 my $socket_address = pack_sockaddr_in(ICMP_PORT, $usable_address);
327              
328 55         94 push(@{$heap->{queue}}, $master_seq);
  55         156  
329 55         197 $heap->{pending}->{$master_seq} = [
330             $msg, # PEND_MSG
331             $socket_address, # PEND_ADDR
332             $sender, # PEND_SENDER
333             $event, # PEND_EVENT
334             $address, # PEND_ADDR ???
335             $timeout, # PEND_TIMEOUT
336             ];
337              
338 55 100 100     206 if ($tries_left and $tries_left > 1) {
339 4         11 $heap->{retrydata}->{$master_seq} = [
340             $sender, # RD_SENDER
341             $event, # RD_EVENT
342             $address, # RD_ADDRESS
343             $timeout, # RD_TIMEOUT
344             $tries_left, # RD_RETRY
345             ];
346             }
347              
348 55         129 _send_next_packet($kernel, $heap);
349             }
350              
351              
352             sub _send_next_packet {
353 102     102   203 my ($kernel, $heap) = @_;
354 102 100       145 return unless (scalar @{$heap->{queue}});
  102         264  
355              
356 55 50 33     245 if ($heap->{parallelism} && $heap->{outstanding} >= $heap->{parallelism}) {
357             # We want to throttle back since we're still waiting for pings
358             # so, let's just leave this till later
359             DEBUG and warn(
360             "throttled since there are $heap->{outstanding} pings outstanding. " .
361 0         0 "queue size=" . (scalar @{$heap->{queue}}) . "\n"
362             );
363 0         0 return;
364             }
365              
366 55         83 my $seq = shift(@{$heap->{queue}});
  55         116  
367              
368             # May have been cleared by caller
369 55 50       121 return unless (exists $heap->{pending}->{$seq});
370              
371 55         117 my $ping_info = delete $heap->{pending}->{$seq};
372             my (
373 55         127 $msg, # PEND_MSG
374             $socket_address, # PEND_ADDR
375             $sender, # PEND_SENDER
376             $event, # PEND_EVENT
377             $address, # PEND_ADDR ???
378             $timeout, # PEND_TIMEOUT
379             ) = @$ping_info;
380              
381             # Send the packet. If send() fails, then we bail with an error.
382 55         103 my @user_args = ();
383 55 100       124 ($event, @user_args) = @$event if ref($event) eq "ARRAY";
384              
385 55         90 DEBUG and warn "sending packet sequence number $seq\n";
386 55 50       3832 unless (send($heap->{socket_handle}, $msg, ICMP_FLAGS, $socket_address)) {
387 0         0 $kernel->post(
388             $sender, $event,
389             [ $address, # REQ_ADDRESS
390             $timeout, # REQ_TIMEOUT
391             time(), # REQ_TIME
392             @user_args, # REQ_USER_ARGS
393             ],
394             [ undef, # RES_ADDRESS
395             undef, # RES_ROUNDTRIP
396             time(), # RES_TIME
397             undef, # RES_TTL
398             ],
399             );
400 0         0 _check_for_close($kernel, $heap);
401 0         0 return;
402             }
403              
404             # Record the message's length. This is constant, but we do it here
405             # anyway. It's also used to flag when we start requesting replies.
406 55         243 $heap->{message_length} = length($msg);
407 55         92 $heap->{outstanding}++;
408              
409             # Set a timeout based on the sequence number.
410 55         312 $kernel->delay( $seq => $timeout );
411              
412 55         10945 DEBUG_PBS and warn "recording ping_by_seq($seq)";
413 55         201 $heap->{ping_by_seq}->{$seq} = [
414             # PBS_POSTBACK
415             $sender->postback(
416             $event,
417             $address, # REQ_ADDRESS
418             $timeout, # REQ_TIMEOUT
419             time(), # REQ_TIME
420             @user_args, # REQ_USER_ARGS
421             ),
422             "$sender", # PBS_SESSION (stringified to weaken reference)
423             $address, # PBS_ADDRESS
424             time() # PBS_REQUEST_TIME
425             ];
426              
427             # Duplicate pings? Forcibly time out the previous one.
428 55 100       3980 if (exists $heap->{addr_to_seq}->{$sender}->{$address}) {
429 4         6 DEBUG and warn "Duplicate ping. Canceling $address";
430              
431 4         15 my $now = time();
432 4         11 my $ping_info = _end_ping_by_requester_and_address(
433             $kernel, $heap, $sender, $address
434             );
435              
436 4         9 $ping_info->[PBS_POSTBACK]->( undef, undef, $now, undef );
437             }
438              
439 55         860 $heap->{addr_to_seq}->{$sender}->{$address} = $seq;
440             }
441              
442             # Clear a ping postback by address. The sender+address pair are a
443             # unique ID into the pinger's data.
444              
445             sub poco_ping_clear {
446 0     0 0 0 my ($kernel, $heap, $sender, $address) = @_[KERNEL, HEAP, SENDER, ARG0];
447              
448             # Is the sender still waiting for anything?
449 0 0       0 return unless exists $heap->{addr_to_seq}->{$sender};
450              
451             # Try to clear a single ping if an address was specified.
452 0 0       0 if (defined $address) {
453 0         0 _end_ping_by_requester_and_address($kernel, $heap, $sender, $address);
454             }
455              
456             # No address was specified. Clear all the pings for this session.
457             else {
458 0         0 _end_pings_by_requester($kernel, $heap, $sender);
459             }
460              
461 0         0 _check_for_close($kernel, $heap);
462             }
463              
464             # (NOT A POE EVENT HANDLER)
465             # Check to see if no more pings are waiting. Close the socket if so.
466              
467             sub _check_for_close {
468 47     47   84 my ($kernel, $heap) = @_;
469              
470 47 50       133 return unless exists $heap->{socket_handle};
471              
472 47 100       70 return if scalar keys %{$heap->{ping_by_seq}};
  47         143  
473              
474 5         13 DEBUG_SOCKET and warn "stopping raw socket watcher";
475 5         30 $kernel->select_read( $heap->{socket_handle} );
476              
477 5 100       777 return if $heap->{keep_socket};
478              
479 4         16 DEBUG_SOCKET and warn "closing raw socket";
480 4         113 delete $heap->{socket_handle};
481             }
482              
483             # (NOT A POE EVENT HANDLER)
484             # Clean up after we're done with a ping.
485             # Remove it from all tracking hashes.
486             # Determine if the socket should be unthrottled or shut down.
487              
488             sub _end_ping_by_sequence {
489 51     51   207 my ($kernel, $heap, $seq) = @_;
490              
491             # Delete the ping information. Cache a copy for other cleanup.
492 51         96 DEBUG_PBS and warn "removing ping by sequence ($seq)";
493 51         118 my $ping_info = delete $heap->{ping_by_seq}->{$seq};
494 51 50       132 return unless $ping_info;
495              
496             # Stop its associated timeout.
497 51         182 $kernel->delay($seq);
498              
499             # Stop mapping the session+address to this sequence number.
500 51         8228 my $pbs_session = $ping_info->[PBS_SESSION];
501 51         172 delete $heap->{addr_to_seq}->{$pbs_session}->{$ping_info->[PBS_ADDRESS]};
502              
503             # Stop tracking the session if that was its last address.
504             delete $heap->{addr_to_seq}->{$pbs_session} unless (
505 51 100       132 scalar(keys %{$heap->{addr_to_seq}->{$pbs_session}})
  51         224  
506             );
507              
508 51         138 $heap->{outstanding}--;
509              
510 51         130 return $ping_info;
511             }
512              
513              
514             sub _end_ping_by_requester_and_address {
515 4     4   7 my ($kernel, $heap, $sender, $address) = @_;
516              
517 4 50       17 return unless exists $heap->{addr_to_seq}->{$sender};
518 4         9 my $addr_to_seq_rec = $heap->{addr_to_seq}->{$sender};
519              
520 4         9 my $seq = delete $addr_to_seq_rec->{$address};
521 4 50       7 unless ($seq) {
522             # TODO - Why?
523 0         0 delete $heap->{pending}->{$sender}->{$address};
524 0         0 return;
525             }
526              
527             # Stop tracking the sender if that was the last address.
528             delete $heap->{addr_to_seq}->{$sender} unless scalar(
529 4 50       8 keys %{$heap->{addr_to_seq}->{$sender}}
  4         15  
530             );
531              
532             # Discard the postback for the discarded sequence number.
533 4         7 DEBUG_PBS and warn "removing ping_by_seq($seq)";
534 4         6 my $ping_info = delete $heap->{ping_by_seq}->{$seq};
535 4         13 $kernel->delay($seq);
536              
537 4         326 $heap->{outstanding}--;
538              
539 4         9 return $ping_info;
540             }
541              
542              
543             sub _end_pings_by_requester {
544 0     0   0 my ($kernel, $heap, $sender) = @_;
545              
546 0 0       0 return unless exists $heap->{addr_to_seq}->{$sender};
547 0         0 my $addr_to_seq_rec = delete $heap->{addr_to_seq}->{$sender};
548              
549             # Discard cross references.
550              
551 0         0 foreach my $seq (values %$addr_to_seq_rec) {
552 0         0 DEBUG_PBS and warn "removing ping_by_seq($seq)";
553 0         0 delete $heap->{ping_by_seq}->{$seq};
554 0         0 $kernel->delay($seq);
555              
556 0         0 $heap->{outstanding}--;
557             }
558              
559 0         0 return;
560             }
561              
562              
563              
564             # Something has arrived. Try to match it against something being
565             # waited for.
566              
567             sub poco_ping_pong {
568 29     29 0 111430 my ($kernel, $heap, $socket) = @_[KERNEL, HEAP, ARG0];
569              
570             # Record the receive time for possible use later.
571 29         66 my $now = time();
572              
573             # Receive a message on the ICMP port.
574 29         46 my $recv_message = '';
575 29         371 my $from_saddr = recv($socket, $recv_message, 1500, ICMP_FLAGS);
576 29 50       103 return unless $from_saddr;
577              
578             # We haven't yet sent a message, so don't bother with whatever we've
579             # received.
580 29 50       78 return unless defined $heap->{message_length};
581              
582             # Unpack the packet's sender address.
583 29         137 my ($from_port, $from_ip) = unpack_sockaddr_in($from_saddr);
584              
585             # Get the response packet's time to live.
586 29         112 my ($ihl, $from_ttl) = unpack('C1@7C1', $recv_message);
587 29         47 $ihl &= 0x0F;
588              
589             # Unpack the packet itself.
590             my (
591             $from_type, $from_subcode,
592             $from_checksum, $from_pid, $from_seq, $from_message
593             ) = unpack(
594 29         169 '@'.$ihl*4 . ICMP_STRUCT.$heap->{data_size}, $recv_message
595             );
596              
597 29         66 DEBUG and do {
598             warn ",----- packet from ", inet_ntoa($from_ip), ", port $from_port\n";
599             warn "| type = $from_type / subtype = $from_subcode\n";
600             warn "| checksum = $from_checksum, pid = $from_pid, seq = $from_seq\n";
601             warn "| message: ", unpack("H*", $from_message), "\n";
602             warn "`------------------------------------------------------------\n";
603             };
604              
605             # Not an ICMP echo reply. Move along.
606 29 100       98 return unless $from_type == ICMP_ECHOREPLY;
607              
608 15         26 DEBUG and warn "it's an ICMP echo reply";
609              
610             # Not from this process. Move along.
611 15 50       60 return unless $from_pid == ($$ & 0xFFFF);
612              
613 15         23 DEBUG and warn "it's from the current process";
614              
615             # Not waiting for a response with that sequence number. Move along.
616 15 100       51 return unless exists $heap->{ping_by_seq}->{$from_seq};
617              
618 11         17 DEBUG and warn "it's one we're waiting for ($from_seq)";
619              
620             # This is the response we're looking for. Calculate the round trip
621             # time, and map it to a postback.
622 11         30 my $trip_time = $now - $heap->{ping_by_seq}->{$from_seq}->[PBS_REQUEST_TIME];
623 11         118 $heap->{ping_by_seq}->{$from_seq}->[PBS_POSTBACK]->(
624             inet_ntoa($from_ip), $trip_time, $now, $from_ttl
625             );
626              
627             # It's a single-reply ping. Clean up after it.
628 11 100       1587 if ($heap->{onereply}) {
629 2         9 _end_ping_by_sequence($kernel, $heap, $from_seq);
630 2         164 _send_next_packet($kernel, $heap);
631 2         8 _check_for_close($kernel, $heap);
632             }
633             }
634              
635             # Default's used to catch ping timeouts, which are named after the
636             # packed socket addresses being pinged. We always send the timeout so
637             # the other session knows that a ping period has ended.
638              
639             sub poco_ping_default {
640 54     54 0 15003105 my ($kernel, $heap, $seq) = @_[KERNEL, HEAP, ARG0];
641              
642             # Record the receive time for possible use later.
643 54         180 my $now = time();
644              
645             # Are we waiting for this sequence number? We should be!
646 54 100       201 unless (exists $heap->{ping_by_seq}->{$seq}) {
647 5         10 warn "this shouldn't technically be displayed ($seq)" if (
648             DEBUG and $seq =~ /^\d+$/
649             );
650 5         21 return;
651             }
652              
653 49         119 my $ping_info = _end_ping_by_sequence($kernel, $heap, $seq);
654              
655 49         95 my $retryinfo = delete $heap->{retrydata}->{$seq};
656 49 100       164 if ($retryinfo) {
657 4         10 my ($sender, $event, $address, $timeout, $remaining) = @{$retryinfo};
  4         16  
658 4         6 DEBUG and warn("retrying ping for $address (", $remaining - 1, ")\n");
659 4         24 _do_ping(
660             $kernel, $heap, $sender, $event, $address, $timeout,
661             $remaining - 1
662             );
663 4         45 return;
664             }
665              
666             # Post a timer tick back to the session. This marks the end of
667             # the request/response transaction.
668 45         156 $ping_info->[PBS_POSTBACK]->( undef, undef, $now, undef );
669 45         5854 _send_next_packet($kernel, $heap);
670 45         123 _check_for_close($kernel, $heap);
671              
672 45         171 return;
673             }
674              
675             1;
676              
677             __END__