[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