12b15cb3dSCy Schubertpackage NTP::Util; 22b15cb3dSCy Schubertuse strict; 32b15cb3dSCy Schubertuse warnings; 42b15cb3dSCy Schubertuse Exporter 'import'; 52b15cb3dSCy Schubertuse Carp; 62b15cb3dSCy Schubertuse version 0.77; 72b15cb3dSCy Schubert 82b15cb3dSCy Schubertour @EXPORT_OK = qw(ntp_read_vars do_dns ntp_peers ntp_sntp_line); 92b15cb3dSCy Schubert 102b15cb3dSCy Schubertmy $ntpq_path = 'ntpq'; 112b15cb3dSCy Schubertmy $sntp_path = 'sntp'; 122b15cb3dSCy Schubert 132b15cb3dSCy Schubertour $IP_AGNOSTIC; 142b15cb3dSCy Schubert 152b15cb3dSCy SchubertBEGIN { 162b15cb3dSCy Schubert require Socket; 172b15cb3dSCy Schubert if (version->parse($Socket::VERSION) >= version->parse(1.94)) { 182b15cb3dSCy Schubert Socket->import(qw(getaddrinfo getnameinfo SOCK_RAW AF_INET)); 192b15cb3dSCy Schubert $IP_AGNOSTIC = 1; 202b15cb3dSCy Schubert } 212b15cb3dSCy Schubert else { 222b15cb3dSCy Schubert Socket->import(qw(inet_aton SOCK_RAW AF_INET)); 232b15cb3dSCy Schubert } 242b15cb3dSCy Schubert} 252b15cb3dSCy Schubert 262b15cb3dSCy Schubertmy %obsolete_vars = ( 272b15cb3dSCy Schubert phase => 'offset', 282b15cb3dSCy Schubert rootdispersion => 'rootdisp', 292b15cb3dSCy Schubert); 302b15cb3dSCy Schubert 312b15cb3dSCy Schubertsub ntp_read_vars { 322b15cb3dSCy Schubert my ($peer, $vars, $host) = @_; 332b15cb3dSCy Schubert my $do_all = !@$vars; 342b15cb3dSCy Schubert my %out_vars = map {; $_ => undef } @$vars; 352b15cb3dSCy Schubert 362b15cb3dSCy Schubert $out_vars{status_line} = {} if $do_all; 372b15cb3dSCy Schubert 382b15cb3dSCy Schubert my $cmd = "$ntpq_path -n -c 'rv $peer ".(join ',', @$vars)."'"; 392b15cb3dSCy Schubert $cmd .= " $host" if defined $host; 402b15cb3dSCy Schubert $cmd .= " |"; 412b15cb3dSCy Schubert 422b15cb3dSCy Schubert open my $fh, $cmd or croak "Could not start ntpq: $!"; 432b15cb3dSCy Schubert 442b15cb3dSCy Schubert while (<$fh>) { 452b15cb3dSCy Schubert return undef if /Connection refused/; 462b15cb3dSCy Schubert 472b15cb3dSCy Schubert if (/^asso?c?id=0 status=(\S{4}) (\S+), (\S+),/gi) { 482b15cb3dSCy Schubert $out_vars{status_line}{status} = $1; 492b15cb3dSCy Schubert $out_vars{status_line}{leap} = $2; 502b15cb3dSCy Schubert $out_vars{status_line}{sync} = $3; 512b15cb3dSCy Schubert } 522b15cb3dSCy Schubert 532b15cb3dSCy Schubert while (/(\w+)=([^,]+),?\s/g) { 542b15cb3dSCy Schubert my ($var, $val) = ($1, $2); 552b15cb3dSCy Schubert $val =~ s/^"([^"]+)"$/$1/; 562b15cb3dSCy Schubert $var = $obsolete_vars{$var} if exists $obsolete_vars{$var}; 572b15cb3dSCy Schubert if ($do_all) { 582b15cb3dSCy Schubert $out_vars{$var} = $val 592b15cb3dSCy Schubert } 602b15cb3dSCy Schubert else { 612b15cb3dSCy Schubert $out_vars{$var} = $val if exists $out_vars{$var}; 622b15cb3dSCy Schubert } 632b15cb3dSCy Schubert } 642b15cb3dSCy Schubert } 652b15cb3dSCy Schubert 662b15cb3dSCy Schubert close $fh or croak "running ntpq failed: $! (exit status $?)"; 672b15cb3dSCy Schubert return \%out_vars; 682b15cb3dSCy Schubert} 692b15cb3dSCy Schubert 702b15cb3dSCy Schubertsub do_dns { 712b15cb3dSCy Schubert my ($host) = @_; 722b15cb3dSCy Schubert 732b15cb3dSCy Schubert if ($IP_AGNOSTIC) { 742b15cb3dSCy Schubert my ($err, $res); 752b15cb3dSCy Schubert 762b15cb3dSCy Schubert ($err, $res) = getaddrinfo($host, '', {socktype => SOCK_RAW}); 772b15cb3dSCy Schubert die "getaddrinfo failed: $err\n" if $err; 782b15cb3dSCy Schubert 792b15cb3dSCy Schubert ($err, $res) = getnameinfo($res->{addr}, 0); 802b15cb3dSCy Schubert die "getnameinfo failed: $err\n" if $err; 812b15cb3dSCy Schubert 822b15cb3dSCy Schubert return $res; 832b15cb3dSCy Schubert } 842b15cb3dSCy Schubert # Too old perl, do only ipv4 852b15cb3dSCy Schubert elsif ($host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) { 862b15cb3dSCy Schubert return gethostbyaddr inet_aton($host), AF_INET; 872b15cb3dSCy Schubert } 882b15cb3dSCy Schubert else { 892b15cb3dSCy Schubert return; 902b15cb3dSCy Schubert } 912b15cb3dSCy Schubert} 922b15cb3dSCy Schubert 932b15cb3dSCy Schubertsub ntp_peers { 942b15cb3dSCy Schubert my ($host) = @_; 952b15cb3dSCy Schubert 96a25439b6SCy Schubert $host ||= ''; 97a25439b6SCy Schubert my $cmd = "$ntpq_path -npw $host |"; 982b15cb3dSCy Schubert 992b15cb3dSCy Schubert open my $fh, $cmd or croak "Could not start ntpq: $!"; 1002b15cb3dSCy Schubert 1012b15cb3dSCy Schubert <$fh> for 1 .. 2; 1022b15cb3dSCy Schubert 103a25439b6SCy Schubert my @columns = qw(tally host refid st t when poll reach delay offset jitter); 1042b15cb3dSCy Schubert my @peers; 1052b15cb3dSCy Schubert while (<$fh>) { 106a25439b6SCy Schubert if (/^([ x+#*o-])((?:[\w.*:-]+\s+){10}|([\w.*:-]+\s+))$/) { 1072b15cb3dSCy Schubert my $col = 0; 108a25439b6SCy Schubert my @line = ($1, split /\s+/, $2); 109a25439b6SCy Schubert if( @line == 2 ) { 110a25439b6SCy Schubert defined ($_ = <$fh>) or last; 111a25439b6SCy Schubert s/^\s+//; 112a25439b6SCy Schubert push @line, split /\s+/; 113a25439b6SCy Schubert } 114a25439b6SCy Schubert my $r = { map {; $columns[ $col++ ] => $_ } @line }; 115a25439b6SCy Schubert $r->{remote} = $r->{tally} . $r->{host}; 116a25439b6SCy Schubert push @peers, $r; 1172b15cb3dSCy Schubert } 1182b15cb3dSCy Schubert else { 1192b15cb3dSCy Schubert #TODO return error (but not needed anywhere now) 1202b15cb3dSCy Schubert warn "ERROR: $_"; 1212b15cb3dSCy Schubert } 1222b15cb3dSCy Schubert } 1232b15cb3dSCy Schubert 1242b15cb3dSCy Schubert close $fh or croak "running ntpq failed: $! (exit status $?)"; 1252b15cb3dSCy Schubert return \@peers; 1262b15cb3dSCy Schubert} 1272b15cb3dSCy Schubert 1282b15cb3dSCy Schubert# TODO: we don't need this but it would be nice to have all the line parsed 1292b15cb3dSCy Schubertsub ntp_sntp_line { 1302b15cb3dSCy Schubert my ($host) = @_; 1312b15cb3dSCy Schubert 1322b15cb3dSCy Schubert my $cmd = "$sntp_path $host |"; 1332b15cb3dSCy Schubert open my $fh, $cmd or croak "Could not start sntp: $!"; 1342b15cb3dSCy Schubert 1352b15cb3dSCy Schubert my ($offset, $stratum); 1362b15cb3dSCy Schubert while (<$fh>) { 1372b15cb3dSCy Schubert next if !/^\d{4}-\d\d-\d\d/; 1382b15cb3dSCy Schubert chomp; 1392b15cb3dSCy Schubert my @output = split / /; 1402b15cb3dSCy Schubert 1412b15cb3dSCy Schubert $offset = $output[3]; 142*9034852cSGleb Smirnoff if (0) { 143*9034852cSGleb Smirnoff } elsif ($output[7] =~ /s(\d{1,2})/) { 144*9034852cSGleb Smirnoff $stratum = $1; 145*9034852cSGleb Smirnoff # warn "Found stratum at #7\n"; 146*9034852cSGleb Smirnoff } elsif ($output[8] =~ /s(\d{1,2})/) { 147*9034852cSGleb Smirnoff $stratum = $1; 148*9034852cSGleb Smirnoff # warn "Found stratum at #8\n"; 149*9034852cSGleb Smirnoff } 1502b15cb3dSCy Schubert } 1512b15cb3dSCy Schubert close $fh or croak "running sntp failed: $! (exit status $?)"; 1522b15cb3dSCy Schubert return ($offset, $stratum); 1532b15cb3dSCy Schubert} 154a25439b6SCy Schubert 155a25439b6SCy Schubert1; 156