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