[Git][NTPsec/ntpsec][master] 2 commits: Uodate TODO; some things have gotten done.

Eric S. Raymond gitlab at mg.gitlab.com
Mon Aug 29 03:05:58 UTC 2016


Eric S. Raymond pushed to branch master at NTPsec / ntpsec


Commits:
bb7b3c37 by Eric S. Raymond at 2016-08-28T14:10:06-04:00
Uodate TODO; some things have gotten done.

- - - - -
c02201db by Eric S. Raymond at 2016-08-28T23:05:05-04:00
Translate ntpwait to Python.

- - - - -


5 changed files:

- INSTALL
- devel/TODO
- + ntpwait/ntp
- ntpwait/ntpwait
- pylib/packet.py


Changes:

=====================================
INSTALL
=====================================
--- a/INSTALL
+++ b/INSTALL
@@ -188,10 +188,10 @@ Other behaviors may be added in future releases.
 == Uninstalled binaries and scripts ==
 
 Due to variations in Perl library paths, our stock source distribution
-makes no attempt to install certain Perl scripts: notably ntpleapfetch
-and ntpwait.  If you installed this software from a binary package
-prepared by your OS distributor, the OS distributor may install them,
-arranging a rendezvous with the Perl implementation's library path.
+makes no attempt to install the Perl script ntpleapfetch.  If you
+installed this software from a binary package prepared by your OS
+distributor, the OS distributor may install them, arranging a
+rendezvous with the Perl implementation's library path.
 
 The utils/ directory contains various other uninstalled programs, some
 of only historical interest. Consult util/README for details.


=====================================
devel/TODO
=====================================
--- a/devel/TODO
+++ b/devel/TODO
@@ -6,7 +6,7 @@
 
 * Document build files.
 
