[Git][NTPsec/ntpsec][master] 2 commits: Add readvars() method to ntp.packet.session class.
Eric S. Raymond
gitlab at mg.gitlab.com
Fri Sep 9 12:05:45 UTC 2016
Eric S. Raymond pushed to branch master at NTPsec / ntpsec
Commits:
f49f031b by Eric S. Raymond at 2016-09-09T05:07:46-04:00
Add readvars() method to ntp.packet.session class.
A convenience method for fetching the host's system variables.
- - - - -
1a7fed9b by Eric S. Raymond at 2016-09-09T08:04:51-04:00
Translate ntpsweep to Python and promote it.
- - - - -
9 changed files:
- + ntpsweep/ntp
- + ntpsweep/ntpsweep
- util/ntpsweep/ntpsweep-man.txt → ntpsweep/ntpsweep-man.txt
- pylib/packet.py
- + pylib/util.py
- util/README
- − util/ntpsweep/ntpsweep
- util/wscript
- wscript
Changes:
=====================================
ntpsweep/ntp
=====================================
--- /dev/null
+++ b/ntpsweep/ntp
@@ -0,0 +1 @@
+../pylib/
\ No newline at end of file
=====================================
ntpsweep/ntpsweep
=====================================
--- /dev/null
+++ b/ntpsweep/ntpsweep
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+"""
+ntpsweep - Print various information about given ntp servers.
+USAGE: ntpsweep [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [hostfile]
+
+ -l, --host-list=str Host to execute actions on
+ - may appear multiple times
+ -p, --peers Recursively list all peers a host synchronizes to
+ -m, --maxlevel=num Traverse peers up to this level (4 is a reasonable number)
+ -s, --strip=str Strip this string from hostnames
+
+Options are specified by doubled hyphens and their name or by a single
+hyphen and the flag character.
+"""
+# SPDX-License-Identifier: BSD-3-clause
+#
+# Python translation by ESR of a Perl script written long ago by
+# Hans Lambermont <ntpsweep at lambermont.dyndns.org>
+#
+# It is unclear how useful this will be under modern conditions (e.g. most
+# hosts refuse to be queried, and requests to them will just time out).
+
+from __future__ import print_function
+
+import os, sys, getopt
+import ntp.packet, ntp.util
+
+def peers(host):
+ "Return a list of peer IP addrs for a specified host, None if query failed."
+ try:
+ with os.popen("ntpq -npw " + host) as rp:
+ hostlines = rp.readlines[2:] # Drop display header
+ # Strip tally mark from first field
+ return [ln.split()[0][1:] for ln in hostlines if ln[0] in " x.-+#*o"]
+ except OSError:
+ return []
+
+def scan_host(host, level):
+ stratum = 0
+ offset = 0
+ daemonversion = ""
+ system = ""
+ processor = ""
+ peers = []
+ known_host = False
+
+ if host in known_host_info:
+ known_host = True
+ else:
+ session = ntp.packet.ntpq_session()
+ session.openhost(host)
+ sysvars = session.readvars()
+
+ # got answers ? If so, go on.
+ if type(sysvars) == type({}):
+ stratum = sysvars['stratum']
+ offset = sysvars['offset']
+ daemonversion = sysvars['version']
+ system = sysvars['system']
+ processor = sysvars['processor']
+
+ # Shorten daemon_version string.
+ #daemonversion =~ s/(|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//
+ daemonversion = daemonversion.replace("version=", "")
+ daemonversion = daemonversion.replace("ntpd ", "")
+ daemonversion = daemonversion.replace("(", "").replace(")", "")
+ daemonversion = daemonversion.replace("beta", "b")
+ daemonversion = daemonversion.replace("multicast", "mc")
+
+ # Shorten system string. Note, the assumptions here
+ # are very old, reflecting ancient big-iron Unixes
+ system = system.replace("UNIX/", "")
+ system = system.replace("RELEASE", "r")
+ system = system.replace("CURRENT", "c")
+
+ # Shorten processor string
+ processor = processor.replace("unknown", "")
+
+ # got answers ? If so, go on.
+ if daemonversion and recurse:
+ # Consider doing something more intelligent on failure
+ # than simply returning an empty list. Though it might
+ # be the right thing to do under modern conditions in
+ # which most hosts will refuse to be queried.
+ known_host_peers[host] = ntp_peers(host)
+
+ # Collect info on host
+ if stratum:
+ known_host_info[host] = "%2d %9.3f %-11s %-12s %s" \
+ % (stratum, offset, daemonversion[:11],
+ system[:12], processor[0:9])
+ else:
+ # Stratum level 0 is considered invalid
+ known_host_info[host] = " ?"
+
+ if stratum or known_host: # Valid or known host
+ printhost = (' ' * level) + (ntp.util.canonicalize_dns(host) or host)
+ # Shorten host string
+ if strip:
+ printhost = printhost.replace(strip, "")
+ # append number of peers in brackets if requested and valid
+ if recurse and known_host_info[host] != " ?" and host in known_host_peers:
+ printhost += " (%d)" % len(known_host_peers[host])
+ # Finally print complete host line
+ print("%-32s %s" % (printhost[:32], known_host_info[host]))
+ if recurse and (maxlevel == 0 or level < maxlevel):
+ trace.append(host)
+ # Loop through peers
+ for peer in known_host_peers[host]:
+ if peer in trace:
+ # we've detected a loop!
+ printhost = (' ' * (level + 1)) + "= " + peer
+ # Shorten host string
+ if strip:
+ printhost = printhost.replace(strip, "")
+ print("%-32s" % printhost[:32])
+ else:
+ # FIXME: Ugh! Magic-address assumption.
+ # Needed to deal with peers running legacy NTP.
+ # Might cause problems in the future. First
+ # part of the guard is an attempt to skip
+ # NTPsec-style clock IDs.
+ if peer[0].isdigit() and not peer.startswith("127"):
+ scan_host(peer, level + 1)
+ else: # We did not get answers from this host
+ printhost = (' ' * level) + (ntp.util.canonicalize_dns(host) or host)
+ if strip:
+ printhost = printhost.replace(strip, "")
+ print("%-32s ?" % printhost[:32])
+
+if __name__ == '__main__':
+ try:
+ (options, arguments) = getopt.getopt(
+ sys.argv[1:], "h:l:m:ps:?",
+ ["host=", "host-list=", "maxlevel=", "peers", "strip="])
+ except getopt.GetoptError as err:
+ sys.stderr.write(str(err) + "\n")
+ raise SystemExit(1)
+ hostlist = []
+ maxlevel = 1
+ recurse = False
+ strip = ""
+ for (switch, val) in options:
+ if switch == "-h" or switch == "--host":
+ hostlist = [val]
+ elif switch == "-l" or switch == "--host-list":
+ hostlist = val.split(",")
+ elif switch == "-m" or switch == "--maxlevel":
+ maxlevel = int(val)
+ elif switch == "-p" or switch == "--peers":
+ recurse = True
+ elif switch == "-s" or switch == "--strip":
+ strip = val
+ elif switch == "-?" or switch == "--help":
+ print(__doc__, file=sys.stderr)
+ raise SystemExit(0)
+
+ if arguments:
+ hostlist += [ln.strip() for ln in open(arguments[0]).readlines()]
+
+ if not hostlist:
+ hostlist = ["localhost"]
+
+ # Print header
+ print("""\
+Host st offset(s) version system processor
+--------------------------------+--+---------+-----------+------------+---------\
+""")
+
+ known_host_info = {}
+ known_host_peers = {}
+ trace = []
+ for host in hostlist:
+ scan_host(host, 0)
+
+ sys.exit(0);
+
+# end
=====================================
util/ntpsweep/ntpsweep-man.txt → ntpsweep/ntpsweep-man.txt
=====================================
--- a/util/ntpsweep/ntpsweep-man.txt
+++ b/ntpsweep/ntpsweep-man.txt
@@ -13,12 +13,13 @@ ntpsweep - print various informations about given NTP servers
seconds, the daemon version, the operating system and the processor.
Optionally recursing through all peers.
-`ntpsweep` relies on `ntpq` and `ntpdig` to probe servers. This depends
-on the remote host's _restrict_ configuration allowing this.
+If no hosts are specified, `ntpsweep` reports on localhost.
-`ntpsweep` is a perl script, and you may need to run it as:
-
-+perl -I <directory above NTP/Util.pm> ntpsweep+
+`ntpsweep` relies on `ntpq` and Mode 6 queries to probe servers. This
+depends on the remote host's _restrict_ configuration allowing
+queries. Nowadays effectively all public hosts set _noquery_, so this
+script is unlikely to be useful unless you have multiple specially-
+configured timeservers on a LAN.
== OPTIONS ==
@@ -40,9 +41,7 @@ appear multiple times.
Strip this string from hostnames.
+-h+ string, +--host+=_string_::
- Specify a single host.
-+
-_NOTE: THIS OPTION IS DEPRECATED_
+ Specify a single host. Deprecated option for backwards compatibility.
== EXIT STATUS ==
=====================================
pylib/packet.py
=====================================
--- a/pylib/packet.py
+++ b/pylib/packet.py
@@ -503,5 +503,30 @@ class ntpq_session:
break
# Return None on success, otherwise an error string
return res;
-
+ def readvars(self):
+ "Read system vars from the host as a dict, or return an error string."
+ self.doquery(opcode=CTL_OP_READVAR, quiet=True)
+ if self.response.startswith("*"):
+ return self.response
+ else:
+ response = self.response
+ # Trim trailing NULs from the text
+ while response.endswith("\x00"):
+ response = response[:-1]
+ response = response.rstrip()
+ items = []
+ for pair in response.split(","):
+ (var, val) = pair.split("=")
+ var = var.strip()
+ val = val.strip()
+ try:
+ val = int(val)
+ except ValueError:
+ try:
+ val = float(val)
+ except ValueError:
+ if val[0] == '"' and val[-1] == '"':
+ val = val[1:-1]
+ items.append((var, val))
+ return dict(items)
# end
=====================================
pylib/util.py
=====================================
--- /dev/null
+++ b/pylib/util.py
@@ -0,0 +1,20 @@
+# Common utility functions
+
+from __future__ import print_function
+
+import socket
+
+def canonicalize_dns(hostname):
+ try:
+ ai = socket.getaddrinfo(hostname, None, 0, 0, 0, socket.AI_CANONNAME)
+ except socket.gaierror as e:
+ print('getaddrinfo failed: %s' % e.strerr, file=sys.stderr)
+ raise SystemExit(1)
+ (family, socktype, proto, canonname, sockaddr) = ai[0]
+ try:
+ name = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
+ except socket.gaierror:
+ return canonname.lower()
+ return name[0].lower()
+
+# end
=====================================
util/README
=====================================
--- a/util/README
+++ b/util/README
@@ -32,14 +32,6 @@ ntpdate:: Wrapper script to maintain compatibility. Maps options
to ntpdig and calls it.
Tested: 20160226
-ntpsweep:: prints per host given in <file> the NTP stratum level, the
- clock offset in seconds, the daemon version, the operating
- system and the processor.
- Needs -I <directory above NTP/Util.pm>
- May not work on today's Internet, because servers do not
- wish to be interrogated.
- Tested: 20160228
-
ntpver:: Simple script using ntpq to print out the suite version.
Tested: 20160226
=====================================
util/ntpsweep/ntpsweep deleted
=====================================
--- a/util/ntpsweep/ntpsweep
+++ /dev/null
@@ -1,251 +0,0 @@
-#!/usr/bin/env perl
-#
-# Copyright (C) 1999,2000 Hans Lambermont and Origin B.V.
-#
-# SPDX-License-Identifier: BSD-3-clause
-#
-# Hans Lambermont <ntpsweep at lambermont.dyndns.org>
-
-package ntpsweep;
-use 5.006_000;
-use strict;
-use NTP::Util qw(do_dns ntp_read_vars ntp_peers ntp_ntpdig_line);
-use Getopt::Long qw(GetOptionsFromArray);
-Getopt::Long::Configure(qw(no_auto_abbrev no_ignore_case_always));
-
-my $usage;
-
-sub usage {
- my ($ret) = @_;
- print STDERR $usage;
- exit $ret;
-}
-
-sub paged_usage {
- my ($ret) = @_;
- my $pager = $ENV{PAGER} || '(less || more)';
-
- open STDOUT, "| $pager" or die "Can't fork a pager: $!";
- print $usage;
-
- exit $ret;
-}
-
-sub processOptions {
- my $args = shift;
-
- my $opts = {
- 'host-list' => [],
- 'peers' => '',
- 'maxlevel' => '',
- 'strip' => '',
- 'host' => '',
- 'help' => '', 'more-help' => ''
- };
- my $argument = '[hostfile]';
- my $ret = GetOptionsFromArray($args, $opts, (
- 'host-list|l=s', 'peers|p', 'maxlevel|m=i',
- 'strip|s=s', 'host|h=s',
- 'help|?', 'more-help'));
-
- $usage = <<'USAGE';
-ntpsweep - Print various informations about given ntp servers.
-USAGE: ntpsweep [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [hostfile]
-
- -l, --host-list=str Host to execute actions on
- - may appear multiple times
- -p, --peers Recursively list all peers a host synchronizes to
- -m, --maxlevel=num Traverse peers up to this level (4 is a reasonable number)
- -s, --strip=str Strip this string from hostnames
- -?, --help Display usage information and exit
- --more-help Pass the extended usage text through a pager
-
-Options are specified by doubled hyphens and their name or by a single
-hyphen and the flag character.
-USAGE
-
- usage(0) if $opts->{'help'};
- paged_usage(0) if $opts->{'more-help'};
- $_[0] = $opts;
- return $ret;
-}
-
-(my $program = $0) =~ s%.*/(.+?)(.pl)?$%$1%;
-my ($showpeers, $maxlevel, $strip);
-my (%known_host_info, %known_host_peers);
-
-exit run(@ARGV) unless caller;
-
-sub run {
- my $opts;
- if (!processOptions(\@_, $opts) ||
- (((@_ != 1) && !$opts->{host} && !@{$opts->{'host-list'}}))) {
- usage(1);
- };
-
- # no STDOUT buffering
- $| = 1;
- ($showpeers, $maxlevel, $strip) =
- ($opts->{peers}, $opts->{maxlevel}, $opts->{strip});
-
- my $hostsfile = shift;
-
- # Main program
-
- my @hosts;
-
- if ($opts->{host}) {
- push @hosts, $opts->{host};
- }
- else {
- @hosts = read_hosts($hostsfile) if $hostsfile;
- push @hosts, @{$opts->{'host-list'}};
- }
-
- # Print header
- print <<EOF;
-Host st offset(s) version system processor
---------------------------------+--+---------+-----------+------------+---------
-EOF
-
- %known_host_info = ();
- %known_host_peers = ();
- scan_hosts(@hosts);
-
- return 0;
-}
-
-sub scan_hosts {
- my (@hosts) = @_;
-
- my $host;
- for $host (@hosts) {
- scan_host($host, 0, $host => 1);
- }
-}
-
-sub read_hosts {
- my ($hostsfile) = @_;
- my @hosts;
-
- open my $hosts, $hostsfile
- or die "$program: FATAL: unable to read $hostsfile: $!\n";
-
- while (<$hosts>) {
- next if /^\s*(#|$)/; # comment/empty
- chomp;
- push @hosts, $_;
- }
-
- close $hosts;
- return @hosts;
-}
-
-sub scan_host {
- my ($host, $level, %trace) = @_;
- my $stratum = 0;
- my $offset = 0;
- my $daemonversion = "";
- my $system = "";
- my $processor = "";
- my @peers;
- my $known_host = 0;
-
- if (exists $known_host_info{$host}) {
- $known_host = 1;
- }
- else {
- ($offset, $stratum) = ntp_ntpdig_line($host);
-
- # got answers ? If so, go on.
- if ($stratum) {
- my $vars = ntp_read_vars(0, [qw(processor system daemon_version)], $host) || {};
- $daemonversion = $vars->{daemon_version};
- $system = $vars->{system};
- $processor = $vars->{processor};
-
- # Shorten daemon_version string.
- $daemonversion =~ s/(;|Mon|Tue|Wed|Thu|Fri|Sat|Sun).*$//;
- $daemonversion =~ s/version=//;
- $daemonversion =~ s/(x|)ntpd //;
- $daemonversion =~ s/(\(|\))//g;
- $daemonversion =~ s/beta/b/;
- $daemonversion =~ s/multicast/mc/;
-
- # Shorten system string
- $system =~ s/UNIX\///;
- $system =~ s/RELEASE/r/;
- $system =~ s/CURRENT/c/;
-
- # Shorten processor string
- $processor =~ s/unknown//;
- }
-
- # got answers ? If so, go on.
- if ($daemonversion) {
- if ($showpeers) {
- my $peers_ref = ntp_peers($host);
- my @peers_tmp = @$peers_ref;
- for (@peers_tmp) {
- $_->{remote} =~ s/^(?: |x|\.|-|\+|#|\*|o)([^ ]+)/$1/;
- push @peers, $_->{remote};
- }
- }
- }
-
- # Add scanned host to known_hosts array
- #push @known_hosts, $host;
- if ($stratum) {
- $known_host_info{$host} = sprintf "%2d %9.3f %-11s %-12s %s",
- $stratum, $offset, (substr $daemonversion, 0, 11),
- (substr $system, 0, 12), (substr $processor, 0, 9);
- }
- else {
- # Stratum level 0 is consider invalid
- $known_host_info{$host} = " ?";
- }
- $known_host_peers{$host} = [@peers];
- }
-
- if ($stratum || $known_host) { # Valid or known host
- my $printhost = ' ' x $level . (do_dns($host) || $host);
- # Shorten host string
- if ($strip) {
- $printhost =~ s/$strip//;
- }
- # append number of peers in brackets if requested and valid
- if ($showpeers && ($known_host_info{$host} ne " ?")) {
- $printhost .= " (" . @{$known_host_peers{$host}} . ")";
- }
- # Finally print complete host line
- printf "%-32s %s\n",
- (substr $printhost, 0, 32), $known_host_info{$host};
- if ($showpeers && ($maxlevel ? $level < $maxlevel : 1)) {
- $trace{$host} = 1;
- # Loop through peers
- foreach my $peer (@{$known_host_peers{$host}}) {
- if (exists $trace{$peer}) {
- # we've detected a loop !
- $printhost = ' ' x ($level + 1) . "= " . $peer;
- # Shorten host string
- $printhost =~ s/$strip// if $strip;
- printf "%-32s\n", substr $printhost, 0, 32;
- } else {
- if ((substr $peer, 0, 3) ne "127") {
- scan_host($peer, $level + 1, %trace);
- }
- }
- }
- }
- }
- else { # We did not get answers from this host
- my $printhost = ' ' x $level . (do_dns($host) || $host);
- $printhost =~ s/$strip// if $strip;
- printf "%-32s ?\n", substr $printhost, 0, 32;
- }
-}
-
-END { close STDOUT };
-
-1;
-__END__
=====================================
util/wscript
=====================================
--- a/util/wscript
+++ b/util/wscript
@@ -18,4 +18,3 @@ def build(ctx):
install_path = False
)
- ctx.manpage(1, "ntpsweep/ntpsweep-man.txt")
=====================================
wscript
=====================================
--- a/wscript
+++ b/wscript
@@ -148,15 +148,12 @@ def build(ctx):
ctx.recurse("util")
ctx.recurse("tests")
- # Some of these presently fail because they require a Perl
- # module that's never installed. Awkwardly, their man pages do
- # get installed. There is a note about this mess in INSTALL.
scripts = [
"ntpleapfetch/ntpleapfetch",
"ntpstats/ntpviz",
"ntptrace/ntptrace",
"ntpwait/ntpwait",
- #"util/ntpsweep/ntpsweep",
+ "ntpsweep/ntpsweep",
]
ctx(
@@ -171,7 +168,7 @@ def build(ctx):
ctx.manpage(1, "ntptrace/ntptrace-man.txt")
ctx.manpage(1, "ntpstats/ntpviz-man.txt")
ctx.manpage(8, "ntpwait/ntpwait-man.txt")
- #ctx.manpage(1, "util/ntpsweep/ntpsweep-man.txt")
+ ctx.manpage(1, "ntpsweep/ntpsweep-man.txt")
# Skip running unit tests on a cross compile build
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/839e421a5d7a6d7429d7eaa2573f72b826519076...1a7fed9b2d0819da7a9fadbc1e810b49b55d786f
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ntpsec.org/pipermail/vc/attachments/20160909/1b159716/attachment.html>
More information about the vc
mailing list