xref: /freebsd-src/contrib/ntp/scripts/lib/NTP/Util.pm (revision 2b15cb3d0922bd70ea592f0da9b4a5b167f4d53f)
1*2b15cb3dSCy Schubertpackage NTP::Util;
2*2b15cb3dSCy Schubertuse strict;
3*2b15cb3dSCy Schubertuse warnings;
4*2b15cb3dSCy Schubertuse Exporter 'import';
5*2b15cb3dSCy Schubertuse Carp;
6*2b15cb3dSCy Schubertuse version 0.77;
7*2b15cb3dSCy Schubert
8*2b15cb3dSCy Schubertour @EXPORT_OK = qw(ntp_read_vars do_dns ntp_peers ntp_sntp_line);
9*2b15cb3dSCy Schubert
10*2b15cb3dSCy Schubertmy $ntpq_path = 'ntpq';
11*2b15cb3dSCy Schubertmy $sntp_path = 'sntp';
12*2b15cb3dSCy Schubert
13*2b15cb3dSCy Schubertour $IP_AGNOSTIC;
14*2b15cb3dSCy Schubert
15*2b15cb3dSCy SchubertBEGIN {
16*2b15cb3dSCy Schubert    require Socket;
17*2b15cb3dSCy Schubert    if (version->parse($Socket::VERSION) >= version->parse(1.94)) {
18*2b15cb3dSCy Schubert        Socket->import(qw(getaddrinfo getnameinfo SOCK_RAW AF_INET));
19*2b15cb3dSCy Schubert        $IP_AGNOSTIC = 1;
20*2b15cb3dSCy Schubert    }
21*2b15cb3dSCy Schubert    else {
22*2b15cb3dSCy Schubert        Socket->import(qw(inet_aton SOCK_RAW AF_INET));
23*2b15cb3dSCy Schubert    }
24*2b15cb3dSCy Schubert}
25*2b15cb3dSCy Schubert
26*2b15cb3dSCy Schubertmy %obsolete_vars = (
27*2b15cb3dSCy Schubert    phase          => 'offset',
28*2b15cb3dSCy Schubert    rootdispersion => 'rootdisp',
29*2b15cb3dSCy Schubert);
30*2b15cb3dSCy Schubert
31*2b15cb3dSCy Schubertsub ntp_read_vars {
32*2b15cb3dSCy Schubert    my ($peer, $vars, $host) = @_;
33*2b15cb3dSCy Schubert    my $do_all   = !@$vars;
34*2b15cb3dSCy Schubert    my %out_vars = map {; $_ => undef } @$vars;
35*2b15cb3dSCy Schubert
36*2b15cb3dSCy Schubert    $out_vars{status_line} = {} if $do_all;
37*2b15cb3dSCy Schubert
38*2b15cb3dSCy Schubert    my $cmd = "$ntpq_path -n -c 'rv $peer ".(join ',', @$vars)."'";
39*2b15cb3dSCy Schubert    $cmd .= " $host" if defined $host;
40*2b15cb3dSCy Schubert    $cmd .= " |";
41*2b15cb3dSCy Schubert
42*2b15cb3dSCy Schubert    open my $fh, $cmd or croak "Could not start ntpq: $!";
43*2b15cb3dSCy Schubert
44*2b15cb3dSCy Schubert    while (<$fh>) {
45*2b15cb3dSCy Schubert        return undef if /Connection refused/;
46*2b15cb3dSCy Schubert
47*2b15cb3dSCy Schubert        if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/gi) {
48*2b15cb3dSCy Schubert            $out_vars{status_line}{status} = $1;
49*2b15cb3dSCy Schubert            $out_vars{status_line}{leap}   = $2;
50*2b15cb3dSCy Schubert            $out_vars{status_line}{sync}   = $3;
51*2b15cb3dSCy Schubert        }
52*2b15cb3dSCy Schubert
53*2b15cb3dSCy Schubert        while (/(\w+)=([^,]+),?\s/g) {
54*2b15cb3dSCy Schubert            my ($var, $val) = ($1, $2);
55*2b15cb3dSCy Schubert            $val =~ s/^"([^"]+)"$/$1/;
56*2b15cb3dSCy Schubert            $var = $obsolete_vars{$var} if exists $obsolete_vars{$var};
57*2b15cb3dSCy Schubert            if ($do_all) {
58*2b15cb3dSCy Schubert                $out_vars{$var} = $val
59*2b15cb3dSCy Schubert            }
60*2b15cb3dSCy Schubert            else {
61*2b15cb3dSCy Schubert                $out_vars{$var} = $val if exists $out_vars{$var};
62*2b15cb3dSCy Schubert            }
63*2b15cb3dSCy Schubert        }
64*2b15cb3dSCy Schubert    }
65*2b15cb3dSCy Schubert
66*2b15cb3dSCy Schubert    close $fh or croak "running ntpq failed: $! (exit status $?)";
67*2b15cb3dSCy Schubert    return \%out_vars;
68*2b15cb3dSCy Schubert}
69*2b15cb3dSCy Schubert
70*2b15cb3dSCy Schubertsub do_dns {
71*2b15cb3dSCy Schubert    my ($host) = @_;
72*2b15cb3dSCy Schubert
73*2b15cb3dSCy Schubert    if ($IP_AGNOSTIC) {
74*2b15cb3dSCy Schubert        my ($err, $res);
75*2b15cb3dSCy Schubert
76*2b15cb3dSCy Schubert        ($err, $res) = getaddrinfo($host, '', {socktype => SOCK_RAW});
77*2b15cb3dSCy Schubert        die "getaddrinfo failed: $err\n" if $err;
78*2b15cb3dSCy Schubert
79*2b15cb3dSCy Schubert        ($err, $res) = getnameinfo($res->{addr}, 0);
80*2b15cb3dSCy Schubert        die "getnameinfo failed: $err\n" if $err;
81*2b15cb3dSCy Schubert
82*2b15cb3dSCy Schubert        return $res;
83*2b15cb3dSCy Schubert    }
84*2b15cb3dSCy Schubert    # Too old perl, do only ipv4
85*2b15cb3dSCy Schubert    elsif ($host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
86*2b15cb3dSCy Schubert        return gethostbyaddr inet_aton($host), AF_INET;
87*2b15cb3dSCy Schubert    }
88*2b15cb3dSCy Schubert    else {
89*2b15cb3dSCy Schubert        return;
90*2b15cb3dSCy Schubert    }
91*2b15cb3dSCy Schubert}
92*2b15cb3dSCy Schubert
93*2b15cb3dSCy Schubertsub ntp_peers {
94*2b15cb3dSCy Schubert    my ($host) = @_;
95*2b15cb3dSCy Schubert
96*2b15cb3dSCy Schubert    my $cmd = "$ntpq_path -np $host |";
97*2b15cb3dSCy Schubert
98*2b15cb3dSCy Schubert    open my $fh, $cmd or croak "Could not start ntpq: $!";
99*2b15cb3dSCy Schubert
100*2b15cb3dSCy Schubert    <$fh> for 1 .. 2;
101*2b15cb3dSCy Schubert
102*2b15cb3dSCy Schubert    my @columns = qw(remote refid st t when poll reach delay offset jitter);
103*2b15cb3dSCy Schubert    my @peers;
104*2b15cb3dSCy Schubert    while (<$fh>) {
105*2b15cb3dSCy Schubert        if (/(?:[\w\.\*-]+\s*){10}/) {
106*2b15cb3dSCy Schubert            my $col = 0;
107*2b15cb3dSCy Schubert            push @peers, { map {; $columns[ $col++ ] => $_ } split /(?<=.)\s+/ };
108*2b15cb3dSCy Schubert        }
109*2b15cb3dSCy Schubert        else {
110*2b15cb3dSCy Schubert            #TODO return error (but not needed anywhere now)
111*2b15cb3dSCy Schubert            warn "ERROR: $_";
112*2b15cb3dSCy Schubert        }
113*2b15cb3dSCy Schubert    }
114*2b15cb3dSCy Schubert
115*2b15cb3dSCy Schubert    close $fh or croak "running ntpq failed: $! (exit status $?)";
116*2b15cb3dSCy Schubert    return \@peers;
117*2b15cb3dSCy Schubert}
118*2b15cb3dSCy Schubert
119*2b15cb3dSCy Schubert# TODO: we don't need this but it would be nice to have all the line parsed
120*2b15cb3dSCy Schubertsub ntp_sntp_line {
121*2b15cb3dSCy Schubert    my ($host) = @_;
122*2b15cb3dSCy Schubert
123*2b15cb3dSCy Schubert    my $cmd = "$sntp_path $host |";
124*2b15cb3dSCy Schubert    open my $fh, $cmd or croak "Could not start sntp: $!";
125*2b15cb3dSCy Schubert
126*2b15cb3dSCy Schubert    my ($offset, $stratum);
127*2b15cb3dSCy Schubert    while (<$fh>) {
128*2b15cb3dSCy Schubert        next if !/^\d{4}-\d\d-\d\d/;
129*2b15cb3dSCy Schubert        chomp;
130*2b15cb3dSCy Schubert        my @output = split / /;
131*2b15cb3dSCy Schubert
132*2b15cb3dSCy Schubert        $offset = $output[3];
133*2b15cb3dSCy Schubert        ($stratum = pop @output) =~ s/s(\d{1,2})/$1/;
134*2b15cb3dSCy Schubert    }
135*2b15cb3dSCy Schubert    close $fh or croak "running sntp failed: $! (exit status $?)";
136*2b15cb3dSCy Schubert    return ($offset, $stratum);
137*2b15cb3dSCy Schubert}
138