-* Cleanup and separate features in pylib/*
+* Cleanup and separate features in wafhelpers/*
 
 * Add 'snapshot' feature to dump config status into a JSON file for collecting
   build + platform information
@@ -121,20 +121,13 @@ Neither is ideal, easy pickings for someone to code on.
   (and multiple inbound NTP connections, and a hole in your firewall) even when
   it has a known-good local timesource like a GPS.  This should be fixed.
 
-* ntpproto.py - a Python library that knows the NTP wire protocol and
-  can be used to generate and interpret packets.  Daniel has speculated
-  about writing this in order to test possible exploits, but there are
-  other uses.  Translate it from the Perl protcol library under scripts/lib.
-
-* Use ntpproto.py to translate the Perl stuff under scripts/ to Python.
-
 * Hal says "We need a way to inspect/debug bits on the wire. `ntpdate
   -du' is the traditional approach."  Hal's straw man is a new program
   rather than trying to make ntpdig do this.  Eric's proposed answer:
-  ntpshark, a command interpreter written around ntpproto.py and
+  ntpshark, a command interpreter written around the ntp Python module and
   loosely modeled on wireshark.
 
-* Replace ntpq with a Python wrapper around ntpproto.py.
+* Replace ntpq with a Python wrapper around the ntp Python module.
 
 * We might be able to eliminate a lot of the Linux runtime
   droproot code by using file capabilities.
@@ -146,9 +139,6 @@ Neither is ideal, easy pickings for someone to code on.
   LIBNTP_C that needs to be refactored.  ntpd should *always* be built as
   a library linked to a main module, these guard symbols should go away.
 
-* seccomp sandboxing fails to build under Ubuntu due to some confusion
-  in the Linux headers.  Investigate.
-
 * Use the snprintb in util/ntptime for flag words like flash
   codes and use it systematically to make reports more readable.
 
@@ -213,14 +203,12 @@ __________
   the old HTML and should be cleaned up.  Requires HTML and CSS
   skills; intern must be willing to learn asciidoc
 
-* seccomp sandboxing fails to build under Ubuntu, apparently due to
-  some confusion in the Linux headers.  Investigate and report.
-  Requires basic C skills.
-
-* All Perl should be translated to Python for maintainability. Look
-  under scripts/. Requires Perl and Python skills.  It is possible
-  some of this is no longer useful - Daniel says some of it is in Perl 4!
+* All remaining Perl (presently ntpleapfetch, calc_tickadj and
+  ntpsweep) should be translated to Python for maintainability.
+  Requires Perl and Python skills.
 
 * In scripts/t/ are, apparently, unit tests for the stuff in scripts/.
   Some attempt should be made to salvage these. Requires Perl and
   Python skills.
+
+// end


=====================================
ntpwait/ntp
=====================================
--- /dev/null
+++ b/ntpwait/ntp
@@ -0,0 +1 @@
+../pylib
\ No newline at end of file


=====================================
ntpwait/ntpwait
=====================================
--- a/ntpwait/ntpwait
+++ b/ntpwait/ntpwait
@@ -1,120 +1,197 @@
-#!/usr/bin/env perl
-
-package ntp_wait;
-use 5.006_000;
-use strict;
-use warnings;
-use NTP::Util qw(ntp_read_vars);
-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 = {
-        'tries' => '100',
-        'sleep' => '6',
-        'verbose' => '',
-        'help' => '', 'more-help' => ''
-    };
-    my $argument = '';
-    my $ret = GetOptionsFromArray($args, $opts, (
-        'tries|n=i', 'sleep|s=i', 'verbose|v',
-        'help|?', 'more-help'));
-
-    $usage = <<'USAGE';
+#!/usr/bin/env python
+"""\
 ntpwait - Wait for ntpd to stabilize the system clock.
-USAGE: ntpwait [ -<flag> [<val>] | --<name>[{=| }<val>] ]...
+USAGE: ntpwait [-n tries] [-s sleeptime] [-v] [-h]
 
     -n, --tries=num              Number of times to check ntpd
     -s, --sleep=num              How long to sleep between tries
     -v, --verbose                Be verbose
-    -?, --help                   Display usage information and exit
-        --more-help              Pass the extended usage text through a pager
+    -h, --help                   Issue help
 
 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;
-}
-
-exit run(@ARGV) unless caller;
-
-sub run {
-    my $opts;
-    if (!processOptions(\@_, $opts)) {
-        usage(1);
-    };
-
-    my $tries   = $opts->{tries};	# How many tries before we give up? (10 min+)
-    my $sleep   = $opts->{sleep};	# Seconds to sleep between tries (6s = 10/min)
-    my $verbose = $opts->{verbose};	# Be verbose?
+A spurious 'not running' message can result from queries being disabled.
+"""
+#SPDX-License-Identifier: BSD-2-Clause
+from __future__ import print_function, division
+
+import os, sys, getopt, re, time
+from ntp.packet import *
+
+# General notes on Python 2/3 compatibility:
+#
+# This code uses the following strategy to allow it to run on both Python 2
+# and Python 3:
+#
+# - Use binary I/O to read/write data from/to files and subprocesses;
+#   where the exact bytes are important (such as in checking for
+#   modified files), use the binary data directly
+#
+# - Use latin-1 encoding to transform binary data to/from Unicode when
+#   necessary for operations where Python 3 expects Unicode; the
+#   polystr and polybytes functions are used to do this so that
+#   when running on Python 2, the byte string data is used unchanged;
+#   also, the make_wrapper function constructs a text stream that can
+#   wrap a file opened in binary mode for cases where a file object
+#   that can be passed around from function to function is needed
+#
+# - Construct custom stdin, stdout, and stderr streams when running
+#   on Python 3 that force latin-1 encoding, and wrap them around the
+#   underlying binary buffers (in Python 2, the streams are binary
+#   and are used unchanged); this ensures that the same transformation
+#   is done on data from/to the standard streams, as is done on binary
+#   data from/to files and subprocesses; the make_std_wrapper function
+#   does this
+
+master_encoding = 'latin-1'
+
+if str is bytes:  # Python 2
+    polystr = str
+    polybytes = bytes
+
+    def string_escape(s):
+        return s.decode('string_escape')
+
+    def make_wrapper(fp):
+        return fp
+
+else:  # Python 3
+
+    def polystr(o):
+        "Polymorphic string factory function"
+        if isinstance(o, str):
+            return o
+        if not isinstance(o, bytes):
+            return str(o)
+        return str(o, encoding=master_encoding)
+
+    def polybytes(s):
+        "Polymorphic string encoding function"
+        if isinstance(s, bytes):
+            return s
+        if not isinstance(s, str):
+            return bytes(s)
+        return bytes(s, encoding=master_encoding)
+
+    def string_escape(s):
+        "Polymorphic string_escape/unicode_escape"
+        # This hack is necessary because Unicode strings in Python 3 don't
+        # have a decode method, so there's no simple way to ask it for the
+        # equivalent of decode('string_escape') in Python 2. This function
+        # assumes that it will be called with a Python 3 'str' instance
+        return s.encode(master_encoding).decode('unicode_escape')
+
+    def make_wrapper(fp):
+        "Wrapper factory function to enforce master encoding"
+        # This can be used to wrap normally binary streams for API
+        # compatibility with functions that need a text stream in
+        # Python 3; it ensures that the binary bytes are decoded using
+        # the master encoding we use to turn bytes to Unicode in
+        # polystr above
+        # newline="\n" ensures that Python 3 won't mangle line breaks
+        return io.TextIOWrapper(fp, encoding=master_encoding, newline="\n")
+
+    def make_std_wrapper(stream):
+        "Standard input/output wrapper factory function"
+        # This ensures that the encoding of standard output and standard
+        # error on Python 3 matches the master encoding we use to turn
+        # bytes to Unicode in polystr above
+        # line_buffering=True ensures that interactive command sessions work as expected
+        return io.TextIOWrapper(stream.buffer, encoding=master_encoding, newline="\n", line_buffering=True)
+
+    sys.stdin = make_std_wrapper(sys.stdin)
+    sys.stdout = make_std_wrapper(sys.stdout)
+    sys.stderr = make_std_wrapper(sys.stderr)
+
+class Unbuffered(object):
+   def __init__(self, stream):
+       self.stream = stream
+   def write(self, data):
+       self.stream.write(data)
+       self.stream.flush()
+   def __getattr__(self, attr):
+       return getattr(self.stream, attr)
+
+if __name__ == "__main__":
+    try:
+        (options, arguments) = getopt.getopt(sys.argv[1:], "hn:s:v", [
+            "tries=", "sleep=", "verbose", "help"
+            ])
+    except getopt.GetoptError as err:
+        sys.stderr.write(str(err) + "\n")
+        raise SystemExit(2)
+    tries = 100
+    sleep = 6
+    verbose = 0
+    help = False
+    for (switch, val) in options:
+        if switch in ("-n", "--tries"):
+            tries = int(val)
+        elif switch in ("-s", "--sleep"):
+            sleep = int(val)
+        elif switch in ("-v", "--verbose"):
+            verbose += 1
+        elif switch in ("-h", "--help"):
+            sys.stdout.write(__doc__)
+            raise SystemExit(0)
 
     # Autoflush stdout
-    $| = 1;
-
-    print "Waiting for ntpd to synchronize...  " if $verbose;
-
-    for my $i (1 .. $tries) {
-        my $info = ntp_read_vars(0, []);
-
-        if (!defined $info) {
-            print "\bntpd is not running!\n" if $verbose;
-            return 1;
-        }
-
-        if (!exists $info->{status_line}{leap}) {
-            print "\bLeap status not avalaible\n";
-            return 1;
-        }
-
-        my $leap   = $info->{status_line}{leap};
-        my $sync   = $info->{status_line}{sync};
-
-        if ($leap =~ /(sync|leap)_alarm/) {
-            print "\b".(substr "*+:.", $i % 4, 1) if $verbose;
-            sleep $sleep if $i < $tries;
-            next;
-        }
-
-        if ($leap =~ /leap_(none|((add|del)_sec))/) {
-            # We could check $sync here to make sure we like the source...
-            print "\bOK!\n" if $verbose;
-            return 0;
-        }
-
-        print "\bUnexpected 'leap' status <$leap>\n";
-        return 1;
-    }
-
-    print "\bNo!\nntpd did not synchronize.\n" if $verbose;
-    return 1;
-}
-
-END { close STDOUT };
-
+    sys.stdout = Unbuffered(sys.stdout)
+
+    if verbose:
+        sys.stdout.write("Waiting for ntpd to synchronize...  ")
+
+    for i in range(1, tries):
+        session = ntpq_session()
+        #session.debug = 4
+        if not session.openhost("localhost"):
+            if verbose:
+                sys.stdout.write("\bntpd is not running!\n")
+            continue
+
+        try:
+            msg = session.doquery(2)	# Request system variables
+        except socket.error:
+            if verbose:
+                sys.stdout.write("\b" + "*+:."[i % 4])
+            time.sleep(sleep)
+            continue
+
+        if verbose >= 2:
+            sys.stderr.write(repr(session.response) + "\n")
+
+        if msg and msg.startswith("***"):
+            if verbose:
+                sys.stdout.write("\b" + msg + "\n")
+            sys.exit(1)
+
+        m = re.search(r"leap=([^,]*),", session.response)
+        if m:
+            leap = int(m.group(1))
+        else:
+            sys.stdout.write("\bLeap status not available\n")
+            sys.exit(1)
+
+        if leap == LEAP_NOTINSYNC:
+            if verbose:
+                sys.stdout.write("\b" + "*+:."[i % 4])
+            if i < tries:
+                time.sleep(sleep)
+            continue
+
+        if leap in (LEAP_NOWARNING, LEAP_ADDSECOND, LEAP_DELSECOND):
+            # We could check "sync" here to make sure we like the source...
+            if verbose:
+                sys.stdout.write("\bOK!\n")
+            sys.exit(0)
+
+        sys.stdout.write("\bUnexpected 'leap' status <%s>\n" % leap)
+        sys.exit(1)
+
+    if verbose:
+        sys.stdout.write("\bNo!\nntpd did not synchronize.\n")
+    sys.exit(1)
+
+# end
 
-1;
-__END__


=====================================
pylib/packet.py
=====================================
--- a/pylib/packet.py
+++ b/pylib/packet.py
@@ -59,6 +59,11 @@ NERR_NORESOURCE	= NERR_PERMISSION	# wish there was a different code
 PEERVARS  = CTL_OP_READVAR
 #CLOCKVARS = CTL_OP_CLOCKVAR
 
+LEAP_NOWARNING	= 0x0	# leap_none: normal, no leap second warning
+LEAP_ADDSECOND	= 0x1	# leap_add_sec: last minute of day has 61 seconds
+LEAP_DELSECOND	= 0x2	# leap_del_sec: last minute of day has 59 seconds
+LEAP_NOTINSYNC	= 0x3	# leap_alarm: overload, clock is free running
+
 NTP_OLDVERSION	= 1	# C code said "oldest credible version"
 NTP_VERSION	= 4	# Current version
 
@@ -257,7 +262,7 @@ class ntpq_session:
         except AttributeError:
             sys.stderr.write("ntpq: API error, missing socket attributes\n")
         return None
-    def openhost(self, hname, fam):
+    def openhost(self, hname, fam=socket.AF_UNSPEC):
         "openhost - open a socket to a host"
         res = self.__lookuphost(hname, fam)
         if res is None:
@@ -468,13 +473,13 @@ class ntpq_session:
                     if fragments[f-1].endpoint() != fragments[f].offset:
                         break
                 else:
-                    warn("%d packets reassembled\n" % len(fragments))
+                    #warn("%d packets reassembled\n" % len(fragments))
                     self.response = "".join([frag.data for frag in fragments])
                     if self.debug >= 4:
                         sys.stdout.write("Response packet:\n")
                         dump_hex_printable(self.response)
                     return None
-    def doquery(self, opcode, associd, qdata, auth=False, quiet=False):
+    def doquery(self, opcode, associd=0, qdata="", auth=False, quiet=False):
         "send a request and save the response"
         if not self.havehost():
             return SERR_NOHOST



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/319bbbb7f168e16b6a2715132c451171edea9a28...c02201dbd432943c199e8459d4cf046c33151a14
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ntpsec.org/pipermail/vc/attachments/20160829/b4b076c0/attachment.html>


More information about the vc mailing list