File Coverage

blib/lib/Net/NTP.pm
Criterion Covered Total %
statement 19 51 37.2
branch 0 10 0.0
condition 0 4 0.0
subroutine 6 8 75.0
pod 0 1 0.0
total 25 74 33.7


line stmt bran cond sub pod time code
1             package Net::NTP;
2              
3 1     1   23668 use 5.006;
  1         5  
  1         46  
4 1     1   7 use strict;
  1         2  
  1         39  
5 1     1   6 use warnings;
  1         7  
  1         343  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             our @EXPORT = qw(
12             get_ntp_response
13             );
14              
15             our $VERSION = '1.3';
16              
17             our $TIMEOUT = 60;
18              
19             our %MODE = (
20             '0' => 'reserved',
21             '1' => 'symmetric active',
22             '2' => 'symmetric passive',
23             '3' => 'client',
24             '4' => 'server',
25             '5' => 'broadcast',
26             '6' => 'reserved for NTP control message',
27             '7' => 'reserved for private use'
28             );
29              
30             our %STRATUM = (
31             '0' => 'unspecified or unavailable',
32             '1' => 'primary reference (e.g., radio clock)',
33             );
34              
35             for (2 .. 15) {
36             $STRATUM{$_} = 'secondary reference (via NTP or SNTP)';
37             }
38              
39             for (16 .. 255) {
40             $STRATUM{$_} = 'reserved';
41             }
42              
43             our %STRATUM_ONE_TEXT = (
44             'LOCL' =>
45             'uncalibrated local clock used as a primary reference for a subnet without external means of synchronization',
46             'PPS' =>
47             'atomic clock or other pulse-per-second source individually calibrated to national standards',
48             'ACTS' => 'NIST dialup modem service',
49             'USNO' => 'USNO modem service',
50             'PTB' => 'PTB (Germany) modem service',
51             'TDF' => 'Allouis (France) Radio 164 kHz',
52             'DCF' => 'Mainflingen (Germany) Radio 77.5 kHz',
53             'MSF' => 'Rugby (UK) Radio 60 kHz',
54             'WWV' => 'Ft. Collins (US) Radio 2.5, 5, 10, 15, 20 MHz',
55             'WWVB' => 'Boulder (US) Radio 60 kHz',
56             'WWVH' => 'Kaui Hawaii (US) Radio 2.5, 5, 10, 15 MHz',
57             'CHU' => 'Ottawa (Canada) Radio 3330, 7335, 14670 kHz',
58             'LORC' => 'LORAN-C radionavigation system',
59             'OMEG' => 'OMEGA radionavigation system',
60             'GPS' => 'Global Positioning Service',
61             'GOES' => 'Geostationary Orbit Environment Satellite',
62             );
63              
64             our %LEAP_INDICATOR = (
65             '0' => 'no warning',
66             '1' => 'last minute has 61 seconds',
67             '2' => 'last minute has 59 seconds)',
68             '3' => 'alarm condition (clock not synchronized)'
69             );
70              
71 1     1   6 use constant NTP_ADJ => 2208988800;
  1         2  
  1         419  
