File Coverage

blib/lib/POE/Component/Client/Ping.pm
Criterion Covered Total %
statement 204 231 88.3
branch 63 96 65.6
condition 23 39 58.9
subroutine 22 24 91.6
pod 0 6 0.0
total 312 396 78.7


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