[Git][NTPsec/ntpsec][ntp.util-codacy] 19 commits: Add dedicated CI image for Coverity

James Browning gitlab at mg.gitlab.com
Mon Oct 12 22:53:38 UTC 2020



James Browning pushed to branch ntp.util-codacy at NTPsec / ntpsec


Commits:
aa7e4cda by Matt Selsky at 2020-09-23T23:13:48-04:00
Add dedicated CI image for Coverity

- - - - -
d4c07483 by Matt Selsky at 2020-09-24T05:42:22+00:00
Switch to using dedicated Coverity CI image

NTPsec/ntpsec#669

- - - - -
faae6b74 by Richard Laager at 2020-09-24T16:53:49-05:00
INSTALL: Use env for python3 shebang

This avoids assumptions about where in the PATH python3 lives.

- - - - -
54a43ba6 by Richard Laager at 2020-09-24T16:54:43-05:00
Document pyshebang in NEWS

Fixes #675

- - - - -
366569b1 by James Browning at 2020-09-27T00:58:00+00:00
Add and by default build/use ctypes wrapper for FFI ntp library...

- - - - -
78a96b1e by James Browning at 2020-09-27T00:58:52+00:00
ntpq: Add noflake and doflake for packet loss simulation.




- - - - -
c6147f6b by James Browning at 2020-09-27T00:58:52+00:00
mode 6: Allow returning multiple specific MRU entries ...

if limit == 1 and multiple addresses provided.
mode 6: Solve packet storm when mrulist limit=1
- - - - -
4cb4e4c3 by James Browning at 2020-09-27T00:58:52+00:00
pylib: add first 16 addr & last arguments to mrulist


- - - - -
cb655c22 by James Browning at 2020-09-26T18:25:35-07:00
Update documentation and NEWS.


- - - - -
7657a68c by Hal Murray at 2020-09-27T23:42:01-07:00
typo: s => ms

- - - - -
60fb5a8b by James Browning at 2020-10-01T02:07:14-07:00
doc: Revise mrulist documentation to be less opaque.

- - - - -
e880627f by James Browning at 2020-10-01T07:35:33-07:00
doc: Repahrasing in ntpq and mode 6 docs/comments.

- - - - -
b675e786 by James Browning at 2020-10-01T08:34:15-07:00
docs: Specify starting point for ntpq/mode6 mrulist.

- - - - -
1feb8f90 by Mark Atwood at 2020-10-06T04:23:34+00:00
version 1.2.0

Signed-off-by: Mark Atwood <mark.atwood at ntpsec.org>

- - - - -
be91c5ca by Mark Atwood at 2020-10-06T05:20:11+00:00
add new gpg public key

- - - - -
13b02aab by Mark Atwood at 2020-10-06T05:22:27+00:00
start new NEWS.adoc section

- - - - -
2cd86676 by Mark Atwood at 2020-10-08T02:11:24+00:00
add new gpg pubkey and minisign pubkey

- - - - -
f5738bd0 by James Browning at 2020-10-12T14:57:42-07:00
Most sh code style fixes option testers


- - - - -
da92a66e by James Browning at 2020-10-12T15:18:19-07:00
Codacy Python codestyle: solve some pyflakes issues.

- - - - -


29 changed files:

- + .dockerfiles/coverity
- .gitlab-ci-docker-images.yml
- .gitlab-ci.yml
- INSTALL.adoc
- NEWS.adoc
- VERSION
- − devel/gpgkey.adoc
- + devel/ntpsec.gpg.pub.asc
- + devel/ntpsec.minisign.pub
- devel/release
- docs/asciidoc.css
- docs/debug.adoc
- docs/includes/ntpq-body.adoc
- docs/rate.adoc
- + libntp/ntp_c.c
- libntp/wscript
- ntpclients/ntploggps.py
- ntpclients/ntpmon.py
- ntpclients/ntpq.py
- ntpclients/ntpviz.py
- ntpd/ntp_control.c
- + pylib/ntpc.py
- pylib/packet.py
- pylib/wscript
- tests/option-tester.sh
- tests/python2-tester.sh
- tests/python3-tester.sh
- wafhelpers/options.py
- wscript


Changes:

=====================================
.dockerfiles/coverity
=====================================
@@ -0,0 +1,6 @@
+# Coverity is picky about the versions of gcc that it has support for, so
+# please test this thoroughly before switching to a newer Debian distribution
+FROM debian:stable-slim
+
+RUN apt-get update
+RUN apt-get install -y netbase bison gcc libssl-dev libcap-dev pps-tools python3-dev libbsd-dev curl git


=====================================
.gitlab-ci-docker-images.yml
=====================================
@@ -41,6 +41,9 @@ build-centos-latest:
 build-clang:
   <<: *build_definition
 
+build-coverity:
+  <<: *build_definition
+
 build-debian-oldstable:
   <<: *build_definition
 


=====================================
.gitlab-ci.yml
=====================================
@@ -371,9 +371,8 @@ gentoo-hardened-refclocks:
     - python ./waf configure --disable-doc --disable-manpage --refclock=all build
 
 coverity-scan:
-  image: $CI_REGISTRY/ntpsec/ntpsec/debian-unstable
+  image: $CI_REGISTRY/ntpsec/ntpsec/coverity
   script:
-    - apt-get install -y curl git
     - curl -o /tmp/cov-analysis-linux64.tgz --form project=ntpsec --form token="${COVERITY_TOKEN}" https://scan.coverity.com/download/linux64
     - tar zxf /tmp/cov-analysis-linux64.tgz
     - python3 ./waf configure --disable-doc --disable-manpage --refclock=all


=====================================
INSTALL.adoc
=====================================
@@ -55,7 +55,7 @@ Python 2.x, x >= 6, or Python 3.x, x >= 3::
 
    If you are running on a distro with no `python` executable, you will
    need to run waf as `python3 waf` rather than `./waf`.  You will also
-   need `python3 waf configure --pyshebang=/usr/bin/python3` or similar.
+   need `python3 waf configure --pyshebang=/usr/bin/env python3` or similar.
 
    Some OS distros won't find our installed python libraries.
    More info in README-PYTHON.


=====================================
NEWS.adoc
=====================================
@@ -12,11 +12,34 @@ on user-visible changes.
 
 == Repository Head ==
 
+== 2020-10-06: 1.2.0 ==
+
+The minor version bump is to indicate official official support of
+RFC8915 "Network Time Security for the Network Time Protocol" which
+was released 2020-09-30.
+
+On this day in 1783, Benjamin Hanks received a patent for a
+self-winding clock he planned to install in the Old Dutch Church in
+Kingston, New York, supposedly making it the first public clock in
+what became the New York City metropolitan area.
+ 
 NTS-KE client now defaults to port 4460.
 
 NTS-KE server now listens on port 4460.
 (Listening on port 123 has been removed.)
 
+The shebang of installed Python scripts can now be customized with:
+  waf configure --pyshebang="..."
+This has multiple uses, but one example is for distros (like CentOS 8 or
+Ubuntu 20.04) with no `python` executable:
+  python3 waf configure --pyshebang="/usr/bin/env python3"
+
+NTP clients now use a shared library with Python instead of an extension..
+
+Add flakiness option to ntpq and fixed limit=1 in mrulist.
+
+Fixed a minor formatting issue in rate page.
+
 == 2020-05-23: 1.1.9 ==
 
 Today is Blursday, Maprilay 84th, 2020, of the COVID-19 panic.


=====================================
VERSION
=====================================
@@ -1 +1 @@
-1.1.9
+1.2.0


=====================================
devel/gpgkey.adoc deleted
=====================================
@@ -1,101 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-
-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
-21g=
-=hdEX
------END PGP PUBLIC KEY BLOCK-----


=====================================
devel/ntpsec.gpg.pub.asc
=====================================
@@ -0,0 +1,134 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
++z7an3UhBdX8UAhjgSOt3rzVZiNxyp5N0w==
+=9YTT
+-----END PGP PUBLIC KEY BLOCK-----


=====================================
devel/ntpsec.minisign.pub
=====================================
@@ -0,0 +1,2 @@
+untrusted comment: minisign public key DD4AB70D11D6D8BF
+RWS/2NYRDbdK3ZPLenPqKnYX5fqx1PB+zhaTYcOmECxFSkpNDWxzBfy5


=====================================
devel/release
=====================================
@@ -3,7 +3,8 @@
 #
 # VERSION must contain the correct release number.
 