72              
73             my @ntp_packet_fields = (
74             'Leap Indicator',
75             'Version Number',
76             'Mode',
77             'Stratum',
78             'Poll Interval',
79             'Precision',
80             'Root Delay',
81             'Root Dispersion',
82             'Reference Clock Identifier',
83             'Reference Timestamp',
84             'Originate Timestamp',
85             'Receive Timestamp',
86             'Transmit Timestamp',
87             );
88              
89             my $frac2bin = sub {
90             my $bin = '';
91             my $frac = shift;
92             while (length($bin) < 32) {
93             $bin = $bin . int($frac * 2);
94             $frac = ($frac * 2) - (int($frac * 2));
95             }
96             return $bin;
97             };
98              
99             my $bin2frac = sub {
100             my @bin = split '', shift;
101             my $frac = 0;
102             while (@bin) {
103             $frac = ($frac + pop @bin) / 2;
104             }
105             return $frac;
106             };
107              
108             my $percision = sub {
109             my $number = shift;
110             if ($number > 127) {
111             $number -= 255;
112             }
113             return sprintf("%1.4e", 2**$number);
114             };
115              
116             my $unpack_ip = sub {
117             my $ip;
118             my $stratum = shift;
119             my $tmp_ip = shift;
120             if ($stratum < 2) {
121             $ip = unpack("A4", pack("H8", $tmp_ip));
122             }
123             else {
124             $ip = sprintf("%d.%d.%d.%d", unpack("C4", pack("H8", $tmp_ip)));
125             }
126             return $ip;
127             };
128              
129             sub get_ntp_response {
130 1     1   942 use IO::Socket;
  1         45865  
  1         5  
131 1     1   935 use constant HAVE_SOCKET_INET6 => eval { require IO::Socket::INET6 };
  1         1  
  1         3  
  1         1129  
132              
133 0   0 0 0   my $host = shift || 'localhost';
134 0   0       my $port = shift || 'ntp';
135              
136 0           my %args = (
137             Proto => 'udp',
138             PeerHost => $host,
139             PeerPort => $port
140             );
141 0           my $sock;
142 0 0         if (HAVE_SOCKET_INET6) {
143 0           $sock = IO::Socket::INET6->new(%args);
144             }
145             else {
146 0           $sock = IO::Socket::INET->new(%args);
147             }
148 0 0         die $@ unless $sock;
149              
150 0           my %tmp_pkt;
151             my %packet;
152 0           my $data;
153              
154 0           my $client_localtime = time;
155 0           my $client_adj_localtime = $client_localtime + NTP_ADJ;
156 0           my $client_frac_localtime = $frac2bin->($client_adj_localtime);
157              
158 0           my $ntp_msg =
159             pack("B8 C3 N10 B32", '00011011', (0) x 12, int($client_localtime), $client_frac_localtime);
160              
161 0 0         $sock->send($ntp_msg)
162             or die "send() failed: $!\n";
163              
164 0           eval {
165 0     0     local $SIG{ALRM} = sub { die "Net::NTP timed out geting NTP packet\n"; };
  0            
166 0           alarm($TIMEOUT);
167 0 0         $sock->recv($data, 960)
168             or die "recv() failed: $!\n";
169 0           alarm(0);
170             };
171              
172 0 0         if ($@) {
173 0           die "$@";
174             }
175              
176 0           my @ntp_fields = qw/byte1 stratum poll precision/;
177 0           push @ntp_fields, qw/delay delay_fb disp disp_fb ident/;
178 0           push @ntp_fields, qw/ref_time ref_time_fb/;
179 0           push @ntp_fields, qw/org_time org_time_fb/;
180 0           push @ntp_fields, qw/recv_time recv_time_fb/;
181 0           push @ntp_fields, qw/trans_time trans_time_fb/;
182              
183 0           @tmp_pkt{@ntp_fields} = unpack("a C3 n B16 n B16 H8 N B32 N B32 N B32 N B32", $data);
184              
185 0           @packet{@ntp_packet_fields} = (
186             (unpack("C", $tmp_pkt{byte1} & "\xC0") >> 6),
187             (unpack("C", $tmp_pkt{byte1} & "\x38") >> 3),
188             (unpack("C", $tmp_pkt{byte1} & "\x07")),
189             $tmp_pkt{stratum},
190             (sprintf("%0.4f", $tmp_pkt{poll})),
191             $tmp_pkt{precision} - 255,
192             ($bin2frac->($tmp_pkt{delay_fb})),
193             (sprintf("%0.4f", $tmp_pkt{disp})),
194             $unpack_ip->($tmp_pkt{stratum}, $tmp_pkt{ident}),
195             (($tmp_pkt{ref_time} += $bin2frac->($tmp_pkt{ref_time_fb})) -= NTP_ADJ),
196             (($tmp_pkt{org_time} += $bin2frac->($tmp_pkt{org_time_fb}))),
197             (($tmp_pkt{recv_time} += $bin2frac->($tmp_pkt{recv_time_fb})) -= NTP_ADJ),
198             (($tmp_pkt{trans_time} += $bin2frac->($tmp_pkt{trans_time_fb})) -= NTP_ADJ)
199             );
200              
201 0           return %packet;
202             }
203              
204              
205             1;
206             __END__