xref: /netbsd-src/external/bsd/ntp/dist/scripts/lib/NTP/Util.pm (revision a6f3f22f245acb8ee3bbf6871d7dce989204fa97)
18585484eSchristospackage NTP::Util;
28585484eSchristosuse strict;
38585484eSchristosuse warnings;
48585484eSchristosuse Exporter 'import';
58585484eSchristosuse Carp;
6b8ecfcfeSchristosuse version 0.77;
78585484eSchristos
88585484eSchristosour @EXPORT_OK = qw(ntp_read_vars do_dns ntp_peers ntp_sntp_line);
98585484eSchristos
108585484eSchristosmy $ntpq_path = 'ntpq';
118585484eSchristosmy $sntp_path = 'sntp';
128585484eSchristos
138585484eSchristosour $IP_AGNOSTIC;
148585484eSchristos
158585484eSchristosBEGIN {
168585484eSchristos    require Socket;
17b8ecfcfeSchristos    if (version->parse($Socket::VERSION) >= version->parse(1.94)) {
188585484eSchristos        Socket->import(qw(getaddrinfo getnameinfo SOCK_RAW AF_INET));
198585484eSchristos        $IP_AGNOSTIC = 1;
208585484eSchristos    }
218585484eSchristos    else {
228585484eSchristos        Socket->import(qw(inet_aton SOCK_RAW AF_INET));
238585484eSchristos    }
248585484eSchristos}
258585484eSchristos
268585484eSchristosmy %obsolete_vars = (
278585484eSchristos    phase          => 'offset',
288585484eSchristos    rootdispersion => 'rootdisp',
298585484eSchristos);
308585484eSchristos
318585484eSchristossub ntp_read_vars {
328585484eSchristos    my ($peer, $vars, $host) = @_;
338585484eSchristos    my $do_all   = !@$vars;
348585484eSchristos    my %out_vars = map {; $_ => undef } @$vars;
358585484eSchristos
368585484eSchristos    $out_vars{status_line} = {} if $do_all;
378585484eSchristos
388585484eSchristos    my $cmd = "$ntpq_path -n -c 'rv $peer ".(join ',', @$vars)."'";
398585484eSchristos    $cmd .= " $host" if defined $host;
408585484eSchristos    $cmd .= " |";
418585484eSchristos
428585484eSchristos    open my $fh, $cmd or croak "Could not start ntpq: $!";
438585484eSchristos
448585484eSchristos    while (<$fh>) {
458585484eSchristos        return undef if /Connection refused/;
468585484eSchristos
478585484eSchristos        if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/gi) {
488585484eSchristos            $out_vars{status_line}{status} = $1;
498585484eSchristos            $out_vars{status_line}{leap}   = $2;
508585484eSchristos            $out_vars{status_line}{sync}   = $3;
518585484eSchristos        }
528585484eSchristos
538585484eSchristos        while (/(\w+)=([^,]+),?\s/g) {
548585484eSchristos            my ($var, $val) = ($1, $2);
558585484eSchristos            $val =~ s/^"([^"]+)"$/$1/;
568585484eSchristos            $var = $obsolete_vars{$var} if exists $obsolete_vars{$var};
578585484eSchristos            if ($do_all) {
588585484eSchristos                $out_vars{$var} = $val
598585484eSchristos            }
608585484eSchristos            else {
618585484eSchristos                $out_vars{$var} = $val if exists $out_vars{$var};
628585484eSchristos            }
638585484eSchristos        }
648585484eSchristos    }
658585484eSchristos
668585484eSchristos    close $fh or croak "running ntpq failed: $! (exit status $?)";
678585484eSchristos    return \%out_vars;
688585484eSchristos}
698585484eSchristos
708585484eSchristossub do_dns {
718585484eSchristos    my ($host) = @_;
728585484eSchristos
738585484eSchristos    if ($IP_AGNOSTIC) {
748585484eSchristos        my ($err, $res);
758585484eSchristos
768585484eSchristos        ($err, $res) = getaddrinfo($host, '', {socktype => SOCK_RAW});
778585484eSchristos        die "getaddrinfo failed: $err\n" if $err;
788585484eSchristos
798585484eSchristos        ($err, $res) = getnameinfo($res->{addr}, 0);
808585484eSchristos        die "getnameinfo failed: $err\n" if $err;
818585484eSchristos
828585484eSchristos        return $res;
838585484eSchristos    }
848585484eSchristos    # Too old perl, do only ipv4
858585484eSchristos    elsif ($host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
868585484eSchristos        return gethostbyaddr inet_aton($host), AF_INET;
878585484eSchristos    }
888585484eSchristos    else {
898585484eSchristos        return;
908585484eSchristos    }
918585484eSchristos}
928585484eSchristos
938585484eSchristossub ntp_peers {
948585484eSchristos    my ($host) = @_;
958585484eSchristos
96b5bbe2e3Schristos    $host ||= '';
97b5bbe2e3Schristos    my $cmd = "$ntpq_path -npw $host |";
988585484eSchristos
998585484eSchristos    open my $fh, $cmd or croak "Could not start ntpq: $!";
1008585484eSchristos
1018585484eSchristos    <$fh> for 1 .. 2;
1028585484eSchristos
103b5bbe2e3Schristos    my @columns = qw(tally host refid st t when poll reach delay offset jitter);
1048585484eSchristos    my @peers;
1058585484eSchristos    while (<$fh>) {
106b5bbe2e3Schristos        if (/^([ x+#*o-])((?:[\w.*:-]+\s+){10}|([\w.*:-]+\s+))$/) {
1078585484eSchristos            my $col = 0;
108b5bbe2e3Schristos	    my @line = ($1, split /\s+/, $2);
109b5bbe2e3Schristos	    if( @line == 2 ) {
110b5bbe2e3Schristos		defined ($_ = <$fh>) or last;
111b5bbe2e3Schristos		s/^\s+//;
112b5bbe2e3Schristos		push @line, split /\s+/;
113b5bbe2e3Schristos	    }
114b5bbe2e3Schristos	    my $r = { map {; $columns[ $col++ ] => $_ } @line };
115b5bbe2e3Schristos	    $r->{remote} = $r->{tally} . $r->{host};
116b5bbe2e3Schristos            push @peers, $r;
1178585484eSchristos        }
1188585484eSchristos        else {
1198585484eSchristos            #TODO return error (but not needed anywhere now)
1208585484eSchristos            warn "ERROR: $_";
1218585484eSchristos        }
1228585484eSchristos    }
1238585484eSchristos
1248585484eSchristos    close $fh or croak "running ntpq failed: $! (exit status $?)";
1258585484eSchristos    return \@peers;
1268585484eSchristos}
1278585484eSchristos
1288585484eSchristos# TODO: we don't need this but it would be nice to have all the line parsed
1298585484eSchristossub ntp_sntp_line {
1308585484eSchristos    my ($host) = @_;
1318585484eSchristos
1328585484eSchristos    my $cmd = "$sntp_path $host |";
1338585484eSchristos    open my $fh, $cmd or croak "Could not start sntp: $!";
1348585484eSchristos
1358585484eSchristos    my ($offset, $stratum);
1368585484eSchristos    while (<$fh>) {
1378585484eSchristos        next if !/^\d{4}-\d\d-\d\d/;
1388585484eSchristos        chomp;
1398585484eSchristos        my @output = split / /;
1408585484eSchristos
1418585484eSchristos        $offset = $output[3];
142*a6f3f22fSchristos	if (0) {
143*a6f3f22fSchristos	} elsif ($output[7] =~ /s(\d{1,2})/) {
144*a6f3f22fSchristos		$stratum = $1;
145*a6f3f22fSchristos		# warn "Found stratum at #7\n";
146*a6f3f22fSchristos	} elsif ($output[8] =~ /s(\d{1,2})/) {
147*a6f3f22fSchristos		$stratum = $1;
148*a6f3f22fSchristos		# warn "Found stratum at #8\n";
149*a6f3f22fSchristos	}
1508585484eSchristos    }
1518585484eSchristos    close $fh or croak "running sntp failed: $! (exit status $?)";
1528585484eSchristos    return ($offset, $stratum);
1538585484eSchristos}
154b5bbe2e3Schristos
155b5bbe2e3Schristos1;
156