-KEY=B48237761A2690222C995F445A22E330161C3978	# Release manager's GPG key ID
+# KEY=B48237761A2690222C995F445A22E330161C3978
+KEY=E57235D22764129FA4F2F4D17F52608ED0E49D76	# Release manager's GPG key ID
 DST=markatwood at service1.ntpsec.org:/usr/local/jail/ftp.ntpsec.org/data/ftp/pub/releases/
 
 # No user-serviceable parts below this line


=====================================
docs/asciidoc.css
=====================================
@@ -1,88 +1,170 @@
+ at charset utf-8;
+
 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
 
 /* Dave Mills's CSS definitions first */
+body {
+  background: #fdf1e1;
+  color: #060;
+  font-family: "verdana", sans-serif;
+  margin-left: 5px;
+  text-align: justify;
+}
 
-body {background: #FDF1E1;
-      color: #006600;
-      font-family: "verdana", sans-serif;
-      text-align: justify;
-      margin-left: 5px;}
+p,
+h4,
+hr,
+li {
+  margin-top: 0.6em;
+  margin-bottom: 0.6em;
+}
 
-p, h4, hr, li {margin-top: .6em; margin-bottom: .6em}
-li.inline {text-align: left; margin-top: 0; margin-bottom: 0}
+li.inline {
+  text-align: left;
+  margin-top: 0;
+  margin-bottom: 0;
+}
 
-ul, dl, ol, {margin-top: .6em; margin-bottom: .6em; margin-left 5em}
+ul,
+dl,
+ol {
+  margin-top: 0.6em;
+  margin-bottom: 0.6em;
+  margin-left: 5em;
+}
 
-dt {margin-top: .6em}
-dd {margin-bottom: .6em}
+dt {
+  margin-top: 0.6em;
+  color: #060;
+  font-style: normal;
+  margin-bottom: 0;
+  /* margin-top: 0.5em; */
+  /* color: navy; */
+}
 
-div.header {text-align: center;
-            font-style: italic;}
+dd {
+  margin-bottom: 0.6em;
+}
 
-div.footer {text-align: center;
-            font-size: 60%;}
+div.header {
+  font-style: italic;
+  text-align: center;
+}
 
-img.cell {align: left;}
+div.footer {
+  font-size: 60%;
+  text-align: center;
+}
 
-td.sidebar {width: 40px; align: center; valign: top;}
-img.sidebar {align: center; margin-top: 5px;}
-h4.sidebar {align: center;}
+img.cell {
+  /* align: left; */
+}
 
-p.top {background: #FDF1E1;
-       color: #006600;
-       position: absolute;
-       margin-left: -90px;
-       text-align: center;}
+td.sidebar {
+  width: 40px;
+/*
+  align: center;
+  valign: top;
+*/
+}
 
-a:link.sidebar {background: transparent;
-                color: #990033;
-                font-weight: bold;}
+img.sidebar {
+  /* align: center; */
+  margin-top: 5px;
+}
 
-a:visited.sidebar {background: transparent;
-                   color: #990033;
-                   font-weight: bold;}
+h4.sidebar {
+  /* align: center; */
+}
 
-a:hover.sidebar {background: #FDF1E1;
-                 color: #006600;}
+p.top {
+  background: #fdf1e1;
+  color: #060;
+  margin-left: -90px;
+  position: absolute;
+  text-align: center;
+}
 
-img {margin: 5px;}
+a:link.sidebar {
+  background: transparent;
+  color: #903;
+  font-weight: bold;
+}
 
-/* div {text-align: center;} */
+a:visited.sidebar {
+  background: transparent;
+  color: #903;
+  font-weight: bold;
+}
 
-h1 {text-align: center;
-    font-size: 250%;}
+a:hover.sidebar {
+  background: #fdf1e1;
+  color: #060;
+}
 
-caption {background: #EEEEEE;
-         color: #339999;}
+img {
+  margin: 5px;
+}
 
-tx {text-align: center;}
+/* div {
+  text-align: center;
+}
 
-th {background: #FFFFCC;
-    color: #006600;
-    text-align: center;
-    text-decoration: underline;
-    padding-top: 5px;}
+*/
+h1 {
+  font-size: 250%;
+  text-align: center;
+}
 
-th.caption {background: #EEEEEE;
-            color: #006600;
-            text-align: center;}
+caption {
+  background: #eee;
+  color: #399;
+}
 
+/*
+tx {
+  text-align: center;
+}
+*/
+
+th {
+  background: #ffc;
+  color: #060;
+  padding-top: 5px;
+  text-align: center;
+  text-decoration: underline;
+}
+
+th.caption {
+  background: #eee;
+  color: #060;
+  text-align: center;
+}
 
 /* hacked version of asciidoc stylesheet */
 
 /* remove asciidoc's body declaration */
 
 /* Title font. */
-h1, h2, h3, h4, h5, h6,
-div.title, caption.title,
-thead, p.table.header,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+div.title,
+caption.title,
+thead,
+p.table.header,
 #toctitle,
-#author, #revnumber, #revdate, #revremark,
+#author,
+#revnumber,
+#revdate,
+#revremark,
 #footer {
-  font-family: Arial,Helvetica,sans-serif;
+  font-family: Arial, Helvetica, sans-serif;
 }
 
-
 /* remove asciidoc's body margin declaration */
 
 /* remove asciidoc's messing with the link colors */
@@ -92,27 +174,38 @@ thead, p.table.header,
 /* remove asciidoc's styling for strong */
 
 /* use asciidoc's header styling with Dave's color */
-h1, h2, h3, h4, h5, h6 {
-  color: #006600;
-  margin-top: 1.2em;
-  margin-bottom: 0.5em;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  color: #060;
   line-height: 1.3;
+  margin-bottom: 0.5em;
+  margin-top: 1.2em;
 }
 
-h1, h2, h3 {
+h1,
+h2,
+h3 {
   border-bottom: 2px solid silver;
 }
+
 h2 {
   padding-top: 0.5em;
 }
+
 h3 {
   float: left;
 }
+
 h3 + * {
   clear: left;
 }
+
 h5 {
-  font-size: 1.0em;
+  font-size: 1em;
 }
 
 div.sectionbody {
@@ -124,116 +217,144 @@ hr {
 }
 
 p {
-  margin-top: 0.5em;
   margin-bottom: 0.5em;
+  margin-top: 0.5em;
 }
 
-ul, ol, li > p {
+ul,
+ol,
+li > p {
   margin-top: 0;
 }
-/* ul > li     { color: #aaa; } */
-/* ul > li > * { color: black; } */
 
-.monospaced, code, pre {
+/*
+ul > li {
+  color: #aaa;
+}
+
+ul > li > * {
+  color: black;
+}
+*/
+
+.monospaced,
+code,
+pre {
+  color: #006600;
   font-family: "Courier New", Courier, monospace;
   font-size: inherit;
-  /* color: navy; */
-  color: #006600;
-  padding: 0;
   margin: 0;
+  padding: 0;
+
+  /* color: navy; */
 }
+
 pre {
   white-space: pre-wrap;
 }
 
 #author {
-  /* color: #527bbd; */
-  font-weight: bold;
   font-size: 1.1em;
-}
-#email {
-}
-#revnumber, #revdate, #revremark {
+  font-weight: bold;
+
+  /* color: #527bbd; */
 }
 
 #footer {
-  font-size: small;
   border-top: 2px solid silver;
+  font-size: small;
+  margin-top: 4em;
   padding-top: 0.5em;
-  margin-top: 4.0em;
 }
+
 #footer-text {
   float: left;
   padding-bottom: 0.5em;
 }
+
 #footer-badges {
   float: right;
   padding-bottom: 0.5em;
 }
 
 #preamble {
-  margin-top: 1.5em;
   margin-bottom: 1.5em;
+  margin-top: 1.5em;
 }
-div.imageblock, div.exampleblock, div.verseblock,
-div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+
+div.imageblock,
+div.exampleblock,
+div.verseblock,
+div.quoteblock,
+div.literalblock,
+div.listingblock,
+div.sidebarblock,
 div.admonitionblock {
-  margin-top: 1.0em;
   margin-bottom: 1.5em;
+  margin-top: 1em;
 }
+
 div.admonitionblock {
-  margin-top: 2.0em;
-  margin-bottom: 2.0em;
+  margin-bottom: 2em;
   margin-right: 10%;
+  margin-top: 2em;
+
   /* color: #606060; */
 }
 
-div.content { /* Block element content. */
+div.content {
+  /* Block element content. */
   padding: 0;
 }
 
 /* Block element titles. */
-div.title, caption.title {
-  /* color: #527bbd; */
+div.title,
+caption.title {
   font-weight: bold;
-  text-align: left;
-  margin-top: 1.0em;
   margin-bottom: 0.5em;
+  margin-top: 1em;
+  text-align: left;
+
+  /* color: #527bbd; */
 }
+
 div.title + * {
   margin-top: 0;
 }
 
 td div.title:first-child {
-  margin-top: 0.0em;
-}
-div.content div.title:first-child {
-  margin-top: 0.0em;
+  margin-top: 0;
 }
+
 div.content + div.title {
-  margin-top: 0.0em;
+  margin-top: 0;
+}
+
+div.content div.title:first-child {
+  margin-top: 0;
 }
 
 div.sidebarblock > div.content {
-  background: #ffffee;
-  border: 1px solid #dddddd;
+  background: #ffe;
+  border: 1px solid #ddd;
   border-left: 4px solid #f0f0f0;
   padding: 0.5em;
 }
 
 div.listingblock > div.content {
-  border: 1px solid #dddddd;
-  border-left: 5px solid #f0f0f0;
   background: #f8f8f8;
+  border: 1px solid #ddd;
+  border-left: 5px solid #f0f0f0;
   padding: 0.5em;
 }
 
-div.quoteblock, div.verseblock {
-  padding-left: 1.0em;
-  margin-left: 1.0em;
-  margin-right: 10%;
+div.quoteblock,
+div.verseblock {
   border-left: 5px solid #f0f0f0;
   color: #888;
+  margin-left: 1em;
+  margin-right: 10%;
+  padding-left: 1em;
 }
 
 div.quoteblock > div.attribution {
@@ -245,104 +366,125 @@ div.verseblock > pre.content {
   font-family: inherit;
   font-size: inherit;
 }
+
 div.verseblock > div.attribution {
   padding-top: 0.75em;
   text-align: left;
 }
+
 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
 div.verseblock + div.attribution {
   text-align: left;
 }
 
 div.admonitionblock .icon {
-  vertical-align: top;
+  color: #527bbd;
   font-size: 1.1em;
   font-weight: bold;
-  text-decoration: underline;
-  color: #527bbd;
   padding-right: 0.5em;
+  text-decoration: underline;
+  vertical-align: top;
 }
+
 div.admonitionblock td.content {
+  border-left: 3px solid #ddd;
   padding-left: 0.5em;
-  border-left: 3px solid #dddddd;
 }
 
 div.exampleblock > div.content {
-  border-left: 3px solid #dddddd;
+  border-left: 3px solid #ddd;
   padding-left: 0.5em;
 }
 
-div.imageblock div.content { padding-left: 0; }
-span.image img { border-style: none; vertical-align: text-bottom; }
-a.image:visited { color: white; }
+div.imageblock div.content {
+  padding-left: 0;
+}
+
+span.image img {
+  border-style: none;
+  vertical-align: text-bottom;
+}
+
+a.image:visited {
+  color: white;
+}
 
 dl {
-  margin-top: 0.8em;
   margin-bottom: 0.8em;
+  margin-top: 0.8em;
 }
-dt {
-  margin-top: 0.5em;
-  margin-bottom: 0;
-  font-style: normal;
-  /* color: navy; */
-  color: #006600;
-}
+
 dd > *:first-child {
   margin-top: 0.1em;
 }
 
-ul, ol {
-    list-style-position: outside;
+ul,
+ol {
+  list-style-position: outside;
 }
+
 ol.arabic {
   list-style-type: decimal;
 }
+
 ol.loweralpha {
   list-style-type: lower-alpha;
 }
+
 ol.upperalpha {
   list-style-type: upper-alpha;
 }
+
 ol.lowerroman {
   list-style-type: lower-roman;
 }
+
 ol.upperroman {
   list-style-type: upper-roman;
 }
 
-div.compact ul, div.compact ol,
-div.compact p, div.compact p,
-div.compact div, div.compact div {
-  margin-top: 0.1em;
+div.compact ul,
+div.compact ol,
+div.compact p,
+div.compact div {
   margin-bottom: 0.1em;
+  margin-top: 0.1em;
 }
 
 tfoot {
   font-weight: bold;
 }
+
 td > div.verse {
   white-space: pre;
 }
 
 div.hdlist {
-  margin-top: 0.8em;
   margin-bottom: 0.8em;
+  margin-top: 0.8em;
 }
+
 div.hdlist tr {
   padding-bottom: 15px;
 }
-dt.hdlist1.strong, td.hdlist1.strong {
+
+dt.hdlist1.strong,
+td.hdlist1.strong {
   font-weight: bold;
 }
+
 td.hdlist1 {
-  vertical-align: top;
   font-style: normal;
   padding-right: 0.8em;
+  vertical-align: top;
+
   /* color: navy; */
 }
+
 td.hdlist2 {
   vertical-align: top;
 }
+
 div.hdlist.compact tr {
   margin: 0;
   padding-bottom: 0;
@@ -352,11 +494,13 @@ div.hdlist.compact tr {
   background: yellow;
 }
 
-.footnote, .footnoteref {
+.footnote,
+.footnoteref {
   font-size: 0.8em;
 }
 
-span.footnote, span.footnoteref {
+span.footnote,
+span.footnoteref {
   vertical-align: super;
 }
 
@@ -373,23 +517,26 @@ span.footnote, span.footnoteref {
   border: none;
   border-top: 1px solid silver;
   height: 1px;
-  text-align: left;
   margin-left: 0;
-  width: 20%;
   min-width: 100px;
+  text-align: left;
+  width: 20%;
 }
 
 div.colist td {
-  padding-right: 0.5em;
   padding-bottom: 0.3em;
+  padding-right: 0.5em;
   vertical-align: top;
 }
+
 div.colist td img {
   margin-top: 0.3em;
 }
 
 @media print {
-  #footer-badges { display: none; }
+  #footer-badges {
+    display: none;
+  }
 }
 
 #toc {
@@ -397,188 +544,319 @@ div.colist td img {
 }
 
 #toctitle {
-  /* color: #527bbd; */
   font-size: 1.1em;
   font-weight: bold;
-  margin-top: 1.0em;
   margin-bottom: 0.1em;
+  margin-top: 1em;
+
+  /* color: #527bbd; */
 }
 
-div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
-  margin-top: 0;
+div.toclevel0,
+div.toclevel1,
+div.toclevel2,
+div.toclevel3,
+div.toclevel4 {
   margin-bottom: 0;
+  margin-top: 0;
 }
+
 div.toclevel2 {
-  margin-left: 2em;
   font-size: 0.9em;
+  margin-left: 2em;
 }
+
 div.toclevel3 {
-  margin-left: 4em;
   font-size: 0.9em;
+  margin-left: 4em;
 }
+
 div.toclevel4 {
-  margin-left: 6em;
   font-size: 0.9em;
+  margin-left: 6em;
 }
 
-span.aqua { color: aqua; }
-span.black { color: black; }
-span.blue { color: blue; }
-span.fuchsia { color: fuchsia; }
-span.gray { color: gray; }
-span.green { color: green; }
-span.lime { color: lime; }
-span.maroon { color: maroon; }
-span.navy { color: navy; }
-span.olive { color: olive; }
-span.purple { color: purple; }
-span.red { color: red; }
-span.silver { color: silver; }
-span.teal { color: teal; }
-span.white { color: white; }
-span.yellow { color: yellow; }
-
-span.aqua-background { background: aqua; }
-span.black-background { background: black; }
-span.blue-background { background: blue; }
-span.fuchsia-background { background: fuchsia; }
-span.gray-background { background: gray; }
-span.green-background { background: green; }
-span.lime-background { background: lime; }
-span.maroon-background { background: maroon; }
-span.navy-background { background: navy; }
-span.olive-background { background: olive; }
-span.purple-background { background: purple; }
-span.red-background { background: red; }
-span.silver-background { background: silver; }
-span.teal-background { background: teal; }
-span.white-background { background: white; }
-span.yellow-background { background: yellow; }
-
-span.big { font-size: 2em; }
-span.small { font-size: 0.6em; }
-
-span.underline { text-decoration: underline; }
-span.overline { text-decoration: overline; }
-span.line-through { text-decoration: line-through; }
-
-div.unbreakable { page-break-inside: avoid; }
+span.aqua {
+  color: aqua;
+}
 
+span.black {
+  color: black;
+}
 
-/*
- * xhtml11 specific
- *
- * */
+span.blue {
+  color: blue;
+}
+
+span.fuchsia {
+  color: fuchsia;
+}
+
+span.gray {
+  color: gray;
+}
+
+span.green {
+  color: green;
+}
+
+span.lime {
+  color: lime;
+}
+
+span.maroon {
+  color: maroon;
+}
 
+span.navy {
+  color: navy;
+}
+
+span.olive {
+  color: olive;
+}
+
+span.purple {
+  color: purple;
+}
+
+span.red {
+  color: red;
+}
+
+span.silver {
+  color: silver;
+}
+
+span.teal {
+  color: teal;
+}
+
+span.white {
+  color: white;
+}
+
+span.yellow {
+  color: yellow;
+}
+
+span.aqua-background {
+  background: aqua;
+}
+
+span.black-background {
+  background: black;
+}
+
+span.blue-background {
+  background: blue;
+}
+
+span.fuchsia-background {
+  background: fuchsia;
+}
+
+span.gray-background {
+  background: gray;
+}
+
+span.green-background {
+  background: green;
+}
+
+span.lime-background {
+  background: lime;
+}
+
+span.maroon-background {
+  background: maroon;
+}
+
+span.navy-background {
+  background: navy;
+}
+
+span.olive-background {
+  background: olive;
+}
+
+span.purple-background {
+  background: purple;
+}
+
+span.red-background {
+  background: red;
+}
+
+span.silver-background {
+  background: silver;
+}
+
+span.teal-background {
+  background: teal;
+}
+
+span.white-background {
+  background: white;
+}
+
+span.yellow-background {
+  background: yellow;
+}
+
+span.big {
+  font-size: 2em;
+}
+
+span.small {
+  font-size: 0.6em;
+}
+
+span.underline {
+  text-decoration: underline;
+}
+
+span.overline {
+  text-decoration: overline;
+}
+
+span.line-through {
+  text-decoration: line-through;
+}
+
+div.unbreakable {
+  page-break-inside: avoid;
+}
+
+/* * xhtml11 specific * * */
 div.tableblock {
-  margin-top: 1.0em;
   margin-bottom: 1.5em;
+  margin-top: 1em;
 }
+
 div.tableblock > table {
   border: 3px solid #527bbd;
 }
-thead, p.table.header {
+
+thead,
+p.table.header {
   font-weight: bold;
-  /* color: #527bbd; */
+
+  /* color: #527bbd;
+  */
 }
+
 p.table {
   margin-top: 0;
 }
+
 /* Because the table frame attribute is overridden by CSS in most browsers. */
 div.tableblock > table[frame="void"] {
   border-style: none;
 }
+
 div.tableblock > table[frame="hsides"] {
   border-left-style: none;
   border-right-style: none;
 }
+
 div.tableblock > table[frame="vsides"] {
-  border-top-style: none;
   border-bottom-style: none;
+  border-top-style: none;
 }
 
-
-/*
- * html5 specific
- *
- * */
-
+/* * html5 specific * * */
 table.tableblock {
-  margin-top: 1.0em;
   margin-bottom: 1.5em;
+  margin-top: 1em;
+  border-collapse: collapse;
+  border-color: #527bbd;
+  border-spacing: 0;
+  border-style: solid;
+  border-width: 3px;
 }
-thead, p.tableblock.header {
-  font-weight: bold;
+
+thead,
+p.tableblock.header {
   color: #527bbd;
+  font-weight: bold;
 }
+
 p.tableblock {
   margin-top: 0;
 }
-table.tableblock {
-  border-width: 3px;
-  border-spacing: 0px;
-  border-style: solid;
+
+th.tableblock,
+td.tableblock {
   border-color: #527bbd;
-  border-collapse: collapse;
-}
-th.tableblock, td.tableblock {
+  border-style: solid;
   border-width: 1px;
   padding: 4px;
-  border-style: solid;
-  border-color: #527bbd;
 }
 
 table.tableblock.frame-topbot {
   border-left-style: hidden;
   border-right-style: hidden;
 }
+
 table.tableblock.frame-sides {
-  border-top-style: hidden;
   border-bottom-style: hidden;
+  border-top-style: hidden;
 }
-table.tableblock.frame-none, table.tableblock.frame-none td {
+
+table.tableblock.frame-none,
+table.tableblock.frame-none td {
   border-style: hidden;
 }
 
-th.tableblock.halign-left, td.tableblock.halign-left {
+th.tableblock.halign-left,
+td.tableblock.halign-left {
   text-align: left;
 }
-th.tableblock.halign-center, td.tableblock.halign-center {
+
+th.tableblock.halign-center,
+td.tableblock.halign-center {
   text-align: center;
 }
-th.tableblock.halign-right, td.tableblock.halign-right {
+
+th.tableblock.halign-right,
+td.tableblock.halign-right {
   text-align: right;
 }
 
-th.tableblock.valign-top, td.tableblock.valign-top {
+th.tableblock.valign-top,
+td.tableblock.valign-top {
   vertical-align: top;
 }
-th.tableblock.valign-middle, td.tableblock.valign-middle {
+
+th.tableblock.valign-middle,
+td.tableblock.valign-middle {
   vertical-align: middle;
 }
-th.tableblock.valign-bottom, td.tableblock.valign-bottom {
+
+th.tableblock.valign-bottom,
+td.tableblock.valign-bottom {
   vertical-align: bottom;
 }
 
-
-/*
- * manpage specific
- *
- * */
-
+/* * manpage specific * * */
 body.manpage h1 {
-  padding-top: 0.5em;
-  padding-bottom: 0.5em;
-  border-top: 2px solid silver;
   border-bottom: 2px solid silver;
+  border-top: 2px solid silver;
+  padding-bottom: 0.5em;
+  padding-top: 0.5em;
 }
+
 body.manpage h2 {
   border-style: none;
 }
+
 body.manpage div.sectionbody {
   margin-left: 3em;
 }
 
 @media print {
-  body.manpage div#toc { display: none; }
+  body.manpage div#toc {
+    display: none;
+  }
 }


=====================================
docs/debug.adoc
=====================================
@@ -75,7 +75,7 @@ restarts automatically.
 
 When started for the first time and a frequency file - usually ntp.drift -
 is not present, the daemon enters a special mode in order to calibrate the
-frequency. This takes 900 s during which the time is not disciplined. When
+frequency. This takes 900 ms during which the time is not disciplined. When
 calibration is complete, the daemon creates the frequency file and enters
 normal mode to amortize whatever residual offset remains.
 


=====================================
docs/includes/ntpq-body.adoc
=====================================
@@ -150,6 +150,12 @@ as following.
 +debug more | less | off+::
   Turns internal query program debugging on and off.
 
++noflake+::
++doflake 'probability'::
+  Disables or enables the dropping of control packets by ntpq for testing.
+  Probabilities 0 and 1 should be certainly accepted and discarded
+  respectively. No default, but 0.1 should be a one in ten loss rate.
+
 +logfile <stderr> | filename+::
   Displays or sets the file for debug logging. <stderr> will send logs
   to standard error.
@@ -301,26 +307,44 @@ ind assid status conf reach auth condition last_event cnt
   server so loaded that none of its MRU entries age out before they
   are shipped. With this option, each segment is reported as it arrives.
 
-[[mrulist]]+mrulist+ [+limited+ | +kod+ | +mincount=+'count' | +mindrop=+'drop' | +minscore=+'score' | +maxlstint=+'seconds' | +minlstint=+'seconds' | +laddr=+'localaddr' | +sort=+'sortorder' | +resany=+'hexmask' | +resall=+'hexmask']::
+[[mrulist]]+mrulist+ [+limited+ | +kod+ | +mincount=+'count' | +mindrop=+'drop' | +minscore=+'score' | +maxlstint=+'seconds' | +minlstint=+'seconds' | +laddr=+'localaddr' | +sort=+'sortorder' | +resany=+'hexmask' | +resall=+'hexmask' | +limit=+'limit' |
++addr.'num'=+'address']::
   Obtain and print traffic counts collected and maintained by the
   monitor facility. This is useful for tracking who _uses_ or
   _abuses_ your server.
 +
-With the exception of +sort=+'sortorder', the options
+Except for +sort=+'sortorder', the options
 filter the list returned by +ntpd+. The +limited+ and +kod+ options
 return only entries representing client addresses from which the last
 packet received triggered either discarding or a KoD response.
-The +mincount=+'count' option filters entries that have received less
+the +addr.'num'=+ option adds specific addresses to retrieve when
++limit=+'1'. Values of 0 to 15 are supported for 'num'. Also, used
+internally with +last.'num'=+'hextime' to select the starting point
+for retrieving continued response.
+the +frags=+'frags' option limits the number of datagrams
+(fragments) in response.  Used by newer ntpq versions instead
+of limit= when retrieving multiple entries.
+The +limit=+ option limits the MRU entries returned per response.
+limit=1 is a special case:  Instead of fetching beginning with
+the supplied starting points (provided by a last.x and addr.x
+where 0 <= x <= 15, default the beginning of time) newer neighbor,
+fetch the supplied entries. This enables fetching multiple entries
+from given IP addresses (provided by addr.x= entries where 0 <= x
+<= 15). When limit is not one and frags= is provided,
+the fragment limit controls. NOTE: a single mrulist command may
+cause many query/response rounds allowing limits as low as 3 to
+potentially retrieve thousands of entries in responses.
+The +mincount=+'count' option filters out entries that have received less
 than 'count' packets.
-The +mindrop=+'drop' option filters entries that have dropped less
+The +mindrop=+'drop' option filters out entries that have dropped less
 than 'drop' packets.
-The +minscore=+'score' option filters entries with a score less
+The +minscore=+'score' option filters out entries with a score less
 than 'score'.
-The +maxlstint=+'seconds' option filters entries where no packets have
+The +maxlstint=+'seconds' option filters out entries where no packets have
 arrived within 'seconds'.
-The +minlstint=+'seconds' option filters entries with a packet has
+The +minlstint=+'seconds' option filters out entries with a packet has
 arrived within 'seconds'.
-The +laddr=+'localaddr' option filters entries for packets
+The +laddr=+'localaddr' option filters out entries for packets
 received on any local address other than 'localaddr'. +resany=+'hexmask'
 and +resall=+'hexmask' filter entries containing none or less than all,
 respectively, of the bits in 'hexmask', which must begin with +0x+.


=====================================
docs/rate.adoc
=====================================
@@ -45,8 +45,7 @@ overload on the Network Time Protocol public servers. _Proc. Precision
 Time and Time Interval (PTTI) Applications and Planning Meeting_
 (Washington DC, December 2004), 5-16. Paper:
 {millshome}database/papers/ptti/ptti04a.pdf[PDF],
-Slides:{millshome}database/brief/ptti/ptti04.pdf[PDF]
-|
+Slides: {millshome}database/brief/ptti/ptti04.pdf[PDF] |
 {millshome}database/brief/ptti/ptti04.ppt[PowerPoint]]
 over 750,000 clients demonstrated this abuse. There have been occasions
 where this abuse has persisted for days at a time. These scenarios are
@@ -187,7 +186,7 @@ overload on the Network Time Protocol public servers. _Proc. Precision
 Time and Time Interval (PTTI) Applications and Planning Meeting_
 (Washington DC, December 2004), 5-16. Paper:
 {millshome}database/papers/ptti/ptti04a.pdf[PDF],
-Slides:{millshome}database/brief/ptti/ptti04.pdf[PDF]
+Slides: {millshome}database/brief/ptti/ptti04.pdf[PDF]
 |
 {millshome}database/brief/ptti/ptti04.ppt[PowerPoint]
 


=====================================
libntp/ntp_c.c
=====================================
@@ -0,0 +1,119 @@
+/*
+ * Copyright the NTPsec project contributors
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Python binding for selected libntp library functions
+ */
+
+/* This include has to come early or we get warnings from redefining
+ * _POSIX_C_SOURCE and _XOPEN_SOURCE on some systems.
+ */
+#include "config.h"
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+#include "timespecops.h"
+
+#include "ntp_config.h"
+#include "ntp_assert.h"
+
+#include "ntp_control.h"
+
+#include "pymodule-mac.h"
+
+void ntpc_setprogname(char*);
+char *ntpc_prettydate(char*);
+double ntpc_lfptofloat(char*);
+int ntpc_set_tod(int, int);
+bool ntpc_adj_systime(double);
+bool ntpc_step_systime(double);
+
+/* Don't include anything from OpenSSL */
+
+const char *progname = "libntpc";
+int   SYS_TYPE = TYPE_SYS;
+int  PEER_TYPE = TYPE_PEER;
+int CLOCK_TYPE = TYPE_CLOCK;
+
+/*
+ * Client utility functions
+ */
+
+void
+ntpc_setprogname(char *s)
+{
+	/*
+	 * This function is only called from clients.  Therefore
+	 * log to stderr rather than syslog, and suppress logfile
+	 * impediments.  If we ever want finer-grained control, that
+	 * will be easily implemented with additional arguments.
+	 */
+	syslogit = false;	/* don't log messages to syslog */
+	termlogit = true;	/* duplicate to stdout/err */
+	termlogit_pid = false;
+	msyslog_include_timestamp = false;
+	progname = strdup(s);
+}
+
+char *
+ntpc_prettydate(char *s)
+{
+	l_fp ts;
+
+	if (false == hextolfp(s+2, &ts)) {
+		errno = EINVAL;
+		return strdup("ERROR");
+	}
+	errno = 0;
+	return prettydate(ts);
+}
+
+double
+ntpc_lfptofloat(char *s)
+{
+	l_fp ts;
+	struct timespec tt;
+
+	if (false == hextolfp(s+2, &ts)) {
+		errno = EINVAL;
+		return -0;
+	}
+	errno = 0;
+	tt = lfp_stamp_to_tspec(ts, time(NULL));
+	return tt.tv_sec + (tt.tv_nsec * S_PER_NS);
+}
+
+int
+ntpc_set_tod(int seconds, int fractional)
+{
+	struct timespec ts;
+	ts.tv_sec = seconds;
+	ts.tv_nsec = fractional;
+
+	return ntp_set_tod(&ts);
+}
+
+bool
+ntpc_adj_systime(double adjustment)
+{
+	return adj_systime(adjustment, adjtime) ? 1 : 0;
+}
+
+bool
+ntpc_step_systime(double adjustment)
+{
+	doubletime_t full_adjustment;
+
+	/*
+	 * What we really want is for Python to parse a long double.
+	 * As this is, it's a potential source of problems in the Python
+	 * utilities if and when the time difference between the Unix epoch
+	 * and now exceeds the range of a double.
+	 */
+	full_adjustment = adjustment;
+	return step_systime(full_adjustment, ntp_set_tod);
+}


=====================================
libntp/wscript
=====================================
@@ -1,3 +1,5 @@
+import os
+
 def build(ctx):
 
     libntp_source = [
@@ -47,12 +49,24 @@ def build(ctx):
         use="CRYPTO SSL",
     )
 
-    # Loadable Python extension
-    ctx(
-        features="c cshlib pyext",
-        install_path='${PYTHONARCHDIR}/ntp',
-        includes=[ctx.bldnode.parent.abspath(), "../include"],
-        source=["pymodule.c", "pymodule-mac.c"] + libntp_source_sharable,
-        target="../pylib/ntpc",  # Put the output in the pylib directory
-        use="M RT CRYPTO",
-    )
+    if ctx.env['ntpc'] == 'ffi':
+        # Loadable FFI stub
+        ctx(
+            features="c cshlib",
+            install_path='${LIBDIR}/ntp',
+            includes=[ctx.bldnode.parent.abspath(), "../include"],
+            source=["ntp_c.c", "pymodule-mac.c"] + libntp_source_sharable,
+            target="../pylib/ntpc",  # Put the output in the pylib directory
+            use="M RT CRYPTO",
+            vnum=ctx.env['ntpcver'],
+        )
+    elif ctx.env['ntpc'] == 'ext':
+        # Loadable Python extension
+        ctx(
+            features="c cshlib pyext",
+            install_path='${PYTHONARCHDIR}/ntp',
+            includes=[ctx.bldnode.parent.abspath(), "../include"],
+            source=["pymodule.c", "pymodule-mac.c"] + libntp_source_sharable,
+            target="../pylib/ntpc",  # Put the output in the pylib directory
+            use="M RT CRYPTO",
+        )


=====================================
ntpclients/ntploggps.py
=====================================
@@ -218,7 +218,7 @@ if __name__ == '__main__':
                         # just once
                         break
 
-            except AttributeError as e:
+            except AttributeError:
                 print('parse error\n')
 
             # wait a bit before next log


=====================================
ntpclients/ntpmon.py
=====================================
@@ -392,7 +392,7 @@ if __name__ == '__main__':
                                 strconvert = ntp.util.cook(clockvars,
                                                            showunits, " ")
                                 stdscr.addstr(strconvert.encode('UTF-8'))
-                            except ntp.packet.ControlException as e:
+                            except ntp.packet.ControlException:
                                 pass
                         elif span.entries:
                             strconvert = ntp.util.MRUSummary.header + "\n"


=====================================
ntpclients/ntpq.py
=====================================
@@ -1215,6 +1215,42 @@ usage: config_from_file <configuration filename>
         for entry in entries:
             self.say(self.formatter.summary(entry) + "\n")
 
+    def do_noflake(self):
+        """Disables the dropping of control packets by ntpq for testing."""
+        self.session.flakey = False
+
+    def help_noflake(self):
+        """Print help for noflake."""
+        self.say("""\
+function: Disables the dropping of received packets by ntpq for testing.
+usage: noflake
+""")
+
+    def do_doflake(self, line):
+        """Drop some received packets for testing.
+
+        Probabilities 0 and 1 should be certainly accepted
+        and discarded respectively. No default, but 0.1
+        should be a one in ten loss rate.
+        """
+        try:
+            _ = float(line)
+            if not 0 < _ < 1:
+                raise ValueError
+            self.session.flakey = _
+        except ValueError:
+            self.say('Flakiness must be a (positive) float less than 1.')
+
+    def help_doflake(self):
+        """Print help for doflake."""
+        self.say("""\
+function: Enables the dropping of control packets by
+ntpq for testing. Probabilities 0 and 1 should be
+certainly accepted and discarded respectively. No
+default, but 0.1 should be a one in ten loss rate.
+usage: doflake <probability>
+""")
+
     def do_mrulist(self, line):
         """display the list of most recently seen source addresses,
            tags mincount=... resall=0x... resany=0x..."""


=====================================
ntpclients/ntpviz.py
=====================================
@@ -793,7 +793,7 @@ plot \\
             plot_template += """\
 '-' using 1:2 title '%s TDOP' with line ls 1, \\
 '-' using 1:3 title '%s nSat' with line ls 2 axis x1y2, \\
-""" % (key,)
+""" % (key,key)
 
         # strip the trailing ", \\n"
         plot_template = plot_template[:-4] + "\n"
@@ -1295,7 +1295,7 @@ def local_offset_multiplot(statlist):
     plot = NTPViz.Common + '''\
 set terminal %(terminal)s size %(size)s
 set title "Multiplot Local Clock Offsets"
-set ytics format "%1.2f μs" nomirror textcolor rgb "#0060ad"
+set ytics format "%%1.2f μs" nomirror textcolor rgb "#0060ad"
 set key bottom right box
 plot \\
 ''' % out


=====================================
ntpd/ntp_control.c
=====================================
@@ -3347,12 +3347,19 @@ send_mru_entry(
  *	limit=		Limit on MRU entries returned.  One of frags= or
  *			limit= must be provided.
  *			limit=1 is a special case:  Instead of fetching
- *			beginning with the supplied starting point's
- *			newer neighbor, fetch the supplied entry, and
- *			in that case the #.last timestamp can be zero.
- *			This enables fetching a single entry by IP
- *			address.  When limit is not one and frags= is
- *			provided, the fragment limit controls.
+ *			beginning with the supplied starting points
+ *			(provided by a last.x and addr.x where 0 <= x
+ *			<= 15, default the beginning of time) newer
+ *			neighbor, fetch the supplied entries (provided
+ *			by addr.x= entries where 0 <= x <= 15), and in
+ *			that case the #.last timestamp can be zero.
+ *			This enables fetching a multiple entries from
+ *			given  IP addresses.  When limit is not one and
+ *			frags= is provided, the fragment limit controls.
+ *			NOTE: a single mrulist command may cause many
+ *			query/response rounds allowing limits as low as
+ *			3 to potentially retrieve thousands of entries
+ *			in responses.
  *	mincount=	(decimal) Return entries with count >= mincount.
  *	mindrop=	(decimal) Return entries with drop >= mindrop.
  *	minscore=	(float) Return entries with score >= minscore.
@@ -3637,10 +3644,25 @@ static void read_mru_list(
 	} else if (0 != limit && 0 == frags)
 		frags = MRU_FRAGS_LIMIT;
 
+	mon = NULL;
+	if (limit == 1) {
+		for (i = 0; i < COUNTOF(last); i++) {
+			mon = mon_get_slot(&addr[i]);
+			if (mon != NULL) {
+				send_mru_entry(mon, i);
+			}
+		}
+		generate_nonce(rbufp, buf, sizeof(buf));
+		ctl_putunqstr("nonce", buf, strlen(buf));
+		get_systime(&now);
+		ctl_putts("now", &now);
+		ctl_flushpkt(0);
+		return;
+	}
+
 	/*
 	 * Find the starting point if one was provided.
 	 */
-	mon = NULL;
 	for (i = 0; i < (size_t)priors; i++) {
 		mon = mon_get_slot(&addr[i]);
 		if (mon != NULL) {


=====================================
pylib/ntpc.py
=====================================
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+# SPDX-License-Identifier: BSD-2-Clause
+"""Access libntp funtions from Python."""
+from __future__ import absolute_import
+import ctypes
+import ctypes.util
+import errno
+import os
+import os.path
+import sys
+import ntp.poly
+
+LIB = 'ntpc'
+
+
+def _fmt():
+    """Produce library naming scheme."""
+    if sys.platform.startswith('darwin'):
+        return 'lib%s.dylib'
+    if sys.platform.startswith('win32'):
+        return '%s.dll'
+    if sys.platform.startswith('cygwin'):
+        return 'lib%s.dll'
+    return 'lib%s.so'
+
+
+def _importado():
+    """Load the ntpc library or throw an OSError trying."""
+    ntpc_paths = []         # places to look
+
+    j = __file__.split(os.sep)[:-1]
+    ntpc_paths.append(os.sep.join(j + [_fmt() % LIB]))
+
+    ntpc_path = ctypes.util.find_library(LIB)
+    if ntpc_path:
+        ntpc_paths.append(ntpc_path)
+
+    return _dlo(ntpc_paths)
+
+
+def _dlo(paths):
+    """Try opening library from a list."""
+    for ntpc_path in paths:
+        try:
+            lib = ctypes.CDLL(ntpc_path, use_errno=True)
+            return lib
+        except OSError:
+            pass
+    raise OSError("Can't find %s library" % LIB)
+
+
+_ntpc = _importado()
+progname = ctypes.c_char_p.in_dll(_ntpc, 'progname')
+# log_sys = ctypes.c_bool.in_dll(_ntpc, 'syslogit')
+# log_term = ctypes.c_bool.in_dll(_ntpc, 'termlogit')
+# log_pid = ctypes.c_bool.in_dll(_ntpc, 'termlogit_pid')
+# log_time = ctypes.c_bool.in_dll(_ntpc, 'msyslog_include_timestamp')
+
+TYPE_SYS = ctypes.c_int.in_dll(_ntpc, 'SYS_TYPE').value
+TYPE_PEER = ctypes.c_int.in_dll(_ntpc, 'PEER_TYPE').value
+TYPE_CLOCK = ctypes.c_int.in_dll(_ntpc, 'CLOCK_TYPE').value
+
+
+def checkname(name):
+    """Check if name is a valid algorithm name."""
+    _ntpc.do_checkname.restype = ctypes.c_int
+    mid_bytes = ntp.poly.polybytes(name)
+    _ntpc.do_checkname.argtypes = [ctypes.c_char_p]
+    return _ntpc.do_checkname(mid_bytes)
+
+
+def mac(data, key, name):
+    """Compute HMAC or CMAC from data, key, and algorithm name."""
+    resultlen = ctypes.c_size_t()
+    result = (ctypes.c_char * 64)()
+    result.value = b'\0' * 64
+    _ntpc.do_mac.restype = None
+    _ntpc.do_mac(ntp.poly.polybytes(name),
+                 ntp.poly.polybytes(data), len(data),
+                 ntp.poly.polybytes(key), len(key),
+                 ctypes.byref(result), ctypes.byref(resultlen))
+    return result.value
+
+
+def setprogname(in_string):
+    """Set program name for logging purposes."""
+    mid_bytes = ntp.poly.polybytes(in_string)
+    _setprogname(mid_bytes)
+
+
+def _lfp_wrap(callback, in_string):
+    """NTP l_fp to other Python-style format."""
+    mid_bytes = ntp.poly.polybytes(in_string)
+    out_value = callback(mid_bytes)
+    err = ctypes.get_errno()
+    if err == errno.EINVAL:
+        raise ValueError('ill-formed hex date')
+    return out_value
+
+
+def statustoa(i_type, i_st):
+    """Convert a time stamp to something readable."""
+    mid_str = _statustoa(i_type, i_st)
+    return ntp.poly.polystr(mid_str)
+
+
+def prettydate(in_string):
+    """Convert a time stamp to something readable."""
+    mid_str = _lfp_wrap(_prettydate, in_string)
+    return ntp.poly.polystr(mid_str)
+
+
+def lfptofloat(in_string):
+    """NTP l_fp to Python-style float time."""
+    return _lfp_wrap(_lfptofloat, in_string)
+
+
+def msyslog(level, in_string):
+    """Log send a message to terminal or output."""
+    mid_bytes = ntp.poly.polybytes(in_string)
+    _msyslog(level, mid_bytes)
+
+
+# Set return type and argument types of hidden ffi handlers
+_msyslog = _ntpc.msyslog
+_msyslog.restype = None
+_msyslog.argtypes = [ctypes.c_int, ctypes.c_char_p]
+
+_setprogname = _ntpc.ntpc_setprogname
+_setprogname.restype = None
+_setprogname.argtypes = [ctypes.c_char_p]
+
+_prettydate = _ntpc.ntpc_prettydate
+_prettydate.restype = ctypes.c_char_p
+_prettydate.argtypes = [ctypes.c_char_p]
+
+_lfptofloat = _ntpc.ntpc_lfptofloat
+_lfptofloat.restype = ctypes.c_double
+_lfptofloat.argtypes = [ctypes.c_char_p]
+
+# Status string display from peer status word.
+_statustoa = _ntpc.statustoa
+_statustoa.restype = ctypes.c_char_p
+_statustoa.argtypes = [ctypes.c_int, ctypes.c_int]
+
+# Set time to nanosecond precision.
+set_tod = _ntpc.ntpc_set_tod
+set_tod.restype = ctypes.c_int
+set_tod.argtypes = [ctypes.c_int, ctypes.c_int]
+
+# Adjust system time by slewing.
+adj_systime = _ntpc.ntpc_adj_systime
+adj_systime.restype = ctypes.c_bool
+adj_systime.argtypes = [ctypes.c_double]
+
+# Adjust system time by stepping.
+step_systime = _ntpc.ntpc_step_systime
+step_systime.restype = ctypes.c_bool
+step_systime.argtypes = [ctypes.c_double]


=====================================
pylib/packet.py
=====================================
@@ -207,6 +207,7 @@ from __future__ import print_function, division
 import getpass
 import hmac
 import os
+import random
 import select
 import socket
 import string
@@ -740,6 +741,7 @@ class ControlSession:
         self.logfp = sys.stdout
         self.nonce_xmit = 0
         self.slots = 0
+        self.flakey = None
 
     def warndbg(self, text, threshold):
         ntp.util.dolog(self.logfp, text, self.debug, threshold)
@@ -1019,6 +1021,10 @@ class ControlSession:
                 # usually, errno 111: connection refused
                 raise ControlException(SERR_SOCKET)
 
+            if self.flakey and self.flakey >= random.random():
+                warndbg('Flaky: I deliberately dropped a packet.', 1)
+                rawdata = None
+
             warndbg("Received %d octets" % len(rawdata), 3)
             rpkt = ControlPacket(self)
             try:
@@ -1597,6 +1603,11 @@ def parse_mru_variables(variables):
                  "maxlstint", "minlstint", "laddr", "recent",
                  "sort", "frags", "limit"):
             continue
+        elif k.startswith('addr.') or k.startswith('last.'):
+            kn = k.split('.')
+            if len(kn) != 2 or kn[1] not in map(str, list(range(16))):
+                raise ControlException(SERR_BADPARAM % k)
+            continue
         else:
             raise ControlException(SERR_BADPARAM % k)
     if 'frags' in variables:


=====================================
pylib/wscript
=====================================
@@ -36,7 +36,11 @@ def build(ctx):
     target1 = bldnode.make_node('control.py')
     target2 = bldnode.make_node('magic.py')
 
-    sources = srcnode.ant_glob('*.py')
+    sources = []
+    if ctx.env['ntpc'] == 'ext':
+        sources = srcnode.ant_glob("*.py", excl='ntpc.py')
+    elif ctx.env['ntpc'] == 'ffi':
+        sources = srcnode.ant_glob('*.py')
     builds = [x.get_bld() for x in sources]
 
     # The rm's here were added to fix a reported (but underdocumented) problem
@@ -52,6 +56,9 @@ def build(ctx):
     #ctx.exec_command("rm -f %s" % target1.abspath())
     #ctx.exec_command("rm -f %s" % target2.abspath())
 
+    if ctx.env['ntpc'] is None:
+        return
+        
     # Make sure Python sees .py as well as .pyc/.pyo
     ctx(
         features="subst",


=====================================
tests/option-tester.sh
=====================================
@@ -19,7 +19,7 @@ PURGE=""
 SECCOMP="$(pkg-config libseccomp --variable=includedir)"
 SECCOMP="$SECCOMP/seccomp.h"
 LINUX=""
-if [ `uname -s` = "Linux" -a -n "$SECCOMP" -a -f "$SECCOMPH" ]
+if [ "$(uname -s)" = "Linux" -a -n "$SECCOMP" -a -f "$SECCOMPH" ]
 then
   # Not supported on CentOS 6
   LINUX="--enable-seccomp"
@@ -38,7 +38,7 @@ then
     DISABLE_NTS="--disable-nts"
   fi
 else
-  if ! $PYTHON ../wafhelpers/tlscheck
+  if ! "${PYTHON}" ../wafhelpers/tlscheck
   then
     DISABLE_NTS="--disable-nts"
   fi
@@ -46,22 +46,22 @@ fi
 
 doit ()
 {
-  DIR=test-$1
-  [ ! -d $DIR ] && mkdir $DIR
-  rm -rf $DIR/*
-  $PYTHON ./waf configure $DISABLE_NTS --out=$DIR $2 2>&1 | tee    $DIR/test.log
+  DIR="test-${1}"
+  [ ! -d "${DIR}" ] && mkdir "${DIR}"
+  rm -rf "${DIR}/"*
+  "${PYTHON}" ./waf configure "${DISABLE_NTS}" --out="${DIR}" $2 2>&1 | tee    "${DIR}/test.log"
   WAF1=$?
   WAF2=0
   WAF3=0
   if [ "$WAF1" = 0 ]
   then
-  echo                                 2>&1    | tee -a $DIR/test.log
-  $PYTHON ./waf build                   2>&1    | tee -a $DIR/test.log
+  echo                                 2>&1    | tee -a "${DIR}/test.log"
+  "${PYTHON}" ./waf build                   2>&1    | tee -a "${DIR}/test.log"
   WAF2=$?
   if [ "$WAF2" = 0 ]
   then
-  echo                                 2>&1    | tee -a $DIR/test.log
-  $PYTHON ./waf check                   2>&1    | tee -a $DIR/test.log
+  echo                                 2>&1    | tee -a "${DIR}/test.log"
+  "${PYTHON}" ./waf check                   2>&1    | tee -a "${DIR}/test.log"
   WAF3=$?
   else
     PURGE="${PURGE} ${PYTHON}-${DIR}-build"
@@ -71,8 +71,8 @@ doit ()
   fi
   if [ "$WAF1" != 0 -o "$WAF2" != 0 -o "$WAF3" != 0 ]
   then
-    echo                               2>&1   | tee -a $DIR/test.log
-    echo "Trouble with $DIR"           2>&1   | tee -a $DIR/test.log
+    echo                                 2>&1   | tee -a "${DIR}/test.log"
+    echo "Trouble with ${DIR}"           2>&1   | tee -a "${DIR}/test.log"
   fi
   if [ "$WAF3" != 0 ]
   then
@@ -91,8 +91,8 @@ doit classic "--enable-classic-mode --refclock=all --disable-doc --disable-manpa
 
 doit all     "--enable-warnings --enable-debug --enable-debug-gdb --enable-debug-timing --refclock=all --enable-leap-smear --enable-mssntp --enable-early-droproot --disable-fuzz $LINUX --disable-doc --disable-manpage"
 
-if [ "`which asciidoc 2>/dev/null`" != "" -a \
-     "`which xsltproc 2>/dev/null`" != "" ]
+if [ "$(which asciidoc 2>/dev/null)" != "" -a \
+     "$(which xsltproc 2>/dev/null)" != "" ]
 then
 doit doc     ""
 fi
@@ -107,10 +107,10 @@ grep "The configuration failed"  test*/test.log
 grep ^Trouble                    test*/test.log
 echo
 
-echo -n "## ";  $PYTHON --version
-if test -n "$PYTHONPATH"
+echo -n "## ";  "${PYTHON}" --version
+if test -n "${PYTHONPATH}"
 then
-  echo "## PYTHONPATH is" \"$PYTHONPATH\"
+  echo "## PYTHONPATH is \"${PYTHONPATH}\""
 fi
 
 if ! (set -o pipefail) 2>/dev/null
@@ -121,7 +121,7 @@ then
   PURGE="${PURGE} pipefail"
 fi
 
-if [ `uname -s` = "Linux" -a -z "$SECCOMP" ]
+if [ $(uname -s) = "Linux" -a -z "$SECCOMP" ]
 then
     echo
     echo "### Warning: Missing seccomp.h (on a Linux system)"


=====================================
tests/python2-tester.sh
=====================================
@@ -5,7 +5,7 @@
 # This is a clone of option-tester.sh
 # to build with python2 and do minimal (version) testing.
 
-if [ "`which python2 2>/dev/null`" = "" ]
+if [ "$(which python2 2>/dev/null)" = "" ]
 then
   echo "# Error: No python2 on this system."
   exit 1
@@ -21,28 +21,28 @@ fi
 doit ()
 {
   DIR=test-$1
-  [ ! -d $DIR ] && mkdir $DIR
-  rm -rf $DIR/*
-  python2 ./waf configure --out=$DIR $2 2>&1 | tee    $DIR/test.log
+  [ ! -d "${DIR}" ] && mkdir "${DIR}"
+  rm -rf "${DIR}"/*
+  python2 ./waf configure --out="${DIR}" $2 2>&1 | tee    "${DIR}/test.log"
   WAF1=$?
   WAF2=0
   WAF3=0
   if [ "$WAF1" = 0 ]
   then
-  echo                            2>&1   | tee -a $DIR/test.log
-  python2 ./waf build             2>&1   | tee -a $DIR/test.log
+  echo                            2>&1   | tee -a "${DIR}/test.log"
+  python2 ./waf build             2>&1   | tee -a "${DIR}/test.log"
   WAF2=$?
   if [ "$WAF2" = 0 ]
   then
-  echo                            2>&1   | tee -a $DIR/test.log
-  python2 ./waf check             2>&1   | tee -a $DIR/test.log
+  echo                            2>&1   | tee -a "${DIR}/test.log"
+  python2 ./waf check             2>&1   | tee -a "${DIR}/test.log"
   WAF3=$?
   fi
   fi
   if [ "$WAF1" != 0 -o "$WAF2" != 0 -o "$WAF3" != 0 ] 
   then
-    echo                          2>&1   | tee -a $DIR/test.log
-    echo "Trouble with $DIR"      2>&1   | tee -a $DIR/test.log
+    echo                          2>&1   | tee -a "${DIR}/test.log"
+    echo "Trouble with ${DIR}"      2>&1   | tee -a "${DIR}/test.log"
   fi
   echo
   echo
@@ -61,9 +61,9 @@ grep ^Trouble                    test*/test.log
 echo
 
 echo -n "## ";  python2 --version
-if test -n "$PYTHONPATH"
+if test -n "${PYTHONPATH}"
 then
-  echo "## PYTHONPATH is" \"$PYTHONPATH\"
+  echo "## PYTHONPATH is \"${PYTHONPATH}\""
 fi
 
 if ! /bin/sh -c "set -o pipefail" 2> /dev/null


=====================================
tests/python3-tester.sh
=====================================
@@ -5,7 +5,7 @@
 # This is a clone of option-tester.sh
 # to build with python3 and do minimal (version) testing.
 
-if [ "`which python3 2>/dev/null`" = "" ]
+if [ "$(which python3 2>/dev/null)" = "" ]
 then
   echo "# Error: No python3 on this system."
   exit 1
@@ -21,28 +21,28 @@ fi
 doit ()
 {
   DIR=test-$1
-  [ ! -d $DIR ] && mkdir $DIR
-  rm -rf $DIR/*
-  python3 ./waf configure --out=$DIR $2 2>&1 | tee    $DIR/test.log
+  [ ! -d "${DIR}" ] && mkdir "${DIR}"
+  rm -rf "${DIR}"/*
+  python3 ./waf configure --out="${DIR}" $2 2>&1 | tee    "${DIR}/test.log"
   WAF1=$?
   WAF2=0
   WAF3=0
   if [ "$WAF1" = 0 ]
   then
-  echo                            2>&1   | tee -a $DIR/test.log
-  python3 ./waf build             2>&1   | tee -a $DIR/test.log
+  echo                            2>&1   | tee -a "${DIR}/test.log"
+  python3 ./waf build             2>&1   | tee -a "${DIR}/test.log"
   WAF2=$?
   if [ "$WAF2" = 0 ]
   then
-  echo                            2>&1   | tee -a $DIR/test.log
-  python3 ./waf check             2>&1   | tee -a $DIR/test.log
+  echo                            2>&1   | tee -a "${DIR}/test.log"
+  python3 ./waf check             2>&1   | tee -a "${DIR}/test.log"
   WAF3=$?
   fi
   fi
   if [ "$WAF1" != 0 -o "$WAF2" != 0 -o "$WAF3" != 0 ] 
   then
-    echo                          2>&1   | tee -a $DIR/test.log
-    echo "Trouble with $DIR"      2>&1   | tee -a $DIR/test.log
+    echo                          2>&1   | tee -a "${DIR}/test.log"
+    echo "Trouble with ${DIR}"      2>&1   | tee -a "${DIR}/test.log"
   fi
   echo
   echo
@@ -61,9 +61,9 @@ grep ^Trouble                    test*/test.log
 echo
 
 echo -n "## ";  python3 --version
-if test -n "$PYTHONPATH"
+if test -n "${PYTHONPATH}"
 then
-  echo "## PYTHONPATH is" \"$PYTHONPATH\"
+  echo "## PYTHONPATH is \"${PYTHONPATH}\""
 fi
 
 if ! /bin/sh -c "set -o pipefail" 2> /dev/null


=====================================
wafhelpers/options.py
=====================================
@@ -31,6 +31,10 @@ def options_cmd(ctx, config):
     grp.add_option('--enable-debug-timing', action='store_true',
                    default=False,
                    help="Collect timing statistics for debugging.")
+    grp.add_option('--enable-pylib', action='store',
+                   default='ffi', choices=['ext', 'ffi', 'none'],
+                   help="""Choose which Python library to build.\n
+ext, ffi, or none. defaults to ffi.""", nargs=1)
 
     grp = ctx.add_option_group("NTP cross compile options")
     grp.add_option('--cross-compiler', type='string',


=====================================
wscript
=====================================
@@ -120,6 +120,9 @@ def configure(ctx):
             opt = flag.replace("--", "").upper()
             opt_map[opt] = ctx.env.OPT_STORE[flag]
 
+    ctx.env['ntpc'] = ctx.options.enable_pylib
+    ctx.env['ntpcver'] = '1.1.0'
+
     msg("--- Configuring host ---")
     ctx.setenv('host', ctx.env.derive())
 
@@ -1013,15 +1016,24 @@ def build(ctx):
     ctx.load('asciidoc', tooldir='wafhelpers/')
     ctx.load('rtems_trace', tooldir='wafhelpers/')
 
+    if ctx.variant == "host":
+        ctx.recurse("ntpd")
+        return
+
     if ctx.cmd == "build":
         # It's a waf gotcha that if there are object files (including
         # .pyc and .pyo files) in a source directory, compilation to
         # the build directory never happens.  This is how we foil that.
         ctx.add_pre_fun(lambda ctx: ctx.exec_command("rm -f pylib/*.py[co]"))
-
-    if ctx.variant == "host":
-        ctx.recurse("ntpd")
-        return
+        # Start purging ntp.ntpc files from build dir
+        # so old extension won't clobber FFI or reverse
+        bldnode = ctx.bldnode.make_node('pylib')
+        bldnode.mkdir()
+        target3 = bldnode.ant_glob('*ntpc*')
+        for _ in target3:
+            ctx.exec_command("rm -f %s" % _.abspath())
+        # Finish purging ntp.ntpc 
+        ctx.add_group()
 
     if ctx.env.REFCLOCK_GENERIC or ctx.env.REFCLOCK_TRIMBLE:
         # required by the generic and Trimble refclocks



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/8599fdab9a9c827c1b935c0f3a683324add80395...da92a66e7c3196e2d4d3576239dc701e28ab45c1

-- 
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/8599fdab9a9c827c1b935c0f3a683324add80395...da92a66e7c3196e2d4d3576239dc701e28ab45c1
You're receiving this email because of your account on gitlab.com.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20201012/68c57890/attachment-0001.htm>


More information about the vc mailing list