ntpsec-1.1.0+dfsg1/0000775000175000017500000000000013252650652013631 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/devel/0000775000175000017500000000000013252650651014727 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/devel/testing.txt0000644000175000017500000001403413252364117017144 0ustar rlaagerrlaager= How To Test NTPsec = This assumes you have downloaded the source and built a system and now you want to test your new ntpd. For help on getting that far, see the file INSTALL in the top-level directory. == Path problems with the test tools == The very first thing to do, even before you start up ntpd, is to change directory to build/main/ntpclients/ and run ./ntpq there. If you get a message that says --------------------------------------------------------------------- ntpq: can't find Python NTP library -- check PYTHONPATH. --------------------------------------------------------------------- you *may* have a problem. A lot of what was C code in legacy versions (pretty much everything except ntpd itself, in fact) has been moved to Python in order to improve maintainability and decrease attack surface. Most of these tools share a Python library of critical functions. If you can't load that library, you can't test effectively. The waf build is supposed to create a symbolic link from build/main/ntpclients/ntp to pylib in the build directory. This should enable ntpq to "import ntp" before you install to rootspace. If that link is not created or doesn't point to pylib/ under your build directory, report this as a bug. If it is, but ./ntpq startup fails anyway, you may have a mis-configured Python and need to investigate that. Load paths may be more likely to be an issue after rootspace installation. The script should install the Python library in the directory specified by Python itself, which is reported as PYTHONDIR near the end of the configure phase. This directory should be on your Python's sys.path, in which case all will proceed normally. If it's not, you will get the same ntpq failure to launch ./ntpq from anywhere *outside* the source tree. If this is the case, report it as a bug. Be aware that if this sort of problem occurs in an NTPsec instance installed from a binary package, it is almost certainly not an NTPsec bug but a packaging error that the NTPsec maintainers themselves can't fix. In that case you need to report it to your distribution maintainers. == Preliminary one-off test == For a one-off test: 1. Do "ps ax | grep ntpd" or equivalent to find out what the command line is. 2. Use whatever you do on your system to stop the normal ntpd. This is likely to be either something resembling "/etc/init.d/ntp stop" or "systemctl stop timeservice". 3. Run ./build/ntpd/ntpd plus the command line parameters from above. 4. It will daemonize itself and give you back your terminal prompt. Your current /etc/ntp.conf should work correctly. Check your log files to see if there is anything strange. Run "ntpq -p" to see if things look normal. == Full qualification test == For a longer test, including over reboots... Install your new code using "./waf install" (as root). That will install your new ntpd into /usr/local/sbin/ntpd Now you have to patch your (re)start scripts to look there and restart ntpd. The details vary by OS/distro. === Distributions using systemd === 1. Edit /usr/lib/systemd/system/ntpd.service 2. Change ExecStart=/usr/sbin/ntpd -u ntp:ntp $OPTIONS to ExecStart=/usr/local/sbin/ntpd -u ntp:ntp $OPTIONS then do "service ntpd restart" 3. systemctl may barf about an out of date file and tell you how to fix it. If so, follow its directions and try again. Note that under Fedora and CentOS, "dnf update" may undo that edit and revert to running the system version. Older versions used /etc/rc.d/init.d/ntpd. The file /etc/sysconfig/ntpd gets sourced into the init script so you can put real code in there (systemd doesn't do that) You can insert this: -------------------------------------------------- PATH="/usr/local/sbin:$PATH" -------------------------------------------------- === Debian, Ubuntu, Raspbian === Many newer versions use systemd; follow those directions. The rest of this section describes the older set of conventions used with a traditional System V init sequence. Edit /etc/init.d/ntp. Change -------------------------------------------------- DAEMON=/usr/sbin/ntpd -------------------------------------------------- to -------------------------------------------------- DAEMON=/usr/local/sbin/ntpd -------------------------------------------------- A good safety measure in case you want to revert later is to duplicate the line, comment out the first one and edit the second one. That looks like this: -------------------------------------------------- # DAEMON=/usr/sbin/ntpd DAEMON=/usr/local/sbin/ntpd -------------------------------------------------- If you are using DHCP and your DHCP servers provide NTP servers, Debian makes a dummy ntp.conf using those servers and not the ones you put into the normal /etc/ntp.conf. To use your ntp.conf rather than the one it wants you to: 1. Edit /etc/init.d/ntp 2. Comment out the clump of 3 lines that references dhcp: -------------------------------------------------- # if [ -e /var/lib/ntp/ntp.conf.dhcp ]; then # NTPD_OPTS="$NTPD_OPTS -c /var/lib/ntp/ntp.conf.dhcp" # fi -------------------------------------------------- apt-get upgrade may undo those edits and revert to running the system version. === FreeBSD === Edit /etc/rc.conf It needs to contain this: -------------------------------------------------- ntpd_enable="YES" ntpd_program="/usr/local/sbin/ntpd" -------------------------------------------------- If you are running ntpd, the first line will be there already. The default is NO. === NetBSD === Edit /etc/rc.d/ntpd. Change -------------------------------------------------- command="/usr/sbin/${name}" -------------------------------------------------- to -------------------------------------------------- command="/usr/local/sbin/${name}" -------------------------------------------------- A good safety measure in case you want to revert later is to duplicate the line, comment out the first one and edit the second one. That looks like this: -------------------------------------------------- # command="/usr/sbin/${name}" command="/usr/local/sbin/${name}" -------------------------------------------------- // end ntpsec-1.1.0+dfsg1/devel/python_paths.py0000755000175000017500000001253713252364117020031 0ustar rlaagerrlaager#!/usr/bin/env python """Find all Pythons in the command path, and corresponding exe/lib locations. An optional argument specifies an SSH host (or user@host) to use for the run. Note that the ssh-based version will use the system-defult PATH rather than the PATH set up by any login scripts, so it may not find all Pythons that the user would see. It is known to work with all versions of Python >=2.4. """ import glob import operator import os import subprocess import sys import traceback try: reduce except NameError: # For Python 3 from functools import reduce # pylint: disable=redefined-builtin PYTHON_PATTERNS = ['python', 'python[1-9]', 'python[1-9].[0-9]'] PYTHON_COMMAND_MAP = [ ['', 'import sys'], ['', 'from distutils import sysconfig'], ['exec', 'print(sys.executable)'], ['lib-noarch', 'print(sysconfig.get_python_lib(plat_specific=0))'], ['lib-arch', 'print(sysconfig.get_python_lib(plat_specific=1))'], ['sys-prefix', 'print(sys.prefix)'], ['std-prefix', 'print(sysconfig.PREFIX)'], ['exec-prefix', 'print(sysconfig.EXEC_PREFIX)'], ] PYTHON_COMMANDS = [xx[1] for xx in PYTHON_COMMAND_MAP] PYTHON_VALUE_NAMES = [xx[0] for xx in PYTHON_COMMAND_MAP if xx[0]] PYTHON_PREFIX_NAMES = 'sys-prefix std-prefix exec-prefix'.split() class BadReturn(Exception): """Bad return from subprocess.""" pass def _print(arg): """Python3-compatible print, without depending on future import.""" print(arg) # pylint: disable=superfluous-parens def GetPaths(): """Get list of directories in PATH.""" return os.environ['PATH'].split(':') def MakePatterns(paths, patterns): """Construct Cartesian product of paths and file patterns.""" return [os.path.join(path, pat) for path in paths for pat in patterns] def FileList(patterns): """Get list of files from list of glob patterns.""" return reduce(operator.add, map(glob.glob, patterns), []) def ExeFilter(files): """Filter list of files based on executability.""" return [f for f in files if os.access(f, os.X_OK)] def PythonCommands(python, commands): """Run a multiline Python command string in a specified Python.""" proc = subprocess.Popen(python, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) # Force text mode in Python3 try: result, _ = proc.communicate(commands) except TypeError: # For Python 3.1 only result, _ = proc.communicate(bytes(commands, encoding='latin-1')) if proc.returncode: raise BadReturn(proc.returncode) return result.splitlines() def PrintExe(prefix, exe, real): """Print executable path with optional symlink reporting.""" if real == exe: _print('%s: %s' % (prefix, exe)) else: _print('%s: %s -> %s' % (prefix, exe, real)) def main(argv=None): # pylint: disable=too-many-locals """Top-level main function.""" if argv and len(argv) >= 2: # If arg is given, run entire script remotely sys.stderr.write('NOTE: Remote PATH may be incomplete.\n') this_file = open(__file__) this_script = this_file.read() this_file.close() result = PythonCommands(['ssh', '-T', argv[1], 'python'], this_script) if result and result[-1]: result += [''] # Add trailing EOL if needed sys.stdout.write('\n'.join(result)) return 0 python_list = ExeFilter(FileList(MakePatterns(GetPaths(), PYTHON_PATTERNS))) done = set() unique = total = 0 for python in python_list: try: values = PythonCommands([python], '\n'.join(PYTHON_COMMANDS)) except (OSError, BadReturn): # Avoid 'as' for <2.6 compatibility exmsg = traceback.format_exception_only(*sys.exc_info()[:2])[-1].strip() _print('Skipping %s due to %s' % (python, exmsg)) continue if len(values) != len(PYTHON_VALUE_NAMES): _print('Skipping %s due to number of results (%d) != %d' % (python, len(values), len(PYTHON_VALUE_NAMES))) continue valdict = dict(zip(PYTHON_VALUE_NAMES, values)) total += 1 exe = valdict['exec'] real = os.path.realpath(exe) if real in done: PrintExe('Redundant', python, os.path.realpath(python)) PrintExe(' (Executable)', exe, real) continue done |= set([real]) unique += 1 PrintExe('Command', python, os.path.realpath(python)) PrintExe(' Executable', exe, real) if valdict['lib-noarch'] == valdict['lib-arch']: _print(' Libs(all): %s' % valdict['lib-noarch']) else: _print(' Libs(arch=any): %s' % valdict['lib-noarch']) _print(' Libs(arch=specific): %s' % valdict['lib-arch']) if len(set([valdict[x] for x in PYTHON_PREFIX_NAMES])) == 1: _print(' Prefix(all): %s' % valdict['sys-prefix']) else: _print(' Prefix(sys): %s' % valdict['sys-prefix']) _print(' Prefix(std): %s' % valdict['std-prefix']) _print(' Prefix(exec): %s' % valdict['exec-prefix']) plural = unique != 1 and 's' or '' # pylint: disable=consider-using-ternary print('Found %d unique Python installation%s out of %d total' % (unique, plural, total)) return 0 if __name__ == '__main__': sys.exit(main(sys.argv)) # pragma: no cover ntpsec-1.1.0+dfsg1/devel/release0000755000175000017500000000247413252364117016301 0ustar rlaagerrlaager#!/bin/sh # Generate commands to cut an ntpsec release. # # VERSION must contain the correct release number. KEY=477C7528 # Release manager's GPG key ID DST=markatwood@service1.ntpsec.org:/usr/local/jail/ftp.ntpsec.org/data/ftp/pub/releases/ # No user-serviceable parts below this line V=`cat ../VERSION` UV=`cat ../VERSION | tr '.' '_'` cat <${TAR}.sum # GPG sign that sha256sum file. gpg -u ${KEY} -a --output ${TAR}.sum.asc --clearsign ${TAR}.sum # Copy the release tarball, GPG detached signature of the release # tarball, and the signed sha256sum file to the ftp artifact server. scp ntpsec-${V}.tar* ${DST} echo "Don't forget to bump the minor version number!" EOF # end ntpsec-1.1.0+dfsg1/devel/linkcheck0000755000175000017500000000620013252364117016603 0ustar rlaagerrlaager#!/usr/bin/env python # # linkcheck - check link integrity in an asciidoc document tree # # Output is a list of unsatisfied references in the format of GCC # error messages, suitable for stepping through with Emacs compilation # mode. # # Optional argument is the root directory of the tree, otherwise it # is run from the corrent directory. # # SPDX-License-Identifier: BSD-2-clause from __future__ import print_function, division import os import re import sys boxanchor_re = re.compile(r"\[\[([a-z0-9_-]*)\]\]") linkanchor_re = re.compile(r"anchor:([a-z0-9_-]*)\[\]") refanchor_re = re.compile(r"link:([^.]*).html#([a-z0-9_-]*)") if len(sys.argv) > 1: prefix = sys.argv[1] else: prefix = "" def tabulate(path): iostack.append((0, open(path))) linecount = 0 while True: line = iostack[-1][1].readline() linecount += 1 if line == "": iostack[-1][1].close() (linecount, _) = iostack.pop() if not iostack: return else: continue if line.startswith("include::"): filestem = line.strip()[9:][:-2] dirname = "" if iostack: includer = iostack[-1][1].name dirname = os.path.dirname(includer[len(prefix):]) newpath = os.path.join(prefix, dirname, filestem) try: iostack.append((linecount, open(newpath))) except (IOError, OSError): sys.stderr.write("linkcheck: Can't open %s\n" % newpath) raise SystemExit(1) continue # Ordinary processing # This is the tricky part. Because we're reference-checking the # generated HTML, the file part of a stored anchor name needs to be # the original path from the top of the include stack, not # whatever inclusion we might be walking through now. html = path[len(prefix):].replace(".txt", ".html") m = boxanchor_re.search(line) if m: anchor = "link:" + html + "#" + m.group(1) anchors.add(anchor) m = linkanchor_re.search(line) if m: anchor = "link:" + html + "#" + m.group(1) anchors.add(anchor) m = refanchor_re.search(line) if m: references[m.group(0)] = (iostack[-1][1].name, linecount) if __name__ == '__main__': iostack = [] references = {} anchors = set([]) for dirpath, dnames, fnames in os.walk(prefix): for f in fnames: fullpath = os.path.join(dirpath, f) if fullpath.endswith(".txt") and "includes" not in fullpath: tabulate(fullpath) # print("References: %s" % references) # print("Anchors: %s" % anchors) hanging = [] for ref in references: if ref not in anchors: hanging.append(ref) print("%d anchors, %d references, %d references unsatisfied" % (len(anchors), len(references), len(hanging))) unsatisfied = list(hanging) unsatisfied.sort() for item in unsatisfied: print('"%s", line %d: %s' % (references[item][0], references[item][1], item)) # end ntpsec-1.1.0+dfsg1/devel/units.txt0000644000175000017500000000645013252364117016634 0ustar rlaagerrlaagerThis document is a one stop shop of the important time variables and what units they represent in various areas throughout the program. Important note: This document is very incomplete, and may have the same variable listed more than once. Categorization is haphazard as well. Outstanding general questions: * Are all seconds-per-second units the same thing as parts-per-million? * How many of these are just different names for the same value? # The following were harvested from ntpd.h ntp_loopfilter.c Name | internal | Comment ============================================= drift_comp | seconds/s | clock frequency clock_stability | seconds/s | clock_max_back | seconds | max backward offset before step clock_max_fwd | seconds | max forward offset before step clock_panic | seconds | max offset before panic clock_phi | seconds/s | dispersion rate (this is jitter, right?) clock_minstep | seconds | step timeout Clock state machine variables Name | internal | Comment ======================================== sys_poll | log2 seconds | system poll interval last_time | seconds | time of last clock update last_offset | seconds | last clock offset allan_xpt | log2 seconds | Allan intercept clock_jitter | seconds | cyc_offset | seconds | sys_jitter | seconds | # The following were mostly harvested from ntp_control.c System Variables Name | internal | mode 6 ================================================= leap | seconds | seconds precision | log2(seconds) | log2(seconds) rootdelay | seconds | milliseconds rootdisp | seconds | milliseconds rootdist | seconds | milliseconds offset | seconds | milliseconds frequency (aka drift) | seconds/s | microseconds/s (aka ppm) sys_jitter | seconds | milliseconds clk_jitter | seconds | milliseconds clk_wander | seconds/s | microseconds/s leapsmearinterval | UNKNOWN | UNKNOWN leapsmearoffset | seconds | milliseconds mintc (aka CR_RATE) | UNKNOWN | UNKNOWN authdelay | seconds | milliseconds koffset | seconds | milliseconds kmaxerr | seconds | milliseconds kesterr | seconds | milliseconds kprecis | seconds | milliseconds kppsjitter | seconds | milliseconds fuzz | seconds | milliseconds clk_wander_threshold | seconds | microseconds tick | seconds | milliseconds tai | seconds | seconds Peer Variables Name | internal | mode 6 ===================================== in | seconds | milliseconds out | seconds | milliseconds rootdelay | seconds | milliseconds rootdisp | seconds | milliseconds bias | seconds | milliseconds delay | seconds | milliseconds offset | seconds | milliseconds jitter | seconds | milliseconds dispersion | seconds | milliseconds Clock Variables Name | internal | mode 6 ==================================== poll | seconds | seconds fudgetime1 | seconds | milliseconds fudgetime2 | seconds | milliseconds ntpsec-1.1.0+dfsg1/devel/ChangeLog0000644000175000017500000065672013252364117016516 0ustar rlaagerrlaagerThis is the historical change log from before the DVCS repository conversion. If you want the level of detail this contained about post-conversion changes, read the per-commit change comments. This describes changes from 2001 to early 2015. For earlier changes, see the file CommitLog-4.1.0. (4.3.34) 2015/06/04 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2824] Convert update-leap to perl. (also see 2769) * [Bug 2832] refclock_jjy.c supports the TDC-300. * [Bug 2834] Correct a broken html tag in html/refclock.html * [Bug 2837] Allow a configurable DSCP value. (4.3.33) 2015/05/12 Released by Harlan Stenn Below are from 4.2.8p3: * 4.2.8p3-RC1 * [Bug 2745] ntpd -x steps clock on leap second Do leap second stepping only of the step adjustment is beyond the proper jump distance limit and step correction is allowed at all. (4.3.32) 2015/05/09 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2792] If the IFF_RUNNING interface flag is supported then an interface is ignored as long as this flag is not set since the interface is not usable (e.g., no link). * [Bug 2808] GPSD_JSON driver enhancements, step 1. Increase internal token buffer to parse all JSON data, even SKY. (4.3.31) 2015/05/08 Released by Harlan Stenn Below are from 4.2.8p3: * CID 739725: Fix a rare resource leak in libevent/listener.c. * [Bug 2750] build for Win64 Building for 32bit of loopback ppsapi needs def file * [Bug 2808] GPSD_JSON driver enhancements, step 1. Increase internal token buffer to parse all JSON data, even SKY. Defer logging of errors during driver init until the first unit is started, so the syslog is not cluttered when the driver is not used. * [Bug 2821] Add a missing NTP_PRINTF and a missing const. * Add an assert to the ntpq ifstats code. * Clean up the RLIMIT_STACK code. (4.3.30) 2015/05/07 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2825] Quiet file installation in html/ . (4.3.29) 2015/05/05 Released by Harlan Stenn (4.3.28) 2015/05/04 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2822] New leap column in sntp broke NTP::Util.pm. (4.3.27) 2015/05/03 Released by Harlan Stenn Below are from 4.2.8p3: * Windows port build cleanup. (4.3.26) 2015/05/02 Released by Harlan Stenn Below are from 4.2.8p3: * CID 1296235: Fix refclock_jjy.c and correcting type of the driver40-ja.html (4.3.25) 2015/05/01 Released by Harlan Stenn * [Bug 2818] refclock_shm.c missing a line from -stable version. Below are from 4.2.8p3: * [Bug 2590] autogen-5.18.5. * [Bug 2650] fix includefile processing. (4.3.24) 2015/04/30 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2745] ntpd -x steps clock on leap second Do leap second stepping only of the step adjustment is beyond the proper jump distance limit and step correction is allowed at all. * [Bug 2806] refclock_jjy.c supports the Telephone JJY * [Bug 2808] GPSD_JSON driver enhancements, step 1 Various improvements, see http://bugs.ntp.org/2808 for details. Changed libjsmn to a more recent version. fix coverity issues with refclock_gpsdjson and refclock_shm Add a few more tallies as per Hal Murray's suggestions (4.3.23) 2015/04/29 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2805] ntpd fails to join multicast group. (4.3.22) 2015/04/28 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2612] restrict: Warn when 'monitor' can't be disabled because of 'limited'. (4.3.21) 2015/04/27 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2776] Clean up EVP_MD_do_all_sorted() test. * [Bug 2813] HP-UX needs -D__STDC_VERSION__=199901L and limits.h. * [Bug 2815] net-snmp before v5.4 has circular library dependencies. (4.3.20) 2015/04/26 Released by Harlan Stenn Below are from 4.2.8p3: * CID 1295478: Quiet a pedantic potential error from the fix for Bug 2776. * CID 1269537: Clean up a line of dead code in getShmTime(). * ntpq.c cleanup. * Improve the ntpq documentation around the controlkey keyid. * autogen-5.18.5. (4.3.19) 2015/04/25 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2804] Fix regression in previous fix. (4.3.18) 2015/04/24 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2776] Improve ntpq's 'help keytype'. * [Bug 2800] refclock_true.c true_debug() can't open debug log because of incompatible open/fdopen parameters. * [Bug 2804] install-local-data assumes GNU 'find' semantics. (4.3.17) 2015/04/23 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2776] Improve ntpq's 'help keytype' on pre-OpenSSL 1.0. (4.3.16) 2015/04/22 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2776] Improve ntpq's 'help keytype'. * [Bug 2794] Clean up kernel clock status reports. (4.3.15) 2015/04/20 Released by Harlan Stenn Below are from 4.2.8p3: * [Bug 2804] install-local-data assumes GNU 'find' semantics. * [Bug 2808] GPSD_JSON driver enhancements, step 1. Various improvements, see http://bugs.ntp.org/2808 for details. Changed libjsmn to a more recent version. * [Bug 2810] refclock_shm.c memory barrier code needs tweaks for QNX. (4.3.14) 2015/04/07 Released by Harlan Stenn Below are from 4.2.8p2: * [Sec 2779] ntpd accepts unauthenticated packets with symmetric key crypto. * [Sec 2781] Authentication doesn't protect symmetric associations against DoS attacks. (4.3.13) 2015/04/03 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 2763] Fix for different thresholds for forward and backward steps. (4.3.12) 2015/04/02 Released by Harlan Stenn * [Bug 2795] fixed some minor warnings. Below are from 4.2.8p2: * [Bug 2788] New flag -G (force_step_once). * [Bug 2592] FLAG_TSTAMP_PPS cleanup for refclock_parse.c. * [Bug 2794] Clean up kernel clock status reports. * [Bug 2795] Cannot build without OpenSLL (on Win32). Provided a Win32 specific wrapper around libevent/arc4random.c. * [Bug 2796] ntp-keygen crashes in 'getclock()' on Win32. * [Bug 2797] ntp-keygen trapped in endless loop for MD5 keys on big-endian machines. * [Bug 2798] sntp should decode and display the leap indicator. * Simple cleanup to html/build.html (4.3.11) 2015/03/29 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 2346] "graceful termination" signals do not do peer cleanup. * [Bug 2769] cleannup for update-leap (4.3.10) 2015/03/22 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 1787] DCF77's formerly "antenna" bit is "call bit" since 2003. * [Bug 2769] New script: update-leap * [Bug 2777] Fixed loops and decoding of Meinberg GPS satellite info. Removed non-ASCII characters from some copyright comments. Removed trailing whitespace. Updated definitions for Meinberg clocks from current Meinberg header files. Now use C99 fixed-width types and avoid non-ASCII characters in comments. Account for updated definitions pulled from Meinberg header files. Updated comments on Meinberg GPS receivers which are not only called GPS16x. Replaced some constant numbers by defines from ntp_calendar.h Modified creation of parse-specific variables for Meinberg devices in gps16x_message(). Reworked mk_utcinfo() to avoid printing of ambiguous leap second dates. Modified mbg_tm_str() which now expexts an additional parameter controlling if the time status shall be printed. * [Bug 2789] Quiet compiler warnings from libevent. * [Bug 2790] If ntpd sets the Windows MM timer highest resolution pause briefly before measuring system clock precision to yield correct results. * Comment from Juergen Perlinger in ntp_calendar.c to make the code clearer. * Use predefined function types for parse driver functions used to set up function pointers. Account for changed prototype of parse_inp_fnc_t functions. Cast parse conversion results to appropriate types to avoid compiler warnings. Let ioctl() for Windows accept a (void *) to avoid compiler warnings when called with pointers to different types. (4.3.9) 2015/03/16 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 2763] Allow different thresholds for forward and backward steps. (4.3.8) 2015/03/10 Released by Harlan Stenn * [Bug 2752] Update for mkver.bat for Windows from David Taylor. Account for release numbering scheme for 4.3.x and later. Below are from 4.2.8p2: * [Bug 2774] Unreasonably verbose printout - leap pending/warning (4.3.7) 2015/03/07 Released by Harlan Stenn * [Bug 2784] Fix for 2782 uses clock_gettime() instead of time(). (4.3.6) 2015/03/06 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 2782] Refactor refclock_shm.c, add memory barrier protection. (4.3.5) 2015/03/05 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 2783] Quiet autoconf warnings about missing AC_LANG_SOURCE. (4.3.4) 2015/03/04 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 2773] Early leap announcement from Palisade/Thunderbolt * [Bug 2775] ntp-keygen.c fails to compile under Windows. (4.3.3) 2015/02/28 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 2751] jitter.h has stale copies of l_fp macros. * [Bug 2756] ntpd hangs in startup with gcc 3.3.5 on ARM. * [Bug 2757] Quiet compiler warnings. * [Bug 2759] Expose nonvolatile/clk_wander_threshold to ntpq. * [Bug 2766] ntp-keygen output files should not be world-readable. * [Bug 2767] ntp-keygen -M should symlink to ntp.keys. * [Bug 2771] nonvolatile value is documented in wrong units. (4.3.2) 2015/02/25 Released by Harlan Stenn (4.3.1) 2015/02/21 Released by Harlan Stenn Below are from 4.2.8p2: * [Bug 1960] setsockopt IPV6_MULTICAST_IF: Invalid argument. * [Bug 2728] See if C99-style structure initialization works. * [Bug 2749] ntp/lib/NTP/Util.pm needs update for ntpq -w, IPv6, .POOL. . * [Bug 2751] jitter.h has stale copies of l_fp macros. * [Bug 2757] Quiet compiler warnings. (4.3.0) 2015/02/11 Released by Harlan Stenn From 4.2.8p2: * [Bug 2747] Upgrade libevent to 2.1.5-beta. ntp-4.3 begins. --- * [Bug 2824] Convert update-leap to perl. (also see 2769) * [Bug 2832] refclock_jjy.c supports the TDC-300. * [Bug 2834] Correct a broken html tag in html/refclock.html * [Bug 2837] Allow a configurable DSCP value. --- (4.2.8p3-RC1) 2015/05/12 Released by Harlan Stenn * CID 739725: Fix a rare resource leak in libevent/listener.c. * CID 1295478: Quiet a pedantic potential error from the fix for Bug 2776. * CID 1296235: Fix refclock_jjy.c and correcting type of the driver40-ja.html * CID 1269537: Clean up a line of dead code in getShmTime(). * [Bug 2590] autogen-5.18.5. * [Bug 2612] restrict: Warn when 'monitor' can't be disabled because of 'limited'. * [Bug 2650] fix includefile processing. * [Bug 2745] ntpd -x steps clock on leap second Fixed an initial-value problem that caused misbehaviour in absence of any leapsecond information. Do leap second stepping only of the step adjustment is beyond the proper jump distance limit and step correction is allowed at all. * [Bug 2750] build for Win64 Building for 32bit of loopback ppsapi needs def file * [Bug 2776] Improve ntpq's 'help keytype'. * [Bug 2782] Refactor refclock_shm.c, add memory barrier protection. * [Bug 2792] If the IFF_RUNNING interface flag is supported then an interface is ignored as long as this flag is not set since the interface is not usable (e.g., no link). * [Bug 2794] Clean up kernel clock status reports. * [Bug 2800] refclock_true.c true_debug() can't open debug log because of incompatible open/fdopen parameters. * [Bug 2804] install-local-data assumes GNU 'find' semantics. * [Bug 2805] ntpd fails to join multicast group. * [Bug 2806] refclock_jjy.c supports the Telephone JJY. * [Bug 2808] GPSD_JSON driver enhancements, step 1. Fix crash during cleanup if GPS device not present and char device. Increase internal token buffer to parse all JSON data, even SKY. Defer logging of errors during driver init until the first unit is started, so the syslog is not cluttered when the driver is not used. Various improvements, see http://bugs.ntp.org/2808 for details. Changed libjsmn to a more recent version. fix coverity issues with refclock_gpsdjson and refclock_shm Add a few more tallies as per Hal Murray's suggestions * [Bug 2810] refclock_shm.c memory barrier code needs tweaks for QNX. * [Bug 2813] HP-UX needs -D__STDC_VERSION__=199901L and limits.h. * [Bug 2815] net-snmp before v5.4 has circular library dependencies. * [Bug 2821] Add a missing NTP_PRINTF and a missing const. * [Bug 2822] New leap column in sntp broke NTP::Util.pm. * [Bug 2825] Quiet file installation in html/ . * Add an assert to the ntpq ifstats code. * Clean up the RLIMIT_STACK code. * Improve the ntpq documentation around the controlkey keyid. * ntpq.c cleanup. * Windows port build cleanup. --- (4.2.8p2) 2015/04/07 Released by Harlan Stenn (4.2.8p2-RC3) 2015/04/03 Released by Harlan Stenn * [Bug 2763] Fix for different thresholds for forward and backward steps. --- (4.2.8p2-RC2) 2015/04/03 Released by Harlan Stenn * [Bug 2592] FLAG_TSTAMP_PPS cleanup for refclock_parse.c. * [Bug 2769] New script: update-leap * [Bug 2769] cleannup for update-leap * [Bug 2788] New flag -G (force_step_once). * [Bug 2794] Clean up kernel clock status reports. * [Bug 2795] Cannot build without OpenSLL (on Win32). Provided a Win32 specific wrapper around libevent/arc4random.c. fixed some minor warnings. * [Bug 2796] ntp-keygen crashes in 'getclock()' on Win32. * [Bug 2797] ntp-keygen trapped in endless loop for MD5 keys on big-endian machines. * [Bug 2798] sntp should decode and display the leap indicator. * Simple cleanup to html/build.html --- (4.2.8p2-RC1) 2015/03/30 Released by Harlan Stenn * [Bug 2794] Don't let reports on normal kernel status changes look like errors. * [Bug 2788] New flag -G (force_step_once). * [Bug 2592] Account for PPS sources which can provide an accurate absolute time stamp, and status information. Fixed indention and removed trailing whitespace. * [Bug 1787] DCF77's formerly "antenna" bit is "call bit" since 2003. * [Bug 1960] setsockopt IPV6_MULTICAST_IF: Invalid argument. * [Bug 2346] "graceful termination" signals do not do peer cleanup. * [Bug 2728] See if C99-style structure initialization works. * [Bug 2747] Upgrade libevent to 2.1.5-beta. * [Bug 2749] ntp/lib/NTP/Util.pm needs update for ntpq -w, IPv6, .POOL. . * [Bug 2751] jitter.h has stale copies of l_fp macros. * [Bug 2756] ntpd hangs in startup with gcc 3.3.5 on ARM. * [Bug 2757] Quiet compiler warnings. * [Bug 2759] Expose nonvolatile/clk_wander_threshold to ntpq. * [Bug 2763] Allow different thresholds for forward and backward steps. * [Bug 2766] ntp-keygen output files should not be world-readable. * [Bug 2767] ntp-keygen -M should symlink to ntp.keys. * [Bug 2771] nonvolatile value is documented in wrong units. * [Bug 2773] Early leap announcement from Palisade/Thunderbolt * [Bug 2774] Unreasonably verbose printout - leap pending/warning * [Bug 2775] ntp-keygen.c fails to compile under Windows. * [Bug 2777] Fixed loops and decoding of Meinberg GPS satellite info. Removed non-ASCII characters from some copyright comments. Removed trailing whitespace. Updated definitions for Meinberg clocks from current Meinberg header files. Now use C99 fixed-width types and avoid non-ASCII characters in comments. Account for updated definitions pulled from Meinberg header files. Updated comments on Meinberg GPS receivers which are not only called GPS16x. Replaced some constant numbers by defines from ntp_calendar.h Modified creation of parse-specific variables for Meinberg devices in gps16x_message(). Reworked mk_utcinfo() to avoid printing of ambiguous leap second dates. Modified mbg_tm_str() which now expexts an additional parameter controlling if the time status shall be printed. * [Sec 2779] ntpd accepts unauthenticated packets with symmetric key crypto. * [Sec 2781] Authentication doesn't protect symmetric associations against DoS attacks. * [Bug 2783] Quiet autoconf warnings about missing AC_LANG_SOURCE. * [Bug 2784] Fix for 2782 uses clock_gettime() instead of get_ostime(). * [Bug 2789] Quiet compiler warnings from libevent. * [Bug 2790] If ntpd sets the Windows MM timer highest resolution pause briefly before measuring system clock precision to yield correct results. * Comment from Juergen Perlinger in ntp_calendar.c to make the code clearer. * Use predefined function types for parse driver functions used to set up function pointers. Account for changed prototype of parse_inp_fnc_t functions. Cast parse conversion results to appropriate types to avoid compiler warnings. Let ioctl() for Windows accept a (void *) to avoid compiler warnings when called with pointers to different types. --- (4.2.8p1) 2015/02/04 Released by Harlan Stenn * Update the NEWS file. * [Sec 2671] vallen in extension fields are not validated. --- (4.2.8p1-RC2) 2015/01/29 Released by Harlan Stenn * [Bug 2627] shm refclock allows only two units with owner-only access rework: reverted sense of mode bit (so default reflects previous behaviour) and updated ducumentation. * [Bug 2732] - Leap second not handled correctly on Windows 8 use 'GetTickCount()' to get the true elapsed time of slew (This should work for all versions of Windows >= W2K) * [Bug 2738] Missing buffer initialization in refclock_parse.c::parsestate(). * [Bug 2739] Parse driver with PPS enabled occasionally evaluates PPS timestamp with wrong sign. Removed some German umlauts. * [Bug 2740] Removed some obsolete code from the parse driver. * [Bug 2741] Incorrect buffer check in refclock_parse.c::parsestatus(). --- (4.2.8p1-RC1) 2015/01/24 Released by Harlan Stenn * Start the RC for 4.2.8p1. * [Bug 2187] Update version number generation scripts. * [Bug 2617] Fix sntp Usage documentation section. * [Sec 2672] Code cleanup: On some OSes ::1 can be spoofed... * [Bug 2736] Show error message if we cannot open the config file. * Copyright update. * Fix the package name. --- (4.2.8p1-beta5) 2015/01/07 Released by Harlan Stenn * [Bug 2695] Windows build: __func__ not supported under Windows. * [Bug 2728] Work around C99-style structure initialization code for older compilers, specifically Visual Studio prior to VS2013. --- (4.2.8p1-beta4) 2015/01/04 Released by Harlan Stenn * [Bug 1084] PPSAPI for ntpd on Windows with DLL backends * [Bug 2695] Build problem on Windows (sys/socket.h). * [Bug 2715] mdnstries option for ntp.conf from NetBSD. * Fix a regression introduced to timepps-Solaris.h as part of: [Bug 1206] Required compiler changes for Windows (4.2.5p181) 2009/06/06 --- (4.2.8p1-beta3) 2015/01/02 Released by Harlan Stenn * [Bug 2627] shm refclock allows only two units with owner-only access Use mode bit 0 to select public access for units >= 2 (units 0 & 1 are always private. * [Bug 2681] Fix display of certificate EOValidity dates on 32-bit systems. * [Bug 2695] 4.2.8 does not build on Windows. * [bug 2700] mrulist stopped working in 4.2.8. * [Bug 2706] libparse/info_trimble.c build dependencies are broken. * [Bug 2713] variable type/cast, parameter name, general cleanup from NetBSD. * [Bug 2714] libevent may need to be built independently of any build of sntp. * [Bug 2715] mdnstries option for ntp.conf from NetBSD. --- (4.2.8p1-beta2) 2014/12/27 Released by Harlan Stenn * [Bug 2674] Install sntp in sbin on NetBSD. * [Bug 2693] ntp-keygen doesn't build without OpenSSL and sntp. * [Bug 2707] Avoid a C90 extension in libjsmn/jsmn.c. * [Bug 2709] see if we have a C99 compiler (not yet required). --- (4.2.8p1-beta1) 2014/12/23 Released by Harlan Stenn * [Sec 2672] On some OSes ::1 can be spoofed, bypassing source IP ACLs. * [Bug 2693] ntp-keygen doesn't build without OpenSSL. * [Bug 2697] IN6_IS_ADDR_LOOPBACK build problems on some OSes. * [Bug 2699] HAVE_SYS_SELECT_H is misspelled in refclock_gpsdjson.c. --- (4.2.8) 2014/12/19 Released by Harlan Stenn * [Sec 730] Increase RSA_generate_key modulus. * [Sec 2666] Use cryptographic random numbers for md5 key generation. * [Sec 2667] buffer overflow in crypto_recv(). * [Sec 2668] buffer overflow in ctl_putdata(). * [Sec 2669] buffer overflow in configure(). * [Sec 2670] Missing return; from error clause. * [Sec 2671] vallen in extension fields are not validated. * [Sec 2672] On some OSes ::1 can be spoofed, bypassing source IP ACLs. * [Bug 2691] Wrong variable name in refclock_ripencc.c. (4.2.7p486-RC) 2014/12/18 Released by Harlan Stenn * [Bug 2687] RefClock 26/hpgps doesn't work at default line speed (4.2.7p485-RC) 2014/12/12 Released by Harlan Stenn * [Bug 2686] refclock_gpsdjson needs strtoll(), which is not always present. (4.2.7p484-RC) 2014/12/11 Released by Harlan Stenn (4.2.7p483) 2014/12/08 Released by Harlan Stenn * [Bug 2685] Better document the KOD file for sntp. (4.2.7p482) 2014/12/02 Released by Harlan Stenn * [Bug 2641] sntp is installed in the wrong location in Solaris. * [Bug 2678] nmea_control() now checks 'refclock_params()' result. (4.2.7p481) 2014/11/22 Released by Harlan Stenn * [Bug 2314] Only enable PPS if kernel consumer binding succeeds. * [Bug 2314] Kernel PPS binding EOPNOTSUPP is a failure condition. * Rename pps_enable to hardpps_enable. (4.2.7p480) 2014/11/21 Released by Harlan Stenn * [Bug 2677] PATH_MAX isn't #define'd under Windows. Regression from the patch fixing Bug 2639. (4.2.7p479) 2014/11/15 Released by Harlan Stenn * [Bug 2651] Certificates with ASN timestamps w/ 4-digit years mis-parsed. (4.2.7p478) 2014/11/14 Released by Harlan Stenn * [Sec 2630] buffer overrun in ntpq tokenize(). * [Bug 2639] Check return value of ntp_adjtime(). * [Bug 2650] includefile processing broken. * [Bug 2661] ntpq crashes with mreadvar. (4.2.7p477) 2014/11/13 Released by Harlan Stenn * [Bug 2657] Document that "restrict nopeer" intereferes with "pool". (4.2.7p476) 2014/10/08 Released by Harlan Stenn * [Bug 2503] SHT utility outdated (4.2.7p475) 2014/09/11 Released by Harlan Stenn * [Bug 2654] refclock_true.c doesn't identify the Mk III. (4.2.7p474) 2014/09/10 Released by Harlan Stenn * [Bug 2536] ntpd sandboxing support (libseccomp2) cleanup. * [Bug 2649] Clean up html/ page installation. (4.2.7p473) 2014/09/06 Released by Harlan Stenn * [Bug 2649] Clean up html/ page installation. (4.2.7p472) 2014/09/06 Released by Harlan Stenn * [Bug 2556] mrulist is missing from the generated ntpq man page. (4.2.7p471) 2014/09/05 Released by Harlan Stenn * [Bug 2649] "make install" leaves wrong owner for files in html/. * [Bug 2652] Windows hates directory names that contain a :. (4.2.7p470) 2014/09/02 Released by Harlan Stenn * [Bug 2502] Autogen text replacement errors. * autogen-5.18.5pre1 * html/ cleanups from Hal Murray. (4.2.7p469) 2014/09/01 Released by Harlan Stenn * [Bug 2536] ntpd sandboxing support (libseccomp2) cleanup. (4.2.7p468) 2014/08/31 Released by Harlan Stenn * [Bug 2556] ntpq man page cleanup. * autogen-5.18.4 (4.2.7p467) 2014/08/28 Released by Harlan Stenn * [Bug 2639] Check return value of ntp_adjtime(). * [Bug 2640] STA_NANO can result in invalid ntv.constant. (4.2.7p466) 2014/08/27 Released by Harlan Stenn * [Bug 2536] ntpd sandboxing support (libseccomp2) cleanup. (4.2.7p465) 2014/08/23 Released by Harlan Stenn * [Bug 2538] NTP programs print exit code in help/usage text. * [Bug 2595] Man page quirks: ntpdate references in ntpd. * [Bug 2613] www.ntp.org/bugs.html tells folks to email doc bugs to DLM. * [Bug 2636] Clutter in syslog if gpsd not running - found (hopefully) last cause for clutter in protocol version - log GPSD revision and release numbers with protocol version (4.2.7p464) 2014/08/22 Released by Harlan Stenn * [Bug 2636] Fix coverity warning from previous patch. (4.2.7p463) 2014/08/21 Released by Harlan Stenn * [Bug 2636] Clutter in syslog if gpsd not running - make driver work with GPSD protocol version 3.9 - use exponential back-off for connection problems - implement rate-limit for syslog entries (4.2.7p462) 2014/08/16 Released by Harlan Stenn * [Bug 2622] Synchronisation problem using SHM [...] Add 'control' function -- fudge values not available during start. (4.2.7p461) 2014/08/14 Released by Harlan Stenn * [Bug 1128] ntpq truncates "remote" host information. * More autogen-5.18.4pre14 cleanup. (4.2.7p460) 2014/08/13 Released by Harlan Stenn * More autogen-5.18.4pre14 cleanup. (4.2.7p459) 2014/08/12 Released by Harlan Stenn * [Bug 2630] Limit the ntpq command buffer to 512 bytes. * FlexeLint cleanups. * Try bison-3.0.2 instead of bison-2.5. (4.2.7p458) 2014/08/11 Released by Harlan Stenn * [Bug 2633] Provide stdnoreturn.h for windows port. (4.2.7p457) 2014/08/09 Released by Harlan Stenn * [Bug 2622] Synchronisation problem using SHM when time difference is more than four hours: Change SHM driver so TOY restricted API is not used any more. (Plus some minor cleanup in logic and flow control) * Pass the configration source into the parser as argument rather than through a global variable. * Fix nits in the ntpq man page. * autogen-5.18.4pre14 (4.2.7p456) 2014/08/07 Released by Harlan Stenn * CID 739722: Change the way the extention and MAC fields are processed. (4.2.7p455) 2014/08/03 Released by Harlan Stenn * [Bug 2565] ntpd sometimes logs unexpected getifaddrs() errors. * CID 739722: Clean up the definition of the exten field of struct pkt. (4.2.7p454) 2014/07/30 Released by Harlan Stenn * [Bug 2628] 'mon_getmoremem()' relies on undefined behaviour (4.2.7p453) 2014/07/19 Released by Harlan Stenn * [Bug 2597] leap file loose ends (follow-up) - uniform expiration check messages for config and timer triggered leap file loads - timer triggered loads log messages only once per day (4.2.7p452) 2014/07/18 Released by Harlan Stenn * Make all of the html/ .html files use the same format for "Last update". (4.2.7p451) 2014/07/17 Released by Harlan Stenn * Fix the "Last update" entries in the html/ subtree. (4.2.7p450) 2014/07/16 Released by Harlan Stenn * Distribute the scripts needed for the fix for Bug 2547. (4.2.7p449) 2014/07/16 Released by Harlan Stenn * [Bug 2547] Automate update of "Last Update" datestamps in .html files. * [Bug 2623] Missing {} in refclock_oncore.c. * Quiet warnings from ntp_calendar.h: avoid using argument names. * Fix typos in decode.html and debug.html . (4.2.7p448) 2014/07/15 Released by Harlan Stenn * [Bug 2621] Avoid use of indeterminate address after 'free()' (minor C standard conformance issue) * Quiet warnings from ntp_calendar.h: avoid using argument names. (4.2.7p447) 2014/07/05 Released by Harlan Stenn * [Bug 2620] Use version.pm for checking version numbers in NTP::Util. * [Bug 2624] Fix signed compare on 'l_fp'. (4.2.7p446) 2014/06/28 Released by Harlan Stenn * [Bug 2597] leap file processing -- loose ends. * [Bug 2614] use 'unsigned long' consistently in ntp_random.c to avoid possibly undefined behaviour in signed int overflow * [Bug 2619] Save a signed int copy of the return value of i2d_DSA_SIG(). Provide missing msyslog() message in crypto_alice(). * Fix a variable lifetime issue. * Allow for version suffix in libevent in ntp_libevent.m4. (4.2.7p445) 2014/06/12 Released by Harlan Stenn * [Bug 2556] mrulist isn't mentioned in the ntpq man page. (4.2.7p444) 2014/05/19 Released by Harlan Stenn * [Bug 2597] leap file processing -- loose ends fixed coverity issues (4.2.7p443) 2014/05/10 Released by Harlan Stenn * [Bug 2594] Update the year in sntp/include/copyright.def. (4.2.7p442) 2014/05/09 Released by Harlan Stenn * [Bug 2589] Update VS2013 project files for libntp. * [Bug 2600] Fix "Undisicplined Local Clock" driver1.html page. (4.2.7p441) 2014/05/04 Released by Harlan Stenn * [Bug 2597] leap file processing -- loose ends log daily warning when leap info less than 28 days to expiration or already expired; nag hourly on last day before expiration; log when leapfile name is invalid (4.2.7p440) 2014/04/09 Released by Harlan Stenn * [Bug 2536] ntpd sandboxing support (libseccomp2) cleanup. * [Bug 2570] cleanup: fix log format for successful leapfile load (4.2.7p439) 2014/04/03 Released by Harlan Stenn * [Bug 2589] fix VS2009 compile problem. (4.2.7p438) 2014/04/01 Released by Harlan Stenn * [Bug 2546] Windows build documentation updates. (4.2.7p437) 2014/03/31 Released by Harlan Stenn * [Bug 2537] ntpd truncates symmetric keys to 20 bytes. * [Bug 2546] Documentation updates. (4.2.7p436) 2014/03/31 Released by Harlan Stenn * Update to libopts-40.2.15, and autogen-5.18.3pre18. * [Bug 2311] Add more tags to mdoc2xxx. * [Bug 2502] Assorted text replacement errors in 4.2.7p345 * [Bug 2538] ntp programs print exit code as part of the "usage" text. (4.2.7p435) 2014/03/29 Released by Harlan Stenn * [Bug 2570] cleanup: reduced logging noise, moved some functions into libntp. (4.2.7p434) 2014/03/21 Released by Harlan Stenn * [Bug 2577] Update VS2013 solution and project files. (4.2.7p433) 2014/03/10 Released by Harlan Stenn * Clean up last-update timestamps of html/*.html files. * [Bug 2546] Documentation updates. (4.2.7p432) 2014/03/09 Released by Harlan Stenn * CID 711660: Do a non-NULL pointer assertion check a bit earlier. (4.2.7p431) 2014/03/05 Released by Harlan Stenn * [Bug 2572] cross-compiling fails for --with-yielding-select. (4.2.7p430) 2014/03/04 Released by Harlan Stenn * Upgrade to libevent-2.1.3-alpha-dev. * [Bug 2572] cross-compiling fails for --with-yielding-select. (4.2.7p429) 2014/03/03 Released by Harlan Stenn * CID 1165098: Remove logically dead code from refclock_true.c. * CID 1189401: Use INSIST() instead of a belt-and-suspenders pointer check. * In ntp_dir_sep.m4, we care about $host_os, not $target_os. * [Bug 2170] Use AC_PREPROC_IFELSE instead of AC_EGREP_CPP. * [Bug 2540] bootstrap script needs to 'touch' files in finer-grained groups. * [Bug 2570] refuse to load leapsec file with bad/missing SHA1 hash -- change reading the hash line code: NIST omits leading zeros. * [Bug 2576] refclock_gpsdjson.c doesn't compile if CLOCK_GPSDJSON is not enabled at configure time. (4.2.7p428) 2014/03/03 Released by Harlan Stenn * [Bug 2570] refuse to load leapsec file with bad/missing SHA1 hash * [Bug 2562] Distribute the code in libjsmn/ . (4.2.7p427) 2014/03/02 Released by Harlan Stenn * [Bug 2562] GPSD_JSON: fix solaris issues (asprintf(), isfinite()) * [Bug 2562] first release of the GPSD client clock (type 46) (4.2.7p426) 2014/02/28 Released by Harlan Stenn * [Bug 2113] Warn about ignored extra args in ntpq. * [Bug 2540] bootstrap script needs to 'touch' files in finer-grained groups. * [Bug 2561] Allow wildcards in the target of the "interface" command. * [Bug 2572] cross-compiling fails for --with-yielding_select. (4.2.7p425) 2014/02/26 Released by Harlan Stenn * Copyright file update. (4.2.7p424) 2014/02/24 Released by Harlan Stenn * [Bug 2541] ntpd terminates itself with SIGHUP unexpectedly. (4.2.7p423) 2014/02/23 Released by Harlan Stenn * [Bug 2565] Handle EINTR on getifaddrs(). (4.2.7p422) 2014/02/17 Released by Harlan Stenn * [Bug 2536] ntpd sandboxing support (libseccomp2). (4.2.7p421) 2014/02/10 Released by Harlan Stenn * [Bug 898] More documentation fixes. * [Bug 2555] Autogen mdoc man pages all stamped with SunOS 5.10. * calc_tickadj/Makefile.am man/mdoc page build cleanup. (4.2.7p420) 2014/02/09 Released by Harlan Stenn * [Bug 492] Clearly document ntpdate's pending deprecation. * [Bug 1186] ntpd fails with link local IPv6 addresses. * [Sec 2542] Strengthen the mrulist nonce. (4.2.7p419) 2014/02/08 Released by Harlan Stenn * [Bug 2466] Wrap NMEA timestamps in 1024 week cycles. (4.2.7p418) 2014/02/05 Released by Harlan Stenn * [Bug 2551] --disable-local-libevent breaks the build. (4.2.7p417) 2014/02/02 Released by Harlan Stenn * [Bug 2539] doc and code tweaks for NMEA driver. * Add check for enable stats to ntpd/complete.conf.in * Fix typo in html/confopt.html (4.2.7p416) 2014/01/31 Released by Harlan Stenn * Tweak the 'Modified' line on appropriate html pages. * Note in the deprecation of ntpdc in its documentation. * [Bug 2332] Be more careful about when we use 'libgcc_s'. (4.2.7p415) 2014/01/28 Released by Harlan Stenn * Fix the man page installation for the scripts/ files. (4.2.7p414) 2014/01/28 Released by Harlan Stenn * [Bug 792] TrueTime TL-3 WWV refclock support. * [Bug 898] Documentation fixes. * [Bug 930] ntpdc docs refer to 'clockinfo', but mean 'clockstat'. * [Bug 1002] ntp-keygen option and documentation updates: -p/--pvt-passwd is now -p/--password, and -q/--get-pvt-passwd is now -q/--export-passwd. * [Bug 1349] statistics command not documented in HTML documentation. In html/monopt.html, add statistics id, definition, description, and correct typo. In html/scripts/monopt.txt, add statistics item, href, and comment. In ntpd/ntp.conf.def, under statistics correct four to eight kinds. In ntpd/complete.conf.in, add all eight kinds to statistics. In html/comdex.html, remove duplicate footer. * [Bug 1734] Include man page for ntp.conf (fixed in 4.2.7p297). * [Bug 2049] Clarify ntpdate's -d option behavior. * [Bug 2366] ntpdc.html: burst/iburst only work on servers. * [Bug 2493] ntptrace needs a man page (fixed in 4.2.7p402). * [Bug 2545] Cleanup of scripts/monitoring/ntptrap. (4.2.7p413) 2014/01/27 Released by Harlan Stenn * Require a version string for perl scripts that use autogen. * html/ cleanup. (4.2.7p412) 2014/01/20 Released by Harlan Stenn * [Bug 2540] bootstrap script needs to 'touch' files in finer-grained groups. (4.2.7p411) 2014/01/12 Released by Harlan Stenn * [Bug 2532] Note in ntpdc docs that "enable pps" only works on older ntpd. (4.2.7p410) 2014/01/08 Released by Harlan Stenn * [Bug 2332] Force reference to 'libgcc_s' when using GCC, because threading+restricted user+locked memory otherwise fails on Linux. * [Bug 2530] Fix documentation for enable/disable mode7 and pps. * Cleanup to the new scripts/*/Makefile.am files. (4.2.7p409) 2014/01/04 Released by Harlan Stenn * [Bug 2060] Warn about restrictions with "kod" but not "limited". (4.2.7p408) 2013/12/29 Released by Harlan Stenn * [Bug 2187] Update version number generation scripts. (4.2.7p407) 2013/12/29 Released by Harlan Stenn * [Bug 2519] mktime.c does not compile on 64-bit Solaris but we do not need timegm() and the Solaris provides mktime(). * [Bug 2522] Revert Bug 2513 fix - it breaks backward compatibility. (4.2.7p406) 2013/12/28 Released by Harlan Stenn * [Bug 2521] VPATH tweaks for perl -opts files. (4.2.7p405) 2013/12/27 Released by Harlan Stenn * [Bug 2521] bootstrap script needs a tweak for perl -opts files. * [Bug 2524] Add ntpsweep to sntp/loc/* files. * [Bug 2526] Add "noinst" support to the sntp/loc/ framework. (4.2.7p404) 2013/12/24 Released by Harlan Stenn * [Bug 135] AIX5: "Address already in use" for IPv6 wildcard. (4.2.7p403) 2013/12/23 Released by Harlan Stenn * [Bug 2513] Remove any PIDFILE in finish(). * [Bug 2516] Enable clock_gettime() support for AIX 5+. * [Bug 2517] Fix peer status errors in decode.html. (4.2.7p402) 2013/12/23 Released by Harlan Stenn * Incorporate Oliver Kindernay's GSoC 2013 scripts/ cleanup. (4.2.7p401) 2013/11/30 Released by Harlan Stenn * [Bug 2491] VS20xx compile fixes. (4.2.7p400) 2013/11/29 Released by Harlan Stenn * [Bug 2491] VS2013 project files. (4.2.7p399) 2013/11/28 Released by Harlan Stenn * [Bug 2326] More leapsecond file notification cleanup. * [Bug 2506] make sure routing updates are always tracked * [Bug 2514] secs/* #define usage cleanup. (4.2.7p398) 2013/11/25 Released by Harlan Stenn * [Bug 2326] More leapsecond file notification cleanup. * Improve sntp KoD data file fopen() error message. (4.2.7p397) 2013/11/20 Released by Harlan Stenn * [Bug 2326] More leapsecond file notification cleanup. (4.2.7p396) 2013/11/19 Released by Harlan Stenn * [Bug 2326] Improve stale leapsecond notifications. (4.2.7p395) 2013/11/12 Released by Harlan Stenn * Upgrade to autogen-5.18.3pre5 and libopts-40.1.15. (4.2.7p394) 2013/11/05 Released by Harlan Stenn * [Bug 1050] Change ONCORE log message for leap second announcement to avoid misunderstandings. * [Bug 2499] Win32 user-space/loopback ppsapi provider drops samples. * [Bug 2256] Improve configure's function searches in libraries. (4.2.7p393) 2013/10/16 Released by Harlan Stenn * [Bug 2272] Use C99 integer types. ntp_calendar.h and ntp_types.h . (4.2.7p392) 2013/10/15 Released by Harlan Stenn * [Bug 2375] Improve AIX compatibility. * [Bug 2490] Fixed non-const initializer coming from [Bug 2250] fix. (4.2.7p391) 2013/10/12 Released by Harlan Stenn * [Bug 2250] Rework of leap second handling machine. * [Bug 2419] [rc-nmea] Improve clockstats reporting when receiver sends data without valid GPS fix. (4.2.7p390) 2013/09/26 Released by Harlan Stenn * [Bug 2482] Cleanup of droproot and jail support for Solaris. (4.2.7p389) 2013/09/24 Released by Harlan Stenn * [Bug 2473] revisited: NTPD exits after clock is stepped backwards Avoid possible unsigned underrun for startup condition when testing for clock backstep. * [Bug 2481] ntpd aborts when both user and group are specified with -u. * [Bug 2482] Add droproot and jail support for Solaris. (4.2.7p388) 2013/09/19 Released by Harlan Stenn * [Bug 2473] NTPD exits after clock is stepped backwards externally (4.2.7p387) 2013/09/16 Released by Harlan Stenn * [Bug 1642] ntpdsim can't find simnulate block in config file. (4.2.7p386) 2013/09/01 Released by Harlan Stenn * [Bug 2472] (WinXP) Avoid self-termination of IO thread during exit(). (4.2.7p385) 2013/08/19 Released by Harlan Stenn * CID 975596: Copy/paste error: vallen should be siglen. * CID 1009579: Check return status of X509_add_ext(). * [2085] Fix root distance and root dispersion calculations. * [Bug 2426] Possibly uninitialized data in crypto_send() - CID 975596. (4.2.7p384) 2013/08/18 Released by Harlan Stenn * [Bug 2450] --version has bogus short option. (4.2.7p383) 2013/08/10 Released by Harlan Stenn * (no changes - force a rebuild for a new Coverity scan) (4.2.7p382) 2013/08/08 Released by Harlan Stenn * [Bug 2454] Need way to set file descriptor limit - cleanup. (4.2.7p381) 2013/08/07 Released by Harlan Stenn * [Bug 2451] rlimit command is missing from the table of contents in miscopt.html . * [Bug 2452] provide io_handler/input_handler only on non HAVE_IO_COMPLETION_PORT platforms * [Bug 2453] Need a way to avoid calling mlockall. * [Bug 2454] Need way to set file descriptor limit. * [Bug 2458] AM_CONFIG_HEADER is obsolete. (4.2.7p380) 2013/08/03 Released by Harlan Stenn * CID 984511: Some systems have different printf needs for sizeof. (4.2.7p379) 2013/08/02 Released by Harlan Stenn * CID 739724: Fix printf arg mismatch in a debug line. * [Bug 2425] compile io_handler() in ntp_io.c unconditionally * [Bug 2448] Fix checks for configure --with-stack-limit and --with-memlock values. (4.2.7p378) 2013/08/01 Released by Harlan Stenn * [Bug 2425] move part of input handler code from ntpd.c to ntp_io.c and fix select()-only platforms calling input_handler directly. * [Bug 2446] Quiet warnings from Oracle's Studio compiler. * Upgrade to AutoGen-5.18.1pre3 * Upgrade to libopts-40.1.15. (4.2.7p377) 2013/07/28 Released by Harlan Stenn * [Bug 2397] License/copyright cleanup. * [Bug 2439] Fix check of EscapeCommFunction() in ports/winnt/libntp/termios.c. (4.2.7p376) 2013/07/24 Released by Harlan Stenn * [Bug 2322] Oncore driver should send 0 PPS offset to GPS. (4.2.7p375) 2013/07/22 Released by Harlan Stenn * [Bug 883] log warning arguments swapped in refclock_gpsvme.c. * [Bug 2368] Correct bug in previous attempt. * [Bug 2413] Fix "make check" with automake >= 1.13. * [Bug 2434] Line-buffer (v. block-buffer) stdout. (4.2.7p374) 2013/07/21 Released by Harlan Stenn * [Bug 2368] make check troubles in libevent. * [Bug 2425] setup SIGIO/SIGPOLL for asyncio on the read side of a socketpair for the worker thread. (4.2.7p373) 2013/07/20 Released by Harlan Stenn * [Bug 2427] configure fails to detect recvmsg() on Solaris. (4.2.7p372) 2013/07/17 Released by Harlan Stenn * [Bug 1466] Oncore should set FLAG_PPS. * [Bug 2375] AIX 7 doesn't like a libevent validation check. * [Bug 2423] Log command-line args at LOG_INFO. * [Bug 2428] do_unconf() should reset 'items' before the 2nd loop. (4.2.7p371) 2013/07/07 Released by Harlan Stenn * CID 1042586: Check the return value of clock_gettime() in worker_sleep(). * Upgrade to libopts-39.0.14 from 5.17.5pre10. (4.2.7p370) 2013/07/06 Released by Harlan Stenn * Remove \n's from syslog output strings. (4.2.7p369) 2013/07/05 Released by Harlan Stenn * [Bug 2415] RES_LIMITED flags check should use &, not &&. * Have NTP_LIBNTP check for time.h and clock_getres(). * Fix ntpsweep to use sntp instead of ntpdate, from Oliver Kindernay. (4.2.7p368) 2013/05/01 Released by Harlan Stenn * [Bug 2145] ntpq dumps core when displaying sys_var_list and more. (4.2.7p367) 2013/04/25 Released by Harlan Stenn * [Bug 1485] Sometimes ntpd crashes * [Bug 2382] Implement LOGTOD using ldexp() instead of shifting. (4.2.7p366) 2013/04/17 Released by Harlan Stenn * [Bug 1866] Disable some debugging output in refclock_oncore. (4.2.7p365) 2013/04/16 Released by Harlan Stenn * [Bug 2149] Log an error message if /proc/net/if_inet6 cannot be opened. (4.2.7p364) 2013/03/26 Released by Harlan Stenn * Bump sntp/include/autogen-version.def . (4.2.7p363) 2013/03/26 Released by Harlan Stenn * [Bug 2357] sntp/libopts/usage.c sometimes needs -lintl. * Upgrade to libopts from 5.17.3pre10. (4.2.7p362) 2013/03/19 Released by Harlan Stenn * [Bug 2364] "sed -i" is not portable. (4.2.7p361) 2013/03/17 Released by Harlan Stenn * [Bug 2357] sntp/libopts/usage.c sometimes needs -lintl. * [Bug 2365] "make check" fails in libevent. (4.2.7p360) 2013/03/15 Released by Harlan Stenn * Upgrade libevent (coverity fixes, etc.). * EEXIST is OK for mkdir() in sntp/kod_management.c. (4.2.7p359) 2013/03/03 Released by Harlan Stenn * [Bug 2359] Fix send_via_ntp_signd() prototype. (4.2.7p358) 2013/02/27 Released by Harlan Stenn * Upgrade to autogen-5.17.3pre4 and libopts-38.0.13. * [Bug 2357] sntp/libopts/usage.c on NetBSD needs -lintl. (4.2.7p357) 2013/02/22 Released by Harlan Stenn * Upgrade to autogen-5.17.2pre and libopts-38.0.13. (4.2.7p356) 2013/02/19 Released by Harlan Stenn * Added loc/debian. (4.2.7p355) 2013/02/18 Released by Harlan Stenn * CID 739708: Check return status of fcntl() in refclock_arc.c. * CID 739709: Check return status of fcntl() in refclock_datum.c. * CID 739710: Check return status of mkdir() in sntp/kod_management.c. * CID 739711: Ignore return status of remove() in ntp-keygen.c. * CID 739723: Print sizeof as unsigned. * CID 971094: Clean up time of check/time of use in check_leap_file(). (4.2.7p354) 2013/02/10 Released by Harlan Stenn * CID 97194: Check return from setsockopt(). * CID 739473,739532: Out-of-bounds access/illegal address computation. * CID 739558: Double close. * CID 739559: Double close. * CID 739713: devmask/recmask copy/paste error. * CID 739714: Fix code indentation level. * CID 739715: Clean up sockaddr_dump(). (4.2.7p353) 2013/02/09 Released by Harlan Stenn * [Bug 2326] Check hourly for a new leapfile if the old one expired. (4.2.7p352) 2013/01/28 Released by Harlan Stenn * [Bug 2326] Notice when a new leapfile has been installed. (4.2.7p351) 2013/01/24 Released by Harlan Stenn * [Bug 2328] Don't apply small time adjustments on Windows versions which don't support this. (4.2.7p350) 2013/01/21 Released by Harlan Stenn * Added sntp/loc/netbsd based on info from Christos Zoulas. (4.2.7p349) 2013/01/20 Released by Harlan Stenn * [Bug 2321] Fixed Windows build, but autogen update still required. (4.2.7p348) 2013/01/17 Released by Harlan Stenn * [Bug 2327] Rename sntp/ag-tpl/:Old to sntp/ag-tpl/Old. * Cleanup to ntpsnmpd-opts.def. * Cleanup to ntpq.texi. * Documentation cleanup to the ntpd, ntpdc, ntpq and ntp-wait .def files. * In ntp.conf.def, cleanup SEE ALSO, document 'rlimit' options. * Add a reference to RFC5907 in the ntpsnmpd documentation. (4.2.7p347) 2013/01/07 Released by Harlan Stenn * [Bug 2325] Re-enable mlockall() check under Linux post-1223 fix. (4.2.7p346) 2013/01/06 Released by Harlan Stenn * [Bug 1223] reorganize inclusion of sys/resource.h. (4.2.7p345) 2013/01/04 Released by Harlan Stenn * Update several .def files to use autogen-5.17 feature set. (4.2.7p344) 2013/01/03 Released by Harlan Stenn * Refactor and enhance mdoc2texi. * Make sure agtexi-file.tpl defines label-str. * Cleanup to ntp.conf.def. * Upgrade to autogen-5.17 and libopts-37.0.12. (4.2.7p343) 2013/01/02 Released by Harlan Stenn * Update the copyright year. (4.2.7p342) 2012/12/31 Released by Harlan Stenn * [Bug 2081 - Backward Incompatible] rawstats now logs everything. (4.2.7p341) 2012/12/30 Released by Harlan Stenn (4.2.7p340) 2012/12/29 Released by Harlan Stenn * mdoc2texi fixes: trailing punctuation. (4.2.7p339) 2012/12/26 Released by Harlan Stenn * mdoc2texi fixes: parseQuote, closing of list item tables. * ntp-wait, ntpd, ntpdc, ntpq, ntpsnmpd autogen documentation updates. (4.2.7p338) 2012/12/25 Released by Harlan Stenn * mdoc2texi fixes: Handle_ArCmFlIc, Handle_Fn, HandleQ. * ntp-keygen autogen documentation updates. * ntpq autogen docs. (4.2.7p337) 2012/12/22 Released by Harlan Stenn * [Bug 1223] More final cleanup for rlimit changes. (4.2.7p336) 2012/12/21 Released by Harlan Stenn * [Bug 1223] Final cleanup for rlimit changes. (4.2.7p335) 2012/12/18 Released by Harlan Stenn * Update documentation templates and definitions. * Create agtexi-file.tpl . (4.2.7p334) 2012/12/10 Released by Harlan Stenn * [Bug 2114] Update tests for sntp's synch distance. * Create ntp-keygen.{html,texi}. (4.2.7p333) 2012/12/07 Released by Harlan Stenn * Autogen documentation cleanup. (4.2.7p332) 2012/12/06 Released by Harlan Stenn * sntp documentation cleanup. (4.2.7p331) 2012/12/03 Released by Harlan Stenn * [Bug 2114] Correctly calculate sntp's synch distance. (4.2.7p330) 2012/12/03 Released by Harlan Stenn * autogen doc cleanup (4.2.7p329) 2012/12/01 Released by Harlan Stenn * [Bug 2278] ACTS flag3 mismatch between code and driver18.html. * Use an enum for the ACTS state table. * html doc reconciliation with DLM's copy. (4.2.7p328) 2012/11/30 Released by Harlan Stenn * html doc reconciliation with DLM's copy. (4.2.7p327) 2012/11/29 Released by Harlan Stenn * [Bug 2024] Identify Events in the system status word in decode.html.' * [Bug 2040] Provide a command-line option for the identity key bits. * Create loc/darwin for Mac OSX (4.2.7p326) 2012/11/21 Released by Harlan Stenn * [Bug 1214] 'proto: precision = ...' should be at INFO, not NOTICE. * [Bug 2246] Clear sys_leap when voting says to disarm the leap. (4.2.7p325) 2012/11/20 Released by Harlan Stenn * [Bug 2202] ntpq.html: there is no "acv" billboard. * [Bug 2306] keep pps hack for Win32 even if user-mode/loopback PPS API is activated on a serial line. (4.2.7p324) 2012/11/19 Released by Harlan Stenn * Reinstate doc fix to authentic.html from Mike T. * [Bug 1223] cleanup for rlimit changes. * [Bug 2098] Install DLM's HTML documentation. * [Bug 2306] Added user-mode/loop-back PPS API provider for Win32 (4.2.7p323) 2012/11/18 Released by Harlan Stenn * html/ updates from Dave Mills. (4.2.7p322) 2012/11/15 Released by Harlan Stenn * [Bug 1223] Allow configurable values for RLIMIT_STACK and RLIMIT_MEMLOCK. * [Bug 1320] Log ntpd's initial command-line parameters. (updated fix) * [Bug 2120] no sysexits.h under QNX. * [Bug 2123] cleanup to html/leap.html. (4.2.7p321) 2012/11/13 Released by Harlan Stenn * [Bug 1320] Log ntpd's initial command-line parameters. (4.2.7p320) 2012/11/12 Released by Harlan Stenn * [Bug 969] Clarify ntpdate.html documentation about -u and ntpd. * [Bug 1217] libisc/ifiter_sysctl.c:internal_current(): Ignore RTM messages with wrong version (4.2.7p319) 2012/11/11 Released by Harlan Stenn * [Bug 2296] Fix compile problem with building with old OpenSSL. (4.2.7p318) 2012/11/05 Released by Harlan Stenn * [Bug 2301] Remove spurious debug output from ntpq. (4.2.7p317) 2012/11/05 Released by Harlan Stenn * [Bug 922] Allow interspersed -4 and -6 flags on the ntpq command line. (4.2.7p316) 2012/10/27 Released by Harlan Stenn * [Bug 2296] Update fix for Bug 2294 to handle --without-crypto. (4.2.7p315) 2012/10/26 Released by Harlan Stenn * [Bug 2294] ntpd crashes in FIPS mode. (4.2.7p314) 2012/10/23 Released by Harlan Stenn * Document a tricky malloc() of dns_ctx in sntp. (4.2.7p313) 2012/10/23 Released by Harlan Stenn * [Bug 2291] sntp should report why it cannot open file.kod. * [Bug 2293] add support for SO_BINTIME, refine support for SO_TIMESTAMPNS (bug 1374) (4.2.7p312) 2012/10/11 Released by Harlan Stenn * Clean up testing/debugging of fix for [Bug 938] from sntp/main.c . (4.2.7p311) 2012/10/10 Released by Harlan Stenn * [Bug 938] The argument to the -D flag takes a number, not a string. * [Bug 1013] ntpdate's HTML page claims wrong default version. * [Bug 1374] Support SO_TIMESTAMPNS. (4.2.7p310) 2012/10/09 Released by Harlan Stenn * [Bug 1374] Support SO_TIMESTAMPNS. * [Bug 2266] Remove deprecated refclock_trak.c from Windows Makefile equivalents. * [Bug 2274] Bring libopts/enum.c back to (old) ANSI C compliance. (4.2.7p309) 2012/10/04 Released by Harlan Stenn * [Bug 2287] ntpdate returns 0 even if adjtime() call fails. (4.2.7p308) 2012/09/29 Released by Harlan Stenn * CID 97198: Check return from ioctl() calls in refclock_acts.c. (4.2.7p307) 2012/09/29 Released by Harlan Stenn * [Bug 1997] Fix sntp broadcast timeouts. * [Bug 2234] Fix incorrect ntptrace html documentation. * [Bug 2262] Install html docs in $htmldir. * Fix typo in html/select.html. (4.2.7p306) 2012/09/15 Released by Harlan Stenn * [Bug 752] ToS cleanup from Mike Tatarinov. (4.2.7p305) 2012/09/15 Released by Harlan Stenn * [Bug 752] Use proper ToS network packet markings for IPv4 and IPv6. * [Bug 1232] Convert SHM refclock to use struct timespec. * [Bug 2258] Add syslog message about leap insertion. * [Bug 2263] broadcast server doesn't work for host with OS_MISSES_SPECIFIC_ROUTE_UPDATES. * [Bug 2271] Decode refclock types when built with --disable-all-clocks. * [Bug 2276] clk_sel240x.c #define's _XOPEN_SOURCE, breaking QNX6. * Updates to driver28.html. (4.2.7p304) 2012/09/06 Released by Harlan Stenn * [Bug 2264] Cleanup SEL240X Refclock. * In refclock_wwv.c rename SECOND to WWV_SEC and MINUTE to WWV_MIN. (4.2.7p303) 2012/09/05 Released by Harlan Stenn * [Bug 1232] Add nanosecond support to SHM driver. (4.2.7p302) 2012/09/05 Released by Harlan Stenn * [Bug 2160] Log warning about expired leapseconds file. (4.2.7p301) 2012/09/03 Released by Harlan Stenn * [Bug 2164] Greater precision needed for ntpq offset report. * Clean the man5_MANS in ntpd/ . (4.2.7p300) 2012/09/03 Released by Harlan Stenn * [Bug 2262] Install sntp.html into htmldir. * [Bug 2270] Install fails due to repeated man5 page names. (4.2.7p299) 2012/09/01 Released by Harlan Stenn * More cleanup to the bootstrap script. (4.2.7p298) 2012/09/01 Released by Harlan Stenn * Handle additional man page sections in the bootstrap script. * Remove extraneous parens. * Add a missing "%s" syslog format string. (4.2.7p297) 2012/09/01 Released by Harlan Stenn * Fix mdoc2man. * Distribute ntp.conf.def and ntp.keys.def. (4.2.7p296) 2012/08/31 Released by Harlan Stenn * Begin support for autogen maintaining ntp.conf and ntp.keys docs. * Upgrade to autogen-5.16.2 and libopts-36.5.11. * Potential bugfix for agtexi-cmd.tpl. (4.2.7p295) 2012/08/11 Released by Harlan Stenn * Look for syslog's facilitynames[]. (4.2.7p294) 2012/08/08 Released by Harlan Stenn * [Bug 2242] configure fails to detect getifaddrs function on Solaris. * [Bug 2249] Bad operator for 'test' in 'make check' of libevent. * [Bug 2252] palisade: formats nanosecs to a 6-char field. * Attempt to resolve strict-aliasing violation in refclock_tsyncpci.c. * Fix && -> & typo in refclock_palisade.c debug statements. (4.2.7p293) 2012/08/04 Released by Harlan Stenn * [Bug 2247] (more) Get rid of the TRAK refclock - deprecated since 2006. * Documentation cleanup from Mike T. * Cleanup kclk_sel240x.o rules in libparse/Makefile.am. (4.2.7p292) 2012/08/02 Released by Harlan Stenn * [Bug 1545] Note why we are logging the Version string. * [Bug 1872] Remove legacy ppsclock fdpps, #ifdef PPS. * [Bug 2075] Fix spelling of 'incompatible'. * [Bug 2247] Get rid of the TRAK refclock - deprecated since 2006. * Clean up an exit status in ntpq.c. (4.2.7p291) 2012/07/31 Released by Harlan Stenn * [Bug 2241] MDNS registration should only happen if requested. (4.2.7p290) 2012/07/20 Released by Harlan Stenn * [Bug 1454] Add parse clock support for the SEL-240x GPS products. * CID 709185: refclock_chu.c will leak fd==0 (better fix) (4.2.7p289) 2012/07/16 Released by Harlan Stenn * CID 97123: Future-proof possible change to refclock_nmea.c. * CID 97377: ntp-keygen.c's followlink() might not NUL-terminate. * CID 709185: refclock_chu.c will leak fd==0 (which should be impossible). (4.2.7p288) 2012/07/03 Released by Harlan Stenn * CID 709173: Make sure a libisc function we do not use is called properly. (4.2.7p287) 2012/07/03 Released by Harlan Stenn * Remove 1024 associations-per-server limit from ntpq. * Remove blank line between ntpq mreadvar associations. (4.2.7p286) 2012/06/28 Released by Harlan Stenn * CID 97193: check return from sscanf() in ntp_config.c. * CID 709169: check return from open("/dev/null", 0) and friends. * CID 709207: Initialize "quality" for ulink_receive. (4.2.7p285) 2012/06/18 Released by Harlan Stenn * [Bug 2227] Enable mrulist access control via "restrict ... nomrulist". * Automake-1.12 wants us to use AM_PROG_AR. * Conditionalize msyslog messages about rejected mode 6 requests due to nomodify and nomrulist restrictions under "logconfig +sysinfo". * Increment sys_restricted in a few rejection paths due to nomodify restrictions where previosuly overlooked. (4.2.7p284) 2012/06/16 Released by Harlan Stenn * [Bug 2225] libevent configure hangs. * Update bundled libevent to git master, post libevent 2.1.1-alpha. (4.2.7p283) 2012/06/16 Released by Harlan Stenn * In sntp/m4/ntp_openssl.m4, Support multiple package names for the crypto library. Add legacy support for -Wl,-rpath. (4.2.7p282) 2012/06/15 Released by Harlan Stenn * tickadj may need to be linked with PTHREAD_LIBS. (4.2.7p281) 2012/06/14 Released by Harlan Stenn * U_INT32_MAX cleanup in include/ntp_types.h . * When linking, ntp_keygen and tickadj need $(LIBM). (4.2.7p280) 2012/06/13 Released by Harlan Stenn * [Bug 2224] Use-after-free in routing socket code after dropping root. (4.2.7p279) 2012/06/10 Released by Harlan Stenn * [Bug 2211] findbcastinter(): possibly undefined variable iface used. * [Bug 2220] Incorrect check for maximum association id in ntpq. (4.2.7p278) 2012/06/03 Released by Harlan Stenn * [Bug 2204] Build with --enable-getifaddrs=glibc fails. * [Bug 2178] refclock_tsyncpci.c reach register fails to shift. * [Bug 2191] dcfd -Y y2kcheck on CentOS 6.2 x86_64 breaks make check. (4.2.7p277) 2012/05/25 Released by Harlan Stenn * [Bug 2193] Building timestruct tests with Clang 3.1 fails. (4.2.7p276) 2012/05/15 Released by Harlan Stenn * [Bug 2179] Remove sntp/header.h. (4.2.7p275) 2012/04/28 Released by Harlan Stenn * [Bug 1744] Remove obsolete ntpdate/ntptime* items. (4.2.7p274) 2012/04/25 Released by Harlan Stenn * [Bug 2174] ntpd rejects source UDP ports less than 123 as bogus. (4.2.7p273) 2012/04/19 Released by Harlan Stenn * [Bug 2141] handle_sigio() calls get_systime(), which must be reentrant when SIGIO is used. Sanity checks relative to the prior get_systime() are disabled in ntpd on systems with signaled I/O, but active in sntp and ntpdate. * Correct authnumfreekeys accounting broken in 4.2.7p262. (4.2.7p272) 2012/04/14 Released by Harlan Stenn * LCRYPTO is gone - replace with VER_SUFFIX. * Change the link order for ntpsntpd. * Remove extra 'nlist' check from configure.ac. (4.2.7p271) 2012/04/11 Released by Harlan Stenn * [Bug 1122] openssl detection via pkg-config fails when no additional -Idir flags are needed. * Avoid overwriting user variable LDFLAGS with OpenSSL flags, instead they are added to LDFLAGS_NTP. (4.2.7p270) 2012/03/26 Released by Harlan Stenn * Update driver45.html page. (4.2.7p269) 2012/03/25 Released by Harlan Stenn * Clean up configure.ac. * Cleanup configure.ac's TSYNC PCI section. (4.2.7p268) 2012/03/24 Released by Harlan Stenn * Update driver45.html page. (4.2.7p267) 2012/03/23 Released by Harlan Stenn * Initial cut at a basic driver45.html page. (4.2.7p266) 2012/03/21 Released by Harlan Stenn * Add refclock_tsyncpci.c (driver 45) supporting Spectracom TSYNC timing boards. (4.2.7p265) 2012/03/20 Released by Harlan Stenn * Treat zero counter as indication of precise system time in Windows PPSAPI helper function pps_ntp_timestamp_from_counter(), enabling PPSAPI providers to use the Windows 8 precise clock directly. (4.2.7p264) 2012/03/14 Released by Harlan Stenn * [Bug 2160] Note if leapseconds file is past its prime. * Use GetSystemTimePreciseAsFileTime() on Windows 8. (4.2.7p263) 2012/03/13 Released by Harlan Stenn * [Bug 2156] clock instability with LOCAL driver, from Miroslav Lichvar. * [Bug 2159] Windows ntpd using leapfile erroneous leap second 20120401. (4.2.7p262) 2012/02/29 Released by Harlan Stenn * Improve ntpd scalability for servers with many trusted keys. (4.2.7p261) 2012/02/27 Released by Harlan Stenn * [Bug 2048] add the clock variable timecode to SHM refclock. (4.2.7p260) 2012/02/24 Released by Harlan Stenn * Fix the check-scm-rev invocation in several Makefile.am's. (4.2.7p259) 2012/02/22 Released by Harlan Stenn * [Bug 2148] ntpd 4.2.7p258 segfault with 0x0100000 bit in NMEA mode. * refclock_nmea.c merge cleanup thanks to Juergen Perlinger. (4.2.7p258) 2012/02/21 Released by Harlan Stenn * [Bug 2140] Rework of Windows I/O completion port handling to avoid garbling serial input in UNIX line discipline emulation. * [Bug 2143] NMEA driver: discard data if quality indication not good, add statistic counters (mode bit enabled) to clockstats file. (4.2.7p257) 2012/02/17 Released by Harlan Stenn * [Bug 2135] defer calls to 'io_input' to main thread under Windows. (4.2.7p256) 2012/02/08 Released by Harlan Stenn * [Bug 2131] Set the system variable settimeofday only after clock step. * [Bug 2134] --enable-C99-snprintf does not force rpl_snprintf use. (4.2.7p255) 2012/01/29 Released by Harlan Stenn * [Bug 603] Only link with nlist()-related libraries when needed: More cleanup. (4.2.7p254) 2012/01/29 Released by Harlan Stenn * [Bug 603] Only link with nlist()-related libraries when needed. (4.2.7p253) 2012/01/26 Released by Harlan Stenn * [Bug 2126] Compile error on Windows with libopts from Autogen 5.14. * Update one of the license URLs. (4.2.7p252) 2012/01/25 Released by Harlan Stenn * Upgrade to autogen-5.14 (and libopts-36.1.11). (4.2.7p251) 2012/01/17 Released by Harlan Stenn * [Bug 2115] ntptrace should accept both rootdispersion and rootdisp. (4.2.7p250) 2012/01/15 Released by Harlan Stenn * [Bug 2113] Warn about ignored extra args in ntpq. * Update the copyright year. (4.2.7p249) 2012/01/10 Released by Harlan Stenn * [Bug 2111] Remove minpoll delay before iburst for pool and manycastclient. * Move refclock-specific scheduled timer code under #ifdef REFCLOCK and move "action" and "nextaction" data for same from struct peer to struct refclockproc. These provide a way to schedule a callback some seconds in the future. (4.2.7p248) 2012/01/08 Released by Harlan Stenn * [Bug 2109] "make clean check" is broken with gtest available. * [Bug 2110] systime.c typo breaks build on microsecond clocks. (4.2.7p247) 2012/01/07 Released by Harlan Stenn * Fix build break triggered by updating deps-ver and libntp/systime.c at the same time by explicitly depending systime_s.c on systime.c. (4.2.7p246) 2012/01/06 Released by Harlan Stenn * [Bug 2104] ntpdc fault with oversize -c command. * [Bug 2106] Fix warnings when using -Wformat-security. * Refactor timespecops.h and timevalops.h into inline functions. (4.2.7p245) 2011/12/31 Released by Harlan Stenn * [Bug 2100] conversion problem with timespec/timeval <--> l_fp fixed; added tests to expose the bug. (4.2.7p244) 2011/12/25 Released by Harlan Stenn * Updates from 4.2.6p5. (4.2.7p243) 2011/12/23 Released by Harlan Stenn * [Bug 2095] ntptrace now needs 'rv' instead of 'pstat', reported by Michael Tatarinov. (4.2.7p242) 2011/12/21 Released by Harlan Stenn * Include missing html/icons/sitemap.png, reported by Michael Tatarinov. * Documentation updates from Dave Mills. (4.2.7p241) 2011/12/18 Released by Harlan Stenn * [Bug 2015] Overriding sys_tick should recalculate sys_precision. * [Bug 2037] Fuzzed non-interpolated clock may decrease. * [Bug 2068] "tos ceiling" default and cap changed to 15. * Floor peer delay using system precision, as with jitter, reflecting inability to measure shorter intervals. (4.2.7p240) 2011/12/15 Released by Harlan Stenn * [Bug 2092] clock_select() selection jitter miscalculated. * [Bug 2093] Reintroduce smaller stratum factor to system peer metric. (4.2.7p239) 2011/12/11 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p238) 2011/12/09 Released by Harlan Stenn * [Bug 2082] from 4.2.6p5-RC3: 3-char refid sent by ntpd 4.2.6p5-RC2 ends with extra dot. * [Bug 2085] from 4.2.6p5-RC3: clock_update() sys_rootdisp calculation omits root delay. * [Bug 2086] from 4.2.6p5-RC3: get_systime() should not offset by sys_residual. * [Bug 2087] from 4.2.6p5-RC3: sys_jitter calculation overweights sys.peer jitter. * from 4.2.6p5-RC3: Ensure NULL peer->dstadr is not accessed in orphan parent selection. (4.2.7p237) 2011/12/01 Released by Harlan Stenn * [Bug 2050] from 4.2.6p5-RC2: Orphan mode stratum counting to infinity. * [Bug 2059] from 4.2.6p5-RC2: optional billboard column "server" does not honor -n. * [Bug 2066] from 4.2.6p5-RC2: ntpq lopeers ipv6 "local" column overrun. * [Bug 2068] from 4.2.6p5-RC2: ntpd sends nonprintable stratum 16 refid to ntpq. * [Bug 2069] from 4.2.6p5-RC2: broadcastclient, multicastclient spin up duplicate ephemeral associations without broadcastdelay. * [Bug 2072] from 4.2.6p5-RC2: Orphan parent selection metric needs ntohl(). * [Bug 2073] Correct ntpq billboard's MODE_PASSIVE t from 'u' to 'S'. * from 4.2.6p5-RC2: Exclude not-yet-determined sys_refid from use in loopback TEST12 (from Dave Mills). * from 4.2.6p5-RC2: Never send KoD rate limiting response to MODE_SERVER. * Floor calculation of sys_rootdisp at sys_mindisp in clock_update (from Dave Mills). * Restore 4.2.6 clock_combine() weighting to ntp-dev, reverting to pre- 4.2.7p70 method while also avoiding divide-by-zero (from Dave Mills). * Round l_fp traffic interval when converting to integer in rate limit and KoD calculation. (4.2.7p236) 2011/11/16 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p235) 2011/11/16 Released by Harlan Stenn * [Bug 2052] Autokey CRYPTO_ASSOC host@group vallen needs checking. (4.2.7p234) 2011/11/07 Released by Harlan Stenn * Clean up -libm entries regarding libntp.a (4.2.7p233) 2011/11/06 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p232) 2011/11/05 Released by Harlan Stenn * Update the NEWS file so we note the default disable of mode 7 requests. * Clean up some bitrotted code in libntp/socket.c. (4.2.7p231) 2011/11/03 Released by Harlan Stenn * [Bug 1940] ignore auth key if hex decoding fails. * Add ntpq reslist command to query access restrictions, similar to ntpdc's reslist. (4.2.7p230) 2011/11/01 Released by Harlan Stenn * Disable mode 7 (ntpdc) query processing in ntpd by default. ntpq is believed to provide all functionality ntpdc did, and uses a less- fragile protocol that's safer and easier to maintain. If you do find some management via ntpdc is needed, you can use "enable mode7" in the ntpd configuration. * Directly limit the number of datagrams in a mrulist response, rather than limiting the number of entries returned to indirectly limit the datagram count. * Documentation updates from Dave Mills. (4.2.7p229) 2011/10/26 Released by Harlan Stenn * [Bug 1995] fix wrong use of ZERO() macro in 'ntp_calendar.c' (4.2.7p228) 2011/10/23 Released by Harlan Stenn * [Bug 1995] add compile time stamp based era unfolding for 'step_systime()' and necessary support to 'ntp-calendar.c'. (4.2.7p227) 2011/10/22 Released by Harlan Stenn * [Bug 2036] gcc 2.95.3 preprocessor can't nest #ifdef in macro args. * A number of compiler warnings eliminated. (4.2.7p226) 2011/10/21 Released by Harlan Stenn * [Bug 2035] ntpq -c mrulist sleeps 1 sec between queries, not 5 msec. * Documentation updates from Dave Mills. (4.2.7p225) 2011/10/15 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p224) 2011/10/14 Released by Harlan Stenn * ntpq mrulist shows intermediate counts every five seconds while retrieving list, and allows Ctrl-C interruption of the retrieval, showing the incomplete list as retrieved. Reduce delay between successive mrulist retrieval queries from 30 to 5 msec. Do not give up mrulist retrieval when a single query times out. (4.2.7p223) 2011/10/12 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p222) 2011/10/11 Released by Harlan Stenn * [Bug 2029] "make check" clutters syslog. * Log signal description along with number on ntpd exit. (4.2.7p221) 2011/10/10 Released by Harlan Stenn * [Bug 2025] Switching between daemon and kernel loops can doubly- correct drift * [Bug 2028] ntpd -n (nofork) redirects logging to stderr. * Documentation updates from Dave Mills. (4.2.7p220) 2011/10/05 Released by Harlan Stenn * [Bug 1945] mbg_gps166.h use of _TM_DEFINED conflicts with MS VC. * [Bug 1946] parse_start uses open; does not work on Windows. * [Bug 1947] Porting parse-based Wharton refclock driver to Windows. * [Bug 2024] Remove unused system event code EVNT_CLKHOP. (4.2.7p219) 2011/10/04 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p218) 2011/10/03 Released by Harlan Stenn * [Bug 2019] Allow selection of cipher for private key files. * Documentation updates from Dave Mills. * ntp-keygen private key cipher default now triple-key triple DES CBC. * ntp-keygen -M is intended to ignore all other defaults and options, so do not attempt to open existing Autokey host certificate before generating symmetric keys and terminating. * Restore IFF, MV, and GQ identity parameter filename convention to ntpkey_par_ in ntpd, matching ntp-keygen. * Change some error logging to syslog to ignore logconfig mask, such as reporting PPSAPI failure in NMEA and WWVB refclocks. * ntp-keygen on Windows XP and later systems will now create links expected by ntpd. They are hardlinks on Windows, soft on POSIX. * Conditionalize NMEA serial open message under clockevent. * Send all peer variables to trappers in report_event(). (4.2.7p217) 2011/09/29 Released by Harlan Stenn * [Bug 2020] ntp-keygen -s no longer sets host in cert file name. * [Backward Incompatible] ntp-keygen -i option long name changed from misleading --issuer-name to --ident. (4.2.7p216) 2011/09/27 Released by Harlan Stenn * sntp documentation tag cleanup. * mdoc2man improvements. (4.2.7p215) 2011/09/24 Released by Harlan Stenn * Use patched mdoc2man script, from Eric Feng. * Sync with ntp-4.2.6p4 (a no-op). (4.2.7p214) 2011/09/20 Released by Harlan Stenn * [Bug 1981] Initial offset convergence applies frequency correction 2x with kernel discipline. * [Bug 2008] Initial offset convergence degraded with 500 PPM adjtime(). * [Bug 2009] EVNT_NSET adj_systime() mishandled by Windows ntpd. (4.2.7p213) 2011/09/08 Released by Harlan Stenn * [Bug 1999] NMEA does not send PMOTG messages any more. (4.2.7p212) 2011/09/07 Released by Harlan Stenn * [Bug 2003] from 4.2.6p4-RC3: ntpq_read_assoc_peervars() broken. (4.2.7p211) 2011/09/01 Released by Harlan Stenn * Update libevent to git head (2.1 branch) as of 2.0.14-stable. (4.2.7p210) 2011/08/31 Released by Harlan Stenn * Require -D4 or higher for ntpd SIGALRM debug trace from [Bug 2000]. (4.2.7p209) 2011/08/27 Released by Harlan Stenn * [Bug 2000] ntpd worker threads must block signals expected in main thread. * [Bug 2001] add ntpq -c timerstats like ntpdc -c timerstats. * [Bug 2001] from 4.2.6p4-RC3: ntpdc timerstats reports overruns as handled. * Update sntp tests to track the change of root dispersion to synchronization distance. (4.2.7p208) 2011/08/24 Released by Harlan Stenn * Fix the CLOCK_MONOTONIC TRACE() message. (4.2.7p207) 2011/08/22 Released by Harlan Stenn * Restore the original CLOCK_MONOTONIC output format in sntp. * Cleanups for ntp-wait-opts.def and ntp.keys.def . (4.2.7p206) 2011/08/20 Released by Harlan Stenn * [Bug 1993] ntpd Windows port adj_systime() broken in 4.2.7p203. * sntp documentation and behavior improvements suggested by Steven Sommars. * Have sntp report synchronization distance instead of root dispersion. * Clean up ntp-wait-opts.def . (4.2.7p205) 2011/08/19 Released by Harlan Stenn * [Bug 1992] util/tg2 doesn't compile, needs libntp. (4.2.7p204) 2011/08/16 Released by Harlan Stenn * Added support for Garmin's $PGRMF sentence to NMEA driver * [Bug 1988] Better sntp send failed error message needed. * [Bug 1989] sntp manual page sometimes refers to SNTP as a program. * [Bug 1990] sntp output should include stratum. (4.2.7p203) 2011/08/13 Released by Harlan Stenn * [Bug 1986] Require Visual C++ 2005 or later compilers in Windows port. * Actually use long long for (u_)int64 by correcting spelling of SIZEOF_LONG_LONG in ntp_types.h. * Force .exe minimum Windows version to 0x0400 to allow NT4 in vs2005/*.vcproj files. * Fix make distcheck with --enable-libevent-regress problem with unwritable $srcdir. * Correct init_logging()'s def_syslogmask type to u_int32 following change of ntp_syslogmask from u_long to u_int32 in p202. (4.2.7p202) 2011/08/09 Released by Harlan Stenn * [Bug 1983] --without-sntp build breaks in sntp subdir. * [Bug 1984] from 4.2.6p4-RC3: ntp/libisc fails to compile on OS X 10.7. * [Bug 1985] from 4.2.6p4-RC3: "logconfig =allall" rejected. (4.2.7p201) 2011/08/05 Released by Harlan Stenn * sntp: change -h/--headspace to -g/--gap, and change the default gap from 10 to 50ms * [Backward Incompatible] from 4.2.6p4: sntp: -l/--filelog -> -l/--logfile, to be consistent with ntpd. * Documentation updates from Dave Mills. * From 4.2.6p4: libopts/file.c fix from Bruce Korb (arg-type=file). (4.2.7p200) 2011/08/04 Released by Harlan Stenn * Sync with 4.2.6p4-RC2. (4.2.7p199) 2011/07/29 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p198) 2011/07/28 Released by Harlan Stenn * remove old binsubdir stuff from SNTP, as NTP_LOCINFO does that now. (4.2.7p197) 2011/07/28 Released by Harlan Stenn * [Bug 1975] from 4.2.6p4-RC2: libntp/mktime.c won't work with 64-bit time_t * [Bug 1976] genLocInfo writes to srcdir break 'make distcheck'. * [Bug 1977] Fix flag/description mismatches in ntp-keygen-opts.def. * Do not force "legacy" when --with-locfile is not given, genLocInfo will find the correct default for the system. * Fix warnings in ntp_request.c ([Bug 1973] oversight) and sntp/main.c (CID 159, apparent overrun due to union, actually correct). * Update sntp/loc/solaris to conform to stock locations. (4.2.7p196) 2011/07/27 Released by Harlan Stenn * DEFAULT INSTALLATION DIRECTORY CHANGES ON SOME OSes: to get the old behavior, pass --with-locfile=legacy to 'configure' * [Bug 1972] from 4.2.6p4-RC2: checking for struct rtattr fails. * [Bug 1973] Widen reference clock mode from 8 to 32 bits. * Removed sntp/m4/ntp_bindir.m4 - no longer needed. * Move loc/ to sntp/loc/ . * Move scripts/cvo.sh to sntp/scripts/cvo.sh . * Move scripts/genLocInfo to sntp/scripts/genLocInfo . * Give NTP_LOCINFO an optional path-to argument. * Remove hacks to get NTP_LOCINFO-related data to sntp/ . * Move sntp/include/mansec2subst.sed to sntp/scripts/mansec2subst.sed . * If no "more specific" loc file is found for redhat* or fedora*, look for a loc/redhat file. * If no "more specific" loc file is found and uname says this is Linux, look for a loc/linux file. * Improve the help text: --with-locfile=XXX . * work around solaris /bin/sh issues for genLocInfo. (4.2.7p195) 2011/07/25 Released by Harlan Stenn * Added loc/redhat. (4.2.7p194) 2011/07/25 Released by Harlan Stenn * [Bug 1608] from 4.2.6p4-RC2: Parse Refclock driver should honor trusttime. * Add support for installing programs and scripts to libexec. * Added loc/solaris. (4.2.7p193) 2011/07/24 Released by Harlan Stenn * [Bug 1970] from 4.2.6p4-RC2: UNLINK_EXPR_SLIST() causes crash if list is empty. * Update libevent to 2.1 HEAD as of merge of 2.0.13-stable-dev. * Match addr_eqprefix() sizeof and memcpy destination to make it clear to static analysis that there is no buffer overrun (CID 402). (4.2.7p192) 2011/07/18 Released by Harlan Stenn * [Bug 1966] Broken FILES section for ntp.keys.def. (4.2.7p191) 2011/07/17 Released by Harlan Stenn * [Bug 1948] Update man page section layout. * [Bug 1963] add reset command for ntpq :config, similar to ntpdc's. * [Bug 1964] --without-sntp should not build sntp. (4.2.7p190) 2011/07/13 Released by Harlan Stenn * [Bug 1961] from 4.2.6p4: html2man update: distribute ntp-wait.html. * Require autogen-5.12. (4.2.7p189) 2011/07/11 Released by Harlan Stenn * [Bug 1134] from 4.2.6p4-RC1: ntpd fails binding to tentative IPv6 addresses. * [Bug 1790] from 4.2.6p4-RC1: Update config.guess and config.sub to detect AIX6. (4.2.7p188) 2011/06/28 Released by Harlan Stenn * [Bug 1958] genLocInfo must export PATH. * ntp-wait: some versions of ntpd spell "associd" differently. (4.2.7p187) 2011/06/24 Released by Harlan Stenn * [Bug 1954] Fix typos in [s]bin_PROGRAMS in ntpd/Makefile.am. * Implement --with-locfile=filename configure argument. If filename is empty we'll look under loc/ for a good fit. If the filename contains a / character, it will be treated as a "normal" pathname. Otherwise, that explicit file will be searched for under loc/ . (4.2.7p186) 2011/06/23 Released by Harlan Stenn * [Bug 1950] Control installation of event_rpcgen.py. * Update .point-changed-filelist for the new man pages. * Update the building of OS-specific programs. * Finish conversion to genLocInfo. * validate MANTAGFMT in genLocInfo. * Documentation update from Dave Mills. (4.2.7p185) 2011/06/21 Released by Harlan Stenn * ntp_locs.m4: handle the case where . is not in the PATH. * More genLocInfo cleanup. (4.2.7p184) 2011/06/20 Released by Harlan Stenn * Added ntp_locs.m4. * genLocInfo improvements. * Add the man page tag "flavor" to the loc.* files. * Add/distribute genLocInfo. (4.2.7p183) 2011/06/19 Released by Harlan Stenn * Update the autogen include list for scripts/Makefile.am. * Added loc.freebsd (and distribute it). * Added loc.legacy (and distribute it). (4.2.7p182) 2011/06/15 Released by Harlan Stenn * [Bug 1304] Update sntp.html to reflect new implementation. * Update .point-changed-filelist . * ntpdc documentation fixes. * Update ntp-wait autogen docs. * Update the ntpd autogen docs. * Update the ntpsnmpd autogen docs. * Use autogen to produce ntp-keygen docs. * Add "license name" to ntp.lic for autogen-5.11.10. * Prepare for ntp.keys.5. (4.2.7p181) 2011/06/07 Released by Harlan Stenn * [Bug 1938] addr_eqprefix() doesn't clear enough storage. (4.2.7p180) 2011/06/06 Released by Harlan Stenn * Upgrade to libevent-2.0.12. * More sntp.1 cleanups. * Produce ntpq.1 with the new autogen macros. * Remove the deprecated "detail" stanza from ntpdc-opts.def. (4.2.7p179) 2011/06/03 Released by Harlan Stenn * Update cmd-doc.tlib to autogen-5.11.10pre5. * Upgrade local autoopts templates to 5.11.10pre5. (4.2.7p178) 2011/06/02 Released by Harlan Stenn * Update the std_def_list to include the ntp.lic file. * Distribute the ntp.lic file. * Add http://ntp.org/license to the ntp.lic file. (4.2.7p177) 2011/06/01 Released by Harlan Stenn * Use the latest autogen's new copyright template code. * Clean up the ntp.lic file. (4.2.7p176) 2011/05/31 Released by Harlan Stenn * sntp documentation cleanup. * autogen documentation template cleanup. (4.2.7p175) 2011/05/30 Released by Harlan Stenn * [Bug 1936] Correctly set IPV6_MULTICAST_LOOP. * cmd-doc.tlib cleanup from Bruce Korb. * sntp documentation cleanup. (4.2.7p174) 2011/05/28 Released by Harlan Stenn * ntpdc documentation cleanup. * sntp documentation cleanup. * Don't build libevent with openssl support. Right now, libevent doesn't use pkg-config to find openssl's installation location. (4.2.7p173) 2011/05/25 Released by Harlan Stenn * Typo in emalloc.c hides file and line number from emalloc() error msg. * parsesolaris.c compile fails on SPARC Solaris with conflicting printf. * ntp_util.c compile fails on AIX and OSF with conflicting statsdir. (4.2.7p172) 2011/05/24 Released by Harlan Stenn * Remove hardcoded 1/960 s. fudge for transmission time at 9600 8n1 from WWVB/Spectracom driver introduced in 4.2.7p169. (4.2.7p171) 2011/05/23 Released by Harlan Stenn * Eliminate warnings about shadowing global "basename" on Linux. * Use filegen_config() consistently when changing filegen options. * mprintf() should go to stdout, not stderr. DPRINTF() uses mprintf(). * Repair a few simulator problems (more remain). * Documentation updates from Dave Mills. (4.2.7p170) 2011/05/19 Released by Harlan Stenn * [Bug 1932] libevent/util_internal.h builtin_expect compile error with gcc 2.95. * Use 64-bit scalars in LFPTOD() and DTOLFP() on more platforms by conditionalizing on HAVE_U_INT64 rather than UINT64_MAX. (4.2.7p169) 2011/05/18 Released by Harlan Stenn * [Bug 1933] WWVB/Spectracom driver timestamps LFs, not CRs. (4.2.7p168) 2011/05/16 Released by Harlan Stenn * Convert receive buffer queue from doubly-linked list to FIFO. (4.2.7p167) 2011/05/14 Released by Harlan Stenn * [Bug 1927] io_closeclock() should purge pending recvbufs. * [Bug 1931] cv always includes fudgetime1, never fudgetime2. * Use acts_close() in acts_shutdown() to avoid leaving a stale lockfile if unpeered via runtime configuration while the modem is open. * Correct acts_close() test of pp->io.fd to see if it is open. * 4.2.7p164 documentation updates re: 'tos orphanwait' expanded scope. (4.2.7p166) 2011/05/13 Released by Harlan Stenn * If we have local overrides for autogen template files, use them. * Convert more of the sntp-opt.def documentation from man to mdoc. (4.2.7p165) 2011/05/11 Released by Harlan Stenn * Convert snmp docs to mdoc format, which requires autogen 5.11.9. * from 4.2.6p4-RC1: Require autogen 5.11.9. (4.2.7p164) 2011/05/11 Released by Harlan Stenn * [Bug 988] Local clock eats up -g option, so ntpd stops with large initial time offset. * [Bug 1921] LOCAL, ACTS drivers with "prefer" excluded from initial candidate list. * [Bug 1922] "tos orphanwait" applied incorrectly at startup. * [Bug 1923] orphan parent favored over LOCAL, ACTS drivers. * [Bug 1924] Billboard tally codes sometimes do not match operation, variables. * Change "pool DNS" messages from msyslog to debug trace output. * Remove unused FLAG_SYSPEER from peer->status. * Respect "tos orphanwait" at startup. Previously there was an unconditional 300 s. startup orphanwait, though other values were respected for subsequent orphan wait periods after no_sys_peer events. * Apply "tos orphanwait" (def. 300 seconds) to LOCAL and ACTS reference clock drivers, in addition to orphan parent operation. LOCAL and ACTS are not selectable during the orphanwait delay at startup and after each no_sys_peer event. This prevents a particular form of clock- hopping, such as using LOCAL briefly at startup before remote peers are selectable. This fixes the issue reported in [Bug 988]. * Documentation updates from Dave Mills. (4.2.7p163) 2011/05/08 Released by Harlan Stenn * [Bug 1911] missing curly brace in libntp/ntp_rfc2553.c (4.2.7p162) 2011/05/03 Released by Harlan Stenn * [Bug 1910] Support the Tristate Ltd. TS-GPSclock-01. (4.2.7p161) 2011/05/02 Released by Harlan Stenn * [Bug 1904] 4.2.7p160 Windows build broken (POSIX_SHELL). * [Bug 1906] 4.2.7p160 - libtool: compile: cannot determine name of library object in ./libevent * Share a single sntp/libevent/build-aux directory between all three configure scripts. * Add missing --enable-local-libevent help to top-level configure. (4.2.7p160) 2011/05/01 Released by Harlan Stenn * from 4.2.6p4-RC1: Upgrade to libopts 35.0.10 from AutoGen 5.11.9pre8. * [Bug 1901] Simulator does not set progname. (4.2.7p159) 2011/04/28 Released by Harlan Stenn * Fix a couple of unused variable warnings. * cleanup in timespecops.c / timevalops.c (4.2.7p158) 2011/04/24 Released by Harlan Stenn * Update libevent --disable-libevent-regress handling to work when building libevent using mingw. (4.2.7p157) 2011/04/21 Released by Harlan Stenn * [Bug 1890] 4.2.7p156 segfault in duplicate freeaddrinfo(). (4.2.7p156) 2011/04/19 Released by Harlan Stenn * [Bug 1851] freeaddrinfo() called after getaddrinfo() fails. (4.2.7p155) 2011/04/18 Released by Harlan Stenn * Fix leak in refclock_datum.c start failure path. (4.2.7p154) 2011/04/17 Released by Harlan Stenn * [Bug 1887] DNS fails on 4.2.7p153 using threads. (4.2.7p153) 2011/04/16 Released by Harlan Stenn * A few more Coverity Scan cleanups. (4.2.7p152) 2011/04/15 Released by Harlan Stenn * Update embedded libevent to current 2.1 git HEAD. (4.2.7p151) 2011/04/14 Released by Harlan Stenn * Detect vsnprintf() support for "%m" and disable our "%m" expansion. * Add --enable-c99-sprintf to configure args for -noopenssl variety of flock-build to avoid regressions in (v)snprintf() replacement. * More msnprintf() unit tests. * Coverity Scan error checking fixes. * Log failure to fetch time from HOPF_P hardware. * Check HOPF_S sscanf() conversion count before converted values. (4.2.7p150) 2011/04/13 Released by Harlan Stenn * Remove never-used, incomplete ports/winnt/ntpd/refclock_trimbledc.[ch] * On systems without C99-compliant (v)snprintf(), use C99-snprintf replacements (http://www.jhweiss.de/software/snprintf.html) * Remove remaining sprintf() calls except refclock_ripencc.c (which is kept out of --enable-all-clocks as a result), upstream libs which use sprintf() only after careful buffer sizing. (4.2.7p149) 2011/04/11 Released by Harlan Stenn * [Bug 1881] describe the {+,-,s} characters in configure --help output. (4.2.7p148) 2011/04/09 Released by Harlan Stenn * Use _mkgmtime() as timegm() in the Windows port, rather than libntp/mktime.c's timegm(). Fixed [Bug 1875] on Windows using the old asn2ntp() code from before 4.2.7p147. * ntp_crypto.c string buffer safety. * Remove use of MAXFILENAME in mode 7 (ntpdc) on-wire structs. * Change ntpd MAXFILENAME from 128 to 256 to match ntp-keygen. * Buffer safety and sign extension fixes (thanks Coverity Scan). (4.2.7p147) 2011/04/07 Released by Harlan Stenn * [Bug 1875] 'asn2ntp()' rewritten with 'caltontp()'; 'timegm()' substitute likely to crash with 64bit time_t. (4.2.7p146) 2011/04/05 Released by Harlan Stenn * String buffer safety cleanup, converting to strlcpy() and strlcat(). * Use utmpname() before pututline() so repeated steps do not accidentally record into wtmp where utmp was intended. * Use setutent() before each pututline() including first. (4.2.7p145) 2011/04/04 Released by Harlan Stenn * [Bug 1840] ntp_lists.h FIFO macros buggy. (4.2.7p144) 2011/04/03 Released by Harlan Stenn * [Bug 1874] ntpq -c "rv 0 sys_var_list" empty. (4.2.7p143) 2011/03/31 Released by Harlan Stenn * [Bug 1732] ntpd ties up CPU on disconnected USB refclock. * [Bug 1861] tickadj build failure using uClibc. * [Bug 1862] in6addr_any test in configure fooled by arm gcc 4.1.3 -O2. * Remove kernel line discipline driver code for clk and chu, deprecate related LDISC_ flags, and remove associated ntpd code to decode the timestamps, remove clktest line discipline test program. * Remove "signal_no_reset: signal 17 had flags 4000000" logging, as it indicates no problem and is interpreted as an error. Previously some bits had been ignored one-by-one, but Linux SA_RESTORER definition is unavailable to user headers. (4.2.7p142) 2011/03/21 Released by Harlan Stenn * [Bug 1844] ntpd 4.2.7p131 NetBSD, --gc-sections links bad executable. * Fix "make distcheck" break in libevent/sample caused by typo. (4.2.7p141) 2011/03/20 Released by Harlan Stenn * Add "ntpq -c iostats" similar to "ntpdc -c iostats". * Compare entire timestamp to reject duplicates in refclock_pps(). (4.2.7p140) 2011/03/17 Released by Harlan Stenn * [Bug 1848] ntpd 4.2.7p139 --disable-thread-support does not compile. * Add --disable-thread-support to one flock-build variation. * One more lock-while-init in lib/isc/task.c to quiet lock analysis. (4.2.7p139) 2011/03/16 Released by Harlan Stenn * [Bug 1848] make check ntpd --saveconfigquit clutters syslog. (4.2.7p138) 2011/03/08 Released by Harlan Stenn * [Bug 1846] MacOSX: debug symbol not found by propdelay or tickadj. (4.2.7p137) 2011/03/07 Released by Harlan Stenn * Use TRACE() instead of DPRINTF() for libntp and utilities, which use the "debug" variable regardless of #ifdef DEBUG. * Declare debug in libntp instead of each program. Expose extern declaration to utilities, libntp, and DEBUG ntpd. * Lock under-construction task, taskmgr objects to satisfy Coverity's mostly-correct assumptions about which variables are protected by which locks. (4.2.7p136) 2011/03/02 Released by Harlan Stenn * [Bug 1839] 4.2.7p135 still installs libevent ev*.h headers. (4.2.7p135) 2011/03/02 Released by Harlan Stenn * libevent: When building on systems with CLOCK_MONOTONIC available, separate the internal timeline (possibly counting since system boot) from the gettimeofday() timeline in event_base cached timevals. Adds new event_base_tv_cached() to retrieve cached callback round start time on the internal timeline, and changes event_based_gettimeofday_cached() to always return times using the namesake timeline. This preserves the benefit of using the never- stepped monotonic clock for event timeouts while providing clients with times consistently using gettimeofday(). * Correct event_base_gettimeofday_cached() workaround code in sntp to work with corrected libevent. * Remove sntp l_fp_output() test now that it uses prettydate(). * [Bug 1839] 4.2.7p131 installs libevent ev*.h headers. * Ensure CONFIG_SHELL is not empty before relying on it for #! scripts. (4.2.7p134) 2011/02/24 Released by Harlan Stenn * [Bug 1837] Build fails on Win7 due to regedit requiring privilege. * Provide fallback definitions for GetAdaptersAddresses() for Windows build environments lacking iphlpapi.h. * Rename file containing 1.xxxx ChangeSet revision from version to scm-rev to avoid invoking GNU make implicit rules attempting to compile version.c into version. Problem was with sntp/version.o during make distcheck after fix for spurious sntp rebuilds. * Add INC_ALIGNED_PTR() macro to align pointers like malloc(). (4.2.7p133) 2011/02/23 Released by Harlan Stenn * [Bug 1834] ntpdate 4.2.7p131 aborts with assertion failure. * Move sntp last in top-level Makefile.am SUBDIRS so that the libevent tearoff (if required) and sntp are compiled after the rest. * Use a single set of Automake options for each package in configure.ac AM_INIT, remove Makefile.am AUTOMAKE_OPTIONS= lines. * Correct spurious sntp rebuilds triggered by a make misperception sntp/version was out-of-date relative to phony target FRC.version. * Do not cache paths to perl, test, or pkg-config, searching the PATH at configure time is worth it to pick up tool updates. (4.2.7p132) 2011/02/22 Released by Harlan Stenn * [Bug 1832] ntpdate doesn't allow timeout > 2s. * [Bug 1833] The checking sem_timedwait() fails without -pthread. * ElectricFence was suffering bitrot - remove it. valgrind works well. * Enable all relevant automake warnings. * Correct Solaris 2.1x PTHREAD_ONCE_INIT extra braces test to avoid triggering warnings due to excess braces. * Remove libevent-cfg from sntp/Makefile.am. * Provide bug report and URL options to Autoconf. * Avoid relying on remake rules for routine build/flock-build for libevent as for the top-level and sntp subproject. (4.2.7p131) 2011/02/21 Released by Harlan Stenn * [Bug 1087] -v/--normalverbose conflicts with -v/--version in sntp. * [Bug 1088] sntp should (only) report the time difference without -s/-a. * older autoconf sometimes dislikes []. * Move "can't write KoD file" warning from sntp shutdown to startup. * refclock_acts.c cleanup from Dave Mills. * Convert sntp to libevent event-driven socket programming. Instead of blocking name resolution and querying one NTP server at a time, resolve server names and send NTP queries without blocking. Add sntp command-line options to adjust timing and optionally wait for all servers to respond instead of exiting after the first. * Import libevent 2.0.10-stable plus local patches as a tearoff, used only if the target system lacks an installed libevent 2.0.9 or later. * Move blocking worker and resolver to libntp from ntpd. * Use threads rather than forked child processes for blocking worker when possible. Override with configure --disable-thread-support. * Move init_logging(), change_logfile(), and setup_logfile() from ntpd to libntp, use them in sntp. * Test --without-sntp in flock-build script's -no-refclocks variety. * Avoid invoking config.status twice in a row in build script. * Move more m4sh tests needed by libntp to shared .m4 files. * Split up ntp_libntp.m4 into smaller, more specific subsets. * Enable gcc -Wcast-align, fix many instances of warnings when casting a pointer to a more-strictly-aligned underlying type. (4.2.7p130) 2011/02/12 Released by Harlan Stenn * [Bug 1811] Update the download location in WHERE-TO-START. (4.2.7p129) 2011/02/09 Released by Harlan Stenn * Add missing "break;" to ntp_control.c ctl_putsys() for caliberrs, used by ntpq -c kerninfo introduced in 4.2.7p104. * Fix leak in ntp_control.c read_mru_list(). (4.2.7p128) 2011/01/30 Released by Harlan Stenn * [Bug 1799] ntpq mrv crash. * [Bug 1801] ntpq mreadvar requires prior association caching. (4.2.7p127) 2011/01/28 Released by Harlan Stenn * [Bug 1797] Restore stale timestamp check from the RANGEGATE cleanup. (4.2.7p126) 2011/01/27 Released by Harlan Stenn * Fix unexposed fencepost error in format_time_fraction(). * Add more unit tests for timeval_tostr() and timespec_tostr(). (4.2.7p125) 2011/01/26 Released by Harlan Stenn * [Bug 1794] ntpq -c rv missing clk_wander information. * [Bug 1795] ntpq readvar does not display last variable. (4.2.7p124) 2011/01/25 Released by Harlan Stenn * sntp/Makefile.am needs any passed-in CFLAGS. (4.2.7p123) 2011/01/24 Released by Harlan Stenn * [Bug 1788] tvtots.c tables inaccurate (4.2.7p122) 2011/01/22 Released by Harlan Stenn * ACTS refclock cleanup from Dave Mills. * Avoid shadowing the "group" global variable. (4.2.7p121) 2011/01/21 Released by Harlan Stenn * [Bug 1786] Remove extra semicolon from ntp_proto.c . (4.2.7p120) 2011/01/20 Released by Harlan Stenn * Change new timeval and timespec to string routines to use snprintf() rather than hand-crafted conversion, avoid signed int overflow there. * Add configure support for SIZEOF_LONG_LONG to enable portable use of snprintf() with time_t. * Grow ntpd/work_thread.c arrays as needed. * Add DEBUG_* variants of ntp_assert.h macros which compile away using ./configure --disable-debugging. * Fix tvalops.cpp unit test failures for 32-bit builds. * Return to a single autoreconf invocation in ./bootstrap script. * Fix warnings seen on FreeBSD 9. * crypto group changes from Dave Mills. * Lose the RANGEGATE check in PPS, from Dave Mills. * ACTS refclock cleanup from Dave Mills. * Documentation updates from Dave Mills. * NMEA driver documentation update from Juergen Perlinger. (4.2.7p119) 2011/01/18 Released by Harlan Stenn * added timespecops.{c,h} and tievalops.{c.h} to libntp and include added tspecops.cpp to tests/libntp * Correct msyslog.c build break on Solaris 2.9 from #ifdef/#if mixup. (4.2.7p118) 2011/01/15 Released by Harlan Stenn * Simplify the built-sources stuff in sntp/ . * Fix check for -lipv6 on HP-UX 11. (4.2.7p117) 2011/01/13 Released by Harlan Stenn * Add configure --without-sntp option to disable building sntp and sntp/tests. withsntp=no in the environment changes the default. * Build infrastructure cleanup: Move m4 directory to sntp/m4. Share a single set of genver output between sntp and the top level. Share a single set of autogen included .defs in sntp/include. Share a single set of build-aux scripts (e.g. config.guess, missing). Add ntp_libntp.m4 and ntp_ipv6.m4 to reduce configure.ac duplication. Warn and exit build/flock-build if bootstrap needs to be run. (4.2.7p116) 2011/01/10 Released by Harlan Stenn * refclock_nmea.c refactoring by Juergen Perlinger. (4.2.7p115) 2011/01/09 Released by Harlan Stenn * [Bug 1780] Windows ntpd 4.2.7p114 crashes in ioctl(). * [Bug 1781] longlong undefined in sntp handle_pkt() on Debian amd64. (4.2.7p114) 2011/01/08 Released by Harlan Stenn * Fix for openssl pkg-config detection eval failure. * Add erealloc_zero(), refactor estrdup(), emalloc(), emalloc_zero() to separate tracking callsite file/line from using debug MS C runtime, and to reduce code duplication. (4.2.7p113) 2011/01/07 Released by Harlan Stenn * [Bug 1776] sntp mishandles -t/--timeout and -a/--authentication. * Default to silent make rules, override with make V=1 or ./configure --disable-silent-rules. * Correct --with-openssl-incdir defaulting with pkg-config. * Correct ./build on systems without gtest available. * Begin moving some of the low-level socket stuff to libntp. (4.2.7p112) 2011/01/06 Released by Harlan Stenn * [Bug 1773] openssl not detected during ./configure. * [Bug 1774] Segfaults if cryptostats enabled and built without OpenSSL. * Use make V=0 in build script to increase signal/noise ratio. (4.2.7p111) 2011/01/05 Released by Harlan Stenn * [Bug 1772] refclock_open() return value check wrong for ACTS. * Default --with-openssl-libdir and --with-openssl-incdir to the values from pkg-config, falling back on our usual search paths if pkg-config is not available or does not have openssl.pc on PKG_CONFIG_PATH. * Change refclock_open() to return -1 on failure like open(). * Update all refclock_open() callers to check for fd <= 0 indicating failure, so they work with older and newer refclock_open() and can easily backport. * Initialize refclockproc.rio.fd to -1, harmonize refclock shutdown entrypoints to avoid crashing, particularly if refclock_open() fails. * Enable tickadj-like taming of wildly off-spec Windows clock using NTPD_TICKADJ_PPM env. var. specifying baseline slew. (4.2.7p110) 2011/01/04 Released by Harlan Stenn * [Bug 1771] algorithmic error in 'clocktime()' fixed. * Unit tests extended for hard-coded system time. * make V=0 and configure --enable-silent-rules supported. * setvar modemsetup = ATE0... overrides ACTS driver default. * Preserve last timecode in ACTS driver (ntpq -ccv). * Tolerate previous ATE1 state when sending ACTS setup. * Enable raw tty line discipline in Windows port. * Allow tty open/close/open to succeed on Windows port. * Enable ACTS and CHU reference clock drivers on Windows. (4.2.7p109) 2011/01/02 Released by Harlan Stenn * Remove nearly all strcpy() and most strcat() from NTP distribution. One major pocket remains in ntp_crypto.c. libopts & libisc also have (safe) uses of strcpy() and strcat() remaining. * Documentation updates from Dave Mills. (4.2.7p108) 2011/01/01 Released by Harlan Stenn * [Bug 1764] Move Palisade modem control logic to configure.ac. * [Bug 1768] TIOCFLUSH undefined in linux for refclock_acts. * Autokey multiple identity group improvements from Dave Mills. * from 4.2.6p3: Update the copyright year. (4.2.7p107) 2010/12/31 Released by Harlan Stenn * [Bug 1764] Palisade driver doesn't build on Linux. * [Bug 1766] Oncore clock has offset/high jitter at startup. * Move ntp_control.h variable IDs to ntp_control.c, remove their use by ntpq. They are implementation details private to ntpd. [Bug 597] was caused by ntpq's reliance on these IDs it need not know about. * refclock_acts.c updates from Dave Mills. (4.2.7p106) 2010/12/30 Released by Harlan Stenn * from 4.2.6p3: Update genCommitLog for the bk-5 release. (4.2.7p105) 2010/12/29 Released by Harlan Stenn (4.2.7p104) 2010/12/28 Released by Harlan Stenn * from 4.2.6p3: Create and use scripts/check--help when generating .texi files. * from 4.2.6p3: Update bk triggers for the bk-5 release. * Support for multiple Autokey identity groups from Dave Mills. * Documentation updates from Dave Mills. * Add ntpq kerninfo, authinfo, and sysinfo commands similar to ntpdc's. (4.2.7p103) 2010/12/24 Released by Harlan Stenn * Add ntpq pstats command similar to ntpdc's. * Remove ntpq pstatus command, rv/readvar does the same and more. * Documentation updates from Dave Mills. (4.2.7p102) 2010/12/23 Released by Harlan Stenn * Allow ntpq &1 associd use without preceding association-fetching. * Documentation updates from Dave Mills. (4.2.7p101) 2010/12/22 Released by Harlan Stenn * from 4.2.6p3-RC12: Upgrade to libopts 34.0.9 from AutoGen 5.11.6pre7. * from 4.2.6p3-RC12: Relax minimum Automake version to 1.10 with updated libopts.m4. (4.2.7p100) 2010/12/21 Released by Harlan Stenn * [Bug 1743] from 4.2.6p3-RC12: Display timezone offset when showing time for sntp in the local timezone (documentation updates). (4.2.7p99) 2010/12/21 Released by Harlan Stenn * Add unit tests for msnprintf(). (4.2.7p98) 2010/12/20 Released by Harlan Stenn * [Bug 1761] clockstuff/clktest-opts.h omitted from tarball. * [Bug 1762] from 4.2.6p3-RC12: manycastclient responses interfere. * Documentation updates from Dave Mills. (4.2.7p97) 2010/12/19 Released by Harlan Stenn * [Bug 1458] from 4.2.6p3-RC12: Can not compile NTP on FreeBSD 4.7. * [Bug 1760] from 4.2.6p3-RC12: ntpd Windows interpolation cannot be disabled. * from 4.2.6p3-RC12: Upgrade to libopts 34.0.9 from AutoGen 5.11.6pre5. * Documentation updates from Dave Mills. (4.2.7p96) 2010/12/18 Released by Harlan Stenn * [Bug 1758] from 4.2.6p3-RC12: setsockopt IPV6_MULTICAST_IF with wrong ifindex. * Documentation updates from Dave Mills. (4.2.7p95) 2010/12/17 Released by Harlan Stenn * [Bug 1753] 4.2.7p94 faults on startup in newpeer(), strdup(NULL). * [Bug 1754] from 4.2.6p3-RC12: --version output should be more verbose. * [Bug 1757] from 4.2.6p3-RC12: oncore snprintf("%m") doesn't expand %m. * from 4.2.6p3-RC12: Suppress ntp-keygen OpenSSL version display for --help, --version, display both build and runtime OpenSSL versions when they differ. * from 4.2.6p3-RC12: Upgrade to libopts 33.5.8 from AutoGen 5.11.6pre3. * Documentation updates from Dave Mills. (4.2.7p94) 2010/12/15 Released by Harlan Stenn * [Bug 1751] from 4.2.6p3-RC12: Support for Atari FreeMiNT OS. * Documentation updates from Dave Mills. (4.2.7p93) 2010/12/13 Released by Harlan Stenn * [Bug 1510] from 4.2.6p3-RC12: Add modes 20/21 for driver 8 to support RAWDCF @ 75 baud. * [Bug 1741] from 4.2.6p3-RC12: Enable multicast reception on each address (Windows). * from 4.2.6p3-RC12: Other manycastclient repairs: Separate handling of scope ID embedded in many in6_addr from ifindex used for IPv6 multicasting ioctls. Add INT_PRIVACY endpt bit flag for IPv6 RFC 4941 privacy addresses. Enable outbound multicast from only one address per interface in the same subnet, and in that case prefer embedded MAC address modified EUI-64 IPv6 addresses first, then static, and last RFC 4941 privacy addresses. Use setsockopt(IP[V6]_MULTICAST_IF) before each send to multicast to select the local source address, using the correct socket is not enough. * "server ... ident " changes from Dave Mills. * Documentation updates from Dave Mills. (4.2.7p92) 2010/12/08 Released by Harlan Stenn * [Bug 1743] from 4.2.6p3-RC12: Display timezone offset when showing time for sntp in the local timezone. (4.2.7p91) 2010/12/07 Released by Harlan Stenn * [Bug 1732] ntpd ties up CPU on disconnected USB device. * [Bug 1742] form 4.2.6p3-RC12: Fix a typo in an error message in the "build" script. (4.2.7p90) 2010/12/06 Released by Harlan Stenn * [Bug 1738] Windows ntpd has wrong net adapter name. * [Bug 1740] ntpdc -c reslist packet count wrongly treated as signed. (4.2.7p89) 2010/12/04 Released by Harlan Stenn * [Bug 1736] tos int, bool options broken in 4.2.7p66. * from 4.2.6p3-RC12: Clean up the SNTP documentation. (4.2.7p88) 2010/12/02 Released by Harlan Stenn * [Bug 1735] 'clocktime()' aborts ntpd on bogus input (4.2.7p87) 2010/12/01 Released by Harlan Stenn * from 4.2.6p3-RC12: Clean up m4 quoting in configure.ac, *.m4 files, resolving intermittent AC_LANG_PROGRAM possibly undefined errors. (4.2.7p86) 2010/11/29 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p85) 2010/11/24 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p84) 2010/11/22 Released by Harlan Stenn * [Bug 1618] Unreachable code in jjy_start(). * [Bug 1725] from 4.2.6p3-RC11: ntpd sends multicast from only one address. * from 4.2.6p3-RC11: Upgrade libopts to 33.3.8. * from 4.2.6p3-RC11: Bump minimum Automake version to 1.11, required for AM_COND_IF use in LIBOPTS_CHECK. * An almost complete rebuild of the initial loopfilter configuration process, including the code that determines the interval between frequency file updates, from Dave Mills. * Documentation updates from Dave Mills. * Add ntp-keygen -l/--lifetime to control certificate expiry. * JJY driver improvements for Tristate JJY01/02, including changes to its clockstats format. * Add "nonvolatile" ntp.conf directive to control how often the driftfile is written. (4.2.7p83) 2010/11/17 Released by Harlan Stenn * [Bug 1727] ntp-keygen PLEN, ILEN undeclared --without-crypto. * Remove top-level libopts, use sntp/libopts. * from 4.2.6p3-RC11: Remove log_msg() and debug_msg() from sntp in favor of msyslog(). * Documentation updates from Dave Mills. (4.2.7p82) 2010/11/16 Released by Harlan Stenn * [Bug 1728] from 4.2.6p3-RC11: In ntp_openssl.m4, don't add -I/usr/include or -L/usr/lib to CPPFLAGS or LDFLAGS. (4.2.7p81) 2010/11/14 Released by Harlan Stenn * [Bug 1681] from 4.2.6p3-RC10: More sntp logging cleanup. * [Bug 1683] from 4.2.6p3-RC10: Non-localhost on loopback exempted from nic rules. * [Bug 1719] Cleanup for ntp-keygen and fix -V crash, from Dave Mills. (4.2.7p80) 2010/11/10 Released by Harlan Stenn * [Bug 1574] from 4.2.6p3-RC9: sntp doesn't set tv_usec correctly. * [Bug 1681] from 4.2.6p3-RC9: sntp logging cleanup. * [Bug 1683] from 4.2.6p3-RC9: Interface binding does not seem to work as intended. * [Bug 1708] make check fails with googletest 1.4.0. * [Bug 1709] from 4.2.6p3-RC9: ntpdate ignores replies with equal receive and transmit timestamps. * [Bug 1715] sntp utilitiesTest.IPv6Address failed. * [Bug 1718] Improve gtest checks in configure.ac. (4.2.7p79) 2010/11/07 Released by Harlan Stenn * Correct frequency estimate with no drift file, from David Mills. (4.2.7p78) 2010/11/04 Released by Harlan Stenn * [Bug 1697] filegen implementation should be improved. * Refactor calendar functions in terms of new common code. * Documentation updates from Dave Mills. (4.2.7p77) 2010/11/03 Released by Harlan Stenn * [Bug 1692] packageinfo.sh needs to be "sourced" using ./ . * [Bug 1695] ntpdate takes longer than necessary. (4.2.7p76) 2010/11/02 Released by Harlan Stenn * [Bug 1690] Unit tests fails to build on some systems. * [Bug 1691] Use first NMEA sentence each second. * Put the sntp tests under sntp/ . * ... and only build/run them if we have gtest. * Documentation updates from Dave Mills. (4.2.7p75) 2010/10/30 Released by Harlan Stenn * Documentation updates from Dave Mills. * Include Linus Karlsson's GSoC 2010 testing code. (4.2.7p74) 2010/10/29 Released by Harlan Stenn * [Bug 1685] from 4.2.6p3-RC8: NMEA driver mode byte confusion. * from 4.2.6p3-RC8: First cut at using scripts/checkChangeLog. * Documentation updates from Dave Mills. (4.2.7p73) 2010/10/27 Released by Harlan Stenn * [Bug 1680] Fix alignment of clock_select() arrays. * refinements to new startup behavior from David Mills. * For the bootstrap script, touch .html files last. * Add 'make check' test case that would have caught [Bug 1678]. (4.2.7p72) 2010/10/26 Released by Harlan Stenn * [Bug 1679] Fix test for -lsocket. * Clean up missing ;; entries in configure.ac. (4.2.7p71) 2010/10/25 Released by Harlan Stenn * [Bug 1676] from 4.2.6p3-RC7: NMEA: $GPGLL did not work after fix for Bug 1571. * [Bug 1678] "restrict source" treated as "restrict default". * from 4.2.6p3-RC7: Added scripts/checkChangeLog. (4.2.7p70) 2010/10/24 Released by Harlan Stenn * [Bug 1571] from 4.2.6p3-RC6: NMEA does not relate data to PPS edge. * [Bug 1572] from 4.2.p63-RC6: NMEA time adjustment for GPZDG buggy. * [Bug 1675] from 4.2.6p3-RC6: Prohibit includefile remote config. * Enable generating ntpd/ntp_keyword.h after keyword-gen.c changes on Windows as well as POSIX platforms. * Fix from Dave Mills for a rare singularity in clock_combine(). (4.2.7p69) 2010/10/23 Released by Harlan Stenn * [Bug 1671] Automatic delay calibration is sometimes inaccurate. (4.2.7p68) 2010/10/22 Released by Harlan Stenn * [Bug 1669] from 4.2.6p3-RC5: NTP fails to compile on IBM AIX 5.3. * [Bug 1670] Fix peer->bias and broadcastdelay. * Documentation updates from Dave Mills. * Documentation EOL cleanup. (4.2.7p67) 2010/10/21 Released by Harlan Stenn * [Bug 1649] from 4.2.6p3-RC5: Require NMEA checksum if $GPRMC or previously seen. (4.2.7p66) 2010/10/19 Released by Harlan Stenn * [Bug 1277] Provide and use O(1) FIFOs, esp. in the config tree code. * Remove unused 'bias' configuration keyword. (4.2.7p65) 2010/10/16 Released by Harlan Stenn * [Bug 1584] from 4.2.6p3-RC4: wrong SNMP type for precision, resolution. * Remove 'calldelay' and 'sign' remnants from parser, ntp_config.c. (4.2.7p64) 2010/10/15 Released by Harlan Stenn * [Bug 1584] from 4.2.6p3-RC3: ntpsnmpd OID must be mib-2.197. * [Bug 1659] from 4.2.6p3-RC4: Need CLOCK_TRUETIME not CLOCK_TRUE. * [Bug 1663] ntpdsim should not open net sockets. * [Bug 1665] from 4.2.6p3-RC4: is_anycast() u_int32_t should be u_int32. * from 4.2.6p3: ntpsnmpd, libntpq warning cleanup. * Remove 'calldelay' and 'sign' keywords (Dave Mills). * Documentation updates from Dave Mills. (4.2.7p63) 2010/10/13 Released by Harlan Stenn * [Bug 1080] from 4.2.6p3-RC3: ntpd on ipv6 routers very chatty. * Documentation nit cleanup. * Documentation updates from Dave Mills. (4.2.7p62) 2010/10/12 Released by Harlan Stenn * [Bug 750] from 4.2.6p3-RC3: Non-existing device causes coredump with RIPE-NCC driver. * [Bug 1567] from 4.2.6p3-RC3: Support Arbiter 1093C Satellite Clock on Windows. * [Bug 1581] from 4.2.6p3-RC3: printf format string mismatch leftover. * [Bug 1659] from 4.2.6p3-RC3: Support Truetime Satellite Clocks on Windows. * [Bug 1660] from 4.2.6p3-RC3: On some systems, test is in /usr/bin, not /bin. * [Bug 1661] from 4.2.6p3-RC3: Re-indent refclock_ripencc.c. * Lose peer_count from ntp_peer.c and ntp_proto.c (Dave Mills). * Documentation updates from Dave Mills. (4.2.7p61) 2010/10/06 Released by Harlan Stenn * Documentation and code cleanup from Dave Mills. No more NTP_MAXASSOC. (4.2.7p60) 2010/10/04 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p59) 2010/10/02 Released by Harlan Stenn * Documentation updates from Dave Mills. * Variable name cleanup from Dave Mills. * [Bug 1657] darwin needs res_9_init, not res_init. (4.2.7p58) 2010/09/30 Released by Harlan Stenn * Clock select bugfix from Dave Mills. * [Bug 1554] peer may stay selected as system peer after becoming unreachable. * [Bug 1644] from 4.2.6p3-RC3: cvo.sh should use lsb_release to identify linux distros. * [Bug 1646] ntpd crashes with relative path to logfile. (4.2.7p57) 2010/09/27 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p56) 2010/09/25 Released by Harlan Stenn * Clock combining algorithm improvements from Dave Mills. * Documentation updates from Dave Mills. * [Bug 1642] ntpdsim can't find simulate block in config file. * [Bug 1643] from 4.2.6p3-RC3: Range-check the decoding of the RIPE-NCC status codes. (4.2.7p55) 2010/09/22 Released by Harlan Stenn * Documentation updates from Dave Mills. * [Bug 1636] from 4.2.6p3-RC2: segfault after denied remote config. (4.2.7p54) 2010/09/21 Released by Harlan Stenn * More Initial convergence improvements from Dave Mills. * Documentation updates from Dave Mills. * [Bug 1635] from 4.2.6p3-RC2: "filegen ... enable" is not default. (4.2.7p53) 2010/09/20 Released by Harlan Stenn * Documentation updates from Dave Mills. * More Initial convergence improvements from Dave Mills. (4.2.7p52) 2010/09/19 Released by Harlan Stenn * Initial convergence improvements from Dave Mills. (4.2.7p51) 2010/09/18 Released by Harlan Stenn * [Bug 1344] from 4.2.6p3-RC1: ntpd on Windows exits without logging cause. * [Bug 1629] 4.2.7p50 configure.ac changes invalidate config.cache. * [Bug 1630] 4.2.7p50 cannot bootstrap on Autoconf 2.61. (4.2.7p50) 2010/09/16 Released by Harlan Stenn * Cleanup NTP_LIB_M. * [Bug 1628] Clean up -lxnet/-lsocket usage for (open)solaris. (4.2.7p49) 2010/09/13 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p48) 2010/09/12 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p47) 2010/09/11 Released by Harlan Stenn * Documentation updates from Dave Mills. * [Bug 1588] finish configure --disable-autokey implementation. * [Bug 1616] refclock_acts.c: if (pp->leap == 2) is always false. * [Bug 1620] [Backward Incompatible] "discard minimum" value should be in seconds, not log2 seconds. (4.2.7p46) 2010/09/10 Released by Harlan Stenn * Use AC_SEARCH_LIBS instead of AC_CHECK_LIB for NTP_LIB_M. (4.2.7p45) 2010/09/05 Released by Harlan Stenn * [Bug 1578] Consistently use -lm when needed. (4.2.7p44) 2010/08/27 Released by Harlan Stenn * [Bug 1573] from 4.2.6p3-beta1: Miscalculation of offset in sntp. (4.2.7p43) 2010/08/26 Released by Harlan Stenn * [Bug 1602] Refactor some of the sntp/ directory to facililtate testing. (4.2.7p42) 2010/08/18 Released by Harlan Stenn * [Bug 1593] ntpd abort in free() with logconfig syntax error. * [Bug 1595] from 4.2.6p3-beta1: empty last line in key file causes duplicate key to be added * [Bug 1597] from 4.2.6p3-beta1: packet processing ignores RATE KoD packets, Because of a bug in string comparison. (4.2.7p41) 2010/07/28 Released by Harlan Stenn * [Bug 1581] from 4.2.6p3-beta1: ntp_intres.c size_t printf format string mismatch. * [Bug 1586] ntpd 4.2.7p40 doesn't write to syslog after fork on QNX. * Avoid race with parallel builds using same source directory in scripts/genver by using build directory for temporary files. * orphanwait documentation updates. (4.2.7p40) 2010/07/12 Released by Harlan Stenn * [Bug 1395] ease ntpdate elimination with ntpd -w/--wait-sync * [Bug 1396] allow servers on ntpd command line like ntpdate (4.2.7p39) 2010/07/09 Released by Harlan Stenn * Fix typo in driver28.html. * [Bug 1581] from 4.2.6p2: size_t printf format string mismatches, IRIG string buffers undersized. Mostly backported from earlier ntp-dev fixes by Juergen Perlinger. (4.2.7p38) 2010/06/20 Released by Harlan Stenn * [Bug 1570] backported to 4.2.6p2-RC7. * [Bug 1575] from 4.2.6p2-RC7: use 'snprintf' with LIB_BUFLENGTH in inttoa.c, tvtoa.c and utvtoa.c * [Bug 1576] backported to 4.2.6p2-RC7. * Typo fix in a comment in ntp_proto.c. (4.2.7p37) 2010/06/19 Released by Harlan Stenn * [Bug 1576] sys/sysctl.h depends on sys/param.h on OpenBSD. (4.2.7p36) 2010/06/15 Released by Harlan Stenn * [Bug 1560] Initial support for orphanwait, from Dave Mills. * clock_filter()/reachability fixes from Dave Mills. (4.2.7p35) 2010/06/12 Released by Harlan Stenn * Rewrite of multiprecision macros in 'ntp_fp.h' from J. Perlinger * [Bug 715] from 4.2.6p2-RC6: libisc Linux IPv6 interface iteration drops multicast flags. (4.2.7p34) 2010/06/05 Released by Harlan Stenn * [Bug 1570] serial clock drivers get outdated input from kernel tty line buffer after startup (4.2.7p33) 2010/06/04 Released by Harlan Stenn * [Bug 1561] from 4.2.6p2-RC5: ntpq, ntpdc "passwd" prompts for MD5 password w/SHA1. * [Bug 1565] from 4.2.6p2-RC5: sntp/crypto.c compile fails on MacOS over vsnprintf(). * from 4.2.6p2-RC5: Windows port: do not exit in ntp_timestamp_from_counter() without first logging the reason. (4.2.7p32) 2010/05/19 Released by Harlan Stenn * Copyright file cleanup from Dave Mills. * [Bug 1555] from 4.2.6p2-RC4: sntp illegal C (mixed code and declarations). * [Bug 1558] pool prototype associations have 0.0.0.0 for remote addr. * configure.ac: add --disable-autokey, #define AUTOKEY to enable future support for building without Autokey, but with OpenSSL for its digest algorithms (hash functions). Code must be modified to use #ifdef AUTOKEY instead of #ifdef OPENSSL where appropriate to complete this. * include/ntp_crypto.h: make assumption AUTOKEY implies OPENSSL explicit. (4.2.7p31) 2010/05/11 Released by Harlan Stenn * [Bug 1325] from 4.2.6p2-RC3: unreachable code sntp recv_bcst_data(). * [Bug 1459] from 4.2.6p2-RC3: sntp MD5 authentication does not work with ntpd. * [Bug 1552] from 4.2.6p2-RC3: update and complete broadcast and crypto features in sntp. * [Bug 1553] from 4.2.6p2-RC3: sntp/configure.ac OpenSSL support. * from 4.2.6p2-RC3: Escape unprintable characters in a refid in ntpq -p billboard. * from 4.2.6p2-RC3: Simplify hash client code by providing OpenSSL EVP_*() API when built without OpenSSL. (already in 4.2.7) * from 4.2.6p2-RC3: Do not depend on ASCII in sntp. (4.2.7p30) 2010/05/06 Released by Harlan Stenn * [Bug 1526] ntpd DNS pipe read EINTR with no network at startup. * Update the ChangeLog entries when merging items from -stable. (4.2.7p29) 2010/05/04 Released by Harlan Stenn * [Bug 1542] ntpd mrulist response may have incorrect last.older. * [Bug 1543] ntpq mrulist must refresh nonce when retrying. * [Bug 1544] ntpq mrulist sscanf timestamp format mismatch on 64-bit. * Windows compiling hints/winnt.html update from G. Sunil Tej. (4.2.7p28) 2010/05/03 Released by Harlan Stenn * [Bug 1512] from 4.2.6p2-RC3: ntpsnmpd should connect to net-snmpd via a unix-domain socket by default. Provide a command-line 'socket name' option. * [Bug 1538] from 4.2.6p2-RC3: update refclock_nmea.c's call to getprotobyname(). * [Bug 1541] from 4.2.6p2-RC3: Fix wrong keyword for "maxclock". (4.2.7p27) 2010/04/27 Released by Harlan Stenn (4.2.7p26) 2010/04/24 Released by Harlan Stenn * [Bug 1465] from 4.2.6p2-RC2: Make sure time from TS2100 is not invalid (backport from -dev). * [Bug 1528] from 4.2.6p2-RC2: Fix EDITLINE_LIBS link order for ntpq and ntpdc. * [Bug 1531] Require nonce with mrulist requests. * [Bug 1532] Remove ntpd support for ntpdc's monlist in favor of ntpq's mrulist. * [Bug 1534] from 4.2.6p2-RC2: conflicts with VC++ 2010 errno.h. * [Bug 1535] from 4.2.6p2-RC2: "restrict -4 default" and "restrict -6 default" ignored. (4.2.7p25) 2010/04/20 Released by Harlan Stenn * [Bug 1528] from 4.2.6p2-RC2: Remove --with-arlib from br-flock. * [Bug 1503] [Bug 1504] [Bug 1518] [Bug 1522] from 4.2.6p2-RC2: all of which were fixed in 4.2.7 previously. (4.2.7p24) 2010/04/13 Released by Harlan Stenn * [Bug 1390] Control PPS on the Oncore M12. * [Bug 1518] Windows ntpd should lock to one processor more conservatively. * [Bug 1520] '%u' formats for size_t gives warnings with 64-bit builds. * [Bug 1522] Enable range syntax "trustedkey (301 ... 399)". * Documentation updates for 4.2.7p22 changes and additions, updating ntpdc.html, ntpq.html, accopt.html, confopt.html, manyopt.html, miscopt.html, and miscopt.txt. * accopt.html: non-ntpport doc changes from Dave Mills. * Modify full MRU list preemption when full to match "discard monitor" documentation, by removing exception for count == 1. (4.2.7p23) 2010/04/04 Released by Harlan Stenn * [Bug 1516] unpeer by IP address fails, DNS name works. * [Bug 1517] ntpq and ntpdc should verify reverse DNS before use. ntpq and ntpdc now use the following format for showing purported DNS names from IP address "reverse" DNS lookups when the DNS name does not exist or does not include the original IP address among the results: "192.168.1.2 (fake.dns.local)". (4.2.7p22) 2010/04/02 Released by Harlan Stenn * [Bug 1432] Don't set inheritable flag for linux capabilities. * [Bug 1465] Make sure time from TS2100 is not invalid. * [Bug 1483] AI_NUMERICSERV undefined in 4.2.7p20. * [Bug 1497] fudge is broken by getnetnum() change. * [Bug 1503] Auto-enabling of monitor for "restrict ... limited" wrong. * [Bug 1504] ntpdate tickles ntpd "discard minimum 1" rate limit if "restrict ... limited" is used. * ntpdate: stop querying source after KoD packet response, log it. * ntpdate: rate limit each server to 2s between packets. * From J. N. Perlinger: avoid pointer wraparound warnings in dolfptoa(), printf format mismatches with 64-bit size_t. * Broadcast client (ephemeral) associations should be demobilized only if they are not heard from for 10 consecutive polls, regardless of surviving the clock selection. Fix from David Mills. * Add "ntpq -c ifstats" similar to "ntpdc -c ifstats". * Add "ntpq -c sysstats" similar to "ntpdc -c sysstats". * Add "ntpq -c monstats" to show monlist knobs and stats. * Add "ntpq -c mrulist" similar to "ntpdc -c monlist" but not limited to 600 rows, and with filtering and sorting options: ntpq -c "mrulist mincount=2 laddr=192.168.1.2 sort=-avgint" ntpq -c "mrulist sort=addr" ntpq -c "mrulist mincount=2 sort=count" ntpq -c "mrulist sort=-lstint" * Modify internal representation of MRU list to use l_fp fixed-point NTP timestamps instead of seconds since startup. This increases the resolution and substantially improves accuracy of sorts involving timestamps, at the cost of flushing all MRU entries when the clock is stepped, to ensure the timestamps can be compared with the current get_systime() results. * Add ntp.conf "mru" directive to configure MRU parameters, such as "mru mindepth 600 maxage 64 maxdepth 5000 maxmem 1024" or "mru initalloc 0 initmem 16 incalloc 99 incmem 4". Several pairs are equivalent with one in units of MRU entries and its twin in units of kilobytes of memory, so the last one used in ntp.conf controls: maxdepth/maxmem, initalloc/initmem, incalloc/incmem. With the above values, ntpd will preallocate 16kB worth of MRU entries, allocating 4kB worth each time more are needed, with a hard limit of 1MB of MRU entries. Until there are more than 600 entries none would be reused. Then only entries for addresses last seen 64 seconds or longer ago are reused. * Limit "ntpdc -c monlist" response in ntpd to 600 entries, the previous overall limit on the MRU list depth which was driven by the monlist implementation limit of one request with a single multipacket response. * New "pool" directive implementation modeled on manycastclient. * Do not abort on non-ASCII characters in ntp.conf, ignore them. * ntpq: increase response reassembly limit from 24 to 32 packets, add discussion in comment regarding results with even larger MAXFRAGS. * ntpq: handle "passwd MYPASSWORD" (without prompting) as with ntpdc. * ntpdc: do not examine argument to "passwd" if not supplied. * configure: remove check for pointer type used with qsort(), we require ANSI C which mandates void *. * Reset sys_kodsent to 0 in proto_clr_stats(). * Add sptoa()/sockporttoa() similar to stoa()/socktoa() adding :port. * Use memcpy() instead of memmove() when buffers can not overlap. * Remove sockaddr_storage from our sockaddr_u union of sockaddr, sockaddr_in, and sockaddr_in6, shaving about 100 bytes from its size and substantially decreasing MRU entry memory consumption. * Extend ntpq readvar (alias rv) to allow fetching up to three named variables in one operation: ntpq -c "rv 0 version offset frequency". * ntpq: use srchost variable to show .POOL. prototype associations' hostname instead of address 0.0.0.0. * "restrict source ..." configures override restrictions for time sources, allows tight default restrictions to be used with the pool directive (where server addresses are not known in advance). * Ignore "preempt" modifier on manycastclient and pool prototype associations. The resulting associations are preemptible, but the prototype must not be. * Maintain and use linked list of associations (struct peer) in ntpd, avoiding walking 128 hash table entries to iterate over peers. * Remove more workarounds unneeded since we require ISO C90 AKA ANSI C: - remove fallback implementations for memmove(), memset, strstr(). - do not test for atexit() or memcpy(). * Collapse a bunch of code duplication in ntpd/ntp_restrict.c added with support for IPv6. * Correct some corner case failures in automatically enabling the MRU list if any "restrict ... limited" is in effect, and in disabling MRU maintenance. (ntp_monitor.c, ntp_restrict.c) * Reverse the internal sort order of the address restriction lists, but preserve the same behavior. This allows removal of special-case code related to the default restrictions and more straightforward lookups of restrictions for a given address (now, stop on first match). * Move ntp_restrict.c MRU doubly-linked list maintenance code into ntp_lists.h macros, allowing more duplicated source excision. * Repair ntpdate.c to no longer test HAVE_TIMER_SETTIME. * Do not reference peer_node/unpeer_node after freeing when built with --disable-saveconfig and using DNS. (4.2.7p21) 2010/03/31 Released by Harlan Stenn * [Bug 2399] Reset sys_kodsent in proto_clr_stats(). * [Bug 1514] from 4.2.6p1-RC6: Typo in ntp_proto.c: fabs(foo < .4) should be fabs(foo) < .4. * [Bug 1464] from 4.2.6p1-RC6: synchronization source wrong for refclocks ARCRON_MSF (27) and SHM (28). * From 4.2.6p1-RC6: Correct Windows port's refclock_open() to return 0 on failure not -1. * From 4.2.6p1-RC6: Correct CHU, dumbclock, and WWVB drivers to check for 0 returned from refclock_open() on failure. * From 4.2.6p1-RC6: Correct "SIMUL=4 ./flock-build -1" to prioritize -1/--one. * [Bug 1306] constant conditionals in audio_gain(). (4.2.7p20) 2010/02/13 Released by Harlan Stenn * [Bug 1483] hostname in ntp.conf "restrict" parameter rejected. * Use all addresses for each restrict by hostname. * Use async DNS to resolve trap directive hostnames. (4.2.7p19) 2010/02/09 Released by Harlan Stenn * [Bug 1338] Update the association type codes in ntpq.html. * [Bug 1478] from 4.2.6p1-RC5: linking fails: EVP_MD_pkey_type. * [Bug 1479] from 4.2.6p1-RC5: not finding readline headers. * [Bug 1484] from 4.2.6p1-RC5: ushort is not defined in QNX6. (4.2.7p18) 2010/02/07 Released by Harlan Stenn * [Bug 1480] from 4.2.6p1-RC5: snprintf() cleanup caused unterminated refclock IDs. * Stop using getaddrinfo() to convert numeric address strings to on-wire addresses in favor of is_ip_address() alone. (4.2.7p17) 2010/02/05 Released by Harlan Stenn * [Bug 1477] from 4.2.6p1-RC5: First non-gmake make in clone w/VPATH can't make COPYRIGHT. * Attempts to cure CID 108 CID 118 CID 119 TAINTED_SCALAR warnings. * Broaden ylwrap workaround VPATH_HACK to all non-GNU make. (4.2.7p16) 2010/02/04 Released by Harlan Stenn * [Bug 1474] from 4.2.6p1-RC4: ntp_keygen LCRYPTO after libntp.a. * Include 4.2.6p1-RC4: Remove arlib. (4.2.7p15) 2010/02/03 Released by Harlan Stenn * [Bug 1455] from 4.2.6p1: ntpd does not try /etc/ntp.audio. * Include 4.2.6p1: Convert many sprintf() calls to snprintf(), also strcpy(), strcat(). * Include 4.2.6p1: Fix widely cut-n-pasted bug in refclock shutdown after failed start. * Include 4.2.6p1: Remove some dead code checking for emalloc() returning NULL. (4.2.7p14) 2010/02/02 Released by Harlan Stenn * [Bug 1338] ntpq displays incorrect association type codes. * [Bug 1469] u_int32, int32 changes broke HP-UX 10.20 build. * [Bug 1470] from 4.2.6p1: "make distdir" compiles keyword-gen. * [Bug 1471] CID 120 CID 121 CID 122 is_ip_address() uninit family. * [Bug 1472] CID 116 CID 117 minor warnings in new DNS code. * [Bug 1473] from 4.2.6p1: "make distcheck" version.m4 error. (4.2.7p13) 2010/01/31 Released by Harlan Stenn * [Bug 1467] from 4.2.6p1: Fix bogus rebuild of sntp/sntp.html. (4.2.7p12) 2010/01/30 Released by Harlan Stenn * [Bug 1468] 'make install' broken for root on default NFS mount. (4.2.7p11) 2010/01/28 Released by Harlan Stenn * [Bug 47] Debugging and logging do not work after a fork. * [Bug 1010] getaddrinfo() could block and thus should not be called by the main thread/process. * New async DNS resolver in ntpd allows nonblocking queries anytime, instead of only once at startup. (4.2.7p10) 2010/01/24 Released by Harlan Stenn * [Bug 1140] from 4.2.6p1-RC5: Clean up debug.html, decode.html, and ntpq.html. * Include 4.2.6p1-RC3: Use TZ=UTC instead of TZ= when calling date in scripts/mkver.in . * [Bug 1448] from 4.2.6p1-RC3: Some macros not correctly conditionally or absolutely defined on Windows. * [Bug 1449] from 4.2.6p1-RC3: ntpsim.h in ntp_config.c should be used conditionally. * [Bug 1450] from 4.2.6p1-RC3: Option to exclude warnings not unconditionally defined on Windows. (4.2.7p9) 2010/01/13 Released by Harlan Stenn (4.2.7p8) 2010/01/12 Released by Harlan Stenn * [Bug 702] ntpd service logic should use libopts to examine cmdline. * [Bug 1451] from 4.2.6p1-RC3: sntp leaks KoD entry updating. * [Bug 1453] from 4.2.6p1-RC3: Use $CC in config.cache filename. (4.2.7p7) 2009/12/30 Released by Harlan Stenn * [Bug 620] ntpdc getresponse() esize != *rsize s/b size != *rsize. * [Bug 1446] 4.2.7p6 requires autogen, missing ntpd.1, *.texi, *.menu. (4.2.7p6) 2009/12/28 Released by Harlan Stenn * [Bug 1443] Remove unnecessary dependencies on ntp_io.h * [Bug 1442] Move Windows functions into libntp files * [Bug 1127] from 4.2.6p1-RC3: Check the return of X590_verify(). * [Bug 1439] from 4.2.6p1-RC3: .texi gen after binary is linked. * [Bug 1440] from 4.2.6p1-RC3: Update configure.ac to support kfreebsd. * [Bug 1445] from 4.2.6p1-RC3: IRIX does not have -lcap or support linux capabilities. (4.2.7p5) 2009/12/25 Released by Harlan Stenn * Include 4.2.6p1-RC2 (4.2.7p4) 2009/12/24 Released by Harlan Stenn * [Bug 1429] ntpd -4 option does not reliably force IPv4 resolution. * [Bug 1431] System headers must come before ntp headers in ntp_intres.c . (4.2.7p3) 2009/12/22 Released by Harlan Stenn * [Bug 1426] scripts/VersionName needs . on the search path. * [Bug 1427] quote missing in ./build - shows up on NetBSD. * [Bug 1428] Use AC_HEADER_RESOLV to fix breaks from resolv.h (4.2.7p2) 2009/12/20 Released by Harlan Stenn * [Bug 1419] ntpdate, ntpdc, sntp, ntpd ignore configure --bindir. * [Bug 1421] add util/tg2, a clone of tg that works on Linux, NetBSD, and FreeBSD (4.2.7p1) 2009/12/15 Released by Harlan Stenn * [Bug 1348] ntpd Windows port should wait for sendto() completion. * [Bug 1413] test OpenSSL headers regarding -Wno-strict-prototypes. * [Bug 1418] building ntpd/ntpdc/ntpq statically with ssl fails. (4.2.7p0) 2009/12/13 Released by Harlan Stenn * [Bug 1412] m4/os_cflags.m4 caches results that depend on $CC. * [Bug 1414] Enable "make distcheck" success with BSD make. (4.2.7) 2009/12/09 Released by Harlan Stenn * [Bug 1407] configure.ac: recent GNU Make -v does not include "version". --- (4.2.6p5) 2011/12/24 Released by Harlan Stenn No changes from 4.2.6p5-RC3. --- (4.2.6p5-RC3) 2011/12/08 Released by Harlan Stenn * [Bug 2082] 3-char refid sent by ntpd 4.2.6p5-RC2 ends with extra dot. * [Bug 2085] clock_update() sys_rootdisp calculation omits root delay. * [Bug 2086] get_systime() should not offset by sys_residual. * [Bug 2087] sys_jitter calculation overweights sys.peer jitter. * Ensure NULL peer->dstadr is not accessed in orphan parent selection. --- (4.2.6p5-RC2) 2011/11/30 Released by Harlan Stenn * [Bug 2050] Orphan mode stratum counting to infinity. * [Bug 2059] optional billboard column "server" does not honor -n. * [Bug 2066] ntpq lopeers ipv6 "local" column overrun. * [Bug 2068] ntpd sends nonprintable stratum 16 refid to ntpq. * [Bug 2069] broadcastclient, multicastclient spin up duplicate ephemeral associations without broadcastdelay. * [Bug 2072] Orphan parent selection metric needs ntohl(). * Exclude not-yet-determined sys_refid from use in loopback TEST12 (from David Mills). * Never send KoD rate limiting response to MODE_SERVER response. --- (4.2.6p5-RC1) 2011/10/18 Released by Harlan Stenn * [Bug 2034] Listening address configuration with prefix misapplied. --- (4.2.6p4) 2011/09/22 Released by Harlan Stenn * [Bug 1984] ntp/libisc fails to compile on OS X 10.7 (Lion). * [Bug 1985] "logconfig =allall" rejected. * [Bug 2001] ntpdc timerstats reports overruns as handled. * [Bug 2003] libntpq ntpq_read_assoc_peervars() broken. * [Backward Incompatible] sntp: -l/--filelog -> -l/--logfile, to be consistent with ntpd. * libopts/file.c fix from Bruce Korb (arg-type=file). --- (4.2.6p4-RC2) 2011/08/04 Released by Harlan Stenn * [Bug 1608] Parse Refclock driver should honor trusttime. * [Bug 1961] html2man update: distribute ntp-wait.html. * [Bug 1970] UNLINK_EXPR_SLIST() causes crash if list is empty. * [Bug 1972] checking for struct rtattr fails. * [Bug 1975] libntp/mktime.c won't work with 64-bit time_t * [Bug 1978] [Bug 1134] fix in 4.2.6p4-RC1 doesn't build on older Linux. * Backport several fixes for Coverity warnings from ntp-dev. * Backport if_nametoindex() check for hpux. --- (4.2.6p4-RC1) 2011/07/10 Released by Harlan Stenn * [Bug 1134] ntpd fails binding to tentative IPv6 addresses. * [Bug 1790] Update config.guess and config.sub to detect AIX6. * [Bug 1961] html2man needs an update. * Update the NEWS file. --- (4.2.6p4-beta2) 2011/05/25 Released by Harlan Stenn * [Bug 1695] ntpdate takes longer than necessary. * [Bug 1832] ntpdate doesn't allow timeout > 2s. * [Bug 1933] WWVB/Spectracom driver timestamps LFs, not CRs. * Backport utility routines from ntp-dev: mprintf(), emalloc_zero(). --- (4.2.6p4-beta1) 2011/05/16 Released by Harlan Stenn * [Bug 1554] peer may stay selected as system peer after becoming unreachable. * [Bug 1921] LOCAL, ACTS drivers with "prefer" excluded from initial candidate list. * [Bug 1923] orphan parent favored over LOCAL, ACTS drivers. * [Bug 1924] Billboard tally codes sometimes do not match operation, variables. * Enable tickadj-like taming of wildly off-spec Windows clock using NTPD_TICKADJ_PPM env. var. specifying baseline slew. * Upgrade to AutoGen 5.11.9 (and require it). * Upgrade to libopts 35.0.10 from AutoGen 5.11.9pre8. --- (4.2.6p3) 2011/01/03 Released by Harlan Stenn * [Bug 1764] Palisade driver doesn't build on Linux * Create and use scripts/check--help when generating .texi files. * Update bk triggers for the bk-5 release. * Update genCommitLog for the bk-5 release. * Update the copyright year. --- (4.2.6p3-RC12) 2010/12/25 Released by Harlan Stenn * [Bug 1458] Can not compile NTP on FreeBSD 4.7. * [Bug 1510] Add modes 20/21 for driver 8 to support RAWDCF @ 75 baud. * [Bug 1618] Unreachable code in jjy_start(). (backport from ntp-dev) * [Bug 1719] ntp-keygen -V crash. (backport) * [Bug 1740] ntpdc treats many counters as signed. (backport) * [Bug 1741] Enable multicast reception on each address (Windows). * [Bug 1742] Fix a typo in an error message in the "build" script. * [Bug 1743] Display timezone offset when showing time for sntp in the local timezone. * [Bug 1751] Support for Atari FreeMiNT OS. * [Bug 1754] --version output should be more verbose. * [Bug 1757] oncore snprintf("%m") doesn't expand %m. * [Bug 1758] setsockopt IPV6_MULTICAST_IF with wrong ifindex. * [Bug 1760] ntpd Windows interpolation cannot be disabled. * [Bug 1762] manycastclient solicitation responses interfere. * Upgrade to libopts 34.0.9 from AutoGen 5.11.6pre7. * Relax minimum Automake version to 1.10 with updated libopts.m4. * Suppress ntp-keygen OpenSSL version display for --help, --version, display both build and runtime OpenSSL versions when they differ. * Clean up m4 quoting in configure.ac, *.m4 files, resolving intermittent AC_LANG_PROGRAM possibly undefined errors. * Clean up the SNTP documentation. * Other manycastclient repairs: Separate handling of scope ID embedded in many in6_addr from ifindex used for IPv6 multicasting ioctls. Add INT_PRIVACY endpt bit flag for IPv6 RFC 4941 privacy addresses. Enable outbound multicast from only one address per interface in the same subnet, and in that case prefer embedded MAC address modified EUI-64 IPv6 addresses first, then static, and last RFC 4941 privacy addresses. Use setsockopt(IP[V6]_MULTICAST_IF) before each send to multicast to select the local source address, using the correct socket is not enough. --- (4.2.6p3-RC11) 2010/11/28 Released by Harlan Stenn * [Bug 1725] ntpd sends multicast from only one address. * [Bug 1728] In ntp_openssl.m4, don't add -I/usr/include or -L/usr/lib to CPPFLAGS or LDFLAGS. * [Bug 1733] IRIX doesn't have 'head' (affects scripts/checkChangeLog). * Remove log_msg() and debug_msg() from sntp in favor of msyslog(). * Use a single copy of libopts/, in sntp/. * Upgrade libopts to 33.3.8. * Bump minimum Automake version to 1.11, required for AM_COND_IF use in LIBOPTS_CHECK. * Improvements to the 'build' script. --- (4.2.6p3-RC10) 2010/11/14 Released by Harlan Stenn * [Bug 1681] More sntp logging cleanup. * [Bug 1683] Non-localhost on loopback exempted from nic rules. --- (4.2.6p3-RC9) 2010/11/10 Released by Harlan Stenn * [Bug 1574] sntp:set_time doesn't set tv_usec correctly. * [Bug 1681] sntp logging cleanup. * [Bug 1683] Interface binding does not seem to work as intended. * [Bug 1691] Use first NMEA sentence each second. * [Bug 1692] packageinfo.sh needs to be "sourced" using ./ . * [Bug 1709] ntpdate ignores replies with equal receive and transmit timestamps. * Backport sntp from -dev --- (4.2.6p3-RC8) 2010/10/29 Released by Harlan Stenn * [Bug 1685] NMEA driver mode byte confusion. * First cut at using scripts/checkChangeLog. --- (4.2.6p3-RC7) 2010/10/25 Released by Harlan Stenn * [Bug 1676] NMEA: $GPGLL did not work after fix for Bug 1571. * Added scripts/checkChangeLog. --- (4.2.6p3-RC6) 2010/10/24 Released by Harlan Stenn * [Bug 1571] NMEA does not relate data to PPS edge. * [Bug 1572] NMEA time adjustment for GPZDG buggy. * [Bug 1675] Prohibit includefile remote config. --- (4.2.6p3-RC5) 2010/10/22 Released by Harlan Stenn * [Bug 1649] Require NMEA checksum if $GPRMC or previously seen. * [Bug 1669] NTP 4.2.6p2 fails to compile on IBM AIX 5.3. --- (4.2.6p3-RC4) 2010/10/16 Released by Harlan Stenn * [Bug 1584] wrong SNMP type for precision, resolution. * [Bug 1659] Need CLOCK_TRUETIME not CLOCK_TRUE. * [Bug 1665] is_anycast() u_int32_t should be u_int32. * ntpsnmpd, libntpq warning cleanup. --- (4.2.6p3-RC3) 2010/10/14 Released by Harlan Stenn * [Bug 750] Non-existing device causes coredump with RIPE-NCC driver. * [Bug 1080] ntpd on ipv6 routers very chatty. * [Bug 1567] Support Arbiter 1093C Satellite Clock on Windows. * [Bug 1581] printf format string mismatch leftover. * [Bug 1584] ntpsnmpd OID must be mib-2.197. * [Bug 1643] Range-check the decoding of the RIPE-NCC status codes. * [Bug 1644] cvo.sh should use lsb_release to identify linux distros. * [Bug 1659] Support Truetime Satellite Clocks on Windows. * [Bug 1660] On some systems, test is in /usr/bin, not /bin. * [Bug 1661] Re-indent refclock_ripencc.c. --- (4.2.6p3-RC2) 2010/09/25 Released by Harlan Stenn * [Bug 1635] "filegen ... enable" is not default. * [Bug 1636] yyparse() segfault after denied filegen remote config. --- (4.2.6p3-RC1) 2010/09/18 Released by Harlan Stenn * [Bug 1344] ntpd on Windows exits without logging cause. --- (4.2.6p3-beta1) 2010/09/11 Released by Harlan Stenn * [Bug 1573] Miscalculation of offset in sntp. * [Bug 1595] empty last line in key file causes duplicate key to be added * [Bug 1597] packet processing ignores RATE KoD packets, because of a bug in string comparison. * [Bug 1581] ntp_intres.c size_t printf format string mismatch. --- (4.2.6p2) 2010/07/09 Released by Harlan Stenn * [Bug 1581] size_t printf format string mismatches, IRIG string buffers undersized. Mostly backported from earlier ntp-dev fixes by Juergen Perlinger. --- (4.2.6p2-RC7) 2010/06/19 Released by Harlan Stenn * [Bug 1570] serial clock drivers get outdated input from kernel tty line buffer after startup * [Bug 1575] use 'snprintf' with LIB_BUFLENGTH in inttoa.c, tvtoa.c and utvtoa.c * [Bug 1576] sys/sysctl.h depends on sys/param.h on OpenBSD. --- (4.2.6p2-RC6) 2010/06/12 Released by Harlan Stenn * [Bug 715] libisc Linux IPv6 interface iteration drops multicast flags. --- (4.2.6p2-RC5) 2010/06/03 Released by Harlan Stenn * [Bug 1561] ntpq, ntpdc "passwd" prompts for MD5 password w/SHA1. * [Bug 1565] sntp/crypto.c compile fails on MacOS over vsnprintf(). * Windows port: do not exit in ntp_timestamp_from_counter() without first logging the reason. * Support "passwd blah" syntax in ntpq. --- (4.2.6p2-RC4) 2010/05/19 Released by Harlan Stenn * [Bug 1555] 4.2.6p2-RC3 sntp illegal C (mixed code and declarations). --- (4.2.6p2-RC3) 2010/05/11 Released by Harlan Stenn * [Bug 1325] unreachable code in sntp recv_bcst_data(). * [Bug 1459] sntp MD5 authentication does not work with ntpd. * [Bug 1512] ntpsnmpd should connect to net-snmpd via a unix-domain socket by default. Provide a command-line 'socket name' option. * [Bug 1538] update refclock_nmea.c's call to getprotobyname(). * [Bug 1541] Fix wrong keyword for "maxclock". * [Bug 1552] update and complete broadcast and crypto features in sntp. * [Bug 1553] sntp/configure.ac OpenSSL support. * Escape unprintable characters in a refid in ntpq -p billboard. * Simplify hash client code by providing OpenSSL EVP_*() API when built without OpenSSL. (from ntp-dev) * Do not depend on ASCII values for ('A' - '0'), ('a' - '0') in sntp. * Windows compiling hints/winnt.html update from G. Sunil Tej. --- (4.2.6p2-RC2) 2010/04/27 Released by Harlan Stenn * [Bug 1465] Make sure time from TS2100 is not invalid (backport from ntp-dev). * [Bug 1528] Fix EDITLINE_LIBS link order for ntpq and ntpdc. * [Bug 1534] win32/include/isc/net.h conflicts with VC++ 2010 errno.h. * [Bug 1535] "restrict -4 default" and "restrict -6 default" ignored. * Remove --with-arlib from br-flock. --- (4.2.6p2-RC1) 2010/04/18 Released by Harlan Stenn * [Bug 1503] Auto-enabling of monitor for "restrict ... limited" wrong. * [Bug 1504] ntpdate tickles ntpd "discard minimum 1" rate limit if "restrict ... limited" is used. * [Bug 1518] Windows ntpd should lock to one processor more conservatively. * [Bug 1522] Enable range syntax "trustedkey (301 ... 399)". * Update html/authopt.html controlkey, requestkey, and trustedkey docs. --- (4.2.6p1) 2010/04/09 Released by Harlan Stenn (4.2.6p1-RC6) 2010/03/31 Released by Harlan Stenn * [Bug 1514] Typo in ntp_proto.c: fabs(foo < .4) should be fabs(foo) < .4. * [Bug 1464] synchronization source wrong for refclocks ARCRON_MSF (27) and SHM (28). * Correct Windows port's refclock_open() to return 0 on failure not -1. * Correct CHU, dumbclock, and WWVB drivers to check for 0 returned from refclock_open() on failure. * Correct "SIMUL=4 ./flock-build -1" to prioritize -1/--one. --- (4.2.6p1-RC5) 2010/02/09 Released by Harlan Stenn * [Bug 1140] Clean up debug.html, decode.html, and ntpq.html. * [Bug 1438] Remove dead code from sntp/networking.c. * [Bug 1477] 1st non-gmake make in clone w/VPATH can't make COPYRIGHT. * [Bug 1478] linking fails with undefined reference EVP_MD_pkey_type. * [Bug 1479] Compilation fails because of not finding readline headers. * [Bug 1480] snprintf() cleanup caused unterminated refclock IDs. * [Bug 1484] ushort is not defined in QNX6. --- (4.2.6p1-RC4) 2010/02/04 Released by Harlan Stenn * [Bug 1455] ntpd does not try /etc/ntp.audio as documented. * [Bug 1467] Fix bogus rebuild of sntp/sntp.html * [Bug 1470] "make distdir" in $srcdir builds keyword-gen, libntp.a. * [Bug 1473] "make distcheck" before build can't make sntp/version.m4. * [Bug 1474] ntp_keygen needs LCRYPTO after libntp.a. * Convert many sprintf() calls to snprintf(), also strcpy(), strcat(). * Fix widely cut-n-pasted bug in refclock shutdown after failed start. * Remove some dead code checking for emalloc() returning NULL. * Remove arlib. --- (4.2.6p1-RC3) 2010/01/24 Released by Harlan Stenn * Use TZ=UTC instead of TZ= when calling date in scripts/mkver.in . * [Bug 1448] Some macros not correctly conditionally or absolutely defined on Windows. * [Bug 1449] ntpsim.h in ntp_config.c should be used conditionally. * [Bug 1450] Option to exclude warnings not unconditionally defined on Windows. * [Bug 1127] Properly check the return of X590_verify() - missed one. * [Bug 1439] .texi generation must wait until after binary is linked. * [Bug 1440] Update configure.ac to support kfreebsd. * [Bug 1445] IRIX does not have -lcap or support linux capabilities. * [Bug 1451] CID 115: sntp leaks KoD entry when updating existing. * [Bug 1453] Use $CC in config.cache filename in ./build script. --- (4.2.6p1-RC2) 2009/12/25 Released by Harlan Stenn * [Bug 1411] Fix status messages in refclock_oncore.c. * [Bug 1416] MAXDNAME undefined on Solaris 2.6. * [Bug 1419] ntpdate, ntpdc, sntp, ntpd ignore configure --bindir. * [Bug 1424] Fix check for rtattr (rtnetlink.h). * [Bug 1425] unpeer by association ID sets up for duplicate free(). * [Bug 1426] scripts/VersionName needs . on the search path. * [Bug 1427] quote missing in ./build - shows up on NetBSD. * [Bug 1428] Use AC_HEADER_RESOLV to fix breaks from resolv.h * [Bug 1429] ntpd -4 option does not reliably force IPv4 resolution. * [Bug 1431] System headers must come before ntp headers in ntp_intres.c . * [Bug 1434] HP-UX 11 ip_mreq undeclared, _HPUX_SOURCE helps some. * [Bug 1435] sntp: Test for -lresolv using the same tests as in ntp. --- (4.2.6p1-RC1) 2009/12/20 Released by Harlan Stenn * [Bug 1409] Put refclock_neoclock4x.c under the NTP COPYRIGHT notice. This should allow debian and other distros to add this refclock driver in further distro releases. Detect R2 hardware releases. * [Bug 1412] m4/os_cflags.m4 caches results that depend on $CC. * [Bug 1413] test OpenSSL headers regarding -Wno-strict-prototypes. * [Bug 1414] Enable "make distcheck" success with BSD make. * [Bug 1415] Fix Mac OS X link problem. * [Bug 1418] building ntpd/ntpdc/ntpq statically with ssl fails. * Build infrastructure updates to enable beta releases of ntp-stable. --- (4.2.6) 2009/12/09 Released by Harlan Stenn * [Sec 1331] from4.2.4p8: DoS with mode 7 packets - CVE-2009-3563. * [Bug 508] Fixed leap second handling for Windows. (4.2.5p250-RC) 2009/11/30 Released by Harlan Stenn * sntp documentation updates. * [Bug 761] internal resolver does not seem to honor -4/-6 qualifiers * [Bug 1386] Deferred DNS doesn't work on NetBSD * [Bug 1391] avoid invoking autogen twice for .c and .h files. * [Bug 1397] shmget() refclock_shm failing because of file mode. * Pass no_needed to ntp_intres as first part of fixing [Bug 975]. * Add ./configure --enable-force-defer-DNS to help debugging. (4.2.5p249-RC) 2009/11/28 Released by Harlan Stenn * [Bug 1400] An empty KOD DB file causes sntp to coredump. * sntp: documentation cleanup. * sntp: clean up some error messages. * sntp: Use the precision to control how many offset digits are shown. * sntp: Show root dispersion. * Cleanup from the automake/autoconf upgrades. (4.2.5p248-RC) 2009/11/26 Released by Harlan Stenn * Prepare for the generation of sntp.html. * Documentation changes from Dave Mills. * [Bug 1387] Storage leak in ntp_intres (minor). * [Bug 1389] buffer overflow in refclock_oncore.c * [Bug 1391] .texi usage text from installed, not built binaries. * [Bug 1392] intres retries duplicate assocations endlessly. * Correct *-opts.h dependency so default 'get' action isn't used. (4.2.5p247-RC) 2009/11/20 Released by Harlan Stenn * [Bug 1142] nodebug builds shed no light on -d, -D option failure. * [Bug 1179] point out the problem with -i/--jaildir and -u/--user when they are disabled by configure. * [Bug 1308] support systems that lack fork(). * [Bug 1343] sntp doesn't link on Solaris 7, needs -lresolv. (4.2.5p246-RC) 2009/11/17 Released by Harlan Stenn * Upgrade to autogen-5.10 * [Bug 1378] Unnecessary resetting of peers during interface update. * [Bug 1382] p245 configure --disable-dependency-tracking won't build. * [Bug 1384] ntpq :config core dumped with a blank password. (4.2.5p245-RC) 2009/11/14 Released by Harlan Stenn * Cleanup from Dave Mills. * [Bug 1343] sntp illegal C does not compile on Solaris 7. * [Bug 1381] Version .deps generated include file dependencies to allow known dependency-breaking changes to force .deps to be cleaned, triggered by changing the contents of deps-ver and/or sntp/deps-ver. (4.2.5p244-RC) 2009/11/12 Released by Harlan Stenn * keygen.html updates from Dave Mills. * [Bug 1003] ntpdc unconfig command doesn't prompt for keyid. * [Bug 1376] Enable authenticated ntpq and ntpdc using newly-available digest types. * ntp-keygen, Autokey OpenSSL build vs. run version mismatch is now a non-fatal warning. (4.2.5p243-RC) 2009/11/11 Released by Harlan Stenn * [Bug 1226] Fix deferred DNS lookups. * new crypto signature cleanup. (4.2.5p242-RC) 2009/11/10 Released by Harlan Stenn * [Bug 1363] CID 92 clarify fallthrough case in clk_trimtsip.c * [Bug 1366] ioctl(TIOCSCTTY, 0) fails on NetBSD *[0-2].* > 3.99.7. * [Bug 1368] typos in libntp --without-crypto case * [Bug 1371] deferred DNS lookup failing with INFO_ERR_AUTH. * CID 87 dead code in ntpq.c atoascii(). * Fix authenticated ntpdc, broken in p240. * Stub out isc/mem.h, shaving 47k from a MIPS ntpd binary. * Shrink keyword scanner FSM entries from 64 to 32 bits apiece. * Documention updates from Dave Mills. * authkeys.c cleanup from Dave Mills. (4.2.5p241-RC) 2009/11/07 Released by Harlan Stenn * html/authopt.html update from Dave Mills. * Remove unused file from sntp/Makefile.am's distribution list. * new crypto signature cleanup. (4.2.5p240-RC) 2009/11/05 Released by Harlan Stenn * [Bug 1364] clock_gettime() not detected, need -lrt on Debian 5.0.3. * Provide all of OpenSSL's signature methods for ntp.keys (FIPS 140-2). (4.2.5p239-RC) 2009/10/30 Released by Harlan Stenn * [Bug 1357] bogus assert from refclock_shm. * [Bug 1359] Debug message cleanup. * CID 101: more pointer/array cleanup. * [Bug 1356] core dump from refclock_nmea when can't open /dev/gpsU. * [Bug 1358] AIX 4.3 sntp/networking.c IPV6_JOIN_GROUP undeclared. * CID 101: pointer/array cleanup. (4.2.5p238-RC) 2009/10/27 Released by Harlan Stenn * Changes from Dave Mills. * driver4.html updates from Dave Mills. * [Bug 1252] PPSAPI cleanup on ntpd/refclock_wwvb.c. * [Bug 1354] libtool error building after bootstrap with Autoconf 2.64. * Allow NTP_VPATH_HACK configure test to handle newer gmake versions. * CIDs 94-99 make it more clearly impossible for sock_hash() to return a negative number. * CID 105, 106 ensure ntpdc arrays are not overrun even if callers misbehave. * CID 113 use va_end() in refclock_true.c true_debug(). * Get rid of configure tests for __ss_family and __ss_len when the more common ss_family and ss_len are present. (4.2.5p237-RC) 2009/10/26 Released by Harlan Stenn * [Bug 610] NMEA support for using PPSAPI on a different device. * [Bug 1238] use only fudge time2 to offset NMEA serial timestamp. * [Bug 1355] ntp-dev won't compile on OpenBSD 4.6. (4.2.5p236-RC) 2009/10/22 Released by Harlan Stenn * Cleanup from Dave Mills. * [Bug 1343] ntpd/ntp_io.c close_fd() does not compile on Solaris 7. * [Bug 1353] ntpq "rv 0 settimeofday" always shows UNKNOWN on unix. * Do not attempt to execute built binaries from ntpd/Makefile when cross-compiling (keyword-gen and ntpd --saveconfigquit). * sntp/main.c: Remove duplicate global adr_buf[] (also defined in networking.c) which Piotr Grudzinski identified breaking his build. * Correct in6addr_any test in configure.ac to attempt link too. (4.2.5p235-RC) 2009/10/18 Released by Harlan Stenn * [Bug 1343] lib/isc build breaks on systems without IPv6 headers. (4.2.5p234-RC) 2009/10/16 Released by Harlan Stenn * [Bug 1339] redux, use unmodified lib/isc/win32/strerror.c and move #define strerror... to a header not used by lib/isc code. * [Bug 1345] illegal 'grep' option prevents compilation. * [Bug 1346] keyword scanner broken where char defaults to unsigned. * [Bug 1347] ntpd/complete.conf missing multicastclient test case. (4.2.5p233-RC) 2009/10/15 Released by Harlan Stenn * [Bug 1337] cast setsockopt() v4 address pointer to void *. * [Bug 1342] ignore|drop one IPv6 address on an interface blocks all addresses on that interface. * Documentation cleanup and updates. (4.2.5p232-RC) 2009/10/14 Released by Harlan Stenn * [Bug 1302] OpenSSL under Windows needs applink support. * [Bug 1337] fix incorrect args to setsockopt(fd, IP_MULTICAST_IF,...). * [Bug 1339] Fix Windows-only ntp_strerror() infinite recursion. * [Bug 1341] NMEA driver requires working PPSAPI #ifdef HAVE_PPSAPI. * Construct ntpd keyword scanner finite state machine at compile time rather than at runtime, shrink entries from 40+ to 8 bytes. * Update documentation for ntpq --old-rv, saveconfig, saveconfigdir, ntpd -I -L and -M, and interface/nic rules. (From Dave Hart) * [Bug 1337] fix incorrect args to setsockopt(fd, IP_MULTICAST_IF,...) (4.2.5p231-RC) 2009/10/10 Released by Harlan Stenn * [Bug 1335] Broadcast client degraded by wildcard default change. (4.2.5p230-RC) 2009/10/09 Released by Harlan Stenn * Start the 4.2.6 Release Candidate cycle. * Broadcast and transit phase cleanup from Dave Mills. (4.2.5p229) 2009/10/07 Released by Harlan Stenn * [Bug 1334] ntpsnmpd undefined reference to `ntpqOptions'. * Change ntpsnmpd/Makefile.am include file order to fix FreeBSD build. (4.2.5p228) 2009/10/06 Released by Harlan Stenn * Reclaim syntax tree memory after application in ntpd built with configure --disable-saveconfig. * [Bug 1135] ntpq uses sizeof(u_long) where sizeof(u_int32) is meant. * [Bug 1333] ntpd --interface precedence over --novirtualips lost. (4.2.5p227) 2009/10/05 Released by Harlan Stenn * [Bug 1135] :config fails with "Server disallowed request" * [Bug 1330] disallow interface/nic rules when --novirtualips or --interface are used. * [Bug 1332] ntpq -c 'rv 0 variablename' returns extra stuff. * Add test of ntpd --saveconfigquit fidelity using new complete.conf. * Documentation updates from Dave Hart/Dave Mills. (4.2.5p226) 2009/10/04 Released by Harlan Stenn * [Bug 1318] Allow multiple -g options on ntpd command line. * [Bug 1327] ntpq, ntpdc, ntp-keygen -d & -D should work with configure --disable-debugging. * Add ntpd --saveconfigquit option for future build-time testing of saveconfig fidelity. * Clockhop and autokey cleanup from Dave Mills. * Documentation updates from Dave Mills. (4.2.5p225) 2009/09/30 Released by Harlan Stenn * authopt documentation changes from Dave Mills/Dave Hart. * [Bug 1324] support bracketed IPv6 numeric addresses for restrict. (4.2.5p224) 2009/09/29 Released by Harlan Stenn * Clockhop and documentation fixes from Dave Mills. * Remove "tos maxhop" ntp.conf knob. (4.2.5p223) 2009/09/28 Released by Harlan Stenn * [Bug 1321] build doesn't work if . isn't on $PATH. * [Bug 1323] Implement "revoke #" to match documentation, deprecate "crypto revoke #". (4.2.5p222) 2009/09/27 Released by Harlan Stenn * Update libisc code using bind-9.6.1-P1.tar.gz, rearrange our copy to mirror the upstream layout (lib/isc/...), and merge in NTP-local modifications to libisc. There is a new procedure to ease future libisc merges using a separate "upstream" bk repo. That will enable normal bk pull automerge to handle carrying forward any local changes and should enable us to take updated libisc snapshots more often. * Updated build and flock-build scripts. flock-build --one is a way to perform a flock-build compatible solitary build, handy for a repo clone's first build on a machine with autoconf, automake, etc. * Compiling ntp_parser.y using BSD make correctly places ntp_parser.h in the top-level ntpd directory instead of A.*/ntpd. * bootstrap script updated to remove potentially stale .deps dirs. * Remove unneeded Makefile.am files from the lib/isc/include tree. (4.2.5p221) 2009/09/26 Released by Harlan Stenn * [Bug 1316] segfault if refclock_nmea can't open file. * [Bug 1317] Distribute cvo.sh. (4.2.5p220) 2009/09/25 Released by Harlan Stenn * Rearrange libisc code to match the upstream layout in BIND. This is step one of two, changing the layout but keeping our existing libisc. (4.2.5p219) 2009/09/24 Released by Harlan Stenn * [Bug 1315] "interface ignore 0.0.0.0" is ignored. * add implicit "nic ignore all" rule before any rules from ntp.conf, so "nic listen eth0" alone means the same as "-I eth0". * add wildcard match class for interface/nic rules. * fix mistaken carryover of prefixlen from one rule to the next. * Ensure IPv6 localhost address ::1 is included in libisc's Windows IPv6 address enumeration, allowing ntpq and ntpdc's hardcoding to 127.0.0.1 on Windows to end. (4.2.5p218) 2009/09/21 Released by Harlan Stenn * [Bug 1314] saveconfig emits -4 and -6 on when not given. * correct parsing and processing of setvar directive. * highlight location of ntpq :config syntax errors with ^. * clarify (former) NO_ARG, SINGLE_ARG, MULTIPLE_ARG renaming to FOLLBY_TOKEN, FOLLBY_STRING, FOLLBY_STRINGS_TO_EOC. * parser, saveconfig cleanup to store T_ identifiers in syntax tree. (4.2.5p217) 2009/09/20 Released by Harlan Stenn * [Bug 1300] reject remote configuration of dangerous items. (4.2.5p216) 2009/09/19 Released by Harlan Stenn * [Bug 1312] ntpq/ntpdc MD5 passwords truncated to 8 chars on Suns. * CID 10 missing free(up); in refclock_palisade.c error return, again. * CID 83 added assertion to demonstrate config_nic_rules() does not call strchr(NULL, '/'). (4.2.5p215) 2009/09/18 Released by Harlan Stenn * [Bug 1292] Workaround last VC6 unsigned __int64 kink. (4.2.5p214) 2009/09/17 Released by Harlan Stenn * [Bug 1303] remove top-level "autokey" directive. * use "nic listen 192.168.0.0/16" instead of "nic listen 192.168.0.0 prefixlen 16". (4.2.5p213) 2009/09/16 Released by Harlan Stenn * [Bug 1310] fix Thunderbolt mode in refclock_palisade.c (4.2.5p212) 2009/09/15 Released by Harlan Stenn * [Bug 983] add interface [listen | ignore | drop] ... directive. * [Bug 1243] MD5auth_setkey zero-fills key from first zero octet. * [Bug 1295] leftover fix, do not crash on exit in free_config_trap() when "trap 1.2.3.4" is used without any further options. * [Bug 1311] 4.2.5p211 doesn't build in no-debug mode. * document interface (alias nic) and unpeer. * Correct syntax error line & column numbers. * CID 79: kod_init_kod_db() fails to fclose(db_s) in two error paths. * CID 80: attempt to quiet Coverity false positive re: leaking "reason" in main(). * Documentation updates from Dave Mills. * CID 81: savedconfig leaked in save_config(). * Make the code agree with the spec and the book (Dave Mills). (4.2.5p211) 2009/09/14 Released by Harlan Stenn * [Bug 663] respect ntpq -c and -p order on command line. * [Bug 1292] more VC6 unsigned __int64 workarounds. * [Bug 1296] Added Support for Trimble Acutime Gold. (4.2.5p210) 2009/09/06 Released by Harlan Stenn * [Bug 1294] Use OPENSSL_INC and OPENSSL_LIB macros for Windows and remove unnecessary reference to applink.c for Windows * [Bug 1295] trap directive options are not optional. * [Bug 1297] yylex() must always set yylval before returning. (4.2.5p209) 2009/09/01 Released by Harlan Stenn * [Bug 1290] Fix to use GETTIMEOFDAY macro * [Bug 1289] Update project files for VC6, VS2003, VS2005, VS 2008 (4.2.5p208) 2009/08/30 Released by Harlan Stenn * [Bug 1293] make configuration dumper ready for release, specifically: * rename ntpq dumpcfg command to "saveconfig". * require authentication for saveconfig. * "restrict ... nomodify" prevents saveconfig and :config. * "saveconfig ." shorthand to save to startup configuration file. * support strftime() substitution in saveconfig arg to timestamp the output filename, for example "saveconfig %Y%m%d-%H%M%S.conf". * display saveconfig response message from ntpd in ntpq. * save output filename in "savedconfig" variable, fetched with ntpq -c "rv 0 savedconfig". * document saveconfig in html/ntpq.html. * add ./configure --disable-saveconfig to build a smaller ntpd. * log saveconfig failures and successes to syslog. (4.2.5p207) 2009/08/29 Released by Harlan Stenn * [Bug 1292] Minor Windows source tweaks for VC6-era SDK headers. (4.2.5p206) 2009/08/26 Released by Harlan Stenn * accopt.html typo fixes from Dave Mills. * [Bug 1283] default to remembering KoD in sntp. * clean up numerous sntp/kod_management.c bugs. * use all addresses resolved from each DNS name in sntp. (4.2.5p205) 2009/08/18 Released by Harlan Stenn * accopt.html typo fixes from Dave Mills. * [Bug 1285] Log ntpq :config/config-from-file events. * [Bug 1286] dumpcfg omits statsdir, mangles filegen. (4.2.5p204) 2009/08/17 Released by Harlan Stenn * [Bug 1284] infinite loop in ntpd dumping more than one trustedkey (4.2.5p203) 2009/08/16 Released by Harlan Stenn * Add ntpq -c dumpcfg, Google Summer of Code project of Max Kuehn (4.2.5p202) 2009/08/14 Released by Harlan Stenn * install the binary and man page for sntp. (4.2.5p201) 2009/08/13 Released by Harlan Stenn * sntp: out with the old, in with the new. (4.2.5p200) 2009/08/12 Released by Harlan Stenn * [Bug 1281] Build ntpd on Windows without big SDK download, burn, and install by checking in essentially unchanging messages.mc build products to avoid requiring mc.exe, which is not included with VC++ 2008 EE. (4.2.5p199) 2009/08/09 Released by Harlan Stenn * [Bug 1279] Cleanup for warnings from Veracode static analysis. (4.2.5p198) 2009/08/03 Released by Harlan Stenn * Upgrade to autogen-5.9.9-pre5. (4.2.5p197) 2009/07/30 Released by Harlan Stenn * The build script now has . at the end of PATH for config.guess. (4.2.5p196) 2009/07/29 Released by Harlan Stenn * [Bug 1272] gsoc_sntp IPv6 build problems under HP-UX 10. * [Bug 1273] CID 10: Palisade leaks unit struct in error path. * [Bug 1274] CID 67: ensure resolve_hosts() output count and pointers are consistent. * [Bug 1275] CID 45: CID 46: old sntp uses uninitialized guesses[0], precs[0]. * [Bug 1276] CID 52: crypto_xmit() may call crypto_alice[23]() with NULL peer. (4.2.5p195) 2009/07/27 Released by Harlan Stenn * cvo.sh: Add support for CentOS, Fedora, Slackware, SuSE, and QNX. (4.2.5p194) 2009/07/26 Released by Harlan Stenn * Documentation updates from Dave Mills. * Use scripts/cvo.sh in the build script to get better subdir names. (4.2.5p193) 2009/07/25 Released by Harlan Stenn * [Bug 1261] CID 34: simulate_server() rbuf.msg_flags uninitialized. * [Bug 1262] CID 35: xpkt.mac uninitialized in simulate_server(). * [Bug 1263] CID 37: CID 38: CID 40: CID 43: multiple refclocks uninitialized tm_zone (arc, chronolog, dumbclock, pcf). * [Bug 1264] CID 64: gsoc_sntp on_wire() frees wrong ptr receiving KoD. * [Bug 1265] CID 65: CID 66: gsoc_sntp on_wire() leaks x_pkt, r_pkt. * [Bug 1266] CID 39: datum_pts_start() uninitialized arg.c_ospeed. * [Bug 1267] CID 44: old sntp handle_saving() writes stack garbage to file when clearing. * [Bug 1268] CID 63: resolve_hosts() leaks error message buffer. * [Bug 1269] CID 74: use assertion to ensure move_fd() does not return negative descriptors. * [Bug 1270] CID 70: gsoc_sntp recv_bcst_data mdevadr.ipv6mr_interface uninitialized. (4.2.5p192) 2009/07/24 Released by Harlan Stenn * [Bug 965] CID 42: ss_family uninitialized. * [Bug 1250] CID 53: kod_init_kod_db() overruns kod_db malloc'd buffer. * [Bug 1251] CID 68: search_entry() mishandles dst argument. * [Bug 1252] CID 32: Quiet Coverity warning with assertion. * [Bug 1253] CID 50: gsoc_sntp/crypto.c auth_init() always returns a list with one entry. * [Bug 1254] CID 56: tv_to_str() leaks a struct tm each call. * [Bug 1255] CID 55: pkt_output() leaks a copy of each packet. * [Bug 1256] CID 51: Coverity doesn't recognize our assertion macros as terminal. * [Bug 1257] CID 57: gsoc_sntp auth_init() fails to fclose(keyfile). * [Bug 1258] CID 54: gsoc_sntp resolve_hosts() needs simplification. * [Bug 1259] CID 59: gsoc_sntp recv_bcast_data() fails to free(rdata) on error paths. * [Bug 1260] CID 60: gsoc_sntp recvpkt() fails to free(rdata). * Updated to AutoGen-5.9.9pre2. (4.2.5p191) 2009/07/21 Released by Harlan Stenn * Updated to AutoGen-5.9.9pre1. (4.2.5p190) 2009/07/20 Released by Harlan Stenn * Updated to AutoGen-5.9.8. * [Bug 1248] RES_MSSNTP typo in ntp_proto.c. * [Bug 1246] use a common template for singly-linked lists, convert most doubly-linked lists to singly-linked. * Log warning about signd blocking when restrict mssntp used. (4.2.5p189) 2009/07/16 Released by Harlan Stenn * Documentation cleanup from Dave Mills. (4.2.5p188) 2009/07/15 Released by Harlan Stenn * [Bug 1245] Broken xmt time sent in fast_xmit() of 4.2.5p187. (4.2.5p187) 2009/07/11 Released by Harlan Stenn * [Bug 1042] multicast listeners IPv4+6 ignore new interfaces. * [Bug 1237] Windows serial code treat CR and LF both as line terminators. * [Bug 1238] use fudge time2 for serial timecode offset in NMEA driver. * [Bug 1242] Remove --enable-wintime, symmetric workaround is now always enabled. * [Bug 1244] NTP_INSIST(fd != maxactivefd) failure in intres child * Added restrict keyword "mssntp" for Samba4 DC operation, by Dave Mills. (4.2.5p186) 2009/07/08 Released by Harlan Stenn * ntp_proto.c cleanup from Dave Mills. (4.2.5p185) 2009/07/01 Released by Harlan Stenn * Documentation updates from Dave Mills. * [Bug 1234] convert NMEA driver to use common PPSAPI code. * timepps-Solaris.h pps_handle_t changed from pointer to scalar * Spectracom refclock added to Windows port of ntpd * [Bug 1236] Declaration order fixed. * Bracket private ONCORE debug statements with #if 0 rather than #ifdef DEBUG * Delete ONCORE debug statement that is now handled elsewhere. (4.2.5p184) 2009/06/24 Released by Harlan Stenn * [Bug 1233] atom refclock fudge time1 sign flipped in 4.2.5p164. (4.2.5p183) 2009/06/23 Released by Harlan Stenn * [Bug 1196] setsockopt(SO_EXCLUSIVEADDRUSE) can fail on Windows 2000 and earlier with WSAINVAL, do not log a complaint in that case. * [Bug 1210] ONCORE driver terminates ntpd without logging a reason. * [Bug 1218] Correct comment in refclock_oncore on /etc/ntp.oncore* configuration file search order. * Change ONCORE driver to log using msyslog as well as to any clockstats file. * [Bug 1231] ntpsnmpd build fails after sockaddr union changes. (4.2.5p182) 2009/06/18 Released by Harlan Stenn * Add missing header dependencies to the ntpdc layout verification. * prefer.html updates from Dave Mills. * [Bug 1205] Add ntpd --usepcc and --pccfreq options on Windows * [Bug 1215] unpeer by association ID * [Bug 1225] Broadcast address miscalculated on Windows 4.2.5p180 * [Bug 1229] autokey segfaults in cert_install(). * Use a union for structs sockaddr, sockaddr_storage, sockaddr_in, and sockaddr_in6 to remove casts and enable type checking. Collapse some previously separate IPv4/IPv6 paths into a single codepath. (4.2.5p181) 2009/06/06 Released by Harlan Stenn * [Bug 1206] Required compiler changes for Windows * [Bug 1084] PPSAPI for ntpd on Windows with DLL backends * [Bug 1204] Unix-style refclock device paths on Windows * [Bug 1205] partial fix, disable RDTSC use by default on Windows * [Bug 1208] decodenetnum() buffer overrun on [ with no ] * [Bug 1211] keysdir free()d twice #ifdef DEBUG * Enable ONCORE, ARCRON refclocks on Windows (untested) (4.2.5p180) 2009/05/29 Released by Harlan Stenn * [Bug 1200] Enable IPv6 in Windows port * Lose FLAG_FIXPOLL, from Dave Mills. (4.2.5p179) 2009/05/23 Released by Harlan Stenn * [Bug 1041] xmt -> aorg timestamp cleanup from Dave Mills, reported by Dave Hart. * [Bug 1193] Compile error: conflicting types for emalloc. * [Bug 1196] VC6 winsock2.h does not define SO_EXCLUSIVEADDRUSE. * Leap/expire cleanup from Dave Mills. (4.2.5p178) 2009/05/21 Released by Harlan Stenn * Provide erealloc() and estrdup(), a la emalloc(). * Improve ntp.conf's parser error messages. * [Bug 320] "restrict default ignore" does not affect IPv6. * [Bug 1192] "restrict -6 ..." reports a syntax error. (4.2.5p177) 2009/05/18 Released by Harlan Stenn * Include 4.2.4p7 * [Bug 1174] nmea_shutdown assumes that nmea has a unit assigned * [Bug 1190] NMEA refclock fudge flag4 1 obscures position in timecode * Update NMEA refclock documentation in html/drivers/driver20.html (4.2.5p176) 2009/05/13 Released by Harlan Stenn * [Bug 1154] mDNS registration should be done later, repeatedly and only if asked for. (second try for fix) (4.2.5p175) 2009/05/12 Released by Harlan Stenn * Include 4.2.4p7-RC7 * [Bug 1180] ntpd won't start with more than ~1000 interfaces * [Bug 1182] Documentation typos and missing bits. * [Bug 1183] COM port support should extend past COM3 * [Bug 1184] ntpd is deaf when restricted to second IP on the same net * Clean up configure.ac NTP_CACHEVERSION interface, display cache version when clearing. Fixes a regression. (4.2.5p174) 2009/05/09 Released by Harlan Stenn * Stale leapsecond file fixes from Dave Mills. (4.2.5p173) 2009/05/08 Released by Harlan Stenn * Include 4.2.4p7-RC6 (4.2.5p172) 2009/05/06 Released by Harlan Stenn * [Bug 1175] Instability in PLL daemon mode. * [Bug 1176] refclock_parse.c does not compile without PPSAPI. (4.2.5p171) 2009/05/04 Released by Harlan Stenn * Autokey documentation cleanup from Dave Mills. * [Bug 1171] line editing libs found without headers (Solaris 11) * [Bug 1173] NMEA refclock fails with Solaris PPSAPI * Fix problem linking msntp on Solaris when sntp subdir is configured before parent caused by different gethostent library search order. * Do not clear config.cache when it is empty. (4.2.5p170) 2009/05/02 Released by Harlan Stenn * [Bug 1152] adjust PARSE to new refclock_pps logic * Include 4.2.4p7-RC5 * loopfilter FLL/PLL crossover cleanup from Dave Mills. * Documentation updates from Dave Mills. * ntp-keygen cleanup from Dave Mills. * crypto API cleanup from Dave Mills. * Add NTP_CACHEVERSION mechanism to ignore incompatible config.cache * Enable gcc -Wstrict-overflow for gsoc_sntp as well (4.2.5p169) 2009/04/30 Released by Harlan Stenn * [Bug 1171] Note that we never look for -lreadline by default. * [Bug 1090] Fix bogus leap seconds in refclock_hpgps. (4.2.5p168) 2009/04/29 Released by Harlan Stenn * Include 4.2.4p7-RC4 * [Bug 1169] quiet compiler warnings * Re-enable gcc -Wstrict-prototypes when not building with OpenSSL * Enable gcc -Wstrict-overflow * ntpq/ntpdc emit newline after accepting password on Windows * Updates from Dave Mills: * ntp-keygen.c: Updates. * Fix the error return and syslog function ID in refclock_{param,ppsapi}. * Make sure syspoll is within the peer's minpoll/maxpoll bounds. * ntp_crypto.c: Use sign_siglen, not len. sign key filename cleanup. * Bump NTP_MAXEXTEN from 1024 to 2048, update values for some field lengths. * m4/ntp_lineeditlibs.m4: fix warnings from newer Autoconf * [Bug 1166] Remove truncation of position (blanking) code in refclock_nmea.c (4.2.5p167) 2009/04/26 Released by Harlan Stenn * Crypto cleanup from Dave Mills. (4.2.5p166) 2009/04/25 Released by Harlan Stenn * [Bug 1165] Clean up small memory leaks in the config file parser * Correct logconfig keyword declaration to MULTIPLE_ARG * Enable filename and line number leak reporting on Windows when built DEBUG for all the typical C runtime allocators such as calloc, malloc, and strdup. Previously only emalloc calls were covered. * Add DEBUG-only code to free dynamically allocated memory that would otherwise remain allocated at ntpd exit, to allow less forgivable leaks to stand out in leaks reported after exit. * Ensure termination of strings in ports/winnt/libisc/isc_strerror.c and quiet compiler warnings. * [Bug 1057] ntpdc unconfig failure * [Bug 1161] unpeer AKA unconfig command for ntpq :config * PPS and crypto cleanup in ntp_proto.c from Dave Mills. (4.2.5p165) 2009/04/23 Released by Harlan Stenn * WWVB refclock cleanup from Dave Mills. * Code cleanup: requested_key -> request_key. * [Bug 833] ignore whitespace at end of remote configuration lines * [Bug 1033] ntpdc/ntpq crash prompting for keyid on Windows * [Bug 1028] Support for W32Time authentication via Samba. * quiet ntp_parser.c malloc redeclaration warning * Mitigation and PPS/PPSAPI cleanup from Dave Mills. * Documentation updates from Dave Mills. * timepps-Solaris.h patches from Dave Hart. (4.2.5p164) 2009/04/22 Released by Harlan Stenn * Include 4.2.4p7-RC3 * PPS/PPSAPI cleanup from Dave Mills. * Documentation updates from Dave Mills. * [Bug 1125] C runtime per-thread initialization on Windows * [Bug 1152] temporarily disable refclock_parse, refclock_true until maintainers can repair build break from pps_sample() * [Bug 1153] refclock_nmea should not mix UTC with GPS time * [Bug 1159] ntpq overlap diagnostic message test buggy (4.2.5p163) 2009/04/10 Released by Harlan Stenn (4.2.5p162) 2009/04/09 Released by Harlan Stenn * Documentation updates from Dave Mills. * Mitigation and PPS cleanup from Dave Mills. * Include 4.2.4p7-RC2 * [Bug 216] New interpolation scheme for Windows eliminates 1ms jitter * remove a bunch of #ifdef SYS_WINNT from portable code * 64-bit time_t cleanup for building on newer Windows compilers * Only set CMOS clock during ntpd exit on Windows if the computer is shutting down or restarting. * [Bug 1148] NMEA reference clock improvements * remove deleted gsoc_sntp/utilities.o from repository so that .o build products can be cleaned up without corrupting the repository. (4.2.5p161) 2009/03/31 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.5p160) 2009/03/30 Released by Harlan Stenn * [Bug 1141] refclock_report missing braces cause spurious "peer event: clock clk_unspec" log entries * Include 4.2.4p7-RC1 (4.2.5p159) 2009/03/28 Released by Harlan Stenn * "bias" changes from Dave Mills. (4.2.5p158) 2009/01/30 Released by Harlan Stenn * Fix [CID 72], a typo introduced at the latest fix to prettydate.c. (4.2.5p157) 2009/01/26 Released by Harlan Stenn * Cleanup/fixes for ntp_proto.c and ntp_crypto.c from Dave Mills. (4.2.5p156) 2009/01/19 Released by Harlan Stenn * [Bug 1118] Fixed sign extension for 32 bit time_t in caljulian() and prettydate(). Fixed some compiler warnings about missing prototypes. Fixed some other simple compiler warnings. * [Bug 1119] [CID 52] Avoid a possible null-dereference in ntp_crypto.c. * [Bug 1120] [CID 51] INSIST that peer is non-null before we dereference it. * [Bug 1121] [CID 47] double fclose() in ntp-keygen.c. (4.2.5p155) 2009/01/18 Released by Harlan Stenn * Documentation updates from Dave Mills. * CHU frequency updates. * Design assertion fixes for ntp_crypto.c from Dave Mills. (4.2.5p154) 2009/01/13 Released by Harlan Stenn * [Bug 992] support interface event change on Linux from Miroslav Lichvar. (4.2.5p153) 2009/01/09 Released by Harlan Stenn * Renamed gsoc_sntp/:fetch-stubs to gsoc_sntp/fetch-stubs to avoid file name problems under Windows. Removed German umlaut from log msg for 4.2.5p142. (4.2.5p152) 2009/01/08 Released by Harlan Stenn * Include 4.2.4p6: 2009/01/08 Released by Harlan Stenn (4.2.5p151) 2008/12/23 Released by Harlan Stenn * Stats file logging cleanup from Dave Mills. (4.2.5p150) 2008/12/15 Released by Harlan Stenn * [Bug 1099] Fixed wrong behaviour in sntp's crypto.c. * [Bug 1103] Fix 64-bit issues in the new calendar code. (4.2.5p149) 2008/12/05 Released by Harlan Stenn * Fixed mismatches in data types and OID definitions in ntpSnmpSubAgent.c * added a premliminary MIB file to ntpsnmpd (ntpv4-mib.mib) (4.2.5p148) 2008/12/04 Released by Harlan Stenn * [Bug 1070] Fix use of ntpq_parsestring() in ntpsnmpd. (4.2.5p147) 2008/11/27 Released by Harlan Stenn * Update gsoc_sntp's GCC warning code. (4.2.5p146) 2008/11/26 Released by Harlan Stenn * Update Solaris CFLAGS for gsoc_sntp. (4.2.5p145) 2008/11/20 Released by Harlan Stenn * Deal with time.h for sntp under linux. * Provide rpl_malloc() for sntp for systems that need it. * Handle ss_len and socklen type for sntp. * Fixes to the sntp configure.ac script. * Provide INET6_ADDRSTRLEN if it is missing. * [Bug 1095] overflow in caljulian.c. (4.2.5p144) 2008/11/19 Released by Harlan Stenn * Use int32, not int32_t. * Avoid the sched*() functions under OSF - link problems. (4.2.5p143) 2008/11/17 Released by Harlan Stenn * sntp cleanup and fixes. (4.2.5p142) 2008/11/16 Released by Harlan Stenn * Imported GSoC SNTP code from Johannes Maximilian Kuehn. (4.2.5p141) 2008/11/13 Released by Harlan Stenn * New caltontp.c and calyearstart.c from Juergen Perlinger. (4.2.5p140) 2008/11/12 Released by Harlan Stenn * Cleanup lint from the ntp_scanner files. * [Bug 1011] gmtime() returns NULL on windows where it would not under Unix. * Updated caljulian.c and prettydate.c from Juergen Perlinger. (4.2.5p139) 2008/11/11 Released by Harlan Stenn * Typo fix to driver20.html. (4.2.5p138) 2008/11/10 Released by Harlan Stenn * [Bug 474] --disable-ipv6 is broken. * IPv6 interfaces were being looked for twice. * SHM driver grabs more samples, add clockstats * decode.html and driver20.html updates from Dave Mills. (4.2.5p137) 2008/11/01 Released by Harlan Stenn * [Bug 1069] #undef netsnmp's PACKAGE_* macros. * [Bug 1068] Older versions of netsnmp do not have netsnmp_daemonize(). (4.2.5p136) 2008/10/27 Released by Harlan Stenn * [Bug 1078] statsdir configuration parsing is broken. (4.2.5p135) 2008/09/23 Released by Harlan Stenn * [Bug 1072] clock_update should not allow updates older than sys_epoch. (4.2.5p134) 2008/09/17 Released by Harlan Stenn * Clean up build process for ntpsnmpd. (4.2.5p133) 2008/09/16 Released by Harlan Stenn * Add options processing to ntpsnmpd. * [Bug 1062] Check net-snmp headers before deciding to build ntpsnmpd. * Clean up the libntpq.a build. * Regenerate ntp_parser.[ch] from ntp_parser.y (4.2.5p132) 2008/09/15 Released by Harlan Stenn * [Bug 1067] Multicast DNS service registration must come after the fork on Solaris. * [Bug 1066] Error messages should log as errors. (4.2.5p131) 2008/09/14 Released by Harlan Stenn * [Bug 1065] Re-enable support for the timingstats file. (4.2.5p130) 2008/09/13 Released by Harlan Stenn * [Bug 1064] Implement --with-net-snmp-config=progname * [Bug 1063] ntpSnmpSubagentObject.h is missing from the distribution. (4.2.5p129) 2008/09/11 Released by Harlan Stenn * Quiet some libntpq-related warnings. (4.2.5p128) 2008/09/08 Released by Harlan Stenn * Import Heiko Gerstung's GSoC2008 NTP MIB daemon. (4.2.5p127) 2008/09/01 Released by Harlan Stenn * Regenerate ntpd/ntp_parser.c (4.2.5p126) 2008/08/31 Released by Harlan Stenn * Stop libtool-1.5 from looking for C++ or Fortran. * [BUG 610] Documentation update for NMEA reference clock driver. * [Bug 828] Fix IPv4/IPv6 address parsing. * Changes from Dave Mills: Documentation updates. Fix a corner case where a frequency update was reported but not set. When LEAP_NOTINSYNC->LEAP_NOWARNING, call crypto_update() if we have crypto_flags. (4.2.5p125) 2008/08/18 Released by Harlan Stenn * [Bug 1052] Add linuxPPS support to ONCORE driver. (4.2.5p124) 2008/08/17 Released by Harlan Stenn * Documentation updates from Dave Mills. * Include 4.2.4p5: 2008/08/17 Released by Harlan Stenn * [Bug 861] leap info was not being transmitted. * [Bug 1046] refnumtoa0.c is using the wrong header file. * [Bug 1047] enable/disable options processing fix. * header file cleanup. * [Bug 1037] buffer in subroutine was 1 byte short. * configure.ac: cleanup, add option for wintime, and lay the groundwork for the changes needed for bug 1028. * Fixes from Dave Mills: 'bias' and 'interleave' work. Separate phase and frequency discipline (for long poll intervals). Update TAI function to match current leapsecond processing. * Documentation updates from Dave Mills. * [Bug 1037] Use all 16 of the MD5 passwords generated by ntp-keygen. * Fixed the incorrect edge parameter being passed to time_pps_kcbind in NMEA refclock driver. * [Bug 399] NMEA refclock driver does not honor time1 offset if flag3 set. * [Bug 985] Modifications to NMEA reference clock driver to support Accord GPS Clock. * poll time updates from Dave Mills. * local refclock documentation updates from Dave Mills. * [Bug 1022] Fix compilation problems with yesterday's commit. * Updates and cleanup from Dave Mills: I've now spent eleven months of a sabbatical year - 7 days a week, 6-10 hours most days - working on NTP. I have carefully reviewed every major algorithm, examined its original design and evolution from that design. I've trimmed off dead code and briar patches and did zillions of tests contrived to expose evil vulnerabilities. The development article is in rather good shape and should be ready for prime time. 1. The protostats statistics files have been very useful in exposing little twitches and turns when something hiccups, like a broken PPS signal. Most of what used to be syslog messages are now repackaged as protostats messages with optional syslog as well. These can also be sent as traps which might be handy to tiggle a beeper or celltext. These, the sysstats files and cryptostats files reveal the ambient health of a busy server, monitor traffic and error counts and spot crypto attacks. 2. Close inspection of the clock discipline behavior at long poll intervals (36 h) showed it not doing as well as it should. I redesigned the FLL loop to improve nominal accuracy from several tens of milliseconds to something less than ten milliseconds. 3. Autokey (again). The enhanced error checking was becoming a major pain. I found a way to toss out gobs of ugly fat code and replace the function with a much simpler and more comprehensive scheme. It resists bait-and-switch attacks and quickly detect cases when the protocol is not correctly synchronized. 4. The interface code for the kernel PPS signal was not in sync with the kernel code itself. Some error checks were duplicated and some ineffective. I found none of the PPS-capable drivers, including the atom driver, do anything when the prefer peer fails; the kernel PPS signal remains in control. The atom driver now disables the kernel PPS when the prefer peer comes bum. This is important when the prefer peer is not a reference clock but a remote NTP server. 5. The flake restrict bit turned out to be really interesting, especially with symmtric modes and of those especially those using Autokey. Small changes in the recovery procedures when packets are lost now avoid almost all scenarios which previously required protocol resets. 6. I've always been a little uncomfortable when using the clock filter with long poll intervals because the samples become less and less correlated as the sample age exceeds the Allan intercept. Various schemes have been used over the years to cope with this fact. The latest one and the one that works the best is to use a modified sort metric where the delay is used when the age of the sample is less than the intercept and the sum of delay and dispersion above that. The net result is that, at small poll intervals the algorithm operates as a minimum filter, while at larger poll intervals it morphs to FIFO. Left unmodified, a sample could be used when twelve days old. This along with the FLL modifications has made a dramatic improvement at large poll intervals. - [Backward Incompatible] The 'state' variable is no longer reported or available via ntpq output. The following system status bit names have been changed: - sync_alarm -> leap_alarm - sync_atomic -> sync_pps - sync_lf_clock -> sync_lf_radio - sync_hf_clock -> sync_hf_radio - sync_uhf_clock -> sync_uhf_radio - sync_local_proto -> sync_local - sync_udp/time -> sync_other Other names have been changed as well. See the change history for libntp/statestr.c for more details. Other backward-incompatible changes in ntpq include: - assID -> associd - rootdispersion -> rootdisp - pkt_head -> pkt_neader See the change history for other details. * Updates and cleanup from Dave Mills. * [Bug 995] Remove spurious ; from ntp-keygen.c. * More cleanup and changes from Dave Mills. * [Bug 980] Direct help to stdout. --- (4.2.4p8) 2009/12/08 Released by Harlan Stenn * [Sec 1331] DoS with mode 7 packets - CVE-2009-3563. --- (4.2.4p7) 2009/05/18 Released by Harlan Stenn * [Sec 1151] Remote exploit if autokey is enabled - CVE-2009-1252. * [Bug 1187] Update the copyright date. * [Bug 1191] ntpd fails on Win2000 - "Address already in use" after fix for [Sec 1149]. --- (4.2.4p7-RC7) 2009/05/12 Released by Harlan Stenn * ntp.isc.org -> ntp.org cleanup. * [Bug 1178] Use prior FORCE_DNSRETRY behavior as needed at runtime, add configure --enable-ignore-dns-errors to be even more stubborn --- (4.2.4p7-RC6) 2009/05/08 Released by Harlan Stenn * [Bug 784] Make --enable-linuxcaps the default when available * [Bug 1179] error messages for -u/--user and -i lacking droproot * Updated JJY reference clock driver from Takao Abe * [Bug 1071] Log a message and exit before trying to use FD_SET with a descriptor larger than FD_SETSIZE, which will corrupt memory * On corruption of the iface list head in add_interface, log and exit --- (4.2.4p7-RC5) 2009/05/02 Released by Harlan Stenn * [Bug 1172] 4.2.4p7-RC{3,4} fail to build on linux. * flock-build script unportable 'set -m' use removed --- (4.2.4p7-RC4) 2009/04/29 Released by Harlan Stenn * [Bug 1167] use gcc -Winit-self only if it is understood --- (4.2.4p7-RC3) 2009/04/22 Released by Harlan Stenn * [Bug 787] Bug fixes for 64-bit time_t on Windows * [Bug 813] Conditional naming of Event * [Bug 1147] System errors should be logged to msyslog() * [Bug 1155] Fix compile problem on Windows with VS2005 * [Bug 1156] lock_thread_to_processor() should be declared in header * [Bug 1157] quiet OpenSSL warnings, clean up configure.ac * [Bug 1158] support for aix6.1 * [Bug 1160] MacOS X is like BSD regarding F_SETOWN --- (4.2.4p7-RC2) 2009/04/09 Released by Harlan Stenn * [Sec 1144] limited buffer overflow in ntpq. CVE-2009-0159 * [Sec 1149] use SO_EXCLUSIVEADDRUSE on Windows --- (4.2.4p7-RC1) 2009/03/30 Released by Harlan Stenn * [Bug 1131] UDP sockets should not use SIGPOLL on Solaris. * build system email address cleanup * [Bug 774] parsesolaris.c does not compile under the new Solaris * [Bug 873] Windows serial refclock proper TTY line discipline emulation * [Bug 1014] Enable building with VC9 (in Visual Studio 2008, Visual C++ 2008, or SDK) * [Bug 1117] Deferred interface binding under Windows works only correctly if FORCE_DNSRETRY is defined * [BUG 1124] Lock QueryPerformanceCounter() client threads to same CPU * DPRINTF macro made safer, always evaluates to a statement and will not misassociate an else which follows the macro. --- (4.2.4p6) 2009/01/08 Released by Harlan Stenn * [Bug 1113] Fixed build errors with recent versions of openSSL. * [Sec 1111] Fix incorrect check of EVP_VerifyFinal()'s return value. * Update the copyright year. --- (4.2.4p5) 2008/08/17 Released by Harlan Stenn * [BUG 1051] Month off by one in leap second message written to clockstats file fixed. * [Bug 450] Windows only: Under original Windows NT we must not discard the wildcard socket to workaround a bug in NT's getsockname(). * [Bug 1038] Built-in getpass() function also prompts for password if not built with DEBUG. * [Bug 841] Obsolete the "dynamic" keyword and make deferred binding to local interfaces the default. Emit a warning if that keyword is used for configuration. * [Bug 959] Refclock on Windows not properly releasing recvbuffs. * [Bug 993] Fix memory leak when fetching system messages. * much cleanup, fixes, and changes from Dave Mills. * ntp_control.c: LEAPTAB is a filestamp, not an unsigned. From Dave Mills. * ntp_config.c: ntp_minpoll fixes from Dave Mills. * ntp-keygen updates from Dave Mills. * refresh epoch, throttle, and leap cleanup from Dave Mills. * Documentation cleanup from Dave Mills. * [Bug 918] Only use a native md5.h if MD5Init() is available. * [Bug 979] Provide ntptimeval if it is not otherwise present. * [Bug 634] Re-instantiate syslog() and logfiles after the daemon fork. * [Bug 952] Use md5 code with a friendlier license. * [Bug 977] Fix mismatching #ifdefs for builds without IPv6. * [Bug 830] Fix the checking order of the interface options. * Clean up the logfile/syslog setup. * [Bug 970] Lose obsolete -g flag to ntp-keygen. * The -e flag to ntp-keygen can write GQ keys now, too. * ntp_proto.c: sys_survivors and hpoll cleanup from Dave Mills. * ntp_loopfilter.c: sys_poll cleanup from Dave Mills. * refclock_wwv.c: maximum-likelihood digit and DSYNC fixes from Dave Mills. * [Bug 967] preemptable associations are lost forever on a step. * ntp_config.c: [CID 48] missing "else" clause. * [Bug 833] ntpq config keyword is quote-mark unfriendly. * Rename the ntpq "config" keyword to ":config". * Dave Mills shifted some orphan processing. * Fix typos in the [Bug 963] patch. * bootstrap: squawk if genver fails. Use -f with cp in case Dave does a chown. * Remove obsolete simulator command-line options. * ntp_request.c: [CID 36] zero sin_zero. * [Bug 963] get_systime() is too noisy. * [Bug 960] spurious syslog:crypto_setup:spurious crypto command * [Bug 964] Change *-*-linux* to *-*-*linux* to allow for uclinux. * Changes from Dave Mills: - ntp_util.c: cleanup. - ntp_timer.c: watch the non-burst packet rate. - ntp_request.c: cleanup. - ntp_restrict.c: RES_LIMITED cleanup. - ntp_proto.c: RES_LIMITED, rate bucktes, counters, overall cleanup. - ntp_peer.c: disallow peer_unconfig(). - ntp_monitor.c: RES_LIMITED cleanup. - ntp_loopfilter.c: poll interval cleanup. - ntp_crypto.c: volley -> retry. Cleanup TAI leap message. - ntp_config: average and minimum are ^2 values. - ntpdc: unknownversion is really "declined", not "bad version". - Packet retry cleanup. * [Bug 961] refclock_tpro.c:tpro_poll() calls refclock_receive() twice. * [Bug 957] Windows only: Let command line parameters from the Windows SCM GUI override the standard parameters from the ImagePath registry key. * Added HAVE_INT32_T to the Windows config.h to avoid duplicate definitions. * Work around a VPATH difference in FreeBSD's 'make' command. * Update bugreport URL. * Update -I documentation. * [Bug 713] Fix bug reporting information. * A bug in the application of the negative-sawtooth for 12 channel receivers. * The removal of unneeded startup code used for the original LinuxPPS, it now conforms to the PPSAPI and does not need special code. * ntp-keygen.c: Coverity fixes [CID 33,47]. * Volley cleanup from Dave Mills. * Fuzz cleanup from Dave Mills. * [Bug 861] Leap second cleanups from Dave Mills. * ntpsim.c: add missing protypes and fix [CID 34], a nit. * Upgraded bison at UDel. * Update br-flock and flock-build machine lists. * [Bug 752] QoS: add parse/config handling code. * Fix the #include order in tickadj.c for picky machines. * [Bug 752] QoS: On some systems, netinet/ip.h needs netinet/ip_systm.h. * [Bug 752] Update the QoS tagging (code only - configuration to follow). * Orphan mode and other protocol cleanup from Dave Mills. * Documentation cleanup from Dave Mills. * [Bug 940] ntp-keygen uses -v. Disallow it as a shortcut for --version. * more cleanup to ntp_lineeditlibs.m4. * Documentation updates from Dave Mills. * -ledit cleanup for ntpdc and ntpq. * Association and other cleanup from Dave Mills. * NTP_UNREACH changes from Dave Mills. * Fix the readline history test. * [Bug 931] Require -lreadline to be asked for explicitly. * [Bug 764] When looking for -lreadline support, also try using -lncurses. * [Bug 909] Fix int32_t errors for ntohl(). * [Bug 376/214] Enhancements to support multiple if names and IP addresses. * [Bug 929] int32_t is undefined on Windows. Casting wrong. * [Bug 928] readlink missing braces. * [Bug 788] Update macros to support VS 2005. * ntpd/ntp_timer.c: add missing sys_tai parameter for debug printf * [Bug 917] config parse leaves files open * [Bug 912] detect conflicting enable/disable configuration on interfaces sharing an IP address * [Bug 771] compare scopeid if available for IPv6 addresses * Lose obsolete crypto subcommands (Dave Mills). * WWV is an HF source, not an LF source (Dave Mills). * [Bug 899] Only show -i/--jaildir -u/--user options if we HAVE_DROPROOT. * [Bug 916] 'cryptosw' is undefined if built without OpenSSL. * [Bug 891] 'restrict' config file keyword does not work (partial fix). * [Bug 890] the crypto command seems to be required now. * [Bug 915] ntpd cores during processing of x509 certificates. * Crypto lint cleanup from Dave Mills. * [Bug 897] Check RAND_status() - we may not need a .rnd file. * Crypto cleanup from Dave Mills. * [Bug 911] Fix error message in cmd_args.c. * [Bug 895] Log assertion failures via syslog(), not stderr. * Documentation updates from Dave Mills. * Crypto cleanup from Dave Mills. * [Bug 905] ntp_crypto.c fails to compile without -DDEBUG. * Avoid double peer stats logging. * ntp-keygen cleanup from Dave Mills. * libopts needs to be built after ElectricFence. * [Bug 894] Initialize keysdir before calling crypto_setup(). * Calysto cleanup for ntpq. * ntp-keygen -i takes an arg. * Cleanup and fixes from Dave Mills. * [Bug 887] Fix error in ntp_types.h (for sizeof int != 4). * Bug 880 bug fixes for Windows build * Improve Calysto support. * The "revoke" parameter is a crypto command. * The driftfile wander threshold is a real number. * [Bug 850] Fix the wander threshold parameter on the driftfile command. * ntp_io.c: Dead code cleanup - Coverity View 19. * Leap file related cleanup from Dave Mills. * ntp_peer.c: Set peer->srcadr before (not after) calling set_peerdstadr(). * Initialize offset in leap_file() - Coverity View 17. * Use the correct stratum on KISS codes. * Fuzz bits cleanup. * Show more digits in some debug printf's. * Use drift_file_sw internally to control writing the drift file. * Implement the wander_threshold option for the driftfile config keyword. * reformat ntp_control.c; do not use c++ // comments. * [Bug 629] Undo bug #629 fixes as they cause more problems than were being solved * Changes from Dave Mills: in/out-bound data rates, leapsecond cleanup, driftfile write cleanup, packet buffer length checks, documentation updates. * More assertion checks and malloc()->emalloc(), courtesy of Calysto. * [Bug 864] Place ntpd service in maintenance mode if using SMF on Solaris * [Bug 862] includefile nesting; preserve phonelist on reconfig. * [Bug 604] ntpd regularly dies on linux/alpha. * more leap second infrastructure fixes from Dave Mills. * [Bug 858] recent leapfile changes broke non-OpenSSL builds. * Use emalloc() instead of malloc() in refclock_datum.c (Calysto). * Start using 'design by contract' assertions. * [Bug 767] Fast sync to refclocks wanted. * Allow null driftfile. * Use YYERROR_VERBOSE for the new parser, and fix related BUILT_SOURCES. * [Bug 629] changes to ensure broadcast works including on wildcard addresses * [Bug 853] get_node() must return a pointer to maximally-aligned memory. * Initial leap file fixes from Dave Mills. * [Bug 858] Recent leapfile changes broke without OPENSSL. * Use a char for DIR_SEP, not a string. * [Bug 850] driftfile parsing changes. * driftfile maintenance changes from Dave Mills. Use clock_phi instead of stats_write_tolerance. * [Bug 828] refid string not being parsed correctly. * [Bug 846] Correct includefile parsing. * [Bug 827] New parsing code does not handle "fudge" correctly. * Enable debugging capability in the config parser. * [Bug 839] Crypto password not read from ntp.conf. * Have autogen produce writable output files. * [Bug 825] Correct logconfig -/+ keyword processing. * [Bug 828] Correct parsing of " delimited strings. * Cleanup FILE * usage after fclose() in ntp_filegen.c. * [Bug 843] Windows Completion port code was incorrectly merged from -stable. * [Bug 840] do fudge configuration AFTER peers (thus refclocks) have been configured. * [Bug 824] Added new parser modules to the Windows project file. * [Bug 832] Add libisc/log.c headers to the distribution. * [Bug 808] Only write the drift file if we are in state 4. * Initial import of libisc/log.c and friends. * [Bug 826] Fix redefinition of PI. * [Bug 825] ntp_scanner.c needs to #include . * [Bug 824] New parser code has some build problems with the SIM code. * [Bug 817] Use longnames for setting ntp variables on the command-line; Allowing '-v' with and without an arg to disambiguate usage is error-prone. * [Bug 822] set progname once, early. * [Bug 819] remove erroneous #if 0 in Windows completion port code. * The new config code missed an #ifdef for building without refclocks. * Distribute some files needed by the new config parsing code. * [Bug 819] Timeout for WaitForMultipleObjects was 500ms instead of INFINITE * Use autogen 5.9.1. * Fix clktest command-line arg processing.' * Audio documentation updates from Dave Mills. * New config file parsing code, from Sachin Kamboj. * fuzz bit cleanup from Dave Mills. * replay cleanup from Dave Mills. * [Bug 542] Tolerate missing directory separator at EO statsdir. * [Bug 812] ntpd should drop supplementary groups. * [Bug 815] Fix warning compiling 4.2.5p22 under Windows with VC6. * [Bug 740] Fix kernel/daemon startup drift anomaly. * refclock_wwv.c fixes from Dave Mills. * [Bug 810] Fix ntp-keygen documentation. * [Bug 787] Bug fixes for 64-bit time_t on Windows. * [Bug 796] Clean up duplicate #defines in ntp_control.c. * [Bug 569] Use the correct precision for the Leitch CSD-5300. * [Bug 795] Moved declaration of variable to top of function. * [Bug 798] ntpq [p typo crashes ntpq/ntpdc. * [Bug 786] Fix refclock_bancomm.c on Solaris. * [Bug 774] parsesolaris.c does not compile under the new Solaris. * [Bug 782] Remove P() macros from Windows files. * [Bug 778] ntpd fails to lock with drift=+500 when started with drift=-500. * [Bug 592] Trimble Thunderbolt GPS support. * IRIG, CHU, WWV, WWVB refclock improvements from Dave Mills. * [Bug 757] Lose ULONG_CONST(). * [Bug 756] Require ANSI C (function prototypes). * codec (audio) and ICOM changes from Dave Mills. --- * [Bug 450] Windows only: Under original Windows NT we must not discard the wildcard socket to workaround a bug in NT's getsockname(). * [Bug 1038] Built-in getpass() function also prompts for password if not built with DEBUG. * [Bug 841] Obsolete the "dynamic" keyword and make deferred binding to local interfaces the default. Emit a warning if that keyword is used for configuration. * [Bug 959] Refclock on Windows not properly releasing recvbuffs. * [Bug 993] Fix memory leak when fetching system messages. * [Bug 987] Wake up the resolver thread/process when a new interface has become available. * Correctly apply negative-sawtooth for oncore 12 channel receiver. * Startup code for original LinuxPPS removed. LinuxPPS now conforms to the PPSAPI. * [Bug 1000] allow implicit receive buffer allocation for Windows. fixes startup for windows systems with many interfaces. reduces dropped packets on network bursts. additionally fix timer() starvation during high load. * [Bug 990] drop minimum time restriction for interface update interval. * [Bug 977] Fix mismatching #ifdefs for builds without IPv6. * Update the copyright year. * Build system cleanup (make autogen-generated files writable). * [Bug 957] Windows only: Let command line parameters from the Windows SCM GUI override the standard parameters from the ImagePath registry key. * Fixes for ntpdate: * [Bug 532] nptdate timeout is too long if several servers are supplied. * [Bug 698] timeBeginPeriod is called without timeEndPeriod in some NTP tools. * [Bug 857] ntpdate debug mode adjusts system clock when it shouldn't. * [Bug 908] ntpdate crashes sometimes. * [Bug 982] ntpdate(and ntptimeset) buffer overrun if HAVE_POLL_H isn't set (dup of 908). * [Bug 997] ntpdate buffer too small and unsafe. * ntpdate.c: Under Windows check whether NTP port in use under same conditions as under other OSs. * ntpdate.c: Fixed some typos and indents (tabs/spaces). (4.2.4p4) Released by Harlan Stenn * [Bug 902] Fix problems with the -6 flag. * Updated include/copyright.def (owner and year). * [Bug 878] Avoid ntpdc use of refid value as unterminated string. * [Bug 881] Corrected display of pll offset on 64bit systems. * [Bug 886] Corrected sign handling on 64bit in ntpdc loopinfo command. * [Bug 889] avoid malloc() interrupted by SIGIO risk * ntpd/refclock_parse.c: cleanup shutdown while the file descriptor is still open. * [Bug 885] use emalloc() to get a message at the end of the memory unsigned types cannot be less than 0 default_ai_family is a short lose trailing , from enum list clarify ntp_restrict.c for easier automated analysis * [Bug 884] don't access recv buffers after having them passed to the free list. * [Bug 882] allow loopback interfaces to share addresses with other interfaces. --- (4.2.4p3) Released by Harlan Stenn * [Bug 863] unable to stop ntpd on Windows as the handle reference for events changed --- (4.2.4p2) Released by Harlan Stenn * [Bug 854] Broadcast address was not correctly set for interface addresses * [Bug 829] reduce syslog noise, while there fix Enabled/Disable logging to reflect the actual configuration. * [Bug 795] Moved declaration of variable to top of function. * [Bug 789] Fix multicast client crypto authentication and make sure arriving multicast packets do not disturb the autokey dance. * [Bug 785] improve handling of multicast interfaces (multicast routers still need to run a multicast routing software/daemon) * ntpd/refclock_parse.c: cleanup shutdown while the file descriptor is still open. * [Bug 885] use emalloc() to get a message at the end of the memory unsigned types cannot be less than 0 default_ai_family is a short lose trailing , from enum list * [Bug 884] don't access recv buffers after having them passed to the free list. * [Bug 882] allow loopback interfaces to share addresses with other interfaces. * [Bug 527] Don't write from source address length to wrong location * Upgraded autogen and libopts. * [Bug 811] ntpd should not read a .ntprc file. --- (4.2.4p1) (skipped) --- (4.2.4p0) Released by Harlan Stenn * [Bug 793] Update Hans Lambermont's email address in ntpsweep. * [Bug 776] Remove unimplemented "rate" flag from ntpdate. * [Bug 586] Avoid lookups if AI_NUMERICHOST is set. * [Bug 770] Fix numeric parameters to ntp-keygen (Alain Guibert). * [Bug 768] Fix io_setbclient() error message. * [Bug 765] Use net_bind_service capability on linux. * [Bug 760] The background resolver must be aware of the 'dynamic' keyword. * [Bug 753] make union timestamp anonymous (Philip Prindeville). * confopt.html: move description for "dynamic" keyword into the right section. * pick the right type for the recv*() length argument. --- (4.2.4) Released by Harlan Stenn * monopt.html fixes from Dave Mills. * [Bug 452] Do not report kernel PLL/FLL flips. * [Bug 746] Expert mouseCLOCK USB v2.0 support added.' * driver8.html updates. * [Bug 747] Drop tags from ntpdc.html. * sntp now uses the returned precision to control decimal places. * sntp -u will use an unprivileged port for its queries. * [Bug 741] "burst" doesn't work with !unfit peers. * [Bug 735] Fix a make/gmake VPATH issue on Solaris. * [Bug 739] ntpd -x should not take an argument. * [Bug 737] Some systems need help providing struct iovec. * [Bug 717] Fix libopts compile problem. * [Bug 728] parse documentation fixes. * [Bug 734] setsockopt(..., IP_MULTICAST_IF, ...) fails on 64-bit platforms. * [Bug 732] C-DEX JST2000 patch from Hideo Kuramatsu. * [Bug 721] check for __ss_family and __ss_len separately. * [Bug 666] ntpq opeers displays jitter rather than dispersion. * [Bug 718] Use the recommended type for the saddrlen arg to getsockname(). * [Bug 715] Fix a multicast issue under Linux. * [Bug 690] Fix a Windows DNS lookup buffer overflow. * [Bug 670] Resolved a Windows issue with the dynamic interface rescan code. * K&R C support is being deprecated. * [Bug 714] ntpq -p should conflict with -i, not -c. * WWV refclock improvements from Dave Mills. * [Bug 708] Use thread affinity only for the clock interpolation thread. * [Bug 706] ntpd can be running several times in parallel. * [Bug 704] Documentation typos. * [Bug 701] coverity: NULL dereference in ntp_peer.c * [Bug 695] libopts does not protect against macro collisions. * [Bug 693] __adjtimex is independent of ntp_{adj,get}time. * [Bug 692] sys_limitrejected was not being incremented. * [Bug 691] restrictions() assumption not always valid. * [Bug 689] Deprecate HEATH GC-1001 II; the driver never worked. * [Bug 688] Fix documentation typos. * [Bug 686] Handle leap seconds better under Windows. * [Bug 685] Use the Windows multimedia timer. * [Bug 684] Only allow debug options if debugging is enabled. * [Bug 683] Use the right version string. * [Bug 680] Fix the generated version string on Windows. * [Bug 678] Use the correct size for control messages. * [Bug 677] Do not check uint_t in configure.ac. * [Bug 676] Use the right value for msg_namelen. * [Bug 675] Make sure ntpd builds without debugging. * [Bug 672] Fix cross-platform structure padding/size differences. * [Bug 660] New TIMESTAMP code fails tp build on Solaris Express. * [Bug 659] libopts does not build under Windows. * [Bug 658] HP-UX with cc needs -Wp,-H8166 in CFLAGS. * [Bug 656] ntpdate doesn't work with multicast address. * [Bug 638] STREAMS_TLI is deprecated - remove it. * [Bug 635] Fix tOptions definition. * [Bug 628] Fallback to ntp discipline not working for large offsets. * [Bug 622] Dynamic interface tracking for ntpd. * [Bug 603] Don't link with libelf if it's not needed. * [Bug 523] ntpd service under Windows does't shut down properly. * [Bug 500] sntp should always be built. * [Bug 479] Fix the -P option. * [Bug 421] Support the bc637PCI-U card. * [Bug 342] Deprecate broken TRAK refclock driver. * [Bug 340] Deprecate broken MSF EES refclock driver. * [Bug 153] Don't do DNS lookups on address masks. * [Bug 143] Fix interrupted system call on HP-UX. * [Bug 42] Distribution tarballs should be signed. * Support separate PPS devices for PARSE refclocks. * [Bug 637, 51?] Dynamic interface scanning can now be done. * Options processing now uses GNU AutoGen. --- (4.2.2p4) Released by Harlan Stenn * [Bug 710] compat getnameinfo() has off-by-one error * [Bug 690] Buffer overflow in Windows when doing DNS Lookups --- (4.2.2p3) Released by Harlan Stenn * Make the ChangeLog file cleaner and easier to read * [Bug 601] ntpq's decodeint uses an extra level of indirection * [Bug 657] Different OSes need different sized args for IP_MULTICAST_LOOP * release engineering/build changes * Documentation fixes * Get sntp working under AIX-5 --- (4.2.2p2) (broken) * Get sntp working under AIX-5 --- (4.2.2p1) * [Bug 661] Use environment variable to specify the base path to openssl. * Resolve an ambiguity in the copyright notice * Added some new documentation files * URL cleanup in the documentation * [Bug 657]: IP_MULTICAST_LOOP uses a u_char value/size * quiet gcc4 complaints * more Coverity fixes * [Bug 614] manage file descriptors better * [Bug 632] update kernel PPS offsets when PPS offset is re-configured * [Bug 637] Ignore UP in*addr_any interfaces * [Bug 633] Avoid writing files in srcdir * release engineering/build changes --- (4.2.2) * SNTP * Many bugfixes * Implements the current "goal state" of NTPv4 * Autokey improvements * Much better IPv6 support * [Bug 360] ntpd loses handles with LAN connection disabled. * [Bug 239] Fix intermittent autokey failure with multicast clients. * Rewrite of the multicast code * New version numbering scheme --- (4.2.0) * More stuff than I have time to document * IPv6 support * Bugfixes * call-gap filtering * wwv and chu refclock improvements * OpenSSL integration --- (4.1.2) * clock state machine bugfix * Lose the source port check on incoming packets * (x)ntpdc compatibility patch * Virtual IP improvements * ntp_loopfilter fixes and improvements * ntpdc improvements * GOES refclock fix * JJY driver * Jupiter refclock fixes * Neoclock4X refclock fixes * AIX 5 port * bsdi port fixes * Cray unicos port upgrade * HP MPE/iX port * Win/NT port upgrade * Dynix PTX port fixes * Document conversion from CVS to BK * readline support for ntpq --- (4.1.0) * CERT problem fixed (99k23) * Huff-n-Puff filter * Preparation for OpenSSL support * Resolver changes/improvements are not backward compatible with mode 7 requests (which are implementation-specific anyway) * leap second stuff * manycast should work now * ntp-genkeys does new good things. * scripts/ntp-close * PPS cleanup and improvements * readline support for ntpdc * Crypto/authentication rewrite * WINNT builds with MD5 by default * WINNT no longer requires Perl for building with Visual C++ 6.0 * algorithmic improvements, bugfixes * Solaris dosynctodr info update * html/pic/* is *lots* smaller * New/updated drivers: Forum Graphic GPS, WWV/H, Heath GC-100 II, HOPF serial and PCI, ONCORE, ulink331 * Rewrite of the audio drivers --- (4.0.99) * Driver updates: CHU, DCF, GPS/VME, Oncore, PCF, Ulink, WWVB, burst If you use the ONCORE driver with a HARDPPS kernel module, you *must* have a properly specified: pps [assert/clear] [hardpps] line in the /etc/ntp.conf file. * PARSE cleanup * PPS cleanup * ntpd, ntpq, ntpdate cleanup and fixes * NT port improvements * AIX, BSDI, DEC OSF, FreeBSD, NetBSD, Reliant, SCO, Solaris port improvements --- (4.0.98) * Solaris kernel FLL bug is fixed in 106541-07 * Bug/lint cleanup * PPS cleanup * ReliantUNIX patches * NetInfo support * Ultralink driver * Trimble OEM Ace-II support * DCF77 power choices * Oncore improvements --- (4.0.97) * NT patches * AIX,SunOS,IRIX portability * NeXT portability * ntptimeset utility added * cygwin portability patches --- (4.0.96) * -lnsl, -lsocket, -lgen configuration patches * Y2K patches from AT&T * Linux portability cruft --- (4.0.95) * NT port cleanup/replacement * a few portability fixes * VARITEXT Parse clock added --- (4.0.94) * PPS updates (including ntp.config options) * Lose the old DES stuff in favor of the (optional) RSAREF stuff * html cleanup/updates * numerous drivers cleaned up * numerous portability patches and code cleanup --- (4.0.93) * Oncore refclock needs PPS or one of two ioctls. * Don't make ntptime under Linux. It doesn't compile for too many folks. * Autokey cleanup * ReliantUnix patches * html cleanup * tickadj cleanup * PARSE cleanup * IRIX -n32 cleanup * byte order cleanup * ntptrace improvements and patches * ntpdc improvements and patches * PPS cleanup * mx4200 cleanup * New clock state machine * SCO cleanup * Skip alias interfaces --- (4.0.92) * chronolog and dumbclock refclocks * SCO updates * Cleanup/bugfixes * Y2K patches * Updated palisade driver * Plug memory leak * wharton kernel clock * Oncore clock upgrades * NMEA clock improvements * PPS improvements * AIX portability patches --- (4.0.91) * New ONCORE driver * New MX4200 driver * Palisade improvements * config file bugfixes and problem reporting * autoconf upgrade and cleanup * HP-UX, IRIX lint cleanup * AIX portability patches * NT cleanup --- (4.0.90) * Nanoseconds * New palisade driver * New Oncore driver --- (4.0.73) * README.hackers added * PARSE driver is working again * Solaris 2.6 has nasty kernel bugs. DO NOT enable pll! * DES is out of the distribution. --- (4.0.72) * K&R C compiling should work again. * IRIG patches. * MX4200 driver patches. * Jupiter driver added. * Palisade driver added. Needs work (ANSI, ntoh/hton, sizeof double, ???) ntpsec-1.1.0+dfsg1/devel/ntpv5.txt0000644000175000017500000001352713252364117016551 0ustar rlaagerrlaager= Preliminary design notes for the NTPv5 protocol = NTPv4 is showing its age. There are functional capabilities that would be very useful if they could be standardized, but currently are not. This document will first list these missing bits, then discuss ways to incorporate them. == The missing data: a semantic view == === REFIDs === Reference IDs are used by Stratum 1 sources to identify clocks and clock types, and by hosts at higher strata to perform loop detection. The REFID field is 4 octets long, sufficient to hold an IPv4 address for loop detection. This is inadequate for IPv6, so the reference ID of an IPv6 host is a 4-octet hash of its actual address. Hash collisions have been observed in the wild, possibly resulting in false-positive loop detection. The new protocol should support REFIDs at least as long as an IPv6 address (16 octets). === Timescale === Most servers ship UTC. Some ship TAI. Some perform leap-second smearing, some do not. The new protocol should enable a server to advertise its timescale, including, if applicable in its timescale, its current leapsecond offset. === Era === NTP dates are 64-bit counters based on an epoch. The new protocol should enable a server to ship a year identifying its epoch. === NTS === The IETF is attempting to develop a new cryptographic standard for secure/authenticated time exchange: Network Time Security. Further information on this is available at: https://tools.ietf.org/wg/ntp/ The new protocol needs to allow a block of data of as-yet unspecified and possibly variable size to be dedicated to NTS use. == Extensions vs. replacement == There are three possible scenarios for NTPv5 design. === NTPv4+ === In this incremental approach, the NTP port number (123) is retained and the 48-byte header v4 header is preserved. New data fields are passed in RFC7822 extension blocks. The NTP version number is not incremented; "v5" becomes a set of required extension blocks. A difficulty with this approach is that some firewalls and routers are known to silently discard RFC7822 extension blocks as a way of preventing DoS attacks. This would create propagation issues difficult to diagnose. === NTPNG === In this approach, a new port number is allocated. The protocol is design is unconstrained except that it must carry the semantic content of the v4 header minus the unused Reference Timestamp field. The principal difficulty with this approach is that getting all the world's firewalls to pass through a new port is not easy. === Newmode === In this approach, the NTP port number is retained. So is at least the first byte of the v4 packet header structure, so that the version number and packet mode are at the same offset as in v4. The version field *is* incremented to 5. The following payload is design is unconstrained except that it must carry the semantic content of the v4 header minus the unused Reference Timestamp field. The principal difficulty with this approach is that implementations might not reject Version 5 packets, and therefore mis-parse the header. NTP Classic and NTPsec *do* perform this check. == Payload format design for the NTPNG and Newmode cases == NTP is running out of version numbers. The version field is only 3 bits wide. Accordingly, the Newmode payload should be structured like PNG, as a sequence of self-describing chunks that can be retired and replaced as needed to change payload semantics. Though NTPNG is not constrained by the width of the v4 mode field, the versionless semantics of a PNG-style chunk stream would confer a desirable degree of flexibility. The PNG standard can be found at https://www.w3.org/TR/PNG/ A chunk system appropriate for NTP can be summarized as follows: * Each chunk begins with a four-octet big-endian length. The length does not count itself. * Each chunk continues with a 4-octet type identifier composed of printable ASCII characters. * If the first character is uppercase, the chunk is *critical*; that is, implementations encountering a critical chunk type they do not recognize should treat the packet as erroneous. * If the first character is not uppercase, the chunk is non-critical and may be skipped. * Chunk content is not constrained and is interpreted based in the chunk type. Note that this is not identical to PNG chunk layout; one difference is that PNG chunks have only two-byte lengths and always end with a CRC. This chunk system is deliberately more similar to RFC7822 extension blocks. == Daniel weighs in == There aren't many deficiencies in NTPv4 which can't be fixed by adding extension fields. A change big enough to make a version bump worthwhile would incorporate at least most of the following: 1. Drop everything other than client/server mode. Replace mode 6 with something that runs over HTTPS on the NTS-KE port. 2. Let client and server packets be formatted differently. Achieve data minimization by just taking the unnecessary fields out of client packets altogether. 3. Forbid use of the legacy MAC field, thus fixing the hairiness around extension parsing. 4. Make NTS mandatory. In the NTPv5 packet format, the version, mode, NTS unique identifier, and (in client packets) NTS cookie come first in plaintext, then the whole rest of the packet is encrypted. 5. Ditch the useless poll, stratum, refid, and reference timestamp fields. Given that all of the above are implemented, origin timestamp also becomes redundant (NTS takes the place of its anti-spoofing role). 6. Represent timestamps as days, seconds, and fractions so that the time can be represented unambiguously during leap seconds. Make the day field 64 bits wide so that its range comfortable exceeds the lifespan of the solar system. 7. Don't implement leap smearing in the wire protocol (servers should always report accurate, unsmeared time), but standardize a formula for translating NTP time into smeared UNIX time seen by other applications. // end ntpsec-1.1.0+dfsg1/devel/y2k.txt0000644000175000017500000002250213252364117016173 0ustar rlaagerrlaager= Y2K certification = The NTP suite version 4.0.91 (dating from before continuous version-control history) was tested for Y2K conformance by the AT&T Freeware project. == Test notes == ............................................................................ Name of the Application: xntp Version Number: 4.0.91 Download Size: 4541953 bytes Downloaded from: http://www.eecis.udel.edu/~ntp/ Operating Systems Supported: many Operating Systems Tested: unix Testing Dates tested (CPU clock set) 1999-12-31 2000-01-01 2000-02-29 Critical fragments of code tested with other dates by special algorithms. Hardware Platform: Sun Sparc OS: Solaris 2.6 Compiler: gcc Version: 2.8.1 Repairs: 9 No. of files Repaired: 13 Compilation of Patches Required: yes Results Description: 1) Tested suspicious code. 2) Repaired problem code and added documentation to ntp.h. 3) Verified ntpd works on critical Y2K dates. Comments: 1) Errors were found in improper use of tm_year within struct tm, calculations that did not support year 2000 as a leap year (it truly is, despite any unchanged comments remaining in the NTP source), and some incorrect date calculations, while not traditional Y2K errors, would break in the year 2000. 2) include/ntpd.h Added some definitions and documentation about the right way of doing things. Definitions used by most, if not all, of the Y2K repairs. Cautions: 1) Some of the Y2K repairs were to reference clock drivers that we did not have the local hardware to test. While I believe the changes are sound, they really need to be tested. This includes: refclock_arc.c refclock_heath.c refclock_hpgps.c Also, parseutil/dcfd.c is another hardware dependent module that was repaired without live testing. Non-Y2K Problems Observed: 1) Inconsistent casts of variables containing time values may make expansion to 64 bit integer values in a portable manner difficult. 2) libntp/caltontp.c: Has logic I believe will fail starting in year 2100 or so. Left unchanged/untested as it works well beyond basic NTP 2036 limit checked by check_y2k.c. If NTP is implemented on 64-bit machines, this should be fixed 3) ntpd/refclock_acts.c: ACTS time format has changed somewhat since the code was written. In particular the '*' '#' character switch no longer occurs... only '*' is typed. NOTE: Author (falsely) stated Y2K is NOT a leap year when it really is. TRUTH: ACTS will go beyond Y2K: it uses FourDigitYear % 100 values for year so year 2000 will revert to "00". 4) ntpd/refclock_oncore.c Some very strange logic in manipulating year values: 1122 instance->pp->year = buf[6]*256+buf[7]; Multiply by 256???? Response from PHK: The entire protocol is binary, the year is a 16 bit quantity which according to the manual can have the range 1998-2018. ............................................................................ The "Non-Y2K Problems Observed" have been fixed. == Test results == ............................................................................ Script started on Sat Jan 1 04:14:09 2000 [root@timetest ntpdate]# date Sat Jan 1 04:14:11 EST 2000 [root@timetest ntpdate]# ./ntpdate -b timelord.att.com 14 Jul 13:44:39 ntpdate[11723]: step time server 135.16.xxxx.xxxx offset -14740193.210537 sec [root@timetest ntpdate]# date Wed Jul 14 13:44:42 EST 1999 [root@timetest ntpdate]# cd ../ntptrace [root@timetest ntptrace]# ./ntptrace timelord.att.com timelord.att.com: stratum 2, offset -0.000879, synch distance 0.07207 timemaster.att.com: stratum 1, offset -0.004876, synch distance 0.03485, refid 'GPS' [root@timetest ntptrace]# cd - [root@timetest ntpdate]# date Mon Feb 28 01:00:04 EST 2000 [root@timetest ntpdate]# ./ntpdate -b timelord.att.com 14 Jul 13:49:01 ntpdate[11760]: step time server 135.16.xxxx.xxxx offset -19739467.533126 sec [root@timetest ntpdate]# date Wed Jul 14 13:49:03 EST 1999 [root@timetest ntpdate]# cd - [root@timetest ntptrace]# ./ntptrace timelord.att.com timelord.att.com: stratum 2, offset 0.001383, synch distance 0.05644 timemaster.att.com: stratum 1, offset -0.006355, synch distance 0.04178, refid 'GPS' [root@timetest ntptrace]# cd - [root@timetest ntpdate]# date Tue Feb 29 01:00:05 EST 2000 [root@timetest ntpdate]# ./ntpdate -b timelord.att.com 14 Jul 13:57:41 ntpdate[12423]: step time server 135.16.xxxx.xxxx offset -19825349.396585 sec [root@timetest ntpdate]# date Wed Jul 14 13:57:43 EST 1999 [root@timetest ntpdate]# cd - [root@timetest ntptrace]# ./ntptrace timelord.att.com timelord.att.com: stratum 2, offset -0.000094, synch distance 0.06522 timemaster.att.com: stratum 1, offset -0.010803, synch distance 0.03078, refid 'GPS' [root@timetest ntptrace]# cd - [root@timetest ntpdate]# date Wed Mar 1 01:00:03 EST 2000 [root@timetest ntpdate]# ./ntpdate -b timelord.att.com 14 Jul 13:58:10 ntpdate[12525]: step time server 135.16.xxxx.xxxx offset -19911719.766061 sec [root@timetest ntpdate]# date Wed Jul 14 13:58:12 EST 1999 [root@timetest ntpdate]# cd - [root@timetest ntptrace]# ./ntptrace timelord.att.com timelord.att.com: stratum 2, offset -0.000719, synch distance 0.06561 timemaster.att.com: stratum 1, offset -0.013598, synch distance 0.03116, refid 'GPS' Script done on Wed Jul 14 13:58:28 1999 RESULTS OK. --------------------END OF TEST1-------------------- ### freeware test configuration server 127.127.1.0 prefer fudge 127.127.1.0 stratum 0 driftfile drift.log ntpdate timelord.att.com server 135.16.xxxx.xxxx stratum 1, offset 0.000033, delay 0.02975 31 Dec 23:58:59 ntpdate[83551]: adjust time server 135.16.74.3 offset 0.039057 s ec ntpdate timelord.att.com server 135.16.xxxx.xxxx stratum 1, offset 0.000019, delay 0.02504 01 Jan 00:01:05 ntpdate[8352]: adjust time server 135.16.74.3 offset 0.039057 s ec ntpdate timelord.att.com server 135.25.xxxx.xxxx, stratum 1, offset -0.000023, delay 0.02731 29 Feb 00:02:15 ntpdate[8353]: adjust time server 135.25.xxxx.xxxx offset -0.000023 sec ............................................................................ == AT&T README == This is most of the AT&T README file for the Y2K patches. Some now-irrelevant material has been removed. ............................................................................ AT&T Freeware Year 2000 Certification This is the "readme" file for the freeware application which has been certified by AT&T Labs as part of the "Freeware Y2K Certification Project". DISCLAIMER For its own internal business purposes AT&T Labs has assessed various programs obtained from the Internet for Year-2000 (Y2K) readiness that were not sufficiently certified for AT&T's needs. As a service to the computing community AT&T Labs is freely releasing this information to the public as a series of "Y2K Application Updates", one update for what AT&T Labs considers an "application". For use outside of AT&T, AT&T Labs is not certifying this information is correct, that any software, including repairs and tests, will help others in any way, survive the year 2000, nor work with current applications. Nor is AT&T taking any responsibility for repairing any Y2K problems that were overlooked nor repairing any bugs in any "Y2K Application Update". All risk of using this Y2K Application Update remains with the user who is expected to test that this update meets their needs. LICENSE TO USE AT&T's intent is to ensure these Y2K patches are freely available to the public but will not maintain a public web site for their distribution. Any copyright claims only only apply to the specific changes made by Y2K to the code. Any original copyright holders retain rights to unchanged code. Wherever possible patches will be returned to the current owner(s) of the code. Owners and publishers are free to incorporate appropriate patches, upgrades, and tests within legal future distributions as long as they include the credit: Various Y2K updates and tests provided by AT&T Labs. Copyright 1999 AT&T. and any AT&T "comments" on the changed code remain intact. Any distributions of the updates must keep the entire update intact, without any change, including copyright and disclaimer information. If integrated with the original application items not needed for an integrated release may be omitted. When distributed on the same media as the original application there must be no charge for this "Y2k Application Update". CONTACTS If you find any overlooked Y2K problems, or have other strictly Y2K repairs for the software, please E-mail: y2k@y2k.labs.att.com This address is strictly reserved for the topic at hand. AT&T makes no commitments to answer any E-mail to this address. AT&T is free to use any submissions, including publishing in future Y2K related release notes, without payment or advance notice to the submitting person or persons... appropriate credit will be given in any future publications to the first person submitting something that AT&T uses. SUPPORT See http://y2k.labs.att.com/freeware. There will be no ongoing support for the project. But if you have some very important issue, you may email us at: y2k@y2k.labs.att.com ............................................................................ //end ntpsec-1.1.0+dfsg1/devel/dot.emacs0000644000175000017500000000101113252364117016515 0ustar rlaagerrlaager;; This is how Dave Mills likes to see the NTP code formatted. (defconst ntp-c-style '((c-basic-offset . 8) (fill-column . 72) (c-offsets-alist . ((arglist-intro . +) (case-label . *) (statement-case-intro . *) (statement-cont . *) (substatement-open . 0)))) "David L. Mills; NTP code indentation style") (defun ntp-c-mode-common-hook () ;; add ntp c style (c-add-style "ntp" ntp-c-style nil)) (add-hook 'c-mode-common-hook 'ntp-c-mode-common-hook) ;; 1997112600 ntpsec-1.1.0+dfsg1/devel/TODO0000644000175000017500000001505513252364117015422 0ustar rlaagerrlaager= TO DO = === Testing === * We need to live-test various refclocks. It would be nice to test the full matrix of refclock x platform, but that's too much work. We should probably test as many refclocks as we can on at least one platform and test the NMEA, PPS, and SHM drivers on most platforms. === Slow convergence === [quote, Gary Miller] __________ ntpd takes way, way, way longer to converge than chronyd. Which is why on the fly reconfiguration in ntpd is SO important. Last thing you ever want to do is restart ntpd. Right now, after 10 mins, ntpd has 2,000 times the jitter as chronyd had when I turned it off. __________ Also see https://gitlab.com/NTPsec/ntpsec/issues/68 where the dubiousness of the current startup logic gets discussed. === Future plans === * Add .tar.xz tarball. * Fix ntpq retransmissions. Too many cases don't work well enough (may be inherited from ntp classic). * Test that the pool code properly replaces a server if it goes dark. (Not clear how to arrange this.) * Full support for NTS, once IETF has the spec ready * We could open only IPv6 sockets and allow them to handle mapped IPv4 addresses, as described at http://man7.org/linux/man-pages/man7/ipv6.7.html * A clock driver or auxiliary daemon for PTP. * 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 the ntp Python module (or scapy) and loosely modeled on wireshark. * Measure time from the clock reading to transmission for outbound packets. Use the measurement to correct for computation time before transmission. This is more interesting for authenticated packets. * We might be able to eliminate a lot of the Linux runtime droproot code by using file capabilities. * Use the snprintb in ntptime/ntptime.c for flag words like flash codes and use it systematically to make reports more readable. * Timer events need, as much as possible, to be eliminated - they eat power on laptops and mobile devices, usually unnecessarily. To fix this, go to an event-queue architecture that wakes the daemon up just in time for the next scheduled event (rather than a once-per-second timer tick). * Take another look at SHM. Hal thinks we can make a version that is read-only and solid. Maybe write a support package that does all the work if you call it with the date/time. Plug restrict holes: The current code pokes a hole in any restrictions to let servers through if they come from DNS lookups. We should add something like a nohole flag to prevent this so you can really block pool servers if you know there are some you don't want to use. Solaris maintenance mode There is code in ntp_proto.c that drops into Solaris maintenance mode if the clock offset exceeds the panic threshold. We should either drop that code or move it to where it can be used on all fatal errors. (Search for HAVE_LIBSCF_H) Logging cleanup: Most OSes/distros have a cron job that scans log files and mails a summary to the sysadmin. They look in /var/log/messages or similar rather than in ntpd's private log files. We should teach ntpd to log "interesting" messages to syslog as well as its log file. We should review the log messages that those utilities can process and update them to process any new messages we consider to be important. Remove clock fuzzing: ntpd has code to fuzz the clocks. It's scattered all over the place. That made sense when clocks were updated in big steps on a scheduler interrupt. But most modern OSes use something like Intel's TSC to implement fine grained clocks. We should review this area to verify that fuzzing is still useful. SIGHUP should do more: reload keys file (easy, it already reloads the leap file) reload ntp.conf (hard, but needed to add new keys, we could kludge a partial reload to add/delete servers) Waf detect need configure: There are problems with people forgetting to run configure or clean after a git pull. (From email on 23 May 2017) Version string cleanup: This is tangled up with EPOCH configure has --build-version-tag= configure sets up NTPSEC_VERSION_STRING ntptime is the only useage. The version string should change if I make an edit and rebuild currently it only changes when something in git changes We need to be sure not to break the stable checksum feature. Pivot cleanup: Currently, ntpd does a pivot deep in step_systime in libntp/systime.c That handles the problem of 32 bits of seconds wraping in 2038. Posix is shifting to a 64 bit time_t, but we only have 32 bits of seconds in l_fp. We can clean up a lot of code if l_fp is only used for offsets except when used in packets. That requires pushing the pivot logic down close to the packet processing. There may be interactions with ntpq. Recvbuff cleanup: ntpd currently has a pool of recv buffers. That made sense when it was using SIGIO to get time stamps. Now it is useless and just adds useless cycles moving things to/from the free list. Receive packet processing cleanup: There is an AM table in the top of ntp_peer that does a table lookup to figure out how to process a received packet. We can eliminate that. I think it's leftover from peer and broadcast modes. [quote, Hal] __________ I think there is some interaction between when the ACTS driver calls and the state of other stuff, like the connection to the net. This is probably a good candidate to get cleaned up. There is another possible tangle in this area. If you set "noselect" on the server line in the config file, ntpd goes through all the action of collecting the data and writing log files, but then drops the clock. I don't know things well enough to be sure that this sort of logic won't pick one back up. __________ == Old, sometime ancient stuff == 970318: in hourly_stats(?), squawk if the magnitude of the drift is, say, >400. == Simple tasks for an intern/trainee == * A conformant SNMP subagent in Python - see RFC 5907. * In the docs subdirectory, include/command.txt is an HTML passthrough in a not entirely successful attempt to emulate the look of the Mills HTML documentation this directory was made from. It should be cleaned up or replaced. Requires HTML and CSS skills; intern must be willing to learn asciidoc. * Similarly, includes/footer.txt is a tabular hack made to resemble the old HTML and should be cleaned up. Requires HTML and CSS skills; intern must be willing to learn asciidoc // end ntpsec-1.1.0+dfsg1/devel/HISTORIC-NEWS0000644000175000017500000010170713252364117016653 0ustar rlaagerrlaager--- NTP 4.2.8p3 (Harlan Stenn , 2015/05/xx) Focus: Bug fixes and enhancements. Leap-second improvements. Severity: MEDIUM Bug Fixes and Improvements: * CID 739725: Fix a rare resource leak in libevent/listener.c. * CID 1295478: Quiet a pedantic potential error from the fix for Bug 2776. * CID 1296235: Fix refclock_jjy.c and correcting type of the driver40-ja.html * CID 1269537: Clean up a line of dead code in getShmTime(). * [Bug 2590] autogen-5.18.5. * [Bug 2612] restrict: Warn when 'monitor' can't be disabled because of 'limited'. * [Bug 2650] fix includefile processing. * [Bug 2745] ntpd -x steps clock on leap second Fixed an initial-value problem that caused misbehaviour in absence of any leapsecond information. Do leap second stepping only of the step adjustment is beyond the proper jump distance limit and step correction is allowed at all. * [Bug 2750] build for Win64 Building for 32bit of loopback ppsapi needs def file * [Bug 2776] Improve ntpq's 'help keytype'. * [Bug 2782] Refactor refclock_shm.c, add memory barrier protection. * [Bug 2792] If the IFF_RUNNING interface flag is supported then an interface is ignored as long as this flag is not set since the interface is not usable (e.g., no link). * [Bug 2794] Clean up kernel clock status reports. * [Bug 2800] refclock_true.c true_debug() can't open debug log because of incompatible open/fdopen parameters. * [Bug 2804] install-local-data assumes GNU 'find' semantics. * [Bug 2806] refclock_jjy.c supports the Telephone JJY. * [Bug 2808] GPSD_JSON driver enhancements, step 1. Fix crash during cleanup if GPS device not present and char device. Increase internal token buffer to parse all JSON data, even SKY. Defer logging of errors during driver init until the first unit is started, so the syslog is not cluttered when the driver is not used. Various improvements, see http://bugs.ntp.org/2808 for details. Changed libjsmn to a more recent version. * [Bug 2810] refclock_shm.c memory barrier code needs tweaks for QNX. * [Bug 2813] HP-UX needs -D__STDC_VERSION__=199901L and limits.h. * [Bug 2815] net-snmp before v5.4 has circular library dependencies. * [Bug 2821] Add a missing NTP_PRINTF and a missing const. * [Bug 2822] New leap column in sntp broke NTP::Util.pm. * [Bug 2825] Quiet file installation in html/ . * Add an assert to the ntpq ifstats code. * Clean up the RLIMIT_STACK code. * Improve the ntpq documentation around the controlkey keyid. * ntpq.c cleanup. * Windows port build cleanup. --- NTP 4.2.8p2 (Harlan Stenn , 2015/04/07) Focus: Security and Bug fixes, enhancements. Severity: MEDIUM In addition to bug fixes and enhancements, this release fixes the following medium-severity vulnerabilities involving private key authentication: * [Sec 2779] ntpd accepts unauthenticated packets with symmetric key crypto. References: Sec 2779 / CVE-2015-1798 / VU#374268 Affects: All NTP4 releases starting with ntp-4.2.5p99 up to but not including ntp-4.2.8p2 where the installation uses symmetric keys to authenticate remote associations. CVSS: (AV:A/AC:M/Au:N/C:P/I:P/A:P) Base Score: 5.4 Date Resolved: Stable (4.2.8p2) 07 Apr 2015 Summary: When ntpd is configured to use a symmetric key to authenticate a remote NTP server/peer, it checks if the NTP message authentication code (MAC) in received packets is valid, but not if there actually is any MAC included. Packets without a MAC are accepted as if they had a valid MAC. This allows a MITM attacker to send false packets that are accepted by the client/peer without having to know the symmetric key. The attacker needs to know the transmit timestamp of the client to match it in the forged reply and the false reply needs to reach the client before the genuine reply from the server. The attacker doesn't necessarily need to be relaying the packets between the client and the server. Authentication using autokey doesn't have this problem as there is a check that requires the key ID to be larger than NTP_MAXKEY, which fails for packets without a MAC. Mitigation: Upgrade to 4.2.8p2, or later, from the NTP Project Download Page or the NTP Public Services Project Download Page Configure ntpd with enough time sources and monitor it properly. Credit: This issue was discovered by Miroslav Lichvar, of Red Hat. * [Sec 2781] Authentication doesn't protect symmetric associations against DoS attacks. References: Sec 2781 / CVE-2015-1799 / VU#374268 Affects: All NTP releases starting with at least xntp3.3wy up to but not including ntp-4.2.8p2 where the installation uses symmetric key authentication. CVSS: (AV:A/AC:M/Au:N/C:P/I:P/A:P) Base Score: 5.4 Note: the CVSS base Score for this issue could be 4.3 or lower, and it could be higher than 5.4. Date Resolved: Stable (4.2.8p2) 07 Apr 2015 Summary: An attacker knowing that NTP hosts A and B are peering with each other (symmetric association) can send a packet to host A with source address of B which will set the NTP state variables on A to the values sent by the attacker. Host A will then send on its next poll to B a packet with originate timestamp that doesn't match the transmit timestamp of B and the packet will be dropped. If the attacker does this periodically for both hosts, they won't be able to synchronize to each other. This is a known denial-of-service attack, described at https://www.eecis.udel.edu/~mills/onwire.html . According to the document the NTP authentication is supposed to protect symmetric associations against this attack, but that doesn't seem to be the case. The state variables are updated even when authentication fails and the peers are sending packets with originate timestamps that don't match the transmit timestamps on the receiving side. This seems to be a very old problem, dating back to at least xntp3.3wy. It's also in the NTPv3 (RFC 1305) and NTPv4 (RFC 5905) specifications, so other NTP implementations with support for symmetric associations and authentication may be vulnerable too. An update to the NTP RFC to correct this error is in-process. Mitigation: Upgrade to 4.2.8p2, or later, from the NTP Project Download Page or the NTP Public Services Project Download Page Note that for users of autokey, this specific style of MITM attack is simply a long-known potential problem. Configure ntpd with appropriate time sources and monitor ntpd. Alert your staff if problems are detected. Credit: This issue was discovered by Miroslav Lichvar, of Red Hat. * New script: update-leap The update-leap script will verify and if necessary, update the leap-second definition file. It requires the following commands in order to work: wget logger tr sed shasum Some may choose to run this from cron. It needs more portability testing. Bug Fixes and Improvements: * [Bug 1787] DCF77's formerly "antenna" bit is "call bit" since 2003. * [Bug 1960] setsockopt IPV6_MULTICAST_IF: Invalid argument. * [Bug 2346] "graceful termination" signals do not do peer cleanup. * [Bug 2728] See if C99-style structure initialization works. * [Bug 2747] Upgrade libevent to 2.1.5-beta. * [Bug 2749] ntp/lib/NTP/Util.pm needs update for ntpq -w, IPv6, .POOL. . * [Bug 2751] jitter.h has stale copies of l_fp macros. * [Bug 2756] ntpd hangs in startup with gcc 3.3.5 on ARM. * [Bug 2757] Quiet compiler warnings. * [Bug 2759] Expose nonvolatile/clk_wander_threshold to ntpq. * [Bug 2763] Allow different thresholds for forward and backward steps. * [Bug 2766] ntp-keygen output files should not be world-readable. * [Bug 2767] ntp-keygen -M should symlink to ntp.keys. * [Bug 2771] nonvolatile value is documented in wrong units. * [Bug 2773] Early leap announcement from Palisade/Thunderbolt * [Bug 2774] Unreasonably verbose printout - leap pending/warning * [Bug 2775] ntp-keygen.c fails to compile under Windows. * [Bug 2777] Fixed loops and decoding of Meinberg GPS satellite info. Removed non-ASCII characters from some copyright comments. Removed trailing whitespace. Updated definitions for Meinberg clocks from current Meinberg header files. Now use C99 fixed-width types and avoid non-ASCII characters in comments. Account for updated definitions pulled from Meinberg header files. Updated comments on Meinberg GPS receivers which are not only called GPS16x. Replaced some constant numbers by defines from ntp_calendar.h Modified creation of parse-specific variables for Meinberg devices in gps16x_message(). Reworked mk_utcinfo() to avoid printing of ambiguous leap second dates. Modified mbg_tm_str() which now expexts an additional parameter controlling if the time status shall be printed. * [Sec 2779] ntpd accepts unauthenticated packets with symmetric key crypto. * [Sec 2781] Authentication doesn't protect symmetric associations against DoS attacks. * [Bug 2783] Quiet autoconf warnings about missing AC_LANG_SOURCE. * [Bug 2789] Quiet compiler warnings from libevent. * [Bug 2790] If ntpd sets the Windows MM timer highest resolution pause briefly before measuring system clock precision to yield correct results. * Comment from Juergen Perlinger in ntp_calendar.c to make the code clearer. * Use predefined function types for parse driver functions used to set up function pointers. Account for changed prototype of parse_inp_fnc_t functions. Cast parse conversion results to appropriate types to avoid compiler warnings. Let ioctl() for Windows accept a (void *) to avoid compiler warnings when called with pointers to different types. --- NTP 4.2.8p1 (Harlan Stenn , 2015/02/04) Focus: Security and Bug fixes, enhancements. Severity: HIGH In addition to bug fixes and enhancements, this release fixes the following high-severity vulnerabilities: * vallen is not validated in several places in ntp_crypto.c, leading to a potential information leak or possibly a crash References: Sec 2671 / CVE-2014-9297 / VU#852879 Affects: All NTP4 releases before 4.2.8p1 that are running autokey. CVSS: (AV:N/AC:L/Au:N/C:P/I:P/A:P) Base Score: 7.5 Date Resolved: Stable (4.2.8p1) 04 Feb 2015 Summary: The vallen packet value is not validated in several code paths in ntp_crypto.c which can lead to information leakage or perhaps a crash of the ntpd process. Mitigation - any of: Upgrade to 4.2.8p1, or later, from the NTP Project Download Page or the NTP Public Services Project Download Page. Disable Autokey Authentication by removing, or commenting out, all configuration directives beginning with the "crypto" keyword in your ntp.conf file. Credit: This vulnerability was discovered by Stephen Roettger of the Google Security Team, with additional cases found by Sebastian Krahmer of the SUSE Security Team and Harlan Stenn of Network Time Foundation. * ::1 can be spoofed on some OSes, so ACLs based on IPv6 ::1 addresses can be bypassed. References: Sec 2672 / CVE-2014-9298 / VU#852879 Affects: All NTP4 releases before 4.2.8p1, under at least some versions of MacOS and Linux. *BSD has not been seen to be vulnerable. CVSS: (AV:N/AC:L/Au:N/C:P/I:P/A:C) Base Score: 9 Date Resolved: Stable (4.2.8p1) 04 Feb 2014 Summary: While available kernels will prevent 127.0.0.1 addresses from "appearing" on non-localhost IPv4 interfaces, some kernels do not offer the same protection for ::1 source addresses on IPv6 interfaces. Since NTP's access control is based on source address and localhost addresses generally have no restrictions, an attacker can send malicious control and configuration packets by spoofing ::1 addresses from the outside. Note Well: This is not really a bug in NTP, it's a problem with some OSes. If you have one of these OSes where ::1 can be spoofed, ALL ::1 -based ACL restrictions on any application can be bypassed! Mitigation: Upgrade to 4.2.8p1, or later, from the NTP Project Download Page or the NTP Public Services Project Download Page Install firewall rules to block packets claiming to come from ::1 from inappropriate network interfaces. Credit: This vulnerability was discovered by Stephen Roettger of the Google Security Team. Additionally, over 30 bugfixes and improvements were made to the codebase. See the ChangeLog for more information. --- NTP 4.2.8 (Harlan Stenn , 2014/12/18) Focus: Security and Bug fixes, enhancements. Severity: HIGH In addition to bug fixes and enhancements, this release fixes the following high-severity vulnerabilities: ************************** vv NOTE WELL vv ***************************** The vulnerabilities listed below can be significantly mitigated by following the BCP of putting restrict default ... noquery in the ntp.conf file. With the exception of: receive(): missing return on error References: Sec 2670 / CVE-2014-9296 / VU#852879 below (which is a limited-risk vulnerability), none of the recent vulnerabilities listed below can be exploited if the source IP is restricted from sending a 'query'-class packet by your ntp.conf file. ************************** ^^ NOTE WELL ^^ ***************************** * Weak default key in config_auth(). References: [Sec 2665] / CVE-2014-9293 / VU#852879 CVSS: (AV:N/AC:L/Au:M/C:P/I:P/A:C) Base Score: 7.3 Vulnerable Versions: all releases prior to 4.2.7p11 Date Resolved: 28 Jan 2010 Summary: If no 'auth' key is set in the configuration file, ntpd would generate a random key on the fly. There were two problems with this: 1) the generated key was 31 bits in size, and 2) it used the (now weak) ntp_random() function, which was seeded with a 32-bit value and could only provide 32 bits of entropy. This was sufficient back in the late 1990s when the code was written. Not today. Mitigation - any of: - Upgrade to 4.2.7p11 or later. - Follow BCP and put 'restrict ... noquery' in your ntp.conf file. Credit: This vulnerability was noticed in ntp-4.2.6 by Neel Mehta of the Google Security Team. * Non-cryptographic random number generator with weak seed used by ntp-keygen to generate symmetric keys. References: [Sec 2666] / CVE-2014-9294 / VU#852879 CVSS: (AV:N/AC:L/Au:M/C:P/I:P/A:C) Base Score: 7.3 Vulnerable Versions: All NTP4 releases before 4.2.7p230 Date Resolved: Dev (4.2.7p230) 01 Nov 2011 Summary: Prior to ntp-4.2.7p230 ntp-keygen used a weak seed to prepare a random number generator that was of good quality back in the late 1990s. The random numbers produced was then used to generate symmetric keys. In ntp-4.2.8 we use a current-technology cryptographic random number generator, either RAND_bytes from OpenSSL, or arc4random(). Mitigation - any of: - Upgrade to 4.2.7p230 or later. - Follow BCP and put 'restrict ... noquery' in your ntp.conf file. Credit: This vulnerability was discovered in ntp-4.2.6 by Stephen Roettger of the Google Security Team. * Buffer overflow in crypto_recv() References: Sec 2667 / CVE-2014-9295 / VU#852879 CVSS: (AV:N/AC:L/Au:N/C:P/I:P/A:P) Base Score: 7.5 Versions: All releases before 4.2.8 Date Resolved: Stable (4.2.8) 18 Dec 2014 Summary: When Autokey Authentication is enabled (i.e. the ntp.conf file contains a 'crypto pw ...' directive) a remote attacker can send a carefully crafted packet that can overflow a stack buffer and potentially allow malicious code to be executed with the privilege level of the ntpd process. Mitigation - any of: - Upgrade to 4.2.8, or later, or - Disable Autokey Authentication by removing, or commenting out, all configuration directives beginning with the crypto keyword in your ntp.conf file. Credit: This vulnerability was discovered by Stephen Roettger of the Google Security Team. * Buffer overflow in ctl_putdata() References: Sec 2668 / CVE-2014-9295 / VU#852879 CVSS: (AV:N/AC:L/Au:N/C:P/I:P/A:P) Base Score: 7.5 Versions: All NTP4 releases before 4.2.8 Date Resolved: Stable (4.2.8) 18 Dec 2014 Summary: A remote attacker can send a carefully crafted packet that can overflow a stack buffer and potentially allow malicious code to be executed with the privilege level of the ntpd process. Mitigation - any of: - Upgrade to 4.2.8, or later. - Follow BCP and put 'restrict ... noquery' in your ntp.conf file. Credit: This vulnerability was discovered by Stephen Roettger of the Google Security Team. * Buffer overflow in configure() References: Sec 2669 / CVE-2014-9295 / VU#852879 CVSS: (AV:N/AC:L/Au:N/C:P/I:P/A:P) Base Score: 7.5 Versions: All NTP4 releases before 4.2.8 Date Resolved: Stable (4.2.8) 18 Dec 2014 Summary: A remote attacker can send a carefully crafted packet that can overflow a stack buffer and potentially allow malicious code to be executed with the privilege level of the ntpd process. Mitigation - any of: - Upgrade to 4.2.8, or later. - Follow BCP and put 'restrict ... noquery' in your ntp.conf file. Credit: This vulnerability was discovered by Stephen Roettger of the Google Security Team. * receive(): missing return on error References: Sec 2670 / CVE-2014-9296 / VU#852879 CVSS: (AV:N/AC:L/Au:N/C:N/I:N/A:P) Base Score: 5.0 Versions: All NTP4 releases before 4.2.8 Date Resolved: Stable (4.2.8) 18 Dec 2014 Summary: Code in ntp_proto.c:receive() was missing a 'return;' in the code path where an error was detected, which meant processing did not stop when a specific rare error occurred. We haven't found a way for this bug to affect system integrity. If there is no way to affect system integrity the base CVSS score for this bug is 0. If there is one avenue through which system integrity can be partially affected, the base score becomes a 5. If system integrity can be partially affected via all three integrity metrics, the CVSS base score become 7.5. Mitigation - any of: - Upgrade to 4.2.8, or later, - Remove or comment out all configuration directives beginning with the crypto keyword in your ntp.conf file. Credit: This vulnerability was discovered by Stephen Roettger of the Google Security Team. See http://support.ntp.org/security for more information. New features / changes in this release: Important Changes * Internal NTP Era counters The internal counters that track the "era" (range of years) we are in rolls over every 136 years'. The current "era" started at the stroke of midnight on 1 Jan 1900, and ends just before the stroke of midnight on 1 Jan 2036. In the past, we have used the "midpoint" of the range to decide which era we were in. Given the longevity of some products, it became clear that it would be more functional to "look back" less, and "look forward" more. We now compile a timestamp into the ntpd executable and when we get a timestamp we us the "built-on" to tell us what era we are in. This check "looks back" 10 years, and "looks forward" 126 years. * ntpdc responses disabled by default Dave Hart writes: For a long time, ntpq and its mostly text-based mode 6 (control) protocol have been preferred over ntpdc and its mode 7 (private request) protocol for runtime queries and configuration. There has been a goal of deprecating ntpdc, previously held back by numerous capabilities exposed by ntpdc with no ntpq equivalent. I have been adding commands to ntpq to cover these cases, and I believe I've covered them all, though I've not compared command-by-command recently. As I've said previously, the binary mode 7 protocol involves a lot of hand-rolled structure layout and byte-swapping code in both ntpd and ntpdc which is hard to get right. As ntpd grows and changes, the changes are difficult to expose via ntpdc while maintaining forward and backward compatibility between ntpdc and ntpd. In contrast, ntpq's text-based, label=value approach involves more code reuse and allows compatible changes without extra work in most cases. Mode 7 has always been defined as vendor/implementation-specific while mode 6 is described in RFC 1305 and intended to be open to interoperate with other implementations. There is an early draft of an updated mode 6 description that likely will join the other NTPv4 RFCs eventually. (http://tools.ietf.org/html/draft-odonoghue-ntpv4-control-01) For these reasons, ntpd 4.2.7p230 by default disables processing of ntpdc queries, reducing ntpd's attack surface and functionally deprecating ntpdc. If you are in the habit of using ntpdc for certain operations, please try the ntpq equivalent. If there's no equivalent, please open a bug report at http://bugs.ntp.org./ In addition to the above, over 1100 issues have been resolved between the 4.2.6 branch and 4.2.8. The ChangeLog file in the distribution lists these. --- NTP 4.2.6p5 (Harlan Stenn , 2011/12/24) Focus: Bug fixes Severity: Medium This is a recommended upgrade. This release updates sys_rootdisp and sys_jitter calculations to match the RFC specification, fixes a potential IPv6 address matching error for the "nic" and "interface" configuration directives, suppresses the creation of extraneous ephemeral associations for certain broadcastclient and multicastclient configurations, cleans up some ntpq display issues, and includes improvements to orphan mode, minor bugs fixes and code clean-ups. New features / changes in this release: ntpd * Updated "nic" and "interface" IPv6 address handling to prevent mismatches with localhost [::1] and wildcard [::] which resulted from using the address/prefix format (e.g. fe80::/64) * Fix orphan mode stratum incorrectly counting to infinity * Orphan parent selection metric updated to includes missing ntohl() * Non-printable stratum 16 refid no longer sent to ntp * Duplicate ephemeral associations suppressed for broadcastclient and multicastclient without broadcastdelay * Exclude undetermined sys_refid from use in loopback TEST12 * Exclude MODE_SERVER responses from KoD rate limiting * Include root delay in clock_update() sys_rootdisp calculations * get_systime() updated to exclude sys_residual offset (which only affected bits "below" sys_tick, the precision threshold) * sys.peer jitter weighting corrected in sys_jitter calculation ntpq * -n option extended to include the billboard "server" column * IPv6 addresses in the local column truncated to prevent overruns --- NTP 4.2.6p4 (Harlan Stenn , 2011/09/22) Focus: Bug fixes and portability improvements Severity: Medium This is a recommended upgrade. This release includes build infrastructure updates, code clean-ups, minor bug fixes, fixes for a number of minor ref-clock issues, and documentation revisions. Portability improvements affect AIX, HP-UX, Linux, OS X and 64-bit time_t. New features / changes in this release: Build system * Fix checking for struct rtattr * Update config.guess and config.sub for AIX * Upgrade required version of autogen and libopts for building from our source code repository ntpd * Back-ported several fixes for Coverity warnings from ntp-dev * Fix a rare boundary condition in UNLINK_EXPR_SLIST() * Allow "logconfig =allall" configuration directive * Bind tentative IPv6 addresses on Linux * Correct WWVB/Spectracom driver to timestamp CR instead of LF * Improved tally bit handling to prevent incorrect ntpq peer status reports * Exclude the Undisciplined Local Clock and ACTS drivers from the initial candidate list unless they are designated a "prefer peer" * Prevent the consideration of Undisciplined Local Clock or ACTS drivers for selection during the 'tos orphanwait' period * Prefer an Orphan Mode Parent over the Undisciplined Local Clock or ACTS drivers * Improved support of the Parse Refclock trusttime flag in Meinberg mode * Back-port utility routines from ntp-dev: mprintf(), emalloc_zero() * Added the NTPD_TICKADJ_PPM environment variable for specifying baseline clock slew on Microsoft Windows * Code cleanup in libntpq ntpdc * Fix timerstats reporting ntpdate * Reduce time required to set clock * Allow a timeout greater than 2 seconds sntp * Backward incompatible command-line option change: -l/--filelog changed -l/--logfile (to be consistent with ntpd) Documentation * Update html2man. Fix some tags in the .html files * Distribute ntp-wait.html --- NTP 4.2.6p3 (Harlan Stenn , 2011/01/03) Focus: Bug fixes and portability improvements Severity: Medium This is a recommended upgrade. This release includes build infrastructure updates, code clean-ups, minor bug fixes, fixes for a number of minor ref-clock issues, and documentation revisions. Portability improvements in this release affect AIX, Atari FreeMiNT, FreeBSD4, Linux and Microsoft Windows. New features / changes in this release: Build system * Use lsb_release to get information about Linux distributions. * 'test' is in /usr/bin (instead of /bin) on some systems. * Basic sanity checks for the ChangeLog file. * Source certain build files with ./filename for systems without . in PATH. * IRIX portability fix. * Use a single copy of the "libopts" code. * autogen/libopts upgrade. * configure.ac m4 quoting cleanup. ntpd * Do not bind to IN6_IFF_ANYCAST addresses. * Log the reason for exiting under Windows. * Multicast fixes for Windows. * Interpolation fixes for Windows. * IPv4 and IPv6 Multicast fixes. * Manycast solicitation fixes and general repairs. * JJY refclock cleanup. * NMEA refclock improvements. * Oncore debug message cleanup. * Palisade refclock now builds under Linux. * Give RAWDCF more baud rates. * Support Truetime Satellite clocks under Windows. * Support Arbiter 1093C Satellite clocks under Windows. * Make sure that the "filegen" configuration command defaults to "enable". * Range-check the status codes (plus other cleanup) in the RIPE-NCC driver. * Prohibit 'includefile' directive in remote configuration command. * Fix 'nic' interface bindings. * Fix the way we link with openssl if openssl is installed in the base system. ntp-keygen * Fix -V coredump. * OpenSSL version display cleanup. ntpdc * Many counters should be treated as unsigned. ntpdate * Do not ignore replies with equal receive and transmit timestamps. ntpq * libntpq warning cleanup. ntpsnmpd * Correct SNMP type for "precision" and "resolution". * Update the MIB from the draft version to RFC-5907. sntp * Display timezone offset when showing time for sntp in the local timezone. * Pay proper attention to RATE KoD packets. * Fix a miscalculation of the offset. * Properly parse empty lines in the key file. * Logging cleanup. * Use tv_usec correctly in set_time(). * Documentation cleanup. --- NTP 4.2.6p2 (Harlan Stenn , 2010/07/08) Focus: Bug fixes and portability improvements Severity: Medium This is a recommended upgrade. This release includes build infrastructure updates, code clean-ups, minor bug fixes, fixes for a number of minor ref-clock issues, improved KOD handling, OpenSSL related updates and documentation revisions. Portability improvements in this release affect Irix, Linux, Mac OS, Microsoft Windows, OpenBSD and QNX6 New features / changes in this release: ntpd * Range syntax for the trustedkey configuration directive * Unified IPv4 and IPv6 restrict lists ntpdate * Rate limiting and KOD handling ntpsnmpd * default connection to net-snmpd via a unix-domain socket * command-line 'socket name' option ntpq / ntpdc * support for the "passwd ..." syntax * key-type specific password prompts sntp * MD5 authentication of an ntpd * Broadcast and crypto * OpenSSL support --- NTP 4.2.6p1 (Harlan Stenn , 2010/04/09) Focus: Bug fixes, portability fixes, and documentation improvements Severity: Medium This is a recommended upgrade. --- NTP 4.2.6 (Harlan Stenn , 2009/12/08) Focus: enhancements and bug fixes. --- NTP 4.2.4p8 (Harlan Stenn , 2009/12/08) Focus: Security Fixes Severity: HIGH This release fixes the following high-severity vulnerability: * [Sec 1331] DoS with mode 7 packets - CVE-2009-3563. See http://support.ntp.org/security for more information. NTP mode 7 (MODE_PRIVATE) is used by the ntpdc query and control utility. In contrast, ntpq uses NTP mode 6 (MODE_CONTROL), while routine NTP time transfers use modes 1 through 5. Upon receipt of an incorrect mode 7 request or a mode 7 error response from an address which is not listed in a "restrict ... noquery" or "restrict ... ignore" statement, ntpd will reply with a mode 7 error response (and log a message). In this case: * If an attacker spoofs the source address of ntpd host A in a mode 7 response packet sent to ntpd host B, both A and B will continuously send each other error responses, for as long as those packets get through. * If an attacker spoofs an address of ntpd host A in a mode 7 response packet sent to ntpd host A, A will respond to itself endlessly, consuming CPU and logging excessively. Credit for finding this vulnerability goes to Robin Park and Dmitri Vinokurov of Alcatel-Lucent. THIS IS A STRONGLY RECOMMENDED UPGRADE. --- ntpd now syncs to refclocks right away. Backward-Incompatible changes: ntpd no longer accepts '-v name' or '-V name' to define internal variables. Use '--var name' or '--dvar name' instead. (Bug 817) --- NTP 4.2.4p7 (Harlan Stenn , 2009/05/04) Focus: Security and Bug Fixes Severity: HIGH This release fixes the following high-severity vulnerability: * [Sec 1151] Remote exploit if autokey is enabled. CVE-2009-1252 See http://support.ntp.org/security for more information. If autokey is enabled (if ntp.conf contains a "crypto pw whatever" line) then a carefully crafted packet sent to the machine will cause a buffer overflow and possible execution of injected code, running with the privileges of the ntpd process (often root). Credit for finding this vulnerability goes to Chris Ries of CMU. This release fixes the following low-severity vulnerabilities: * [Sec 1144] limited (two byte) buffer overflow in ntpq. CVE-2009-0159 Credit for finding this vulnerability goes to Geoff Keating of Apple. * [Sec 1149] use SO_EXCLUSIVEADDRUSE on Windows Credit for finding this issue goes to Dave Hart. This release fixes a number of bugs and adds some improvements: * Improved logging * Fix many compiler warnings * Many fixes and improvements for Windows * Adds support for AIX 6.1 * Resolves some issues under MacOS X and Solaris THIS IS A STRONGLY RECOMMENDED UPGRADE. --- NTP 4.2.4p6 (Harlan Stenn , 2009/01/07) Focus: Security Fix Severity: Low This release fixes oCERT.org's CVE-2009-0021, a vulnerability affecting the OpenSSL library relating to the incorrect checking of the return value of EVP_VerifyFinal function. Credit for finding this issue goes to the Google Security Team for finding the original issue with OpenSSL, and to ocert.org for finding the problem in NTP and telling us about it. This is a recommended upgrade. --- NTP 4.2.4p5 (Harlan Stenn , 2008/08/17) Focus: Minor Bugfixes This release fixes a number of Windows-specific ntpd bugs and platform-independent ntpdate bugs. A logging bugfix has been applied to the ONCORE driver. The "dynamic" keyword and is now obsolete and deferred binding to local interfaces is the new default. The minimum time restriction for the interface update interval has been dropped. A number of minor build system and documentation fixes are included. This is a recommended upgrade for Windows. --- NTP 4.2.4p4 (Harlan Stenn , 2007/09/10) Focus: Minor Bugfixes This release updates certain copyright information, fixes several display bugs in ntpdc, avoids SIGIO interrupting malloc(), cleans up file descriptor shutdown in the parse refclock driver, removes some lint from the code, stops accessing certain buffers immediately after they were freed, fixes a problem with non-command-line specification of -6, and allows the loopback interface to share addresses with other interfaces. --- NTP 4.2.4p3 (Harlan Stenn , 2007/06/29) Focus: Minor Bugfixes This release fixes a bug in Windows that made it difficult to terminate ntpd under windows. This is a recommended upgrade for Windows. --- NTP 4.2.4p2 (Harlan Stenn , 2007/06/19) Focus: Minor Bugfixes This release fixes a multicast mode authentication problem, an error in NTP packet handling on Windows that could lead to ntpd crashing, and several other minor bugs. Handling of multicast interfaces and logging configuration were improved. The required versions of autogen and libopts were incremented. This is a recommended upgrade for Windows and multicast users. --- NTP 4.2.4 (Harlan Stenn , 2006/12/31) Focus: enhancements and bug fixes. Dynamic interface rescanning was added to simplify the use of ntpd in conjunction with DHCP. GNU AutoGen is used for its command-line options processing. Separate PPS devices are supported for PARSE refclocks, MD5 signatures are now provided for the release files. Drivers have been added for some new ref-clocks and have been removed for some older ref-clocks. This release also includes other improvements, documentation and bug fixes. K&R C is no longer supported as of NTP-4.2.4. We are now aiming for ANSI C support. --- NTP 4.2.0 (Harlan Stenn , 2003/10/15) Focus: enhancements and bug fixes. ntpsec-1.1.0+dfsg1/devel/hacking.txt0000644000175000017500000005606013252366670017106 0ustar rlaagerrlaager= How-to for NTPsec hackers = Annoying but necessary legalese: By submitting patches to this project, you agree to allow them to be redistributed under the project's license according to the normal forms and usages of the open-source community. If you want to learn more about the code internals, consult the ../devel/tour.txt file. This document is about development practices and project conventions. You may also find the articles at https://blog.ntpsec.org/ interesting. == Choice of language == In order to reduce the complexity of maintenance and testing due to multiple languages, we hold the set of allowed languages to a minimum. Time-critical code must be written in C. Use Python for any code that is not time-critical, as this reduces line count and complexity (thus, also, expected defect rates). If you need a NTP-specific C function not available in Python, try to improve the libntpc extension in ../libntp/pymodule.c to expose it. The only scripting language allowed and supported other than Python is POSIX sh (this is more restricted than bash!). Python is preferred, as it's easier to verify portability in Python than it is in sh. Please read our C and Python guidelines carefully. They're not just about style, they're serious measures to reduce defect rates. == C Guidelines == === C API standards === The baseline C standard to write to is ISO/IEC 9899:1999, also known as C99 (but see the following section on banned functions). Note that C99 is a coding standard, not an ABI standard, so it can happily coexist with distributions that use C89 conventions. You can download the C99 standard for free from here: https://atrey.karlin.mff.cuni.cz/projekty/vrr/doc/c99.pdf You *may* use C11-style anonymous unions. Only POSIX-1.2001/SUSv3 library functions should be used (a few specific exceptions are noted below). If a library function not in that standard is required, then a wrapper function for back compatibility must be provided. One notable case is clock_gettime() which is used, when available, for increased accuracy, and has a fallback implementation using native time calls. You can view POSIX-1.2001, with 2004 Corrigendum, online for free here: http://pubs.opengroup.org/onlinepubs/009695399/toc.htm You can view POSIX.1-2001, SUSv3, online for free here: http://www.unix-systems.org/version3/ POSIX threads *are* considered part of the standardized API and may be used. You *may* assume the clock_gettime(2) and clock_settime(2) calls, and the related getitimer(2)/setitimer(2), from POSIX-1.2008. Here are the non-standardized APIs that may be used: * Either ntp_adjtime() or the older BSD adjtime(2). * Berkeley sockets and the IPv6 API defined in RFC 2493 and RFC 2553. * getifaddrs(3) or an equivalent local API for iterating over the system's active UDP interfaces. However, the local details should be hidden as an implementation of the isc_interfaceiter.c functions, not called directly from the rest of the code. * Local facilities for reading packet arrival timestamps out of the UDP packet layer. See ntpd/ntp_packetstamp.c for details - if you add a local hack to do this, it needs to be sealed inside that module. === Banned functions === The following C99/POSIX functions are BANNED. They are unsafe, tending to cause buffer overruns and (all too often) exploitable security holes: * strcpy, strncpy, strcat: Use strlcpy and strlcat instead. * sprintf, vsprintf: use snprintf and vsnprintf instead. * In scanf and friends, the %s format without length limit is banned. * strtok: use strtok_r() or unroll this into the obvious loop. * gets: Use fgets instead. * gmtime(), localtime(), asctime(), ctime(): use the reentrant *_r variants. * tmpnam(): use mkstemp() or tmpfile() instead. Do not rely on dirname() being reentrant, and assume it will modify (truncate) its argument. The Linux version is re-entrant, but this property is not portable. In general, avoid functions that are non-reentrant. When in doubt, see http://www.unix.org/whitepapers/reentrant.html[Thread-safety and POSIX.1] === Banned features === All C files should be in plain US-ASCII encoding; do not use trigraphs. === Other interfaces to be avoided === Don't use gettimeofday(2). While this is nominally POSIX, it is deprecated and may be removed in the future. Use clock_gettime(2) instead. Use pselect(2) rather that select(2), to avoid introducing struct timeval. In general, avoid introducing the type struct timeval into the code, in favor of the higher-resolution struct timespec. Early on in NTPsec's history we found a bug introduced by poor data management where these two time representations bumped into each other; we don't want that to happen again. Thus, if you must refer to struct timeval due to an external interface, move the data to/from a struct timespec as close to that callsite as possible. === Coding style and indentation === Dr. Dave Mills liked this code indented formatted in a consistent way. The file "dot.emacs" has the emacs C-mode indentation style that Dave likes. It's a bit archaic, but we've stuck with it to preserve continuity; you should, too. A discussion about using uncrustify to mass convert all the C sources to a more modern indentation and format style is ongoing. As it will result in a coordinated flag day in ongoing development, it will be carefully announced in the mailto:devel@ntpsec.org mailing list before being merged and pushed. === Conventions for #ifdef guard names === Parts of this code are a thicket of C preprocessor conditionals. In an attempt to make these halfway comprehensible, we use the following conventions to distinguish classes of macro names: ENABLE_*:: Gates the code for an optional feature. Set by a switch on the "waf configure" invocation. GUARD_*:: Symbols with the GUARD_ prefix are idempotency guards - that is, they're used to nullify inclusions of a header file after the first. They don't interact with the build system's configuration logic in any way at all. HAVE_*_H:: Guard symbol derived by configuration logic from checking for the presence of a system header. For example, the symbol HAVE_SYS_FOOBAR_H gets defined only if waf configure detects the presence of a sys/foobar.h in the system include directory. HAVE_*:: Without an H suffix, a HAVE symbol is set on the availability of a specified function in the system libraries. NEED_*:: Need symbols conditionalize porting hacks the need for which cannot be detected by checking for a system header or function, but instead have to be probed for by some ad-hoc test in waf configure. OVERRIDE_*:: Override a default for debugging purposes. These are values (buffer lengths and the like) which waf is not expected to normally override but which might need to be forced. USE_*:: Use symbols are set internally within other conditionals to gate use of sections of code that must be conditionally compiled depending on *combinations* of HAVE and NEED symbols. === Cross-platform portability === Do not bake in any assumptions about 32-vs-64-bit word size. It is OK to assume the code will never run on a 16-bit machine. When in doubt, and whenever possible, use the fixed-width integral types from . You *may* assume that the compiler supports POSIX 64-bit integral types (int64_t, uint64_t and friends) even if the target hardware is 32-bit. Do not assume any particular endianness. When in doubt, use htons()/htonl()/ntohs()/ntohl() and do your bit-bashing in network (big-endian) byte order. Do not assume anything about sign-bit interpretation in chars. Target machines may have either signed or unsigned characters. Do not rely on assumptions about how structure or unions are padded. Historically, the NTP code assumed self-alignment. We're trying to eliminate that assumption, but the work isn't finished. Do not assume pointers can be cast to ints, or vice-versa. While this is true on effectively all modern hardware, the code runs on some sufficiently old iron that this is not necessarily the case even if the compiler and toolchain have been modernized. == Python guidelines == You may assume Python 2 at 2.6 or later, or Python 3 at 3.3 or later. Please read https://www.python.org/dev/peps/pep-0008/[PEP 8] and use that style. The only PEP 8 style rule we relax is that you may specify multiple module names in an import rather than going strictly with one per line. The point is to encourage you to group your import declarations in informative ways. You *must* write Python code to be 'polyglot', that is able to run unaltered under 2 or 3. Practices for doing so are documented in detail at http://www.catb.org/esr/faqs/practical-python-porting/ Note that Python 3.x versions before 3.3 had sufficiently serious back-compatibility issues that trying to make them run is probably doomed. The first 3.x version under which our Python has been extensively tested is 3.5. Please check your Python code with Pyflakes. If your code fails a Pyflakes test, we probably will not merge it. https://pypi.python.org/pypi/pyflakes Note: In the future, the Python baseline may http://www.curiousefficiency.org/posts/2015/04/stop-supporting-python26.html[change t 2.7.2]. == General notes == === Build system === The build uses waf, replacing a huge ancient autoconf hairball that caused many problems. The waf script is embedded in the top level of the distribution; run "./waf --help" or consult INSTALL for basic instructions. Full waf documentation is at: https://waf.io/ === Naming conventions === Every binary and script we install has an "ntp" prefix on the name, because namespace pollution is rude. If you write a new tool that you want us to install, follow this convention. Generally we favor "ntp" rather than "ntp-" for consistency and to reduce the amount people have to type. Choose tastefully. === Well-tempered output === We are devotees of the Unix rule that programs should play nicely with other programs. We like output formats that are simple, regular, and machine-parseable without ambiguity. The practical goal to aim at, given our choice of scripting languages, is is to make writing script wrappers in Python easy. There is more than one way to arrange this. If you can design a simple tabular output format, or something resembling an RFC 2822 header that's easy for both human eyes and programs to parse, do that. Besides being simple, formats like these are easily handled by either Python or shellscripts. Such simplicity is often difficult or impractical for heterogeneous data that needs to be both grouped and labeled, so we have another convention for those cases. Here it is: [quote] Wherever it is reasonable, tools that generate complex reports to standard output should be able to emit two formats. The default can be relatively unstructured multiline text for human eyeballs. There should also be a -j/--json option that emits a self-describing JSON object. You can read about JSON at http://www.json.org/ Be aware that if you present a tool design with a messy output format and no JSON option, it is quite likely to be rejected. Our preferred format for dates is RFC 3339 (a version of ISO 8601 for UTC with some options frozen; full year required, medial T required, explicit Zulu timezone). Local times should be expressed in ISO 8601, always with full 4-digit year. === Copyrights and licenses === Much of the historic code in this distribution is under the "NTP License" resembling BSD-2-clause. Our favored licenses are BSD-2-clause for code and Creative Commons Attribution 4.0 License for documentation. Please do *not* submit code under GPL or other licenses which place conditions on derived works; we cannot accept such code. It is your responsibility to make sure you have the necessary rights to contribute a patch to the project. We use the SPDX convention for inclusion by reference. You can read about this at https://spdx.org/licenses When you create a new file, mark it as follows (updating the year) as required: ------------------------------------------------ /* Copyright 2017 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-Clause */ ------------------------------------------------ For documentation: ------------------------------------------------ // Copyright 2017 by the NTPsec project contributors // SPDX-License-Identifier: CC-BY-4.0 ------------------------------------------------ Modify as needed for whatever comment syntax the language or markup uses. Good places for these markings are at the end of an extended header comment, or at the very top of the file. When you modify a file, leave existing copyright markings in place - especially all references to Dr. Dave Mills, to Mr. Harlan Stenn, and to the Network Time Foundation. You *may* add a project copyright and replace the inline license with an SPDX tag. For example: ------------------------------------------------ /* Copyright 2017 by the NTPsec project contributors * SPDX-License-Identifier: NTP */ ------------------------------------------------ We recognize that occasionally a file may have changed so much that the historic copyright is no longer appropriate, but such decisions cannot be made casually. Discuss it with the project management before moving. === Document what you do === When you change anything user-visible, you are expected to update the relevant documentation *in the same commit*. No exceptions. Otherwise we'd have to inflict long, tedious document reviews on everybody. Nobody wants that. === Documentation format and structure === All our documentation is mastered in asciidoc. That includes internal documentation like this file. We do this because asciidoc is easy to edit and gives us good options for rendering to multiple formats, including both HTML and Unix manual pages If you're going to write in anything else you need to have a good reason, and the bar for "good" will be set high. "I like Markdown", in particular, does not qualify - Markdown doesn't have good enough table support for our needs. ReST does, but the hassle costs of supporting two different master markups are too high. If you must use non-ASCII characters, use UTF-8 and not Latin-1 or any other encoding. Best practice is to use XML character entities. The NTP Classic documentation had a terrible problem with duplicative documentation gradually diverging as the duplicates mutated and bitrotted. Therefore one of our house rules is to have a *single point of truth* for everything. Accordingly, the way we handle pairs of manual and Web pages that need to have the same content is to have both be thin wrappers around a large common include file. These includes live in docs/includes and are probably what you need to edit if you're updating anything that appears on a man page. === Version string === We use a variant of three part Semantic Versioning, of the form X.Y.Z. X, Y, and Z are non-negative decimal integers. X is the "major" version number. Y is the "minor" version number. Z is the "revision" number. Each release will result in an incremented version number, and the version number string will be tagged into the git repository. We have dropped even/odd minor version number stable/development release semantics. Development on NTPsec has been carefully incremental with a strong emphasis on stabilty and correctness, such that it turned out to be unneeded. The first public release was version 0.9.0. The first production and distribution ready release was 1.0.0. We currently have no formal policies for the criteria for releases, for the schedule of releases, and no formal policies for backporting bugfixes. Feel free to discuss it with project management. Note that this is a different numbering system from NTP Classic. In their A.B.C numbers, A was the protocol version, B was the major, and C was the minor. They also use release-candidate suffixes. == Contribution workflow and conventions == Please work on one piece of conceptual work at a time. Please make sure your code builds and passes the test suite before you commit it, and especially before you push it. Before starting significant work, propose it first in the mailto:devel@ntpsec.org mailing list. Other people may have suggestions, will want to collaborate, and will wish to review your code. === Git === We use Git as our distributed version control system. If you ever get stuck or confused while using Git, please consult http://ohshitgit.com/ === GitLab.com === We use GitLab.com as our forge. Our GitLab group is at https://gitlab.com/groups/NTPsec Please use the issue tracker and the pull request process at GitLab.com. If you wish, you can request to join the GitLab project team at https://gitlab.com/groups/NTPsec/group_members and we will add you to the team with Guest access. This will cause GitLab to send issue tracker updates and pipeline updates to your email address. You do not have to formally be a member of the GitLab team to participate, contribute, or send issues, patches, or pull requests. Granting other levels of GitLab project team access is at the discretion of the Project Manager, after consulting with the existing core team. Generally, other levels of access will not be granted, as they are not necessary to be a welcome and effective contributor. === Optional: Send patches by email === If you do not want to use GitLab pull requests, we welcome simple fix and "drive-by" patches submitted by email. Please create the patch with git format-patch. If for some reason it is impossible for you to use git format-patch, at least send context (-c) or unified (-u) diffs rather than the default ed (-e) style, which is very brittle. You can email your patch to mailto:devel@ntpsec.org if you are a member of that mailing list, or you can email your patch to mailto:contact@ntpsec.org if you are not. Please make sure your "From:" header in the email is correct, as that is what will be used as attribution of the commit. The person on the team that merges in your patch will use the git parameter ---author from the email From header and the git parameter --date from the email Date header. For complex patches and contribution narratives, please use GitLab. == Commit comments == And please follow git conventions for change comments. That means your comment should consist of: * A summary line, never more than 69 characters long and ideally no more than 50 characters long. These numbers are set by the window sizes of various common web views of git repositories. * Your summary line should be terse and imperative. "Fix bug #666" "Add DWIM feature" and "Typo repair" are good summary lines. * If your comment is longer than the summary line, separate it from the summary with a blank line. * The remainder of your comment should be one or more paragraphs line-wrapped at 72 characters - please do *not* enter entire paragraphs as single lines, it makes life more difficult for browsing tools and people viewing the output of git format-patch. Bulleted list items are also OK. * In some cases it may be appropriate to end your summary line with a comma or ellipsis ("...") to indicate that it runs directly into a following paragraph. You should still try to make the summary self-contained when you do this. Finally, it is not necessary (and is in fact bad style) to list all the files a multi-file commit touches in the comment for it. The --name-status, --name-only, and --stat options of git log will report this information to users who want it. It is still appropriate to list a file path if you have something specific to say about that individual file, or if it's the only one touched in a single-file change and you can easily fit it in the summary line along with your summary. Yes, we know the pre-git portions of the history violate some of these. That was then, this is now. === How to refer to previous commits === The best (most human-friendly) way to reference a commit is by quoting its summary line. If you need to disambiguate, give its date and author. The worst way is to quote its git hash, because humans are not good at keeping random strings of hex digits in working memory. Besides, hashes will break if the history is ever moved to another VCS or the repository has to be surgically altered. === Avoid unnecessary merge bubbles === There are two kinds of merge bubbles in git commit graphs. The necessary kind happens because branches have genuinely diverged enough that a merge by hand is required - common files have been touched in incompatible ways. In the unnecessary kind, the patches on either side of the bubble commute cleanly, but the developer on one side or the other forgot to rebase so his commit would be a fast-forward. We strongly dislike unnecessary merge bubbles. They make the repository history difficult to read, and can make bisection tests trickier. We prefer the code to have a simple, close-to-linear history even if that means older commits are sometimes fast-forwarded from new ones because a long-lived branch was rebased. To avoid merge bubbles, git pull --rebase before pushing. This will pull all pending commits from the repo, then attempt to rebase your local commits on the new tip. You may find it helpful to set "rebase = true" in your .git/config, so it looks like this: ------------------------------------------------------------- [branch "master"] remote = origin merge = refs/heads/master rebase = true ------------------------------------------------------------- Setting this option adds --rebase to all your pulls. This may cause minor inconvenience when you have uncommitted local changes; you should be able to use "git stash" to get around that. == Logging tags == To facilitate analysis of logs, log messages are tagged with an initial topic group token. These are: DNS:: DNS Lookup MAC:: Message authentication hash computation AUTH:: Authorization-key handling ERR:: Low-level errors from resource-management libraries LOG:: Log switching and debug levels CLOCK:: Low-level clock manipulation and validation checks & leap-second code CONFIG:: Configuration parsing and interpretation INIT:: Daemon setup SYNC:: Server synchronization PROTO:: Protocol machine actions REFCLOCK:: Reference clock and driver actions MODE6:: Processing of Mode 6 requests == Release Checklist == This is the release checklist to be used by the project manager to cut each 0.9.X release. . Decide that it is time to cut a release. This decision can be driven by landing a significant new feature, landing a critical fix, or just that enough time has passed with ongoing improvements and fixes. . Email a warning message to the mailto:devel@ntpsec.org list, and ask the major contributors to chime in, and to each assure that the .../NEWS file and the .../devel/TODO file is up to date. . Wait for the contributors to answer and for the discussion to settle down. If the discussion suggests that now is not a good time to cut a release, wait until the raised issues are resolved. . Check with the buildbot reports, assure that there are no unplanned regressions on the supported platforms. . Modify the .../VERSION file with the new version number. Version number strings look like "0.9.1" . Modify the .../NEWS file, changing the "Repository head". to the current date and the version string. . Run the "release" script in this (devel) directory. //end ntpsec-1.1.0+dfsg1/devel/README0000644000175000017500000000226113252364117015605 0ustar rlaagerrlaager= README for development documentation = This directory contains internal and historical documentation of interest to NTP developers. Some of the files are: CommitLog-4.1.0:: Changes from 1997 to 2001. ChangeLog:: Changes from 2001 to mid-2015. More recently, consult the version- control comment log. dot.emacs:: C-mode indentation rules for code "Just the way Dave likes it". hacking.txt:: Notes to folks who want to hack on the code. ifdex-ignores:: Lists and explains a large number of internal configuration symbols. make-tarball:: Script for building a release tarball. ntpv5.txt:: Design notes towards NTPv5. pre-release.txt:: A collection of ideas about testing before a release. release:: Script for shipping a release. TODO:: List of items the NTPsec developers are working on. testing.txt:: How to qualify NTPsec for production use. tour.txt: A tour of the NTP internals. HISTORIC-NEWS:: NTP Classic news file up to 4.2.8p3 (May 2015). y2k.txt:: Notes on Y2K conformance. release.nix:: NixOS build recipe. trace/:: Configuration files for the RTEMS trace utility. These are for an experimental cross-port that is not yet officially supported. //end ntpsec-1.1.0+dfsg1/devel/make-tarball0000755000175000017500000000247113252364117017212 0ustar rlaagerrlaager#!/bin/sh # # Make a release tarball from current head. If you give an argument, it # will override the version normally taken from ../VERSION. Produces # a file named ntpsec-$V.tar.gz. The name of this tarball is echoed to # standard output as part of the success message. # # The tricky part is that it has to include the autorevision cache file, # which can't be checked into the repo or there'd be an infinite loop. # # Do not try running this outside of devel/ # # *Do* configure and test-build before running it. if [ `basename $PWD` != devel ] then echo "You are doomed to fail." exit 1 fi if [ ! -r ../wafhelpers/.autorevision-cache ] then echo "Autorevision cache file does not exist, waf build and try again" exit 1 fi set -e if [ "$1" != "" ] then V=$1 else V=`cat ../VERSION` fi # Build the tarball rm -fr .tmp (cd ..; git ls-files; find build -print | grep '\.[0-9]$'; echo "wafhelpers/.autorevision-cache") >MANIFEST (cd ..; tar --transform="s:^:ntpsec-${V}/:" -T devel/MANIFEST -czf ntpsec-${V}.tar.gz) rm MANIFEST mv ../ntpsec-${V}.tar.gz . # Test-build from it, bailing out if we fail set -e mkdir .tmp cd .tmp tar -xzf ../ntpsec-${V}.tar.gz cd ntpsec-${V} ./waf configure --refclock=all --enable-doc ./waf build cd ../.. rm -fr .tmp echo "" echo "Success: " ntpsec-${V}.tar.gz # end ntpsec-1.1.0+dfsg1/devel/pre-release.txt0000644000175000017500000000512213252364117017671 0ustar rlaagerrlaagerThis is a collection of ideas about how/what to test when we are preparing for a release. Most of them require manual action. It assumes we have a release candidate tarball and git tag. We should probably make a web page so we can collect check-boxes when we get reports that something works and graphs of typical operations so we have a reference for the next release. The first step is to make sure it builds cleanly on as many OSes/distros as we can. The second step is to make sure it runs. Unfortunately, "it" gets complicated due to the collection of features and options and the possible interactions. In this context, we should treat different versions of an OS/distro as separate items. Similarly, we should have separate slots within an OS/distro for different hardware platforms. The obvious platforms are Intel-64, Intel-32, ARM-32. ARM-64 would be nice but I don't know if that is ready yet. (Jan 2017) We really need to test on an other-endian platform. NetBSD runs Big Endian on some ARM systems. https://wiki.netbsd.org/ports/evbarm/allwinner/ Debian runs Big Endian on MIBSbe (tested May 2017). We should try as many refclocks as we can on as many OS/distros as we can. This is testing the OS kernel as well as ntpd. If possible, we should at least test the NMEA driver with the PPS driver and via SHM. Note that there are two levels of PPS to test. The first is just being able to get the data. The second is to see if the in-kernel PLL as activated by flag3 on the PPS driver works. On Linux, that needs building a custom kernel with CONFIG_NO_HZ turned off and CONFIG_NTP_PPS on. We should review any documentation for best-practices and verify that any sample code and build recipies work as expected. In particular, we should make sure the pool option works and that the Raspberry Pi Stratum 1 server HOWTO works. (There are two sample ntp.conf files in contrib/ . Both use the pool. Jan 2017) We should bump the clock (ntpfrob -b) and watch how it recovers. We should probably do this on every OS. There should be 4 cases. For small excursions, less than 128 ms, the clock should be adjusted. For most OSes, it will slew. For larger excursions, the clock will be stepped. If the clock is way off (over 600 seconds), ntpd should panic and exit, expecting a human to fix it. The -g command line switch will override that panic and allow one big step. We should verify that the cx_Freeze recipe for converting our Python based utilities to non-Python works. Details are in devel/packaging.txt We should test cold-start with no drift file. xx All the options in ntp.conf, debug, crypto ntpsec-1.1.0+dfsg1/devel/CommitLog-4.1.00000644000175000017500000057561413252364117017221 0ustar rlaagerrlaagerThis is the historical change log from before the source was under version control. Describes changes from 1997 to 2001. For changes from 2001 to mid-2015, see ChangeLog. For newer changes, browse the comments in the version-control system. 2001-08-01 Harlan Stenn * configure.in: 4.1.0 2001-07-27 Harlan Stenn * ntpd/refclock_oncore.c (oncore_start): Set pps_enable=1, just like the atom driver does. From: reg@dwf.com * ntpd/refclock_nmea.c (nmea_ppsapi): Set pps_enable=1, just like the atom driver does. From: Scott Allendorf * ntpd/ntp_config.c (getconfig): CONF_CLOCK_PANIC was using the wrong config flag. From: 2001-07-10 Harlan Stenn * configure.in: 4.0.99m-rc3 2001-07-06 Harlan Stenn * ntp_update: COPYRIGHT needs a touch. From: Mike Stump 2001-07-04 Harlan Stenn * html/config.htm: Major cleanup. From: Martin Janzen * configure.in (rt library check): Don't look for -lrt under Linux. Under glibc-2.1.2 and -2.2.2 (at least), the POSIX- compatibility real-time library does strange things with threads as other processes and we're getting lots of complaints about it. Reported by: Juha Sarlin 2001-06-30 Harlan Stenn * html/driver35.htm: Update email address. 2001-06-25 Harlan Stenn * ntpd/refclock_oncore.c (oncore_msg_BaEaHa): Fix wrong offset for rsm.bad_almanac From: Reynir Siik 2001-06-12 Harlan Stenn * configure.in: 4.0.99m-rc2 2001-06-10 Harlan Stenn * ntpd/ntp_config.c: * include/ntp_config.h: includefile config keyword support From: Dean Gibson 2001-06-08 Harlan Stenn * configure.in: 4.0.99m-rc1b * ntpd/refclock_true.c (true_debug): Bump some buffer sizes to reduce/eliminate chance of buffer overflow. Use snprintf() instead of sprintf(). Do a better job of opening the debug file. * ntpd/ntp_control.c (ctl_getitem): Count overflow packets as bad and return a BADFMT. * ntpd/ntp_config.c (save_resolve): call fdopen() with the correct mode. From: Bela Lubkin 2001-06-03 Harlan Stenn * include/ntp.h (RES_ALLFLAGS): Add RES_DEMOBILIZE. From: Dean Gibson * configure.in: 4.0.99m-rc1a 2001-06-02 Harlan Stenn * ntpd/ntp_refclock.c (refclock_open): Add O_NOCTTY to the open() flags when opening a serial port. Reported by: joseph lang 2001-05-31 Harlan Stenn * html/notes.htm: Typo fix. From: John Stone * configure.in: 4.0.99m-rc1 * html/monopt.htm: Typo fix. * html/confopt.htm: Cruft removal. From: John Stone 2001-05-30 Harlan Stenn * README.cvs: More updates and cleanup. * ntpd/ntp_loopfilter.c (loop_config): Check against STA_NANO instead of (NTP_API > 3) to catch kernels that were rolled while the spec was evolving. From: John.Hay@icomtek.csir.co.za * README.cvs: Note that we want to check out NTP into a clean subdir. Reported by jrd@cc.usu.edu (Joe Doupnik) 2001-05-27 Harlan Stenn * configure.in: 4.0.99k40 * include/ntp_refclock.h: Median Filter (SAMPLE - macro) - change to use most recent MAXSTAGE entries when the filter overflows (ie driver poking say once per second with poll > MAXSTAGE) rather than blocking after MAXSTAGE entries (turf oldest rather than turf most recent). From: John Woolner * ntpd/refclock_true.c: a. Don't cream pp->a_lastcode when we get a pair b. Fix up pp->leap handling to work correctly c. clear CEVNT_BADTIME etc warnings when we get good clock CEVNT_NOMINAL. From: John Woolner * kernel/sys/pcl720.h: Add support for the XL clock to refclock_true.c From: Paul A Vixie * ntpd/ntp_loopfilter.c (local_clock): One more attempt at "improving" the panic message. 2001-05-26 Harlan Stenn * configure.in (ac_cv_func_ctty_for_f_setown): BSDI3 needs a ctty for F_SETOWN, too. From: Paul A Vixie 2001-05-24 Harlan Stenn * html/ntpd.htm: Typo. From: John Stone 2001-05-23 Harlan Stenn * configure.in: 4.0.99k39 * ntpd/ntp_loopfilter.c (local_clock): huffpuff cleanup/improvements. (huffpuff): Cleanup/improvements. (loop_config): huffpuff initialization cleanup/improvements. From: Dave Mills, Terje, Mark, and John? 2001-05-22 Harlan Stenn * html/release.htm: * html/ntpd.htm: * html/miscopt.htm: From: Dave Mills: Updates. 2001-05-21 Harlan Stenn * configure.in: 4.0.99k38 * ntpd/ntp_proto.c (clock_filter): Huff-n-Puff and Popcorn improvements. * ntpd/ntp_loopfilter.c (local_clock): Debug cleanup From: Dave Mills. * include/ntp_syscall.h (ntp_gettime): Updated patch from Ulrich. My original attempt was not backwards compatible. 2001-05-17 Harlan Stenn * include/ntp_syscall.h (ntp_gettime): Fill in the tai member. From: Ulrich Windl * configure.in: 4.0.99k37 * ntpd/ntp_proto.c (clock_filter): Lose "off", xtemp and ytemp, and some obsoleted calculations. Set the peer->offset and peer->delay from the filter stages. * ntpd/ntp_loopfilter.c: Comment/document improvements. (local_clock): correct the offset by one-half the difference between the sample delay and minimum delay. Lose "mu" from the debug message. From: Dave Mills. 2001-05-15 Harlan Stenn * configure.in: 4.0.99k36 * ntpd/ntp_loopfilter.c: Huff-n-puff cleanup From: Dave Mills. 2001-05-14 Harlan Stenn * configure.in: 4.0.99k35 * ntpd/refclock_atom.c (atom_ppsapi): set pps_enable=1 if enb_hardpps. * ntpd/ntp_timer.c: huffpuff support. (init_timer): huffpuff support. (timer): huffpuff support. * ntpd/ntp_proto.c (init_proto): Initialize pps_enable to 0, not 1. * ntpd/ntp_loopfilter.c (CLOCK_HUFFPUFF): Added. Add huff-n-puff filter variables. (local_clock): Lose "pps sync enabled" log noise. (huffpuff): Added. (loop_config): LOOP_MINPOLL and LOOP_ALLAN were missing the trailing break; add LOOP_HUFFPUFF. * ntpd/ntp_config.c: tinker huffpuff added. (getconfig): CONF_CLOCK_HUFFPUFF support. * include/ntpd.h: huffpuff() declaration. * include/ntp_config.h (CONF_CLOCK_HUFFPUFF): Added. * include/ntp.h (HUFFPUFF): Added. (LOOP_HUFFPUFF): Added. From: Dave Mills. 2001-05-11 Harlan Stenn * html/driver20.htm: Reality check. * ntpd/refclock_nmea.c: Comment cleanup From: John Woolner * html/release.htm: Cleanup (at least). * html/refclock.htm: Cleanup (at least). * html/kern.htm: Cleanup (at least). * html/index.htm: Cleanup (at least). * html/extern.htm: Cleanup (at least). * html/driver1.htm: Cleanup (at least). * html/debug.htm: Cleanp (at least). * html/accopt.htm: KoD documentation update. From: Dave Mills. * configure.in: 4.0.99k34 * ntpd/ntp_util.c (record_loop_stats): values are now passed in. * ntpd/ntp_loopfilter.c (local_clock): pass the values to record_loop_stats(). * include/ntpd.h: Pass the parameters in to record_loop_stats(). With the discipline loop opened (disable ntp) the local clock updates were not being sent to loopstats. That now is. From: Dave Mills. 2001-05-10 Harlan Stenn * configure.in: 4.0.99k33 * ntpd/ntp_proto.c (receive): Validate the source port. Lose NTPv1 support. * ntpd/ntp_loopfilter.c (local_clock): Sanity check sys_poll earlier instead of later. From: Dave Mills. * ntpd/refclock_oncore.c (oncore_msg_any): We don't always have GETTIMEOFDAY(). 2001-05-09 Harlan Stenn * ntpd/refclock_shm.c (shm_poll): Apply JAN_1970 correction after calling TVTOTS(), just like everybody else does. From: David Malone * ntpd/refclock_ulink.c: fixed 33x quality flag, added more debugging stuff, updated 33x time code explanation. From: s.l.smith (via j.c.lang). 2001-05-08 Harlan Stenn * configure.in: 4.0.99k32 * ntpd/ntp_loopfilter.c: rstclock now takes a 3rd argument, the last offset. (init_loopfilter): Use it. (local_clock): Use it. Clean up the code. (loop_config): Use it. (rstclock): Implement it. Clean up the code. From Dave Mills. 2001-05-06 Harlan Stenn * configure.in: 4.0.99k31 * ntpdc/ntpdc_ops.c (sysstats): That's 'bad packet format' (instead of '... length'), and 'packets rejected' (instead of 'limitation rejects'. * ntpd/ntp_proto.c (receive): PUBKEY fixes. Move KoD stuff to process_packet(). (process_packet): Move KoD stuff here... (peer_clear): Unspec the stratum, too. (clock_filter): Don't update peer->epoch here. Fix the filter test when checking the epoch. (fast_xmit): Send back STRATUM_UNSPEC on a KoD packet. (init_proto): Initialize sys_jitter. * ntpd/ntp_loopfilter.c: rstclock() takes 2 parameters now. (init_loopfilter): Use it... (local_clock): Ditto, and change the "mu" calculation. Improve the jitter test in S_SYNC. Use peer->epoch (not current_time) to update the last_time. Update debug info. (rstclock): 2nd arg - the epoch to use. Use it. (loop_config): update call to rstclock. From: Dave Mills. 2001-05-01 Harlan Stenn * ports/winnt/ntpd/ntpd.dsp: Add cmd_args.c From: Wink Saville 2001-04-29 Harlan Stenn * ntpq/ntpq.c (tstflags): 11 now. From: John Cochran * ntpd/ntp_proto.c (receive): KoD updates. Improve the comments. Lose the AM_PROCPKT restrictions test. (peer_xmit): Check/report on no encryption key in packet. (fast_xmit): Use peer_xmit's new packet length check code. From Dave Mills. 2001-04-28 Harlan Stenn * configure.in: 4.0.99k30 2001-04-27 Harlan Stenn * ntpdc/ntpdc_ops.c: Added "kod", lost "demobilize". * ntpd/ntp_config.c: Added "kod" keyword. Lose "demobilize" keyword. * html/release.htm: Updated. * html/accopt.htm: Updated. From: Dave Mills. * ntpq/ntpq.c: Reorder and add some TEST flag bits. * ntpd/ntp_proto.c (transmit): Also bail if access denied. (receive): Lose RES_DEMOBILIZE and (some?) RES_DONTSERVE and RES_LIMITIED stuff. Update Kiss-Of-Death (KoD) docs. Call fast_xmit with new 3rd parameter (restrict_mask). Before checking for an authentic packet, check the restrict_mask for RES_{DONTSERVE,LIMITED,NOPEER}. Check restrictions in AM_PROCPKT case. (peer_clear): Don't lose the stratum if the peer->flags don't indicate FLAG_REFCLOCK. (fast_xmit): Take restrict mask as a new argument, and handle KoD. Reorder some code. From: Dave Mills. 2001-04-26 Harlan Stenn * ntpdc/ntpdc_ops.c: restrict/unrestrict support for version and demobilize. Implement demobilze. * ntpd/ntp_proto.c (receive): Improve version testing, including RES_DEMOBILIZE support. (fast_xmit): Patches to kiss-of-death packet. * ntpd/ntp_loopfilter.c (local_clock): S_SYNC case now also checks abs(clock_offset) against CLOCK_PGATE*sys_jitter. * ntpd/ntp_config.c: CONF_RES_DEMOBILIZE/demobilize support. * include/ntp_config.h (CONF_RES_DEMOBILIZE): Added. * include/ntp.h (RES_DEMOBILIZE): Added. From Dave Mills. 2001-04-25 Harlan Stenn * html/accopt.htm: Document the "version" parameter From Dave Mills. * ntpd/ntp_proto.c (fast_xmit): Implement DENY mode. From Dave Mills. * ntpd/ntp_config.c: Add the "allan" tinker variable. From: Juha Sarlin * ntpd/refclock_hopfpci.c (hopfpci_start): Lose the "correct_any" stuff - it's both obsolete and wrong. * ntpd/ntp_proto.c (receive): Keep track of packet versions. Implement RES_LIMITED. * include/ntp_config.h (CONF_RES_LIMITED): * include/ntp.h (RES_LIMITED): Leave the bits in the original order. From Dave Mills. * util/timetrim.c: * util/Makefile.am: * ntpdc/ntpdc_ops.c: * ntpd/refclock_nmea.c: * libntp/snprintf.c: * configure.in: * configure: * config.h.in: * aclocal.m4: * acconfig.h: Lint cleanup from: Marc Brett * ntpd/ntp_config.c: Add "version" support. (getconfig): version support. * include/ntp_config.h (CONF_RES_VERSION): Added. * include/ntp.h (RES_VERSION): Added. From: Dave Mills. * include/ntp_machine.h (ifreq): WinNT cleanup 2001-04-23 Harlan Stenn * configure.in: 4.0.99k29 * html/miscopt.htm: Document the "allan" tinker variable. * ntpd/ntp_proto.c (clock_filter): Update comments. Lose etemp; we now use allan_xpt for this. * ntpd/ntp_loopfilter.c: Added allan_xpt as a tinker variable. Reorganize variables and improve comments. (local_clock): Improve comments, use (new) allan_xpt instead of CLOCK_ALLAN. Fix test in S_SYNC state. Update debug info. (rstclock): No longer force allan_xpt to CVLOCK_ALLAN in S_FREQ, S_SYNC, or default case. (loop_config): Document dangerous tinker variables, and add LOOP_ALLAN to the list. * include/ntp_config.h (CONF_CLOCK_ALLAN): Added. * include/ntp.h (LOOP_ALLAN): Added. Allan intercept fixes from Dave Mills. * scripts/mkver.in: Use the C locale so the dates come out in a consistent format. From: ASANO Naoyuki * build: Run "config.status" before the "make" because it probably saves time and trouble. Probably... * flock-build: Try building sequentially. 2001-04-22 Harlan Stenn * configure.in (ac_cv_make_tickadj): Fix it right... * util/ntp-genkeys.c: extern config_netinfo, too. * util/hist.c: * ntptrace/ntptrace.c: * ntpq/ntpq.c: * ntpdc/ntpdc.c: * ntpdate/ntptimeset.c: * ntpdate/ntpdate.c: * ntpd/refclock_parse.c: * ntpd/refclock_msfees.c: * ntpd/refclock_jupiter.c: * ntpd/ntp_refclock.c: * ntpd/ntp_io.c: * libparse/clk_wharton.c: * libparse/clk_varitext.c: * libparse/clk_trimtaip.c: * libparse/clk_schmid.c: * libparse/clk_rcc8000.c: * libparse/clk_rawdcf.c: * libparse/clk_meinberg.c: * libparse/clk_hopf6021.c: * libparse/clk_dcf7000.c: * libparse/clk_computime.c: Lint. From: Simon Burge 2001-04-21 Harlan Stenn * ntpd/refclock_nmea.c (nmea_receive): Fixes. From: John Woolner * util/ntp-genkeys.c: Declare check_netinfo, don't define it. From: Jack Bryans * configure.in (RSASRCS): rsaref2 needs digit.h (I thought I fixed this already). * configure.in (CFLAGS): Disable -Wconversion, enable -Wmissing-prototypes, and allow for -Werror. From: Simon Burge * util/ntp-genkeys.c (main): Reset the standard mask so the symlinks are created with the standard mask. * configure.in: 4.0.99k28 * ntpd/ntpd.c (ntpdmain): Use mode_t for umask value. * util/ntp-genkeys.c: Create files with the right umask. * util/ntp-genkeys.c: config_file should be declared, not defined. * ntpd/refclock_mx4200.c (mx4200_pps): debug cleanup. * ntpd/refclock_hopfser.c: If we're not using it, provide the _bs. * ntpd/refclock_heath.c (heath_receive): Add missing "break" statements. * ntpd/ntp_proto.c: Lose extra definition of mode_ntpdate. * librsaref/Makefile.am (nodist_librsaref_a_SOURCES): Put RSASRCS on the same line as rsaref.h to improve portability. * libntp/msyslog.c: Lint cleanup. From: Marc.Brett@westerngeco.com * util/ntp-genkeys.c: * ntpdate/ntpdate.c: * ntpd/ntp_config.c: Netinfo header reorder. From: Jack Bryans * configure.in: timespec can be found by looking in goofy places under SunOS. 2001-04-20 Harlan Stenn * ntpd/refclock_nmea.c: PPSAPI cleanup, default to RMC sentences, handle milliseconds, multiple sentences, other good stuff. From: John Woolner , Marc.Brett@westerngeco.com, John.Hay@icomtek.csir.co.za * ntpd/ntp_proto.c (receive): In the AM_NEWBCL case, return in all cases at the end. * ntpd/ntp_peer.c (newpeer): Check cast_flags against MDF_BCLNT, not against MDF_BCAST. * ntpd/ntp_loopfilter.c (local_clock): Lose debug info. * ntpd/ntp_crypto.c (crypto_recv): Bugfix. From: Dave Mills. * configure.in: 4.0.99k27 * ntpd/ntp_loopfilter.c (local_clock): Check clock_panic > 0. Check clock_max > 0. * html/ntpd.htm: Cleanup. * html/miscopt.htm: Cleanup. * html/confopt.htm: Cleanup minpoll documentation. From: Dave Mills. 2001-04-19 Harlan Stenn * ntpd/cmd_args.c (getstartup): check_netinfo needs an extern declaration. Reported by: Jack Bryans * configure.in (ac_cv_make_timetrim): Added. * util/Makefile.am (bin_PROGRAMS): MAKE_TIMETRIM Requested by: Jack Bryans * configure.in: 4.0.99k26 * util/ntp-genkeys.c: * ntpd/refclock_oncore.c: * ntpd/ntp_peer.c: * libntp/msyslog.c: * libntp/audio.c: Lint cleanup. From: Simon Burge * ntpd/ntp_loopfilter.c (local_clock): debug message improvements from Dave Mills. * libntp/emalloc.c (emalloc): Tell people we are exiting if we log an out-of-memory condition. * util/ntp-genkeys.c (main): Don't allow '#' in a generated MD5 key. Reported by: Dave Tyson 2001-04-18 Harlan Stenn * ntpd/ntp_proto.c (clock_update): minpoll cleanup. (clock_select): minpoll cleanup. (clock_filter): Bugfixes from Mark Martinec * ntpd/ntp_loopfilter.c (rstclock): minpoll cleanup. Debug cleanup. * ntpd/ntp_config.c (getconfig): Initialize/bounds check minpoll using NTP_MINDPOLL insted of sys_minpoll. From: Dave Mills. 2001-04-17 Harlan Stenn * libntp/msyslog.c: * ElectricFence/page.c (stringErrorReport): Follow Rainer's lead and use strerror(). * ntpd/refclock_shm.c (shm_start): Always use strerror. * libntp/msyslog.c (msyslog): Use strerror if present. From: Rainer Orth * libparse/parsesolaris.c (rdchar): Cast ~0 to unsigned long. * libntp/buftvtots.c (buftvtots): Allow for 8-byte tv_sec, tv_usec in struct timeval. From: Rainer Orth 2001-04-16 Harlan Stenn * ntpd/ntp_config.c (getconfig): move "tinker" so it's generally available. 2001-04-15 Harlan Stenn * configure.in: Look for getclock(). * ntpd/ntp_config.c (getconfig): Squawk if provided minpoll or maxpoll values are out of range. * ntpd/ntp_proto.c (poll_update): Some operations can only be done if we're compiling with some REFCLOCKs. From Dave Mills. * configure.in (RSASRCS): Added. * librsaref/Makefile.am (nodist_librsaref_a_SOURCES): Use RSASRCS. * configure.in: Limit the DECL_HSTRERROR_0 to aix4.3.*. RSN, we could also limit it to xlc... * configure.in: 4.0.99k25 * html/leap.htm: Added. * html/index.htm: Update. * html/driver7.htm: Update. * html/driver6.htm: Update. * html/driver36.htm: Update. * html/audio.htm: Update. * html/y2k.htm: Removed. From Dave Mills. 2001-04-14 Harlan Stenn * acconfig.h: Lose extra declarations of PACKAGE and VERSION. * acconfig.h: * configure.in: * include/l_stdlib.h: DECL_HSTRERROR_0 needed for xlc under AIX 4.3.2. Reported by: Harald Barth * ntpd/ntp_proto.c (proto_config): cal_enable (PROTO_CAL) is invalid if no refclocks are present. From: Frodo Looijaard * README.cvs: On some systems, the -C option fails. * ntpd/refclock_nmea.c: * ntpd/ntp_refclock.c: * html/driver20.htm: PPSAPI patches for NMEA driver. From: John.Hay@icomtek.csir.co.za * README.rsa: Describe RSAEuro support, provide alternate rsa.c patch. * configure.in: Check for rsaeuro1, RSAOBJS, RSADIR respectively. * html/build.htm: Hint at rsaeuro1 directory. * include/global.h (BYTE): Define. * librsaref/Makefile.am (nodist_librsaref_a_SOURCES): Removed rsaref2 specific sources. (librsaref_a_LIBADD): Add appropriate objects. (librsaref_a_DEPENDENCIES): Work around automake limitation. (stamp-rsaref): Use RSADIR. * scripts/README: Document ntp-close. * scripts/Makefile.am (EXTRA_DIST): Distribute it. * Makefile.am (DISTCLEANFILES): Remove .warning. * librsaref/Makefile.am (DISTCLEANFILES): Remove copied/touched librsaref sources, stamp-rsaref. * ntpdate/Makefile.am (DISTCLEANFILES): Remove version.c. * ntpq/Makefile.am (DISTCLEANFILES): Likewise. * parseutil/Makefile.am (DISTCLEANFILES): Remove $(EXTRA_PROGRAMS). Rainer Orth * ntpd/ntp_control.c: Header cleanup 2001-04-13 Harlan Stenn * configure.in: Properly align --help output. Explain ElectricFence. From: Rainer Orth * ntpd/ntp_loopfilter.c (local_clock): Lose debugging statements. * ntpd/ntp_proto.c (clock_filter): Rewrite. From: Dave Mills * ntpd/ntp_control.c (ctl_getitem): msyslog() possible buffer overflow exploit. * configure.in: 4.0.99k24 * html/pic/radio2.jpg: * html/release.htm: * html/refclock.htm: * html/pps.htm: * html/ntpd.htm: * html/miscopt.htm: * html/driver22.htm: * html/confopt.htm: Updated documentation from Dave Mills. * util/ntp-genkeys.c: sys_minpoll. * ntpd/refclock_atom.c: Comment additions. * ntpd/ntp_proto.c: mode_ntpdate and peer_ntpdate added. (transmit): We want 3, not 2, consecutive polls. hpoll logic cleanup. mode_ntpdate changes. (receive): When setting up a newpeer, use our sys_minpoll, not the peer->ppoll. (clock_update): sys_minpoll changes. Reorder some case 1 code. Don't exit in case 2. (poll_update): hpoll cleanup. (peer_clear): u_rand. Use u_rand to randomize the initial poll. * ntpd/ntp_peer.c (newpeer): Bump peer_ntpdate if we're in mode_ntpdate. * ntpd/ntp_loopfilter.c: Initialize sys_poll and sys_minpoll to NTP_MINDPOLL. (local_clock): Clean up some debug/info messages. (rstclock): Use sys_minpoll. (loop_config): KERNEL_PLL sanity checks. LOOP_MINPOLL support. * ntpd/ntp_crypto.c (crypto_recv): Turn off FLAG_AUTOKEY when we turn off TEST10. * ntpd/ntp_control.c (ctl_getitem): Buffer overflow check. Clean up some loop logic. * ntpd/ntp_config.c: Added "tinker" and "minpoll". Use sys_minpoll now, instead of old manifest constant. (save_resolve): Print keyid using decimal, not hex. From Lars-Owe Ivarsson * include/ntpd.h: Added peer_ntpdate and sys_minpoll. * include/ntp_config.h (CONF_CLOCK_MINPOLL): Added. * include/ntp.h: keyid cleanup. LOOP_* cleanup. From Dave Mills. 2001-04-03 Harlan Stenn * ntpd/ntp_proto.c (clock_filter): Swell stuff. From: Mark Martinec * ports/winnt/ntpd/ntpd.dsp: * ports/winnt/ntpd/hopf_PCI_io.c: * ports/winnt/include/hopf_PCI_io.h: * ports/winnt/include/config.h: * ntpd/refclock_hopfser.c: * ntpd/refclock_hopfpci.c: * ntpd/refclock_conf.c: * ntpd/ntp_control.c: * ntpd/Makefile.am: * libntp/clocktypes.c: * include/ntp.h: * include/hopf6039.h: * include/Makefile.in: * include/Makefile.am: * html/pic/fg6039.jpg: * html/refclock.htm: * html/driver39.htm: * html/driver38.htm: * html/copyright.htm: Updated Oncore dudes. * configure.in: HOPF drivers and documentation. From: Bernd Altmeier (with some light hacking from Harlan to clean up indentation and lose the // comments) * ntpd/refclock_oncore.c: * ntpd/refclock_conf.c: Make it go. From: Reg Clemens * configure.in (openssl): Publish and default to RSAREF; hide openssl, and only use it if explicitly requested (at least until we work with it). 2001-04-02 Harlan Stenn * html/y2k.htm: * html/tickadj.htm: * html/release.htm: * html/refclock.htm: * html/quick.htm: * html/pps.htm: * html/ntptrace.htm: * html/ntptime.htm: * html/ntpq.htm: * html/ntpdc.htm: * html/ntpdate.htm: * html/ntpd.htm: * html/miscopt.htm: * html/index.htm: * html/genkeys.htm: * html/exec.htm: * html/driver7.htm: * html/driver22.htm: * html/copyright.htm: * html/confopt.htm: * html/build.htm: * html/authopt.htm: * html/assoc.htm: Updates from Dave Mills. 2001-04-01 Harlan Stenn * configure.in (OPENSSL): Just use -lcrypto. Reported by Dave Mills. 2001-03-31 Harlan Stenn * configure.in: 4.0.99k20 * ntpd/refclock_heath.c: Add support for GC-1000 II. From Dave Mills. * ntpd/ntp_proto.c (transmit): Check peer->unreach. (peer_clear): peer->outdate is a f(BURST_INTERVAL1), not NTP_MINPOLL. * ntpd/ntp_loopfilter.c (local_clock): mode_ntpdate stuff. * ntpd/ntp_crypto.c: OpenSSL/RSAREF support. * ntpd/cmd_args.c: Use -q, not -z, for mode_ntpdate. (getstartup): nofork on mode_ntpdate. Usage update. * include/ntp_crypto.h: OpenSSL/RSAREF support. From: Dave Mills. * configure.in (rsaref): Buglet. 2001-03-30 Harlan Stenn * ntpd/ntp_proto.c (clock_update): mode_ntpdate support. * ntpd/ntp_loopfilter.c (local_clock): mode_ntpdate support. * ntpd/cmd_args.c: Added -z (mode_ntpdate). * include/ntpd.h: mode_ntpdate added. * include/ntp_crypto.h: RSAREF/OPENSSL cleanup. From: Dave Mills. 2001-03-29 Harlan Stenn * config.h.in: * aclocal.m4: * configure.in: Prepare for OpenSSL support 2001-03-28 Harlan Stenn * README.rsa: Note that RSAEURO will not work. Reported by: pieter.delacourt@banksys.be 2001-03-25 Harlan Stenn * include/ntp_if.h: * include/ntp_machine.h: * include/ntp_unixtime.h: * libntp/humandate.c: * libntp/iosignal.c: * libntp/mktime.c: * libntp/prettydate.c: * libntp/systime.c: * libntp/tvtoa.c: * libntp/uglydate.c: * libntp/utvtoa.c: * libparse/clk_computime.c: * libparse/clk_dcf7000.c: * libparse/clk_hopf6021.c: * libparse/clk_meinberg.c: * libparse/clk_rawdcf.c: * libparse/clk_rcc8000.c: * libparse/clk_schmid.c: * libparse/clk_trimtaip.c: * libparse/clk_trimtsip.c: * libparse/clk_varitext.c: * libparse/parse.c: * libparse/parse_conf.c: * ntpd/check_y2k.c: * ntpd/ntp_config.c: * ntpd/ntp_control.c: * ntpd/ntp_intres.c: * ntpd/ntp_io.c: * ntpd/ntp_loopfilter.c: * ntpd/ntp_monitor.c: * ntpd/ntp_proto.c: * ntpd/ntp_refclock.c: * ntpd/ntp_request.c: * ntpd/ntp_resolver.c: * ntpd/ntp_timer.c: * ntpd/ntp_util.c: * ntpd/ntpd.c: * ntpd/refclock_acts.c: * ntpd/refclock_arbiter.c: * ntpd/refclock_arc.c: * ntpd/refclock_as2201.c: * ntpd/refclock_atom.c: * ntpd/refclock_bancomm.c: * ntpd/refclock_chronolog.c: * ntpd/refclock_chu.c: * ntpd/refclock_datum.c: * ntpd/refclock_dumbclock.c: * ntpd/refclock_fg.c: * ntpd/refclock_gpsvme.c: * ntpd/refclock_heath.c: * ntpd/refclock_hpgps.c: * ntpd/refclock_irig.c: * ntpd/refclock_jupiter.c: * ntpd/refclock_leitch.c: * ntpd/refclock_local.c: * ntpd/refclock_msfees.c: * ntpd/refclock_mx4200.c: * ntpd/refclock_nmea.c: * ntpd/refclock_oncore.c: * ntpd/refclock_pcf.c: * ntpd/refclock_pst.c: * ntpd/refclock_shm.c: * ntpd/refclock_tpro.c: * ntpd/refclock_trak.c: * ntpd/refclock_true.c: * ntpd/refclock_ulink.c: * ntpd/refclock_usno.c: * ntpd/refclock_wwv.c: * ntpd/refclock_wwvb.c: * ntpdate/ntpdate.c: * ntpdate/ntptime_config.c: * ntpdate/ntptimeset.c: * ntpdc/ntpdc.c: * ntpdc/ntpdc_ops.c: * ntpq/ntpq.c: * ntpq/ntpq_ops.c: * ntptrace/ntptrace.c: * parseutil/testdcf.c: * util/hist.c: * util/ntp-genkeys.c: * util/ntptime.c: * util/precision.c: * util/tickadj.c: time.h and sys/time.h cleanup. 2001-03-24 Harlan Stenn * configure.in: '99k19 * ntpd/refclock_atom.c (atom_ppsapi): PPS API RFC alignment patches. From: Ulrich Windl * util/ntptime.c: MNT options From: Ulrich Windl * ntpd/ntp_refclock.c (refclock_newpeer): Lose "extra" free(). From: Ulrich Windl * configure.in: 4.0.99k18 and auto* upgrade 2001-03-14 Harlan Stenn * ntpdc/ntpdc_ops.c (printpeer): No more "valid". * ntpd/ntp_request.c (peer_info): No more "valid". * ntpd/ntp_refclock.c (refclock_transmit): valid/hpoll cleanup. * ntpd/ntp_proto.c (transmit): valid/hpoll and peer->ttl cleanup. peer->valid/oreach cleanup. (receive): Call newpeer() with the pkt->ppoll, not NTP_MINDPOLL (in several places). In AM_NEWPASS, if we have a NULL peer, return. (poll_update): Added xpoll definition, fixed oldpoll definition. Algorithmic improvements. * ntpd/ntp_peer.c (newpeer): Better minpoll/maxpoll initialization. (resetmanycast): That's a poll_update() on an MDF_ACAST, not a poll_clear(). * ntpd/ntp_crypto.c: include . (crypto_recv): Leave the crypto_flags alone when wiggling the peer-> stuff. (crypto_cert): Make room for daddy. Do a real open() on the cert file. Read the cert. Initial hack and slash. Better debug info. * ntpd/ntp_control.c: CP_VALID now does "unreach". (ctl_putpeer): Ditto. * include/ntp_request.h: info_peer gets a placeholder for "valid". * include/ntp_crypto.h (CRYPTO_FLAG_CERT): Comment update. * include/ntp.h: Lose "valid" from struct peer. From: Dave Mills. 2001-03-05 Harlan Stenn * ntpd/ntp_proto.c (transmit): hpoll calc logic cleanup. (receive): New cert stuff. (poll_update): Improvements. (peer_clear): New cert stuff. (peer_xmit): New cert stuff. * ntpd/ntp_crypto.c: New cert stuff, documentation cleanup. Lose extraneous poll_uopdate()s. * ntpd/ntp_control.c: Deal with new cert stuff. * ntpd/ntp_config.c (getconfig): Handle CONF_CRYPTO_CERT. * include/ntp_crypto.h (CRYPTO_FLAG_CERT): Added. (CRYPTO_CERT): Added. (CRYPTO_CONF_CERT): Added. Add declaration for struct value certif. * include/ntp_control.h (CS_CERTIF): Added. (CP_CERTIF): Added. * include/ntp_config.h (CONF_CRYPTO_CERT): Added. * include/ntp.h (TEST10,TEST11): New meaning. Add certif to struct peer. (FLAG_PROVEN): Added. (MAX_EXT_LEN): Removed. exten grew from 672/4 to 5000/4 for PUBKEY. From: Dave Mills. 2001-03-03 Harlan Stenn * ntpd/ntp_proto.c (transmit): Documentation cleanup. (receive): Watch for NULL peer->pubkey.ptr (TEST11). (poll_update): peer->nextdate, not ->outdate. More cleanup around the disabled PUBKEY chunk. * ntpd/ntp_crypto.c (make_keylist): ltemp might be smaller than sys_automax - check peer->kpoll, too. Other ltemp cleanup. (crypto_recv): fstamp is a PUBKEY-only variable. * include/ntp.h (NTP_AUTOMAX): 13, not 12. From: Dave Mills. 2001-03-01 Harlan Stenn * ntpd/ntp_proto.c (transmit): hpoll cleanup. Call clock_select() after calling poll_update, not before. (receive): Call poll_update after crypto_recv if FLAG_SKEY. (process_packet): Set peer->ppoll Later. (poll_update): peer->hpoll sanity checking. Set peer->outdate, not ->nextate, when burst > 0. MDF_ACAST cleanup. (clock_select): Fix hpoll typo in call to poll_update(). * ntpd/ntp_crypto.c (crypto_xmit): tstamp's value is a function of PUBKEY. * include/ntp.h (clear_to_zero): #define value is a function of AUTOKEY. From: Dave Mills. 2001-02-28 Harlan Stenn * ntpd/ntp_proto.c (transmit): Documentation/code update. (poll_update): Sanity check peer->hpoll. Improve debug info. (peer_clear): Improve debug info. Turn off FLAG_ASSOC in addition to FLAG_AUTOKEY. (clock_select): peer->status is CTL_PST_SEL_DISTSYSPEER, and don't call poll_update(). Make each entry in the peer_list a CTL_PST_SEL_SELCAND sooner, too. Rework similar logic later on. Change debug level on some info. (peer_xmit): Check peer->flags using FLAG_ASSOC, not CRYPTO_FLAG_AUTO in a couple places. Don't call poll_update() if sendlen > LEN_PKT_NOMAC. * ntpd/ntp_loopfilter.c (local_clock): Improve debug info. Sanity-check sys_poll sooner. * ntpd/ntp_crypto.c: New artwork. (COOKIE_LEN,AUTOKEY_LEN,VALUE_LEN): New. (make_keylist): More debug info. Use FLAG_ASSOC, not CRYPTO_FLAG_ASSOC. (crypto_recv): More debug info. Clean up/improve sanity checks on CRYPTO_ASSOC and CRYPTO_RESP packets, and in other places. (crypto_xmit): Clean up/improve sanity checks on CRYPTO_ASSOC and CRYPTO_RESP packets. Use FLAG_ASSOC, not CRYPTO_FLAG_ASSOC. More debug info. * include/ntp.h (NTP_CANLOCK): Lose it. (clear_to_zero): is now "assoc". (FLAG_ASSOC): Added. From: Dave Mills 2001-02-23 Harlan Stenn * ntpdate/ntpdate.h (NTP_MAXAGE): Added. * ntpd/ntp_refclock.c (refclock_receive): Cleanup. * ntpd/ntp_proto.c (transmit): Don't reset peer->ppoll in one case. Update peer->hpoll based on CTL_PST_SEL_CORRECT, not FLAG_SYSPEER. Don't update peer->ppoll based on MDF_[BM]CAST. (peer_clear): ppoll is initialized to maxpoll. (clock_select): call poll_update(peer->hpoll) earlier. (peer_xmit): Call poll_update later. * ntpd/ntp_peer.c (peer_config): Rework initial values of [hkp]poll. * ntpd/ntp_loopfilter.c (CLOCK_PHI): Added. Deal with other (allow_*) stuff. Treat Windows/NT the same as others regarding panic steps. Deal with tinker stuff. * ntpd/ntp_config.c: Tinker stuff. * ntpd/cmd_args.c (getCmdOpts): -g now wiggles "allow_panic" (renamed from "correct_any"). -x now wiggles "allow_step" (renamed from "allow_step_backward"). * include/ntpd.h: Add tinker variables. Rename/rework variables associated with "permission to step" and "permission to make a panic correction" * include/ntp_config.h (CONFIG_TINKER): Added. (CONF_CLOCK_MAX): Tinker keyword (CONF_CLOCK_PANIC): Tinker keyword (CONF_CLOCK_PHI): Tinker keyword (CONF_CLOCK_MINSTEP): Tinker keyword * include/ntp.h (NTP_MINCLOCK): Tinker and other cleanup. From: Dave Mills 2001-02-19 Harlan Stenn * ntpd/ntp_proto.c (transmit): Don't peer_clear() and reset minpoll unconditionally; make sure the peer is configured. (poll_update): When updating peer->ppoll, check on BCAST and MCAST, not ACAST (peer_clear): PUBKEY cleanup. Zero out the peer structure earlier. Initialization cleanup/fixes. (peer_xmit): CRYPTO_FLAG_AUTO is in peer->flags now. (key_expire): Debug output. * ntpd/ntp_peer.c (unpeer): PUBKEY cleanup. (newpeer): peer variable setup cleanup. * ntpd/ntp_crypto.c (make_keylist): Keep CRYPTO_FLAG_AUTO in peer->flags, not crypto_flags. (crypto_xmit): Ditto. (crypto_recv): Fix up RV_TSP logic (several places). * include/ntp.h (clear_to_zero): Moved... From: Dave Mills. 2001-02-14 Harlan Stenn * ntpd/ntp_proto.c (peer_xmit): Crypto-related fixes From Dave Mills. * ntpd/ntp_crypto.c (crypto_recv): Allocate space for the trailing NUL on the keystr. 2001-01-28 Harlan Stenn * configure.in: 4.0.99k17 * ntpd/refclock_local.c (STRATUM): 3 -> 5 * ntpd/ntp_proto.c: sys_maxd -> sys_selerr, sys_epsil -> sys_syserr. various cleanups and improvements. From: Dave Mills. 2001-01-19 Harlan Stenn * configure.in: 4.0.99k16 * config.h.in: Regenerated - became empty somehow. Reported by John.Hay@icomtek.csir.co.za * ntpd/ntp_proto.c (clock_select): Fix sdisp calculation. From Dave Mills. * util/ntp-genkeys.c: * ntpd/refclock_chu.c: * ntpd/refclock_atom.c: * ntpd/ntpd.c: * ntpd/ntp_loopfilter.c: * ntpd/ntp_io.c: * ntpd/cmd_args.c: * libntp/audio.c: * include/l_stdlib.h: * html/copyright.htm: Lint fixes (Thanks bunches!) From: Marc.Brett@westerngeco.com 2001-01-18 Harlan Stenn * configure.in: 4.0.99k15 * ntpd/ntp_proto.c (clock_select): Track error[] items sooner. Typo grabbing the dtemp value and in the sdisp calculation. From Dave Mills. 2001-01-17 Harlan Stenn * configure.in: 4.0.99k14 * ntpd/ntp_proto.c: Change description of sys_rootdelay and sys_rootdispersion. (process_packet): Fix p_del test (bad distance). (process_packet): Fix bad synch distance test. (process_packet): Fix call to clock_filter (p_disp) (clock_update): Fix sys_rootdelay calculation. (clock_filter): Initialize jit to f(sys_precision) (clock_filter): Update jit using distance[i] instead of SQUARE(). peer->jitter uses dtemp instead of SQUARE(). (clock_filter): Updated CLOCK_SGATE checks. When printing debug info, show jitter along with popcorn spike. (clock_select): New sdisp calc. (root_distance): New return value calc. (peer_xmit): xpkt.rootdispersion value change. * include/ntp.h (CLOCK_SGATE): Popcorn spike gate (Whoa, Molly!) From Dave Mills. 2001-01-13 Harlan Stenn * config.sub (Repository): Updated. * config.guess (Repository): Updated. * ntpd/ntp_loopfilter.c (local_clock): Just use sys_jitter in the calculation for rootdispersion. From Dave Mills. 2001-01-02 Harlan Stenn * ntpd/ntp_proto.c (transmit): Fix documentation. Set peer->outdate and call poll_update in a new place. Sanity checks in the MODE_BROADCAST case. (clock_select): Track the old peer. Use the old peer in subsequent checks, where appropriate. Clean up unpeer() logic. From Dave Mills. 2001-01-01 Harlan Stenn * ntpd/refclock_chu.c: Start using HAVE_AUDIO. * ntpd/ntp_proto.c (clock_select): If about to discard an ephemeral association, do it only if not the system peer. From Dave Mills. * html/pic/wingdorothy.gif: * html/pic/bustardfly.gif: * html/pic/boom3a.gif: * html/pic/tonea.gif: * html/pic/stack1a.jpg: * html/pic/pogoa.gif: * html/pic/pogo8.gif: * html/pic/pogo6.gif: * html/pic/pogo5.gif: * html/pic/pogo4.gif: * html/pic/pogo3.gif: * html/pic/pogo1.gif: * html/pic/oz2.gif: * html/pic/flatheads.gif: * html/pic/boom4.gif: * html/pic/boom3.gif: * html/pic/appletree.gif: * html/pic/alice51.gif: * html/pic/alice44.gif: * html/pic/alice35.gif: * html/pic/alice31.gif: * html/pic/alice15b.gif: * html/pic/alice13.gif: * html/pic/alice11.gif: * html/release.htm: * html/rdebug.htm: * html/prefer.htm: * html/porting.htm: * html/ntptrace.htm: * html/ntpq.htm: * html/ntpdate.htm: * html/monopt.htm: * html/kernpps.htm: * html/index.htm: * html/hints.htm: * html/gadget.htm: * html/driver7.htm: * html/copyright.htm: * html/config.htm: * html/build.htm: * html/authopt.htm: * html/assoc.htm: * html/accopt.htm: Cleanup from Dave Mills. 2000-12-30 Harlan Stenn * configure.in: 4.0.99k13 * ntpd/refclock_wwv.c (wwv_start): Call audio_init with DEVICE_AUDIO. * ntpd/refclock_irig.c (irig_start): Call audio_init with DEVICE_AUDIO. * ntpd/refclock_chu.c: Documentation cleanup. (DEVICE_AUDIO): Added. (fd_audio): Added. (chu_start): Separate audio from serial device. (chu_receive): Rewrite - get data from serial or audio device as appropriate. (chu_audio_receive): Renamed (from chu_receive) to allow both audio and serial capability. (chu_serial_receive): Ditto. (chu_decode): Do the Right Thing based on audio/serial data. * ntpd/ntp_refclock.c (refclock_open): Check for failure using <0 instead of ==-1. * libntp/audio.c: Header cleanup, and remove HAVE_STRUCT_AUDIO_INFO_* related fields. (audio_init): Func arg is device to attempt to open. * include/audio.h (audio_init): Now takes a char * argument. From Dave Mills. * configure.in (ntp_refclock): HAVE_AUDIO added. Remove HAVE_STRUCT_AUDIO_INFO_* stuff; Dave rewrote the audio stuff. 2000-12-28 Harlan Stenn * configure.in: 4.0.99k12 2000-12-27 Harlan Stenn * html/release.htm: * html/patches.htm: * html/measure.htm: * html/confopt.htm: * html/clockopt.htm: * html/biblio.htm: * html/authopt.htm: * html/assoc.htm: Updates from Dave Mills. * include/ntp_crypto.h: Make sure crypto_flags is visible. From Dave Mills. 2000-12-14 Harlan Stenn * ntpd/ntp_proto.c (process_packet): pleap/pstratum. (peer_xmit): Use CRYPTO_FLAG_AUTO. * ntpd/ntp_crypto.c (make_keylist): Use CRYPTO_FLAG_AUTO. Only sign host name and timestamps if the clock is synched. * include/ntp_crypto.h (CRYPTO_FLAG_AUTO): Added. From: Dave Mills 2000-12-11 Harlan Stenn * ntpd/ntp_proto.c (transmit): Call clock_select in a few new places. BURST/IBURST cleanup. Don't turn off FLAG_BURST at the EOburst. (receive): Set peer->unreach = 0 before we call process_packet(). (process_packet): ditto, before calling poll_update(). Lose some debugging, MODE_BCLIENT/CLIENT cleanup. (poll_update): Bump nextupdate on FLAG_REFCLOCK, not _REFCLOCK or _IBURST. (peer_clear): Don't set IBURST on MDF_BCLNT. From: Dave Mills. * ntpdate/ntpdate.c (alarming): Appease ansi2knr. 2000-12-10 Harlan Stenn * ntpd/ntp_control.c (ctl_putpeer): CP_TTL and CP_TTLMAX MDF_ACAST and MDF_MCAST cleanup. * ntpd/refclock_wwv.c (wwv_start): ttlmax/ttl cleanup. * ntpd/refclock_usno.c (usno_timeout): ttlmax/ttl cleanup. * ntpd/refclock_parse.c (CLK_REALTYPE): ttlmax/ttl cleanup. * ntpd/refclock_chu.c (chu_start): ttlmax/ttl cleanup. * ntpd/refclock_acts.c (acts_timeout): ttlmax/ttl cleanup. * ntpd/ntp_refclock.c (refclock_newpeer): Don't do the any_interface -> loopback_interface trick. * ntpd/ntp_proto.c (transmit): Broadcast/manycast cleanup. * ntpd/ntp_peer.c: Cleanup. * ntpd/ntp_io.c: Cleanup. * ntpd/ntp_crypto.c (crypto_recv): AUTOKEY based on BCLNT, not MCAST2. * include/ntpd.h: Declare findbcastinter(). * include/ntp.h: struct peer's ttlmax is now max ttl/refclock mode. ttl is now ttl for manycast mode. (FLAG_MCAST): Reworked several FLAG_ bits. From Dave Mills. 2000-12-05 Harlan Stenn * ntpq/ntpq.c: CP_TTLMAX support. * ntpd/ntp_proto.c (transmit): MDF_ACAST ttl fixes. * ntpd/ntp_peer.c (resetmanycast): Reset ttl if MDF_ACAST. (peer_config): Save max ttl in ttlmax. * ntpd/ntp_control.c: ttlmax support. * include/ntp_control.h (CP_TTLMAX): Added. * include/ntp.h: Added ttlmax to struct peer. Dave Mills. 2000-12-03 Harlan Stenn * ntpd/ntp_proto.c (receive): That any_interface is now an rbufp->dstadr. Various other doc and code cleanup. * ntpd/ntp_peer.c (findmanycastpeer): Fixes From Dave Mills 2000-12-02 Harlan Stenn * ntpd/ntp_request.c (do_conf): call peer_config with any_interface, not 0. * ntpd/ntp_proto.c (transmit): Manycast cleanup * ntpd/ntp_peer.c (findmanycastpeer): manycast cleanup * ntpd/ntp_io.c (sendpkt): Only check ttl if we have a ttl (findinterface): Cleanup * ntpd/ntp_control.c: cleanup * include/ntpd.h: Added resetmanycast. * include/ntp_control.h (CP_TTL): disp -> ttl * ntpq/ntpq.c: disp -> ttl From Dave Mills 2000-11-26 Harlan Stenn * configure.in: 4.0.99k11 * ntpd/ntp_proto.c (transmit): * ntpd/ntp_peer.c: * ntpd/ntp_io.c: * ntpd/ntp_control.c (ctl_putpeer): * ntpd/ntp_config.c (getconfig): * include/ntpd.h: mcast/ucast interface cleanup. From: Dave Mills * include/ntp_request.h: Put data[] as MAXFILENAME+16. This will fix the conf_peer requests again, but re-break compatibility with old versions of the daemon. Sigh. * util/ntp-genkeys.c (cleanlinks): Don't do it if nosymlinks. 2000-11-19 Harlan Stenn * ntpd/refclock_parse.c (rawdcf_init_1): make Linux happier with some modem control stuff. From: Wolfram Pienkoss (via Frank Kardel) * ntpd/refclock_pcf.c (pcf_poll): isdst fix From: Andreas Voegele 2000-10-28 Harlan Stenn * configure.in: 4.0.99k10 * ntpd/refclock_wwvb.c (wwvb_start): Cosmetic reorder. * ntpd/refclock_atom.c (RANGEGATE): Cleanup. Add ASTAGE. Add ppsparams to struct ppsunit. (atom_start): Init peer->burst to ASTAGE. (atom_shutdown): Multi-handle (atom_pps): Multi-handle (atom_pps): RANGEGATE cleanup (atom_poll): Poll count cleanup. Error check cleanup. Burst cleanup. * ntpd/ntp_refclock.c (refclock_transmit): Lose the pre-burst check poll_update(). (refclock_sample): Fix the jitter calc. (refclock_receive): Pass the jitter to the clock_filter(). * ntpd/ntp_proto.c (clock_update): If we lose sync, reset the poll to NTP_MINDPOLL. (poll_update): Poll wiggles. Make sure peer->nextdate is timely. (clock_select): If we lose sync, reset the poll to NTP_MINDPOLL. * ntpd/ntp_loopfilter.c (local_clock): Show the asocid in debug output. popcorn debug message changes. Clamp the poll interval if the system peer has changed. PPS wiggle changes. From Dave Mills. 2000-10-16 Harlan Stenn * ntpd/refclock_pcf.c (pcf_start): * html/driver35.htm: The radio clock transmits 69 bits with a period of 2.5 milliseconds per bit. Thus the driver now sets the default calibration offset to 0.1725 (69 * 2.5 = 172.5). Its now possible to disable the check of the radio clock's synchronisation status bit. Several users requested this option. From: Andreas Voegele * html/refclock.htm: * html/rdebug.htm: * html/prefer.htm: * html/pps.htm: * html/ntpdc.htm: * html/miscopt.htm: * html/ldisc.htm: * html/kern.htm: * html/index.htm: * html/exec.htm: * html/driver22.htm: * html/clockopt.htm: Updates from Dave Mills * ntpd/ntp_intres.c (request): Sanity check the size of the response 2000-10-15 Harlan Stenn * ntpq/ntpq_ops.c (dopeers): Dave didn't like the patch to show the units on the times... * ntpdc/ntpdc_ops.c (doset): SYS_FLAG_PPS cleanup * ntpd/refclock_wwv.c (wwv_newchan): Update the peer refid if we're talking to a stratum 0 source * ntpd/refclock_trak.c: Needs PPS * ntpd/refclock_oncore.c: Disable for now * ntpd/refclock_mx4200.c: Needs PPSAPI, not PPS Header cleanup. PPS interface cleanup. Process sentences with a switch Cleanup and sanity checks * ntpd/refclock_datum.c: header cleanup, light body cleanup * ntpd/refclock_conf.c: CLOCK_TRAK needs PPS MX4200 needs PPSAPI, not PPS Disable ONCORE for now * ntpd/refclock_bancomm.c: Surgery * ntpd/refclock_atom.c: Cleanup (atom_control): added (atom_ppsapi): added * ntpd/ntp_request.c (setclr_flags): SYS_FLAG_PPS cleanup * ntpd/ntp_refclock.c: stropts.h back in in TTYCLK and HAVE_SYS_CLKDEFS_H Get ntp_syscall if KERNEL_PLL Define cal_enable (refclock_receive): Cleanup (refclock_control): sanity check procptr * ntpd/ntp_proto.c (init_proto): pps_enable (proto_config): Turn on/off PPS discipline * ntpd/ntp_loopfilter.c: pps_enable (local_clock): record_loop_stats() if !ntp_enable (local_clock): Turn off PPS if it's not enabled Other cleanup/fixes * ntpd/ntp_config.c: pps and calibrate keywords. Initialize pps_assert to 0, not 1 (swap assert/clear?) * include/ntpd.h: We have pll_status if KERNEL_PLL Added pps_enable and cal_enable * include/ntp_request.h (SYS_FLAG_PPS): Renamed from SYS_FLAG_AUTHENTICATE * include/ntp.h (PROTO_PPS): Added (PROTO_CAL): Added From: Dave Mills 2000-09-23 Harlan Stenn * include/ntp_refclock.h (stropts.h, sys/clkdefs.h): Harmful and useless file include's turned off. * libntp/iosignal.c (netinet/in.h, sys/sockio.h): Duplicate file include's turned off. * ntpd/ntp_refclock.c (ntp_tty.h): File included. (refclock_open, refclock_ioctl): Use `TTY' from ntp_tty.h. * ntpd/refclock_atom.c: Grab a few headers regardless; if we don't CLOCK_ATOM we provide a stub pps_sample() routine so the WHARTON can be compiled/used. * ntpq/ntpq_ops.c (dopeers, doopeers): Print the units for each column header. Tue Sep 12 16:25:51 2000 Philippe De Muyter * ntpd/refclock_atom.c (atom_start): Lose "temp", because we now initially either CAPTUREASSERT or CAPTURECLEAR. (atom_pps): pps_info_t is our friend. Update comments to reflect reality. DTRT with pps_info. Do some overflow checks. From: Dave Mills. 2000-09-21 Harlan Stenn * configure.in: Much improved Solaris patch-level check for the FLL bug test. From: Marc.Brett@westgeo.com 2000-09-19 Harlan Stenn * include/Makefile.am (noinst_HEADERS): Added ntp_tty.h Reported by Dave Mills 2000-09-16 Harlan Stenn * ntptrace/ntptrace.c: * ntpdate/ntptimeset.c (receive): * ntpdate/ntpdate.c (receive): STRATUM cleanup * ntpd/refclock_atom.c (atom_poll): Autostratum. Lose the leap. * ntpd/ntp_proto.c: sys_prefer (process_packet): stratum cleanup (clock_select): Autostratum the ATOM * ntpd/ntp_loopfilter.c: pps_update/pps_stratum wiggle. * include/ntpd.h: Lose pps_update, gain sys_prefer * include/ntp.h: STRATUM variable cleanup From Dave Mills 2000-09-13 Harlan Stenn * ntpd/refclock_oncore.c (oncore_get_timestamp): Print debug messages being aware of HAVE_STRUCT_TIMESPEC. * ntpd/refclock_atom.c: Have pps_params tag along in the ppsunit structure, where it really belongs. (atom_pps): Use PPS_CAPTURE{ASSERT,CLEAR} From: Dave Mills. 2000-09-12 Harlan Stenn * configure.in (ac_cv_var_atom_ok): Cleanup ATOM/PPSAPI stuff... * scripts/ntp-close: Find "close" ntp servers. From: Neal McBurnett * ntpd/refclock_conf.c: * ntpd/refclock_oncore.c: Re-enabled oncore driver for HAVE_PPSAPI case only. 2000-09-12 Philippe De Muyter * ntpd/refclock_parse.c (we400a_pollinfo): Useless variable removed. [WHARTON slot]: Set NO_POLL, NO_INIT and NO_DATA; fix `fixed format' and `offset' fields. * include/ntp_tty.h: New file * libntp/icom.c: Use it. * ntp_update (UPDATE_OPTIONS): Use -d, too. Fix Pass 1 comment. 2000-09-12 Harlan Stenn * ntpd/refclock_oncore.c: shmem_fname added. oncore_init_shmem() updated. (oncore_start): Comment cleanup (oncore_read_config): Move call to oncore_shmem_init() (oncore_init_shmem): Prototype change (oncore_init_shmem): Don't exit on errors (oncore_msg_any): timespec/timeval cleanup (oncore_msg_Cj_id): shmem_fname changes (oncore_msg_BaEaHa): saw_At bugfix (oncore_get_timestamp): Added current_mode/current_params. Commented. Added time_pps_getcap() calls. From: Reg Clemens * ntpd/ntp_io.c (input_handler): Better recvfrom() error message From: Dean Gibson * ntpdc/ntpdc.c (passwd): Get them working again. From: Benjamin Greenwald 2000-09-11 Harlan Stenn * ntpd/refclock_atom.c: (atom_start): * ntpd/ntp_refclock.c: Comment cleanup. PPS/PPSAPI cleanup (refclock_open): PPS/PPSAPI cleanup From: Dave Mills * ntpd/refclock_oncore.c: * ntpd/refclock_mx4200.c: HAVE_TIMESPEC -> HAVE_STRUCT_TIMESPEC * configure.in: ATOM requires struct timespec, not PPSAPI. Clean up dependencies accordingly. 2000-09-09 Harlan Stenn * configure.in (ac_cv_var_atom_ok): Improve ATOM configure message PARSE requires ATOM. * ntpd/ntpd.c (set_process_priority): Clean up debug messages. 2000-09-07 Harlan Stenn * configure.in: ac_cv_atom_ok, depends on HAVE_PPSAPI. I notice the PARSE clocks require ATOM. Could be interesting... 2000-09-06 Harlan Stenn * Makefile.in (distdir): Seems to be a bug in an automake library somewhere... 2000-09-05 Harlan Stenn * ntpd/ntp_loopfilter.c (loop_config): V3 API needs MOD_BITS when initializing ntv.modes. Initialize ntv.{maxerror,esterror,status} earlier. Clean up KERNEL_PLL code. 2000-09-04 Harlan Stenn * ntpq/ntpq.c: report offset as "offset", not "phase". Lose compliance. * ntpd/refclock_local.c (local_poll): variance -> jitter * ntpd/refclock_chu.c (chu_major): Lose variance. * ntpd/ntp_util.c (hourly_stats): sys_error -> sys_jitter (record_loop_stats): ditto * ntpd/ntp_request.c (peer_info): variance -> jitter * ntpd/ntp_refclock.c (refclock_sample): variance -> jitter (refclock_receive): variance -> jitter * ntpd/ntp_proto.c (process_packet): variance -> jitter (clock_filter): variance -> jitter (clock_select): variance -> jitter (root_distance): variance -> jitter * ntpd/ntp_peer.c (newpeer): variance -> jitter * ntpd/ntp_loopfilter.c: Cleanup pll_nano selection bogon. Centralize the kernel API data. (local_clock): Lose sys_error. (loop_config): Code cleanup. * ntpd/ntp_control.c: Call offset "offset" and not "phase". Lose CS_COMPLIANCE. Deal with variance/jitter rename. * include/ntp_refclock.h: Rename variance to jitter in struct refclockproc. * include/ntp_control.h (CS_COMPLIANCE): Lose it. * include/ntp.h: Rename variance to jitter in struct peer. From: Dave Mills 2000-09-01 Harlan Stenn * ntpd/refclock_atom.c: Use the new ppsunit. Cleanup and improve documentation. * ntpd/ntp_refclock.c (refclock_sample): Don't accumulate variance. From Dave Mills 2000-08-31 Harlan Stenn * html/driver22.htm: Update the docs. * ntpd/refclock_atom.c (atom_start): Open the device if it hasn't been opened already. (pps_sample): Make it more visible. From Dave Mills. * configure.in: 4.0.99k8 Revert to the older automake. * configure.in: The PPSAPI headers use "inline", so require a STDC compiler. * ntpd/refclock_atom.c (atom_shutdown): Typo From Dave Mills * configure.in: Convert to autoconf-2.49 * ntpd/refclock_atom.c: Header cleanup Comment cleanup. Lose the TTYCLK stuff. Convert to PPSAPI. * ntpd/ntp_refclock.c (refclock_newpeer): Move refclock_unpeer(). From: Dave Mills 2000-08-29 Harlan Stenn * configure: Fix the autoconf problem... 2000-08-20 Harlan Stenn * configure.in: 99k7 * util/ntptime.c (main): Report TAI stuff * ntpq/ntpq.c: CS_COMPLIANCE/CS_JITTER cleanup * ntpd/ntp_loopfilter.c (local_clock): sys_error/sys_jitter cleanup. kernel PPL cleanup. * ntpd/ntp_crypto.c: Check NTP_API if we're doing KERNEL_PLL so we can get the TAI stuff. * ntpd/ntp_control.c: CS_COMPLIANCE now reports "error" instead of "jitter". CS_JITTER now reports jitter. * include/ntpd.h: Added sys_jitter * include/ntp_control.h (CS_JITTER): Added From: Dave Mills * ntpd/cmd_args.c (getCmdOpts): Crack -N at pre-scan, as we do the priority wiggle before the final scan. From: Tom Smith We might do better to move the priority wiggle to after the final scan. Especially if we want to permit command-line options to have decent control over the priority. When we rewrite the config file stuff we might go to a multi-scan to solve some of these problems. 2000-08-19 Harlan Stenn * configure.in: '99k6, and manually fix configure. * include/ntp_request.h (NTP_MAXHOSTNAME): 144 -> 32 2000-08-18 Harlan Stenn * util/ntp-genkeys.c (main): Don't call fclose if stream is NULL. 2000-08-17 Harlan Stenn * html/driver35.htm: * ntpd/refclock_pcf.c: Updates and improvements From: Andreas Voegele * configure.in (ac_cv_struct_ntptimeval): Lose the TAI check - we don't need it since we can check NTP_API. Re-hack the generated configure script. * configure: Manual hack to the ntptimeval.time.tv_nsec stuff because we're running an old autoconf. 2000-08-16 Harlan Stenn * util/ntptime.c: Use: HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC, it's the standard name. * configure.in: Look for struct ntptimeval.tai in sys/timex.h Cleanup struct tptimeval member tests. * util/ntp-genkeys.c: New command-line arguments 2000-08-14 Harlan Stenn * util/ntp-genkeys.c (main): More small steps... 2000-08-13 Harlan Stenn * ntpd/ntp_crypto.c (crypto_rsa): Now that we're using NTP_KEYSDIR, make sure there is a '/ between the dir and the file. * util/ntp-genkeys.c (main): More small steps... 2000-08-12 Harlan Stenn * util/ntp-genkeys.c (main): Another small step... * configure.in: 99k5 * include/ntp_request.h: Make data[] member of req_pkt 32 again. Bump the version number... * ntpd/ntp_loopfilter.c (local_clock): Change 0. to 0 in a couple of places. From Dave Mills 2000-08-11 Harlan Stenn * util/ntp-genkeys.c (main): Minimal progress... 2000-08-06 Harlan Stenn * ntpd/check_y2k.c: Make debug definition match ntpd.h's declaration * ntpd/Makefile.am (check-local): Use test in favor of [ 2000-08-05 Harlan Stenn * configure.in (NTP_KEYSDIR): Typo 2000-08-04 Harlan Stenn * ElectricFence/Makefile.am (check-local): use test instead of [ * configure.in: AC_REPLACE_FUNCS(strdup) * libntp/strdup.c (strdup): Added. * libntp/Makefile.am (EXTRA_libntp_a_SOURCES): Added strdup.c * util/Makefile.am (ntp_genkeys_DEPENDENCIES): Use $U on .o files (ntp_genkeys_LDADD): ditto. * ntpd/ntp_crypto.c: Use NTP_KEYSDIR * util/ntp-genkeys.c (snifflink): Ignore ENOENT, too. * ntpd/ntp_proto.c (peer_xmit): Crypto cleanup * ntpd/ntp_crypto.c: Cleanup * ntpd/ntp_control.c: Join the club * ntpd/ntp_config.c: Call crypto_config() instead; we got rid of crypto_flags. * include/ntp_crypto.h (CRYPTO_FLAG_ENAB): New crypto flags, rework * include/ntp_control.h (CS_FLAGS): Wiggle in. * include/ntp.h: Added crypto peer status to struct peer From Dave Mills 2000-08-03 Harlan Stenn * util/ntp-genkeys.c: Initialize path_keysdir to NTP_KEYSDIR. * configure.in (NTP_KEYSDIR): Added * acinclude.m4: Added AC_DEFINE_DIR macro * util/ntp-genkeys.c (main): Sanity checks on the file paths. 2000-08-02 Harlan Stenn * util/ntp-genkeys.c (crypto_config): Only #ifdef PUBKEY (PATH_MAX): Try harder... 2000-08-01 Harlan Stenn * util/ntp-genkeys.c (main): Use snifflink() (snifflink): Implement... * configure.in: Check for readlink() 2000-07-31 Harlan Stenn * util/ntp-genkeys.c (main): Use strdup on the tokens returned from ntp_config... (crypto_config): Fix a typo... (crypto_config): Even more... (usage): Flesh it out. * include/ntp_config.h: * ntpd/ntp_config.c: Move a whack of #defines to ntp_config.h so ntp-genkeys.c can see them, too. * util/ntp-genkeys.c: Add stubs to work with ../ntpd/ntp_config.o, start hooking things up. (main): debugging (crypto_config): better implementation * ntpd/ntp_config.c (getconfig): * ntpd/ntpd.c: Initialize "debug" here, not in ntp_config.c * util/Makefile.am (ntp_genkeys_LDADD): Added ../ntpd/ntp_config.o * util/Makefile.am (ntp_genkeys_DEPENDENCIES): Added. 2000-07-30 Harlan Stenn * configure.in: 4.0.99k4 * util/ntp-genkeys.c: Start hacking for new functionality. * include/Makefile.am (noinst_HEADERS): Added ntp_cmdargs.h and ntp_config.h . * ntpd/ntp_config.c: Grab ntp_cmdargs.h. Make some new globals (ugh), move ntpd-specific variables and code to cmd_args.c . * ntpd/cmd_args.c: Move command argument processing functions from ntp_config.c to this file. * ntpd/Makefile.am (ntpd_SOURCES): Added cmd_args.c * include/ntpd.h: Move getstartup() to ntp_cmdargs.h * include/ntp_cmdargs.h: New file Begin the hacking fest to make it easier for ntp-genkeys to use ntpd's config processing code. I really hope this is the lesser of the evils... 2000-07-28 Harlan Stenn * util/ntp-genkeys.c (usage): Added. * ntpd/ntp_crypto.c: Cleanup * ntpd/ntp_proto.c (transmit): Add some parens. (peer_xmit): Add ntohl when grabbing sndauto.seq for broadcast. * ntpd/ntp_peer.c (findpeer): Cleanup * ntpd/ntp_loopfilter.c (local_clock): Typo From Dave Mills * include/ntp_config.h: Created * util/ntp-genkeys.c: Always build, but realize we may not have RSAREF. Compile parts appropriately. * util/Makefile.am (bin_PROGRAMS): Always make ntp-genkeys (ntp-genkeys_LDADD): Use $LIBRSAREF instead of the "real path" * configure.in: Lose MAKE_NTP_GENKEYS * configure.in: * util/ntp-genkeys.c: * util/Makefile.am: Renamed ntp_genkeys to ntp-genkeys. 2000-07-27 Harlan Stenn * ntpdate/ntpdate.c (ntpdatemain): Cleanup error message. * ntpdate/ntpdate.c (ntpdatemain): Add missing authtrust() call From: Jason Heiss * ntpd/refclock_ulink.c (ulink_receive): * ntpd/ntp_crypto.c: * libntp/authparity.c: Lint/noise cleanup From: Marc Brett * ntpd/ntp_proto.c: Specifically track manycastserver and survivors From: Dave Mills 2000-07-26 Sven Dietrich * ntpd/ntpd.c: remove WINNT priority adjustment to the ports/winnt area where it does not clutter up the main distribution. 2000-07-24 Harlan Stenn * ntpd/ntp_proto.c (receive): dstadr_sin needs higher visibility From: Dave Mills * flock-build: Added baldwin * ntpd/ntp_request.c: * ntpd/ntp_proto.c: * ntpd/ntp_peer.c: * ntpd/ntp_io.c: * ntpd/ntp_intres.c: * ntpd/ntp_crypto.c (make_keylist): * ntpd/ntp_control.c: * ntpd/ntp_config.c (CONF_MOD_IBURST, save_resolve): * include/ntpd.h (findpeerbyassoc, newpeer, peer_config, *_interface): * include/ntp_request.h (CONF_FLAG_IBURST): * include/ntp_crypto.h (crypto_xmit, make_keylist): * include/ntp.h (FLAG_IBURST): * html/release.htm: * html/confopt.htm: * html/assoc.htm: Add iburst option, fix broadcast/multicast and some types. From: Dave Mills 2000-07-20 Harlan Stenn * scripts/Makefile.am (bin_SCRIPTS): Install ntp-wait * configure.in: 4.0.99k 2000-07-19 Harlan Stenn * ntpd/ntp_proto.c (peer_xmit): PUBKEY cleanup 2000-07-18 Harlan Stenn * configure.in: 4.0.99j5 * html/ntpd.htm (HREF): Document other missing command-line options * html/ntpd.htm (HREF): Document * html/confopt.htm (href): Undocument * ntpd/ntp_config.c (getconfig): -N high for high-priority. Lose the ntp.conf way of setting priority. * ntpd/ntp_crypto.c: PUBKEY/AUTOKEY cleanup From Dave Mills 2000-07-17 Harlan Stenn * html/confopt.htm (href): Document it. * ntpd/ntp_config.c (getconfig): CONFIG_PRIORITY support * ntpd/ntpd.c (set_process_priority): Use it. * ntpd/ntp_crypto.c: Crypto key stuff * ntpd/ntp_proto.c: pubkey -> pubkey.ptr * ntpd/ntp_control.c (ctl_putpeer): fstamp -> pubkey.fstamp * ntpd/ntp_peer.c: * include/ntpd.h: * include/ntp_types.h: * include/ntp_request.h: * include/ntp_crypto.h: * include/ntp_control.h: * include/ntp.h: Type cleanup From: Dave Mills 2000-07-14 Harlan Stenn * ElectricFence/Makefile.am (check-local): Don't run the tests if we didn't build the programs... (check-local): Same, but watch the return codes... * ElectricFence/page.c: #include config.h if it's there. Properly handle the sys_errlist declaration. * html/ntpq.htm: * html/index.htm: * html/debug.htm: * html/authopt.htm: Reality check. From Dave Mills 2000-07-13 Harlan Stenn * Makefile.am (SUBDIRS): Added ElectricFence * configure.in (AC_CONFIG_FILES): Added ElectricFence support * ElectricFence: Imporpted. 2000-07-12 Harlan Stenn * util/ntp_genkeys.c (main): Cleanup * ntpd/refclock_wwv.c (wwv_qrz): sqrt -> SQRT * ntpd/refclock_chu.c (chu_rf): sqrt -> SQRT * ntpd/ntpd.c (set_process_priority): Disable high-priority for now. PUBKEY cleanup. * ntpd/ntp_timer.c: sys_revoketime cleanup. * ntpd/ntp_proto.c (receive): PUBKEY cleanup. Comment and code cleanup. (process_packet): Comment and code (PUBKEY) cleanup. (peer_xmit): Comment and code cleanup. (fast_xmit): Comment and code cleanup. * ntpd/ntp_peer.c (expire_all): revoketime cleanup. PUBKEY cleanup. * ntpd/ntp_crypto.c: Comment reorg. DH parameters are now file-static instead of local to subroutines. (make_keylist): peer->pcookie.key cleanup/fix (crypto_recv): Subroutine documentation cleanup, other cleanup (crypto_xmit): Cleanup/document. (crypto_setup): Cleanup/document. (crypto_agree): Cleanup/document. (crypto_rsa): now static (crypto_dh): now static. Comment cleanup. Code cleanup. (crypto_tai): now static. Code and comment cleanup. (crypto_config): Deal with CRYPTO_CONF_LEAP. * ntpd/ntp_control.c (CS_DHPARAMS): Rename corresponding token to "params". Remove CS_TAI from def_sys_var[]. (ctl_putsys): CS_HOST, CS_DHPARAMSm CS_REVTIME, and CS_LEAPTIME bugfix. CS_TAI cleanup. * ntpd/ntp_config.c (CONF_CRYPTO_LEAP): Added (getconfig): Added CONF_CRYPTO_LEAP support. * include/ntp_syslog.h: Lose GIZMO stuff. * include/ntp_crypto.h (CRYPTO_CONF_LEAP): Added * include/ntp.h: struct autokey, cookie,value, and pkt changes for signature field. Update the inline docs on pkt's exten field. From: Dave Mills 2000-07-08 Harlan Stenn * ntpd/ntp_util.c (stats_config): If we read a bogus old_drift, use 0 instead of failing. 2000-07-03 Harlan Stenn * README.cvs: Cleanup. * ntpd/refclock_datum.c (datum_pts_poll): index -> unit_index * ntpd/ntp_resolver.c (findhostaddr): const cleanup * libntp/recvbuff.c: * libntp/msyslog.c: * libntp/emalloc.c: * libntp/authreadkeys.c: Fix header order. From: Simon Burge * ntpd/ntp_util.c (stats_config): Use HAVE_FINITE and HAVE_ISFINITE * configure.in (ac_cv_struct_ntptimeval_timespec): isfinite() checks for HP-UX11. From: Albert Chin-A-Young 2000-07-02 Harlan Stenn * flock-build (LIST): Lose malarky, update some machine/OS descriptions * configure.in: 4.0.99j4 * ntpq/ntpq.c: Lose PUBKEY stuff - older ntpq's will complain when they see the info in a packet. * ntpd/ntp_proto.c (peer_xmit): TAI changes. * ntpd/ntp_crypto.c: Fix host/network byteorder stuff. Follow global->struct changes. TAI changes. Bugfixes. * ntpd/ntp_control.c: Follow field reorder/rename. * include/ntp_crypto.h: Move crypto stuff from separate globals into structs. * include/ntp_control.h (CS_HOST): Reorder/rename some fields From: Dave Mills 2000-06-30 Harlan Stenn * ntpd/ntp_peer.c (unpeer): Moved keystr cleanup inside ifdef PUBKEY * configure.in: 4.0.99j3 * html/release.htm: * html/ntpq.htm: * html/authopt.htm: Updates from Dave Mills * ntpd/ntp_request.c (dns_a): Don't call crypto_public for now... * ntpd/ntp_proto.c (receive): Follow the TEST wiggles (peer_xmit): TAI support * ntpd/ntp_crypto.c: TAI support * ntpd/ntp_control.c: CS_VERSION and CS_TAI support * include/ntp_crypto.h (CRYPTO_FLAG_TAI): Added. * include/ntp_control.h (CS_VERSION): Added. * include/ntp.h (TEST4,TEST5,TEST8,TEST9): Wiggle. From: Dave Mills * ntpd/Makefile.am (ntpd_SOURCES): Lose refclock_ulink331.c because the new refclock_ulink.c handles it. 2000-06-28 Harlan Stenn * ntpd/ntp_config.c (getconfig): Sanity check the right variable From: Dave Mills. 2000-06-25 Harlan Stenn * configure.in: 4.0.99j2 * ntpd/ntp_proto.c: * ntpd/ntp_peer.c: * ntpd/ntp_crypto.c: * include/ntp_crypto.h: * include/ntp.h: AUTOKEY/PUBKEY/DH/crypto changes From: Dave Mills 2000-06-23 Harlan Stenn * html/driver34.htm: * ntpd/refclock_ulink.c: * ntpd/refclock_ulink331.c: (removed) Updated for 320/330 series refclocks From: joseph lang * ntpd/refclock_oncore.c: Patches/improvements * html/driver30.htm: New release From: Reg Clemens 2000-06-17 Harlan Stenn * configure.in: 4.0.99j1 * ntpdc/ntpdc.c (getcmds): * configure.in: Readline support. From: Aaron Sherman * ntpd/refclock_ulink331.c: Added. Sent in by Doug Grosso * ntpd/Makefile.am (ntpd_SOURCES): Added refclock_ulink331.c * libntp/snprintf.c: Added stdio.h From: Marc Brett * include/ntp.h: struct autokey's siglen is u_int32. So is the siglen in struct cookie. So is siglen and value length in struct value. Add fstamp to struct peer. Resize the exten AUTOKEY field in struct pkt. * include/ntp_crypto.h: crypto_enable -> crypto_flags and a variety of other cleanups. * ntpd/ntp_config.c: crypto_enable -> crypto_flags, and some key/fudge cleanup. * ntpd/ntp_control.c: Much cleanup. * ntpd/ntp_crypto.c: Many changes that Harlan is too tired to properly document. * ntpd/ntp_peer.c: crypto_enable -> crypto_flags (peer_config): Hold off on crypto_public() until some resolver issue is fixed. * ntpd/ntp_proto.c (receive): Disable the reverse DNS lookup for now. (process_packet): Don't record_raw_stats() for now. crypto_enable was renamed to crypto_flags. (peer_xmit): In MODE_ACTIVE or MODE_PASSIVE, only call crypto_xmit() if the peer->cmmd is not CRYPTO_AUTO. Reset peer->cmmd to 0 when we're thru. Don't reset peer->cmmd to 0 until the end of MODE_CLIENT. * ntpd/ntpd.c: Lose the call to crypto_init(). Instead, call crypto_setup() later on if we have crypto_flags. * util/ntp_genkeys.c: Lose GENLEN; use PRIMELEN/2 . From Dave Mills. * ntpd/ntp_crypto.c (crypto_rsa): (crypto_dh): Do some debug logging if readlink() fails with something other than EINVAL (file isn't a symlink). 2000-06-04 James R. Van Zandt * html/miscopt.htm (trap): punctuation * html/ntpd.htm (-g): typo * html/miscopt.htm (logconfig): List the "clock" event class. "allprefix" should be two words. 2000-05-31 Harlan Stenn * ntpd/ntp_timer.c: Protect 2000-05-30 Harlan Stenn * ntpd/Makefile.am: Document what we expect from -lm 2000-05-29 Harlan Stenn * configure.in: 4.0.99j 2000-05-22 Harlan Stenn * html/ntptime.htm: More fixes From: Kazu TAKAMUNE 2000-05-16 Harlan Stenn * build (KEYSUF): * flock-build: * configure.in: Lose the "md5" options from the --with-crypto check; Dave hates it. * ntpd/ntp_util.c (stats_config): * ntpd/ntp_loopfilter.c (loop_config): * libntp/systime.c (adj_systime): * include/ntp_proto.h (NTP_MAXFREQ): Renamed MAX_FREQ to NTP_MAXFREQ * ntpd/ntpd.c (ntpdmain): * ntpd/ntp_proto.c (receive): (poll_update): * ntpd/ntp_intres.c (addentry): * ntpd/ntp_config.c (getconfig): Lint cleanup From: Marc Brett * include/ntp_stdlib.h: * libntp/systime.c (adj_systime): * ntpd/ntp_loopfilter.c (loop_config): * ntpd/ntp_util.c (stats_config): * ports/winnt/ntpd/nt_clockstuff.c (adj_systime): MAXFREQ -> MAX_FREQ * include/ntp_proto.h: Define MAX_FREQ 2000-05-15 Harlan Stenn * include/ntp_stdlib.h: * libntp/systime.c (adj_systime): * ntpd/ntp_loopfilter.c (loop_config): * ntpd/ntp_util.c (stats_config): * ports/winnt/ntpd/nt_clockstuff.c (adj_systime): sys_maxfreq -> MAXFREQ Per Dave Mills. 2000-05-14 Harlan Stenn * acinclude.m4: Typo... 2000-05-13 Harlan Stenn * libntp/gpstolfp.c (GPSORIGIN): Try new ULONG_CONST macro * ntpdate/ntptimeset.c: * ntpdate/ntpdate.h: * ntpd/refclock_oncore.c (oncore_msg_En): * ntpd/ntp_util.c (stats_config): * ntpd/ntp_request.c: * ntpd/ntp_intres.c (findhostaddr): * ntpd/ntp_config.c (getconfig): * libntp/systime.c (adj_systime): * libntp/lib_strbuf.c: * libntp/authparity.c: * libntp/audio.c: Header/lint cleanup From/reported by: Simon Burge * ntpd/ntp_resolver.c (findhostaddr): Compiler noise cleanup * ntpd/ntp_intres.c: Compiler noise cleanup * html/ntptime.htm: Document reality check From: Kazu TAKAMUNE 2000-05-12 Harlan Stenn * ntpd/ntp_intres.c (ntp_intres): Quiet some debug messages Reported by: Brian Bergstrand 2000-05-11 Harlan Stenn * scripts/mkver.in (ConfStr): Use -r if we're using RSAREF, otherwise use -a if we're using autokey, otherwise use no extra suffix. 2000-05-11 Sven Dietrich * ports/winnt/include/config.h: New defines to support AUTOKEY * ports/winnt/include/unistd.h: Added another dummy placeholder.h * ports/winnt/ntpd/ntpd.dsp: Added ntp_crypt.c to makefile * ports/winnt/ntpd/ntpd.c: service_main needs a local hostname[] * html/hints/winnt.htm: Add remark about 4.0.99i not compiling. These changes got WINNT running again. No idea if the keys stuff works however. 2000-05-10 Harlan Stenn * configure.in: 4.0.99i9 * ntpd/ntp_crypto.c: tstamp and autokey cleanup From: Dave Mills * ntpd/ntp_proto.c (clock_update): Only call expire_all() if AUTOKEY From many folks, including Reg Clemens 2000-05-07 Harlan Stenn * configure.in: 4.0.99i8 * flock-build: Use new --with-crypto choices * build (KEYSUF): Deal with new --with-crypto * configure.in: --with-crypto={md5,autokey,rsaref}; lose --enable-autokey 2000-05-06 Harlan Stenn * build (KEYSUF): Catch --disable-autokey first. 2000-05-05 Harlan Stenn * flock-build: If we don't use autokey, don't use rsaref either. * configure.in: 4.0.99i7 * build (KEYSUF): * flock-build: It's --disable-autokey now * configure.in: MD5 is not optional (but AUTOKEY is) * include/ntp_stdlib.h: * libntp/a_md5encrypt.c: * libntp/authkeys.c: (authhavekey): (MD5auth_setkey): (auth_delkeys): (authencrypt): (authdecrypt): * libntp/authreadkeys.c: (authreadkeys): * ntpd/ntp_proto.c (init_proto): * libntp/authusekey.c: MD5 is required. 2000-05-04 Harlan Stenn * configure.in: 4.0.99i6 * ntpd/ntp_proto.c (transmit): Fix up the UNREACH code. (receive): Lose some debug code. (clock_update): expire_all() if LEAP_NOTINSYNC crypto_xmit() a CRYPTO_AUTO if !FLAG_AUTOKEY instead of recauto.tstamp crypto_xmit() a CRYPTO_PRIV (not CRYPTO_AUTO) based on pcookie.tstamp crypto_xmit() a CRYPTO_AUTO (not CRYPTO_PRIV) based on FLAG_MCAST2 and !FLAG_AUTOKEY * ntpd/ntp_crypto.c (crypto_recv): Clean up debug output. Don't AUTOKEY if FLAG_MCAST2 From: Dave Mills * flock-build: Also make obe withouyt md5 (no AUTOKEY) * build (BDIR): Handle -noautokey build directory suffix * configure.in: Prepare for AUTOKEY in mkver.in * scripts/mkver.in (ConfStr): Indicate in the version string if we're not using AUTOKEY. 2000-05-03 Harlan Stenn * scripts/ntp-wait.in: Fun things with backspaces * configure.in: 4.0.99i5 * ntptrace/ntptrace.c: Protect sys/ioctl.h; grab config.h * ntpd/ntp_proto.c (transmit): AUTOKEY and tstamp fixes. (clock_update): Check oleap against LEAP_NOTINSYNC (peer_clear): Free pubkey when we're done Check peer's keynumber against the seq in MODE_BROADCAST. Reorder tstamp changes. * ntpd/ntp_crypto.c (crypto_recv): Check the packet if we get a CRYPTO_RESP and reorder the update of tstamp. (crypto_recv): Don't expire the peer until we're thru with it. * include/ntp.h: AUTOKEY and pkeyid changes From Dave Mills * ntpdate/ntpdate.c: Protect sys/ioctl.h 2000-05-01 Harlan Stenn * configure.in: 4.0.99i4 * include/ntp.h: * include/ntp_crypto.h: * include/ntpd.h: * ntpd/ntp_crypto.c: * ntpd/ntp_proto.c: * ntpd/ntpd.c: * util/ntp_genkeys.c: Dave Mills closed some potential vulnerabilities in the key protocol. 2000-04-28 Harlan Stenn * configure.in: 4.0.99i3 * ntpd/ntp_proto.c: Just check tstamp, forget about sndauto.seq * ntpd/ntp_crypto.c (crypto_recv): Lose inappropriate ntohl() conversion on tstamp. AUTOKEY if tstamp>0, not != Stash tstamp before we check pcookie.key (several places) * ntpd/ntp_control.c (ctl_putpeer): In CP_INITSEQ, check recauto.key, not keylist. From: Dave Mills 2000-04-27 Harlan Stenn * configure.in: 4.0.99i2 * ntpq/ntpq.c: PUBKEY stuff * ntpd/ntp_proto.c (clock_select): nreach, and better survivor pick. * ntpd/ntp_peer.c (newpeer): Better nextdate choice. * ntpd/ntp_control.c (ctl_putsys): Buglet in CS_HOSTNAM code. From Dave Mills. 2000-04-24 Harlan Stenn * build (IAM): Show hostname if we trip a buildkey check. 2000-04-23 Harlan Stenn * build: deal with optional 1st argument (SIG), which must match the contents of .buildkey * flock-build: Generalize, implement SIG and .buildkey, and drive list from LIST, which the user can override. 2000-04-21 Harlan Stenn * configure.in: 4.0.99i1 Dave updated some docs, implemented the kpoll variable, and wiggled a host/network byte order thing in the crypto key code. 2000-04-16 Harlan Stenn * ntpd/refclock_wwvb.c (wwvb_receive): Grab any character (instead of just a space) before the DSTchar. From: Dave Mills 2000-04-11 Harlan Stenn * configure.in: 4.0.99i Dave made some documentation updates. 2000-04-08 Harlan Stenn * flock-build: Add malarky. By default, --enable-parse-clocks. Start 2 builds, one with and the other without rsaref. * configure.in: 4.0.99h6 Dave improved the crypto stuff some more. 2000-04-05 Harlan Stenn * ntpd/refclock_acts.c (acts_receive): Do a better job with year conversions and leap-year checks. The PTB stuff caught this. Reported by: Daniel.Aeby@eam.admin.ch 2000-04-02 Harlan Stenn * ntpd/refclock_atom.c (atom_pps): Bugfix From: Philippe Charnier 2000-03-30 Harlan Stenn * libparse/clk_wharton.c (cvt_wharton_400a): Do not return CVT_NONE when receiving the early warning bit set. From: Philippe De Muyter * configure.in: 4.0.99h5 Dave made more changes to the auth key stuff. 2000-03-29 Harlan Stenn * configure.in: 4.0.99h4 Dave made a bunch of changes/fixes to the auth key stuff. 2000-03-22 Harlan Stenn * ntpd/ntp_resolver.c: Typos. 2000-03-21 Harlan Stenn * configure.in: 4.0.99h3 * ntpd/ntp_intres.c: Use LOG_INFO instead of LOG_DEBUG. * ntpd/ntp_resolver.c: Ditto. 2000-03-20 Harlan Stenn * util/Makefile.am (ntp_genkeys_LDADD): Might need snprintf (-lntp) * librsaref/Makefile.am (stamp-rsaref): nodist_HEADERS are not supoprted yet. Hack around it. * ntpd/ntp_resolver.c (findhostaddr): hstrerror isn't always available. * configure.in: Look for hstrerror. * util/ntp_genkeys.c (main): Use snprintf, not sprintf. * ntpd/ntp_crypto.c: Use gethostname, not uname * util/ntp_genkeys.c: Ditto From: Dave Mills 2000-03-19 Harlan Stenn * ntpd/ntp_proto.c (receive): Rename ntp_res_send() to ntp_res_name() and adjust the number of arguments. * ntpd/ntp_resolver.c (ntp_res_name): Ditto * include/ntpd.h: Ditto * ntpd/ntp_resolver.c: Add de_done to the dns_entry structure. 2000-03-18 Harlan Stenn * configure.in: 4.0.99h2 * libparse/Makefile.am (info_trimble.c): Use $(srcdir)/foo.sed instead of foo.sed . * librsaref/Makefile.am (stamp-rsaref): Copy each file to the build directory, not to the source directory. This sucks; it wastes space (but it's more portable). * configure.in (ac_busted_vpath_in_make): Add FreeBSD. I bet all systems that use pmake will need this. (ac_busted_vpath_in_make): Remove FreeBSD - I found a workaround. * Makefile.am: General cleanup * configure.in: 4.0.99h1 * ntpd/ntp_resolver.c: Lose unneeded header. 2000-03-17 Harlan Stenn * libntp/snprintf.c: #include * libntp/Makefile.am (EXTRA_libntp_a_SOURCES): Use it correctly... 2000-03-16 Harlan Stenn * libntp/Makefile.am (EXTRA_DIST): Added snprintf.c * configure.in: Look for (and provide if it's missing) snprintf() * ntpd/ntp_request.c (dns_a): Call crypto_public with the resolved name and the peer pointer. (dns_a): crypto_public() is only available if PUBKEY is #defined. * ntpd/ntp_crypto.c (crypto_public): sprintf is Evil. Use snprintf. (crypto_setup): Ditto (crypto_read): Ditto * ntpd/ntp_resolver.c (ntp_res_send): Lose some debugging noise. * ntpd/ntp_config.c (getconfig): Lose testing code. * ntpd/ntp_request.c (dns_a): Fix buglet in hostname length check. 2000-03-16 Harlan Stenn * ntpd/ntp_request.c (dns_a): Start cracking the returned information. 2000-03-15 Harlan Stenn * ntpd/ntp_resolver.c (ntp_res): Authenticate the keyid. * ntpd/ntp_crypto.c (crypto_line): Fix definition (crypto_read): Ditto. * ntpd/ntp_config.c (getconfig): Move req_keyid generation here. * librsaref/Makefile.am (BUILT_SOURCES): Cleanup more nodist_ stuff. 2000-03-14 Harlan Stenn * build (RSASUF): If we have rsaref2/ and are building --without-rsaref, note it as a build-directory suffix. * configure.in: 4.0.99h Crypto merge * librsaref/Makefile.am (nodist_librsaref_a_SOURCES): Added nodist_ 2000-02-28 Harlan Stenn * configure.in: Lose the changequote calls and fix the quoting. Reported by: Akim Demaille * ntpd/ntp_request.c: Log various error conditions. 2000-02-27 Harlan Stenn * configure.in: 4.0.99g * ntpd/ntpd.c: Only log EINTR if debug > 2. 2000-02-25 Harlan Stenn * scripts/mkver.in (ConfStr): Use `-r' when configured with crypto. * ntpd/refclock_wwvb.c (wwvb_receive): Undo the previous chagne. 2000-02-24 Harlan Stenn * ntpd/refclock_wwvb.c (wwvb_receive): LENWWVB0 can return 6 or 7 fields. From: Michael Sinatra with a cleanup from Ulrich. * scripts/mkver.in (ConfStr): Make RSAREF appear as part of the version. 2000-02-21 Sven Dietrich * ports/winnt/include/config.h: Enable MD5 and RANDOM by default * ports/winnt/libntp/SetSystemTime.c: Fix warning and const declaration From: Carl Byington 2000-02-21 Harlan Stenn * Makefile.am (SUBDIRS): Make librsaref right after includes so we can copy any needed header files over. * libntp/Makefile.am (INCLUDES): Also look in librsaref for des.h * ntpd/Makefile.am (INCLUDES): Ditto * util/Makefile.am (INCLUDES): Ditto * librsaref/Makefile.am (librsaref_a_SOURCES): Use the des.h from the rsaref2 distribution. * include/Makefile.am (noinst_HEADERS): No, we don't want des.h 2000-02-20 Harlan Stenn * include/Makefile.am (noinst_HEADERS): Add des.h 2000-02-19 Harlan Stenn * ntpd/ntp_config.c (do_resolve_internal): Try Real Hard to generate a full random key. From: Carl Byington * include/ntp.h: Now we know we have either mrand48() or random(). * configure.in: If we have mrand48, use it. Otherwise, use (and provide if necessary) random(). * libntp/Makefile.am (EXTRA_DIST): random.c * libntp/random.c: Added. 2000-02-18 Harlan Stenn * librsaref/Makefile.am (stamp-rsaref): Typo * configure.in (ac_cv_func_ctty_for_f_setown): Yes for OpenBSD (ac_cv_var_ntp_syscalls): Fix quoting of description From: Jonathan Rozes * librsaref/Makefile.am: Fix stamp-rsaref dependency order. * configure.in: 4.0.99f 2000-02-17 Harlan Stenn * ntpd/refclock_mx4200.c: Remove the DOP-weighted position averaging code and revert to a simple mean average. The weighted average consistently produced a *worse* result. Go figure. * html/mx4200data.htm: Cleanup, reflect current reality. * html/driver9.htm: Cleanup, reflect current reality. * html/copyright.htm: Cleanup, and credit where credit is due. From: Marc.Brett@westgeo.com * ntpd/refclock_oncore.c: Cleanup/fixes * html/driver30.htm: Cleanup * html/Oncore-SHMEM.htm: Cleanup From: Reg Clemens 2000-02-16 Sven Dietrich * winnt/scripts/mkver.bat: Frederick Czajka [w2k@austin.rr.com] winnt/ntpq/ntpq.dsp: modified mkver.bat to eliminate the winnt/ntpd/ntpd.dsp: need to have Perl installed and the winnt/ntpdc/ntpdc.dsp: PERL environment variable to be set. winnt/ntpdate/ntpdate.dsp: winnt/ntptrace/ntptrace.dsp: Thanks, Frederick! * ntpd/refclock_nmea.c: Correctly interpret the quality indicator. 2000-02-15 Harlan Stenn * ntpd/refclock_nmea.c: Correctly interpret the quality indicator. Deal with the time supplied in centiseconds. GPGCA/GPGGA cleanup. From: Andrew Hood * libparse/Makefile.am (K_CFLAGS): Use instead. Add NEED_NTP_BOPS. 2000-02-10 Harlan Stenn * scripts/ntp-wait.in: Intensify, including some suggestions from Ulrich. * configure.in: 4.0.99e * scripts/ntp-wait.in: Lose cruft, sleep after each try. * scripts/ntp-restart: It's ntpd now. Also, call ntp-wait. * configure.in (AC_CONFIG_*): New scripts that use PATH_PERL * scripts/Makefile.am (noinst_SCRIPTS): Added ntp-wait (noinst_SCRIPTS): Added all of the scripts that now use PATH_PERL * configure.in: Get the path to perl (AC_CONFIG_*): Added scripts/ntp-wait * ntptrace/ntptrace.c (DoTrace): If the server's srcadr is 0.0.0.0 then we haven't synced yet. Note and punt. Reported by: Bdale Garbee ,http://bugs.debian.org/56551 2000-02-09 Harlan Stenn * ports/winnt/include/config.h: Typo (CLOCK_PALISADE comment) From: Carl Byington * configure.in: Disable kernel_fll_bug for Generic_105181-17 under Solaris-2.6 From: Juergen Georgi 2000-02-07 Harlan Stenn * configure.in: 4.0.99d * html/Oncore-SHMEM.htm: New document * html/driver30.htm: Cleanup and improvements From: Reg Clemens 2000-01-30 Harlan Stenn * ntpd/refclock_oncore.c: Patches/improvements From: Reg Clemens 2000-01-29 Harlan Stenn * configure.in: 4.0.99c 2000-01-28 Harlan Stenn * configure.in: Autoconf update... * ntpdate/ntpdate.c (ntpdatemain): Typo From: Jack Bryans * Makefile.am (EXTRA_DIST): Add flock-build. Probably UDel specific... 2000-01-23 Harlan Stenn * ntpd/check_y2k.c (main): Reformat for ansi2knr * configure.in (AC_OUTPUT): Revert to obsolete form until automake catches up. 2000-01-22 Harlan Stenn * configure.in: Use AC_CHECK_TYPES((s_char)) instead of AC_CHECK_TYPE. (ac_cv_var_kernel_fll_bug): Generic_106541-08 is apparently OK, too. * scripts/Makefile.am (EXTRA_DIST): Need to explicitly distribute mkver.in and ntpver.in now, apparently. * configure.in: Search for the various audio_info members so the printing in audio_show is less of a circus. This required an autoconf upgrade. Major sigh. * libntp/audio.c (audio_show): Clean up (more) properly. 2000-01-21 Sven Dietrich * Add pointer to html/hints/winnt.htm to INSTALL file * Fix NT makefiles to accomodate at least one weird version of Visual C that can't handle the LFs without the DOS CR also. 2000-01-20 Sven Dietrich * Update Copyright in Palisade driver to 2000 * Fix Palisade MIN & MAX POLL to optimal values 2000-01-19 Harlan Stenn * html/driver7.htm: Patches from Dave Mills * html/driver36.htm: Patches from Dave Mills * html/copyright.htm: Added Kamal Mostafa 2000-01-19 Harlan Stenn * configure.in: 4.0.99b 2000-01-18 Harlan Stenn * ntpd/refclock_chu.c: NCHAN is used with generic ICOM. * ntpd/refclock_wwv.c: Use new audio stuff * ntpd/refclock_irig.c: Use new audio stuff * ntpd/refclock_chu.c: Use new audio stuff * ntpd/ntp_proto.c: Clean up * ntpd/ntp_loopfilter.c (local_clock): Clean up error message * include/icom.h: Number of arguments changed * libntp/Makefile.am (libntp_a_SOURCES): Added audio.c * libntp/audio.c: New file * include/Makefile.am (noinst_HEADERS): audio.h added * include/audio.h: New file From: Dave Mills * scripts/freq_adj: Added. FreeBSD frequency adjustment script. * configure.in: Do a better job on my oncore_ok check for SCO. 2000-01-15 Harlan Stenn * configure.in: 4.0.99a * scripts/ntpsweep: New version * html/copyright.htm: Added Hans Lambermont From: Hans Lambermont 2000-01-14 Harlan Stenn * ntpd/refclock_oncore.c (oncore_start): Only dance with the pps_device if it's there. From: reg@dwf.com * configure.in: ONCORE is OK if ac_cv_hdr_def_tiocdcdtimestamp=yes * build: Just overwrite the log file each time; sometimes NFS goes a little goofy. * ntpd/refclock_fg.c: Syntax/punctuation cleanup 2000-01-13 Harlan Stenn * scripts/ntpsweep: New version From: Hans Lambermont * ntpd/refclock_fg.c: New version * html/driver37.htm: New version From: Dmitry Smirnov 2000-01-12 Harlan Stenn * README.des: Cleanup 2000-01-12 Harlan Stenn * configure.in: 4.0.99 * html/driver36.htm: Cleanup * html/monopt.htm: Ditto From: Dave Mills * ntpd/ntp_intres.c (ntp_intres): Put "NTP_INTRES running" at a higher debug level 2000-01-11 Harlan Stenn * ntpd/refclock_wwv.c: More improvements From: Dave Mills 2000-01-10 Harlan Stenn * ntpd/refclock_wwv.c: Bugfixes/improvements From: Dave Mills * configure.in: Get the quoting right on the sys_errlist check. From documentation by: Akim Demaille 2000-01-08 Harlan Stenn * configure.in: cannot be detected... * ntpd/ntp_io.h: ...but every OS has it * ntpd/refclock_oncore.c: Lint removal * ntpq/ntpq_ops.c: Lint removal * ntpq/refclock_chu.c: chu_major() is not an audio routine (?), lint * libntp/icom.c: AIX doesn't have From: Marc.Brett@westgeo.com * ntpd/refclock_chu.c: NetBSD needs sys/ioctl.h (chu_debug): NetBSD-specific debug output. From: Frederick Bruckman 2000-01-06 Harlan Stenn * configure.in: 4.0.98m I skipped `l' - it looks like a `1'. * html/driver7.htm: Doc update * html/driver36.htm: Ditto * html/audio.htm: Ditto * ntpd/refclock_wwv.c: Dvae snuck another fix/change in on me. * configure.in: 4.0.98k * ntpd/refclock_chu.c (chu_start): Call icom_init with the speed * ntpd/refclock_wwv.c (wwv_start): Ditto, plus other improvements. * libntp/icom.c (icom_init): Add speed parameter * include/icom.h: Update declaration From: Dave Mills * include/Makefile.am (noinst_HEADERS): Added icom.h From: Frederick Bruckman 2000-01-05 Harlan Stenn * configure.in: 4.0.98j * ntpd/refclock_wwv.c (timecode): Make definition == declaration (wwv_newchan): Ditto (wwv_corr4): Dave fixed the declaration. * flock-build: Add rackety to the flock - it runs SunOS 4.1.3/cc * ntpd/refclock_wwv.c: Undo that declaration cleanup for now... * ntpd/ntp_io.c (open_socket): TOS support From: Marc.Brett@westgeo.com 2000-01-04 Harlan Stenn * ntpd/refclock_wwv.c: Declaration cleanup * ntpd/refclock_fg.c (fg_receive): Not all sprintf's are created equal... From: Marc.Brett@westgeo.com * ntpd/refclock_wwv.c: Dave Cleaned and Improved things. * ntpd/ntp_loopfilter.c (local_clock): Dave fixed something. * ntpd/refclock_wwv.c: Rename max to p_max or s_max as appropriate to avoid native SunOS compiler collision. (wwv_epoch): Make declaration == definition (wwv_rsec): Ditto (wwv_newchan): Ditto (wwv_qsy): Ditto (timecode): Ditto * ntpd/refclock_oncore.c (oncore_init_shmem): Use a cast to widen mmap's NIL offset. * ntpd/refclock_chu.c (chu_rf): Make declaration == definition. Rename min/max to es_min/es_max to avoid native SunOS compiler collision. (chu_uart): Make declaration == definition. * libntp/icom.c (sndpkt): Make declaration and definition equivalent. (doublefreq): Ditto. * ntpd/refclock_oncore.c (MAP_FAILED): Some systems do not #define this. * ntpd/refclock_wwv.c: * ntpd/refclock_chu.c: * libntp/icom.c: * libntp/Makefile.am: * include/icom.h: * html/driver7.htm: * html/driver36.htm: Support for ICOM. The WWV/H driver, by the way, is getting truly awesome. The CHU autotune function works okay as it is. I'd like to find somebody else to test the audio drivers just to make sure I haven't done something stupid. There is a new define ICOM intended for the driver autotune function; however, I crafted the thing in much the same way as the refclock_atom.c thing - it tries to open /dev/icom and, if that fails, goes quietly to sleep. From: Dave Mills 2000-01-03 Harlan Stenn * ntpd/refclock_oncore.c (oncore_read_config): Patches and cleanup From: Poul-Henning Kamp more isascii() stuff from HMS. * ntpd/refclock_fg.c (fg_receive): Cast. From: Tom Smith * ntpd/map_vme.c (map_vme): tx.access_result indicates failure on < 0, not <= 0. A fix that apparently did not get brought over from the ntp3 base. From: Michael Barone * configure.in: Move the ONCORE_SHMEM_STATUS check and definition here. * ntpd/refclock_oncore.c (oncore_init_shmem): Some systems do not have MAP_HASSEMAPHORE. * ntpd/refclock_oncore.c: Drive ONCORE_SHMEM_STATUS based on HAVE_SYS_MMAN_H . If this needs finer checks, do it in configure.in . (oncore_read_config): Add the isascii() checks; older versions of Unix don't guarantee correct behavior of is*() without it. * ntpd/refclock_oncore.c: Add proof-of-concept support for delivering receiver data stream to other processes through a memory-mapped file. From: Poul-Henning Kamp 2000-01-02 Harlan Stenn * configure.in (ac_refclock_chu): Provide the CHU driver by default, and by default prefer the AUDIO version. We used to limit this to SunOS or Solaris; now we drive by the availability of the audioio header file. Select the IRIG and WWV audio drivers the same way. * flock-build: build ignores the -l flag; lose it. (BUILD_ARGS): added. * build: Remove unused -l stuff (LOG variable). * ntpd/ntp_refclock.c: HAVE_PPSAPI header cleanup From: Reg Clemens 2000-01-01 Harlan Stenn * configure.in (CLOCK_WWV): Require or 1999-12-29 Harlan Stenn * configure.in: 4.0.98i * ntpd/refclock_gpsvme.c: Fixes From: Michael Barone Patch severely hacked by HMS to "make it conform". I hope I didn't break anything. * scripts/ntpsweep: Nifty utility From: Hans.Lambermont@nl.origin-it.com * ntpd/refclock_fg.c: * ntpd/refclock_conf.c: * ntpd/ntp_control.c: * ntpd/Makefile.am: * libntp/clocktypes.c: * include/ntp.h: * html/refclock.htm: * html/driver37.htm: * configure.in: * acconfig.h: Forum Graphic GPS clock support From: Dmitry Smirnov * configure.in: Default to compile the IRIG, CHU and WWV/H drivers and compile the CHU driver for audio, not modem. Requested by Dave Mills. * html/audio.htm: * html/driver36.htm: * html/qth.htm: Dave wrote these to go along with the changes I checked in yesterday. 1999-12-28 Harlan Stenn * ntpd/refclock_wwv.c: * ntpd/refclock_conf.c: * ntpd/refclock_chu.c: * ntpd/ntp_refclock.c: * ntpd/ntp_loopfilter.c: * html/refclock.htm: * html/pps.htm: * html/index.htm: * html/driver7.htm: * html/driver6.htm: * html/copyright.htm: I indulged an old agenda to polish up some programs originally written for a DSP evaluation board. The result is a really hot audio driver for the NIST WWV/WWVH time/frequency station plus a makeover for the CHU audio driver. Currently, they and their IRIG audio driver buddy use the SunOS/Solaris audio interface, which is clean and simple. I hook the line in jack to a shortwave radio and these drivers (driver 7 for CHU and driver 36 for WWV) and the drivers do the rest. The WWV/H driver is really hot - I am currently watching the ionosphere move from the doppler that shows up on the tick modulation tracked by the program. During midday when the F2 layer settles down, the program closes in on a few tens of microseconds of spare jitter and wander. This watch on whichever 15/20 MHz signal sounds the best. At night on 5/10 MHz and even 2.5 HMz where the multipath, interference and noise are much worse, the driver bangs around somewhat more. The CHU driver makeover discovered a few broken bones after all these years, but its majority decoder is back in business. For various reasons to icky to reveal here, its 103-modem demodulator is not quite as good as the chip, but it comes close and hey, 2025/2125 FSK is just not a good idea for HF radio. This driver is not nearly as sophisitcated as the WWV/H driver, but here a few hundred miles south of Ottawa, it does a passably good job. I tell you this in the hopes of getting somebody interested in porting the audio interface to other machines, in particular Linux, FreeBSD and anything else with a sound card. When the ionosphere stays cool, the WWV/H driver is as good as anything else around here and with much less jitter than a serial port. The only downside is all the audio drivers chew up a modest fraction of cycles for the DSP routines - a few percent on the newer silicon, up to half the cycles on an old Sparc IPC. Whattheheck, these old machines aren't doing anything else around here except serving time, and even when sucking cycles for DSP they still mind the PPS microseconds. The audio driver documentation had a makeover, too. From: Dave Mills 1999-12-20 Harlan Stenn * configure.in: 4.0.98h 1999-12-19 Harlan Stenn * libntp/syssignal.c: Small cleanup to Marc's patch. * ntpd/ntp_refclock.c: * ntpd/refclock_atom.c: Header cleanup * html/driver30.htm: Cleanup and improvements. From: Reg Clemens * ntpd/ntp_refclock.c: * ntpd/refclock_jupiter.c: * ntpd/refclock_msfees.c: * ntpd/refclock_mx4200.c: Portability (Solaris) patches * ntpd/refclock_mx4200.c: Self-survey improvements, cleanup for, PPS re-activation * libntp/syssignal.c: Fix for "interrupted system call" (EINTR) failure of the PPS ioctl(TIOCGPPSEV) call in Solaris. Not sure why it was interrupted, but this works around the failure. Not sure if the (now silent) interrupt affects the timing accuracy. THERE IS A CHANCE THIS PART OF THE PATCH MAY ADVERSELY AFFECT OTHER SYSTEMS! * scripts/ntp-status: Added. From: Marc.Brett@westgeo.com * ntpdate/ntpdate.c: Deal with multiple addresses. From: Jeffrey C Honig * ntpd/refclock_conf.c: Replaced macro SHM with CLOCK_SHM. * ntpd/refclock_shm.c (shm_poll): Take clock time in UTC. pp->day starts at 1, t->tm_yday starts at 0. From: Jakob Stoklund Olesen 1999-12-16 Harlan Stenn * NEWS: Updated ONCORE instructions From: Kamal A Mostafa 1999-12-13 Harlan Stenn * configure.in: 4.0.98g * ntpd/refclock_oncore.c: Cleanup and fixes From: Reg Clemens and Kamal A Mostafa 1999-12-11 Harlan Stenn * ntpd/refclock_wwv.c: Cleanup/checkin of the current state of affairs. From: Dave Mills * ntpd/refclock_oncore.c: #elif conversion. I can only hope I did it right. * ntpd/refclock_oncore.c: Various patches From: Reg Clemens and Kamal A Mostafa 1999-12-09 Harlan Stenn * ntpd/ntp_proto.c (default_get_precision): Use the right arg type to pass "sizeof freq" to sysctlbyname(). From: Ollivier Robert * ntpd/refclock_wwv.c: Cleanup and fixes. From: Dave Mills 1999-12-08 Harlan Stenn * ntpd/refclock_wwv.c: Cleanup and fixes * ntpd/refclock_conf.c: WWV declaration cleanup. From: Dave Mills 1999-12-07 Harlan Stenn * libparse/clk_rawdcf.c (cvt_rawdcf): Buglet. From: Frank Kardel acm.org> 1999-12-06 Harlan Stenn * ntpd/Makefile.am (ntpd_SOURCES): Added refclock_wwv.c * ntpd/refclock_wwvb.c: * ntpd/refclock_wwv.c: * ntpd/refclock_conf.c: * ntpd/refclock_chu.c: * libntp/clocktypes.c: * include/ntp.h: Dave cleaned some things up Dave cleaned some things up (WWVB -> SPECTRACOM, CHU -> CHU_AUDIO, WWV_AUDIO) * acconfig.h: REFCLOCK_WWVB -> REFCLOCK_SPECTRACOM, added REFCLOCK_WWV * configure.in: Deal with it. 1999-12-05 Harlan Stenn * ntpd/ntp_refclock.c (refclock_open): More PPS cleanup From: Dave Mills * ntpq/ntpq.c: * ntpq/ntpq_ops.c: Make `ntpq -pn' independent of DNS, ad advertised. From: Kamal A Mostafa * ntpd/refclock_mx4200.c (mx4200_start): make missing 3rd parameter a 0. 1999-12-04 Harlan Stenn * ntpd/ntp_refclock.c (refclock_open): "flags" processing cleanup (refclock_open): PPS device initialization cleanup * include/ntp_refclock.h (LDISC_CHU): (LDISC_PPS): Clean up comments From: Dave Mills 1999-12-03 Sven Dietrich * libntp/mexit.c: Moved WINNT port specific file to ports/winnt/libntp * ports/winnt/libntp/libntp.dsp: Fix WinNT makefile for new source loc. 1999-12-03 Harlan Stenn * libntp/Makefile.am (libntp_a_SOURCES): Lose mexit.c - Sven will move it to the winnt port area. 1999-12-03 Sven Dietrich * libntp/systime.c: Removed adjtime hack for WINNT * ports/winnt/ntpd/nt_clockstuff.c: Winnt double precision adjtime * ports/winnt/inlcude/clockstuff.h: Remove no longer needed externs 1999-12-02 Harlan Stenn * libparse/Makefile.in: Manually hacked to remove the libparse_kernel.a.[co] cruft * libparse/Makefile.am (k*.o): Generate these form the Makefile, not from separate .c files * html/tickadj.htm: * html/notes.htm: * html/hints/solaris.html: Point to the new dosynctodr report. * html/hints/solaris.xtra.S99ntpd: Update. Should be rewritten to take advantage of the new -g switch and perhaps a loop to wait until ntpd hits a reasonable "state". * html/hints/solaris-dosynctodr.html: New information From: "John W. Sopko Jr." 1999-12-01 Harlan Stenn * libntp/authkeys.c (auth_moremem): Clear memory allocated for sk. From: Hugo Mildenberger 1999-12-01 Sven Dietrich * libntp/recvbuff.c: Unused functions cleanup * ntpd/ntpd.c: ProcessorAffinity, IO cleanup * ports/winnt/instsrv/instsrv.c: Service name changed to NTP NT port shouldn;t hop between CPUs. Set affinity to first processor. Service name was NetworkTimeProtocol. Too long. Now NTP. 1999-12-01 Harlan Stenn * scripts/plot_summary.pl: Improved option parsing. Fixed one minor Perl compatibility error. * scripts/summary.pl: Official revision for NTPv4: Parse new statistic file formats correctly, provide error checking for bad input files, and guard against negative arguments to sqrt() because of numeric effects. Use one pattern to select valid statistic files. Add selectable output directory (``--output-directory'') and improved option parsing. Directory with statistic files (now settable also with ``--input-directory'') defaults to ``/var/log/ntp''. From: Ulrich Windl * html/driver8.htm: * libparse/clk_computime.c: * libparse/clk_dcf7000.c: * libparse/clk_hopf6021.c: * libparse/clk_meinberg.c: * libparse/clk_rawdcf.c: * libparse/clk_rcc8000.c: * libparse/clk_schmid.c: * libparse/clk_trimtaip.c: * libparse/clk_trimtsip.c: * libparse/data_mbg.c: * libparse/kclk_computime.c: * libparse/kclk_dcf7000.c: * libparse/kclk_hopf6021.c: * libparse/kclk_meinberg.c: * libparse/kclk_rawdcf.c: * libparse/kclk_rcc8000.c: * libparse/kclk_schmid.c: * libparse/kclk_trimtaip.c: * libparse/kclk_trimtsip.c: * libparse/kparse.c: * libparse/kparse_conf.c: * libparse/parse.c: * libparse/parse_conf.c: * libparse/parsesolaris.c: * libparse/parsestreams.c: * ntpd/refclock_parse.c: Mods and updates From: Frank Kardel acm.org> * acconfig.h: PCF refclock * configure.in: * html/driver35.htm: * html/refclock.htm: * include/ntp.h: * libntp/clocktypes.c: * ntpd/Makefile.am: * ntpd/ntp_control.c: * ntpd/refclock_conf.c: * ntpd/refclock_pcf.c: From: Andreas Voegele * acconfig.h: DECL_STIME_1 * configure.in (iCFLAGS): Use -std1 for alpha*-dec-osf* if we are using "cc". Reported by: Tom Smith 1999-11-30 Harlan Stenn * include/l_stdlib.h: DECL_SYSTIME_1 --- a long * * configure.in: Use it for DEC OSF[45] Reported by: Tom Smith * ntpd/refclock_parse.c: Add missing declarations * ntptrace/ntptrace.c: Ditto * ntpd/ntp_proto.c: Ditto * ntpd/refclock_palisade.c: Ditto From: Jonathan Stone 1999-11-18 Sven Dietrich * Win NT port updates * ntpd.dsp: makefile only builds supported refclocks * config.h: cleanup, undefine unsupported clock_atom * win32_io, clock_NMEA: foundation for future refclock support * recvbuff, transmitbuff, IOcompletionport: streamline packet handler * html/hints/winnt.htm: Added up-to-date html docs for WINNT 1999-11-17 Harlan Stenn * html/copyright.htm: Credit Jack for his work. * html/pic/*: Save a *ton* of space on the pictures. From: Jack Sasportas 1999-11-16 Harlan Stenn * configure.in : changequote for osf[45] test. Reported by: Tom Smith * ntp_update: Ignore stuff in any A.* directory. 1999-11-15 Harlan Stenn * configure.in: Clean up header checks for sys/syssgi.h and sys/select.h . Originally, we did not check for sys/select.h under some versions of SCO (see xntp3-5). Somewhere in ntp4 we dropped the SCO check and added the check for sys/syssgi.h, exclusive of checking for sys/select.h. Marc Brett can't think of a reason why we should not be checking for sys/select.h, so let's look for it now. 1999-11-13 Harlan Stenn * ntpdate/ntpdate.c: Add the ability for ntpdate to query a multicast address. We use the response to the multicast address to learn new servers which we then add to the peer list and query. In changing this I made the list of peers a linked list. To be complete I should detect a broadcast address and make it behave the same way. But that requires a scan of the interface list which is more complicated that I want to deal with... Fix a bug, allowing ntpdate to compile if SLEWALWAYS and STEP_SLEW are both defined. From: Jeffrey C Honig * ntpd/ntp_refclock.c: sys/sio.h and SCO5_CLOCK cleanup From: Kamal A Mostafa * ntpd/ntp_loopfilter.c: Let -g do a "correct any" for the first time adjustment. From: Dave Mills * configure.in: sys/sio.h needs to be checked independently. Reported by: Kamal A Mostafa 1999-11-11 Harlan Stenn * configure.in: 4.0.98f * configure.in: DECL_PLOCK_0 and DECL_STIME_0 are for dec-osf5*, too * ntpd/ntpd.c: DEC OSF cleanup (editorial comments by HMS) From: Tom Smith * ntpd/ntp_refclock.c: MAXUNIT bugfix From: Marc.Brett@westgeo.com * ntpd/ntp_refclock.c: * ntpd/ntpd.c: * ntpd/refclock_arc.c: * ntpd/refclock_as2201.c: * ntpd/refclock_atom.c: * ntpdc/ntpdc.c: * ntpq/ntpq.c: Code cleanup. From: Marc.Brett@westgeo.com * include/ntp_stdlib.h: * libntp/systime.c: * ntpd/ntp_proto.c: Replaced the 'sco5_oldclock' variable with 'systime_10ms_ticks'. Cleared libntp/systime.c and include/ntp_stdlib.h of references to SCO5_CLOCK and RELIANTUNIX_CLOCK (moved to ntpd/ntp_proto.c). From: Kamal A Mostafa * configure.in: alpha-dec-osf4* -> alpha*-dec-osf4*|alpha*-dec-osf5* From: Tom Smith * configure.in: Look for . If TIOCDCDTIMESTAMP is there, we have TTYCLK. * acconfig.h: Lose old AIOCTIMESTAMP stuff Reported by: Kamal A Mostafa 1999-11-10 Harlan Stenn * ntpd/ntpd.c (set_process_priority): Clean up nice() and setpriority() 1999-11-09 Harlan Stenn * Makefile.am (EXTRA_DIST): Added README.cvs Reported by: Kamal A Mostafa 1999-11-08 Harlan Stenn * configure.in: 4.0.98e 1999-11-07 Harlan Stenn * configure.in: Lose AIOCTIMESTAMP tests * ntpd/ntpd.c: lose select() EINTR debug warning * ntpd/ntp_refclock.c: AIOCTIMESTAMP -> TIOCDCDTIMESTAMP. Watch CLK_SETSTR. * ntpd/refclock_atom.c: fdpps is only there for PPS or PPSAPI. AIOCTIMESTAMP is gone now. From: Kamal A Mostafa * configure.in (HAVE_MLOCKALL): Deal with dec-osf5 realities * ntpd/refclock_ulink.c (ulink_poll): Fix cast. * libntp/machines.c (ntp_set_tod): Use a long* for the argument to stime(). Reported by: Tom Smith * ntpd/ntpd.c (set_process_priority): Use whatever we have until something works. * ntpd/ntp_loopfilter.c: Keep clock_frequency changes in a temp variable so we can record it to loopstats (near as HMS can tell). From: Dave Mills 1999-11-06 Harlan Stenn * acconfig.h: RELIANTUNIX_CLOCK * configure.in (ac_cv_var_tickadj): RELIANTUNIX_CLOCK * libntp/systime.c (adj_systime): Reliant patches From: Andrej Borsenkow 1999-11-05 Harlan Stenn * ntpd/refclock_parse.c (parse_start): ASYNC_PPS_CD_NEG cleanup * configure.in (ac_cv_make_ntptime): OK on Linux From: * configure.in: NetBSD has PPSAPI now F_SETOWN is needed for NetBSD From: Jonathan Stone 1999-11-02 Harlan Stenn * configure.in: 4.0.98d * ntpd/refclock_parse.c: Cleanup/fixes From: John Hay * ntpd/refclock_parse.c: Lose #include "ntp_select.h" * ntpd/ntpd.c: Lose #include "ntp_select.h" * ntpd/ntp_io.c: Lose #include "ntp_select.h" * ntpd/ntp_intres.c: Lose #include "ntp_select.h" * libntp/iosignal.c: Lose #include "ntp_select.h" * include/ntpd.h: #include "ntp_select.h" for declaration of activefds Reported by: Christian Krackowizer 1999-11-01 Harlan Stenn * configure.in: 4.0.98c * libntp/syssignal.c: Don't warn about SA_RESTART * libntp/recvbuff.c: Fix free buffer count From: Jeffrey C Honig * html/pps.htm: * html/howto.htm: * html/confopt.htm: * html/clockopt.htm: * html/uthopt.htm: Updates. From: Dave Mills * ntpd/refclock_wwvb.c: burst fixes * ntpd/refclock_ulink.c: burst fixes * ntpd/refclock_tpro.c: burst and NSTAGE fixes * ntpd/refclock_pst.c: burst fixes * ntpd/refclock_irig.c: SAMPLE -> SAMPLES * ntpd/refclock_heath.c: burst fixes * ntpd/refclock_dumbclock.c: burst fixes * ntpd/refclock_chronolog.c: burst fixes * ntpd/refclock_bancomm.c: burst fixes * ntpd/refclock_atom.c: burst fixes * ntpd/refclock_as2201.c: burst fixes * ntpd/ntp_refclock.c: PPSAPI, code, and comment cleanup/fixes * ntpd/ntp_proto.c: Broadcast/restrict cleanup * ntpd/ntp_loopfilter.c: Cleanup and fixes * libntp/gpstolfp.c: Lose the UL qualifiers - old compilers hate them From: Dave Mills 1999-10-31 Harlan Stenn * configure.in: TIOCSPPS cleanup 1999-10-20 Harlan Stenn * configure.in: 4.0.98b * ntpd/refclock_atom.c: AIOCTIMESTAMP patch * ntpd/ntpd.c: SCO clock patch * ntpd/ntp_request.c: noselect patch * ntpd/ntp_refclock.c: AIOCTIMESTAMP patch * ntpd/ntp_proto.c: noselect patch * ntpd/ntp_intres.c: noselect patch * ntpd/ntp_config.c: noselect patch * include/ntp_request.h: noselect patch * include/ntp.h: noselect patch From: Kamal A Mostafa * configure.in: * acconfig.h: TTYCLK_AIOCTIMESTAMP Stuff for Kamal * ntpd/refclock_atom.c (atom_pps): make "result" initialization uglier, but more bulletproof. * configure.in (sys/timepps.h): Fixed. From: John Hay 1999-10-19 Harlan Stenn * ntpd/refclock_oncore.c: Rename instance.state to instance.o_state * refclock_oncore.c: * refclock_mx4200.c: * refclock_chu.c: * refclock_atom.c: * ntp_refclock.c: * ntp_peer.c: * ntp_loopfilter.c: * include/ntp_refclock.h: Various cleanup and fixes From: Dave Mills 1999-10-17 Harlan Stenn * ntpd/ntp_config.c (CONFIG_FILE): NT changes From: Sven Dietrich 1999-10-16 Harlan Stenn * configure.in: sys/timepps.h verification changes * ntpd/refclock_atom.c (atom_poll): PPS cleanup From: Dave Mills (atom_pps): Portability patch From: John Hay * libntp/msyslog.c: * libntp/gpstolfp.c: Lint cleanup From: Jonathan Stone * parseutil/dcfd.c: abs() -> l_abs(), time.h (AIX 4.3.2 patches) From: Dana Kaempen * ntpd/refclock_oncore.c: * ntpd/refclock_atom.c: * ntpd/ntp_refclock.c: PPS cleanup From: John.Hay@mikom.csir.co.za * util/ntptime.c: * ntpdate/ntptimeset.c: * ntpdate/ntpdate.c: * ntpd/refclock_trak.c: * ntpd/refclock_oncore.c: * ntpd/refclock_mx4200.c: * ntpd/refclock_msfees.c: * ntpd/refclock_atom.c: * ntpd/ntp_control.c: * ntpd/ntp_config.c: * configure.in: * configure: PPS, Solaris 7, cleanup patches From: Marc.Brett@westgeo.com * ports/winnt/ntptrace/ntptrace.dsp: * ports/winnt/ntpq/ntpq.dsp: * ports/winnt/ntpdc/ntpdc.dsp: * ports/winnt/ntpdate/ntpdate.dsp: * ports/winnt/ntpd/refclock_trimbledc.c: * ports/winnt/ntpd/ntpd.dsp: * ports/winnt/ntpd/ntp_iocompletionport.c: * ports/winnt/ntpd/nt_clockstuff.c: * ports/winnt/libntp/util_clockstuff.c: * ports/winnt/libntp/libntp.dsp: * ports/winnt/libntp/SetSystemTime.c: * ports/winnt/instsrv/instsrv.c: * ports/winnt/include/sys/ioctl.h: * ports/winnt/include/termios.h: * ports/winnt/include/config.h: * ports/winnt/include/clockstuff.h: * ports/winnt/ntp.dsw: * ntpd/refclock_shm.c: * ntpd/refclock_palisade.c: * ntpd/ntpd.c: * ntpd/ntp_timer.c: * ntpd/ntp_refclock.c: * libntp/systime.c: * libntp/machines.c: NT patches From: Sven Dietrich 1999-10-15 Harlan Stenn * ntpd/refclock_wwvb.c: * ntpd/refclock_usno.c: * ntpd/refclock_ulink.c: * ntpd/refclock_tpro.c: * ntpd/refclock_pst.c: * ntpd/refclock_parse.c: * ntpd/refclock_palisade.c: * ntpd/refclock_oncore.c: * ntpd/refclock_mx4200.c: * ntpd/refclock_msfees.c: * ntpd/refclock_jupiter.c: * ntpd/refclock_irig.c: * ntpd/refclock_heath.c: * ntpd/refclock_chu.c: * ntpd/refclock_atom.c: * ntpd/refclock_as2201.c: * ntpd/refclock_arc.c: * ntpd/refclock_arbiter.c: * ntpd/refclock_acts.c: * ntpd/ntp_refclock.c: * include/ntp_refclock.h: Bunches of fixes. From: Dave Mills 1999-10-10 Harlan Stenn * html/driver16.htm: New version * ntpd/refclock_bancomm.c: New version From: "Cliff, Gary" "Ramasivan, Ganesh" * ntpd/refclock_ulink.c (ulink_receive): Cleanup (ulink_poll): Cleanup * ntpd/refclock_atom.c (atom_pps): SunOS timespec/timeval cleanup From: Marc.Brett@westgeo.com * INSTALL: Point NT folks at ports/winnt Reported by: Stephen Gildea * include/ntp_stdlib.h: Noise abatement * include/ntpd.h: Noise abatement Reported by: "W. David Higgins" * configure.in: DECL_STDIO_0 with gcc under solaris. * include/l_stdlib.h: DECL_TOUPPER_0 DECL_STRERROR_0 * configure.in: Fix a bunch of implicit declarations for SunOS * html/release.htm: cleanup - we still provide MD5. Reported by: Winslowe Lacesso 1999-10-09 Harlan Stenn * ntpd/refclock_oncore.c: * ntpd/refclock_atom.c: * ntpd/ntp_refclock.c: PPS API code updated to the current spec From: Dave Mills * configure.in (ac_cv_make_tickadj): Don't make tickadj starting with solaris2.5 Requested by: Dave Mills 1999-10-04 Harlan Stenn * configure.in: We might need -lsocket for the -lnsl check. 1999-09-19 Harlan Stenn * ntpd/refclock_ulink.c: Typos in C++ comment Reported by: Thomas.Tornblom@Sun.SE * configure.in: 4.0.98a * ntpd/ntp_config.c (getconfig): Fix typo. From: "David E. Myers" From: David Godfrey From: Geoffrey Sisson 1999-09-17 Harlan Stenn * configure.in: 4.0.98 NetInfo support: * config.guess * config.sub Add Mac OS (versions 10 and up). * acconfig.h * config.h.in * configure.in Check for NetInfo API; add HAVE_NETINFO macro and friends. * include/ntp.h * ntpd/ntp_config.c * ntpdate/ntpdate.c Add support for reading configuration from NetInfo. * ntpd/ntp_config.c Get rid of unnecessary eol variable in tokenizer. * html/notes.htm * html/ntpd.htm * html/ntpdate.htm Document NetInfo functionality. * util/tickadj.c Use HAVE_KVM_OPEN conditional around kvm.h include. From: Wilfredo Sanchez 1999-09-15 Harlan Stenn * acconfig.h: * config.h.in: * configure.in: * html/driver34.htm: * html/refclock.htm: * include/ntp.h: * libntp/clocktypes.c: * ntpd/Makefile.am: * ntpd/ntp_control.c: * ntpd/refclock_conf.c: * ntpd/refclock_ulink.c: Ultralink driver From: Dave Strout 1999-09-14 Harlan Stenn * configure.in: ReliantUNIX patches From: Andrej Borsenkow * ntpd/refclock_atom.c: PPS cleanup * ntpd/ntp_refclock.c (refclock_ioctl): PPS cleanup From: Dave Mills * ntptrace/ntptrace.c (ReceiveBuf): addserver() can return NIL. Reported by: "Alan J. Wylie" * libntp/ieee754io.c: * ntpd/ntp_proto.c: * ntpd/ntp_refclock.c: Lint cleanup. From: Marc.Brett@westgeo.com 1999-09-12 Harlan Stenn * ntpd/ntp_refclock.c (refclock_ioctl): Declaration cleanup. * ntpd/ntp_proto.c (init_proto): msyslog kern_enable at LOG_DEBUG. * ntpd/refclock_atom.c: Add missing declaration. 1999-09-11 Harlan Stenn * configure.in (ac_cv_make_ntptime): Just look for struct ntptimeval, not timespec or nsec (Solaris 2.7 should get ntptime and it uses msec). (ac_cv_var_oncore_ok): Reorder so it's a "normal" clock * configure.in: Solaris Kernel FLL bug fixed in 106541-07 1999-09-02 Harlan Stenn * configure.in: 4.0.97f * ntptrace/ntptrace.c: * ntpdate/ntptimeset.c: * ntpdate/ntptime_config.c: * ntpdate/ntpdate.c: * util/ntptime.c: * parseutil/dcfd.c: * libparse/parsestreams.c: * libparse/parse_conf.c: * libparse/parse.c: * libparse/clk_varitext.c: * libparse/clk_trimtsip.c: * libparse/clk_trimtaip.c: * libparse/clk_schmid.c: * libparse/clk_rcc8000.c: * libparse/clk_rawdcf.c: * libparse/clk_meinberg.c: * libparse/clk_hopf6021.c: * libparse/clk_dcf7000.c: * libparse/clk_computime.c: * libntp/msyslog.c: * libntp/iosignal.c: * libntp/syssignal.c: * adjtimed/adjtimed.c: * ntpd/refclock_shm.c: * ntpd/refclock_parse.c: * ntpd/refclock_palisade.c: * ntpd/refclock_mx4200.c: * ntpd/refclock_jupiter.c: * ntpd/refclock_datum.c: * ntpd/ntpd.c: * ntpd/ntp_util.c: * ntpd/ntp_timer.c: * ntpd/ntp_request.c: * ntpd/ntp_refclock.c: * ntpd/ntp_monitor.c: * ntpd/ntp_loopfilter.c: * ntpd/ntp_io.c: * ntpd/ntp_intres.c: * ntpd/ntp_filegen.c: * include/l_stdlib.h: and errno declaration cleanup. * ntpd/map_vme.c: cleanup some spacing. 1999-09-01 Harlan Stenn * configure.in: 4.0.97e * configure.in (ac_cv_struct_sigaction_has_sa_sigaction): * acconfig.h: Ditto * parseutil/dcfd.c (main): Use it. From: HOSAKA Eiichi 1999-08-29 Harlan Stenn * configure.in: 4.0.97d * include/ntp_stdlib.h: Clean up previous NeXT patch. From: Jack Bryans * ntpd/refclock_parse.c: Permit RTS to power a DCF77. From: Carsten Paeth * ntpd/refclock_oncore.c (oncore_start): This makes the Oncore run on systems without hardpps(). From: Poul-Henning Kamp 1999-08-28 Harlan Stenn * configure.in: 4.0.97c * configure.in (ac_cv_make_ntptime): Typo. From: Ulrich Windl 1999-08-26 Harlan Stenn * configure.in: 4.0.97b * libntp/iosignal.c: * ntpd/ntp_peer.c: * ntpd/refclock_nmea.c: * ntpdate/ntptime_config.c: * ntpdate/ntptimeset.c: AIX, Irix, and SunOS lint cleanup From: Marc.Brett@westgeo.com 1999-08-24 Harlan Stenn * configure.in 4.0.97a * configure.in (AC_OUTPUT): added scripts/Makefile * Makefile.am (SUBDIRS): Added scripts * scripts/Makefile.am: Added 1999-08-23 Harlan Stenn * ntpd/refclock_nmea.c: Patches for: Trimble OEM Ace-II receiver. Low cost PCB with single voltage input, external active antenna and two serial ports with either NMEA and ITAPs output. Programmable to be tuned for 'time' accuracy in fixed station config. From: Nick Hibma 1999-08-21 Harlan Stenn * ntpd/ntp_config.c: Added listen_to_virtual_ips support (-L flag) * ntpd/ntp_io.c: Ditto 1999-08-19 Harlan Stenn * ntpd/ntp_intres.c (request): Lint cleanup * ntpd/ntp_control.c (ctl_putclock): Ditto * libntp/recvbuff.c (getrecvbufs): Ditto (get_free_recv_buffer): Ditto * libntp/systime.c (adj_systime): Ditto 1999-08-18 Harlan Stenn * configure.in: 4.0.97 * libntp/systime.c: * ntpd/ntp_loopfilter.c: * ntpd/ntpd.c: * ports/winnt/libntp/nt_clockstuff.c: From: Sven Dietrich * README.cvs: Updated. * configure.in: * include/ntp_machine.h: * libntp/mexit.c: * ntpd/ntp_config.c: * ntpd/ntp_peer.c: * ntpd/ntp_restrict.c: * ntpd/refclock_arc.c: * ntpdate/ntpdate.c: Irix, SunOS, AIX, lint patches From: Marc.Brett@westgeo.com * util/ansi2knr.c: New release (fix for bug reported by Marc Brett) From: "L. Peter Deutsch" * include/ntp_stdlib.h: NeXT portability patch From: Jack Bryans * configure.in: * dot.emacs: (cleanup) * ntpdate/Makefile.am: * ntpdate/ntpdate.h: * ntpdate/ntptime_config.c: * ntpdate/ntptimeset.c: ntptimeset patches. From: Jeffrey Hutzelman * ntpd/refclock_parse.c (local_input): ts.l_ui -> ts.fp.l_ui 1999-08-11 Harlan Stenn * configure.in: 4.0.96p1 * ntpd/ntpd.c (sys/resource.h): Include this file only #if HAVE_SYS_RESOURCE_H. (set_process_priority): Use TIOCNOTTY only if it is #define'd. * ntpd/refclock_parse.c (STREAM): STREAM does not imply HAVE_TERMIOS. (termios.h, termio.h, fcntl.h): Do not include those files here; they are already included by ntp_refclock.h or ntp_io.h. * ntpd/refclock_leitch.c (sgtty.h, termios.h, termio.h): Do not include those files here; they are already included by ntp_refclock.h. * ntpdate/ntpdate.c (sys/resource.h) : Include that file only #if HAVE_RESOURCE_H. From: Philippe De Muyter * ntptrace/ntptrace.c (input_handler): Make it a "normal" function definition. Reported by: GIANNI_CATANIA@hp-italy-om6.om.hp.com * configure.in: pc-cygwin32 -> pc-cygwin* because of a change in B20. From: Stephen Gildea 1999-08-09 Harlan Stenn * configure.in: 4.0.96 * parseutil/dcfd.c (main): Replace SA_ONSTACK and SV_ONSTACK with HAVE_SIGACTION and HAVE_SIGVEC, respectively. HP-UX provides both of the former but only one of the latter... 1999-08-08 Harlan Stenn * configure.in: Better tests for -lnsl and -lsocket From: Albert Chin-A-Young Works for me - handle openlog() and -lgen the same way. * Makefile.am (EXTRA_DIST): Add in the y2k notes * parseutil/dcfd.c: Renamed drift_comp to accum_drift * configure.in: Added MAKE_CHECK_Y2K support; check_y2k needs libparse. * ntpd/Makefile.am (check_PROGRAMS): Use MAKE_CHECK_Y2K * ntpd/Makefile.am (check-local): Added. * parseutil/Makefile.am (check-local): Added. * include/ntp.h: Y2KFixes * libparse/parse.c: Ditto * ntpd/Makefile.am (check_PROGRAMS): Ditto * ntpd/refclock_acts.c: Ditto * ntpd/refclock_arc.c (arc_receive): Ditto * ntpd/refclock_heath.c: Ditto * ntpd/refclock_hpgps.c: Ditto * parseutil/Makefile.am (check-local): Ditto * parseutil/dcfd.c (check_y2k): Ditto * NOTES.y2kfixes: Ditto * readme.y2kfixes: Ditto * results.y2kfixes: Ditto * ntpd/check_y2k.c: Ditto From: y2k@y2k.labs.att.com 1999-08-07 Harlan Stenn * configure.in: Look for sys/ppstime.h. 1999-07-31 Harlan Stenn * ntpd/ntp_io.c (create_sockets): Typo. From: Doug Wells 1999-07-29 Harlan Stenn * configure.in (ac_cv_struct_ntptimeval): Explicitly look for struct ntptimeval. (ac_cv_var_kernel_pll): Require struct ntptimeval. Linux. Grrr. Reported by: Ronald Kuetemeier 1999-07-27 Harlan Stenn * configure.in: 4.0.95 * ports/winnt: New release From: Sven Dietrich 1999-07-26 Harlan Stenn * libntp/machines.c (ntp_set_tod): Bugfix From: Andrej Borsenkow 1999-07-25 Harlan Stenn * configure.in: 4.0.94b * acconfig.h: * configure.in: * libparse/Makefile.am: * libparse/parse_conf.c: * libparse/clk_varitext.c: * libparse/kclk_varitext.c: * ntpd/refclock_parse.c: VARITEXT parse clock * ntpdate/ntpdate.c: bugfix From: Tony McConnell 1999-07-24 Harlan Stenn * include/ntp_syscall.h (ntp_gettime): Make it static * configure.in: Added AC_C_INLINE Reported by: "Charles C. Fu" 1999-07-23 Harlan Stenn * include/ntpd.h: * libntp/machines.c: * libntp/systime.c: * ntpd/ntp_config.c: * ntpd/ntp_filegen.c: * ntpd/ntp_io.c: * ntpd/ntp_proto.c: * ntpd/ntp_timer.c: * ntpdate/ntpdate.c: Windows NT port cleanup From: Sven Dietrich 1999-07-22 Harlan Stenn * libntp/authkeys.c: * libntp/ieee754io.c: * libntp/iosignal.c: * libntp/machines.c: * libntp/mexit.c: * libntp/recvbuff.c: * ntpd/ntp_filegen.c: * ntpd/ntp_loopfilter.c: * ntpd/ntp_request.c: * ntpd/ntp_timer.c: * ntpd/ntpd.c: Compile/lint cleanup From: Allen Smith 1999-07-21 Harlan Stenn * configure.in: 4.0.94a * configure.in (ac_cv_make_ntptime): Add tv_nsec check. * include/Makefile.am (noinst_HEADERS): Forgot ntp_syscall.h From: John.Hay@mikom.csir.co.za * configure.in: 4.0.94 * Makefile.am (SUBDIRS): librsaref (dist-hook): Lose CVS subdirs in the distribution tarball * include/Makefile.am (noinst_HEADERS): Added iosignal.h, recvbuff.h * Makefile.am (dist-hook): Don't call dos2unix anymore 1999-07-20 Harlan Stenn * acconfig.h: * util/ntptime.c: FreeBSD nano patches From: Per Hedeland and Allen Smith * include/ntp.h: include/ntp_fp.h: include/ntp_io.h: include/ntp_machine.h: include/ntp_refclock.h: include/ntp_stdlib.h: include/ntpd.h: libntp/Makefile.am: libntp/emalloc.c: libntp/machines.c: libntp/mexit.c: libntp/msyslog.c: libntp/statestr.c: libntp/syssignal.c: libntp/systime.c: libparse/parse.c: libparse/parse_conf.c: ntpd/ntp_control.c: ntpd/ntp_intres.c: ntpd/ntp_io.c: ntpd/ntp_proto.c: ntpd/ntp_refclock.c: ntpd/ntp_request.c: ntpd/ntp_timer.c: ntpd/ntp_util.c: ntpd/ntpd.c: ntpd/refclock_nmea.c: ntpd/refclock_palisade.c: ntpd/refclock_palisade.h: ntpd/refclock_shm.c: ntpdate/ntpdate.c: ntptrace/ntptrace.c: Cleanup * libntp/recvbuff.c: libntp/iosignal.c: include/iosignal.h: include/recvbuff.h: Added From: Sven_Dietrich@Trimble.COM * README: Add README.cvs * configure.in (ac_cv_var_struct_ntptime_val_timespec): Typo. From: John Hay 1999-07-19 Harlan Stenn * Makefile.am (EXTRA_DIST): Lose ntpmak; "build" does a better job. * ntpq/Makefile.am (version.o): Use mkver * ntptrace/Makefile.am (version.o): Ditto * ntpdate/Makefile.am (version.o): Ditto * ntpd/Makefile.am (version.o): Ditto * ntpdc/Makefile.am (version.o): Ditto * configure.in (AC_OUTPUT): scripts/mkver * scripts/mkver.in: Created. Note RSAREF in the version string 1999-07-18 Harlan Stenn * README.des: Updated. * ntpq/Makefile.am (LDADD): Add LIBRSAREF * ntpdc/Makefile.am (LDADD): Add LIBRSAREF * ntpdate/Makefile.am (LDADD): Add LIBRSAREF * ntpd/Makefile.am (LDADD): Add LIBRSAREF * configure.in (AC_OUTPUT): Added librsaref/Makefile Added tests for making/using librsaref.a Lose old DES stuff; AC_DEFINE(DES) if we find the rsaref stuff. 1999-07-11 Harlan Stenn * ntpd/refclock_trak.c (trak_receive): disambiguate expression. At least now it is unambiguous. It may even still be correct. Reported by: Tom Smith * ntp_update (UPDATE_OPTIONS): Typo. 1999-07-07 Harlan Stenn * ntp_update: Check out copyright.htm before COPYRIGHT * ntpd/ntp_config.c: Support for PPS assert/clear/hardpps * ntpd/ntp_refclock.c (refclock_ioctl): Ditto (refclock_gtlin): Ditto * html/clockopt.htm: Document. From: John Hay * html/monopt.htm: We have four types of files now * ntpd/refclock_oncore.c: If debug is on, tell when we are waiting for a valid almanac From: Poul-Henning Kamp * include/ntp_machine.h (HAVE_TERMIOS): STREAMS does not imply HAVE_TERMIOS !!! * include/parse.h (timercmp): Macro defined if needed. * ntpd/ntp_config.c (SIGCHLD): Macro defined as SIGCLD if needed. (sys/wait.h): File included only if HAVE_SYS_WAIT_H. * configure.in (sys/wait.h): File added to AC_CHECK_HEADERS list. From: Philippe De Muyter 1999-06-23 Harlan Stenn * ntpd/refclock_irig.c (irig_debug): NetBSD patches From: Frederick Bruckman * util/ntptime.c (main): ntx.freq bugfix (-f option) From: Frederick Bruckman 1999-06-22 Harlan Stenn * configure.in: Fix typo with DECL_H_ERRNO test * ntpd/ntp_loopfilter.c: Lose syscall decl, it's handled in l_stdlib.h now. * ntpd/ntp_request.c: Ditto * util/ntptime.c: Ditto Mon May 31 18:49:49 1999 Rainer Orth * ntpd/ntp_proto.c (proto_config): Don't set sys_bclient on PROTO_MULTICAST_ADD, only caller can decide; remove wrong set on PROTO_MULTICAST_DEL. Mon May 31 18:49:49 1999 Rainer Orth * ntpd/refclock_parse.c (stream_receive): Cast size_t to int to match format. (local_receive): Likewise. (trimbletaip_event): Likewise. (stream_receive): Cast struct timeval members to long to match format. (local_receive): Likewise. * ntpd/ntp_util.c (stats_config): Cast size_t to int to match format. * libparse/clk_rawdcf.c (cvt_rawdcf): Cast ptr difference to int to match format. * ntpd/refclock_parse.c (gps16x_poll): Likewise. * ntpd/ntp_filegen.c (filegen_open): Use long format, cast arg to match. * ntpd/refclock_parse.c (list_err): Use long format to match arg. (parse_statistics): Likewise. (gps16x_message): Likewise. (cvt_ts): Use long format, cast args to match. (parse_start): Add missing arg. (gps16x_message): Swap args to match format. * ntpd/ntpd.c (ntpdmain): Cast uid to long, adapt format. * ntpd/ntp_intres.c (readconf): Use long format to match arg. * ntpd/ntp_io.c (getrecvbufs): Likewise. * ntpd/ntp_proto.c (default_get_precision): Likewise. * ntpd/ntp_loopfilter.c (local_clock): Cast clock_panic to int to match format. * ntpd/ntp_io.c (io_multicast_add): Print s_addr member, not struct in_addr, to match format. * include/ntp_stdlib.h: Declare msyslog() as printf-like for gcc format checking. Fri May 28 16:39:35 1999 Rainer Orth * ntpdc/ntpdc_ops.c (iostats): Align timereset line. * ntpq/ntpq_ops.c (doopeers): Properly align header. * ntpdc/ntpdc_ops.c (debug): Removed declaration, already in ntp_stdlib.h. * ntpq/ntpq_ops.c: Likewise. * ntpdate/ntpdate.c (debug): Declare volatile to match ntp_stdlib.h. * ntpdc/ntpdc.c, ntpq/ntpq.c, ntptrace/ntptrace.c, util/tickadj.c, util/ntptime.c: Likewise. * include/parse.h (debug): Don't declare to avoid clash with ntp_stdlib.h. * include/Makefile.am (noinst_HEADERS): Add new ntp_syscall.h. * configure.in: Also check for -lrt for POSIX.1c functions. Wed May 26 21:03:30 1999 Rainer Orth * configure.in: Removed -Wwrite-strings from CFLAGS. * ntpdc/ntpdc.c (help): Remove superfluous cast. * ntpq/ntpq.c (help): Likewise. Tue May 25 18:00:49 1999 Rainer Orth * ntpq/ntpq_ops.c (struct varlist): name cannot be const char * since it may be malloc'ed. * ntpdc/ntpdc.c (sendrequest): Declare pass as const char *, don't lose const in cast. * ntpq/ntpq.c (sendrequest): Likewise. * ntpd/ntp_control.c (ctl_getitem): Remove superfluous cast. * include/ntpd.h (struct ctl_var): text cannot be const char * since it's malloc'ed. 1999-06-22 Harlan Stenn * include/l_stdlib.h: Don't include , add forward declaration of struct in_addr instead. From: Rainer Orth Patch: * include/l_stdlib.h: Fixed syscall() declaration. * configure.in: Updated test to match. * configure.in: Check if we need to declare errno and h_errno. Check for which may provide a h_errno declaration and which the latter needs. * acconfig.h: Provide the necessary templates. * include/ntp_syscall.h: New file, hides various implementations of ntp_adjtime() and ntp_gettime() syscalls. * ntpd/ntp_loopfilter.c: Use it. * ntpd/ntp_request.c: Likewise. * ntpd/refclock_local.c: Likewise. * util/ntptime.c: Likewise. * include/l_stdlib.h: Include , declare inet_ntoa if necessary. Moved syscall() declaration here. * kernel/sys/parsestreams.h: Include for it's definition of struct ppsclockev. Include unconditionally for definition of CIOGETEV via TIOCGPPSEV. * kernel/sys/ppsclock.h: Protect struct ppsclockev from redefinition. * include/ntp_refclock.h: Protect it from multiple inclusion. * include/ntp_fp.h: Likewise. * include/ntp.h: Likewise. * include/ntpd.h: Include ntp_refclock.h for MAXDIAL declaration. * libntp/authkeys.c: Include ntpd.h for current_time declaration. * include/ntpd.h (getauthkeys, auth_agekeys, rereadkeys): Moved prototypes to ntp_stdlib.h * include/ntp_stdlib.h: Declare variables exported by libntp. * include/ntpd.h: Likewise for ntpd. * libntp/authkeys.c (key_hash, authnokey, authfreekeys, cache_flags): Made static. * libntp/systime.c (tvu_maxslew, tsf_maxslew, sys_clock_offset, sys_residual): Likewise. * ntpd/ntp_intres.c (confentries): Likewise. * ntpd/ntp_loopfilter.c (clock_offset, clock_panic): Likewise. (pll_nano): Likewise. Removed duplicate definition. * ntpd/ntp_peer.c (peer_free, current_association_ID, assocpeer_calls, init_peer_starttime): Likewise. * ntpd/ntp_proto.c (sys_offset, sys_authdly): Likewise. * ntpd/ntp_request.c (numrequests, numresppkts, errorcounter): Likewise. * ntpd/ntp_restrict.c (res_calls, res_found, res_not_found, res_timereset, res_limited_refcnt): Likewise. * ntpd/ntpd.c (was_alarmed, worker_thread): Likewise. * ntpq/ntpq_ops.c: Moved declaration of external variable from ntpq.c to file scope. * adjtimed/adjtimed.c: Moved declarations of external variables to ntpd.h and ntp_stdlib.h. * clockstuff/propdelay.c: Likewise. * libntp/a_md5encrypt.c, libntp/authencrypt.c, libntp/authkeys.c, libntp/mfp_mul.c, libntp/msyslog.c, libntp/systime.c: Likewise. * ntpd/ntp_config.c, ntpd/ntp_control.c, ntpd/ntp_filegen.c, ntpd/ntp_intres.c, ntpd/ntp_io.c, ntpd/ntp_loopfilter.c, ntpd/ntp_monitor.c, ntpd/ntp_peer.c, ntpd/ntp_proto.c, ntpd/ntp_refclock.c, ntpd/ntp_request.c, ntpd/ntp_restrict.c, ntpd/ntp_timer.c, ntpd/ntp_util.c, ntpd/ntpd.c, ntpd/refclock_acts.c, ntpd/refclock_arbiter.c, ntpd/refclock_arc.c, ntpd/refclock_as2201.c, ntpd/refclock_atom.c, ntpd/refclock_bancomm.c, ntpd/refclock_chronolog.c, ntpd/refclock_chu.c, ntpd/refclock_datum.c, ntpd/refclock_dumbclock.c, ntpd/refclock_gpsvme.c, ntpd/refclock_heath.c, ntpd/refclock_hpgps.c, ntpd/refclock_irig.c, ntpd/refclock_jupiter.c, ntpd/refclock_leitch.c, ntpd/refclock_local.c, ntpd/refclock_msfees.c, ntpd/refclock_mx4200.c, ntpd/refclock_nmea.c, ntpd/refclock_oncore.c, ntpd/refclock_palisade.h, ntpd/refclock_parse.c, ntpd/refclock_pst.c, ntpd/refclock_shm.c, ntpd/refclock_tpro.c, ntpd/refclock_trak.c, ntpd/refclock_true.c, ntpd/refclock_usno.c, ntpd/refclock_wwvb.c: Likewise. * ntpdate/ntpdate.c: Likewise. * ntpdc/ntpdc.c, ntpdc/ntpdc_ops.c: Likewise. * ntpq/ntpq.c: Likewise. * ntptrace/ntptrace.c: Likewise. * util/ntptime.c, til/tickadj.c: Likewise. From: Rainer Orth * include/ntp_machine.h: Removed superfluous yy/mm/dd comments. * include/ntpd.h: Likewise. * libntp/authencrypt.c: Likewise. * libntp/a_md5encrypt.c: Likewise. * libntp/caljulian.c: Likewise. * libntp/ymd2yd.c: Likewise. * libntp/syssignal.c: Likewise. * libntp/ymd2yd.c: Likewise. * ntpd/ntp_control.c: Likewise. * ntpd/ntp_io.c: Likewise. * ntpd/ntp_timer.c: Likewise. * ntpdate/ntpdate.c: Likewise. * ntpq/ntpq_ops.c: Likewise. * ntpd/ntp_peer.c (findpeer): Wrap debug output in DEBUG/debug. From: Rainer Orth * dot.emacs: Removed wrong indentation of substatements. Wrap in c-style. From: Rainer Orth * ntpd/refclock_palisade.c: Patches from Marc Brett * ntpd/refclock_palisade.h: Ditto. * util/hist.c: Ditto. Tue Jun 1 00:40:04 1999 Harlan Stenn * build: mips-dec-ultrix4.4 hates "set -e" * flock-build: Created * build: added -l option Mon May 31 20:28:40 1999 Harlan Stenn * README: Removed auto{make,conf}.patch files Tue May 25 01:20:53 1999 Harlan Stenn * Makefile.am ($(srcdir)/COPYRIGHT): Added (EXTRA_DIST): Remove auto*.patches Thu May 20 01:03:00 1999 Harlan Stenn * Makefile.am (dist-hook): Call dos2unix on the .htm files * ntpd/refclock_palisade.h: Clean up declarations. * configure.in (ac_cv_struct_ntptimeval_timespec): Added. (ac_cv_make_ntptime): Only if ntptimeval does not use timespec. * util/tickadj.c: Linux Patches From: Reg Clemens Wed May 19 01:18:24 1999 Harlan Stenn * configure.in: 4.0.93a * ntpd/refclock_palisade.h: Restore some lost patches From: Kamal A Mostafa Sun May 16 13:18:32 1999 Philippe De Muyter * libparse/clk_wharton.c (cvt_wharton_400a, inp_wharton_400a): Expect serial output format number 1, not 5. (clock_wharton_400a) : Likewise. * ntpd/refclock_parse.c (parse_clockinfo): For Wharton 400a clock, do not poll, but expect a message every second. * html/parsedata.htm : Entry added for Wharton 400a clock. * html/driver8.htm : Entry fixed for Wharton 400a clock. Sun May 16 02:59:46 1999 Harlan Stenn * configure.in: 4.0.93 Sat May 15 18:53:47 1999 Harlan Stenn * configure.in (ntp_refclock): ONCORE requires PPSAPI, CIOGETEV, or TIOCGPPSEV. Reported by: Reg Clemens Fri May 14 23:58:35 1999 Harlan Stenn * configure.in: 4.0.92h2 * configure.in (ac_cv_make_ntptime): Not under Linux. Yes, it works for some people. We're tired of the complaints from the others. Fri May 14 18:58:59 1999 Rainer Orth * libntp/authreadkeys.c (authreadkeys): Reject autokey keys. Include ntp.h for NTP_MAXKEY definition, ntp_fp.h for types used in ntp.h. Wed May 12 23:02:22 1999 Rainer Orth * libntp/authkeys.c (auth_delkeys): Don't remove autokey keys, leave info on KEY_TRUSTED flag alone. Include ntp.h for NTP_MAXKEY definition. Thu May 13 02:19:02 1999 Harlan Stenn * configure.in: 4.0.92h1 * configure.in: patch for ReliantUNIX From: Andrej Borsenkow * ntpd/refclock_oncore.c: Patches From: Reg Clemens Thu Apr 29 14:01:04 1999 Rainer Orth * html/*.htm: Remove unnecessary  . Cleanup

	sections.

	* configure.in: Properly align configure --help output.
	* html/config.htm: Include this version, removing Netscape  
	cruft.

Wed Apr 28 15:08:55 1999  Rainer Orth  

	* kernel/sys/parsestreams.h: Only include  if
	struct ppsclockev is missing from system headers.

	* util/tickadj.c (getoffsets): Define kernels[] only if used.
	(openfile): Rename fd to avoid shadowing global fd.
	(writevar): Likewise.
	(readvar): Likewise.

	* parseutil/dcfd.c (read_drift): drift_file is const char *.
	(update_drift): Likewise.
	(adjust_clock): Likewise.
	(main): Likewise.

	* ntpd/refclock_parse.c (gps16x_poll): Adapt format to match
	parse->localstate type.

	* ntpd/ntp_refclock.c (refclock_gtlin): Only define gotit label
	if used.

	* include/l_stdlib.h (openlog, syslog): char * args are const.

	* configure.in (*-*-osf4*): Enable declaration of stime().

	* ntpd/refclock_oncore.c (oncore_msg_any): Cast args to long to
	match prototype.
	(oncore_msg_En): Likewise.

	* include/ntp_refclock.h (struct refclockstat): Declare p_lastcode
	as const char *.

	* ntpq/ntpq_ops.c (struct varlist): Define name as const.

	* ntpdc/ntpdc.c (tokenize): Define cp as const char *, remove
	wrong cast instead.

	* ntpd/ntp_util.c (record_clock_stats): Make text arg const.
	* include/ntpd.h (record_clock_stats): Adapt declaration.
	* ntpd/refclock_oncore.c (oncore_start): Removed superfluous casts.
	(oncore_msg_Cf): Likewise.
	(oncore_msg_Fa): Likewise.
	(oncore_msg_Cj): Likewise.
	(oncore_msg_Ea): Likewise.
	(oncore_msg_Bj): Likewise.

	* configure.in (*-*-solaris2.4): Enable declarations of
	gettimeofday(), settimeofday(); they are `protected' by
	__cplusplus in .

Tue Apr 27 21:14:47 1999  Rainer Orth  

	* scripts/summary.pl: Use . as default statsdir.
	(do_loop): Accept new loopstats format with additional sys_error
	and clock_stability fields.
	(do_peer): Accept new peerstats format with additional skew field.

Mon Apr 26 01:50:38 1999  Harlan Stenn  

	* Upgraded automake (1.4a) and autoconf (2.14.1)

	* configure.in (ac_refclock_irig): We no longer need stropts.h.
	* ntpd/refclock_irig.c: Ditto

Mon Apr 26 17:33:33 1999  Rainer Orth  

	* configure.in (*-*-irix6*): Don't pass MIPSpro cc-only flag -n32
	to gcc.

Thu Apr 22 15:06:40 1999  Rainer Orth  

	* ntpd/ntp_config.c (getconfig): IN_CLASSD() expects address in
	host byte order, but struct sockaddr_in.s_addr is in network byte
	order.
	* ntpd/ntp_io.c (io_multicast_del): Likewise.

Sat Apr 24 01:00:53 1999  Harlan Stenn  

	* configure.in: 4.0.92h

	* ntptrace/ntptrace.c: -m maxhost patch
	From: "R. Gary Cutbill" 

	* util/ntptime.c: Patches.
	From: Ulrich Windl 

	* html/accopt.htm, html/assoc.htm, html/authopt.htm,
	html/biblio.htm, html/build.htm, html/clockopt.htm,
	html/confopt.htm, html/copyright.htm, html/debug.htm,
	html/exec.htm, html/extern.htm, html/hints.htm, html/index.htm,
	html/kern.htm, html/miscopt.htm, html/monopt.htm, html/notes.htm,
	html/ntpd.htm, html/ntpdate.htm, html/ntpdc.htm, html/ntpq.htm,
	html/ntptime.htm, html/ntptrace.htm, html/patches.htm,
	html/porting.htm, html/pps.htm, html/rdebug.htm,
	html/refclock.htm, html/release.htm, html/tickadj.htm,
	html/hints/solaris.html: Fixed many typos and problems.
	* acconfig.h (DECL_CFSETISPEED_0, DECL_MRAND48_0, DECL_NLIST_0,
	DECL_SRAND48_0, DECL_STIME_0): New templates.
	* include/l_stdlib.h: Include termios.h to get definition of
	speed_t.
	(cfsetispeed, cfsetospeed, mrand48, nlist, srand48, stime): New
	declarations.
	(openlog): Declare 2- or 3-argument form.
	* configure.in: Enable declarations of functions missing from
	Ultrix V4.3 system headers.
	* ntpd/refclock_oncore.c: Include , Ultrix V4.3
	 needs it for dev_t.
	From: Rainer Orth 

	* ntpdc/ntpdc_ops.c: Reality checks.

	* configure.in: netbsd has stubs for the timer_* stuff and doesn't
	support PPSAPI.  IRIG requires  .
	From: Frederick Bruckman 

	* ntpdc/ntpdc_ops.c: (kerninfo)  Report in seconds regardless of
	kernel precision.  Report kernel flags as text.
	From: Poul-Henning Kamp 

Sun Apr 18 14:26:51 1999  Harlan Stenn  

	* configure.in: 4.0.92g

	* ntpd/ntp_refclock.c (refclock_ioctl): We don't want
	PPS_HARDPPSONASSERT by default.
	* ntpd/refclock_oncore.c: Prefer timepps.h over sys/timepps.h
	From: Poul-Henning Kamp 

Tue Apr 13 17:32:35 1999  Harlan Stenn  

	* configure.in: 4.0.92f

	* ntpd/ntp_refclock.c (refclock_open): VMIN should be 1, not 0
	From: Reg Clemens 

Sun Apr 11 18:26:44 1999  Harlan Stenn  

	* ntpd/refclock_mx4200.c: Patches/improvements
	* ntpd/ntpd.c (set_process_priority): Lint
	From: Marc.Brett@westgeo.com

	* util/ntptime.c: Lint, bit definition cleanup
	From: Ulrich Windl 

Wed Apr  7 03:02:23 1999  Harlan Stenn  

	* ntpd/refclock_oncore.c: Use timepps.h or sys/timepps.h
	* configure.in: Look for either timepps.h or sys/timepps.h
	From: Poul-Henning Kamp 

	* ntpd/ntp_io.c (create_sockets): Don't warn about ENXIO.
	(Several places)
	From: Andrej Borsenkow 

	* libntp/mfp_mul.c (mfp_mul): Lint.
	Marc.Brett@westgeo.com

Sun Apr  4 03:23:53 1999  Harlan Stenn  

	* configure.in: 4.0.92e
	Dave redesigned the clock state machine.

1999-02-28  Frank Kardel   acm.org>

	* parseutil/dcfd.c: added DCF77 module powersetup

	* ntpd/refclock_parse.c (parse_control): using gmprettydate instead of prettydate()
	(mk_utcinfo): new function for formatting GPS derived UTC information
	(gps16x_message): changed to use mk_utcinfo()
	(trimbletsip_message): changed to use mk_utcinfo()
	ignoring position information in unsynchronized mode
	(parse_start): augument linux support for optional ASYNC_LOW_LATENCY

	* ntpd/ntp_control.c (ctl_putclock): cleanup of end of buffer handling

	* libparse/parse.c (timepacket): removed unnecessary code

	* libparse/clk_trimtsip.c (struct trimble): new member t_utcknown
	(cvt_trimtsip): fixed status monitoring, bad receiver states are
 	now recognized

	* libntp/prettydate.c (gmprettydate): new function for format date
 	and time with respect to UTC

	* libntp/gpstolfp.c (GPSWRAP): update GPS rollover to 990 weeks

	* include/trimble.h (CMD_RUTCPARAM): control variable name unification

	* include/ntp_fp.h: added prototype for gmprettydate()

Sat Feb 27 00:03:16 1999  Harlan Stenn  

	* libntp/systime.c: definition
	* ntpd/ntp_proto.c: sco5_oldclock declaration
	* configure.in: SCO5_CLOCK for *-*-sco3.2v5*
	* util/tickadj.c (main): SCO5_OLDCLOCK -> SCO5_CLOCK
	From: Kees Hendrikse 

	* ntpd/ntp_config.c (getconfig): Indentation cleanup
	Deal with 'P' case better
	* ntpd/ntpd.c: Declare set_process_priority()
	* ntpd/refclock_dumbclock.c: Lint cleanup
	From: Marc.Brett@westgeo.com

Wed Feb 24 10:22:51 1999  Harlan Stenn  

	* configure.in: 4.0.92d

	* configure.in: Dave says we can't enable PARSE clocks by default.
	Also, Solaris 2.7 still has its kernel bug - disable kernel FLL
	there.
	Reported by: Dave Mills 

Tue Feb 23 23:37:44 1999  Harlan Stenn  

	* libparse/Makefile.am (parsesolaris.o): Devious hack to deal
	with bug in sys/systm.h .
	Suggested by: Chaim Frenkel 

Tue Feb 23 20:46:31 1999  Frank Kardel   acm.org>

	* ntpd/refclock_parse.c: fixed #endifs
	(stream_receive): fixed formats

Mon Feb 22 00:35:06 1999  Harlan Stenn  

	* configure.in: 4.0.92c

	* ntpd/refclock_chronolog.c: Lint
	* ntpd/refclock_dumbclock.c: Ditto
	* ntpd/refclock_oncore.c: Ditto
	From: Marc.Brett@westgeo.com

	* ntpd/refclock_oncore.c (oncore_msg_any): Call GETTIMEOFDAY, not
	gettimeofday().
	From: david.higgins@mail.ccur.com

	* configure.in (MCAST): Not in i386-sequent-sysv4
	Reported by: Joseph Geter 

	* util/ntptime.c: Linux cleanup.
	From: Reg Clemens 

	* configure.in: Rename SCO5_OLDCLOCK to SCO5_CLOCK
	* acconfig.h: Ditto

	* ntpd/ntp_proto.c: SCO5_CLOCK stuff
	(init_proto): Use the SCO5_CLOCK stuff
	* libntp/systime.c: SCO5_CLOCK stuff
	(get_systime): Use the SCO5_CLOCK stuff
	(adj_systime): Use the SCO5_CLOCK stuff
	From: Kees Hendrikse 

	* ntpd/ntp_config.c: Added -P option and associated baggage.
	(getstartup): Update help text
	(getconfig): Process -P option
	(getconfig): Update help text
	* ntpd/ntpd.c (set_process_priority): Created.
	(service_main): remove process priority stuff - we want to do at
	after we start up the resolver, so call set_process_priority()
	after getconfig().
	From: Kamal A Mostafa 

1999-02-21  Frank Kardel   acm.org>

	* ntpd/ntp_util.c (hourly_stats): removed unused variable

	* libntp/ieee754io.c: renamed index to fieldindex to avoid index() name clash

	* ntpd/refclock_parse.c (parse_start): add initialization for Linux PPSkit

Sun Feb 21 17:53:33 1999  Harlan Stenn  

	* ntpd/ntp_io.c (create_sockets): Skip interfaces that are really
	just aliases.
	From: "Erik R. Leo" 

	* configure.in: 4.0.92b

	* ntpd/ntpd.c (service_main): Check for an error return from
	sched_get_priority_max().

Wed Feb 17 03:48:47 1999  Harlan Stenn  

	* configure.in: 4.0.92a

	* configure.in: configure.in requires autoconf 2.13 or later.
	Reported by Ulrich Windl 

Wed Feb 17 00:12:11 1999  Harlan Stenn  

	* acconfig.h: TERMIOS_NEEDS__SVID3
	* configure.in: Ditto
	* ntpd/refclock_palisade.h: Ditto
	* include/ntp_refclock.h: Ditto
	* ntpd/ntpd.c (service_main): We want sched_get_priority_max().
	From: Kamal A Mostafa 

	* ntpd/ntp_refclock.c (refclock_open): Zero the entire c_cc[] array.
	From: Reg Clemens 

Tue Feb 16 23:37:49 1999  Harlan Stenn  

	* Updated ansi2knr
	Reported by: Marc Brett

Mon Feb 15 02:55:28 1999  Harlan Stenn  

	* configure.in: 4.0.92

	* ntpd/ntp_refclock.c: Added refclock_chronolog and
	refclock_dumbclock.
	From: Robert McMillin 

Sun Feb 14 15:57:53 1999  Harlan Stenn  

	* dropped SCO3 support #defines.
	* changed SCO5_TICKADJ #define to SCO5_OLDCLOCK
	* Added code in libntp/systime.c to accumulate changes until a whole
	  tick can be added or dropped. Adjusted gettimeofday() output
	  to include the contents of the accumulator.
	* cleaned up util/tickadj.c; tickadj -As now does the right thing.
	From: Kees Hendrikse 

	* ntpq/ntpq.c: Rename delay() to auth_delay()
	Reported by: Andrej Borsenkow 

	* ntpd/refclock_palisade.h: Cleanup.
	From: Marc.Brett@westgeo.com

	* ntpd/ntp_refclock.c (refclock_ioctl): Typo.
	From: Reg Clemens 

	* ntpd/ntp_io.c (create_sockets): Only bind a given network once.
	From: Wolfgang Rupprecht 

Sat Jan 30 11:48:37 1999  Harlan Stenn  

	* configure.in: 4.0.91f

Thu Jan 28 22:58:40 1999  Harlan Stenn  

	* ntpd/refclock_parse.c (CLK_REALTYPE): We really want ttl, not hmode.
	* ntpd/ntp_config.c (getconfig): "mode" really should update the
	ttl member, not the hmode member.

	* ntpd/refclock_local.c: More offset cleanup from Dave.

Thu Jan 28 00:15:20 1999  Harlan Stenn  

	* configure.in: 4.0.91e

	* ntpd/refclock_local.c: Bugfix.
	From: Dave Mills

	* ntpd/refclock_palisade.c: Lint/IRIX portability cleanup
	* ntpd/refclock_palisade.h: Re-enable the declaration of float()
	* ntpd/ntp_io.c (create_sockets): Initialize size to 0
	From: Marc.Brett@westgeo.com

	* ntpd/refclock_parse.c (CLK_REALTYPE): Use hmode, not ttl.
	* configure.in (ac_cv_var_no_parenb_ignpar): Not under Linux.
	Reported by: Thomas Quinot 

	* ntpdc/ntpdc.c (my_delay): Renamed, from delay.
	Reported by: Andrej Borsenkow 

Tue Jan 26 00:56:10 1999  Harlan Stenn  

	* configure.in: 4.0.91d

	* ntpq/ntpq.c: Y2K patches
	From: Marc.Brett@westgeo.com

	* html/driver29.htm: New version
	* ntpd/refclock_palisade.c: Ditto
	* ntpd/refclock_palisade.h: Ditto
	From: Sven_Dietrich@Trimble.COM

	* upgrade ansi2knr.c

	* Some stuff that Dave did.

	* configure.in: 4.0.91c

	* ntpd/refclock_oncore.c: Prototype cleanup.  Enum cleanup.
	* ntpd/ntp_proto.c (clock_select): Fix memory leak.
	* configure.in (ac_cv_struct_ppsclockev): Might need sys/time.h to
	check for struct clockppsev.  Return pce->serial, not 0;
	From: Marc.Brett@westgeo.com

	* ntpd/refclock_oncore.c (oncore_msg_En): Clean up.
	From: John.Hay@mikom.csir.co.za

Mon Jan 25 11:50:29 1999  Philippe De Muyter  

	* libparse/parse_conf.c (clockformats): Entry added for
 	clock_wharton_400a.
	* libparse/clk_wharton.c: New file.
	* libparse/Makefile.am (libparse_a_SOURCES): clk_wharton.c added;
	(libparse_kernel_a_SOURCES): kclk_wharton.c added.
	(kclk_wharton.o): New dependency rule.
	* ntpd/refclock_parse.c (parse_clockinfo): Entry added for the
	WHARTON clock (mode 15).
	* acconfig.h (CLOCK_WHARTON_400A): New configuration macro.
	* configure.in (CLOCK_WHARTON_400A): Macro defined like other
	CLOCK_xxx macros.

Sun Jan 24 13:51:30 1999  Harlan Stenn  

	* ntpd/ntp_config.c (do_resolve_internal): Missing #ifdef DEBUG
	From: Sven Dietrich 

	* Makefile.am (SUBDIRS): Lose authstuff
	* configure.in: Ditto

Sat Jan 23 15:28:03 1999  Harlan Stenn  

	* configure.in: 4.0.91b

Sat Jan 23 15:02:25 1999  Harlan Stenn  

	* ntpd/refclock_oncore.c: use HAVE_STRUCT_PPSCLOCKEV
	* acconfig.h: HAVE_STRUCT_PPSCLOCKEV
	* configure.in (ac_cv_struct_ppsclockev): Added test

Thu Jan 21 15:35:25 1999  Harlan Stenn  

	* configure.in: 4.0.91a

	* ntpd/refclock_nmea.c (nmea_receive): Call refclock_process()
	every second (or each time a nmea string is received).
	From: John Hay 

	* ntpd/ntp_refclock.c (refclock_ioctl): Use TIOCPPS if we have it.
	(refclock_ioctl): Use LDISC_CLKPPS, not LDISC_PPS when deciding
	how to set str.
	* ntpd/ntp_loopfilter.c: Lose unused ntp_gettime() stuff.
	* ntpd/ntp_request.c: Ditto.
	* ntpd/refclock_local.c: Ditto.
	* ntpd/refclock_shm.c (shm_poll): Fix the refclock_process() call.
	* ntpd/refclock_oncore.c: patches and cleanup
	* configure.in: ioctl/PPS checks, ONCORE cleanup
	* acconfig.h: ONCORE cleanup
	From: Reg Clemens 

	* configure.in (CFLAGS): cc on Sequent wants -Wc,+abi-socket.
	We also need to figure out why -lsocket isn't being detected;
	-lsocket is needed.
	From: Dana Kaempen 

	* include/ntp_stdlib.h: AIX portability patches, header cleanup.
	* ntptrace/ntptrace.c: Ditto.
	* ntpdate/ntpdate.c: Ditto.
	* ntpd/refclock_true.c: Ditto.
	* ntpd/refclock_mx4200.c: Ditto.
	* ntpd/refclock_jupiter.c: Ditto.
	* libntp/msyslog.c: Ditto.
	From: Marc.Brett@waii.com

Sun Jan 10 15:15:07 1999  Harlan Stenn  

	* configure.in: 4.0.91

Sat Jan  9 00:11:34 1999  Harlan Stenn  

	* include/ntp_stdlib.h: msyslog() is declared differently if we're
	not __STDC__.

	* include/ntp_types.h: It's HAVE_PROTOTYPES, not USE_PROTOTYPES.
	* include/ntp_machine.h: Ditto.

Fri Jan  8 20:47:10 1999  Harlan Stenn  

	* configure.in: Upgrade to autoconf-2.13
	Do the prototypes check much earlier, as it might alter CFLAGS and
 	things which will affect other tests.

	* ntpd/ntp_request.c (do_conf): The problem was with a template
	for "version" on an IRIX C compiler...
	From: Marc.Brett@waii.com

	* libntp/authkeys.c: #include config.h first.
	Reported by: brian.bumpass@funb.com

Thu Jan  7 00:24:35 1999  Harlan Stenn  

	* util/tickadj.c (main): return() instead of exit().
	* ntpd/ntp_request.c (do_conf): Disambiguate ||.
	* ntpd/ntp_proto.c (clock_select): Initialize variables.
	From: Marc.Brett@waii.com

	* scripts/ntpver.in: Use PATH_SH

	* configure.in (PATH_SH): Added.

Tue Jan  5 19:02:51 1999  Harlan Stenn  

	* configure.in: 4.0.90h

	* html/driver30.htm: Updated.
	* html/refclock.htm: Refer to driver30
	* ntpd/refclock_oncore.c: Vastly improve and make less FreeBSD centric,
	From: Poul-Henning Kamp  and
		Reg.Clemens 

	* include/ntp.h: Portability/lint patches
	* libntp/binio.c: Ditto.
	* libntp/caljulian.c: Ditto.
	* libntp/caltontp.c: Ditto.
	* libntp/ieee754io.c: Ditto.
	* libntp/md5c.c: Ditto.
	* libntp/mfp_mul.c: Ditto.
	* libntp/msyslog.c: Ditto.
	* libntp/statestr.c: Ditto.
	* libntp/systime.c: Ditto.
	* libparse/clk_trimtsip.c: Ditto.
	* libparse/data_mbg.c: Ditto.
	* libparse/parse.c: Ditto.
	* ntpd/ntp_control.c: Ditto.
	* ntpd/ntp_filegen.c: Ditto.
	* ntpd/ntp_intres.c: Ditto.
	* ntpd/ntp_io.c: Ditto.
	* ntpd/ntp_peer.c: Ditto.
	* ntpd/ntp_proto.c: Ditto.
	* ntpd/ntp_util.c: Ditto.
	* ntpd/ntpd.c: Ditto.
	* ntpd/refclock_arc.c: Ditto.
	* ntpd/refclock_chu.c: Ditto.
	* ntpd/refclock_datum.c: Ditto.
	* ntpd/refclock_leitch.c: Ditto.
	* ntpd/refclock_parse.c: Ditto.
	* ntpd/refclock_usno.c: Ditto.
	* ntpq/ntpq.c: Ditto.
	* util/tickadj.c: Ditto.
	From: Marc.Brett@waii.com

Mon Jan  4 00:56:55 1999  Harlan Stenn  

	* configure.in: 4.0.90g

	* ntpd/ntp_config.c (getconfig): MODE was setting ttl, not hmode.
	Reported by: Carsten Emde 

Fri Dec  4 01:01:14 1998  Harlan Stenn  

	* configure.in: 4.0.90f

	* ntpd/refclock_mx4200.c: New version
	From: Marc.Brett@waii.com

1998-12-02  Harlan Stenn  

	* ntpd/ntp_config.c (do_resolve_internal): If fork fails, say why.
	Reported by: Jeff_Dennison@admin.tc.faa.gov

	* ntpd/ntpd.c (ntpdmain):  fork() can return a -1.  Someday we'll
	report this condition...

1998-12-02  Harlan Stenn  

	* configure.in: 4.0.90e

	* ntpd/refclock_palisade.c: Reformat code so ansi2knr will work
	* ntpd/refclock_palisade.h: Ditto
	From: Marc.Brett@waii.com

Sun Nov 29 21:00:53 1998  Harlan Stenn  

	* configure.in: 4.0.90d

	* configure.in (CFLAGS): Use "-O2 -g3 -n32" by default for Irix6.2
	and later.
	Reported by: Jack Bryans 

1998-11-29  Harlan Stenn  

	* configure.in: 4.0.90c

	* ntpd/refclock_oncore.c (oncore_msg_En): Convert to nano
	From: John Hay 

	* include/ntp_request.h (RM_VN_MODE): Add version parameter, so
	xntpdc will work across v3 and v4.
	* ntpd/ntp_request.c: Track requested version
	(req_ack): Use requested version in RM_VN_MODE
	(more_pkt): Ditto
	(flush_pkt): Ditto
	(process_private): Get requested version
	* ntpd/ntp_intres.c (request): Use default version
	* ntpdc/ntpdc.c (sendrequest): Ditto
	From: John Hay 

Fri Nov 27 14:27:21 1998  Harlan Stenn  

	* ntpd/refclock_palisade.c: Lint cleanup
	* ntpd/refclock_palisade.h: Ditto.
	From: Marc Brett 

Mon Nov 23 04:45:03 1998  Harlan Stenn  

	* configure.in: 4.0.90b

	* New code and cleanup for the NT stuff
	From: Carl Byington 

Sat Nov 21 21:21:45 1998  Harlan Stenn  

	* configure.in: 4.0.90a

	* libntp/systime.c (step_systime): net_set_tod calls clock_settime.
	* libntp/machines.c (ntp_set_tod): Take a 2nd arg for NT.
	* include/ntp_machine.h: ntp_set_tod() has 2 args always.
	* ports/winnt/bldrel.bat: Typo.
	From: Carl Byington 

	* ntpd/ntp_intres.c (findhostaddr): h_errno is a #define under AIX.
	* configure.in:  clock_settime is a stub in AIX4.
	From: Perry Ross 

	* libntp/Makefile.am (EXTRA_DIST): Lose libntp.mak
	* ntpd/Makefile.am (EXTRA_DIST): Ditto.
	* ntpdate/Makefile.am (EXTRA_DIST): Ditto.
	* ntpdc/Makefile.am (EXTRA_DIST): Ditto.
	* ntpq/Makefile.am (EXTRA_DIST): Ditto.
	From: Greg Schueman 

Sat Nov 21 12:33:16 1998  Harlan Stenn  

	* configure.in: 4.0.90

	Nano changes from Dave Mills.

Thu Nov 19 04:23:46 1998  Harlan Stenn  

	* include/ntp_machine.h: STREAM also needs HAVE_SYS_STREAM_H
	Reported by: Ronald Cole 

Mon Nov 16 19:17:34 1998  Harlan Stenn  

	* configure.in: 4.0.73e14

	* util/ntptime.c (main): Protect STA_NANO

	* ntpd/refclock_oncore.c: General overhaul and simplifications.
	The new manual clarifies a lot of fine points, and the driver has
	been suitably simplified.  Uses Site Survey if possible, otherwise
	does it by hand.  Should also work with non-UT models, as long as
	they talk the Motorola Binary Protocol.  The driver Doesn't (need
	to) know where the author lives anymore.
	From: Poul-Henning Kamp 

	* ntpd/refclock_palisade.h: New version.
	* ntpd/refclock_palisade.c: New version.
	From: Sven Dietrich 

Sat Oct 24 01:19:21 1998  Harlan Stenn  

	* configure.in: 4.0.73e13

	* ntpdc/ntpdc_ops.c (clkbug): Patches
	* ntpd/ntp_refclock.c (refclock_buginfo): Patches
	From: Marc.Brett@waii.com

Sat Oct 10 20:13:14 1998  Harlan Stenn  

	* configure.in: 4.0.73e12

	* ntpd/ntp_util.c (hourly_stats): Added prio_set stuff.

	* ntpd/ntpd.c (ntpdmain): HAVE_SETPGRP_0 typo.
	* parseutil/dcfd.c (detach): Ditto.
	* ntpd/ntp_control.c (ctl_putpeer): Sometimes, peer->dstadr is
	NIL.
	From: Perry Ross 

	* ntpd/ntpd.c:
	Some systems use sys/sched.h, not sched.h (Irix)
	* configure.in (CFLAGS): nextstep needs -posix.
	Reported by: Jack Bryans 

Sat Oct  3 02:32:46 1998  Harlan Stenn  

	* configure.in: 4.0.73e11

	* configure.in (ac_refclock_palisade): Needs termios.

	* libntp/mktime.c: Some systems need sys/types.h

	* configure.in: Added AC_TYPE_SIZE_T and AC_CHECK_TYPE(time_t, long)
	The time_t stuff should only be needed on Older machines, so the
	fact that I'm using a long shouldn't be a problem (hollow laugh).

	* include/l_stdlib.h: Sometimes we need to #include 

	* libntp/Makefile.am (../include/des.h): Typo.

Fri Oct  2 20:52:47 1998  Harlan Stenn  

	* ntpd/ntp_intres.c (request): Accept responses back thru V2.

Thu Oct  1 00:11:16 1998  Harlan Stenn  

	* configure.in: 4.0.73e9

	* ntpd/ntpd.c (catch_danger): Added.
	(ntpdmain): AIX SIGDANGER stuff
	From: Lars-Owe Ivarsson 

	* configure.in:
	* include/ntp_machine.h:
	* include/ntp_string.h:
	* libntp/machines.c:
	* libparse/clk_hopf6021.c:
	* libparse/clk_trimtsip.c:
	* ntpd/refclock_leitch.c:
	* ntpd/refclock_palisade.c:
	* ntpd/refclock_parse.c:
	Here are some patches to suppress warnings from various compilers
	(IRIX 5.3, MipsPro C 7.1 on IRIX 6.4, AIX 4.1) and loaders (IRIX
	5.3, IRIX 6.4).  Shouldn't affect functionality at all.
	From: Marc Brett 
	(I got similar patches for AIX from Lars-Owe Ivarsson
	)

Thu Sep 24 21:33:50 1998  Harlan Stenn  

	* configure.in: '73e8

	* configure.in: AIX4 stubs the POSIX timer_ stuff,
	sched_setscheduler, and mlockall.
	Reported by: Lars-Owe Ivarsson 

	* configure.in: OpenBSD stubs the POSIX timer_ stuff.
	Reported by:  sidney august cammeresi iv 
	(and several other folks whose names I can't find at the moment)

Mon Sep 21 15:35:23 1998  Harlan Stenn  

	* configure.in: '73e7

	* ntpd/refclock_parse.c: Missing declaration
	From: Marc Brett 

	* include/README: Remove old MCAST descriptions

	* include/Makefile.am (noinst_HEADERS): Lose sun-in.h .

Mon Sep 21 14:50:12 1998  Harlan Stenn  

	* ntpdate/ntpdate.c (timer): Properly format the definition.

Sun Sep 20 23:02:50 1998  Harlan Stenn  

	* configure.in: '73e6

	* include/Makefile.am (noinst_HEADERS): Renamed in.h to sun-in.h

Fri Sep 18 01:05:55 1998  Harlan Stenn  

	* configure.in: '73e5

	* ntpd/refclock_palisade.c: SCO patch
	From: Kamal A Mostafa 

	* libparse/clk_trimtsip.c (cvt_trimtsip): Fix rollover bug.
	From: "Michael J. Tubby B.Sc. G8TIC" 

	* libntp/authencrypt.c:
	* libntp/systime.c:
	* ntpd/refclock_acts.c:
	* ntpd/refclock_arbiter.c:
	* ntpd/refclock_arc.c:
	* ntpd/refclock_as2201.c:
	* ntpd/refclock_atom.c:
	* ntpd/refclock_chu.c:
	* ntpd/refclock_conf.c:
	* ntpd/refclock_datum.c:
	* ntpd/refclock_heath.c:
	* ntpd/refclock_hpgps.c:
	* ntpd/refclock_irig.c:
	* ntpd/refclock_leitch.c:
	* ntpd/refclock_nmea.c:
	* ntpd/refclock_palisade.c:
	* ntpd/refclock_parse.c:
	* ntpd/refclock_pst.c:
	* ntpd/refclock_trak.c:
	* ntpd/refclock_true.c:
	* ntpd/refclock_usno.c:
	* ntpd/refclock_wwvb.c:
	Typos, cleanup, and bugfixes
	From: Marc Brett 

	* ntpd/ntp_timer.c (timer): Typo.
	* include/ntp_refclock.h: in refclockstat, clockdesc should be const.
	* ntpd/ntp_io.c (create_sockets): Typo.
	* ntpd/ntp_control.c (free_varlist): Use the appropriate cast when
	calling free().
	(set_var): Use char *td for non-const char data.
	(ctl_getitem): Use char * for non-const data.
	(Many of these reported by Marc Brett)

Sun Sep 13 19:19:09 1998  Harlan Stenn  

	* ntpd/ntpd.c: Added nofork declaration.
	(ntpdmain): Initialize it...
	* ntpd/ntp_config.c: added nofork.
	Updated ntp_options.
	(getstartup): Updated "usage" string.  Deal with -n flag.
	(getconfig): Ditto.
	From: Jeffrey Hutzelman 

	* ntpd/ntp_io.c (open_socket): Use ntoa() to print out the address
	when bind() fails. (in 2 places)
	Reported by: "Markus W. Fehr" 
	Only soft-fail if an interface is unavailable.
	(create_sockets):  Don't SO_REUSEADDR if the interface is unavailable.
	From: "Markus W. Fehr" 

	* configure.in:  If we --disable-all-clocks, then don't enable
	parse clocks by default.
	Reported by: Marion Hakanson 

Sat Aug 22 23:58:14 1998  Frank Kardel   acm.org>

	* ntpd/refclock_parse.c (local_input): fixed IO handling for non-STREAM IO

Sun Aug 16 20:13:32 1998  Frank Kardel   acm.org>

	* libntp/ieee754io.c: debug information only compile for LIBDEBUG case

	* ntpd/refclock_parse.c (gps16x_message): reduced UTC parameter information (dropped A0,A1)
	made uval a local variable (killed one of the last globals)
	(sendetx): added logging of messages when in debug mode
	(trimble_check): added periodic checks to facilitate re-initialization
	(trimbletsip_init): made use of EOL character if in non-kernel operation
	(trimbletsip_message): extended message interpretation
	(getdbl): fixed data conversion

	* libparse/parse_conf.c (clockformats): Trimble TSIP driver now also
	available for kernel operation

	* libparse/info_trimble.c: re-generated

	* libparse/clk_trimtsip.c (cvt_trimtsip): initial kernel capable version (no more floats)
	(clock_trimtsip =): new format name

	* libparse/clk_trimtaip.c (clock_trimtaip =): changed format name

	* include/trimble.h (CMD_RSTATTRACK): renamed mode 6 variable name

	* scripts/monitoring/ntploopwatch: moved emacs mode selector

Mon Aug 10 15:32:48 1998  Harlan Stenn  

	* ntpd/refclock_acts.c: Patch cleanup
	* ntpd/ntp_refclock.c: Patch cleanup
	* ntpd/ntp_timer.c: Patch cleanup
	From: qli@huey.udel.edu

Wed Jul 29 15:23:21 1998  Harlan Stenn  

	* libntp/machines.c: IRIX needs time.h
	Reported by: Judith E Bush 

	* ntpd/ntpd.c (service_main): Better AIX PROCLOCK fix.
	From: Matt Ladendorf  and
	Grover Davidson 

Wed Jul 29 01:36:48 1998  Harlan Stenn  

	* include/ntpd.h (MAXINTERFACES): Moved here...
	* ntpd/ntp_io.c: From here...
	(create_sockets): Only deal with MAXINTERFACES.
	(create_sockets): Only deal with specified interfaces.

	* ntpd/ntp_config.c (CONFIG_LISTEN): Added
	Added ifnum and listenaddrs[]
	(getconfig): Added defn for "addr"
	(getconfig): Initialize ifnum.

	* ntpd/ntpd.c (service_main): call init_io after getconfig
	From: Vebjorn Ljosa 

Wed Jul 29 00:42:28 1998  Harlan Stenn  

	* ntpd/refclock_palisade.c: Use NEED_HPUX9_TIOCM_STUFF

	* acconfig.h (NEED_HPUX9_TIOCM_STUFF):  Added.

	* configure.in (REFCLOCK_PALISADE): Needs termio*.h
	(NEED_HPUX9_TIOCM_STUFF): Added.

	* ntpd/ntp_io.c (create_sockets): Use strchr instead of strstr.

	* libntp/mktime.c: #include 

	* libntp/ieee754io.c: #include 

Wed Jul 29 00:24:22 1998  Harlan Stenn  

	* ntpd/refclock_acts.c (ACTS_MAXPOLL): 14 -> 18.
	Import current_nextdate
	(acts_receive): Update peer->nextdate with current_nextdate
	(acts_poll): Call acts_timeout() (debugging)

	* ntpd/ntp_refclock.c: Export current_nextdate.
	(refclock_transmit): Check peer->valid >= NTP_SHIFT - 2, not >.
	(refclock_transmit): hpoll wiggles, update current_nextdate

	* ntpd/ntp_timer.c: #include "ntp_refclock.h"
	(MODE_MANUAL): Added.
	(timer): MODE_MANUAL stuff

	From: qli@huey.udel.edu

Tue Jul 28 23:23:15 1998  Harlan Stenn  

	* configure.in: Check for inet_ntoa in -lbind .

	* ntpd/ntpd.c: #undef PROCLOCK for AIX.

Mon Jul 20 01:06:24 1998  Harlan Stenn  

	* configure.in (AC_TYPE_SIZE_T): Added.

Sat Jul 11 09:38:30 1998  Harlan Stenn  

	* configure.in: 4.0.73e

	* ports/winnt/: Replaced with new code (no SHM or PALISADE)
	From: Greg Schueman 

Fri Jul 10 12:12:59 1998  Harlan Stenn  

	* configure.in: 4.0.73d

	* include/ntp_machine.h (HAVE_SRANDOM): VxWorks patches
	(HAVE_RANDOM): Ditto.
	(CALL): Ditto.
	From: Casey Crellin 

	* ntpd/refclock_parse.c (local_input): Typo.
	Reported by: Tony Li 

Wed Jul  8 01:49:01 1998  Harlan Stenn  

	* configure.in: 4.0.73c

	* PARSE patches from Frank Kardel

	* libntp/machines.c (ntp_set_tod): Get it right.

Sun Jul  5 22:15:34 1998  Harlan Stenn  

	* configure.in: 4.0.73a

	* kernel/sys/timex.h (MOD_CANSCALE): Add rest of patch to handle
	scaling.
	From: Poul-Henning Kamp 

Wed Jun 10 21:16:01 1998  Harlan Stenn  

	* configure.in: 4.0.73

	* ntpd/ntp_loopfilter.c (local_clock): MOD_CANSCALE patches, and
	be careful with the integration if we're nearly perfect.
	From: Poul-Henning Kamp 

	* util/tickadj.c (main): Typo fix...
	From: Marion Hakanson 

	* ntpd/ntp_io.c (create_sockets): Attempt to ignore alias
	interfaces.
	From: Kenneth Maupin 

	* ntpd/ntp_refclock.c: PPS fixes
	* ntpd/refclock_msfees.c (msfees_start): Portability fixes and
	PPS/STREAM enhancements
	From: John Hay 

	* ntpd/ntp_refclock.c (refclock_gtlin): Patch...
	From: Jonathan Stone 

Sun Jun 28 18:43:30 1998  Frank Kardel   acm.org>

	* libntp/buftvtots.c (buftvtots): using WORD_BIGENDIAN instead of XNTP_BIG_ENDIAN

	* libparse/clk_trimtsip.c (getflt): fixed ENDIAN issue
	(getdbl): fixed ENDIAN issue
	(getint): use get_msb_short()
	(cvt_trimtsip): use gpstolfp() for conversion

	* libntp/Makefile.am (libntp_a_SOURCES): added gpstolfp.c source

	* libntp/binio.c: added {get,put}_msb_{short,long}() functions

	* include/ntp_fp.h: added gpstolfp() prototype

	* include/binio.h: added binio MSB prototypes

Sat Jun 13 13:48:17 1998  Frank Kardel   acm.org>

	* parseutil/testdcf.c: signed/unsigned
	SYSV clock name clash fixed

	* parseutil/dcfd.c: signed/unsigned
	SYSV clock name clash fixed
	year wrapping at 1998
	ctype macros take ints as args

	* ntptrace/ntptrace.c (decodeipaddr): ctype macros take ints as args

	* ntpq/ntpq_ops.c (doprintpeers): signed/unsigned

	* ntpq/ntpq.c: ctype macros take ints as args
	signed/unsigned

	* ntpdc/ntpdc.c: signed/unsigned

	* ntpd/refclock_usno.c: signed/unsigned

	* ntpd/refclock_true.c (true_send): signed/unsigned, name clashes

	* ntpd/refclock_parse.c: signed/unsigned, name clashes

	* ntpd/refclock_nmea.c (nmea_receive): ctype macros take ints as args

	* ntpd/refclock_heath.c (heath_receive): prototypes (signed/unsigned issues)

	* ntpd/refclock_arc.c: prototypes (signed/unsigned issues)

	* ntpd/refclock_acts.c: prototypes (signed/unsigned issues)

	* ntpd/ntpd.c: prototypes (signed/unsigned issues)

	* ntpd/ntp_util.c (getauthkeys): prototypes (signed/unsigned issues)
	fix SYSV clock name clash

	* ntpd/ntp_request.c: prototypes (signed/unsigned issues)
	fix SYSV clock name clash

	* ntpd/ntp_io.c (input_handler): variable naming, signed/unsigned

	* ntpd/ntp_intres.c (readconf): signed/unsigned issues

	* ntpd/ntp_control.c: prototypes (signed/unsigned issues)
	fix SYSV clock name clash

	* ntpd/ntp_config.c: fix SYSV clock name clash
        ctype macros take ints as args

	* libparse/parsestreams.c: dirt (debug) removed

	* libparse/parsesolaris.c: more prototypes
	fix name clashes
	allow for ansi2knr

	* libparse/parse.c: bcopy/memcpy cleanup
	fix SYSV clock name clash

	* libparse/clk_trimtsip.c (cvt_trimtsip): fix SYSV clock name clash

	* libparse/clk_trimtaip.c (cvt_trimtaip): fix SYSV clock name clash

	* libparse/clk_schmid.c (cvt_schmid): fix SYSV clock name clash

	* libparse/clk_rcc8000.c (cvt_rcc8000): fix SYSV clock name clash

	* libparse/clk_rawdcf.c (cvt_rawdcf): fix SYSV clock name clash

	* libparse/clk_hopf6021.c (cvt_hopf6021): fix SYSV clock name clash

	* libparse/clk_dcf7000.c (cvt_dcf7000): fix SYSV clock name clash

	* libparse/clk_computime.c: fix SYSV clock name clash

	* libntp/octtoint.c (octtoint): ctype macros take ints as args

	* libntp/mstolfp.c (mstolfp): ctype macros take ints as args

	* libntp/hextolfp.c (hextolfp): ctype macros take ints as args

	* libntp/hextoint.c (hextoint): ctype macros take ints as args

	* libntp/decodenetnum.c (decodenetnum): ctype macros take ints as args

	* libntp/atouint.c (atouint): ctype macros take ints as args

	* libntp/atolfp.c (atolfp): ctype macros take ints as args

	* libntp/atoint.c (atoint): ctype macros take ints as args

	* kernel/sys/parsestreams.h:  STREAM macro gone in favor of HAVE_SYS_STREAM_H

	* include/parse.h: STREAM macro gone in favor of HAVE_SYS_STREAM_H

Fri Jun 12 11:08:53 1998  Frank Kardel   acm.org>

	* ntpd/ntp_timer.c: prototype fixes (ansi2knr/knr compiler)

	* ntpd/ntp_proto.c (make_keylist): type cast for e(!!!)malloc()

	* libparse/Makefile.am: adjust for ansi2knr

	* libntp/ieee754io.c: ansi2knr compatibility

	* include/ntp_refclock.h: added pps_sample() extern declaration
	added refclock_process_offset() extern declaration

	* include/ntp.h: fixed function * prototypes

	* ntpd/refclock_parse.c (bind): added input routine
	(local_input): added input routine

	* ntpd/ntp_io.c (input_handler): direct input processing for
	refclocks to save input recv buffers

	* include/ntp_refclock.h: added int io_input(struct recvbuf *)
	pointer to allow direct processing of read refclock data in
	order to save many bug recv buffers on single character input
	(problem with "fast" machines)

	* parse_conf.c:  conditional compile macros fixed

	* parse.c:  conditional compile macros fixed
	printf prototype

	* clk_trimtaip.c:  conditional compile macros fixed
	printf prototype

	* clk_schmid.c:  conditional compile macros fixed
	printf prototype

	* clk_rcc8000.c:  conditional compile macros fixed
	printf prototype

	* clk_hopf6021.c:  conditional compile macros fixed
	printf prototype

	* clk_dcf7000.c: conditional compile macros fixed
	printf prototype

	* clk_computime.c: conditional compile macros fixed
	printf prototype

Sat Jun  6 07:41:54 1998  Frank Kardel   acm.org>

	* ntpd/refclock_palisade.c: fixed termio.h / termios.h inclusion

	* include/ntp_refclock.h: made refclockproc/clockdesc const

	* ntpd/ntp_control.c (ctl_putpeer): avoided ambigous 'else' (gcc)

	* ntpd/refclock_parse.c (parse_start): added BURST mode initialisation

	* scripts/stats/summary.sh (CLOCK): allow for Y2K log files

	* libparse/clk_rawdcf.c: simplified condidional compile expression

Wed May 27 08:10:43 1998  Frank Kardel   acm.org>

	* include/Makefile.am (noinst_HEADERS): added new header files
	mbg_gps166.h binio.h ascii.h ieee754io.h

	* ntpdc/ntpdc.c (sendrequest): fixed info_auth_keyid setting it
	got accidentally trashed every other round

Mon May 25 22:55:07 1998  Frank Kardel   acm.org>

	* configure.in: PARSE clocks are enabled by default whenever
	possible (termio.h or termios.h present)
	removed RAWDCF_SETDTR feature

	* acconfig.h: removed RAWDCF_SETDTR option (now implicit by
	compilation and run time configuration)

	* ntpd/refclock_parse.c (rawdcf_init): offer a RAWDCF clock (type 14)
	that attempts to set the DTR modem line for receiver power

	* libparse/clk_meinberg.c (cvt_meinberg): support current standard
	Meinberg data formats

Sun May 24 09:43:19 1998  Frank Kardel   acm.org>

	* libparse/clk_rawdcf.c (pps_rawdcf): trigger pps on zero going
	edge - that is simpler wiring (Rx->DCD).

	* parseutil/testdcf.c (wday): const keyword

	* parseutil/dcfd.c (cvt_rawdcf): sign issues and calling interfaces

	* ntpq/ntpq.c (MAXVARLEN): adjusted internal buffer length for
	variable values

	* ntpd/refclock_parse.c: adjust to new io handling (fixed formats
	only)
	(mkreadable): don't include >"< in readable ASCII output (-> ntpq
	parsing)
	output debug messages to stdout instead of msyslog()
	fixed version information string

	* ntpd/refclock_atom.c (pps_sample): new auxiliary pps interface

	* libparse/parsestreams.c (parserput): get event status consistent
	with direct calls
	(zs_xsisr): simulate CARRIER status to avoid unnecessary M_xHANGUP
	events

	* libparse/parsesolaris.c (parserput): get event status consistent
	with direct calls
	(zs_xsisr): simulate CARRIER status to avoid unnecessary M_xHANGUP
	events

	* libparse/parse.c: removed old input cruft
	(parse_restart): new generic input help function
	(parse_addchar): ditto
	(parse_end): ditto
	(pps_one): new generic pps help function
	(pps_zero): ditto

	* libparse/clk_trimtsip.c (clock_trimtsip =): new input handling

	* libparse/clk_trimtaip.c (clock_trimtaip =): new input handling
	(inp_trimtaip): new input handler

	* libparse/clk_schmid.c (clock_schmid =): new input handling
	(inp_schmid): new input handler

	* libparse/clk_rcc8000.c (clock_rcc8000 =): new input handling
	(inp_rcc8000): new input handler

	* libparse/clk_rawdcf.c (clock_rawdcf =): new input handling
	(snt_rawdcf): adjusted to new input handling
	(inp_rawdcf): new input handler

	* libparse/clk_meinberg.c (clock_meinberg): new input handling
	(gps_input): new input handler
	(mbg_input): new input handler

	* libparse/clk_hopf6021.c (clock_hopf6021 =): new input handling
	(inp_hopf6021): new input handler

	* libparse/clk_dcf7000.c (clock_dcf7000 =): new input handling
	(inp_dcf7000): new input handler

	* libparse/clk_computime.c (clock_computime =): new input handling
	(inp_computime): new input handler

	* libparse/Makefile.am: link kernel module with libntp.a

	* include/parse.h (struct parse): removed old data structure cruft
	(new input model) new PARSE_INP* macros for input handling
	removed old SYNC_* macros from old input model
	(struct clockformat): removed old parse functions in favor of the
	new input model
	updated prototypes

	* include/ntp_refclock.h: prototype for refclock_atom pps_sample()
	interface

	* acconfig.h: added PPS_SAMPLE define
	* configure.in (LIBPARSE): added PPS_SAMPLE configuration
	

	* libntp/systime.c (adj_systime): debug output (> level 6) for
	adjtime results

	* libntp/mfp_mul.c (mfp_mul): controlled debug output

	* libntp/ieee754io.c (get_byte): controlled debug output
	(fetch_ieee754): ditto
	(put_ieee754): ditto

Tue May  5 20:09:51 1998  Harlan Stenn  

	* configure.in: document DES is not usually present.

Wed Apr 29 22:00:22 1998  Harlan Stenn  

	* configure.in: 4.0.72h

	* authstuff/Makefile.am (check-local-rsn): check-local doesn't
	work with RSAREF...
	Reported by: "Auteria Wally Winzer Jr." 

	* libntp/machines.c: the settime() choices were ordered badly.
	Reported by: Michael Joosten 

Sat Apr 25 00:35:53 1998  Harlan Stenn  

	* configure.in (ac_cv_var_no_parenb_ignpar): Undo the kernel PLL
	block I just installed - Dave wants to control this via
	KERNEL_FLL_BUG.

Fri Apr 24 20:35:57 1998  Harlan Stenn  

	* libntp/Makefile.am (libntp_a_DEPENDENCIES): Set per libntp_a_LIBADD

	* configure.in: Do a better job of blocking kernel PLL under
	solaris2.6.

Fri Apr 24 00:41:12 1998  Harlan Stenn  

	* configure.in: 4.0.72f
	(ac_cv_struct_nlist_n_un): Don't look for ntp_adjtime or
	ntp_gettime under solaris2.6.

	* ntpd/ntp_proto.c (process_packet): Give verbose error messages

	* include/global.h (PROTOTYPES): Drive via HAVE_PROTOTYPES.

Wed Apr 22 16:55:55 1998  Harlan Stenn  

	* configure.in (ac_cv_var_use_des): Added. 4.0.72e.
	* libntp/Makefile.am (libntp_a_LIBADD): Added DESOBJS

Tue Apr 21 02:08:06 1998  Harlan Stenn  

	* ntpd/refclock_arc.c (arc_receive): Typo...
	From: Sam Steingold 

Fri Apr 10 03:05:35 1998  Harlan Stenn  

	* configure.in (ac_refclock_chu): AUDIO_CHU support.  Disabled by
	default, and currently only supported on SunOS and Solaris.
	* acconfig.h: AUDIO_CHU

Wed Apr  8 19:53:53 1998  Harlan Stenn  

	* libntp/Makefile.am (EXTRA_DIST): Added mktime.c

	* configure.in:  AC_REPLACE_FUNCS(mktime).
	(--enable-dst-minutes=60): Added, for (missing) mktime().

	* ntpd/refclock_heath.c (heath_receive): Use mktime() instead of
	the old hack.

Tue Apr  7 21:15:14 1998  Harlan Stenn  

	* configure.in (LIBOBJS): Hack it before AC_OUTPUT to deal with
	ANSI2KNR-filtering rules.
	From: Jim Meyering 

Mon Apr  6 01:40:45 1998  Harlan Stenn  

	* libntp/strerror.c: ANSIfy strerror's definition.

Thu Mar 12 20:24:45 1998  Harlan Stenn  

	* libntp/statestr.c: Only #include  if HAVE_CONFIG_H is
	#define'd.
	From: Sven Dietrich 

Wed Mar 11 00:27:32 1998  Harlan Stenn  

	* configure.in: Cygwin needs to check for the advapi32 library.
	NT doesn't support a root user, so don't bother with getuid().
	Also, don't bother with umask().

	* ntpd/ntp_io.c: cygwin32 patches
	* ntpd/ntp_proto.c: Ditto.
	* ntpd/ntpd.c: Ditto.
	* ntpd/ntp_timer.c: Ditto.
	* ntpdate/ntpdate.c: Ditto.
	* libntp/machines.c: Ditto.
	* libntp/systime.c: Ditto.
	* include/ntp_machine.h: Ditto.
	* include/ntp_unixtime.h: Ditto.
	From: Sven Dietrich 

Tue Mar 10 22:26:14 1998  Harlan Stenn  

	* configure.in (ac_cv_make_tickadj): Added.
	Now that tickadj is the only(?) utility that cares about tick and
	tickadj, we don't need to have NOKMEM and no PRESET_* be fatal.

Sat Mar  7 02:57:17 1998  Harlan Stenn  

	* ntpd/ntp_loopfilter.c (local_clock): Patch STA_FLL check
	From: Poul-Henning Kamp 

	* various: Renamed ACTS to CLOCK_ACTS, ARBITER to CLOCK_ARBITER,
	ARCRON_MSF to CLOCK_ARCRON_MSF, AS2201 to CLOCK_AS2201, BANC to
	CLOCK_BANC, DATUM to CLOCK_DATUM, GPSVME to CLOCK_GPSVME, HEATH to
	CLOCK_HEATH, HPGPS to CLOCK_HPGPS, IRIG to CLOCK_IRIG, JUPITER to
	CLOCK_JUPITER, LEITCH to CLOCK_LEITCH, MSFEES to CLOCK_MSFEES,
	MX4200 to CLOCK_MX4200, NMEA to CLOCK_NMEA, PALISADE to
	CLOCK_PALISADE, PARSE to CLOCK_PARSE, PPS720 to CLOCK_PPS720, PST
	to CLOCK_PST, PTBACTS to CLOCK_PTBACTS, SHM_CLOCK to CLOCK_SHM,
	ONCORE to CLOCK_ONCORE, TPRO to CLOCK_TPRO, TRAK to CLOCK_TRAK,
	TRUETIME to CLOCK_TRUETIME, USNO to CLOCK_USNO, WWVB to CLOCK_WWVB

	* Makefile.am (ETAGS_ARGS): Added acconfig.h

	* various: Renamed LOCAL_CLOCK to CLOCK_LOCAL.

	* configure.in: First cut at  *-pc-cygwin32 support
	Requested by: Sven Dietrich 

	* configure.in: gdt-surveying code is gone.  Sigh.
	Reported by: Poul-Henning Kamp 

Wed Mar  4 21:41:06 1998  Harlan Stenn  

	* many places: Renamed ATOM to CLOCK_ATOM

Tue Mar  3 03:18:13 1998  Harlan Stenn  

	* ntpd/ntp_timer.c (timer): Only call refclock_transmit if
	REFCLOCK is #define'd.
	Reported by a bunch of folks.

Mon Mar  2 03:46:07 1998  Harlan Stenn  

	* configure.in (ntp_refclock): Use CLOCK_CHU, which no longer
	needs any special headers.
	* ntpd/refclock_chu.c: Call it CLOCK_CHU
	(chu_receive): Define it correctly.

	* include/winnt/sys/time.h (gettimeofday): Prototypes are OK.
	(settimeofday): Prototypes are OK.
	From: JJEVNISEK@qgraph.com

	* ntpq/ntpq_ops.c: varlist name and value aren't const.
	* ntpdc/ntpdc_ops.c (fudge): The flags are u_val, not val.
	* ntpdc/ntpdc.c: const cleanup, exit cleanup.
	* ntpd/refclock_wwvb.c (wwvb_receive): Move the definition of tz
	somewhere more normal.
	* ntpd/ntp_request.c (do_trustkey): kp gets u_long data, not
	u_int32 (but Harlan thinks this patch may be wrong).
	* ntpd/ntp_refclock.c (refclock_process): clocktime needs
	offset.l_ui, not offset.l_i .
	* ntpd/ntp_control.c (set_var): t isn't const.
	* libntp/a_md5encrypt.c (session_key): Cast 2nd arg to MD5auth_setkey.
	* include/ntpd.h: ctl_var's text field isn't const.
	* include/ntp_refclock.h: clockdesc isn't const.
	From: Marc Brett 

	* ntpd/ntp_loopfilter.c (local_clock): Limit ntv.constant to
	MAXTC, and log error returns from ntp_adjtime.
	From: Juha Sarlin 

Mon Mar  2 03:05:23 1998  Harlan Stenn  

	* configure.in (ac_cv_var_kernel_fll_bug): KERNEL_FLL_BUG
	* acconfig.h: KERNEL_FLL_BUG: added.
	* ntpd/ntp_loopfilter.c (local_clock): Only avoid STA_FLL if
	KERNEL_FLL_BUG is #define'd (Solaris2.6)

Sat Feb 21 00:45:10 1998  Harlan Stenn  

	* automake-1.2d.patches: Added ansi2knr.o rules.

	* ntpd/refclock_tpro.c: P() stuff

Fri Feb 20 20:10:20 1998  Harlan Stenn  

	* configure.in: Improve the ${CC} -pipe test (cygwin-32's gcc -pipe
	silently does nothing).
	Reported by: Sven Dietrich 

Wed Feb 18 00:51:08 1998  Harlan Stenn  

* configure.in: 4.0.72 released.

* configure.in:AC_REPLACE_FUNCS(strerror), check for poll.h, and deal
  with the --enable-JUPITER stuff.
* libntp/Makefile.am (libntp_a_LIBADD): Added (for strerror support).
* libntp/clocktypes.c: Added REFCLK_GPS_JUPITER.
* ntpdate/ntpdate.c: poll() support
* ntpd/Makefile.am: Add refclock_jupiter.c
* ntpd/refclock_conf.c: Added refclock_jupiter
* ntpd/refclock_mx4200.c (mx4200_pps): Bugfixes.
* include/ntp.h (REFCLK_GPS_JUPITER): Added, and bumped REFCLK_MAX.
  From: Craig Leres 

Mon Feb 16 21:02:42 1998  Harlan Stenn  

	* ntpd/ntp_proto.c: P()

Mon Feb 16 12:43:11 1998  Harlan Stenn  

* include/ntp_types.h: Added P() prototyping hack back in.
* include/parse.h: Ditto.
* include/ntpd.h:  Ditto.
* include/ntp_unixtime.h:  Ditto.
* include/ntp_stdlib.h: Ditto.
* include/ntp_select.h: Ditto.
* include/ntp_refclock.h: Ditto.
* include/ntp_fp.h: Ditto.
* include/md5.h: Ditto.
* include/ntp_filegen.h: Ditto.
* include/ntp_calendar.h: Ditto.
* include/l_stdlib.h: Ditto.

	* configure.in (ACTS): Sometimes, TIOCMBIS is in sys/ioctl.h
	  Reported by Kenneth Jaldehag 
	* configure.in (HEATH): Ditto.
	* configure.in (PTBACTS): Ditto.
	* configure.in (USNO): Ditto.

Sat Feb 14 00:02:14 1998  Harlan Stenn  

	* ntpd/refclock_irig.c (irig_rf): Rename sincos[] to sin_cos[].

Fri Feb 13 22:22:08 1998  Harlan Stenn  

	* include/ntp.h (RANDPOLL): Use random or mrand48.
	* ntpd/ntp_config.c (do_resolve_internal): Ditto.
	* ntpd/ntp_peer.c (unpeer): Ditto.
	* ntpd/ntp_proto.c (make_keylist): Ditto.

	* ntpd/ntpd.c (xntpdmain): Use srandom or srand48.

	* configure.in: Look for {s,}random() and [ms]rand48().

Wed Feb 11 22:50:24 1998  Harlan Stenn  

	* ntpd/ntp_restrict.c (hack_restrict): Renamed restrict()
	* include/ntpd.h: Ditto
	* ntpd/ntp_request.c (do_restrict): Ditto
	* ntpd/ntp_config.c (getconfig):
	* ntpd/ntp_io.c (create_sockets): Ditto.

1998-01-23  Harlan Stenn  

	* ntpd/refclock_irig.c: Allow either  or
	 .  From Dave Mills.

	* configure.in: Under SunOS, it's sun/audioio.h .

1998-01-22  Harlan Stenn  

	* html/driver6.html: Updated header file info
	* html/irig.html: Ditto.
	* configure.in: sys/bsd_audioirig.h replaced with sys/audioio.h
	for new irig driver that Dave installed.

1998-01-08  Harlan Stenn  

	* Many places: Lose the P(()) prototype stuff

	* util/tickadj.c (writevar): Make offset an off_t
	(readvar): Ditto
	(getoffsets): Make offsets off_t

	* adjtimed/adjtimed.c (GetClockRate): Fix lseek arg 2.
	(SetClockRate): Ditto

	* Many things in many places from many people.

	* configure.in: Added AC_TYPE_OFF_T

1997-11-26  Harlan Stenn  

	* ntpd/refclock_palisade.c: ANSIfied.

Wed Sep  3 23:51:44 1997  Harlan Stenn  

	* configure.in (AM_C_PROTOTYPES): Added.

	* Makefile.am (AUTOMAKE_OPTIONS): Added ansi2knr.

ntpsec-1.1.0+dfsg1/devel/tour.txt0000644000175000017500000006106713252364117016470 0ustar  rlaagerrlaager= A Tour of the NTP Internals =

This document is intended to be helpful to people trying to understand
and modify the NTP code.  It aims to explain how the pieces fit
together. It also explains some oddities that are more understandable
if you know more about the history of this (rather old) codebase.

If you learn a piece of the code well enough to do so, please add
to this document.  Having tricks and traps that impeded understanding
explained here is especially valuable.

However, try not to replicate comments here; it's better to point
at where they live in the code.  This document is intended to convey
a higher-level view than individual comments.

== Key Types ==

=== l_fp, s_fp, u_fp ===

C doesn't have any native fixed-point types, only float types.  In
order to do certain time calculations without loss of precision, NTP
home-brews three fixed-point types of its own.  Of these l_fp is the
most common, with 32 bits of precision in both integer and fractional
parts. Gory details are in include/ntp_fp.h.

One point not covered there is that an l_fp is actually a symmetrical
truncation of the full NTP date (for which no data type exists) about
the fixed point location.  The full NTP date has 64 bits for both the
integer and fractional part, which get truncated to the right 32bit
for the fractional and the left 32bit for the fractional part to
become an l_fp.  For most calculations with l_fp it's easier to
envision it as a 64bit integer that represents time with a unit of
2^-32s, slightly short of 233 ps.

When used to represent dates internally an l_fp must be interpreted in
relation to the NTP era (the left 32 bits of the integer part of the
NTP date that got cut off).  The NTP prime epoch (or epoch 0) is then
an *unsigned* number of seconds since 1900-01-01T00:00:00Z and
unsigned decimal fractional seconds.  Just to complicate matters,
however, some uses of l_fp are time offsets with a signed seconds
part.  Under the assumption that only adjacent epochs get compared,
the offset calculations work correctly without taking the era number
into account.

Most time offsets are much smaller than the NTP epoch, so a 32 bit
representation is used for these.  Actually, there were two of these:
s_fp for signed offsets and u_fp for strictly non-negative ones.  The
signed version has already been removed in NTPsec.

The comments in libntp/ntp_calendar.c are pretty illuminating about
calendar representations.  A high-level point they don't make is
that ignoring time cycles in l_fp is exactly how NTP gets around the
Y2K38 problem. As long as the average clock skew between machines
is much less than the length of a calendar cycle (which it generally
will be, by a factor of at least five million) we can map all incoming
timestamps from whatever cycle into the nearest time in modular
arithmetic relative to the cycle length.

=== time64_t ===

NTP was written well before the 64-bit word size became common. While
compilers on 32-bit machines sometimes have had "long long" as an
integral 64-bit type, this was not guaranteed before C99.

Thus in order to do calendar arithmetic, NTP used to carry a "vint64"
(variant 64-bit int) type that was actually a union with several
different interpretations. This has been replaced with a scalar
time64_t which is used the same way but implemented as a uint64_t.

This still has some utility because NTP still runs on 32-bit machines
with a 32-bit time_t.

=== refid ===

A refid is not a type of its own, but a convention that overloads
different kinds of information in a 32-bit field that occurs in a
couple of different places internally.

In a clock sample (a time-sync packet with stratum number 1), the
refid is interpreted as 4 ASCII characters of clock identification.
In a time-synchronization packet with stratum 2 or higher the refid
identifies the originating time server; it may be an IPv4 or a hash of
an IPv6 address.  (If and when the protocol is fully redesigned for
IPv6 the field length will go to 128 bits and it will become a full
IPv6 address.)

Internally the refid is used for loop detection. (Which is why hashing
IPv6 addresses is risky - hash collisions happen.) It's also part of
ntpq's display in the status line for a reference clock, if you have
one locally attached.

The driver structure for reference clocks has a refid field that is
(by default) copied into samples issued from that clock. It is
not necessarily unique to a driver type; notably, all GPS driver
types ship the refid "GPS". It is possible to fudge this ID to
something more informative in the ntpd configuration command
for the driver.

The conflation of ID-for-humans with loop-detection cookie is not quite
the design error it looks like, as refclocks aren't implicated in
loop detection.

=== struct timespec vs. struct timeval ===

These aren't local types themselves - both are standardized in
ANSI/POSIX.  They're both second/subsecond pairs intended to represent
time without loss of precision due to float operations.  The
difference is that a timespec represents nanoseconds, while a timeval
represents only microseconds.

Historically, struct timeval is associated with the minicomputer-based
Berkeley Unixes of the 1980s, designed when processor clocks were
several orders of magnitude slower than they became after the turn of
the millennium.  The struct timespec is newer and associated with
ANSI/POSIX standardization.

The NTP code was written well before calls like clock_gettime(2) that
use it were standardized, but as part of of its general cleanup NTPsec
has been updated to do all its internal computations at nanosecond
precision or better.  

Thus, when you see a struct timeval in the NTPsec code, it's due to
a precision limit imposed by an external interface.  One such is in
the code for using the old BSD adjtime(2) call; another is in getting
packet timestamps from the IP layer.  Our practice is to convert from
or to nanosecond precision as close to these callsites as possible;
this doesn't pull additional accuracy out of thin air, but it does 
avoid loss-of-precision bugs due to mixing these structures.

=== struct peer ===

In ntpd, there are peer control blocks - one per upstream synchronization
source - that have an IP address in them.  That's what the main
control logic works with.

The peer buffer holds the last 8 samples from the upstream source.
The normal logic uses the one with the lowest round trip time.  That's
a hack to minimize errors from queuing delays out on the big bad
internet.  Refclock data always has a round trip time of 0 and the
code that finds the lowest RTT picks the most recent slot when the
RTTs are equal.

== config file parser ==

There is a minor quirk: numbers come in as integers, type T_Integer.
There is no type T_Unsigned.  Range checking may not work right.

== ntpd control flow ==

In normal operation, after initialization, ntpd simply loops forever
waiting for a UDP packet to arrive on some set of open interfaces, or
a clock sample to arrive from a locally-attached reference clock.
Incoming packets and clock samples are fed to a protocol state
machine, which may generate UDP sends to peers.  This main loop is
captured by a function in ntpd/ntpd.c tellingly named 'mainloop()'.

This main loop is interrupted once per second by a timer tick that
sets an alarm flag visible to the mainloop() logic.  When execution
gets back around to where that flag is checked, a timer() function may
fire.  This is used to adjust the system clock in time and frequency,
implement the kiss-o'-death function and the association polling
function.

There may be one asynchronous thread.  It does DNS lookups of server
and pool hostnames.  This is intended to avoid adding startup delay
and jitter to the time synchronization logic due to address lookups
of unpredictable length.

Input handling used to be a lot more complex.  Due to inability to get
arrival timestamps from the host's UDP layer, the code used to do
asynchronous I/O with packet I/O indicated by signal, with packets
(and their arrival timestamps) being stashed in a ring of buffers that
was consumed by the protocol main loop.  Some of this code hasn't been
cleaned up yet.

This looked partly like a performance hack, but if so it was an
ineffective one. Because there is necessarily a synchronous bottleneck
at protocol handling, packets arriving faster than the main loop could
cycle would pile up in the ring buffer and latecomers would be
dropped.

(Also, it turns out not to be important at post-Y2K machine speeds to
get those arrival timestamps from the UDP layer ASAP, rather than
looking at packet read time in userspace. The cost of the latter,
naive approach is additional jitter dominated by process-scheduling
time.  This used to be significant relative to users' accuracy
expectations for NTP, but scheduler timeslices have since decreased
by orders of magnitude and squashed the issue. We know this from some
tests setup having run for six months with packet-timestamp fetching
accidentally disabled...  But they weren't horribly busy systems.)

The new organization stops pretending; it simply spins on a select
across all interfaces.  If inbound traffic is more than the daemon can
handle, packets will pile up in the UDP layer and be dropped at that
level. The main difference is that dropped packets are less likely to
be visible in the statistics the server can gather. (In order to show,
they'd have to make it out of the system IP layer to userland at a
higher rate than ntpd can process; this is very unlikely.)

There was internal evidence in the NTP Classic build machinery that
asynchronous I/O on Unix machines probably hadn't actually worked for
quite a while before NTPsec removed it.

== System call interface and the PLL ==

All of ntpd's clock management is done through four system calls:
clock_gettime(2), clock_settime(2), and either ntp_adjtime(2) or (in
exceptional cases) the older BSD adjtime(2) call.  For ntp_adjtime(),
ntpd actually uses a thin wrapper that hides the difference between
systems with nanosecond-precision and those with only microsecond
precision; internally, ntpd does all its calculations with nanosecond
precision.

The clock_gettime(2) and clock_settime(2) calls are standardized in
POSIX; ntp_adjtime(2) is not, exhibiting some variability in
behavior across platforms (in particular as to whether it supports
nanosecond or microsecond precision).

Where adjtimex(2) exists (notably under Linux), both ntp_adjtime()
and adjtime() are implemented as library wrappers around it.  The
need to implement adjtime() is why the Linux version of struct timex
has a (non-portable) 'time' member;

There is some confusion abroad about this interface because it has
left a trail of abandoned experiments behind it.

Older BSD systems read the clock using gettimeofday(2) 
(in POSIX but deprecated) and set it using settimeofday(2),
which was never standardized. Neither of these calls are still
used in NTPsec, though the equally ancient BSD adjtime(2) call
is, on systems without kernel PLL support.

Also, glibc (and possibly other C libraries) implement two other
related calls, ntp_gettime(3) and ntp_gettimex(3). These are not used
by the NTP suite itself (except that the ntptime test program attempts
to exercise ntp_gettime(3)), but rather are intended for time-using
applications that also want an estimate of clock error and the
leap-second offset.  Neither has been standardized by POSIX, and they
have not achieved wide use in applications.

Both ntp_gettime(3) and ntp_gettimex(3) can be implemented as wrappers
around ntp_adjtime(2)/adjtimex(2).  Thus, on a Linux system, the
library ntp_gettime(3) call could conceivably go through two levels 
of indirection, being implemented in terms of ntp_adjtime(2) which
is in turn implemented by adjtimex(2).

Unhelpfully, the non-POSIX calls in the above assortment are very
poorly documented.

The roles of clock_gettime(2) and clock_settime(2) are simple.
They're used for reading and setting ("stepping", in NTP jargon) the
system clock.  Stepping is avoided whenever possible because it
introduces discontinuities that may confuse applications.  Stepping is
usually done only at ntpd startup (which is typically at boot time)
and only when the skew between system and NTP time is relatively
large.

The sync algorithm prefers slewing to stepping.  Slewing speeds up or
slows down the clock by a very small amount that will, after a
relatively short time, sync the clock to NTP time.  The advantage of
this method is that it doesn't introduce discontinuities that
applications might notice. The slewing variations in clock speed are so
small that they're generally invisible even to soft-realtime
applications.

The call ntp_adjtime(2) is for clock slewing; NTPsec never calls
adjtimex(2) directly, but it may be used to implement
ntp_adjtime(2). ntp_adjtime(2)/adjtimex(2) uses a kernel interface to
do its work, using a control technique called a PLL/FLL (phase-locked
loop/frequency-locked loop) to do it.

The older BSD adjtime(2) can be used for slewing as well, but doesn't
assume a kernel-level PLL is available.  It is used only when
ntp_adjtime() calls generate a SIGSYS because the system call has not
been implemented.  Without the PLL calls, convergence to good time is
observably a lot slower and tracking will accordingly be less
reliable; support for systems that lack them (notably OpenBSD and
older Mac OS X versions) has been dropped fom NTPsec.

Deep-in-the weeds details about the kernel PLL from Hal Murray follow.
If you can follow these you may be qualified to maintain this code...

Deep inside the kernel, there is code that updates the time by reading the
cycle counter, subtracting off the previous cycle count and multiplying by
the time/cycle.  The actual implementation is complicated mostly to maintain
accuracy.  You need ballpark of 9 digits of accuracy on the time/cycle and
that has to get carried through the calculations.

On PCs, Linux measures the time/cycle at boot time by comparing with another
clock with a known frequency.  If you are building for a specific hardware
platform, you could compile it in as a constant.
You see things like this in syslog:

-----------------------------------------------------------
tsc: Refined TSC clocksource calibration: 1993.548 MHz
-----------------------------------------------------------

You can grep for "MHz" to find these.

(Side note.  1993 MHz is probably 2000 MHz rounded down slightly by
the clock fuzzing to smear the EMI over a broader band to comply with
FCC rules.  It rounds down to make sure the CPU isn't overclocked.)

There is an API call to adjust the time/cycle.  That adjustment is ntpd's
drift.  That covers manufacturing errors and temperature changes and such.
The manufacturing error part is typically under 50 PPM.  I have a few systems
off by over 100.  The temperature part varies by ballpark of 1 PPM / C.

There is another error source which is errors in the calibration code and/or
time keeping code.  If your timekeeping code rounds down occasionally, you
can correct for that by tweaking the time/cycle.

There is another API that says "slew the clock by X seconds".  That is
implemented by tweaking the time/cycle slightly, waiting until the correct
adjustment has happened, then restoring the correct time/cycle.  The "slight"
is 500 PPM.  It takes a long time to make major corrections.

That slewing has nothing (directly) to do with a PLL.  It could be
implemented in user code with reduced accuracy.

There is a PLL kernel option to track a PPS.  It's not compiled into most
Linux kernels.  (It doesn't work with tickless.)  There is an API to turn it
on.  Then ntpd basically sits off to the side and watches.

RFC 1589 covers the above timekeeping and slewing and kernel PLL.

RFC 2783 covers the API for reading a time stamp the kernel grabs when a PPS
happens.

== Refclock management ==

There is an illuminating comment in ntpd/ntp_refclock.c that begins
"Reference clock support is provided here by maintaining the fiction
that the clock is actually a peer."  The code mostly hides the
difference between clock samples and sync updates from peers.

Internally, each refclock has a FIFO holding the last ~64 samples.  For
things like NMEA, each time the driver gets a valid sample it adds it to the
FIFO.  For the Atom/PPS driver there is a hook that gets called/polled each
second.  If it finds good data, it adds a sample to the FIFO.  The FIFO is
actually a ring buffer.  On overflow, old samples are dropped.

At the polling interval, the driver is "polled".  (Note the possible
confusion on "poll".)  That is parallel with sending a packet to the
device, if required - some have to be polled.  The driver can call
back and say "process everything in the FIFO", or do something or set
a flag and call back later.

The process everything step sorts the contents of the FIFO, then discards
outliers, roughly 1/3 of the samples, and then figures out the average and
injects that into the peer buffer for the refclock.

== Asynchronous DNS lookup ==

The DNS code runs in a separate thread in order to avoid stalling
the main loop while it waits for a DNS lookup to return. And DNS
lookups can take a *long* time.  Hal Murray notes that
he thinks he's seen 40 seconds on a failing case.

The old async-DNS support seemed somewhat overengineered.  Whoever
built it was thinking in terms of a general async-worker facility
and implemented things that this use of it probably doesn't
need - notably an input-buffer pool.  (It also had an obscure bug.)

The DNS lookups during initialization - of hostnames specified on the
command line or server lines in ntp.conf - could be done synchronously.

But that would delay startup and there are two cases we know of where
ntpd has to do a DNS lookup during normal operation.

One is the try again when DNS for the normal server case doesn't work during
initialization.  It will try again occasionally until it gets an answer.
(which might be negative)

The main one is the pool code trying for more servers.  There are two
cases for that.  One is that the initial result didn't return as many
addresses as desired.  The other is when pool servers die and need to
be replaced with working ones.

There are several possible extensions in this area.  The main one would
be to verify that a server you are using is still in the pool.  (There
isn't a way to do that yet - the pool doesn't have any DNS support
for it.)  The other would be to try replacing the poorest server
rather than only replacing dead servers.

== The build recipe ==

The build recipe is, essentially, a big Python program using a set of
specialized procedures caled 'waf'.  To learn about waf itself see
the https://waf.io/book/[Waf Book]; this section is about the
organization and particular quirks of NTPsec's build.

If you are used to autoconf, you will find the waf recipe
odd at first.  We replaced autoconf because with waf the
build recipe is literally orders of magnitude less complex,
faster, and more maintainable.

The top level wscript calls wscript files in various subdirectories
using the ctx.recurse() function. These subfiles are all relatively
simple, consisting mainly of calls to the waf function ctx().  Each
such call declares a build target to be composed (often using the
compiler) from various prerequisites.

The top-level wscript does not itself declare a lot of targets (the
exceptions are a handful of installable shellscripts and man pages).
It is mainly concerned with setting up various assumptions for the
configure and build phases.

If you are used to working with Makefiles, you may find the absence
of object files and binaries from the source directory after a build
surprising.  Look under the build/ directory.

Most of the complexity in this build is in the configure phase, when
the build engine is probing the environment.  The product of this
phase is the file build/config.h, which the C programs include to
get symbols that describe the environment's capabilities and
quirks.

The configuration logic consists of a largish number of Python files
in the wafhelpers/ directory. The entire collection is structured as
a loadable Python module.  Here are a few useful generalizations:

* The main sequence of the configuration logic, and most of the simpler
  checks, lives in configure.py.

* Some generic but bulky helper functions live in probes.py.

* The check_*.py files isolate checks for individual capabilities;
  you can generally figure out which ones by looking at the name.

* If you need to add a build or configure option, look in options.py.
  You will usually be able to model your implementation on code that
  is already there.

== The Python tools ==

Project policy is that (a) anything that does not have to be written
in C shouldn't be, and (b) our preferred non-C language is Python.
Most of the auxiliary tools have already been moved.  This section
describes how they fit together.

== The pylib/ntp library

The most important structural thing about the python tools is the
layering of the three most important ones - ntpq, ntpdig, and ntpmon. 
These are front ends to a back-end library of Python service routines that
installs as 'ntp' and lives in the source tree at pylib/. 

=== ntpq and ntpmon ===

ntpq and ntpmon are front ends to back-end class called ControlSession
that lives in ntp.packet.

ntpq proper is mostly one big instance of a class derived from
Python's cmd.Cmd. That command interpreter, the Ntpq class, manages an
instance of a back-end ControlSession class.  ControlSession speaks
the Mode 6 control protocol recognized by the daemon.

The cmd.Cmd methods are mostly pretty thin
wrappers around calls to eight methods of ControlSession
corresponding to each of the implemented Mode 6 request types.

Within ControlSession, those methods turn into wrappers around
doquery() calls.  doquery() encapsulates "send a request, get a
response" and includes all the response fragment reassembly, retry,
and time-out/panic logic.

ntpmon is simpler.  It's a basic TUI modeled on Unix top(1), mutt(1)
and similar programs.  It just calls some of the ControlSession
methods repeatedly, formatting what it gets back as a live display.

The code for making the actual displays in htpq and ntpmon mostly
doesn't live in the front end.  It's in ntp.util, well separated from
both the command interpreter and the protocol back end so it can be
re-used.

=== ntpdig ===

ntpdig also uses the pylib library, but doesn't speak Mode 6.
Instead, it builds and interprets time-synchronization packets 
using some of the same machinery.

=== MRU reporting ===

The mrulist() method in ControlSession is more complex than the rest of the
back-end code put together except do_query() itself.  It is the one part
that was genuinely difficult to write, as opposed to merely having high
friction because the C it was translated from was so grotty.

The way that part of the protocol works is a loop that does two
layers of segment reassembly.  The lower layer is the vanilla UDP
fragment reassembly encapsulated in do_query() and shared with the
other request types.

In order to avoid blocking for long periods of time, and in order to
be cleanly interruptible by control-C, the upper layer does a sequence
of requests for MRU spans, which are multi-frag sequences of
ASCIIizations of MRU records, oldest to newest.  The spans include
sequence metadata intended to allow you to stitch them together on the
fly in O(n) time.

Note that the data for a slot will be returned more than once if a
request arrives after the data was returned but before the collection
has finished.

The code collects all the data, maybe sorts it, then prints it out.

There is also a direct mode that prints the individual slots
as they come in.  This avoids needing lots of memory if you want
to get the MRU data from a system that keeps lots of history.

A further interesting complication is use of a nonce to foil DDoSes by
source-address spoofing.  The mrulist() code begins by requesting a
nonce from ntpd, which it then replays between span requets to
convince ntpd that the address it's firehosing all that MRU data at is
the same one that asked for the nonce. To foil replay attacks, the
nonce is timed out; you have to re-request another every 16 seconds
(the code does this automatically).

The Python code does not replicate the old C logic for stitching
together the MRU spans; that looked pretty fragile in the presence of
span dropouts (we don't know that those can ever happen, but we don't
know that they can't, either).  Instead, it just brute-forces the problem
- accumulates all the MRU spans until either the protocol marker for
the end of the last one or ^C interrupting the span-read loop, and
then quicksorts the list before handing it up to the front end for
display.

There's a keyboard-interrupt catcher *inside* the mrulist() method.
That feels like a layering violation, but nobody has come up with a
better way to partition things.  Under the given constraints there may
not be one.

// end
ntpsec-1.1.0+dfsg1/devel/ifdex-ignores0000644000175000017500000001255513252364117017422 0ustar  rlaagerrlaager# This is an exclusion file for the ifdex tool, which is used to generate
# and filter reports about #if guards in order to locate configuration symbols
# and plain old cruft.
#
# The ifdex tool is available at http://www.catb.org/~esr/ifdex/
#
# Should be used together with waf's config.h output, which ifdex can
# interpret to exclude most of its config symbols. Thus, from the toplevel
# directory:
#
# ifdex -X build/config.h -X devel/ifdex-ignores .
#
# No output from this command means all symbols are accounted for

# These are potential feature configuration symbols
ENABLE_ONCORE_SHMEM	# Should OnCore report via shmem?
ENABLE_KILL_ON_TRAP	# Die on seccomp trap
MSSNTP_PATH		# Set a default path for the MSSNTP feature.
NTP_VAR			# Path to NTP statistics directory

# Formerly used on some embedded systems. Keep around in case of re-port
NO_MAIN_ALLOWED		# Used to be set by VxWorks.

# System capabilities that waf should detect but does not.
# Presently they're either forced on in waf or never set at all
OS_MISSES_SPECIFIC_ROUTE_UPDATES

# Refclock-specific configuration (NTP Classic never set these)
ENABLE_PPS720		# Enable use of PCL720 with TrueTime refclock
NEOCLOCK4X_FIRMWARE	# Only used in the Neoclock4x driver
TRIM_POLLRATE		# In the parse driver

# Other
NEED_SRANDOMDEV		# Random-number initialization - never set by NTP Classic
NTPD_PRIO		# Priority to set the daemon to

# Fine-grained debugging and tracing, not presently settable from waf
DEBUG
USEBACKTRACE	        # Use backtrace code, set by --enable-debug-gdb
DEBUG_PARSELIB		# Enable debugging in the parse library.
NTP_DEBUG_LISTS		# Debug list handling
DEBUG_PPS720		# Only in refclock_true.c

# AIX and Solaris had at one time a bug in the way they declared
# PTHREAD_ONCE_INIT.  This enables a workaround.  It may no longer be
# needed, so let's wait on report rather than pre-complexifying the
# waf logic.
ISC_PLATFORM_BRACEPTHREADONCEINIT

# This is from a Python compatibility hack
PY_MAJOR_VERSION

# This is from a library header
MOD_TIMETICK
NTP_API

# From the Linux PPS header
PPS_API_VERS

# OpenSSL
OPENSSL_VERSION_NUMBER
LIBRESSL_VERSION_NUMBER

# Things WAF sets that don't get #undefs if they're not set
ENABLE_EARLY_DROPROOT
ENABLE_LEAP_SMEAR
ENABLE_DEBUG_TIMING
ENABLE_LOCKCLOCK
ENABLE_MDNS_REGISTRATION
ENABLE_MSSNTP
ENABLE_LEAP_TESTING
HAVE_LINUX_CAPABILITY
HAVE_SECCOMP_H
HAVE_SOLARIS_PRIVS
WORDS_BIGENDIAN

# Constants in time and calendar logic
DAY_GPS_STARTS
DAY_NTP_STARTS
DSTMINUTES

# Conditionals for unit tests
TEST_LIBNTP
TEST_NTPD
TEST_NTPDIG
TEST_LIBPARSE

# Purely internal symbols
CONFIG_H
AUTOREVISION_H
BITSPERCHAR		# Only used in the Arcron refclock
BACKTRACE_MAXFRAME	# Internal to the ISC backtrace code
BACKTRACE_LIBC
BACKTRACE_GCC
BACKTRACE_DISABLED
CMOCK			# Internal to the Unity test framework
CONFIG_FILE		# Only used in ntp_config.h
DEVOLATILE		# Only used in work_thread.c
ENABLE_NLS		# Autogenerated into C by Bison.
FD_CHUNK		# Used for fd allocation in socket.c
FD_PREFERRED_SOCKBOUNDARY
GUARD_.*		# Idempotency guards
INC_MONLIST		# Exposed from ntp_monitor.c for hysterical raisins
INIT_MONLIST		# Exposed from ntp_monitor.c for hysterical raisins
ISC_CHECK_ALL
ISC_CHECK_ENSURE
ISC_CHECK_INSIST
ISC_CHECK_INVARIANT
ISC_CHECK_NONE
ISC_CHECK_REQUIRE
ISC_FIX_TV_USEC
ISC_MUTEX_PROFTABLESIZE
JSMN_PARENT_LINKS
JSMN_STRICT
LIB_BUFLENGTH		# Only referenced by #if as a sanity check
LOG_NTP
MIN			# Minimum macro
MRU_MAXDEPTH_DEF	# Exposed from ntp_monitor.c for hysterical raisins
NEOCLOCK4X_FIRMWARE_VERSION_A	# Only used in the Neoclock4x driver
NMEA_WRITE_SUPPORT	# Only used in the NMEA refclock
NTIME_MSG
OTIME_MSG
FLASH_CODES_UNUSED
NOTUSED
ONCORE_VERBOSE_.*	# Debugging symbols internal to the OnCore refclock
PI			# Math constant used by the sim code
RUN_TEST		# Internal to Unity
SETSOCKOPT_ARG_CAST	# Can probably go away when the Windows port is cleaned up.
THREAD_MINSTACKSIZE
UINTPTR_MAX		# Unity
UINTPTR_MAX0		# Unity
INTPTR_MAX0		# Unity
UNITY_.*		# Unity test framework internal macros
UNUSED_ARG		# Used to quiet compiler warnings
UNUSED_LOCAL		# Used to quiet compiler warnings
UPDATE_GRACE		# ntp_io.c only
USE_COMPILETIME_PIVOT	# Use build date in disambiguating time.

USE_SCM_BINTIME		# to grab timestamp for recv packet
USE_SCM_TIMESTAMP	# "
USE_SCM_TIMESTAMPNS	# "

USE_IPV6_MULTICAST_SUPPORT
USE_LIFC_FAMILY
USE_LIFC_FLAGS
USE_RANDOMIZE_RESPONSES
USE_PCM_STYLE_SOUND
USE_ROUTING_SOCKET
YY_YY_NTP_PARSER_TAB_H_INCLUDED
calloc			# In Unity
close			# In Windows port config
free			# In Unity and ntpd/ntp_parser.tab.c
malloc			# In Unity and ntpd/ntp_parser.tab.c
in_addr6		# In Windows port config
lifr_addrlen		# ISC interface-iterator code
mac			# Avoid name conflict.
ntohl			# In Windows port config
realloc			# Internal to Unity
sem_timedwait		# In worker-thread code
short			# In ntpd/ntp_parser.tab.c
size_t			# In ntpd/ntp_parser.tab.c
timercmp		# Only used in include/parse.h
isinf
isnan
yydebug
_ALLOCA_H
__UNUSED__
_STRING_H		# In ntpd/ntp_parser.tab.c 
__BUILTIN_VA_ARG_INCR	# In ntpd/ntp_parser.tab.c 
__SIZE_TYPE__		# In ntpd/ntp_parser.tab.c 
EREALLOC_.*
INTPTR_MAX		# Unity
__TMS470__		# Unity
HAVE_STRUCT_TIMEX_MODES
HAVE_STRUCT_TIMEX_TIME_TICK
HAVE_STRUCT_NTPTIMEVAL_TAI
HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC

# seccomp-related symbols that may not be available on all systems
__NR_mmap
__NR_openat
__NR_prlimit64

# Exposed by some versions of glibc
NI_MAXSERV

# Inaccessible port guards
SYS_CYGWIN32

# end
ntpsec-1.1.0+dfsg1/devel/release.nix0000644000175000017500000000660313252364117017071 0ustar  rlaagerrlaager# NTPsec derivation for Nix (https://nixos.org). To hack NTPsec on
# NixOS, use `nix-shell -A build.x86_64-linux devel/release.nix`.
{
  ntpsecSrc ? { outPath = ../.; revCount = 1234; shortRev = "abcdef"; },
  officialRelease ? false
}:
let
  pkgs = import  {};
  lib = pkgs.lib;
  derivationOptions = rec {
    version = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
    versionSuffix =
      lib.optionalString (!officialRelease)
      "pre${builtins.toString ntpsecSrc.revCount}-${ntpsecSrc.shortRev}"
    ;
    enableParallelBuilding = true;
    doCheck = false; #XXX: set this to 'true' once we have a test suite
    configureFlags = "--enable-ipv6";
  };
in with derivationOptions; rec {
  tarball = pkgs.releaseTools.sourceTarball (derivationOptions // rec {
    name = "ntpsec-tarball";
    src = ntpsecSrc;
    buildPhases = [ "unpackPhase" "patchPhase" "distPhase" ];

    buildInputs = with pkgs; [ python27 ];

    distPhase = ''
      runHook preDist

      if [ -n "''${versionSuffix}" ]; then
        distFlags="--build-version-tag=$versionSuffix $distFlags"
      fi

      echo "dist flags: $distFlags ''${distFlagsArray[@]}"
      python waf dist $distFlags "''${distFlagsArray[@]}"
      
      if [ "$dontCopyDist" != 1 ]; then       
        mkdir -p "$out/tarballs"
        cp -pvd ''${tarballs:-*.tar.gz} $out/tarballs
      fi
      
      runHook postDist
    '';
  });

  build = lib.genAttrs [ "x86_64-linux" "i686-linux" "x86_64-darwin" ] (system:
    let pkgs = (import  { inherit system; }); in
    with pkgs; with derivationOptions;
    releaseTools.nixBuild (derivationOptions // rec {
      name = "ntpsec";
      src = tarball;

      buildInputs = with pkgs; [ python27 bison openssl asciidoc ];

      configurePhase = ''
        runHook preConfigure

        if [ -z "$dontAddPrefix" ]; then
          configureFlags="''${prefixKey:---prefix=}$prefix $configureFlags"
        fi

        if [ -n "$versionSuffix" ]; then
          configureFlags="--build-version-tag=$versionSuffix $configureFlags"
        fi

        echo "configure flags: $configureFlags ''${configureFlagsArray[@]}"
        python waf configure $configureFlags "''${configureFlagsArray[@]}"

        runHook postConfigure
      '';

      buildPhase = ''
        runHook preBuild

        echo "build flags: $makeFlags ''${makeFlagsArray[@]} $buildFlags ''${buildFlagsArray[@]}"
        python waf build \
          ''${enableParallelBuilding:+-j$NIX_BUILD_CORES} \
          $makeFlags "''${makeFlagsArray[@]}" \
          $buildFlags "''${buildFlagsArray[@]}"

        runHook postBuild
      '';

      checkPhase = ''
        if [ -n "$doCheck" ]; then
          runHook preCheck

          echo "check flags: $makeFlags ''${makeFlagsArray[@]} ''${checkFlags:+-v} ''${checkFlagsArray[@]}"
          python waf check \
            ''${enableParallelBuilding:+-j$NIX_BUILD_CORES} \
            $makeFlags "''${makeFlagsArray[@]}" \
            ''${checkFlags:+-v} "''${checkFlagsArray[@]}"

          runHook postCheck
        fi
      '';

      installPhase = ''
        runHook preInstall

        mkdir -p "$prefix"

        echo "install flags: $makeFlags ''${makeFlagsArray[@]} ''${installFlags:+-v} ''${installFlagsArray[@]}"
        python waf install \
          $makeFlags "''${makeFlagsArray[@]}" \
          ''${installFlags:+-v} "''${installFlagsArray[@]}"

        runHook postInstall
      '';
    })
  );
}
ntpsec-1.1.0+dfsg1/devel/trace/0000775000175000017500000000000013252650651016025 5ustar  rlaagerrlaagerntpsec-1.1.0+dfsg1/devel/trace/ntpsec-ntp-worker.ini0000644000175000017500000000245113252364117022127 0ustar  rlaagerrlaager;
; RTEMS Trace Linker NTPsec Trace Configuration
;
; Copyright 2015 Chris Johns 
;

;--------------------------------------------------------------------------
[ntpsec-ntp-worker]
generator = trace-log-generator
trace = close_all_except, close_all_beyond,
trace = available_blocking_child_slot, queue_blocking_request
trace = queue_blocking_response, process_blocking_resp
trace = blocking_child_common, worker_idle_timer_fired
enable = available_blocking_child_slot, queue_blocking_request
trigger = available_blocking_child_slot, queue_blocking_request

;--------------------------------------------------------------------------
[ntpsec-ntp-worker-funcs]
headers = ntpsec-ntp-worker-headers
signatures = ntpsec-ntp-worker-signatures

[ntpsec-ntp-worker-headers]
header = '#include "config.h"'
header = '#include "ntp_worker.h"'

[ntpsec-ntp-worker-signatures]
close_all_except = void, int
close_all_beyond = void, int
available_blocking_child_slot = unsigned int, void
queue_blocking_request = int, blocking_work_req, void*, size_t, blocking_work_callback, void*
queue_blocking_response = int, blocking_child*, blocking_pipe_header*, size_t, const blocking_pipe_header*
process_blocking_resp = void, blocking_child*
blocking_child_common = int, blocking_child*
worker_idle_timer_fired = void, void
ntpsec-1.1.0+dfsg1/devel/trace/ntpsec-ntp-proto.ini0000644000175000017500000000306713252364117021765 0ustar  rlaagerrlaager;
; RTEMS Trace Linker NTPsec Trace Configuration
;
; Copyright 2015 Chris Johns 
;

;--------------------------------------------------------------------------
[ntpsec-ntp-proto]
trace = transmit
trace = receive
trace = process_packet
trace = poll_update
trace = peer_clear
trace = clock_filter
trace = clock_select
trace = pool_name_resolved
;trace = key_expire
;trace = peer_unfit
;trace = measure_precision
;trace = measure_tick_fuzz
trace = set_sys_tick_precision
trace = init_proto
trace = proto_config
trace = proto_clr_stats
enable = transmit
trigger = transmit

;--------------------------------------------------------------------------
[ntpsec-ntp-proto-funcs]
headers = ntpsec-ntp-proto-headers
signatures = ntpsec-ntp-proto-signatures

[ntpsec-ntp-proto-headers]
header = '#include "config.h"'
header = '#include "ntpd.h"'

[ntpsec-ntp-proto-signatures]
transmit = void, struct peer*
receive = void, struct recvbuf*
process_packet = void, struct peer*, struct pkt*, unsigned int
poll_update = void, struct peer*, uint8_t
peer_clear = void, struct peer*, const char*
clock_filter = void, struct peer*, double, double, double
clock_select = void, void
pool_name_resolved = void, int, int, void*, const char*, const char*, const struct addrinfo*, const struct addrinfo*
key_expire = void, struct peer*
peer_unfit = bool, struct peer*
measure_precision = void, const bool
measure_tick_fuzz = double, void
set_sys_tick_precision = void, double
init_proto = void, const bool
proto_config = void, int, unsigned long, double, sockaddr_u*
proto_clr_stats = void, void
ntpsec-1.1.0+dfsg1/devel/trace/ntpsec-work-thread.ini0000644000175000017500000000216013252364117022243 0ustar  rlaagerrlaager;
; RTEMS Trace Linker NTPsec Trace Configuration
;
; Copyright 2015 Chris Johns 
;

;--------------------------------------------------------------------------
[ntpsec-work-thread]
trace = exit_worker
trace = worker_sleep
trace = interrupt_worker_sleep
trace = send_blocking_req_internal
trace = receive_blocking_req_internal
trace = send_blocking_resp_internal
trace = receive_blocking_resp_internal

;--------------------------------------------------------------------------
[ntpsec-work-thread-funcs]
headers = ntpsec-work-thread-headers
signatures = ntpsec-work-thread-signatures

[ntpsec-work-thread-headers]
header = '#include "config.h"'
;header = '#include "ntp_worker.h"'

[ntpsec-work-thread-signatures]
exit_worker = void, int
worker_sleep = int, blocking_child*, time_t
interrupt_worker_sleep = void, void
send_blocking_req_internal = int, blocking_child*, blocking_pipe_header*, void*
receive_blocking_req_internal = blocking_pipe_header*, blocking_child*
send_blocking_resp_internal = int, blocking_child*, blocking_pipe_header*
receive_blocking_resp_internal = blocking_pipe_header*, blocking_child*
ntpsec-1.1.0+dfsg1/devel/trace/ntpsec-trace-log.ini0000644000175000017500000001147213252364117021677 0ustar  rlaagerrlaager;
; RTEMS Trace Linker Trace Log
;
; Copyright 2015 Chris Johns 
;

;
; A trace log generator logs records to a common log file.
;
[trace-log-generator]
headers = trace-log-generator-headers
code-blocks = trace-log-tracers
lock-model = trace
lock-acquire = " pthread_mutex_lock(&__ntpsec_tlg_lock);"
lock-release = " pthread_mutex_unlock(&__ntpsec_tlg_lock);"
entry-trace = "__ntpsec_tlg_log_entry(@FUNC_NAME@, (void*) &@FUNC_LABEL@);"
arg-trace = "__ntpsec_tlg_log_arg(@ARG_NUM@, @ARG_TYPE@, @ARG_SIZE@, (void*) &@ARG_LABEL@);"
exit-trace = "__ntpsec_tlg_log_exit(@FUNC_NAME@, (void*) &@FUNC_LABEL@);"
ret-trace = "__ntpsec_tlg_log_ret(@RET_TYPE@, @RET_SIZE@, (void*) &@RET_LABEL@);"

[trace-log-generator-headers]
header = "#include "
header = "#include "
header = "#include "
header = "#include "

[trace-log-tracers]
code = <<tv_sec = t1->tv_sec - t2->tv_sec;
  if (t1->tv_nsec < t2->tv_nsec)
  {
    diff->tv_nsec = t1->tv_nsec + (1000000000000 - t2->tv_nsec);
    diff->tv_sec--;
  }
  else
  {
    diff->tv_nsec = t1->tv_nsec - t2->tv_nsec;
  }
}

static void __ntpsec_tlg_log_timestamp(const char* label)
{
  struct timespec now;
  struct timespec delta;
  struct timespec elasped;
  unsigned int    days;
  unsigned int    hours;
  unsigned int    minutes;
  unsigned int    seconds;
  clock_gettime(CLOCK_REALTIME, &now);
  if (__ntpsec_tlg_start.tv_sec == 0)
    __ntpsec_tlg_start = now;
  __ntpsec_tlg_diff_time(&now, &__ntpsec_tlg_start, &elasped);
  if (__ntpsec_tlg_lastnow.tv_sec == 0)
  {
    delta.tv_sec = 0;
    delta.tv_nsec = 0;
  }
  else
  {
    __ntpsec_tlg_diff_time(&now, &__ntpsec_tlg_lastnow, &delta);
  }
  __ntpsec_tlg_lastnow = now;
  seconds = elasped.tv_sec % 60;
  minutes = (elasped.tv_sec / 60) % 60;
  hours = (elasped.tv_sec / (60 * 60)) % 24;
  days = (elasped.tv_sec / (60 * 60)) / 24;
  fprintf(__ntpsec_tlg_log, "%s %3ud %02u:%02u:%02u.%09li (%3li.%09li)",
          label, days, hours, minutes, seconds, elasped.tv_nsec,
	  delta.tv_sec, delta.tv_nsec);
}

static inline void __ntpsec_tlg_executing_id(void)
{
   fprintf(__ntpsec_tlg_log, " [%08x]", pthread_self());
}

static void __ntpsec_tlg_log_entry(const char* func_name,
                                   void*       func_addr)
{
  __ntpsec_tlg_open_log();
  __ntpsec_tlg_log_timestamp(">");
  __ntpsec_tlg_executing_id();
  fprintf(__ntpsec_tlg_log, " %s (%p)\n", func_name, func_addr);
}

static void __ntpsec_tlg_log_arg(int         arg_num,
                                 const char* arg_type,
                                 int         arg_size,
				 void*       arg)
{
  const unsigned char* p = arg;
  int                  i;
  fprintf(__ntpsec_tlg_log, " %2d] %s(%d) = ", arg_num, arg_type, arg_size);
  for (i = 0; i < arg_size;  ++i)
    fprintf(__ntpsec_tlg_log, "%02x", (unsigned int) p[arg_size - 1 - i]);
  fprintf(__ntpsec_tlg_log, "\n");
}

static void __ntpsec_tlg_log_exit(const char* func_name,
                                  void*       func_addr)
{
  __ntpsec_tlg_open_log();
  __ntpsec_tlg_log_timestamp("<");
  __ntpsec_tlg_executing_id();
  fprintf(__ntpsec_tlg_log, " %s (%p)\n", func_name, func_addr);
}

static void __ntpsec_tlg_log_ret(const char* ret_type,
                                 int         ret_size,
                                 void*       ret)
{
  const unsigned char* p = ret;
  int                  i;
  fprintf(__ntpsec_tlg_log, " rt] %s(%d) = ", ret_type, ret_size);
  for (i = 0; i < ret_size; ++i)
    fprintf(__ntpsec_tlg_log, "%02x", (unsigned int) p[ret_size - 1 - i]);
  fprintf(__ntpsec_tlg_log, "\n");
}
CODE
ntpsec-1.1.0+dfsg1/devel/trace/ntpsec-trace.ini0000644000175000017500000000213213252364117021111 0ustar  rlaagerrlaager;
; RTEMS Trace Linker NTPsec Trace Configuration
;
; Copyright 2015 Chris Johns 
;

;--------------------------------------------------------------------------
[tracer]
name = NTPsec tracer
;
; The configuration
;
options = ntpsec-options
traces = ntpsec-ntp-proto, ntpsec-ntp-worker, ntpsec-work-thread
enables = ntpsec-ntp-proto, ntpsec-ntp-worker, ntpsec-work-thread
triggers = ntpsec-ntp-proto, ntpsec-ntp-worker, ntpsec-work-thread
functions = ntpsec-ntp-proto-funcs
functions = ntpsec-ntp-worker-funcs
functions = ntpsec-work-thread-funcs
include = ntpsec-ntp-proto.ini
include = ntpsec-ntp-worker.ini
include = ntpsec-work-thread.ini
include = ntpsec-trace-log.ini
generator = trace-log-generator

;--------------------------------------------------------------------------
[ntpsec-options]
dump-on-error = true
;
; Tools
;
prefix = /usr
cc = clang
ld = clang
cflags = -Ilib/isc/include -Ibuild -Iinclude
;
; Generator options.
;
gen-enables = enable
gen-triggers = enable

;--------------------------------------------------------------------------
[ntpsec-triggers]
trigger = transmit
ntpsec-1.1.0+dfsg1/ntpfrob/0000775000175000017500000000000013252650651015302 5ustar  rlaagerrlaagerntpsec-1.1.0+dfsg1/ntpfrob/jitter.c0000644000175000017500000000602113252364117016743 0ustar  rlaagerrlaager/*
 * This program can be used to calibrate the clock reading jitter of a
 * particular CPU and operating system. It first tickles every element
 * of an array, in order to force pages into memory, then repeatedly
 * reads the system clock and, finally, writes out the time values for
 * later analysis. From this you can determine the jitter and if the
 * clock ever runs backwards.
 *
 * Copyright 2015 by the NTPsec project contributors
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "config.h"

#include 
#include 
#include 

#include "ntpfrob.h"

#include "ntp_fp.h"
#include "ntp_calendar.h"
#include "timespecops.h"

#define NBUF	800002
#define NSAMPLES 10

static doubletime_t sys_residual;
static doubletime_t average;

/*
 * get_clocktime - return system time in NTP timestamp format.
 */
void
get_clocktime(
	l_fp *now		/* system time */
	)
{
	doubletime_t dtemp;

	struct timespec ts;	/* seconds and nanoseconds */

	/*
	 * Convert Unix clock from seconds and nanoseconds to seconds.
	 */
	clock_gettime(CLOCK_REALTIME, &ts);
	*now = lfptouint(ts.tv_sec + (long)JAN_1970);  /* no fraction, yet */
	dtemp = ts.tv_nsec * S_PER_NS;

	/*
	 * Renormalize to seconds past 1900 and fraction.
	 */
	dtemp += sys_residual;
	if (dtemp >= 1) {
		dtemp -= 1;
		bumplfpsint(*now, 1);
	} else if (dtemp < -1) {
		dtemp += 1;
		bumplfpsint(*now, -1);
	}
	dtemp *= FRAC;
	setlfpfrac(*now, (uint32_t)dtemp);
}

static int doublecmp(const void *a, const void *b)
{
    return (int)(*((const double *)a) - *((const double *)b));
}

void jitter(const iomode mode)
{
	l_fp tr;
	int i;
	double gtod[NBUF];

	/*
	 * Force pages into memory
	 */
	for (i = 0; i < NBUF; i ++)
	    gtod[i] = 0;

	/*
	 * Construct gtod array
	 */
	for (i = 0; i < NBUF; i ++) {
		get_clocktime(&tr);
		gtod[i] = lfptod(tr);
	}

	/*
	 * Write out gtod array for later processing with Matlab
	 */
	average = 0;
	for (i = 0; i < NBUF - 2; i++) {
		gtod[i] = gtod[i + 1] - gtod[i];
		if (mode == raw)
			printf("%13.9f\n", gtod[i]);
		average += gtod[i];
	}

	if (mode == raw)
	    exit(0);
	
	/*
	 * Sort the gtod array and display deciles
	 */
	qsort(gtod, NBUF, sizeof(gtod[0]), doublecmp);
	average = average / (NBUF - 2);
	if (mode == json) {
		fprintf(stdout, "{\"Average\":%13.9Lf,", average);
		fprintf(stdout, "\"First rank\":[");
		for (i = 0; i < NSAMPLES; i++) {
		    fprintf(stdout, "%13.9f", gtod[i]);
		    if (i < NSAMPLES - 1)
			fputc(',', stdout);
		    fputs("],", stdout);
		}
		fprintf(stdout, "\"Last rank\":");
		for (i = NBUF - 12; i < NBUF - 2; i++) {
		    fprintf(stdout, "%13.9f\n", gtod[i]);
		    if (i < NSAMPLES - 1)
			fputc(',', stdout);
		    fputs("]}\n", stdout);
		}
	}
	else if (mode != raw)
	{
		fprintf(stdout, "Average %13.9Lf\n", average);
		fprintf(stdout, "First rank\n");
		for (i = 0; i < NSAMPLES; i++)
		    fprintf(stdout, "%2d %13.9f\n", i, gtod[i]);
		fprintf(stdout, "Last rank\n");
		for (i = NBUF - 12; i < NBUF - 2; i++)
		    fprintf(stdout, "%2d %13.9f\n", i, gtod[i]);
	}

	exit(0);
        /* never returns */
}

/* end */
ntpsec-1.1.0+dfsg1/ntpfrob/precision.c0000644000175000017500000001215013252364117017435 0ustar  rlaagerrlaager/*
 * Copyright 2015 by the NTPsec project contributors
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "config.h"

#include 
#include 

#include "ntp_types.h"
#include "ntp_calendar.h"
#include "ntpfrob.h"

#define	DEFAULT_SYS_PRECISION	-99

int default_get_resolution(void);
int default_get_precision(void);

void precision(const iomode mode)
{
	if (mode == json)
		printf("{\"log2 of resolution\":%d, \"log2 of precision\":%d}\n",
		       default_get_resolution(),
		       default_get_precision());
	else
		printf("log2(resolution) = %d, log2(precision) = %d\n",
			default_get_resolution(),
			default_get_precision());
}

/* Find the resolution of the system clock by watching how the current time
 * changes as we read it repeatedly.
 *
 * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
 * probably use the "unused" low order bits as a counter (to force time to be
 * a strictly increaing variable), incrementing it each time any process
 * requests the time [[ or maybe time will stand still ? ]].
 *
 * SO: the logic goes:
 *
 *      IF      the difference from the last time is "small" (< MINSTEP)
 *      THEN    this machine is "counting" with the low order bits
 *      ELIF    this is not the first time round the loop
 *      THEN    this machine *WAS* counting, and has now stepped
 *      ELSE    this machine has resolution < time to read clock
 *
 * SO: if it exits on the first loop, assume "full accuracy" (1us)
 *     otherwise, take the log2(observed difference, rounded UP)
 *
 * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
 * and the first loop, it doesn't stop too early.
 * Making it even greater allows MINSTEP to be reduced, assuming that the
 * chance of MINSTEP-1 other processes getting in and calling clock_gettime()
 * between this processes's calls.
 * Reducing MINSTEP may be necessary as this sets an upper bound for the time
 * to actually call clock_gettime().
 */

#define	DNSECS	1000000000L
#define	HUSECS	(1024 * 1024)
#define	MINSTEP	200	/* assume no system returns less than 200 nansec */
/* Don't use "1" as some *other* process may read too */
/* We assume no system actually *ANSWERS* in this time */
#define MAXSTEP 20000000   /* maximum clock increment (ns) */
#define MINLOOPS 5      /* minimum number of step samples */
#define	MAXLOOPS (HUSECS * 1024)	/* Assume precision < .1s ! */

int
default_get_resolution(void)
{
	struct timespec tp;
	long last;
	int i;
	long diff;
	long val;
	int minsteps = MINLOOPS;	/* need at least this many steps */

	clock_gettime(CLOCK_REALTIME, &tp);
	last = tp.tv_nsec;
	for (i = - --minsteps; i< MAXLOOPS; i++) {
		clock_gettime(CLOCK_REALTIME, &tp);
		diff = tp.tv_nsec - last;
		if (diff < 0) diff += DNSECS;
		if (diff > MINSTEP) if (minsteps-- <= 0) break;
		last = tp.tv_nsec;
	}
	diff /= 1000;	/* step down to milliseconds */

	fprintf(stderr, "resolution = %ld usec after %d loop%s\n",
	       diff, i, (i==1) ? "" : "s");

	diff = (diff *3)/2;
	if (i >= MAXLOOPS) {
	    fprintf(stderr,
		"     (Boy this machine is fast ! %d loops without a step)\n",
		MAXLOOPS);
	    diff = 1; /* No STEP, so FAST machine */
	}
	if (i == 0) {
	    fprintf(stderr,
		"     (The resolution is less than the time to read the clock -- Assume 1us)\n");
	    diff = 1; /* time to read clock >= resolution */
	}
	for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
	fprintf(stderr,
		"     (Oh dear -- that wasn't expected ! I'll guess !)\n");
	return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
}

/* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */

/*
 * This routine calculates the differences between successive calls to
 * clock_gettime(REALTIME). If a difference is less than zero, the ns field
 * has rolled over to the next second, so we add a second in ns. If
 * the difference is greater than zero and less than MINSTEP, the
 * clock has been advanced by a small amount to avoid standing still.
 * If the clock has advanced by a greater amount, then a timer interrupt
 * has occurred and this amount represents the precision of the clock.
 * In order to guard against spurious values, which could occur if we
 * happen to hit a fat interrupt, we do this for MINLOOPS times and
 * keep the minimum value obtained.
 */  
int
default_get_precision(void)
{
	struct timespec tp;
	long last;
	int i;
	long diff;
	long val;
	long nsec;

	nsec = 0;
	val = MAXSTEP;
	clock_gettime(CLOCK_REALTIME, &tp);
	last = tp.tv_nsec;
	for (i = 0; i < MINLOOPS && nsec < HUSECS * 1024;) {
	    clock_gettime(CLOCK_REALTIME, &tp);
		diff = tp.tv_nsec - last;
		last = tp.tv_nsec;
		if (diff < 0)
		    diff += DNSECS;
		nsec += diff;
		if (diff > MINSTEP) {
			i++;
			if (diff < val)
			    val = diff;
		}
	}
	val /= 1000;	/* step down to milliseconds */
	fprintf(stderr, "precision  = %ld usec after %d loop%s\n",
	       val, i, (i == 1) ? "" : "s");
	if (nsec >= HUSECS * 1024) {
	    fprintf(stderr, "     (Boy this machine is fast! nsec was %ld)\n",
		       nsec);
		val = MINSTEP;	/* val <= MINSTEP; fast machine */
	}
	diff = HUSECS;
	for (i = 0; diff > val; i--)
	    diff >>= 1;
	return (i);
}

/* end */
ntpsec-1.1.0+dfsg1/ntpfrob/tickadj.c0000644000175000017500000000346013252364117017057 0ustar  rlaagerrlaager/*
 * tickadj - read, and possibly modify, the kernel `tick' and
 *	     `tickadj' variables', using ntp_adjtime(2).  This is
 *	     included only for compatibility with old scripts.
 *	     Its former support for unsafe /dev/kmem operations
 *	     has been removed.
 *
 * Copyright 2015 by the NTPsec project contributors
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "config.h"

#include "ntp_types.h"
#include "ntp_syscall.h"

#include 
#include 
#include 

#include "ntpfrob.h"

#ifdef HAVE_ADJTIMEX
# include 	/* prerequisite on NetBSD */
# include 

static struct timex txc;

void tickadj(const bool json_b, const int newtick)
{
	if (newtick != 0)
	{
#ifdef HAVE_STRUCT_TIMEX_TIME_TICK
		if ( (txc.time_tick = newtick) < 1 )
#else
		if ( (txc.tick = newtick) < 1 )
#endif /* HAVE_STRUCT_TIMEX_TIME_TICK */
		{
			fprintf(stderr, "ntpfrob: silly value for tick: %d\n", newtick);
			exit(1);
		}
#ifdef MOD_TIMETICK
		txc.modes = MOD_TIMETICK;
#else
#ifdef HAVE_STRUCT_TIMEX_MODES
		txc.modes = ADJ_TICK;
#else
		txc.mode = ADJ_TICK;
#endif /* HAVE_STRUCT_TIMEX_MODES */
#endif /* MOD_TIMETICK */
	}
	else
	{
#ifdef MOD_TIMETICK
		txc.modes = 0;
#else
#ifdef HAVE_STRUCT_TIMEX_MODES
		txc.modes = 0;
#else
		txc.mode = 0;
#endif /* HAVE_STRUCT_TIMEX_MODES */
#endif /* MOD_TIMETICK */
	}

	if (ntp_adjtime(&txc) < 0)
	{
		perror("ntp_adjtime");
	}
	else
	{
#ifdef HAVE_STRUCT_TIMEX_TIME_TICK
		if (json_b)
			printf("{\"tick\":%ld,\"tick_adj\":%ld}\n",
			       txc.time_tick, txc.tickadj);
		else
			printf("tick = %ld\ntick_adj = %ld\n",
			       txc.time_tick, txc.tickadj);
#else
		if (json_b)
			printf("{\"tick\":%ld}\n", txc.tick);
		else
			printf("tick = %ld\n", txc.tick);
#endif /* HAVE_STRUCT_TIMEX_TIME_TICK */
	}

}

#endif /* HAVE_ADJTIMEX */

/* end */
ntpsec-1.1.0+dfsg1/ntpfrob/pps-api.c0000644000175000017500000000657113252364117017025 0ustar  rlaagerrlaager/*
 *
 * Try to run this code to see what the PPS-API finds. You give it the
 * device as argument and you may have to modify the pp.mode = BLA assignment.
 *
 * Code originally by Poul-Henning Kemp.
 *
 * Copyright 2015 by the NTPsec project contributors
 *  SPDX-License-Identifier: BSD-2-Clause
 */

#include "config.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "timespecops.h"
#include "ntpfrob.h"

#ifdef HAVE_SYS_TIMEPPS_H
#include 

#ifdef timespecsub
  /* It's defined on NetBSD */
# undef timespecsub
#endif
#define timespecsub(vvp, uvp)                                           \
        do {                                                            \
                (vvp)->tv_sec -= (uvp)->tv_sec;                         \
                (vvp)->tv_nsec -= (uvp)->tv_nsec;                       \
                if ((vvp)->tv_nsec < 0) {                               \
                        (vvp)->tv_sec--;                                \
                        (vvp)->tv_nsec += NS_PER_S;                   \
                }                                                       \
        } while (0)

static void Chew(struct timespec *, struct timespec *, unsigned, unsigned);

static void
Chew(struct timespec *tsa, struct timespec *tsc, unsigned sa, unsigned sc)
{
	struct timespec ts;

	printf("%ld.%09ld ", (long)tsa->tv_sec, tsa->tv_nsec);
	printf("%ld.%09ld ", (long)tsc->tv_sec, tsc->tv_nsec);
	printf("%u %u ", sa, sc);

	ts = *tsc;
	timespecsub(&ts,tsa);
	printf("%.9f ", ts.tv_sec + ts.tv_nsec * S_PER_NS);
	printf("\n");
	fflush(stdout);
}

static int err(int out, const char *legend)
{
    fprintf(stderr, "ntpfrob: %s\n", legend);
    exit(out);
}
#endif /* HAVE_SYS_TIMEPPS_H */

void ppscheck(const char *device)
{
#ifndef HAVE_SYS_TIMEPPS_H
	(void)device;
 	fputs("ntpfrob: PPS is not available.\n", stderr);
	exit(1);
#else
	int fd;
	pps_info_t pi;
	pps_params_t pp;
	pps_handle_t ph = 0;    /* 0 to prevent spurious uninialized warning */
	int i, mode;
	unsigned int olda = 0, oldc = 0;
	struct timespec to;

	if (device == NULL)
		device = "/dev/cuaa1";
	setbuf(stdout, 0);
	fd = open(device, O_RDONLY);
	if (fd < 0) 
		err(1, device);
	i = time_pps_create(fd, &ph);
	if (i < 0)
		err(1, "time_pps_create");

	i = time_pps_getcap(ph, &mode);
	if (i < 0)
		err(1, "time_pps_getcap");

        memset(&pp, 0, sizeof(pp));
	/* pp.mode = PPS_CAPTUREASSERT | PPS_ECHOASSERT; */
	pp.mode = PPS_CAPTUREBOTH;
	/* pp.mode = PPS_CAPTUREASSERT; */

#ifdef PPS_API_VERS
        pp.api_version = PPS_API_VERS;
#else
        /* FreeBSD, NetBSD do not publicly define PPS_ABI_VERS, assume 1 */
        pp.api_version = 1;
#endif

	i = time_pps_setparams(ph, &pp);
	if (i < 0)
		err(1, "time_pps_setparams");

	while (1) {
		to.tv_nsec = 0;
		to.tv_sec = 0;
		i = time_pps_fetch(ph, PPS_TSFMT_TSPEC, &pi, &to);
		if (i < 0)
			err(1, "time_pps_fetch");
		if (olda == pi.assert_sequence &&
		    oldc == pi.clear_sequence) {
			/* used to be usleep(10000) - 0.1 sec */
			const struct timespec tenth = {0, 100000};
			nanosleep(&tenth, NULL);
			continue;
		}

		Chew(&pi.assert_timestamp, &pi.clear_timestamp,
			pi.assert_sequence, pi.clear_sequence);
		olda = pi.assert_sequence;
		oldc = pi.clear_sequence;
	}
#endif /* HAVE_SYS_TIMEPPS_H */
        /* either way, never returns */
}

/* end */
ntpsec-1.1.0+dfsg1/ntpfrob/ntpfrob-man.txt0000644000175000017500000000063313252364117020265 0ustar  rlaagerrlaager= ntpfrob(8) =
:doctype: manpage
:man source: NTPsec
:man version: @NTPSEC_VERSION@
:man manual: NTPsec

== NAME ==
ntpfrob - frob the clock hardware

include::../docs/includes/ntpfrob-body.txt[]

== EXIT STATUS ==

One of the following exit values will be returned:

0 (EXIT_SUCCESS)::
  Successful program execution.
1 (EXIT_FAILURE)::
  The operation failed or the command invocation was not valid.


// end
ntpsec-1.1.0+dfsg1/ntpfrob/bumpclock.c0000644000175000017500000000262413252364117017426 0ustar  rlaagerrlaager/*
 * Hack to bump the system time for testing ntpd response.
 * Must be run as root.
 * Arg is microseconds.
 */

#include "config.h"

#include 
#include 
#include 
#include 
#include 

#include "ntpfrob.h"

#define NS_PER_S	1000000000

void bumpclock(int bump)
{
    struct timespec was, set, now;
    int rc1, rc2, rc3;
    int er1, er2, er3;

    printf("Bumping clock by %d microseconds.\n", bump);

    rc1 = clock_gettime(CLOCK_REALTIME, &was);
    er1 = errno;

    set = was;
    bump *= 1000;
    /* coverity[tainted_data] */
    set.tv_nsec += bump;
    while (set.tv_nsec >= NS_PER_S ) {
	set.tv_nsec -= NS_PER_S;
	set.tv_sec +=1;
    }
    while (set.tv_nsec <= 0 ) {
	set.tv_nsec += NS_PER_S;
	set.tv_sec -=1;
    }
    rc2 = clock_settime(CLOCK_REALTIME, &set);
    er2 = errno;

    rc3 = clock_gettime(CLOCK_REALTIME, &now);
    er3 = errno;

    /* Defer printing so it doesn't distort timing. */
    if (rc1)
	printf("Couldn't get time: %s\n", strerror(er1));
    else
	printf("Was: %ld.%09ld\n", (long)was.tv_sec, was.tv_nsec);

    if (rc2) {
	printf("Couldn't set time: %s\n", strerror(er2));
	printf("Try: %ld.%09ld\n", (long)set.tv_sec, set.tv_nsec);
    } else
	printf("Set: %ld.%09ld\n", (long)set.tv_sec, set.tv_nsec);
 
   if (rc3)
	printf("Couldn't set time: %s\n", strerror(er3));
    else
	printf("Now: %ld.%09ld\n", (long)now.tv_sec, now.tv_nsec);
}
ntpsec-1.1.0+dfsg1/ntpfrob/main.c0000644000175000017500000000436113252364117016373 0ustar  rlaagerrlaager/* Copyright 2015 by the NTPsec project contributors
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include "config.h"

#include 
#include 
#include 
#include 

#include "ntpfrob.h"

static void usage(void);

static void usage(void)
{
	fputs(
"usage:\n"
"ntpfrob [ -A] [-b bump] [ -a tick ] [ -p ppsdev ] [-c] [-e] [-r] {-h] [-?]\n"
"        -a tick    Set kernel tick\n"
"        -A         Display kernel tick\n"
"        -b bump    Bump clock by specified microseconds.\n"
"        -c         Compute and display clock jitter.\n"
"        -e         Measure clock precision.\n"
"        -j         Report in self-describing JSON.\n"
"        -p ppsdev  Look for PPS pulses on a specified device\n"
"        -r         Raw mode. Only applies to the jitter mode.\n"
"        -?         Print usage\n"
"        -h         Print usage\n"
    , stderr);

}

int
main(int argc, char **argv)
{
	int ch;
        bool got_one = false;
	iomode mode = plain_text;
	while ((ch = getopt(argc, argv, "a:Ab:cejp:rh?")) != EOF) {
                got_one = true;
		switch (ch) {
		case 'A':
#ifdef HAVE_ADJTIMEX
		    tickadj(mode==json, 0);
#else
		    fputs("ntpfrob: no adjtimex(2)\n", stderr);
#endif /* HAVE_ADJTIMEX */
		    break;
		case 'a':
#ifdef HAVE_ADJTIMEX
		    tickadj(mode, atoi(optarg));
#else
		    fputs("ntpfrob: no adjtimex(2)\n", stderr);
#endif /* HAVE_ADJTIMEX */
		    break;
		case 'b':
		    bumpclock(atoi(optarg));
		    break;
		case 'c':
		    jitter(mode);
                    /* never returns */
		    break;
		case 'e':
		    precision(mode);
		    exit(0);
		    break;
		case 'j':
		    mode = json;
		    break;
		case 'p':
#ifdef HAVE_SYS_TIMEPPS_H
		    ppscheck(optarg);
                    /* never returns */
#else
		    fputs("ntpfrob: no PPS kernel interface.\n", stderr);
		    exit(0);
#endif
		    break;
		case 'r':
		    mode = raw;
		    break;
		default:
		    fputs("ntpfrob: no mode option specified.\n", stderr);
                    /* fall through */
                case 'h':
                case '?':
                    usage();
		    exit(1);
		    break;
		}
	}
        if ( !got_one ) {
	    fputs("ntpfrob: no mode option specified.\n", stderr);
	    usage();
	    exit(1);
        }

    exit(0);
}
ntpsec-1.1.0+dfsg1/ntpfrob/wscript0000644000175000017500000000066213252364117016721 0ustar  rlaagerrlaagerdef build(ctx):
    bldnode = ctx.bldnode.abspath()

    frob_sources = ['main.c', 'bumpclock.c', 'jitter.c', 'precision.c',
                    'pps-api.c', 'tickadj.c']

    ctx(
        features="c cprogram bld_include src_include",
        includes=["%s/ntpfrob/" % bldnode],
        install_path='${BINDIR}',
        source=frob_sources,
        target="ntpfrob",
        use="M RT",
    )

    ctx.manpage(8, "ntpfrob-man.txt")
ntpsec-1.1.0+dfsg1/ntpfrob/ntpfrob.h0000644000175000017500000000077313252364117017131 0ustar  rlaagerrlaager/*
 * Our methods, one per linked module
 */
#include       /* for bool */
#include "ntp_fp.h"       /* for l_fp */

typedef enum {plain_text, raw, json} iomode;

extern void bumpclock(int);
extern void get_clocktime(l_fp *now);
extern void jitter(const iomode mode) __attribute__((noreturn));
extern void ppscheck(const char *device) __attribute__((noreturn));
extern void precision(const iomode mode);
extern void stepback(void);
extern void tickadj(const bool mode, const int tick);

/*end */
ntpsec-1.1.0+dfsg1/INSTALL0000644000175000017500000002565213252364117014670 0ustar  rlaagerrlaager= Installation instructions =

== Prerequisites ==

This software should build on any operating system conformant to
POSIX.1-2001 and ISO/IEC 9899:1999 (C99).  In addition, the operating
system must have an ntp_adjtime(2) call. Also, it must support the
IPv6 API defined in RFC 2493 and RFC 2553. Finally, it must support
iterating over active UDP interfaces via getifaddrs(3) or some
equivalent facility.

You can browse a summary of differences from legacy NTP here:

https://docs.ntpsec.org/latest/ntpsec.html

There are some prerequisites.  Libraries need the binary installed
to run and in addition, the development headers installed to build.

=== The short version ===

If you are on a Debian or Ubuntu distribution that uses apt-get, or on
a Fedora or RedHat distribution that uses yum or dnf, or a Suse
distribution that uses yast or zypper, or Gentoo, you can use the
./buildprep script.

In that case, run ./buildprep -n to see what needs to be installed as
prerequisites on your system.  In order that we have a single point of
truth about package requirements, much information about
installable-package names that used to live in this file has moved to
that script.

If you are using other distributions or OSes, such as macOS, Solaris,
or *BSD, you will have to install the build prerequisites by hand on
your system.  Read the buildprep script to get an idea what packages
are required.

buildprep does not attempt to install dependencies for optional
components like ntpviz or building documentation unless asked. See
buildprep --help for details on available options.  asciidoc has a
huge tail of dependencies and you probably don't want it unless you're
planning to modify the documentation.  psutil may require different
packages depending on whether your base Python version is 2 or 3.

=== Details ===

Python 2.x, x >= 6, or Python 3.x, x >= 3::
   Required to build, and for various scripts such as ntpviz (but see
   the guidance for packagers in devel/packaging.txt).  Our Python code
   has been written polyglot to also run with production versions of
   Python 3.  Note that you will need both the ordinary Python
   installation and Python dev tools, if your OS makes such a
   distinction.
   
   Some OS distros won't find our installed python libraries.
   More info in README-PYTHON.

argparse::
   Required for ntpviz when using Python version 2.6
   Install with pip: pip install argparse

psutil::
   Optional for ntpviz.  Allows running with ionice()
   Debian: python-psutil or python3-psutil
   Ubuntu: python-psutil or python3-psutil
   Fedora/CentOS: python-psutil
   CentOS 6: python-psutil in epel ("Extra Packages for Enterprise Linux").
   Gentoo: dev-python/psutil
   SLES: python-psutil

sys/timepps.h::
   If you are building to supply Stratum 1 time service (that is, with
   refclocks linked in) you may find that you need the sys/timepps.h
   file to be installed, depending on which refclocks you enable.
   This won't be necessary for pure client (Stratum 2 or higher)
   installations.

asciidoc, a2x::
   You will need asciidoc to make HTML and a2x to make manual pages from the
   documentation masters.  Only required if you configured with --enable-doc.
   Note, you need asciidoc 8.6.0 at minimum.
   Debian/Ubuntu: asciidoc
   Fedora/CentOS: asciidoc  (CentOS/RHEL6 has only 8.4.5, you must upgrade)
   Gentoo: app-text/asciidoc
   SLES: asciidoc

Local copy of DocBook stylesheets:
   Optional: Prevents documentation build failures when your Internet is down
   Debian/Ubuntu: docbook-xsl
   Fedora/CentOS: docbook-xsl-stylesheets
   Gentoo: app-text/docbook-xsl-stylesheets
   SLES: docbook-xsl-stylesheets - pulled in by `asciidoc`

wget:
   Optional, used by ntpleapfetch

== Basic Installation ==

These are generic Unix installation instructions.  Some notes on
specific platforms follow this section.

Under Unix, the simplest way to compile this package is:

  1. `cd' to the directory containing the package's source code and

  2. Run `./buildprep' as root to get your prerequisites installed.

  3. Run `./waf configure' to configure the package for your system.
  You may want to add configuration options after the verb 'configure';
  see below.

  4. Invoke `./waf build' to compile the package.

  5. Invoke `./waf install' to install the programs and any data files and
     documentation.

  6. You can uninstall cleanly by invoking `./waf uninstall' as root.

  7. Look under "Boot script setup" in wscript for how to set up
     boot time startup of ntpd; this will vary according to your
     init system.

  8. You can remove the program binaries and object files from the
     source code directory by typing `./waf clean'.

  9. To also remove the files that `./waf configure' created (so you can
     configure and compile the package for a different kind of
     computer), type `./waf distclean'.

=== Solaris ===

When building the NTPsec suite using gcc under Solaris, you may see
prototype-mismatch warnings involving adjtime(2).  These warnings are
functionally harmless and can be ignored.  They reflect an unfortunate
choice by Solaris not to const the function's first argument as do
other implementations.

== Build Bugs ==

The configuration system occasionally acts up during builds on new
platforms.  If you see the message "Compilation check failed but
include exists!" this means that an attempt to compile a small test
program using the include header mentioned on the previous line
failed, but waf configure then found that despite this the header file
exists on your system.

When this happens, it is likely that the header has prerequisites
on your system that waf configure doesn't know about - that is,
other headers always need to be included before it in C programs.
Please report this as a bug, along with your platform details.

== Source build limitations ==

When you do an install from source, no attempt is made to generate an
/etc/ntp.conf for you.  The reason is that we, the maintainers, can't
know what pool host you are authorized to use.  If you have an existing
ntp.conf from a legacy version, you can expect it to work.

Due to a limitation of the Python distutils library, if you install
from the source distribution with prefix set to a value other than
/usr (in particular, if it's the default value /usr/local), that
prefix will be honored *only if the corresponding Python library
directory already exists*. Otherwise, under Linux, the install will
drop the ntp Python library in /usr/lib. This layout may violate the
Linux File Hierarchy Standard.

You should have neither issue if you install from an OS distribution's
binary package.

== Caveat for Crossbuilders ==

If you see a build failure with the message "error: No repo or cache
detected.", you have collided with an unusual feature of our build recipe.

To generate the version.h file, the uses a script called
'autorevision.sh' which needs one of two preconditions.  Either (1)
you are building in a local repository clone, or (2) you are building
from an unpacked release tarball containing a file
wafhelpers/.autorevision-cache containing version information.

The motivation for this behavior is to have a version string that
makes sense on every build from either the repository or a release
tarball.  It means, however, that if you try to build when waf can see
neither of these things, you'll get the "No repo or cache detected"
error.  This can happen when atrempting to use a remote buildroot for
cross-compilation or other purposes

There are a couple of possible workarounds.  One is to do your remote build
from a release tarball.  Another is to do a ./waf configure and build in the
source directory before attempting a buildroot build.

== Installation Names ==

By default, `waf install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc.  You can specify an
installation prefix other than `/usr/local' by giving waf the
option `--prefix=PATH'.

You should read the section "Path problems with the test tools" in
devel/testing.txt before setting a non-default prefix.

There is a separate `--destdir' option that changes the root of the
entire installation hierarchy, prepending all paths.  It defaults to `/'.
You might, say, use --destdir=/tmp/ntp for a test install to see what
the installation paths look like without running as root or touching
system directories.

NB: --destdir is specified at install time, --prefix is specified
at configure time and saved until install time.


== Qualification testing ==

Details on how to qualify NTPsec if you've never used it before
are at devel/testing.txt.

== Strict compatibility mode ==

There have been a handful of forward-incompatible changes from NTP Classic.
These are unlikely to affect normal operation.  However, there is a configure
operation, --enable-classic-mode, that restores certain legacy behaviors. This
is not recommended, as it makes the code a little bulkier and slower.

Here's what it presently does:

* Reverts logging to the old format that designates clocks with magic
  addresses rather than the driver shortname and unit number.

* Enables declaring generic-driver refclocks with the old magic-address
  syntax (but the new syntax won't work for these, though it will for
  other driver types).

* Reverts the default baudrate of the NMEA driver to 4800 (from 9600).

* Restores the old (non-RFC 3339) format of logfile timestamps.

Other behaviors may be added in future releases.

== Optional Features ==

The waf builder accepts `--enable-FEATURE' options to where FEATURE
indicates an optional part of the package.  Do `waf --help' for a list
of options.

refclocks are enabled with `--refclock= or --refclock=all'
`waf configure --list' will print a list of available refclocks.

=== --enable-early-droproot ===

Drop root privileges as early as possible.  This requires the refclock
devices to be owned by the same owner or group that ntpd will be
running under (most likely that group will be named "ntp") so that it
can still open the devices.  This can be accomplished by adding
`GROUP="ntp"` or `OWNER="ntp"` to the udev rules that create the
device symlinks for the refclocks.

== Developer options ==

--enable-debug-gdb::
     Enable GDB debugging symbols.

== Operation Controls ==

The waf builder recognizes the following options to control how it
operates.

--help::
     Print a summary of the options to `waf configure', and exit.

--version::
     Print the version of waf used to generate the `configure'
     script, and exit.

== Cross-compiling ==

Set up a cross-compile environment for the target architecture.  At minimum
it will need its own binaries for the OpenSSL library.

Configure NTPSec with:

  waf configure --cross-compiler=/path/to/your/cross/cc

There are also --cross-cflags and --cross-ldflags to supply the cross compiler
with appropriate values.

== Statistics ==

If you want to generate the ntpviz graphs regularly, add these lines to
your root crontab:

53 * * * * cd /usr/local/src/NTP/ntpsec/ntpstats; ./ntpviz -p 1 -o day
45 11,23 * * * cd /usr/local/src/NTP/ntpsec/ntpstats; ./ntpviz -p 7 -o week

// end

ntpsec-1.1.0+dfsg1/ntptime/0000775000175000017500000000000013252650651015310 5ustar  rlaagerrlaagerntpsec-1.1.0+dfsg1/ntptime/ntptime-man.txt0000644000175000017500000000064513252364117020304 0ustar  rlaagerrlaager= ntptime(8) =
:doctype: manpage
:man source: NTPsec
:man version: @NTPSEC_VERSION@
:man manual: NTPsec

== NAME ==
ntptime - read and set kernel time variables

include::../docs/includes/ntptime-body.txt[]

== EXIT STATUS ==

One of the following exit values will be returned:

0 (EXIT_SUCCESS)::
  Successful program execution.
1 (EXIT_FAILURE)::
  The operation failed or the command invocation was not valid.

// end
ntpsec-1.1.0+dfsg1/ntptime/ntptime.c0000644000175000017500000003157213252364117017141 0ustar  rlaagerrlaager/*
 * NTP test program
 *
 * This program tests to see if the NTP user interface routines
 * ntp_gettime() and ntp_adjtime() have been implemented in the kernel.
 * If so, each of these routines is called to display current timekeeping
 * data.
 */

#include "config.h"

#include "ntp_fp.h"
#include "timespecops.h"
#include "ntp_syscall.h"
#include "ntp_stdlib.h"

#include 
#include 
#include 
#include 
#include 

#ifdef HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC
#define tv_frac_sec tv_nsec
#else
#define tv_frac_sec tv_usec
#endif

/*
 * Convert usec to a time stamp fraction.
 */
# define TVUTOTSF(tvu)	\
	(uint32_t)((((uint64_t)(tvu) << 32) + US_PER_S / 2) / US_PER_S)

/* nano seconds per micro second */
#define NS_PER_US_FLOAT	1000.0

#ifndef HAVE_STRUCT_NTPTIMEVAL
struct ntptimeval
{
	struct timeval	time;		/* current time (ro) */
	long int	maxerror;	/* maximum error (us) (ro) */
	long int	esterror;	/* estimated error (us) (ro) */
};

#endif	/* !HAVE_STRUCT_NTPTIMEVAL */

/* MUSL port shim */
#ifndef HAVE_NTP_GETTIME
int ntp_gettime(struct ntptimeval *ntv)
{
	struct timex tntx;
	int result;

	ZERO(tntx);
	result = ntp_adjtime(&tntx);
	ntv->time = tntx.time;
	ntv->maxerror = tntx.maxerror;
	ntv->esterror = tntx.esterror;
#if defined(HAVE_STRUCT_NTPTIMEVAL_TAI)
	ntv->tai = tntx.tai;
#endif
	return result;
}
#endif	/* !HAVE_NTP_GETTIME */


#define TIMEX_MOD_BITS \
"\20\1OFFSET\2FREQUENCY\3MAXERROR\4ESTERROR\5STATUS\6TIMECONST\
\13PLL\14FLL\15MICRO\16NANO\17CLKB\20CLKA"
 
#define TIMEX_STA_BITS \
"\20\1PLL\2PPSFREQ\3PPSTIME\4FLL\5INS\6DEL\7UNSYNC\10FREQHOLD\
\11PPSSIGNAL\12PPSJITTER\13PPSWANDER\14PPSERROR\15CLOCKERR\
\16NANO\17MODE\20CLK"

/*
 * These constants are used to round the time stamps computed from
 * a struct timeval to the microsecond (more or less).  This keeps
 * things neat.
 */
#define	TS_MASK		0xfffff000	/* mask to usec, for time stamps */
#define	TS_ROUNDBIT	0x00000800	/* round at this bit */

/*
 * Function prototypes
 */
const char *	snprintb	(size_t, char *, unsigned int, const char *);
const char *	timex_state	(int);

#ifdef SIGSYS
void pll_trap(int) __attribute__((noreturn));

static struct sigaction newsigsys;	/* new sigaction status */
static struct sigaction sigsys;		/* current sigaction status */
static sigjmp_buf env;		/* environment var. for pll_trap() */
#endif

static volatile int pll_control; /* (0) daemon, (1) kernel loop */
static volatile int status;	/* most recent status bits */
static volatile int flash;	/* most recent ntp_adjtime() bits */
char* progname;
static char optargs[] = "MNT:cde:f:hjm:o:rs:t:";

int
main(
	int argc,
	char *argv[]
	)
{
	struct ntptimeval ntv;
	struct timeval tv;
	struct timex ntx, _ntx;
	int	times[20];
	double ftemp, gtemp, htemp;
	long time_frac;				/* ntv.time.tv_frac_sec (us/ns) */
	l_fp ts;
	volatile unsigned ts_mask = TS_MASK;		/* defaults to 20 bits (us) */
	volatile unsigned ts_roundbit = TS_ROUNDBIT;	/* defaults to 20 bits (us) */
	volatile int fdigits = 6;			/* fractional digits for us */
	size_t c;
	int ch;
	int errflg	= 0;
	int cost	= 0;
	volatile bool json      = false;
	volatile int rawtime	= 0;
	char ascbuf[BUFSIZ];

	ZERO(ntx);
	progname = argv[0];
	while ((ch = ntp_getopt(argc, argv, optargs)) != EOF) {
		switch (ch) {
#ifdef MOD_MICRO
		case 'M':
			ntx.modes |= MOD_MICRO;
			break;
#endif
#ifdef MOD_NANO
		case 'N':
			ntx.modes |= MOD_NANO;
			break;
#endif
#if defined NTP_API && NTP_API > 3
		case 'T':
			ntx.modes = MOD_TAI;
			ntx.constant = atoi(ntp_optarg);
			break;
#endif
		case 'c':
			cost++;
			break;

		case 'e':
			ntx.modes |= MOD_ESTERROR;
			ntx.esterror = atoi(ntp_optarg);
			break;

		case 'f':
			ntx.modes |= MOD_FREQUENCY;
			ntx.freq = (long)FP_SCALE(atof(ntp_optarg));
			break;

		case 'j':
			json = true;
			break;

		case 'm':
			ntx.modes |= MOD_MAXERROR;
			ntx.maxerror = atoi(ntp_optarg);
			break;

		case 'o':
			ntx.modes |= MOD_OFFSET;
			ntx.offset = atoi(ntp_optarg);
			break;

		case 'r':
			rawtime++;
			break;

		case 's':
			ntx.modes |= MOD_STATUS;
			ntx.status = atoi(ntp_optarg);
			if (ntx.status < 0 || ntx.status >= 0x100)
				errflg++;
			break;

		case 't':
			ntx.modes |= MOD_TIMECONST;
			ntx.constant = atoi(ntp_optarg);
			break;

		default:
			errflg++;
		}
	}
	if (errflg || (ntp_optind != argc)) {
		fprintf(stderr,
			"usage: %s [-%s]\n\n\
%s%s%s\
-c		display the time taken to call ntp_gettime (us)\n\
-e esterror	estimate of the error (us)\n\
-f frequency	Frequency error (-500 .. 500) (ppm)\n\
-h		display this help info\n\
-j              report in JSON\n\
-m maxerror	max possible error (us)\n\
-o offset	current offset (ms)\n\
-r		print the unix and NTP time raw\n\
-s status	Set the status bits\n\
-t timeconstant	log2 of PLL time constant (0 .. %d)\n",
			progname, optargs,
#ifdef MOD_MICRO
"-M		switch to microsecond mode\n",
#else
"",
#endif
#ifdef MOD_NANO
"-N		switch to nanosecond mode\n",
#else
"",
#endif
#if defined NTP_API && NTP_API > 3
"-T tai_offset	set TAI offset\n",
#else
"",
#endif
			MAXTC);
		exit(2);
	}

#ifdef SIGSYS
	/*
	 * Test to make sure the sigaction() works in case of invalid
	 * syscall codes.
	 */
	newsigsys.sa_handler = pll_trap;
	newsigsys.sa_flags = 0;
	if (sigaction(SIGSYS, &newsigsys, &sigsys)) {
		perror("sigaction() fails to save SIGSYS trap");
		exit(1);
	}
#endif /* SIGSYS */

	if (cost) {
#ifdef SIGSYS
		if (sigsetjmp(env, 1) == 0) {
#endif
			for (c = 0; c < COUNTOF(times); c++) {
				status = ntp_gettime(&ntv);
				if ((status < 0) && (errno == ENOSYS))
					--pll_control;
				if (pll_control < 0)
					break;
				times[c] = ntv.time.tv_frac_sec;
			}
#ifdef SIGSYS
		}
#endif
		if (pll_control >= 0) {
			printf("[ us %06d:", times[0]);
			for (c = 1; c < COUNTOF(times); c++)
			    printf(" %d", times[c] - times[c - 1]);
			printf(" ]\n");
		}
	}
#ifdef SIGSYS
	if (sigsetjmp(env, 1) == 0) {
#endif
		status = ntp_gettime(&ntv);
		if ((status < 0) && (errno == ENOSYS))
			--pll_control;
#ifdef SIGSYS
	}
#endif
	_ntx.modes = 0;				/* Ensure nothing is set */
#ifdef SIGSYS
	if (sigsetjmp(env, 1) == 0) {
#endif
		status = ntp_adjtime_ns(&_ntx);
		if ((status < 0) && (errno == ENOSYS))
			--pll_control;
		flash = _ntx.status;
#ifdef SIGSYS
	}
#endif
	if (pll_control < 0) {
		printf("NTP user interface routines are not configured in this kernel.\n");
		goto lexit;
	}

	/*
	 * Fetch timekeeping data and display.
	 */
	status = ntp_gettime(&ntv);
	if (status < 0) {
		perror("ntp_gettime() call fails");
	} else {
		/* oldstyle formats */
		const char *ofmt1 = "ntp_gettime() returns code %d (%s)\n";
		const char *ofmt2 = "  time %s, (.%0*d),\n";
		const char *ofmt3 = "  maximum error %lu us, estimated error %lu us";
		const char *ofmt4 = "  ntptime=%x.%x unixtime=%x.%0*d %s";
#if defined NTP_API && NTP_API > 3
		const char *ofmt5 = ", TAI offset %ld\n";
#else
		const char *ofmt6 = "\n";
#endif /* NTP_API */
		/* JSON formats */
		const char *jfmt1 = "{\"gettime-code\":%d,\"gettime-status\":\"%s\",";
		const char *jfmt2 = "\"time\":\"%s\",\"fractional-time\":\".%0*d\",";
		const char *jfmt3 = "\"maximum-error\":%lu,\"estimated-error\":%lu,";
		const char *jfmt4 = "\"raw-ntp-time\":\"%x.%x\",\"raw-unix-time\":\"%x.%0*d %s\",";
#if defined NTP_API && NTP_API > 3
		const char *jfmt5 = "\"TAI-offset\":%d,";
#else
		const char *jfmt6 = "";
#endif /* NTP_API */
		printf(json ? jfmt1 : ofmt1, status, timex_state(status));
		time_frac = ntv.time.tv_frac_sec;
#ifdef STA_NANO
		if (flash & STA_NANO) {
			ntv.time.tv_frac_sec /= 1000;
			ts_mask = 0xfffffffc;	/* 1/2^30 */
			ts_roundbit = 0x00000002;
			fdigits = 9;
		}
#endif
		tv.tv_sec = ntv.time.tv_sec;
		tv.tv_usec = ntv.time.tv_frac_sec;
		ts = tspec_stamp_to_lfp(tval_to_tspec(tv));
		setlfpfrac(ts, lfpfrac(ts) + ts_roundbit);
		setlfpfrac(ts, lfpfrac(ts) & ts_mask);
		printf(json ? jfmt2 : ofmt2,  json ? rfc3339date(ts) : prettydate(ts), fdigits, (int)time_frac);
		printf(json ? jfmt3 : ofmt3,  (unsigned long)ntv.maxerror, (unsigned long)ntv.esterror);
		if (rawtime)
			printf(json ? jfmt4 : ofmt4,
			       (unsigned int)lfpuint(ts),
			       (unsigned int)lfpfrac(ts),
			       (int)ntv.time.tv_sec, fdigits,
			       (int)time_frac,

			       ctime_r((time_t *)&ntv.time.tv_sec, ascbuf));
#if defined(HAVE_STRUCT_NTPTIMEVAL_TAI)
		printf(json ? jfmt5 : ofmt5, (long)ntv.tai);
#else
		fputs(json ? jfmt6 : ofmt6, stdout);
#endif /* HAVE_STRUCT_NTPTIMEVAL_TAI */
	}
	status = ntp_adjtime_ns(&ntx);
	if (status < 0) {
		perror((errno == EPERM) ? 
		   "Must be root to set kernel values\nntp_adjtime() call fails" :
		   "ntp_adjtime() call fails");
	} else {
		char binbuf[132];
		/* oldstyle formats */
		const char *ofmt7 = "ntp_adjtime() returns code %d (%s)\n";
		const char *ofmt8 = "  modes %s,\n";
		const char *ofmt9 = "  offset %.3f";
		const char *ofmt10 = " us, frequency %.3f ppm, interval %d s,\n";
		const char *ofmt11 = "  maximum error %lu us, estimated error %lu us,\n";
		const char *ofmt12 = "  status %s,\n";
		const char *ofmt13 = "  time constant %lu, precision %.3f us, tolerance %.0f ppm,\n";
		const char *ofmt14 = "  pps frequency %.3f ppm, stability %.3f ppm, jitter %.3f us,\n";
		const char *ofmt15 = "  intervals %lu, jitter exceeded %lu, stability exceeded %lu, errors %lu.\n";
		/* JSON formats */
		const char *jfmt7 = "\"adjtime-code\":%d,\"adjtime-status\":\"%s\",";
		const char *jfmt8 = "\"modes\":\"%s\",";
		const char *jfmt9 = "\"offset\":%.3f,";
		const char *jfmt10 = "\"frequency\":%.3f,\"interval\":%d,";
		const char *jfmt11 = "\"maximum-error\":%lu,\"estimated-error\":%lu,";
		const char *jfmt12 = "\"status\":\"%s\",";
		const char *jfmt13 = "\"time-constant\":%lu,\"precision\":%.3f,\"tolerance\":%.0f,";
		const char *jfmt14 = "\"pps-frequency\":%.3f,\"stability\":%.3f,\"jitter\":%.3f,";
		const char *jfmt15 = "\"intervals\":%lu,\"jitter-exceeded\":%lu,\"stability-exceeded\":%lu,\"errors:%lu\n";

		flash = ntx.status;
		printf(json ? jfmt7 : ofmt7, status, timex_state(status));
		printf(json ? jfmt8 : ofmt8,
		       snprintb(sizeof(binbuf), binbuf, ntx.modes, TIMEX_MOD_BITS));
		ftemp = (double)ntx.offset/NS_PER_US_FLOAT;
		printf(json ? jfmt9 : ofmt9, ftemp);
		ftemp = FP_UNSCALE(ntx.freq);
		printf(json ? jfmt10 : ofmt10, ftemp, 1 << ntx.shift);
		printf(json ? jfmt11 : ofmt11,
		     (unsigned long)ntx.maxerror, (unsigned long)ntx.esterror);
		printf(json ? jfmt12 : ofmt12,
		       snprintb(sizeof(binbuf), binbuf,
			       (unsigned int)ntx.status, TIMEX_STA_BITS));
		ftemp = FP_UNSCALE(ntx.tolerance);
		/*
		 * Before the introduction of ntp_adjtime_ns() the
		 * ntptime code divided this by 1000 when the STA_NANO
		 * flash bit was on.  This doesn't match the Linux
		 * documentation; might have been an error, or
		 * possibly some other systems behave differently.
		 */
		gtemp = (double)ntx.precision;
		printf(json ? jfmt13 : ofmt13,
			(unsigned long)ntx.constant, gtemp, ftemp);
		if (ntx.shift != 0) {
		  ftemp = FP_UNSCALE(ntx.ppsfreq);
		  gtemp = FP_UNSCALE(ntx.stabil);
			htemp = (double)ntx.jitter/NS_PER_US_FLOAT;
			printf(json ? jfmt14 : ofmt14,
			    ftemp, gtemp, htemp);
			printf(json ? jfmt15 : ofmt15,
			    (unsigned long)ntx.calcnt, (unsigned long)ntx.jitcnt,
			    (unsigned long)ntx.stbcnt, (unsigned long)ntx.errcnt);
		}
		if (json)
		    /* hack to avoid trailing comma - not semantically needed */
		    fputs("\"version\":\""  NTPSEC_VERSION_STRING "\"}\n", stdout);
		exit(EXIT_SUCCESS);
	}

	/*
	 * Put things back together the way we found them.
	 */
    lexit:
#ifdef SIGSYS
	if (sigaction(SIGSYS, &sigsys, (struct sigaction *)NULL)) {
		perror("sigaction() fails to restore SIGSYS trap");
		exit(1);
	}
#endif
	if (json)
	    fputs("}\n", stdout);
	exit(status < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}

#ifdef SIGSYS
/*
 * pll_trap - trap processor for undefined syscalls
 */
void
pll_trap(
	int arg
	)
{
    UNUSED_ARG(arg);
	pll_control--;
	siglongjmp(env, 1);
}
#endif

/*
 * Print a value a la the %b format of the kernel's printf
 */
const char *
snprintb(
	size_t		buflen,
	char *		buf,
	unsigned int	v,
	const char *	bits
	)
{
	char *cp;
	char *cplim;
	int i;
	bool any;
	char c;

	if (bits != NULL && *bits == 8)
		snprintf(buf, buflen, "0%o", v);
	else
		snprintf(buf, buflen, "0x%x", v);
	cp = buf + strlen(buf);
	cplim = buf + buflen;
	if (bits != NULL) {
		bits++;
		*cp++ = ' ';
		*cp++ = '(';
		any = false;
		while ((i = *bits++) != 0) {
			if (v & (unsigned int)(1 << (i - 1))) {
				if (any) {
					*cp++ = ',';
					if (cp >= cplim)
						goto overrun;
				}
				any = true;
				for (; (c = *bits) > 32; bits++) {
					*cp++ = c;
					if (cp >= cplim)
						goto overrun;
				}
			} else {
				for (; *bits > 32; bits++)
					continue;
			}
		}
		*cp++ = ')';
		if (cp >= cplim)
			goto overrun;
	}
	*cp = '\0';
	return buf;

    overrun:
	return "snprintb buffer too small";
}

const char * const timex_states[] = {
	"OK", "INS", "DEL", "OOP", "WAIT", "ERROR"
};

const char *
timex_state(
	int s
	)
{
	static char buf[32];

	if ((size_t)s < COUNTOF(timex_states))
		return timex_states[s];
	snprintf(buf, sizeof(buf), "TIME-#%d", s);
	return buf;
}
ntpsec-1.1.0+dfsg1/ntptime/wscript0000644000175000017500000000077513252364117016734 0ustar  rlaagerrlaagerdef build(ctx):
    if ctx.env.HEADER_SYS_TIMEX_H:
        srcnode = ctx.srcnode.abspath()
        bldnode = ctx.bldnode.abspath()

        ctx(
            target="ntptime",
            features="c cprogram bld_include src_include",
            source=["ntptime.c"],
            includes=["%s/ntptime/" % bldnode,
                      "%s/ntptime/" % srcnode,
                      ],
            use="ntp M RT",
            install_path='${BINDIR}',
        )

    ctx.manpage(8, "ntptime-man.txt")

# end
ntpsec-1.1.0+dfsg1/VERSION0000644000175000017500000000000613252364142014667 0ustar  rlaagerrlaager1.1.0
ntpsec-1.1.0+dfsg1/ntpclients/0000775000175000017500000000000013252650651016013 5ustar  rlaagerrlaagerntpsec-1.1.0+dfsg1/ntpclients/ntptrace-man.txt0000644000175000017500000000062213252364117021142 0ustar  rlaagerrlaager= ntptrace(1) =
:doctype: manpage
:man source: NTPsec
:man version: @NTPSEC_VERSION@
:man manual: NTPsec

== NAME ==
ntptrace - trace peers of an NTP server

include::../docs/includes/ntptrace-body.txt[]

== EXIT STATUS ==

One of the following exit values will be returned:

0 (EXIT_SUCCESS)::
  Successful program execution.
1 (EXIT_FAILURE)::
  The operation failed or the invocation was not valid.
ntpsec-1.1.0+dfsg1/ntpclients/ntpviz-man.txt0000644000175000017500000000072513252364117020660 0ustar  rlaagerrlaager= ntpviz(1) =
:doctype: manpage
:man source: NTPsec
:man version: @NTPSEC_VERSION@
:man manual: NTPsec

== NAME ==
ntpviz - make visualizations of offset, jitter, etc. from stats file data

include::../docs/includes/ntpviz-body.txt[]

== EXIT STATUS ==

One of the following exit values will be returned:

0::
  Successful program execution.
1::
  The operation failed, usually due to a missing logfile required for
  a plot.
2::
  Illegal command-line option.

// end
ntpsec-1.1.0+dfsg1/ntpclients/ntpdig.py0000644000175000017500000004321213252364117017651 0ustar  rlaagerrlaager#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ntpdig - simple SNTP client

"""
# SPDX-License-Identifier: BSD-2-clause
# This code runs identically under Python 2 and Python 3. Keep it that way!
from __future__ import print_function, division

# This code is somewhat stripped down from the legacy C version.  It
# does however have one additional major feature; it can filter
# out falsetickers from multiple samples, like the ntpdate of old,
# rather than just taking the first reply it gets.
#
# Listening to broadcast addresses is not implemented because that is
# impossible to secure. KOD recording is also not implemented, as it
# can too easily be spammed.  Thus, the options -b and -K are not
# implemented.
#
# There are no version 3 NTP servers left, so the -o version for setting
# NTP version has been omitted.
#
# Because ntpdig doesn't use symmetric-peer mode (it never did, and NTPsec has
# abolished that mode because it was a security hazard), there's no need to
# set the packet source port, so -r/--usereservedport has been dropped.
# If this option ever needs to be reinstated, the magic is described here:
# http://stackoverflow.com/questions/2694212/socket-set-source-port-number
# and would be s.bind(('', 123)) right after the socket creation.
#
# The -w/--wait and -W/--nowait options only made sense with asynchronous
# DNS.  Asynchronous DNS was absurd overkill for this application, we are
# not looking up 20,000 hosts here.  It has not been implemented, so neither
# have these options.
#
# Finally, logging to syslog by default was a design error, violating
# Unix principles, that has been fixed. To get this behavior when
# running in a script, redirect standard error to logger(1).
#
# The one new option in this version is -p, borrowed from ntpdate.

import sys
import socket
import select
import time
import getopt
import math

try:
    import ntp.magic
    import ntp.packet
    import ntp.util
except ImportError as e:
    sys.stderr.write(
        "ntpdig: can't find Python NTP library -- check PYTHONPATH.\n")
    sys.stderr.write("%s\n" % e)
    sys.exit(1)


def read_append(s, packets, packet, sockaddr):
    d, a = s.recvfrom(1024)
    if debug >= 2:
        ntp.packet.dump_hex_printable(d)
    if credentials:
        if not ntp.packet.Authenticator.have_mac(d):
            if debug:
                log("no MAC on reply from %s" % packet.hostname)
        if not credentials.verify_mac(d):
            packet.trusted = False
            log("MAC verification on reply from %s failed"
                % sockaddr[0])
        elif debug:
            log("MAC verification on reply from %s succeeded"
                % sockaddr[0])
    pkt = ntp.packet.SyncPacket(d)
    pkt.hostname = server
    pkt.resolved = sockaddr[0]
    packets.append(pkt)
    return packets


def queryhost(server, concurrent, timeout=5, port=123):
    "Query IP addresses associated with a specified host."
    try:
        iptuples = socket.getaddrinfo(server, port,
                                      af, socket.SOCK_DGRAM,
                                      socket.IPPROTO_UDP)
    except socket.gaierror as e:
        log("lookup of %s failed, errno %d = %s" % (server, e[0], e[1]))
        return []
    sockets = []
    packets = []
    request = ntp.packet.SyncPacket()
    request.transmit_timestamp = ntp.packet.SyncPacket.posix_to_ntp(
        time.time())
    packet = request.flatten()
    needgap = (len(iptuples) > 1) and (gap > 0)
    firstloop = True
    for (family, socktype, proto, canonname, sockaddr) in iptuples:
        if needgap and not firstloop:
            time.sleep(gap)
        if firstloop:
            firstloop = False
        if debug:
            log("querying %s (%s)" % (sockaddr[0], server))
        s = socket.socket(family, socktype)
        if keyid and keytype and passwd:
            if debug:
                log("authenticating with %s key %d" % (keytype, keyid))
            mac = ntp.packet.Authenticator.compute_mac(packet,
                                                       keyid, keytype, passwd)
            if mac is None:
                log("MAC generation failed while querying %s" % server)
                raise SystemExit(1)
            else:
                packet += mac
        try:
            s.sendto(packet, sockaddr)
        except socket.error as e:
            log("socket error on transmission: %s" % e)
            raise SystemExit(1)
        if debug >= 2:
            log("Sent to %s:" % (sockaddr[0],))
            ntp.packet.dump_hex_printable(packet)
        if concurrent:
            sockets.append(s)
        else:
            r, _, _ = select.select([s], [], [], timeout)
            if not r:
                return []
            read_append(s, packets, packet, sockaddr)
        while sockets:
            r, _, _ = select.select(sockets, [], [], timeout)
            if not r:
                return packets
            for s in sockets:
                read_append(s, packets, packet, sockaddr)
                sockets.remove(s)
    return packets


def clock_select(packets):
    "Select the pick-of-the-litter clock from the samples we've got."
    # This is a slightly simplified version of the filter ntpdate used
    NTP_INFIN = 15          # max stratum, infinity a la Bellman-Ford

    # This first chunk of code is supposed to go through all
    # servers we know about to find the servers that
    # are most likely to succeed. We run through the list
    # doing the sanity checks and trying to insert anyone who
    # looks okay. We are at all times aware that we should
    # only keep samples from the top two strata.
    #
    filtered = []
    for response in packets:
        def drop(msg):
            log("%s: Response dropped: %s" % (response.hostname, msg))
        if response.stratum > NTP_INFIN:
            drop("stratum too high")
            continue
        if response.version() < ntp.magic.NTP_OLDVERSION:
            drop("response version %d is too old" % response.version())
            continue
        if response.mode() != ntp.magic.MODE_SERVER:
            drop("unexpected response mode %d" % response.mode())
            continue
        if response.version() > ntp.magic.NTP_VERSION:
            drop("response version %d is too new" % response.version())
            continue
        if response.stratum == 0:
            # FIXME: Do some kind of semi-useful diagnostic dump here
            drop("stratum 0, probable KOD packet")
            continue
        if response.leap() == "unsync":
            drop("leap not in sync")
            continue
        if not response.trusted:
            drop("request was authenticated but server is untrusted")
            continue
        # Bypass this test if we ever support broadcast-client mode again
        if response.origin_timestamp == 0:
            drop("unexpected response timestamp")
            continue
        filtered.append(response)

    if len(filtered) <= 1:
        return filtered

    # Sort by stratum and other figures of merit
    filtered.sort(key=lambda s: (s.stratum, s.synchd(), s.root_delay))

    # Return the best
    return filtered[:1]


def report(packet, json):
    "Report on the SNTP packet selected for display, and its adjustment."
    say = sys.stdout.write

    packet.posixize()

    if time.daylight:
        tmoffset = time.altzone // 60  # In minutes
    else:
        tmoffset = time.timezone // 60  # In minutes

    # The server's idea of the time
    t = time.localtime(int(packet.transmit_timestamp))
    ms = int(packet.transmit_timestamp * 1000000) % 1000000

    # Number of decimal digits of precision indicated by the precision field
    digits = min(6, -int(math.log10(2**packet.precision)))

    date = time.strftime("%Y-%m-%d", t)
    tod = time.strftime("%T", t) + (".%-*d" % (digits, ms)).rstrip()
    sgn = ("%+d" % tmoffset)[0]
    tz = "%s%02d%02d" % (sgn, abs(tmoffset) // 60, tmoffset % 60)

    if json:
        say('{"time":"%sT%s%s","offset":%f,"precision":%f,"host":"%s",'
            'ip:"%s","stratum":%s,"leap":"%s","adjusted":%s}\n'
            % (date, tod, tz,
               packet.adjust(), packet.synchd(),
               packet.hostname, packet.resolved or packet.hostname,
               packet.stratum, packet.leap(),
               "true" if adjusted else "false"))
    else:
        say("%s %s (%s) %+f +/- %f %s"
            % (date, tod, tz,
               packet.adjust(), packet.synchd(),
               packet.hostname))
        if packet.resolved and packet.resolved != packet.hostname:
            say(" " + packet.resolved)
        say(" s%d %s\n" % (packet.stratum, packet.leap()))

usage = """
USAGE:  ntpdig [- [] | --[{=| }]]...
                [ hostname-or-IP ...]
  Flg Arg Option-Name     Description
   -4 no  ipv4           Force IPv4 DNS name resolution
                                - prohibits the option 'ipv6'
   -6 no  ipv6           Force IPv6 DNS name resolution
                                - prohibits the option 'ipv4'
   -a Num authentication  Enable authentication with the numbered key
   -c yes concurrent      Hosts to be queried concurrently
   -d no  debug           Increase debug verbosity
   -D yes set-debug-level Set debug verbosity
   -g yes gap             Set gap between requests in miliseconds
   -j no  json            Use JSON output format
   -l Str logfile         Log to specified logfile
                                 - prohibits the option 'syslog'
   -p yes samples         Number of samples to take (default 1)
   -S no  step            Set (step) the time with clock_settime()
                                 - prohibits the option 'step'
   -s no  slew            Set (slew) the time with adjtime()
                                 - prohibits the option 'slew'
   -t Num timeout         Request timeout in seconds (default 5)
   -k Str keyfile         Specify a keyfile. ntpdig will look in this file
                          for the key specified with -a
   -V no version          Output version information and exit
   -h no  help            Display extended usage information and exit
"""

if __name__ == '__main__':
    try:
        try:
            (options, arguments) = getopt.getopt(
                sys.argv[1:],
                "46a:c:dD:g:hjk:l:M:o:p:r:Sst:wWV",
                ["ipv4", "ipv6",
                 "authentication=",
                 "concurrent=",
                 "gap=", "help", "json",
                 "keyfile=", "logfile=",
                 "replay=",
                 "samples=", "steplimit=",
                 "step", "slew",
                 "timeout=",
                 "debug", "set-debug-level=",
                 "version"])
        except getopt.GetoptError as e:
            print(e)
            raise SystemExit(1)
        progname = sys.argv[0]

        logfp = sys.stderr
        log = lambda m: logfp.write("ntpdig: %s\n" % m)

        af = socket.AF_UNSPEC
        authkey = None
        concurrent_hosts = []
        debug = 0
        gap = .05
        json = False
        keyfile = None
        steplimit = 0       # Default is intentionally zero
        samples = 1
        step = False
        slew = False
        timeout = 5
        replay = None
        try:
            for (switch, val) in options:
                if switch in ("-4", "--ipv4"):
                    af = socket.AF_INET
                elif switch in ("-6", "--ipv6"):
                    af = socket.AF_INET6
                elif switch in ("-a", "--authentication"):
                    errmsg = "Error: -a parameter '%s' not a number\n"
                    authkey = ntp.util.safeargcast(val, int, errmsg, usage)
                elif switch in ("-c", "--concurrent"):
                    concurrent_hosts.append(val)
                elif switch in ("-d", "--debug"):
                    debug += 1
                elif switch in ("-D", "--set-debug-level"):
                    errmsg = "Error: -D parameter '%s' not a number\n"
                    debug = ntp.util.safeargcast(val, int, errmsg, usage)
                elif switch in ("-g", "--gap"):
                    errmsg = "Error: -g parameter '%s' not a number\n"
                    gap = ntp.util.safeargcast(val, int, errmsg, usage)
                elif switch in ("-j", "--json"):
                    json = True
                elif switch in ("-k", "--keyfile"):
                    keyfile = val
                elif switch in ("-l", "--logfile"):
                    try:
                        logfp = open(val, "w")
                    except OSError:
                        sys.stderr.write("logfile open of %s failed.\n" % val)
                        raise SystemExit(1)
                elif switch in ("-M", "--steplimit"):
                    errmsg = "Error: -M parameter '%s' not a number\n"
                    steplimit = ntp.util.safeargcast(val, int, errmsg, usage)
                elif switch in ("-p", "--samples"):
                    errmsg = "Error: -p parameter '%s' not a number\n"
                    samples = ntp.util.safeargcast(val, int, errmsg, usage)
                    if samples < 1:  # If <1 it won't run at all
                        samples = 1
                elif switch in ('-r', "--replay"):
                    replay = val
                elif switch in ("-S", "--step"):
                    step = True
                elif switch in ("-s", "--slew"):
                    slew = True
                elif switch in ("-t", "--timeout"):
                    errmsg = "Error: -t parameter '%s' not a number\n"
                    timeout = ntp.util.safeargcast(val, int, errmsg, usage)
                elif switch in ("-h", "--help"):
                    print(usage)
                    raise SystemExit(0)
                elif switch in ("-V", "--version"):
                    print("ntpdig %s" % ntp.util.stdversion())
                    raise SystemExit(0)
                else:
                    sys.stderr.write(
                        "Unknown command line switch or missing argument.\n")
                    sys.stderr.write(usage)
                    raise SystemExit(1)
        except ValueError:
            sys.stderr.write("Invalid argument.\n")
            sys.stderr.write(usage)
            raise SystemExit(1)

        gap /= 1000  # convert to milliseconds

        credentials = keyid = keytype = passwd = None
        try:
            credentials = ntp.packet.Authenticator(keyfile)
        except (OSError, IOError):
            sys.stderr.write("ntpdig: %s nonexistent or unreadable\n" % keyfile)
            raise SystemExit(1)
        if credentials:
            try:
                (keyid, keytype, passwd) = credentials.control(authkey)
            except ValueError:
                # There are no trusted keys.  Barf.
                log("cannot get authentication key")
                raise SystemExit(1)

        if not credentials and authkey and keyfile is None:
            sys.stderr.write("-a option requires -k.\n")
            raise SystemExit(1)

        if not arguments:
            arguments = ["localhost"]

        if replay:
            (pkt, dst) = replay.split(":")
            packet = ntp.packet.SyncPacket(pkt.decode("hex"))
            packet.received = ntp.packet.SyncPacket.posix_to_ntp(float(dst))
            returned = [packet]
        else:
            returned = []
            needgap = (samples > 1) and (gap > 0)
            firstloop = True
            for s in range(samples):
                if needgap and not firstloop:
                    time.sleep(gap)
                if firstloop is True:
                    firstloop = False
                for server in concurrent_hosts:
                    try:
                        returned += queryhost(server=server,
                                              concurrent=True,
                                              timeout=timeout)
                    except ntp.packet.SyncException as e:
                        log(str(e))
                        continue
                for server in arguments:
                    try:
                        returned += queryhost(server=server,
                                              concurrent=False,
                                              timeout=timeout)
                    except ntp.packet.SyncException as e:
                        log(str(e))
                        continue

            returned = clock_select(returned)

        if returned:
            pkt = returned[0]
            if debug:
                # print(repr(pkt))
                def hexstamp(n):
                    return "%08x.%08x" % (n >> 32, n & 0x00000000ffffffff)
                print("org t1: %s rec t2: %s"
                      % (hexstamp(pkt.t1()), hexstamp(pkt.t2())))
                print("xmt t3: %s dst t4: %s"
                      % (hexstamp(pkt.t3()), hexstamp(pkt.t4())))
            pkt.posixize()
            if debug:
                print("org t1: %f rec t2: %f" % (pkt.t1(), pkt.t2()))
                print("xmt t3: %f dst t4: %f" % (pkt.t3(), pkt.t4()))
                print("rec-org t21: %f  xmt-dst t34: %f"
                      % (pkt.t2() - pkt.t1(), pkt.t3() - pkt.t4()))
            offset = pkt.adjust()
            adjusted = (step and
                        (not slew or (slew and (abs(offset) > steplimit))))
            report(pkt, json)
            # If we can step but we cannot slew, then step.
            # If we can step or slew and |offset| > steplimit, then step.
            rc = True
            ntp.ntpc.setprogname("ntpdig")
            if adjusted:
                rc = ntp.ntpc.step_systime(offset)
            elif slew:
                rc = ntp.ntpc.adj_systime(offset)
            if rc:
                raise SystemExit(0)
            else:
                raise SystemExit(1)
        else:
            log("no eligible servers")
            raise SystemExit(1)
    except KeyboardInterrupt:
        print("")

# end
ntpsec-1.1.0+dfsg1/ntpclients/ntpkeygen-man.txt0000644000175000017500000000064513252364117021333 0ustar  rlaagerrlaager= ntpkeygen(8) =
:doctype: manpage
:man source: NTPsec
:man version: @NTPSEC_VERSION@
:man manual: NTPsec

== NAME ==
ntpkeygen - create and manage NTP host keys

include::../docs/includes/ntpkeygen-body.txt[]

== EXIT STATUS ==

One of the following exit values will be returned:

0 (EXIT_SUCCESS)::
  Successful program execution.
1 (EXIT_FAILURE)::
  The operation failed or the command syntax was not valid.

// end

ntpsec-1.1.0+dfsg1/ntpclients/ntplogtemp-man.txt0000644000175000017500000000063513252364117021517 0ustar  rlaagerrlaager= ntplogtemp(1) =
:doctype: manpage
:man source: NTPsec
:man version: @NTPSEC_VERSION@
:man manual: NTPsec

== NAME ==
ntplogtemp - log temperature data

include::../docs/includes/ntplogtemp-body.txt[]

== EXIT STATUS ==

One of the following exit values will be returned:

0 (EXIT_SUCCESS)::
  Successful program execution.
1 (EXIT_FAILURE)::
  The operation failed or the command syntax was not valid.

// end

ntpsec-1.1.0+dfsg1/ntpclients/ntpleapfetch0000755000175000017500000003023313252364117020414 0ustar  rlaagerrlaager#!/bin/bash

# Copyright (C) 2014 Timothe Litt litt at acm dot org
# Modified 20180105 Sanjeev Gupta ghane0@gmail.com
#
# SPDX-License-Identifier: BSD-2-clause
#
# Bugfixes and improvements would be appreciated by the author.

VERSION="1.003"

# leap-seconds file manager/updater

# Depends on:
#  wget sed, tr, shasum/sha1sum, logger

# ########## Default configuration ##########
#
# Where to get the file
LEAPSRC="ftp://ftp.nist.gov/pub/time/leap-seconds.list"

# How many times to try to download new file
MAXTRIES=6
INTERVAL=10

# Where to find ntp config file
NTPCONF=/etc/ntp.conf

# How long before expiration to get updated file
PREFETCH="60 days"

# How to restart NTP - older NTP: service ntpd? try-restart | condrestart
# Recent NTP checks for new file daily, so there's nothing to do
RESTART=

# Where to put temporary copy before it's validated
TMPFILE="/tmp/leap-seconds.$$.tmp"

# Syslog facility
LOGFAC=daemon
# ###########################################

# Places to look for commands.  Allows for CRON having path to
# old utilities on embedded systems

PATHLIST="/opt/sbin:/opt/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:"

REQUIREDCMDS=" wget logger tr sed"

SELF="`basename $0`"

function displayHelp {
            cat < - ntpd checks file daily
    -r    Specify number of times to retry on get failure
          $MAXTRIES
    -i    Specify number of minutes between retries
          $INTERVAL
    -l    Use syslog for output (Implied if CRONJOB is set)
    -L    Don't use syslog for output
    -P    Specify the syslog facility for logging
          $LOGFAC
    -t    Name of temporary file used in validation
    -q    Only report errors to stdout
    -v    Verbose output
    -z    Specify path for utilities
          $PATHLIST
    -Z    Only use system path

$SELF will validate the file currently on the local system

Ordinarily, the file is found using the "leapfile" directive in $NTPCONF.
However, an alternate location can be specified on the command line.

If the file does not exist, is not valid, has expired, or is expiring soon,
a new copy will be downloaded.  If the new copy validates, it is installed and
NTP is (optionally) restarted.

If the current file is acceptable, no download or restart occurs.

-c can also be used to invoke another script to perform administrative
functions, e.g. to copy the file to other local systems.

This can be run as a cron job.  As the file is rarely updated, and leap
seconds are announced at least one month in advance (usually longer), it
need not be run more frequently than about once every three weeks.

For cron-friendly behavior, define CRONJOB=1 in the crontab.

This script depends on: sha1sum/shasum $REQUIREDCMDS

Version $VERSION
EOF
   return 0
}

# Default: Use syslog for logging if running under cron

SYSLOG="$CRONJOB"

if [ "$1" = "--help" ]; then
    displayHelp
    exit 0
fi

# Parse options

while getopts 46p:P:s:e:f:Fc:r:i:lLt:hqvz:Z opt; do
    case $opt in
        4)
            PROTO="-4"
            ;;
        6)
            PROTO="-6"
            ;;
        p)
            if [ "$OPTARG" = '4' -o "$OPTARG" = '6' ]; then
                PREFER="--prefer-family=IPv$OPTARG"
            else
                echo "Invalid -p $OPTARG" >&2
                exit 1;
            fi
            ;;
	P)
	    LOGFAC="$OPTARG"
	    ;;
        s)
            LEAPSRC="$OPTARG"
            ;;
        e)
            PREFETCH="$OPTARG"
            ;;
	f)
	    NTPCONF="$OPTARG"
	    ;;
        F)
            FORCE="Y"
            ;;
        c)
            RESTART="$OPTARG"
            ;;
        r)
            MAXTRIES="$OPTARG"
            ;;
        i)
            INTERVAL="$OPTARG"
            ;;
        t)
            TMPFILE="$OPTARG"
            ;;
	l)
	    SYSLOG="y"
	    ;;
	L)
	    SYSLOG=
	    ;;
        h)
            displayHelp
            exit 0
            ;;
	q)
	    QUIET="Y"
	    ;;
        v)
            VERBOSE="Y"
            ;;
	z)
	    PATHLIST="$OPTARG:"
	    ;;
	Z)
	    PATHLIST=
	    ;;
        *)
            echo "$SELF -h for usage" >&2
            exit 1
            ;;
    esac
done
shift $((OPTIND-1))

export PATH="$PATHLIST$PATH"

# Add to path to deal with embedded systems
#
for P in $REQUIREDCMDS ; do
    if >/dev/null 2>&1 which "$P" ; then
	continue
    fi
    [ "$P" = "logger" ] && continue
    echo "FATAL: missing $P command, please install"
    exit 1
done

# find sha1sum or shasum
if >/dev/null 2>&1 which "sha1sum" ; then
    SHASUM="sha1sum"
elif >/dev/null 2>&1 which "shasum" ; then
    SHASUM="shasum"
else
    echo "FATAL: Can not find sha1sum or shasum command, please install"
    exit 1
fi

# Handle logging

if ! LOGGER="`2>/dev/null which logger`" ; then
    LOGGER=
fi

function log {
    # "priority" "message"
    #
    # Stdout unless syslog specified or logger isn't available
    #
    if [ -z "$SYSLOG" -o -z "$LOGGER" ]; then
	if [ -n "$QUIET" -a \( "$1" = "info" -o "$1" = "notice" -o "$1" = "debug" \) ]; then
	    return 0
	fi
	echo "`echo \"$1\" | tr a-z A-Z`: $2"
	return 0
    fi

    # Also log to stdout if cron job && notice or higher
    local S
    if [ -n "$CRONJOB" -a \( "$1" != "info" \) -a \( "$1" != "debug" \) ] || [ -n "$VERBOSE" ]; then
	S="-s"
    fi
    $LOGGER $S -t "$SELF[$$]" -p "$LOGFAC.$1" "$2"
}

# Verify interval
INTERVAL=$(( $INTERVAL *1 ))

# Validate a leap-seconds file checksum
#
# File format: (full description in files)
# # marks comments, except:
# #$ number : the NTP date of the last update
# #@ number : the NTP date that the file expires
# Date (seconds since 1900) leaps : leaps is the # of seconds to add for times >= Date
# Date lines have comments.
# #h hex hex hex hex hex is the SHA1 checksum of the data & dates, excluding whitespace w/o leading zeroes

function verifySHA1 {

    if [ ! -f "$1" ]; then
        return 1
    fi

    # Remove comments, except those that are markers for last update, expires and hash

    local RAW="`sed $1 -e'/^\\([0-9]\\|#[\$@h]\)/!d' -e'/^#[\$@h]/!s/#.*\$//g'`"

    # Extract just the data, removing all whitespace

    local DATA="`echo \"$RAW\" | sed -e'/^#h/d' -e's/^#[\$@]//g' | tr -d '[:space:]'`"

    # Compute the SHA1 hash of the data, removing the marker and filename
    # Computed in binary mode, which shouldn't matter since whitespace
    # has been removed
    # shasum/sha1sum comes in several flavors;
    # a portable one is available in Perl (with Digest::SHA)

    local DSHA1="`echo -n \"$DATA\" | $SHASUM | sed -e's/[? *].*$//'`"

    # Extract the file's hash. Restore any leading zeroes in hash segments.

    # The sed [] includes a tab (\t) and space; #h is followed by a tab and space
    # The file of 8-Jul-2016 uses \r\n as line delimiters.  This must
    # be corrected on UNIX systems, else the extra \r is read from the
    # last line.  I am not clear if this is an error in the NIST file,
    # or intentional.  Nevertheless - Sanjeev Gupta 20180105
    #
    local FSHA1="`echo \"$RAW\" | sed -e's/\r//' | sed -e'/^#h/!d' -e's/^#h//' -e's/[ 	] */ 0x/g'`"
    FSHA1=`printf '%08x%08x%08x%08x%08x' $FSHA1`

    if [ -n "$FSHA1" -a \( "$FSHA1" = "$DSHA1" \) ]; then
        if [ -n "$2" ]; then
            log "info" "Checksum of $1 validated"
        fi
    else
        log "error" "Checksum of $1 is invalid:"
	[ -z "$FSHA1" ] && FSHA1="(no checksum record found in file)"
        log "error" "EXPECTED: $FSHA1"
        log "error" "COMPUTED: $DSHA1"
        return 1
    fi

    # Check the expiration date, converting NTP epoch to Unix epoch used by date

    EXPIRES="`echo \"$RAW\" | sed -e'/^#@/!d' -e's/^#@//' | tr -d '[:space:]'`"
    EXPIRES="$(($EXPIRES - 2208988800 ))"

    if [ $EXPIRES -lt `date -u +%s` ]; then
        log "notice" "File expired on `date -u -d \"Jan 1, 1970 00:00:00 +0000 + $EXPIRES seconds\"`"
        return 2
    fi

}

# Verify ntp.conf

if ! [ -f "$NTPCONF" ]; then
    log "critical" "Missing ntp configuration $NTPCONF"
    exit 1
fi

# Parse ntp.conf for leapfile directive
# FIXME broken on MacOS/macports
LEAPFILE="`sed $NTPCONF -e'/^ *leapfile  *.*$/!d' -e's/^ *leapfile  *//'`"
if [ -z "$LEAPFILE" ]; then
    log "warning" "$NTPCONF does not specify a leapfile"
fi

# Allow placing the file someplace else - testing

if [ -n "$1" ]; then
    if [ "$1" != "$LEAPFILE" ]; then
	log "notice" "Requested install to $1, but $NTPCONF specifies $LEAPFILE"
    fi
    LEAPFILE="$1"
fi

# Verify the current file
# If it is missing, doesn't validate or expired
# Or is expiring soon
#  Download a new one

if [ -n "$FORCE" ] || ! verifySHA1 $LEAPFILE "$VERBOSE" || [ $EXPIRES -lt `date -d "NOW + $PREFETCH" +%s` ] ; then
    TRY=0
    while true; do
        TRY=$(( $TRY + 1 ))
        if [ -n "$VERBOSE" ]; then
            log "info" "Attempting download from $LEAPSRC, try $TRY.."
        fi
        if wget -T 10 $PROTO $PREFER -o ${TMPFILE}.log $LEAPSRC -O $TMPFILE ; then
            log "info" "Download of $LEAPSRC succeeded"
            if [ -n "$VERBOSE" ]; then
                cat ${TMPFILE}.log
            fi

            if ! verifySHA1 $TMPFILE "$VERBOSE" ; then
		# There is no point in retrying, as the file on the server is almost
		# certainly corrupt.

                log "warning" "Downloaded file $TMPFILE rejected -- saved for diagnosis"
                cat ${TMPFILE}.log
                rm -f ${TMPFILE}.log
                exit 1
            fi
            rm -f ${TMPFILE}.log

	    # Set correct permissions on temporary file

	    REFFILE="$LEAPFILE"
            if [ ! -f $LEAPFILE ]; then
		log "notice" "$LEAPFILE was missing, creating new copy - check permissions"
                touch $LEAPFILE
		# Can't copy permissions from old file,
                # copy from NTPCONF instead
		REFFILE="$NTPCONF"
            fi
            chmod --reference=$REFFILE $TMPFILE
            chown --reference=$REFFILE $TMPFILE
	    ( which selinuxenabled && selinuxenabled && which chcon ) >/dev/null 2>&1
            if  [ $? == 0 ] ; then
                chcon --reference $REFFILE $TMPFILE
            fi

	    # Replace current file with validated new one

            if mv -f $TMPFILE $LEAPFILE ; then
                log "notice" "Installed new $LEAPFILE from $LEAPSRC"
            else
                log "error" "Install $TMPFILE => $LEAPFILE failed -- saved for diagnosis"
                exit 1
            fi

	    # Restart NTP (or whatever else is specified)

	    if [ -n "$RESTART" ]; then
		if [ -n "$VERBOSE" ]; then
		    log "info" "Attempting restart action: $RESTART"
		fi
		R="$( 2>&1 $RESTART )"
		if [ $? -eq 0 ]; then
		    log "notice" "Restart action succeeded"
		    if [ -n "$VERBOSE" -a -n "$R" ]; then
			log "info" "$R"
		    fi
		else
		    log "error" "Restart action failed"
		    if [ -n "$R" ]; then
			log "error" "$R"
		    fi
		    exit 2
		fi
	    fi
            exit 0
	fi

	# Failed to download.  See about trying again

        rm -f $TMPFILE
        if [ $TRY -ge $MAXTRIES ]; then
            break;
        fi
        if [ -n "$VERBOSE" ]; then
            cat ${TMPFILE}.log
            log "info" "Waiting $INTERVAL minutes before retrying..."
        fi
        sleep $(( $INTERVAL * 60))
    done

    # Failed and out of retries

    log "warning" "Download from $LEAPSRC failed after $TRY attempts"
    if [ -f ${TMPFILE}.log ]; then
        cat ${TMPFILE}.log
        rm -f ${TMPFILE}.log $TMPFILE
    fi
    exit 1
fi
log "info" "Not time to replace $LEAPFILE"

exit 0

# EOF
ntpsec-1.1.0+dfsg1/ntpclients/ntpviz.py0000644000175000017500000021357213252364117017726 0ustar  rlaagerrlaager#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""\
ntpviz - logfile visualizer for NTP log files

ntpviz [-d LOGDIR] [-g] [-n name] [-p DAYS]
         [-s starttime] [-e endtime]
         [-o OUTDIR]
         [-c | --clip]
         [-w SIZE | --width SIZE]
         [--all-peer-jitters |
          --all-peer-offsets |
          --local-error |
          --local-freq-temps |
          --local-gps |
          --local-jitter |
          --local-offset |
          --local-offset-histogram |
          --local-offset-multiplot |
          --local-stability |
          --local-temps |
          --peer-jitters=hosts |
          --peer-offsets=hosts |
         ]
         [-D DLVL | --debug DLVL]
         [-N | --nice]
         [-V | --version]
         [@OPTIONFILE]

See the manual page for details.

Python by ESR, concept and gnuplot code by Dan Drown.
"""
# SPDX-License-Identifier: BSD-2-Clause
from __future__ import print_function, division

import atexit
import binascii
import collections
import csv
import datetime
import math
import re
import os
import socket
import sys
import subprocess
import tempfile
try:
    import argparse
except ImportError:
    sys.stderr.write("""
ntpviz: can't find the Python argparse module
        If your Python version is < 2.7, then manual installation is needed:
        # pip install argparse
""")
    sys.exit(1)

if sys.version_info[0] == 2:
    import codecs
    import sys

    # force UTF-8 strings, otherwise some systems crash on micro.
    reload(sys)
    sys.setdefaultencoding('utf8')

    def open(file, mode='r', buffering=-1, encoding=None, errors=None):
        return codecs.open(filename=file, mode=mode, encoding=encoding,
            errors=errors, buffering=buffering)

# believe it or not, Python has no way to make a simple constant!
MS_PER_S = 1e3          # milliseconds per second
NS_PER_S = 1e9          # nanoseconds per second
US_PER_S = 1e6          # microseconds per second
S_PER_MS = 1.0e-3       # seconds per millisecond
S_PER_NS = 1.0e-9       # seconds per nanosecond
S_PER_US = 1.0e-6       # seconds per microsecond

# table to translate refclock names
refclock_name = {'127.127.20.0': 'NMEA(0)',
                 '127.127.20.1': 'NMEA(1)',
                 '127.127.20.2': 'NMEA(2)',
                 '127.127.20.3': 'NMEA(3)',
                 '127.127.22.0': 'PPS(0)',
                 '127.127.22.1': 'PPS(1)',
                 '127.127.22.2': 'PPS(2)',
                 '127.127.22.3': 'PPS(3)',
                 '127.127.28.0': 'SHM(0)',
                 '127.127.28.1': 'SHM(1)',
                 '127.127.28.2': 'SHM(2)',
                 '127.127.28.3': 'SHM(3)',
                 '127.127.46.0': 'GPS(0)',
                 '127.127.46.1': 'GPS(1)',
                 '127.127.46.2': 'GPS(2)',
                 '127.127.46.3': 'GPS(3)'}


# Gack, python before 3.2 has no defined tzinfo for utc...
# define our own
class UTC(datetime.tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return datetime.timedelta(0)

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return datetime.timedelta(0)

try:
    import ntp.statfiles
    import ntp.util
except ImportError as e:
    sys.stderr.write(
        "ntpviz: can't find Python NTP library.\n")
    sys.stderr.write("%s\n" % e)
    sys.exit(1)

# check Python version
Python26 = False
if ((3 > sys.version_info[0]) and (7 > sys.version_info[1])):
    # running under Python version before 2.7
    Python26 = True


# overload ArgumentParser
class MyArgumentParser(argparse.ArgumentParser):

    def convert_arg_line_to_args(self, arg_line):
        '''Make options file more tolerant'''
        # strip out trailing comments
        arg_line = re.sub('\s+#.*$', '', arg_line)

        # ignore blank lines
        if 0 == len(arg_line):
            return []
        # ignore comment lines
        if '#' == arg_line[0]:
            return []

        return arg_line.split()


def print_profile():
    """called by atexit() on normal exit to print profile data"""
    pr.disable()
    pr.print_stats('tottime')
    pr.print_stats('cumtime')


# standard deviation class
# use this until we can guarantee Python 3.4 and the statistics module
# http://stackoverflow.com/questions/15389768/standard-deviation-of-a-list#21505523

# class to calc:
#   Mean, Variance, Standard Deviation, Skewness and Kurtosis

class RunningStats:
    "Calculate mean, variance, sigma, skewness and kurtosis"

    def __init__(self, values):
        self.num = len(values)     # number of samples
        self.mu = 0.0              # simple arithmetic mean
        self.variance = 0.0        # variance
        self.sigma = 0.0           # aka standard deviation
        self.skewness = 0.0
        self.kurtosis = 3.0

        if 0 >= self.num:
            # no data??
            return

        self.mu = sum(values) / self.num
        self.variance = sum(pow((v-self.mu), 2) for v in values) / self.num
        self.sigma = math.sqrt(self.variance)

        if math.isnan(self.sigma) or 1e-12 >= abs(self.sigma):
            # punt
            self.skewness = float('nan')
            self.kurtosis = float('nan')
            return

        m3 = 0
        m4 = 0
        for val in values:
            m3 += pow(val - self.sigma, 3)
            m4 += pow(val - self.sigma, 4)

        self.skewness = m3 / (self.num * pow(self.sigma, 3))
        self.kurtosis = m4 / (self.num * pow(self.sigma, 4))

# end standard deviation class


# class for calced values
class VizStats(ntp.statfiles.NTPStats):
    percs = {}          # dictionary of percentages
    title = ''          # title
    unit = 's'          # display units: s, ppm, etc.
    skip_summary = False
    clipped = False
    multiplier = 1

    # observe RFC 4180, end lines with CRLF
    csv_head = ["Name", "Min", "1%", "5%", "50%", "95%", "99%", "Max", "",
                "90% Range", "98% Range", "StdDev", "", "Mean", "Units",
                "Skewness", "Kurtosis"]

    table_head = """\

""" table_tail = """\
Percentiles...... Ranges...... Skew- Kurt-
Name Min1%5%50%95% 99%Max   90%95%StdDev  MeanUnits nessosis
""" def __init__(self, values, title, freq=0, units=''): values.sort() self.percs = self.percentiles((100, 99, 95, 50, 5, 1, 0), values) # find the target for autoranging if args.clip: # keep 99% and 1% under 999 in selected units # clip to 1% and 99% target = max(self.percs["p99"], -self.percs["p1"]) else: # keep 99% and 1% under 999 in selected units # but do not let 100% and 1% go over 5000 in selected units target = max(self.percs["p99"], -self.percs["p1"], self.percs["p100"]/5, -self.percs["p0"]/5) if len(units): # fixed scale self.multiplier = 1 self.unit = units elif 1 <= target: self.multiplier = 1 if freq: # go to ppm self.unit = "ppm" else: # go to seconds self.unit = "s" elif S_PER_MS <= target: self.multiplier = MS_PER_S if freq: # go to ppb self.unit = "ppb" else: # go to millisec self.unit = "ms" elif S_PER_US <= target: self.multiplier = US_PER_S if freq: self.unit = "10e-12" else: # go to microsec self.unit = "µs" else: self.multiplier = NS_PER_S if freq: self.unit = "10e-15" else: # go to nanosec self.unit = "ns" sts = RunningStats(values) self.percs["mu"] = sts.mu self.percs["pstd"] = sts.sigma self.title = title # calculate ranges self.percs["r90"] = self.percs["p95"] - self.percs["p5"] self.percs["r98"] = self.percs["p99"] - self.percs["p1"] # calculate mean +/- std dev self.percs["m1sigma"] = self.percs["mu"] - self.percs["pstd"] self.percs["p1sigma"] = self.percs["mu"] + self.percs["pstd"] # pretty print the values self.percs_f = {} for k, v in self.percs.items(): # range the data v *= self.multiplier self.percs[k] = round(v, 4) if 'ppm' == self.unit and 0.020 > abs(self.percs[k]): fmt = ".4f" else: fmt = ".3f" if not Python26: # Python 2.6 does not undertand the comma format option fmt = "," + fmt self.percs_f[k] = format(v, fmt) # don't scale skewness and kurtosis self.percs["skew"] = sts.skewness self.percs["kurt"] = sts.kurtosis if '°C' == units: # skip for temperatures. self.percs_f["skew"] = '' self.percs_f["kurt"] = '' else: self.percs_f["skew"] = format(self.percs["skew"], "6.4g") self.percs_f["kurt"] = format(self.percs["kurt"], "6.4g") if args.clip: self.percs["min_y"] = self.percs["p1"] self.percs["max_y"] = self.percs["p99"] self.percs["clipped"] = " (clipped)" else: self.percs["min_y"] = self.percs["p0"] self.percs["max_y"] = self.percs["p100"] self.percs["clipped"] = "" self.fmt = gnuplot_fmt(self.percs["min_y"], self.percs["max_y"]) # Python is stupid about nested objects, so add in some other stuff self.percs_f["fmt"] = self.percs["fmt"] = self.fmt self.percs_f["multiplier"] = self.percs["multiplier"] = self.multiplier self.percs_f["title"] = self.percs["title"] = self.title self.percs_f["unit"] = self.percs["unit"] = self.unit s = ["%(title)s", "%(p0)s", "%(p1)s", "%(p5)s", "%(p50)s", "%(p95)s", " %(p99)s", "%(p100)s", "", "%(r90)s", "%(r98)s", "%(pstd)s", "", "%(mu)s", "%(unit)s", "%(skew)s", "%(kurt)s", ] # csv is raw, html table is autoranged self.csv = [x % self.percs for x in s] self.table = [x % self.percs_f for x in s] self.table = "".join(self.table) self.table = '''\ %s ''' % self.table return def gnuplot_fmt(min, max): "return optimal gnuplot format" span = max - min if 6 <= span: fmt = '%.0f' elif 0.6 <= span: fmt = '%.1f' elif 0.1 <= span: fmt = '%.2f' else: fmt = '%.3f' return fmt # end calc things now # RMS frequency jitter - Deviation from root-mean-square linear approximation? # Investigate. def gnuplot(template, outfile=None): "Run a specified gnuplot program." if not len(template): # silently ignore empty plots return '' if outfile is None: out = None else: out = open(outfile, "w", encoding='utf-8') ## # can be 30% faster to write to a tmp file than to pipe to gnuplot # bonus, we can keep the plot file for debug. if sys.version_info[0] == 2: tmp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.plt', delete=False) else: tmp_file = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.plt', delete=False) # note that tmp_file is a file handle, it is not a file object tmp_file.write(template) tmp_file.close() # shell=True is a security hazard, do not use try: rcode = subprocess.call(['gnuplot', tmp_file.name], stdout=out) except OSError as e: if e.errno == os.errno.ENOENT: # gnuplot not found sys.stderr.write("ntpviz: ERROR: gnuplot not found in path\n") else: # Something else went wrong while trying to run gnuplot sys.stderr.write("ntpviz: ERROR: gnuplot failed\n") raise SystemExit(1) if 0 != rcode: sys.stderr.write("ntpviz: WARNING: plot returned %s\n" % rcode) sys.stderr.write("ntpviz: WARNING: plot file %s\n" % tmp_file.name) elif 2 <= args.debug_level: sys.stderr.write("ntpviz: INFO: plot file %s\n" % tmp_file.name) else: # remove tmp file os.remove(tmp_file.name) return rcode class NTPViz(ntp.statfiles.NTPStats): "Class for visualizing statistics from a single server." # Python takes single quotes here. Since no % substitution Common = """\ set grid set autoscale xfixmin set autoscale xfixmax set xdata time set xlabel "Time UTC" set xtics format "%d %b\\n%H:%MZ" set timefmt "%s" set lmargin 10 set rmargin 10 """ def __init__(self, statsdir, sitename=None, period=None, starttime=None, endtime=None): ntp.statfiles.NTPStats.__init__(self, statsdir=statsdir, sitename=sitename, period=period, starttime=starttime, endtime=endtime) def plot_slice(self, rows, item1, item2=None): "slice 0,item1, maybe item2, from rows, ready for gnuplot" # speed up by only sending gnuplot the data it will actually use # WARNING: this is hot code, only modify if you profile # since we are looping the data, get the values too plot_data = '' last_time = 0 values1 = [] values2 = [] if item2: for row in rows: try: values1.append(float(row[item1])) values2.append(float(row[item2])) if 2200000 < row[0] - last_time: # more than 2,200 seconds between points # data loss, add a break in the plot line plot_data += '\n' # fields: time, fld1, and fld2 plot_data += row[1] + ' ' + row[item1] + ' ' \ + row[item2] + '\n' last_time = row[0] except IndexError: pass else: for row in rows: try: values1.append(float(row[item1])) if 2200000 < row[0] - last_time: # more than 2,200 seconds between points # data loss, add a break in the plot line plot_data += '\n' # fields: time, fld plot_data += row[1] + ' ' + row[item1] + '\n' last_time = row[0] except IndexError: pass # I know you want to replace the plot_data string concat with # or more join()s, do not do it, it is slower # next you'll want to try %-substitution. it too is slower plot_data += "e\n" if item2: return (plot_data, values1, values2) return (plot_data, values1) def local_offset_gnuplot(self): "Generate gnuplot code graphing local clock loop statistics" if not len(self.loopstats): sys.stderr.write("ntpviz: WARNING: no loopstats to graph\n") return '' # speed up by only sending gnuplot the data it will actually use # fields: time, time offset, freq offset (plot_data, values, values_f) = self.plot_slice(self.loopstats, 2, 3) # compute clock offset stats = VizStats(values, "Local Clock Time Offset") # compute frequency offset stats_f = VizStats(values_f, "Local Clock Frequency Offset", freq=1) out = stats.percs out["fmt"] = stats.percs["fmt"] out["min_y2"] = stats_f.percs["min_y"] out["max_y2"] = stats_f.percs["max_y"] out["unit_f"] = stats_f.percs["unit"] out["fmt_f"] = stats_f.percs["fmt"] out["multiplier_f"] = stats_f.percs["multiplier"] out["sitename"] = self.sitename out['size'] = args.png_size plot_template = NTPViz.Common + """\ set terminal png size %(size)s set title "%(sitename)s: Local Clock Time/Frequency Offsets%(clipped)s" set ytics format "%(fmt)s %(unit)s" nomirror textcolor rgb '#0060ad' set yrange [%(min_y)s:%(max_y)s] set y2tics format "%(fmt_f)s %(unit_f)s" nomirror textcolor rgb '#dd181f' set y2range [%(min_y2)s:%(max_y2)s] set key top right set style line 1 lc rgb '#0060ad' lt 1 lw 1 pt 7 ps 0 # --- blue set style line 2 lc rgb '#dd181f' lt 1 lw 1 pt 5 ps 0 # --- red plot \ "-" using 1:($2*%(multiplier)s) title "clock offset %(unit)s" \ with linespoints ls 1, \ "-" using 1:($3*%(multiplier_f)s) title "frequency offset %(unit_f)s" \ with linespoints ls 2 axis x1y2 """ % out exp = """\

The time and frequency offsets between the ntpd calculated time and the local system clock. Showing frequency offset (red, in parts per million, scale on right) and the time offset (blue, in μs, scale on left). Quick changes in time offset will lead to larger frequency offsets.

These are fields 3 (time) and 4 (frequency) from the loopstats log file.

""" ret = {'html': VizStats.table_head + stats.table + stats_f.table + VizStats.table_tail + exp, 'plot': plot_template + plot_data + plot_data, 'stats': [stats, stats_f], 'title': "Local Clock Time/Frequency Offsets"} return ret def local_freq_temps_plot(self): "Generate gnuplot code graphing local frequency and temps" if not len(self.loopstats): sys.stderr.write("ntpviz: WARNING: no loopstats to graph\n") return '' tempsmap = self.tempssplit() tempslist = list(tempsmap.keys()) tempslist.sort() if not len(tempsmap) or not len(tempslist): sys.stderr.write("ntpviz: WARNING: no temps to graph\n") return '' # speed up by only sending gnuplot the data it will actually use # fields: time, freq offset (plot_data, values_f) = self.plot_slice(self.loopstats, 3) # compute frequency offset stats_f = VizStats(values_f, "Local Clock Frequency Offset", freq=1) stats = [stats_f] table = '' plot_data_t = '' max_temp = -300 min_temp = 1000 for key in tempslist: # speed up by only sending gnuplot the data it will actually use # fields: time, temp (p, v) = self.plot_slice(tempsmap[key], 3) plot_data_t += p s = VizStats(v, 'Temp %s' % key, units='°C') max_temp = max(s.percs["max_y"], max_temp) min_temp = min(s.percs["min_y"], min_temp) table += s.table stats.append(s) # out = stats.percs out = {} if args.clip: out["clipped"] = " (clipped)" else: out["clipped"] = "" out['fmt'] = gnuplot_fmt(min_temp, max_temp) out["fmt_f"] = stats_f.percs["fmt"] out["max_y2"] = stats_f.percs["max_y"] out["min_y2"] = stats_f.percs["min_y"] out["multiplier_f"] = stats_f.percs["multiplier"] out["sitename"] = self.sitename out['size'] = args.png_size out["unit"] = '°C' out["unit_f"] = stats_f.percs["unit"] # let temp autoscale # set yrange [%(min_y)s:%(max_y)s] plot_template = NTPViz.Common + """\ set terminal png size %(size)s set title "%(sitename)s: Local Frequency Offset/Temps%(clipped)s" set ytics format "%(fmt)s %(unit)s" nomirror textcolor rgb '#0060ad' set y2tics format "%(fmt_f)s %(unit_f)s" nomirror textcolor rgb '#dd181f' set y2range [%(min_y2)s:%(max_y2)s] set key top right set style line 1 lc rgb '#dd181f' lt 1 lw 1 pt 5 ps 0 # --- red plot \ "-" using 1:($2*%(multiplier_f)s) title "frequency offset %(unit_f)s" \ with linespoints ls 1 axis x1y2, \ """ % out for key in tempslist: out['key'] = key plot_template += "'-' using 1:2 title '%(key)s' with line, \\\n" \ % out # strip trailing ", \n" plot_template = plot_template[:-4] + "\n" exp = """\

The frequency offsets and temperatures. Showing frequency offset (red, in parts per million, scale on right) and the temeratures.

These are field 4 (frequency) from the loopstats log file, and field 3 from the temp log .

""" ret = {'html': VizStats.table_head + stats_f.table + table + VizStats.table_tail + exp, 'plot': plot_template + plot_data + plot_data_t, 'stats': stats, 'title': "Local Frequency/Temp"} return ret def local_temps_gnuplot(self): "Generate gnuplot code graphing local temperature statistics" sitename = self.sitename tempsmap = self.tempssplit() tempslist = list(tempsmap.keys()) tempslist.sort() if not len(tempsmap) or not len(tempslist): sys.stderr.write("ntpviz: WARNING: no temps to graph\n") return '' stats = [] plot_data = '' max_temp = -300 min_temp = 1000 for key in tempslist: # speed up by only sending gnuplot the data it will actually use # fields: time, temp (p, v) = self.plot_slice(tempsmap[key], 3) s = VizStats(v, 'Temp %s' % key, units='°C') max_temp = max(s.percs["max_y"], max_temp) min_temp = min(s.percs["min_y"], min_temp) plot_data += p out = {} out['fmt'] = gnuplot_fmt(min_temp, max_temp) out['sitename'] = sitename out['size'] = args.png_size plot_template = NTPViz.Common + """\ set terminal png size %(size)s set title "%(sitename)s: Local Temperatures" set ytics format "%(fmt)s °C" nomirror textcolor rgb '#0060ad' set style line 1 lc rgb '#0060ad' lt 1 lw 1 pt 7 ps 0 # --- blue set key top right plot \\ """ % out for key in tempslist: out['key'] = key plot_template += "'-' using 1:2 title '%(key)s' with line, \\\n" \ % out # strip the trailing ", \n" plot_template = plot_template[:-4] + "\n" exp = """\

Local temperatures. These will be site specific depending on what temperature sensors you have and collect data from. Temperature changes affect the local clock crystal frequency and stability. The math of how temperature changes frequency is complex, and also depends on crystal aging. So there is no easy way to correct for it in software. This the single most important component of frequency drift.

The Local Termperatures are from field 3 from the tempstats log file.

""" ret = {'html': exp, 'stats': stats} ret['title'] = "Local Temperatures" ret['plot'] = plot_template + plot_data return ret def local_gps_gnuplot(self): "Generate gnuplot code graphing local GPS statistics" sitename = self.sitename gpsmap = self.gpssplit() gpslist = list(gpsmap.keys()) gpslist.sort() if not len(gpsmap) or not len(gpslist): if 1 <= args.debug_level: sys.stderr.write("ntpviz: INFO: no GPS data to graph\n") return '' # build the output dictionary, because Python can not format # complex objects. gps_data = () values_nsat = [] values_tdop = [] plot_data = "" for key in gpslist: # fields: time, TDOP, nSats (ps, values_tdop, values_nsat) = self.plot_slice(gpsmap[key], 3, 4) plot_data += ps stats = VizStats(values_nsat, "nSats", units='nSat') stats_tdop = VizStats(values_tdop, "TDOP", units=' ') out = stats_tdop.percs out['sitename'] = sitename out['size'] = args.png_size if out['min_y'] == out['max_y']: # some GPS always output the same TDOP if 0 == out['min_y']: # scale 0:1 out['max_y'] = 1 else: # scale +/- 20% out['min_y'] = out['max_y'] * 0.8 out['max_y'] = out['max_y'] * 1.2 elif 2 > out['min_y']: # scale 0:max_x out['min_y'] = 0 # recalc fmt out['fmt'] = gnuplot_fmt(out["min_y"], out["max_y"]) plot_template = NTPViz.Common + """\ set terminal png size %(size)s set title "%(sitename)s: Local GPS%(clipped)s set ytics format "%(fmt)s TDOP" nomirror textcolor rgb '#0060ad' set yrange [%(min_y)s:%(max_y)s] set y2tics format "%%2.0f nSat" nomirror textcolor rgb '#dd181f' set style line 1 lc rgb '#0060ad' lt 1 lw 1 pt 7 ps 0 # --- blue set style line 2 lc rgb '#dd181f' lt 1 lw 1 pt 5 ps 0 # --- red set key top right plot \\ """ % out for key in gpslist: plot_template += """\ '-' using 1:2 title '%(key)s TDOP' with line ls 1, \\ '-' using 1:3 title '%(key)s nSat' with line ls 2 axis x1y2, \\ """ % locals() # strip the trailing ", \\n" plot_template = plot_template[:-4] + "\n" exp = """\

Local GPS. The Time Dilution of Precision (TDOP) is plotted in blue. The number of visible satellites (nSat) is plotted in red.

TDOP is field 3, and nSats is field 4, from the gpsd log file. The gpsd log file is created by the ntploggps program.

TDOP is a dimensionless error factor. TDOP ranges from 1 to greater than 20. 1 denotes the highest possible confidence level. 2 to 5 is good. Greater than 20 means there will be significant inaccuracy and error.

""" ret = {'html': VizStats.table_head + stats.table + stats_tdop.table + VizStats.table_tail + exp, 'stats': [stats, stats_tdop], 'title': "Local GPS", 'plot': plot_template + plot_data + plot_data} return ret def local_error_gnuplot(self): "Plot the local clock frequency error." if not len(self.loopstats): sys.stderr.write("ntpviz: WARNING: no loopstats to graph\n") return '' # grab and sort the values, no need for the timestamp, etc. # speed up by only sending gnuplot the data it will actually use # fields: time, freq error (plot_data, values) = self.plot_slice(self.loopstats, 3) # compute frequency offset stats = VizStats(values, "Local Clock Frequency Offset", freq=1,) # build the output dictionary, because Python can not format # complex objects. out = stats.percs out["fmt"] = stats.percs["fmt"] out["sitename"] = self.sitename out['size'] = args.png_size plot_template = NTPViz.Common + """\ set terminal png size %(size)s set title "%(sitename)s: Local Clock Frequency Offset%(clipped)s" set ytics format "%(fmt)s %(unit)s" nomirror set yrange [%(min_y)s:%(max_y)s] set key bottom right set style line 1 lc rgb '#0060ad' lt 1 lw 1 pt 7 ps 0 # --- blue set style line 2 lc rgb '#dd181f' lt 1 lw 1 pt 5 ps 0 # --- red plot \ "-" using 1:($2 * %(multiplier)s) title "local clock error" \ with linespoints ls 2, \ %(p99)s title "99th percentile", \ %(p95)s title "95th percentile", \ %(p5)s title "5th percentile", \ %(p1)s title "1st percentile" """ % out exp = """\

This shows the frequency offset of the local clock (aka drift). The graph includes percentile data to show how much the frequency changes over a longer period of time. The majority of this change should come from temperature changes (ex: HVAC, the weather, CPU usage causing local heating).

Smaller changes are better. An ideal result would be a horizontal line at 0ppm. Expected values of 99%-1% percentiles: 0.4ppm

The Frequency Offset comes from field 4 of the loopstats log file.

""" ret = {'html': VizStats.table_head + stats.table + VizStats.table_tail + exp, 'plot': plot_template + plot_data, 'stats': [stats], 'title': "Local Clock Frequency Offset"} return ret def loopstats_gnuplot(self, fld, title, legend, freq): "Generate gnuplot code of a given loopstats field" if not len(self.loopstats): sys.stderr.write("ntpviz: WARNING: no loopstats to graph\n") return '' # speed up by only sending gnuplot the data it will actually use # fields: time, fld (plot_data, values) = self.plot_slice(self.loopstats, fld) # process the values stats = VizStats(values, title, freq=freq) # build the output dictionary, because Python can not format # complex objects. out = stats.percs out["fld"] = fld out["fmt"] = stats.percs["fmt"] out["legend"] = legend out["min_y"] = '0' out["sitename"] = self.sitename out['size'] = args.png_size if freq: exp = """\

This shows the RMS Frequency Jitter (aka wander) of the local clock's frequency. In other words, how fast the local clock changes frequency.

Lower is better. An ideal clock would be a horizontal line at 0ppm.

RMS Frequency Jitter is field 6 in the loopstats log file.

""" else: exp = """\

This shows the RMS Jitter of the local clock offset. In other words, how fast the local clock offset is changing.

Lower is better. An ideal system would be a horizontal line at 0μs.

RMS jitter is field 5 in the loopstats log file.

""" plot_template = NTPViz.Common + """\ set terminal png size %(size)s set title "%(sitename)s: %(title)s%(clipped)s" set ytics format "%(fmt)s %(unit)s" nomirror set yrange [%(min_y)s:%(max_y)s] set key top right set style line 1 lc rgb '#0060ad' lt 1 lw 1 pt 7 ps 0 # --- blue set style line 2 lc rgb '#dd181f' lt 1 lw 1 pt 5 ps 0 # --- red plot \ "-" using 1:($2*%(multiplier)s) title "%(legend)s" with linespoints ls 1, \ %(p99)s title "99th percentile", \ %(p95)s title "95th percentile", \ %(p5)s title "5th percentile", \ %(p1)s title "1st percentile" """ % out ret = {'html': VizStats.table_head + stats.table + VizStats.table_tail + exp, 'plot': plot_template + plot_data, 'stats': [stats], 'title': title} return ret def local_offset_jitter_gnuplot(self): "Generate gnuplot code of local clock loop standard deviation" return self.loopstats_gnuplot(4, "Local RMS Time Jitter", "Jitter", 0) def local_offset_stability_gnuplot(self): "Generate gnuplot code graphing local clock stability" return self.loopstats_gnuplot(5, "Local RMS Frequency Jitter", "Stability", 1) def peerstats_gnuplot(self, peerlist, fld, title, type): "Plot a specified field from peerstats." peerdict = self.peersplit() if not peerlist: peerlist = list(peerdict.keys()) if not len(peerlist): sys.stderr.write("ntpviz: WARNING: no peer data to graph\n") return '' peerlist.sort() # For stability of output namelist = [] # peer names ip_todo = [] for key in peerlist: # Trickiness - we allow peerlist elements to be DNS names. # The socket.gethostbyname() call maps DNS names to IP addresses, # passing through literal IPv4 addresses unaltered. However, # it barfs on either literal IPv6 addresses or refclock names. try: ip = socket.gethostbyname(key) namelist.append(key) except: # ignore it ip = key # socket.getfqdn() is also flakey... namelist.append(socket.getfqdn(key)) if ip in peerdict: ip_todo.append(ip) else: # can this ever happen? sys.stderr.write("ntpviz: ERROR: No such peer as %s" % key) raise SystemExit(1) rtt = 0 percentages = "" stats = [] if len(peerlist) == 1: # only one peer if "offset" == type: # doing offset, not jitter rtt = 1 if "127.127." == peerlist[0][:8]: # don't do rtt for reclocks rtt = 0 title = "Refclock Offset " + str(peerlist[0]) exp = """\

This shows the offset of a local refclock in seconds. This is useful to see how the measured offset is behaving.

Closer to 0s is better. An ideal system would be a horizontal line at 0s. Typical 90%% ranges may be: local serial GPS 200 ms; local PPS 20µs.

Clock Offset is field 5 in the peerstats log file.

""" else: title = "Peer Offset " + str(peerlist[0]) exp = """\

This shows the offset of a peer or server in seconds. This is useful to see how the measured offset is behaving.

The chart also plots offset±rtt. Where rtt is the round trip time to the remote. NTP can not really know the offset of a remote chimer, NTP computes it by subtracting rtt/2 from the offset. Plotting the offset±rtt reverses this calculation to more easily see the effects of rtt changes.

Closer to 0s is better. An ideal system would be a horizontal line at 0s. Typical 90% ranges may be: local LAN peer 80µs; 90% ranges for WAN servers may be 4ms and much larger.

Clock Offset is field 5 in the peerstats log file. The Round Trip Time (rtt) is field 6 in the peerstats file.

""" else: # doing jitter, not offset if "127.127." == peerlist[0][:8]: title = "Refclock RMS Jitter " + str(peerlist[0]) exp = """\

This shows the RMS Jitter of a local refclock. Jitter is the current estimated dispersion; the variation in offset between samples.

Closer to 0s is better. An ideal system would be a horizontal line at 0s.

RMS Jitter is field 8 in the peerstats log file.

""" else: title = "Peer Jitter " + str(peerlist[0]) exp = """\

This shows the RMS Jitter of a remote peer or server. Jitter is the current estimated dispersion; the variation in offset between samples.

Closer to 0s is better. An ideal system would be a horizontal line at 0s.

RMS Jitter is field 8 in the peerstats log file.

""" if len(namelist[0]) and peerlist[0] != namelist[0]: # append hostname, if we have it # after stats to keep summary short title += " (%s)" % namelist[0] else: # many peers title += "s" if "offset" == type: title = "Peer Offsets" exp = """\

This shows the offset of all refclocks, peers and servers. This can be useful to see if offset changes are happening in a single clock or all clocks together.

Clock Offset is field 5 in the peerstats log file.

""" else: title = "Peer Jitters" exp = """\

This shows the RMS Jitter of all refclocks, peers and servers. Jitter is the current estimated dispersion; the variation in offset between samples.

Closer to 0s is better. An ideal system would be a horizontal line at 0s.

RMS Jitter is field 8 in the peerstats log file.

""" if len(peerlist) == 1: if peerlist[0] in refclock_name: title += ' ' + refclock_name[peerlist[0]] plot_data = "" for ip in ip_todo: # 20% speed up by only sending gnuplot the data it will # actually use if rtt: # fields: time, fld, and rtt (p, v1, v2) = self.plot_slice(peerdict[ip], fld, 5) plot_data += p else: # fields: time, fld (p, v1) = self.plot_slice(peerdict[ip], fld) plot_data += p stats = VizStats(v1, title) if len(peerlist) == 1: percentages = " %(p50)s title '50th percentile', " % stats.percs else: # skip stats on peers/offsets plots stats.skip_summary = True stats.table = '' out = stats.percs out['sitename'] = self.sitename out['size'] = args.png_size out['title'] = title out["fmt"] = stats.percs["fmt"] if 6 >= len(peerlist): out['set_key'] = "set key top right" elif 12 >= len(peerlist): # getting crowded out['set_key'] = "set key bmargin" else: # too many keys to show out['set_key'] = "set key off" plot_template = NTPViz.Common + """\ set terminal png size %(size)s set title "%(sitename)s: %(title)s%(clipped)s" set ylabel "" set ytics format "%(fmt)s %(unit)s" nomirror set yrange [%(min_y)s:%(max_y)s] %(set_key)s plot \ """ % out plot_template += percentages for key in peerlist: out['label'] = self.ip_label(key) plot_template += "'-' using 1:($2*%(multiplier)s) " \ " title '%(label)s' with line, \\\n" % out if 1 == rtt: plot_template += """\ '-' using 1:(($2+$3/2)*%(multiplier)s) title 'offset+rtt/2' with line, \\ '-' using 1:(($2-$3/2)*%(multiplier)s) title 'offset-rtt/2' with line """ % stats.percs # sadly, gnuplot needs 3 identical copies of the data. plot_template += plot_data + plot_data else: # strip the trailing ", \n" plot_template = plot_template[:-4] + "\n" if len(peerlist) == 1: # skip stats for multiplots html = VizStats.table_head + stats.table \ + VizStats.table_tail + exp, else: html = exp ret = {'html': html, 'plot': plot_template + plot_data, 'stats': [stats], 'title': title} return ret def peer_offsets_gnuplot(self, peerlist=None): return self.peerstats_gnuplot(peerlist, 4, "Peer Clock Offset", "offset") def peer_jitters_gnuplot(self, peerlist=None): return self.peerstats_gnuplot(peerlist, 7, "Peer Clock Jitter", "jitter") def local_offset_histogram_gnuplot(self): "Plot a histogram of clock offset values from loopstats." if not len(self.loopstats): sys.stderr.write("ntpviz: WARNING: no loopstats to graph\n") return '' # TODO normalize to 0 to 100? # grab and sort the values, no need for the timestamp, etc. values = [float(row[2]) for row in self.loopstats] stats = VizStats(values, 'Local Clock Offset') out = stats.percs out["fmt_x"] = stats.percs["fmt"] out['sitename'] = self.sitename # flip the axis out['min_x'] = out['min_y'] out['max_x'] = out['max_y'] rnd1 = 7 # round to 100 ns boxes out['boxwidth'] = 1e-7 # between -10us and 10us if 1e-5 > stats.percs["p99"] and -1e-5 < stats.percs["p1"]: # go to nanosec rnd1 = 9 # round to 1 ns boxes out['boxwidth'] = S_PER_NS # Python 2.6 has no collections.Counter(), so fake it. cnt = collections.defaultdict(int) for value in values: # put into buckets # for a +/- 50 microSec range that is 1,000 buckets to plot cnt[round(value, rnd1)] += 1 sigma = True if args.clip: if stats.percs['p1sigma'] > stats.percs['p99'] or \ stats.percs['m1sigma'] < stats.percs['p1']: # sigma out of range, do not plot sigma = '' out['sigma'] = '' if sigma: # plus/minus of one sigma range out['sigma'] = """\ set style arrow 1 nohead set linestyle 1 linecolor rgb "#009900" set arrow from %(m1sigma)s,graph 0 to %(m1sigma)s,graph 0.90 as 1 ls 1 set arrow from %(p1sigma)s,graph 0 to %(p1sigma)s,graph 0.90 as 1 ls 1 set label 1 "-1σ" at %(m1sigma)s, graph 0.96 left front offset -1,-1 \ textcolor rgb "#009900" set label 2 "+1σ" at %(p1sigma)s, graph 0.96 left front offset -1,-1 \ textcolor rgb "#009900" """ % out out['size'] = args.png_size # in 2016, 25% of screens are 1024x768, 42% are 1388x768 # but leave some room for the browser frame plot_template = '''\ set terminal png size %(size)s set title "%(sitename)s: Local Clock Time Offset Histogram%(clipped)s" set grid set boxwidth %(boxwidth)s set xtics format "%(fmt_x)s %(unit)s" nomirror set xrange [%(min_x)s:%(max_x)s] set yrange [0:*] set style arrow 3 nohead set arrow from %(p99)s,graph 0 to %(p99)s,graph 0.30 as 3 set style arrow 4 nohead set arrow from %(p95)s,graph 0 to %(p95)s,graph 0.45 as 4 set style arrow 5 nohead set arrow from %(p5)s,graph 0 to %(p5)s,graph 0.45 as 5 set style arrow 6 nohead set arrow from %(p1)s,graph 0 to %(p1)s,graph 0.30 as 6 set key off set lmargin 10 set rmargin 10 set style fill solid 0.5 set label 3 "99%%" at %(p99)s, graph 0.35 left front offset -1,-1 set label 4 "95%%" at %(p95)s, graph 0.50 left front offset -1,-1 set label 5 "1%%" at %(p1)s, graph 0.35 left front offset -1,-1 set label 6 "5%%" at %(p5)s, graph 0.50 left front offset -1,-1 %(sigma)s plot \ "-" using ($1 * %(multiplier)s):2 title "histogram" with boxes ''' % out histogram_data = ["%s %s\n" % (k, v) for k, v in cnt.items()] exp = """\

This shows the clock offsets of the local clock as a histogram.

The Local Clock Offset is field 3 from the loopstats log file.

""" # don't return stats, it's just a dupe ret = {'html': VizStats.table_head + stats.table + VizStats.table_tail + exp, 'plot': plot_template + "".join(histogram_data) + "e\n", 'stats': [], 'title': "Local Clock Time Offset Histogram"} return ret # Multiplotting can't live inside NTPViz because it consumes a list # of such objects, not a single one. def local_offset_multiplot(statlist): "Plot comparative local offsets for a list of NTPViz objects." out = [] out['size'] = args.png_size plot = NTPViz.Common + '''\ set terminal png size %(size)s set title "Multiplot Local Clock Offsets" set ytics format "%1.2f μs" nomirror textcolor rgb "#0060ad" set key bottom right box plot \\ ''' % out # FIXME: probably need to be more flexible about computing the plot label sitenames = [os.path.basename(os.path.dirname(d)) for d in args.statsdirs] for (i, stats) in enumerate(statlist): plot += '"-" using 1:($2*1000000) title "%s clock offset μs" ' \ 'with linespoints, \\\n' % (sitenames[i]) plot = plot[:-4] + "\n" plot_data = '' for stats in statlist: # speed up by only sending gnuplot the data it will actually use # fields: time, offset (p, v) = NTPViz.plot_slice(stats.loopstats, 2) plot_data += p ret = {'html': '', 'stats': []} ret['title'] = "Multiplot" ret['plot'] = plot + plot_data return ret # here is how to create the base64 from an image file: # with open("path/to/file.png", "rb") as f: # data = f.read() # print data.encode("base64") # ntpsec_logo = """ iVBORw0KGgoAAAANSUhEUgAAAEAAAABKCAQAAACh+5ozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC AK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAJiS0dE AP7wiPwpAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFKElEQVRo3s2ZT0wcVRzHPzMLKCwsNgqLkYPS xBjbRF3TcKlC4VAhFU0AdRN7a+zBEsUEL0qImqoxMTWhBzEkTdqmREhMCgpeiiV6KVE46MVE1KQg uxv/df81tLvzPOzsMjs7sztvd7b4ndPsfPf3vu/33vv93vs9yGCIJMLyWaKJXTSxZMMTCITilJ1k KENRdeoB6rHGYboNb80cpAjEQZoNr90ctiHWcyBfgD0aCZTk2CFAYylKTd7bVZYNknycwGf5ryjT RE2/OWVr9Bh9ahbwnuGtnRdsTZ5h0/Rbhr1PDYhNUZyt2guwRjdazi8+G0lZeMWoeExna3mzxwbO BDgwlIWQYhefhCkSNl8SpCpkO/JAiHFO00D+kCokGa8JpRyylSTjIlSeAPiC7/AU/JomknLM9qRb Ijv8XaaANNs4hyU7VcJE6UBUZeR7wLjgqgXT4jQL6JYw5Qqy/U3e6YazLWY9cJ5DDOc+/kvU9aHQ 8HFP7m2O8/kCwoyQYgAvAD8xwja1rjUugA7e15NzgnlGCRfSvATZII1A4yv1KIqL/R/iF9IIBCGC itfOtEoHs/qeJURQ90elaGOCbQSCtLKhDOd/LJTiZ1KfDXGW+aFiP2h00o8CJJhX3m75PabdLMZX jIrdfIq6vhDDhFxtfkV9xtqXlrmgjltzHGIMSBMhXcEAeGjFAyxrX1sTLAXcAvTsHuE5tixjgga6 NA92OUXjAS5zfzGFpXZEabb5w7Jn99LMAI3EmecGf9n4SS3lPydbskKjD3GcIM3ch4c0Y9xghgv8 hiZvrBwBg3zIgwj+1FN9LfsZ52Uu8ikhWWPyAoY5Swu/coEZYmio+DhGD31M8CgjViG2PEwgEFyn 3dR8GMEsHahAF+/SBezGjkums1A71xEIJtwR0K837zdwdk0HiRNnQE6ATNL1cpJWFjll4+YF5vFy Qi6DyAhop5MkU0Rsvsd5hzC99FZLwAB+NlktwtjkGg08US0BDcDlogstwRoQkBkE2WVYePw6ondD ZZUFAALssz2mVSwgHzFCPMwjAHhoY1HehKyAAF5D76aZNXyL6nF/jX+qI2CdJJ2087Ohyfw6iZcA sOZ8AOQm4Sqb+HmpCKOXXhKsS9iUEhDiEnCc/TbfWzmJlytcqZYAuMgG+/kgF4qN8HOWfiJMyQxA MRRLRoscy0s62e18GNOmu3QukF0Fc8AkfTzFN6zwJXEET9LF83QQ4RRz7vTe3gOg0McCMQQpQmyx RRRBnAX6LPa9rnsABEt8yxG6eFavC8dZYYqrxMvpZ3mRMM4Ci3ycqwhFC+qmVRYAsvWjsgX4GC2/ d5SurNoK8Oo1ch9vuNFP+XN2kJjLR9Nh64asPNDEa7xKIxVNLgN8+PAzCVZRwurEGuQzGoEwr7Ni USmVQ5ouPsFPpgzkIFBlD+a2TpOF6txmPtXVMpkTCZ5d2jaDblaoABjUqy4mCcZ2+jlHK3CTt/gc xdUqmUDwIqepBzY4ykahgFbO0Q9AirCp6u8OFPz6qpvhlcLMMeZ6Wcr+iSu5E+TuTGvIyqzuA4BX 5E5P5kAUrZuucSP42CDl2zHdLhYI2DmzsylhURYFd5F7fmOy5wJqaFbb7h5Q65PdGoDvrtEqz4HM APTUfn97HZW4whKPKy14sgvf9QhoQi7ARImi8KNSlZAjgewqcCfzy0DfrGUFTPORi1c0pXGbNzOb vV0PuFZgdAjd4/+DZZjBnbgzNSJ3f7rnq0AltrcCPMR4mro9a3/9Pwl2Z1Rsm9zNAAAAJXRFWHRk YXRlOmNyZWF0ZQAyMDE1LTA2LTI5VDE4OjMwOjA3LTA0OjAwZxkj2wAAACV0RVh0ZGF0ZTptb2Rp ZnkAMjAxNS0wNi0yOVQxODozMDowNy0wNDowMBZEm2cAAAAASUVORK5CYII= """ ntpsec_ico = """\ AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/ AAAA/wAAAP8AAAD/AAAAAAAAAP+fn59gn5+fYJ+fn2Cfn59gn5+fYJ+fn2Cfn59gn5+fYJ+fn2Cf n59gn5+fYJ+fn2B/f39/AAAA/wAAAAAAAAAAAAAA/5+fn2Cfn59gn5+fYJ+fn2Cfn59gn5+fYJ+f n2Cfn59gn5+fYJ+fn2Cfn59gAAAA/wAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA /wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/5+fn2Cfn59g n5+fYJ+fn2Cfn59gn5+fYJ+fn2Cfn59gn5+fYAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+f n59gn5+fYAAAAP8AAAD/AAAA/wAAAP8AAAD/n5+fYJ+fn2AAAAD/AAAAAAAAAAAAAAAAAAAAAAAA AAAAAAD/n5+fYAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+fn59gAAAA/wAAAAAAAAAAAAAA AAAAAAAAAAAAAAAA/5+fn2AAAAD/AAAAAAAAAAAAAAD/AAAA/01NTWAAAAD/n5+fYAAAAP8AAAAA AAAAAAAAAAAAAAAAAAAAAAAAAP+fn59gAAAA/wAAAAAAAAAAAAAA/wAAAAAAAAAAAAAA/5+fn2AA AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/n5+fYAAAAP8AAAAAAAAAAE1NTWAAAAAAAAAAAAAA AP+fn59gAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/5+fn2Cfn59gAAAA/wAAAP8AAAD/AAAA /wAAAP+fn59gn5+fYAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAEBAf+fn59gn5+fYJ+fn2Cfn59g n5+fYJ+fn2Cfn59gn5+fYJ+fn2AAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8A AAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAA /wAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAAAAAAA/wAAAAAAAAAA AAAAAAAAAP8AAAD/AAAA/wAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAD/AAAA/wAAAP8A AAAAgAAAAL/+AADf/QAAwAEAAO/7AADsGwAA6+sAAOsrAADrawAA6+sAAOwbAADv+wAA4AMAAN/9 AADRxQAAxBEAAA== """ if __name__ == '__main__': parser = MyArgumentParser(description="ntpd stats visualizer", fromfile_prefix_chars='@', epilog=""" You can place command line options in a file, one per line. See the manual page for details. Python by ESR, concept and gnuplot code by Dan Drown. """) group = parser.add_mutually_exclusive_group() parser.add_argument('-c', '--clip', action="store_true", dest='clip', help="Clip plots at 1%% and 99%%") parser.add_argument('-d', '--datadir', default="/var/log/ntpstats", dest='statsdirs', help="one or more logfile directories to read", type=str) parser.add_argument('-e', '--endtime', dest='endtime', help="End time in POSIX (seconds) or ISO 8601", type=str) parser.add_argument('-g', '--generate', action="store_true", dest='generate', help="Run plot through gnuplot to make png") parser.add_argument('-n', '--name', default=socket.getfqdn(), dest='sitename', help="sitename (title)", type=str) parser.add_argument('-o', '--outdir', default="ntpgraphs", dest='outdir', help="output directory", type=str) parser.add_argument('-p', '--period', default=7, # default to 7 days dest='period', help="period in days to graph (float)", type=float) parser.add_argument('-s', '--starttime', dest='starttime', help="Start time in POSIX (seconds) or ISO 8601", type=str) parser.add_argument('-w', '--width', choices=['s', 'm', 'l'], default='m', dest='width', help="PNG width: s, m, or l", type=str) group.add_argument('--all-peer-jitters', action="store_true", dest='show_peer_jitters', help="Plot all peer jitters") group.add_argument('--peer-jitters', default='', dest='peer_jitters', help="Plot peer jitters. Comma separated host list.", type=str) group.add_argument('--all-peer-offsets', action="store_true", dest='show_peer_offsets', help="Plot all peer offsets") group.add_argument('--peer-offsets', default='', dest='peer_offsets', help="Plot peer offsets. Comma separated host list.", type=str) group.add_argument('--local-error', action="store_true", dest='show_local_error', help="Plot local clock frequency offsets") group.add_argument('--local-freq-temps', action="store_true", dest='show_freq_temps', help="Plot local frequency vs temperature data") group.add_argument('--local-gps', action="store_true", dest='show_gps', help="Plot gpsd TDOP and nSats") group.add_argument('--local-jitter', action="store_true", dest='show_local_jitter', help="Plot clock time jitter") group.add_argument('--local-offset', action="store_true", dest='show_local_offset', help="Plot Clock frequency offset") group.add_argument('--local-offset-histogram', action="store_true", dest='show_local_offset_histogram', help="Plot histogram of loopstats time offsets") group.add_argument('--local-offset-multiplot', action="store_true", dest='show_local_offset_multiplot', help="Plot comparative local offsets for " "multiple directories") group.add_argument('--local-stability', action="store_true", dest='show_local_stability', help="Plot RMS frequency-jitter") group.add_argument('--local-temps', action="store_true", dest='show_temps', help="Plot local temperature data") parser.add_argument('-D', '--debug', default=0, dest='debug_level', help="debug level, 0 (none) to 9 (most)", type=int) # some OS do not support os.nice() try: os.nice(0) parser.add_argument('-N', '--nice', action="store_true", dest='nice', help="Run as lowest priority") except: pass parser.add_argument('-V', '--version', action="version", version="ntpviz %s" % ntp.util.stdversion()) args = parser.parse_args() if args.nice: # run at lowest possible priority nice = os.nice(19) if args.debug_level: sys.stderr.write("ntpviz: INFO: Now running at nice %s\n" % nice) if 's' == args.width: # fit in 1024x768 browser # in 2016 this is 22% of all browsers args.png_size = '1000,720' elif 'l' == args.width: # fit in 1920x1080 browser args.png_size = '1850,1000' else: # fit in 1388x768 browser # in 2016 this is 42% of all browsers args.png_size = '1340,720' args.period = int(float(args.period) * ntp.statfiles.NTPStats.SecondsInDay) if args.endtime is not None: args.endtime = ntp.statfiles.iso_to_posix(args.endtime) if args.starttime is not None: args.starttime = ntp.statfiles.iso_to_posix(args.starttime) args.statsdirs = [os.path.expanduser(path) for path in args.statsdirs.split(",")] if args.show_peer_offsets is True: args.show_peer_offsets = [] elif 0 < len(args.peer_offsets): args.show_peer_offsets = args.peer_offsets.split(",") else: args.show_peer_offsets = None if args.show_peer_jitters is True: args.show_peer_jitters = [] elif 0 < len(args.peer_jitters): args.show_peer_jitters = args.peer_jitters.split(",") else: args.show_peer_jitters = None if 0 < args.debug_level: sys.stderr.write("ntpviz: INFO: now running at debug: %s\n" % args.debug_level) sys.stderr.write("ntpviz: INFO: Version: %s\n" % ntp.util.stdversion()) sys.stderr.write("ntpviz: INFO: Parsed Options %s\n" % args) if 9 == args.debug_level: # crazy debug, also profile import cProfile pr = cProfile.Profile() pr.enable() # register to dump debug on all normal exits atexit.register(print_profile) nice = 19 # always run nicely if 0 != nice: try: import psutil # set ionice() to idle p = psutil.Process(os.getpid()) p.ionice(psutil.IOPRIO_CLASS_IDLE) except ImportError: if 0 < args.debug_level: sys.stderr.write("ntpviz: INFO: psutil not found\n") pass # set nice() nice = os.nice(nice) if 2 < args.debug_level: sys.stderr.write("ntpviz: INFO: now running at nice: %s\n" % nice) for fontpath in ("/usr/share/fonts/liberation", "/usr/share/fonts/liberation-fonts", "/usr/share/fonts/truetype/liberation"): if os.path.exists(fontpath): os.environ["GDFONTPATH"] = fontpath break else: sys.stderr.write( "ntpviz: WARNING: liberation truetype fonts not found\n") os.environ["GNUPLOT_DEFAULT_GDFONT"] = "LiberationSans-Regular" plot = None if 1 == len(args.statsdirs): statlist = [NTPViz(statsdir=args.statsdirs[0], sitename=args.sitename, period=args.period, starttime=args.starttime, endtime=args.endtime)] else: statlist = [NTPViz(statsdir=d, sitename=d, period=args.period, starttime=args.starttime, endtime=args.endtime) for d in args.statsdirs] if len(statlist) == 1: stats = statlist[0] if args.show_local_offset or \ args.show_local_error or \ args.show_local_jitter or \ args.show_local_stability or \ args.show_local_offset_histogram: if not len(stats.loopstats): sys.stderr.write("ntpviz: ERROR: missing loopstats data\n") raise SystemExit(1) if args.show_local_offset: plot = stats.local_offset_gnuplot() elif args.show_local_error: plot = stats.local_error_gnuplot() elif args.show_local_jitter: plot = stats.local_offset_jitter_gnuplot() elif args.show_local_stability: plot = stats.local_offset_stability_gnuplot() elif args.show_local_offset_histogram: plot = stats.local_offset_histogram_gnuplot() if args.show_peer_offsets is not None or \ args.show_peer_jitters is not None: if not len(stats.peerstats): sys.stderr.write("ntpviz: ERROR: missing peerstats data\n") raise SystemExit(1) if args.show_peer_offsets is not None: plot = stats.peer_offsets_gnuplot(args.show_peer_offsets) if args.show_peer_jitters is not None: plot = stats.peer_jitters_gnuplot(args.show_peer_jitters) if args.show_freq_temps: if not len(stats.temps): sys.stderr.write("ntpviz: ERROR: missing temps data\n") raise SystemExit(1) plot = stats.local_freq_temps_plot() if args.show_temps: if not len(stats.temps): sys.stderr.write("ntpviz: ERROR: missing temps data\n") raise SystemExit(1) plot = stats.local_temps_gnuplot() if args.show_gps: if not len(stats.gpsd): sys.stderr.write("ntpviz: ERROR: missing gps data\n") raise SystemExit(1) plot = stats.local_gps_gnuplot() if args.show_local_offset_multiplot: plot = local_offset_multiplot(statlist) if plot is not None: # finish up the plot, and exit if args.generate: gnuplot(plot['plot']) else: sys.stdout.write(plot['plot']) raise SystemExit(0) # Fall through to HTML code generation if not os.path.isdir(args.outdir): try: os.mkdir(args.outdir) except SystemError: sys.stderr.write("ntpviz: ERROR: %s can't be created.\n" % args.outdir) raise SystemExit(1) # if no ntpsec favicon.ico, write one. ico_filename = os.path.join(args.outdir, "favicon.ico") if not os.path.lexists(ico_filename): with open(ico_filename, "wb") as wp: wp.write(binascii.a2b_base64(ntpsec_ico)) # if no ntpsec logo, write one. logo_filename = os.path.join(args.outdir, "ntpsec-logo.png") if not os.path.lexists(logo_filename): with open(logo_filename, "wb") as wp: wp.write(binascii.a2b_base64(ntpsec_logo)) # report_time = datetime.datetime.utcnow() # the time now is... report_time = datetime.datetime.now(UTC()) # the time now is... report_time = report_time.strftime("%c %Z") # format it nicely title = args.sitename index_header = '''\ %(title)s
NTPsec

%(title)s

Report generated: %(report_time)s
''' % locals() # Ugh. Not clear what to do in the multiplot case if len(statlist) == 1: start_time = datetime.datetime.utcfromtimestamp( stats.starttime).strftime('%c') end_time = datetime.datetime.utcfromtimestamp( stats.endtime).strftime('%c') index_header += 'Start Time: %s UTC
\n' \ 'End Time: %s UTC
\n' \ % (start_time, end_time) index_header += 'Report Period: %1.1f days
\n' \ % (float(stats.period) / float(ntp.statfiles.NTPStats.SecondsInDay)) if args.clip: index_header += """\ Warning: plots clipped
""" index_header += '
\n
' index_trailer = '''\

Glossary:

frequency offset:
The difference between the ntpd calculated frequency and the local system clock frequency (usually in parts per million, ppm)
jitter, dispersion:
The short term change in a value. NTP measures Local Time Jitter, Refclock Jitter, and Peer Jitter in seconds. Local Frequency Jitter is in ppm or ppb.
kurtosis, Kurt:
The kurtosis of a random variable X is the fourth standardized moment and is a dimension-less ratio. ntpviz uses the Pearson's moment coefficient of kurtosis. A normal distribution has a kurtosis of three. NIST describes a kurtosis over three as "heavy tailed" and one under three as "light tailed".
ms, millisecond:
One thousandth of a second = 0.001 seconds, 1e-3 seconds
mu, mean:
The arithmetic mean: the sum of all the values divided by the number of values. The formula for mu is: "mu = (∑xi) / N". Where xi denotes the data points and N is the number of data points.
ns, nanosecond:
One billionth of a second, also one thousandth of a microsecond, 0.000000001 seconds and 1e-9 seconds.
percentile:
The value below which a given percentage of values fall.
ppb, parts per billion:
Ratio between two values. These following are all the same: 1 ppb, one in one billion, 1/1,000,000,000, 0.000,000,001, 1e-9 and 0.000,000,1%
ppm, parts per million:
Ratio between two values. These following are all the same: 1 ppm, one in one million, 1/1,000,000, 0.000,001, and 0.000,1%
‰, parts per thousand:
Ratio between two values. These following are all the same: 1 ‰. one in one thousand, 1/1,000, 0.001, and 0.1%
refclock:
Reference clock, a local GPS module or other local source of time.
remote clock:
Any clock reached over the network, LAN or WAN. Also called a peer or server.
time offset:
The difference between the ntpd calculated time and the local system clock's time. Also called phase offset.
σ, sigma:
Sigma denotes the standard deviation (SD) and is centered on the arithmetic mean of the data set. The SD is simply the square root of the variance of the data set. Two sigma is simply twice the standard deviation. Three sigma is three times sigma. Smaller is better.
The formula for sigma is: "σ = √[ ∑(xi-mu)^2 / N ]". Where xi denotes the data points and N is the number of data points.
skewness, Skew:
The skewness of a random variable X is the third standardized moment and is a dimension-less ratio. ntpviz uses the Pearson's moment coefficient of skewness. Wikipedia describes it best: "The qualitative interpretation of the skew is complicated and unintuitive."
A normal distribution has a skewness of zero.
upstream clock:
Any remote clock or reference clock used as a source of time.
µs, us, microsecond:
One millionth of a second, also one thousandth of a millisecond, 0.000,001 seconds, and 1e-6 seconds.



This page autogenerated by ntpviz, part of the NTPsec project
html 5    Valid CSS!
''' imagewrapper = "%s plot\n" # buffer the index.html output so the index.html is not empty # during the run index_buffer = index_header # if header file, add it to index.html header = os.path.join(args.outdir, "header") if os.path.isfile(header): try: header_file = open(header, 'r', encoding='utf-8') header_txt = header_file.read() index_buffer += '
\n' + header_txt + '\n' except IOError: pass if len(statlist) > 1: index_buffer += local_offset_multiplot(statlist) else: # imagepairs in the order of the heml entries imagepairs = [ ("local-offset", stats.local_offset_gnuplot()), # skipa next one, redundant to one above # ("local-error", stats.local_error_gnuplot()), ("local-jitter", stats.local_offset_jitter_gnuplot()), ("local-stability", stats.local_offset_stability_gnuplot()), ("local-offset-histogram", stats.local_offset_histogram_gnuplot()), ("local-temps", stats.local_temps_gnuplot()), ("local-freq-temps", stats.local_freq_temps_plot()), ("local-gps", stats.local_gps_gnuplot()), ("peer-offsets", stats.peer_offsets_gnuplot()), ] peerlist = list(stats.peersplit().keys()) # sort for output order stability peerlist.sort() for key in peerlist: imagepairs.append(("peer-offset-" + key, stats.peer_offsets_gnuplot([key]))) imagepairs.append(("peer-jitters", stats.peer_jitters_gnuplot())) for key in peerlist: imagepairs.append(("peer-jitter-" + key, stats.peer_jitters_gnuplot([key]))) stats = [] for (imagename, image) in imagepairs: if not image: continue if 1 <= args.debug_level: sys.stderr.write("ntpviz: plotting %s\n" % image['title']) stats.append(image['stats']) # give each H2 an unique ID. id = image['title'].lower() id = id.replace(' ', '_').replace(':', '_') index_buffer += """\
\n

%s

""" % (id, id, image['title']) div_name = imagename.replace('-', ' ') index_buffer += imagewrapper % \ (imagename.replace(':', '%3A'), div_name) if image['html']: index_buffer += "
\n%s
\n" % image['html'] index_buffer += "

\n" gnuplot(image['plot'], os.path.join(args.outdir, imagename + ".png")) index_buffer += "
\n" # dump stats csvs = [] if True: stats_to_output = {} for stat in stats: if [] == stat: continue for sta in stat: if sta.skip_summary: continue # This removes duplicates stats_to_output[sta.title] = sta index_buffer += '
\n' \ '

Summary

\n' index_buffer += VizStats.table_head for key in sorted(stats_to_output.keys()): index_buffer += str(stats_to_output[key].table) csvs.append(stats_to_output[key].csv) # RFC 4180 specifies the mime-type of a csv: text/csv # your webserver should be programmed the same index_buffer += VizStats.table_tail index_buffer += """\ Summary as CSV file
""" # if footer file, add it to index.html footer = os.path.join(args.outdir, "footer") if os.path.isfile(footer): try: footer_file = open(footer, 'r', encoding='utf-8') footer_txt = footer_file.read() index_buffer += '
\n' + footer_txt + '\n' except IOError: pass index_buffer += index_trailer # and send the file buffer index_filename = os.path.join(args.outdir, "index.html") with open(index_filename + ".tmp", "w", encoding='utf-8') as ifile: ifile.write(index_buffer) # create csv file, as a tmp file csv_filename = os.path.join(args.outdir, "summary.csv") with open(csv_filename + ".tmp", "w", encoding='utf-8') as csv_file: csv_ob = csv.writer(csv_file) csv_ob.writerow(VizStats.csv_head) for row in csvs: csv_ob.writerow(row) # move new index and summary into place # windows python 2.7, 3.6 has no working rename, so delete and move try: os.remove(csv_filename) os.remove(index_filename) except: pass os.rename(csv_filename + ".tmp", csv_filename) os.rename(index_filename + ".tmp", index_filename) # end ntpsec-1.1.0+dfsg1/ntpclients/ntptrace.py0000644000175000017500000001170213252364117020203 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- """ ntptrace - trace peers of an NTP server Usage: ntptrace [-n | --numeric] [-m number | --max-hosts=number] [-r hostname | --host=hostname] [--help | --more-help] hostname See the manual page for details. """ # SPDX-License-Identifier: BSD-2-Clause from __future__ import print_function import getopt import re import subprocess import sys try: import ntp.util except ImportError as e: sys.stderr.write( "ntptrace: can't find Python NTP library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) def get_info(host): info = ntp_read_vars(0, [], host) if info is None or 'stratum' not in info: return info['offset'] = round(float(info['offset']) / 1000, 6) info['syncdistance'] = \ (float(info['rootdisp']) + (float(info['rootdelay']) / 2)) / 1000 return info def get_next_host(peer, host): info = ntp_read_vars(peer, ["srcadr"], host) if info is None: return return info['srcadr'] def ntp_read_vars(peer, vars, host): obsolete = {'phase': 'offset', 'rootdispersion': 'rootdisp'} if not len(vars): do_all = True else: do_all = False outvars = {}.fromkeys(vars) if do_all: outvars['status_line'] = {} cmd = ["ntpq", "-n", "-c", "rv %s %s" % (peer, ",".join(vars))] if host is not None: cmd.append(host) try: # sadly subprocess.check_output() is not in Python 2.6 proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) out = proc.communicate()[0] output = out.decode('utf-8').splitlines() except subprocess.CalledProcessError as e: print("Could not start ntpq: %s" % e.output, file=sys.stderr) raise SystemExit(1) except OSError as e: print("Could not start ntpq: %s" % e.strerror, file=sys.stderr) raise SystemExit(1) for line in output: if re.search(r'Connection refused', line): return match = re.search(r'^asso?c?id=0 status=(\S{4}) (\S+), (\S+),', line, flags=re.IGNORECASE) if match: outvars['status_line']['status'] = match.group(1) outvars['status_line']['leap'] = match.group(2) outvars['status_line']['sync'] = match.group(3) iterator = re.finditer(r'(\w+)=([^,]+),?\s?', line) for match in iterator: key = match.group(1) val = match.group(2) val = re.sub(r'^"([^"]+)"$', r'\1', val) if key in obsolete: key = obsolete[key] if do_all or key in outvars: outvars[key] = val return outvars usage = r"""ntptrace - trace peers of an NTP server USAGE: ntptrace [- [] | --[{=| }]]... [host] -n, --numeric Print IP addresses instead of hostnames -m, --max-hosts=num Maximum number of peers to trace -r, --host=str Single remote host -?, --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.""" + "\n" try: (options, arguments) = getopt.getopt( sys.argv[1:], "m:nr:?", ["help", "host=", "max-hosts=", "more-help", "numeric"]) except getopt.GetoptError as err: sys.stderr.write(str(err) + "\n") raise SystemExit(1) numeric = False maxhosts = 99 host = '127.0.0.1' for (switch, val) in options: if switch == "-m" or switch == "--max-hosts": errmsg = "Error: -m parameter '%s' not a number\n" maxhosts = ntp.util.safeargcast(val, int, errmsg, usage) elif switch == "-n" or switch == "--numeric": numeric = True elif switch == "-r" or switch == "--host": host = val elif switch == "-?" or switch == "--help" or switch == "--more-help": print(usage, file=sys.stderr) raise SystemExit(0) if len(arguments): host = arguments[0] hostcount = 0 while True: hostcount += 1 info = get_info(host) if info is None: break if not numeric: host = ntp.util.canonicalize_dns(host) print("%s: stratum %d, offset %f, synch distance %f" % (host, int(info['stratum']), info['offset'], info['syncdistance']), end='') if int(info['stratum']) == 1: print(", refid '%s'" % info['refid'], end='') print() if (int(info['stratum']) == 0 or int(info['stratum']) == 1 or int(info['stratum']) == 16): break if re.search(r'^127\.127\.\d{1,3}\.\d{1,3}$', info['refid']): break if hostcount == maxhosts: break next_host = get_next_host(info['peer'], host) if next_host is None: break if re.search(r'^127\.127\.\d{1,3}\.\d{1,3}$', next_host): break host = next_host ntpsec-1.1.0+dfsg1/ntpclients/ntpwait-man.txt0000644000175000017500000000067013252364117021013 0ustar rlaagerrlaager= ntpwait(8) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpwait - wait for ntpd to stabilize the system clock include::../docs/includes/ntpwait-body.txt[] == EXIT STATUS == One of the following exit values will be returned: 0:: Successful program execution. 1:: The operation failed or the command syntax was not valid. 2:: Operation was interrupted by signal. // end ntpsec-1.1.0+dfsg1/ntpclients/ntpmon-man.txt0000644000175000017500000000036413252364117020640 0ustar rlaagerrlaager= ntpmon(1) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpmon - real-time NTP status monitor include::../docs/includes/ntpmon-body.txt[] == EXIT STATUS == Always returns 0. // end ntpsec-1.1.0+dfsg1/ntpclients/ntpq.py0000644000175000017500000017262313252364117017357 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- # # ntpq - query an NTP server using mode 6 commands # # Freely translated from the old C ntpq code by ESR. The idea was to # cleanly separate ntpq-that-was into a thin front-end layer handling # mainly command interpretation and a back-end that presents the take # from ntpd as objects that can be re-used by other front # ends. Reusable pieces live in pylib. # # SPDX-License-Identifier: BSD-2-clause from __future__ import print_function, division import cmd import getopt import hashlib import os import re import resource import socket import sys import time try: import ntp.control import ntp.ntpc import ntp.packet import ntp.util except ImportError as e: sys.stderr.write( "ntpq: can't find Python NTP library -- check PYTHONPATH.\n") sys.stderr.write("%s\n" % e) sys.exit(1) version = ntp.util.stdversion() # 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 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. # # - Construct custom stdout and stderr streams when running # on Python 3 that force UTF-8 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. # # anyone that changes this needs to test with all combinations of # python2, python3, LC_ALL=ascii, LC_ALL=latin-1, LC_ALL=en_US.utf8, and # piping output to a file. While looking at the UTF-8 in the output. forced_utf8 = False if str is bytes: # Python 2 polystr = unicode polybytes = bytes def string_escape(s): return s.decode('string_escape') # This used to force UTF-8 encoding, but that breaks the readline system. # Unfortunately sometimes sys.stdout.encoding lies about the encoding, # so expect random false positives. ntp.util.check_unicode() else: # Python 3 import io 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_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="utf-8", newline="\n", line_buffering=True) # This is the one situation where we *can* force unicode. if "UTF-8" != sys.stdout.encoding: forced_utf8 = True sys.stdout = make_std_wrapper(sys.stdout) if "UTF-8" != sys.stderr.encoding: forced_utf8 = True sys.stderr = make_std_wrapper(sys.stderr) # NTP-specific parts resume here # Flags for forming descriptors. OPT = 0x80 # this argument is optional, or'd with type */ NO = 0x0 NTP_STR = 0x1 # string argument NTP_UINT = 0x2 # unsigned integer NTP_INT = 0x3 # signed integer NTP_ADD = 0x4 # IP network address IP_VERSION = 0x5 # IP version NTP_ADP = 0x6 # IP address and port NTP_LFP = 0x7 # NTP timestamp NTP_MODE = 0x8 # peer mode NTP_2BIT = 0x9 # leap bits NTP_FLOAT = 0xa # Float value class Ntpq(cmd.Cmd): "ntpq command interpreter" def __init__(self, session): cmd.Cmd.__init__(self) self.session = session self.prompt = "ntpq> " if os.isatty(0) else "" self.interactive = False # set to True when we should prompt # self.auth_keyid = 0# Keyid used for authentication. # self.auth_keytype = "NID_md5"# MD5 (FIXME: string value is a dummy) # self.auth_hashlen = 16# MD5 # I do not know if the preceding are there for a specific reason # so I am leaving them, and possibly duplicating them. self.rawmode = False # Flag which indicates raw mode output. self.directmode = False # Flag for direct MRU output. self.showhostnames = True # If false, display numeric IPs self.showunits = False # If False, show old style float self.auth_delay = 20 # delay time (default 20msec) self.wideremote = False # show wide remote names? self.ccmds = [] # Queued commands self.chosts = [] # Command-line hosts self.peers = [] # Data from NTP peers. self.debug = 0 self.logfp = sys.stderr self.pktversion = ntp.magic.NTP_OLDVERSION + 1 self.uservars = ntp.util.OrderedDict() self.ai_family = socket.AF_UNSPEC self.termwidth = ntp.util.termsize()[0] def get_names(self): # Overrides a method in Cmd return [x.replace('hot_', ':').replace('config_from_file', 'config-from-file') for x in dir(self.__class__)] def emptyline(self): "Called when an empty line is entered in response to the prompt." pass def precmd(self, line): if line.startswith(":config"): line = "hot_" + line[1:] elif line.startswith("config-from-file"): line = line.replace("config-from-file", "config_from_file") return line def default(self, line): "Called on an input line when the command prefix is not recognized." cmd, arg, line = self.parseline(line) try: dotext = 'do_'+cmd cmdprefixlist = [a[3:] for a in self.get_names() if a.startswith(dotext)] if len(cmdprefixlist) == 1: line = line.replace(cmd, cmdprefixlist[0]) cmd = cmdprefixlist[0] elif len(cmdprefixlist) > 1: self.warn("***Command `%s' ambiguous\n" % cmd) return elif len(cmdprefixlist) == 0: self.warn("***Command `%s' unknown\n" % cmd) return if cmd == "help" and arg: helptext = 'help_'+arg if helptext not in self.get_names(): argprefixlist = [a[5:] for a in self.get_names() if a.startswith(helptext)] if len(argprefixlist) == 1: line = line.replace(arg, argprefixlist.pop()) elif len(argprefixlist) > 1: self.warn("Command `%s' is ambiguous\n" % arg) return elif len(argprefixlist) == 0: self.warn("Command `%s' is unknown\n" % arg) return self.onecmd(line) except TypeError: self.warn("Command `%s' is unknown\n" % line) def do_help(self, arg): if arg: helptext = 'help_'+arg if helptext not in self.get_names(): argprefixlist = [a[5:] for a in self.get_names() if a.startswith(helptext)] if len(argprefixlist) == 1: arg = argprefixlist.pop() elif len(argprefixlist) > 1: self.warn("Command `%s' is ambiguous.\n" % arg) return cmd.Cmd.do_help(self, arg) def say(self, msg): try: sys.stdout.write(polystr(msg)) except UnicodeEncodeError as e: print("Unicode failure:", e) print("msg:\n", repr(msg)) raise e sys.stdout.flush() # In case we're piping the output def warn(self, msg): sys.stderr.write(polystr(msg)) def help_help(self): self.say("""\ function: tell the use and syntax of commands usage: help [ command ] """) # Unexposed helper tables and functions begin here def __dogetassoc(self): try: self.peers = self.session.readstat() except ntp.packet.ControlException as e: self.warn(e.message + "\n") return False except IOError as e: print(e.strerror) return False if len(self.peers) == 0: if self.chosts: self.say("server=%s " % self.session.hostname) self.say("No association IDs returned\n") return False if self.debug: self.warn("\n%d associations total\n" % len(self.peers)) # sortassoc() return True def __printassoc(self, showall): if not self.peers: self.say("No association IDs in list\n") return self.say( "\nind assid status conf reach auth condition last_event cnt\n") self.say( "===========================================================\n") for (i, peer) in enumerate(self.peers): statval = ntp.control.CTL_PEER_STATVAL(peer.status) if ((not showall and (statval & (ntp.control.CTL_PST_CONFIG | ntp.control.CTL_PST_REACH)) == 0)): continue sw = ntp.util.PeerStatusWord(peer.status) display = "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu" \ % (i + 1, peer.associd, peer.status, sw.conf, sw.reach, sw.auth, sw.condition, sw.last_event, sw.event_count) self.say(display + "\n") def __dopeers(self, showall, mode): if not self.__dogetassoc(): return report = ntp.util.PeerSummary(mode, self.pktversion, self.showhostnames, self.wideremote, self.showunits, termwidth=interpreter.termwidth, debug=interpreter.debug, logfp=self.logfp) try: maxhostlen = 0 if len(self.chosts) > 1: maxhostlen = max([len(host) for (host, _af) in self.chosts]) self.say("%-*.*s " % (maxhostlen, maxhostlen+1, "server")) self.say(report.header() + "\n") if len(self.chosts) > 1: maxhostlen = max([len(host) for (host, _af) in self.chosts]) self.say("=" * (maxhostlen + 1)) self.say(("=" * report.width()) + "\n") for peer in self.peers: if ((not showall and not (ntp.control.CTL_PEER_STATVAL(peer.status) & (ntp.control.CTL_PST_CONFIG | ntp.control.CTL_PST_REACH)))): if self.debug: self.warn("eliding [%d]\n" % peer.associd) continue try: variables = self.session.readvar(peer.associd, raw=True) except ntp.packet.ControlException as e: self.warn(e.message + "\n") return except IOError as e: print(e.strerror) return if not variables: if len(self.chosts) > 1: self.warn("server=%s " % self.session.hostname) self.warn("***No information returned for association %d\n" % peer.associd) continue if len(self.chosts) > 1: self.say(ntp.util.PeerSummary.high_truncate( self.session.hostname, maxhostlen) + " ") self.say(report.summary(self.session.rstatus, variables, peer.associd)) except KeyboardInterrupt: pass def __assoc_valid(self, line, required=False): "Process a numeric associd or index." # FIXME: This does a useless call to __dogetassoc() when associd == 0 # No big deal most of the time. Just a useless packet exchange. if not line: if required: self.warn("An associd argument is required.\n") return -1 else: return 0 if not self.peers: self.__dogetassoc() if line.startswith("&"): try: idx = int(line[1:].split()[0]) except: self.warn("Invalid index literal.\n") return -1 if idx < 0 or idx >= 2**16-1: self.warn("%d is not a valid association number.\n" % idx) return -1 elif idx not in range(1, len(self.peers)+1): self.warn("No such association as %d.\n" % idx) return -1 else: return self.peers[idx - 1].associd else: try: associd = int(line.split()[0]) except: self.warn("Invalid associd literal.\n") return -1 if ((associd != 0 and associd not in [peer.associd for peer in self.peers])): self.warn("Unknown associd.\n") return -1 else: return associd def __assoc_range_valid(self, line): "Try to get a range of assoc IDs." tokens = line.split() if len(tokens) < 2: return () lo = self.__assoc_valid(tokens[0]) hi = self.__assoc_valid(tokens[1]) if lo < 0 or hi < 0 or hi < lo: return () if lo == hi: return(lo,) return range(lo, hi+1) def printvars(self, variables, dtype, quiet): "Dump variables in raw (actually, semi-cooked) mode." if self.rawmode: if not quiet: self.say("status=0x%04x,\n" % self.session.rstatus) # C ntpq not only suppressed \r but tried to visibilize # high-half characters. We won't do that unless somebody # files a bug, Mode 6 never seems to generate those in # variable fetches. text = polystr(session.response.replace(polybytes(",\r\n"), polybytes(",\n"))) else: if not quiet: self.say("status=%04x %s,\n" % (self.session.rstatus, ntp.ntpc.statustoa(dtype, self.session.rstatus))) text = ntp.util.cook(variables, self.showunits) text = text.replace("'", '"') self.say(text) def __dolist(self, varlist, associd, op, type, quiet=False): "List variables associated with a specified peer." try: variables = self.session.readvar(associd, varlist, op, raw=True) except ntp.packet.ControlException as e: self.warn(e.message + "\n") return False except IOError as e: print(e.strerror) return False if len(self.chosts) > 1: self.say("server=%s " % self.session.hostname) if not variables: if associd == 0: self.say("No system%s variables returned\n" % " clock" if (type == ntp.ntpc.TYPE_CLOCK) else "") else: self.say("No information returned for%s association %d\n" % (" clock" if (type == ntp.ntpc.TYPE_CLOCK) else "", associd)) return True if not quiet: self.say("associd=%d " % associd) self.printvars(variables, type, not (not varlist)) return True # Unexposed helper tables and functions end here def do_units(self, _unused): "toggle unit display" self.showunits = not self.showunits def help_units(self): self.say("""\ function: toggle unit display usage: units """) def do_EOF(self, _unused): "exit ntpq" self.say("\n") return True def do_timeout(self, line): "set the primary receive time out" if line: try: self.session.primary_timeout = int(line) except ValueError: print("What?") print("primary timeout %d ms" % self.session.primary_timeout) def help_timeout(self): self.say("""\ function: set the primary receive time out usage: timeout [ msec ] """) def collect_display(self, associd, variables, decodestatus): "Query and display a collection of variables from the system." try: queried = self.session.readvar(associd, [v[0] for v in variables], raw=True) except ntp.packet.ControlException as e: if ntp.control.CERR_UNKNOWNVAR == e.errorcode: self.warn("Unknown variable. Trying one at a time.\n") varlist = [v[0] for v in variables] items = [] for var in varlist: try: queried = self.session.readvar(associd, [var], raw=True) for (name, (value, rawvalue)) in queried.items(): items.append((name, (value, rawvalue))) except ntp.packet.ControlException as e: if ntp.control.CERR_UNKNOWNVAR == e.errorcode: items.append((var, "???")) continue raise e queried = ntp.util.OrderedDict(items) else: self.warn(e.message + "\n") return except IOError as e: print(e.strerror) return if self.rawmode: self.say(self.session.response) return if decodestatus: if associd == 0: statype = ntp.ntpc.TYPE_SYS else: statype = ntp.ntpc.TYPE_PEER self.say("associd=%u status=%04x %s,\n" % (associd, self.session.rstatus, ntp.ntpc.statustoa(statype, self.session.rstatus))) try: for (name, legend, fmt) in variables: if name not in queried: continue value = queried[name][0] rawvalue = queried[name][1] if fmt in (NTP_ADD, NTP_ADP): if self.showhostnames: if self.debug: self.say("DNS lookup begins...") value = ntp.util.canonicalize_dns( value, family=self.ai_family) if self.debug: self.say("DNS lookup complete.") self.say("%s %s\n" % (legend, value)) elif fmt == NTP_STR: if value: self.say("%s %s\n" % (legend, value)) elif fmt in (NTP_UINT, NTP_INT, NTP_FLOAT): if self.showunits: displayvalue = ntp.util.unitifyvar(rawvalue, name) else: displayvalue = value self.say("%s %s\n" % (legend, displayvalue)) elif fmt == NTP_LFP: self.say("%s %s\n" % (legend, ntp.ntpc.prettydate(value))) elif fmt == NTP_2BIT: self.say("%s %s\n" % (legend, ("00", "01", "10", "11")[value])) elif fmt == NTP_MODE: modes = ( "unspec", "sym_active", "sym_passive", "client", "server", "broadcast", "control", "private", "bclient" ) try: self.say("%s %s\n" % (legend, modes[value])) except IndexError: self.say("%s %s%d\n" % (legend, "mode#", value)) else: self.warn("unexpected vc type %s for %s, value %s\n" % (fmt, name, value)) except KeyboardInterrupt: self.warn("display interrupted\n") def do_delay(self, line): "set the delay added to encryption time stamps" if not line: self.say("delay %d ms\n" % self.auth_delay) else: try: self.auth_delay = int(line) if self.auth_delay < 0: raise ValueError except ValueError: self.say("Huh?") def help_delay(self): self.say("""\ function: set the delay added to encryption time stamps usage: delay [ msec ] """) def do_host(self, line): "specify the host whose NTP server we talk to" if not line: if self.session.havehost(): print("current host is %s" % self.session.hostname) else: print("no current host") else: tokens = line.split() if tokens[0] == '-4': session.ai_family = socket.AF_INET tokens.pop(0) elif tokens[0] == '-6': session.ai_family = socket.AF_INET6 tokens.pop(0) try: if ((tokens and self.session.openhost(tokens[0], session.ai_family))): print("current host set to %s" % self.session.hostname) elif self.session.havehost(): print("current host remains %s" % self.session.hostname) else: print("still no current host") except KeyboardInterrupt: print("lookup interrupted") def help_host(self): self.say("""\ function: specify the host whose NTP server we talk to usage: host [-4|-6] [hostname] """) def do_poll(self, line): "poll an NTP server in client mode `n' times" # And it's not in the C version, so we're off the hook here print("poll not implemented yet") def help_poll(self): self.say("""\ function: poll an NTP server in client mode `n' times usage: poll [n] [verbose] """) def do_passwd(self, line): "specify a password to use for authenticated requests" try: self.session.password() except ntp.packet.ControlException as e: self.warn(e.message + "\n") def help_passwd(self): self.say("""\ function: specify a password to use for authenticated requests usage: passwd [] """) def do_hostnames(self, line): "specify whether hostnames or net numbers are printed" if not line: pass elif line == "yes": self.showhostnames = True elif line == "no": self.showhostnames = False else: print("What?") if self.showhostnames: print("hostnames being shown") else: print("hostnames not being shown") def help_hostnames(self): self.say("""\ function: specify whether hostnames or net numbers are printed usage: hostnames [yes|no] """) def do_debug(self, line): "set/change debugging level" if not line: pass elif line == "more": self.debug += 1 elif line == "less": if self.debug > 0: self.debug -= 1 elif line == "no": self.debug = 0 else: try: self.debug = int(line) # C version didn't implement this except ValueError: print("What?") self.session.debug = self.debug print("debug level is %d" % self.debug) def do_logfile(self, line): """view/change logfile. \"\" will log to stderr instead of a file""" if not line: print(repr(self.logfp.name)) return if self.logfp != sys.stderr: self.logfp.close() if line == "": self.logfp = self.session.logfp = sys.stderr else: try: logfp = open(line, "a", 1) # 1 => line buffered self.logfp = self.session.logfp = logfp print("Logfile set to", line) except: print("Could not open", line, "for logging.") def help_debug(self): self.say("""\ function: set/change debugging level usage: debug [no|more|less|n] """) def do_exit(self, line): "exit ntpq" return True def help_exit(self): self.say("""\ function: exit ntpq usage: exit """) do_quit = do_exit def help_quit(self): self.say("""\ function: exit ntpq usage: quit """) def do_keyid(self, line): "set keyid to use for authenticated requests" if line: try: self.session.keyid = int(line) except ValueError: print("What?") if self.session.keyid is None: print("no keyid defined") else: print("keyid is %d" % self.session.keyid) def help_keyid(self): self.say("""\ function: set keyid to use for authenticated requests usage: keyid [key#] """) def do_version(self, line): "print version number" print(version) def help_version(self): self.say("""\ function: print version number usage: version """) def do_direct(self, line): "toggle direct mode output" self.directmode = not self.directmode if self.directmode: print("Direct mode is on") else: print("Direct mode is off") def help_direct(self): self.say("""\ function: toggle direct-mode MRU output usage: direct """) def do_raw(self, line): "do raw mode variable output" self.rawmode = True print("Output set to raw") def help_raw(self): self.say("""\ function: do raw mode variable output usage: raw """) def do_cooked(self, line): "do cooked mode variable output" self.rawmode = False print("Output set to cooked") def help_cooked(self): self.say("""\ function: do cooked mode variable output usage: cooked """) def do_authenticate(self, line): "always authenticate requests to this server" if not line: pass elif line == "yes": self.session.always_auth = True elif line == "no": self.session.always_auth = False else: print("What?") if self.session.always_auth: print("authenticated requests being sent") else: print("unauthenticated requests being sent") def help_authenticate(self): self.say("""\ function: always authenticate requests to this server usage: authenticate [yes|no] """) def do_ntpversion(self, line): "set the NTP version number to use for requests" if not line: pass else: try: newversion = int(line) if ((newversion >= ntp.magic.NTP_OLDVERSION and newversion <= ntp.magic.NTP_VERSION)): self.pktversion = newversion else: print("versions %d to %d, please" % (ntp.magic.NTP_OLDVERSION, ntp.magic.NTP_VERSION)) except ValueError: print("What?") print("NTP version being claimed is %d" % self.pktversion) def help_ntpversion(self): self.say("""\ function: set the NTP version number to use for requests usage: ntpversion [version number] """) def do_keytype(self, line): "set key type to use for authenticated requests" if not line: self.say("Keytype: %s\n" % self.session.keytype) elif line not in "DSA, MD4, MD5, MDC2, RIPEMD160, SHA1": self.warn("Keytype %s is not supported by ntpd.\n" % line) elif line not in hashlib.algorithms_available: self.warn("Keytype %s is not supported by ntpq.\n" % line) else: self.session.keytype = line def help_keytype(self): self.say("""\ function: set key type to use for authenticated requests, one of: DSA, MD4, MD5, MDC2, RIPEMD160, SHA1 usage: keytype [digest-name] """) def do_associations(self, line): "print list of association IDs and statuses for the server's peers" if self.__dogetassoc(): self.__printassoc(showall=True) def help_associations(self): self.say("""\ function: print list of association IDs and statuses for the server's peers usage: associations """) def do_passociations(self, line): "print list of associations returned by last associations command" self.__printassoc(showall=True) def help_passociations(self): self.say("""\ function: print list of associations returned by last associations command usage: passociations """) def do_lassociations(self, line): "print list of associations including all client information" if self.__dogetassoc(): self.__printassoc(showall=True) def help_lassociations(self): self.say("""\ function: print list of associations including all client information usage: lassociations """) def do_lpassociations(self, line): """\ print last obtained list of associations, including client information """ self.__printassoc(showall=True) def help_lpassociations(self): self.say("""\ function: print last obtained list of associations, including client information usage: lpassociations """) def do_addvars(self, line): "add variables to the variable list or change their values" if not line: self.warn("usage: addvars name[=value][,...]\n") return vars_to_add = line.split(',') for add_var in vars_to_add: try: (name, val) = add_var.split("=") except ValueError: (name, val) = (add_var, "") self.uservars[name.strip()] = val.strip() def help_addvars(self): self.say("""\ function: add variables to the variable list or change their values usage: addvars name[=value][,...] """) def do_rmvars(self, line): "remove variables from the variable list" if not line: self.warn("usage: rmvars name[,...]\n") return vars_to_rm = line.split(',') for rm_var in vars_to_rm: if rm_var not in self.uservars: print("%s is not in the variable list" % rm_var) else: del self.uservars[rm_var] def help_rmvars(self): self.say("""\ function: remove variables from the variable list usage: rmvars name[,...] """) def do_clearvars(self, line): "remove all variables from the variable list" self.uservars.clear() def help_clearvars(self): self.say("""\ function: remove all variables from the variable list usage: clearvars """) def do_showvars(self, line): "print variables on the variable list" if len(self.uservars) == 0: print("No variables on list.") for (name, value) in self.uservars.items(): if value: print("%s=%s" % (name, value)) else: print(name) def help_showvars(self): self.say("""\ function: print variables on the variable list usage: showvars """) def do_readlist(self, line): "read the system or peer variables included in the variable list" associd = self.__assoc_valid(line) if associd >= 0: qtype = ntp.ntpc.TYPE_SYS if associd == 0 else ntp.ntpc.TYPE_PEER self.__dolist(self.uservars.keys(), associd, ntp.control.CTL_OP_READVAR, qtype) def help_readlist(self): self.say("""\ function: read the system or peer variables included in the variable list usage: readlist [assocID] """) def do_rl(self, line): "read the system or peer variables included in the variable list" self.do_readlist(line) def help_rl(self): self.say("""\ function: read the system or peer variables included in the variable list usage: rl [assocID] """) def do_writelist(self, line): "write the system or peer variables included in the variable list" pass def help_writelist(self): self.say("""\ function: write the system or peer variables included in the variable list usage: writelist [ assocID ] """) def do_readvar(self, line): "read system or peer variables" associd = self.__assoc_valid(line) if associd >= 0: qtype = ntp.ntpc.TYPE_SYS if associd == 0 else ntp.ntpc.TYPE_PEER self.__dolist(line.split()[1:], associd, ntp.control.CTL_OP_READVAR, qtype, quiet=True) def help_readvar(self): self.say("""\ function: read system or peer variables usage: readvar [assocID] [varname1] [varname2] [varname3] """) def do_rv(self, line): "read system or peer variables" self.do_readvar(line) def help_rv(self): self.say("""\ function: read system or peer variables usage: rv [assocID] [varname1] [varname2] [varname3] """) def do_writevar(self, line): "write system or peer variables" pass def help_writevar(self): self.say("""\ function: write system or peer variables usage: writevar assocID name=value,[...] """) def do_mreadlist(self, line): "read the peer variables in the variable list for multiple peers" if not line: self.warn("usage: mreadlist assocIDlow assocIDhigh\n") return idrange = self.__assoc_range_valid(line) if not idrange: return for associd in idrange: if associd != idrange[0]: self.say("\n") if not self.__dolist(self.uservars, associd, ntp.control.CTL_OP_READVAR, ntp.ntpc.TYPE_PEER): return def help_mreadlist(self): self.say("""\ function: read the peer variables in the variable list for multiple peers usage: mreadlist assocIDlow assocIDhigh """) def do_mrl(self, line): "read the peer variables in the variable list for multiple peers" if not line: self.warn("usage: mrl assocIDlow assocIDhigh\n") return self.do_mreadlist(line) def help_mrl(self): self.say("""\ function: read the peer variables in the variable list for multiple peers usage: mrl assocIDlow assocIDhigh """) def do_mreadvar(self, line): "read peer variables from multiple peers" if not line: self.warn("usage: mreadvar assocIDlow assocIDhigh " "[ name=value[,...] ]\n") return idrange = self.__assoc_range_valid(line) if not idrange: return varlist = line.split()[2:] for associd in idrange: if associd != idrange[0]: self.say("\n") if not self.__dolist(varlist, associd, ntp.control.CTL_OP_READVAR, ntp.ntpc.TYPE_PEER): return def help_mreadvar(self): self.say("""\ function: read peer variables from multiple peers usage: mreadvar assocIDlow assocIDhigh [name=value[,...]] """) def do_mrv(self, line): "read peer variables from multiple peers" if not line: self.warn( "usage: mrv assocIDlow assocIDhigh [name=value[,...]]\n") return self.do_mreadvar(line) def help_mrv(self): self.say("""\ function: read peer variables from multiple peers usage: mrv assocIDlow assocIDhigh [name=value[,...]] """) def do_clocklist(self, line): "read the clock variables included in the variable list" assoc = self.__assoc_valid(line) if assoc >= 0: self.__dolist(self.uservars.keys(), assoc, ntp.control.CTL_OP_READCLOCK, ntp.ntpc.TYPE_CLOCK) def help_clocklist(self): self.say("""\ function: read the clock variables included in the variable list usage: clocklist [assocID] """) def do_cl(self, line): "read the clock variables included in the variable list" self.do_clocklist(line) def help_cl(self): self.say("""\ function: read the clock variables included in the variable list usage: cl [assocID] """) def do_clockvar(self, line): "read clock variables" assoc = self.__assoc_valid(line) if assoc == 0: self.warn("This command requires the association ID of a clock.\n") elif assoc > 0: self.__dolist(line.split()[1:], assoc, ntp.control.CTL_OP_READCLOCK, ntp.ntpc.TYPE_CLOCK) def help_clockvar(self): self.say("""\ function: read clock variables usage: clockvar [assocID] [name=value[,...]] """) def do_cv(self, line): "read clock variables" self.do_clockvar(line) def help_cv(self): self.say("""\ function: read clock variables usage: cv [ assocID ] [ name=value[,...] ] """) def do_pstats(self, line): "show statistics for a peer" pstats = ( ("srcadr", "remote host: ", NTP_ADD), ("dstadr", "local address: ", NTP_ADD), ("timerec", "time last received: ", NTP_INT), ("timer", "time until next send:", NTP_INT), ("timereach", "reachability change: ", NTP_INT), ("sent", "packets sent: ", NTP_INT), ("received", "packets received: ", NTP_INT), ("badauth", "bad authentication: ", NTP_INT), ("bogusorg", "bogus origin: ", NTP_INT), ("oldpkt", "duplicate: ", NTP_INT), ("seldisp", "bad dispersion: ", NTP_INT), ("selbroken", "bad reference time: ", NTP_INT), ("candidate", "candidate order: ", NTP_INT), ) if not line: self.warn("usage: pstats assocID\n") return associd = self.__assoc_valid(line) if associd >= 0: self.collect_display(associd=associd, variables=pstats, decodestatus=True) def help_pstats(self): self.say("""\ function: show statistics for a peer usage: pstats assocID """) def do_peers(self, line): "obtain and print a list of the server's peers [IP version]" self.__dopeers(showall=True, mode="peers") def help_peers(self): self.say("""\ function: obtain and print a list of the server's peers [IP version] usage: peers """) def do_apeers(self, line): """ obtain and print a list of the server's peers and their assocIDs [IP version] """ self.__dopeers(showall=True, mode="apeers") def help_apeers(self): self.say("""\ function: obtain and print a list of the server's peers and their assocIDs [IP version] usage: apeers """) def do_lpeers(self, line): "obtain and print a list of all peers and clients [IP version]" self.__dopeers(showall=True, mode="peers") def help_lpeers(self): self.say("""\ function: obtain and print a list of all peers and clients [IP version] usage: lpeers """) def do_opeers(self, line): """ print peer list the old way, with dstadr shown rather than refid [IP version] """ self.__dopeers(showall=True, mode="opeers") def help_opeers(self): self.say("""\ function: print peer list the old way, with dstadr shown rather than refid [IP version] usage: opeers """) def do_lopeers(self, line): """obtain and print a list of all peers and clients showing dstadr [IP version]""" self.__dopeers(showall=True, mode="opeers") def help_lopeers(self): self.say("""\ function: obtain and print a list of all peers and clients showing dstadr [IP version] usage: lopeers """) def do_hot_config(self, line): "send a remote configuration command to ntpd" try: self.session.password() except ntp.packet.ControlException as e: self.warn(e.message + "\n") return if self.debug > 2: self.warn("In Config\nKeyword = :config\nCommand = %s\n" % line) try: self.session.config(line) m = re.match("column ([0-9]+) syntax error", self.session.response) if m: col = int(m.group(1)) if col >= 0 and col <= len(line): if self.interactive: self.say("_" * (len(self.prompt) + 2 + col)) else: self.say(line + "\n") self.say("_" * (col - 1)) self.say("^\n") self.say(self.session.response + "\n") except ntp.packet.ControlException as e: self.warn(e.message + "\n") def help_hot_config(self): self.say("""\ function: send a remote configuration command to ntpd usage: config """) def do_config_from_file(self, line): "configure ntpd using the configuration filename" try: with open(line) as rfp: self.say("%s\n" % self.session.config(rfp.read())) except IOError: self.warn("Could not read %s\n" % line) def help_config_from_file(self): self.say("""\ function: configure ntpd using the configuration filename usage: config_from_file """) def printdirect(self, entries): for entry in entries: self.say(self.formatter.summary(entry) + "\n") def do_mrulist(self, line): """display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x...""" cmdvars = {} for item in line.split(" "): if not item: continue if '=' not in item: cmdvars[item] = True else: eq = item.index("=") var = item[:eq].strip() val = item[eq+1:].strip() try: val = int(val, 0) except ValueError: try: val = float(val) except ValueError: if val[0] == '"' and val[-1] == '"': val = val[1:-1] cmdvars[var] = val if not self.directmode: self.say("Ctrl-C will stop MRU retrieval and display " "partial results.\n") if self.rawmode: mruhook = lambda v: self.printvars(variables=v, dtype=ntp.ntpc.TYPE_SYS, quiet=True) else: mruhook = None try: formatter = ntp.util.MRUSummary(interpreter.showhostnames, wideremote=True) if self.directmode: formatter.now = None self.formatter = formatter if session.debug: formatter.logfp = session.logfp formatter.debug = session.debug self.session.slots = 0 self.session.start = time.time() direct = self.printdirect if self.directmode else None span = self.session.mrulist(variables=cmdvars, rawhook=mruhook, direct=direct) if not self.directmode and not self.rawmode: if not span.is_complete(): self.say("mrulist retrieval interrupted by operator.\n" "Displaying partial client list.\n") span.now = time.time() try: delta1 = time.time() - self.session.start self.say(ntp.util.MRUSummary.header + "\n") self.say(("=" * len(ntp.util.MRUSummary.header)) + "\n") # reversed puts most recent entries at the top if no sort= # see sort comments in pylib/packet.py formatter.now = span.now for entry in reversed(span.entries): self.say(formatter.summary(entry) + "\n") self.say("# Collected %d slots in %.3f seconds\n" % (self.session.slots, delta1)) except KeyboardInterrupt: pass delta2 = time.time() - self.session.start self.say("# Processed %d slots in %.3f seconds\n" % (self.session.slots, delta2)) usage = resource.getrusage(resource.RUSAGE_SELF) rusage_denom = 1024. if sys.platform == 'darwin': # OSX uses bytes, while every other platform uses kilobytes rusage_denom = rusage_denom * rusage_denom self.say("# Used %d megabytes of memory\n" % (usage.ru_maxrss/rusage_denom)) except ntp.packet.ControlException as e: # Giving up after 8 restarts from the beginning. # With high-traffic NTP servers, this can occur if the # MRU list is limited to less than about 16 seconds' of # entries. See the 'mru' ntp.conf entry. self.warn(e.message + "\n") def help_mrulist(self): self.say("""\ function: display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x... usage: mrulist [tag=value] [tag=value] [tag=value] [tag=value] """) def do_ifstats(self, line): "show statistics for each local address ntpd is using" try: self.session.password() entries = self.session.ifstats() if self.rawmode: print(self.session.response) else: formatter = ntp.util.IfstatsSummary() self.say(ntp.util.IfstatsSummary.header) self.say(("=" * ntp.util.IfstatsSummary.width) + "\n") for (i, entry) in enumerate(entries): self.say(formatter.summary(i, entry)) except ntp.packet.ControlException as e: self.warn(e.message + "\n") return pass def help_ifstats(self): self.say("""\ function: show statistics for each local address ntpd is using usage: ifstats """) def do_reslist(self, line): "show ntpd access control list" try: self.session.password() entries = self.session.reslist() if self.rawmode: print(self.session.response) else: formatter = ntp.util.ReslistSummary() self.say(ntp.util.ReslistSummary.header) self.say(("=" * ntp.util.ReslistSummary.width) + "\n") for entry in entries: self.say(formatter.summary(entry)) except ntp.packet.ControlException as e: self.warn(e.message + "\n") return def help_reslist(self): self.say("""\ function: show ntpd access control list usage: reslist """) def do_sysinfo(self, _line): "display system summary" sysinfo = ( ("peeradr", "system peer: ", NTP_ADP), ("peermode", "system peer mode: ", NTP_MODE), ("leap", "leap indicator: ", NTP_2BIT), ("stratum", "stratum: ", NTP_INT), ("precision", "log2 precision: ", NTP_INT), ("rootdelay", "root delay: ", NTP_FLOAT), ("rootdisp", "root dispersion: ", NTP_FLOAT), ("rootdist", "root distance ", NTP_FLOAT), ("refid", "reference ID: ", NTP_STR), ("reftime", "reference time: ", NTP_LFP), ("sys_jitter", "system jitter: ", NTP_FLOAT), ("clk_jitter", "clock jitter: ", NTP_FLOAT), ("clk_wander", "clock wander: ", NTP_FLOAT), ("authdelay", "symm. auth. delay:", NTP_FLOAT), ) self.collect_display(associd=0, variables=sysinfo, decodestatus=True) def help_sysinfo(self): self.say("""\ function: display system summary usage: sysinfo """) def do_kerninfo(self, _line): "display kernel loop and PPS statistics" kerninfo = ( ("koffset", "pll offset: ", NTP_FLOAT), ("kfreq", "pll frequency: ", NTP_FLOAT), ("kmaxerr", "maximum error: ", NTP_FLOAT), ("kesterr", "estimated error: ", NTP_FLOAT), ("kstflags", "kernel status: ", NTP_STR), ("ktimeconst", "pll time constant: ", NTP_INT), ("kprecis", "precision: ", NTP_FLOAT), ("kfreqtol", "frequency tolerance: ", NTP_INT), ("kppsfreq", "pps frequency: ", NTP_INT), ("kppsstab", "pps stability: ", NTP_INT), ("kppsjitter", "pps jitter: ", NTP_INT), ("kppscalibdur", "calibration interval ", NTP_INT), ("kppscalibs", "calibration cycles: ", NTP_INT), ("kppsjitexc", "jitter exceeded: ", NTP_INT), ("kppsstbexc", "stability exceeded: ", NTP_INT), ("kppscaliberrs", "calibration errors: ", NTP_INT), ) self.collect_display(associd=0, variables=kerninfo, decodestatus=True) def help_kerninfo(self): self.say("""\ function: display kernel loop and PPS statistics usage: kerninfo """) def do_sysstats(self, _line): "display system uptime and packet counts" sysstats = ( ("ss_uptime", "uptime: ", NTP_INT), ("ss_reset", "sysstats reset: ", NTP_INT), ("ss_received", "packets received: ", NTP_INT), ("ss_thisver", "current version: ", NTP_INT), ("ss_oldver", "older version: ", NTP_INT), ("ss_numctlreq", "control requests: ", NTP_INT), ("ss_badformat", "bad length or format: ", NTP_INT), ("ss_badauth", "authentication failed:", NTP_INT), ("ss_declined", "declined: ", NTP_INT), ("ss_restricted", "restricted: ", NTP_INT), ("ss_limited", "rate limited: ", NTP_INT), ("ss_kodsent", "KoD responses: ", NTP_INT), ("ss_processed", "processed for time: ", NTP_INT), ) self.collect_display(associd=0, variables=sysstats, decodestatus=False) def help_sysstats(self): self.say("""\ function: display system uptime and packet counts usage: sysstats """) def do_monstats(self, _line): "display monitor (mrulist) counters and limits" monstats = ( ("mru_enabled", "enabled: ", NTP_INT), ("mru_depth", "addresses: ", NTP_INT), ("mru_deepest", "peak addresses: ", NTP_INT), ("mru_maxdepth", "maximum addresses: ", NTP_INT), ("mru_mindepth", "reclaim above count: ", NTP_INT), ("mru_maxage", "reclaim maxage: ", NTP_INT), ("mru_minage", "reclaim minage: ", NTP_INT), ("mru_mem", "kilobytes: ", NTP_INT), ("mru_maxmem", "maximum kilobytes: ", NTP_INT), ("mru_exists", "alloc: exists: ", NTP_INT), ("mru_new", "alloc: new: ", NTP_INT), ("mru_recycleold", "alloc: recycle old: ", NTP_INT), ("mru_recyclefull", "alloc: recycle full: ", NTP_INT), ("mru_none", "alloc: none: ", NTP_INT), ("mru_oldest_age", "age of oldest slot: ", NTP_INT), ) self.collect_display(associd=0, variables=monstats, decodestatus=False) def help_monstats(self): self.say("""\ function: display monitor (mrulist) counters and limits usage: monstats """) def do_authinfo(self, _line): "display symmetric authentication counters" authinfo = ( ("authreset", "time since reset:", NTP_INT), ("authkeys", "stored keys: ", NTP_INT), ("authfreek", "free keys: ", NTP_INT), ("authklookups", "key lookups: ", NTP_INT), ("authknotfound", "keys not found: ", NTP_INT), ("authkuncached", "uncached keys: ", NTP_INT), ("authkexpired", "expired keys: ", NTP_INT), ("authencrypts", "encryptions: ", NTP_INT), ("authdecrypts", "decryptions: ", NTP_INT), ) self.collect_display(associd=0, variables=authinfo, decodestatus=False) def help_authinfo(self): self.say("""\ function: display symmetric authentication counters usage: authinfo """) def do_iostats(self, _line): "display network input and output counters" iostats = ( ("iostats_reset", "time since reset: ", NTP_INT), ("total_rbuf", "receive buffers: ", NTP_INT), ("free_rbuf", "free receive buffers: ", NTP_INT), ("used_rbuf", "used receive buffers: ", NTP_INT), ("rbuf_lowater", "low water refills: ", NTP_INT), ("io_dropped", "dropped packets: ", NTP_INT), ("io_ignored", "ignored packets: ", NTP_INT), ("io_received", "received packets: ", NTP_INT), ("io_sent", "packets sent: ", NTP_INT), ("io_sendfailed", "packet send failures: ", NTP_INT), ("io_wakeups", "input wakeups: ", NTP_INT), ("io_goodwakeups", "useful input wakeups: ", NTP_INT), ) self.collect_display(associd=0, variables=iostats, decodestatus=False) def help_iostats(self): self.say("""\ function: display network input and output counters usage: iostats """) def do_timerstats(self, line): "display interval timer counters" timerstats = ( ("timerstats_reset", "time since reset: ", NTP_INT), ("timer_overruns", "timer overruns: ", NTP_INT), ("timer_xmts", "calls to transmit: ", NTP_INT), ) self.collect_display(associd=0, variables=timerstats, decodestatus=False) def help_timerstats(self): self.say("""\ function: display interval timer counters usage: timerstats """) # Default values we use. DEFHOST = "localhost" # default host name # # main - parse arguments and handle options # usage = ''' USAGE: ntpq [-46dphinOV] [-c str] [-D lvl] [host ...] Flg Arg Option-Name Description -4 no ipv4 Force IPv4 DNS name resolution - prohibits the option 'ipv6' -6 no ipv6 Force IPv6 DNS name resolution - prohibits the option 'ipv4' -a Num authentication Enable authentication with the numbered key -c Str command Run a command and exit - may appear multiple times -d no debug-level Increase output debug message level - may appear multiple times -l Str logfile Logs debug messages to the provided filename -D Int set-debug-level Set the output debug message level - may appear multiple times -h no help Print a usage message. -p no peers Print a list of the peers -n no numeric Numeric host addresses -k Str keyfile Specify a keyfile. ntpq will look in this file for the key specified with -a -V opt version Output version information and exit -w no wide Enable wide display of addresses / hosts on a separate line -W Num width Force output width to this value instead of querying the terminal size -u no units Display time with units. ''' if __name__ == '__main__': try: (options, arguments) = getopt.getopt( sys.argv[1:], "46a:c:dD:hk:npVwW:ul:", ["ipv4", "ipv6", "authentication=", "command=", "debug", "set-debug-level=", "help", "keyfile", "numeric", "peers", "version", "wide", "width=", "units", "logfile="]) except getopt.GetoptError as e: sys.stderr.write("%s\n" % e) sys.stderr.write(usage) raise SystemExit(1) progname = os.path.basename(sys.argv[0]) ntp.ntpc.setprogname(progname) session = ntp.packet.ControlSession() interpreter = Ntpq(session) keyid = keyfile = None logfp = sys.stderr for (switch, val) in options: if switch in ("-4", "--ipv4"): interpreter.ai_family = socket.AF_INET elif switch in ("-6", "--ipv6"): interpreter.ai_family = socket.AF_INET6 elif switch in ("-a", "--authentication"): errmsg = "Error: -a parameter '%s' not a number\n" keyid = ntp.util.safeargcast(val, int, errmsg, usage) elif switch in ("-c", "--command"): interpreter.ccmds.append(val) elif switch in ("-d", "--debug"): interpreter.debug += 1 session.debug += 1 elif switch in ("-D", "--set-debug-level"): errmsg = "Error: -D parameter '%s' not a number\n" cast = ntp.util.safeargcast(val, int, errmsg, usage) session.debug = interpreter.debug = cast elif switch in ("-h", "--help"): print(usage) raise SystemExit(0) elif switch in ("-n", "--numeric"): interpreter.showhostnames = False elif switch in ("-p", "--peers"): interpreter.ccmds.append("peers") elif switch in ("-k", "--keyfile"): keyfile = val elif switch in ("-V", "--version"): print("ntpq %s" % version) raise SystemExit(0) elif switch in ("-w", "--wide"): interpreter.wideremote = True elif switch in ("-W", "--width"): errmsg = "Error: -W parameter '%s' not a number\n" interpreter.termwidth = ntp.util.safeargcast(val, int, errmsg, usage) elif switch in ("-u", "--units"): interpreter.showunits = True elif switch in ("-l", "--logfile"): if logfp != sys.stderr: logfp.close() logfp = open(val, "a", 1) # 1 => line buffered session.logfp = interpreter.logfp = logfp if forced_utf8 and interpreter.debug: interpreter.warn("\nforced UTF-8 output\n") if keyfile is not None: # Have a -k, setup the auth credentials = None try: credentials = ntp.packet.Authenticator(keyfile) except (OSError, IOError): sys.stderr.write("ntpq: %s nonexistent or unreadable\n" % keyfile) raise SystemExit(1) if credentials: session.auth = credentials if keyid is not None: # Have an -a session.keyid = keyid for token in arguments: if token.startswith("-"): if '4' == token[-1]: session.ai_family = socket.AF_INET elif '6' == token[-1]: session.ai_family = socket.AF_INET6 else: interpreter.warn("%s: unexpected option-like thing.\n" % progname) raise SystemExit(1) arguments.pop(0) else: interpreter.chosts.append((token, session.ai_family)) if len(arguments) == 0: interpreter.chosts.append((DEFHOST, session.ai_family)) if ((len(interpreter.ccmds) == 0 and not interpreter.interactive and os.isatty(0) and os.isatty(1) )): interpreter.interactive = True try: if len(interpreter.ccmds) == 0: if len(interpreter.chosts) > 1: interpreter.warn( "ntpq can only work interactively on one host.\n") interpreter.chosts = interpreter.chosts[:1] session.openhost(*interpreter.chosts[0]) interpreter.cmdloop() else: for ihost in interpreter.chosts: if session.openhost(*ihost): for command in interpreter.ccmds: interpreter.onecmd(command) session.close() raise SystemExit(0) except (KeyboardInterrupt, EOFError): if os.isatty(0): interpreter.say("\n") except ntp.packet.ControlException as e: interpreter.warn(e.message + "\n") except IOError: print("Bailing out...") # end ntpsec-1.1.0+dfsg1/ntpclients/ntploggps.py0000644000175000017500000001553113252364117020404 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- """\ usage: ntploggps [-h] [-o] [-l LOGFILE] [-v] [-V] gpsd log file generator optional arguments: -h, --help show this help message and exit -l LOGFILE, --logfile LOGFILE append log data to LOGFILE instead of stdout -o, --once log one line, then exit -w WAIT, --wait WAIT wait WAIT seconds after each log line, default 5 -v, --verbose be verbose -V, --version show program's version number and exit See the manual page for details. """ from __future__ import print_function import io import logging import logging.handlers import sys import threading import time try: import argparse except ImportError: sys.stderr.write(""" ntploggps: can't find the Python argparse module If your Python version is < 2.7, then manual installation is needed: # pip install argparse """) sys.exit(1) try: import gps except ImportError as e: sys.stderr.write("ntploggps: can't find Python GPSD library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) try: import ntp.util except ImportError as e: sys.stderr.write( "ntploggps: can't find Python NTP library -- check PYTHONPATH.\n") sys.stderr.write("%s\n" % e) sys.exit(1) def logging_setup(): "Create logging object" logFormat = logging.Formatter('%(message)s') # Create logger for gpsd Logger = logging.getLogger() Logger.setLevel(logging.INFO) # Create file handler if args.logfile: # log to logfile file = logging.handlers.TimedRotatingFileHandler( args.logfile[0], when='midnight', interval=1) else: # log to stdout file = logging.StreamHandler(sys.stdout) file.setLevel(logging.INFO) # Create the formatter and add it to the handler file.setFormatter(logFormat) # Add the handler to the logger Logger.addHandler(file) return Logger parser = argparse.ArgumentParser(description="gpsd log file generator", epilog=""" See the manual page for details. """) parser.add_argument('-l', '--logfile', dest='logfile', help="append log data to LOGFILE instead of stdout", nargs=1) parser.add_argument('-o', '--once', action="store_true", dest='once', help="log one line, then exit") parser.add_argument('-w', '--wait', default=[5], dest='wait', help="wait WAIT seconds after each log line, default 5", nargs=1, type=int) parser.add_argument('-v', '--verbose', action="store_true", dest='verbose', help="be verbose") parser.add_argument('-V', '--version', action="version", version="ntploggps %s" % ntp.util.stdversion()) args = parser.parse_args() if args.verbose: print("ntploggps: arguments:") print(args) if args.logfile: # log to logfile try: out = open(args.logfile[0], mode='a') except io.UnsupportedOperation as e: sys.stderr.write("ntploggps: can't open logfile %s\n" % args.logfile) sys.stderr.write("%s\n" % e) sys.exit(1) if args.verbose: print("ntploggps: opened log file %s" % args.logfile[0]) else: # log to stdout out = sys.stdout class GpsPoller(threading.Thread): running = False # True when thread is running. Quit when set False def __init__(self): threading.Thread.__init__(self) self.device = None self.satellites_used = None self.tdop = None # start the streaming of gps data try: self.gpsd = gps.gps(mode=gps.WATCH_ENABLE) except BaseException as e: sys.stderr.write("ntploggps: Can't connect to gpsd, %s\n" " Is gpsd running?\n" % e) sys.exit(1) self.running = True def run(self): while gpsp.running: if self.gpsd.read() == -1: self.running = False break if hasattr(self.gpsd, "data"): if self.gpsd.data.get("class") == "SKY": self.satellites_used = 0 self.tdop = self.gpsd.data.get("tdop", 0) for sat in self.gpsd.data.get("satellites", []): if sat["used"]: self.satellites_used += 1 elif self.gpsd.data.get("class") == "TPV": self.device = self.gpsd.data.get("device") @property def time(self): "Return the gpsd time fix" t = self.gpsd.fix.time if isinstance(t, int): return t if isinstance(t, float): if gps.isnan(t): return None return t return gps.isotime(t) if __name__ == '__main__': # this is the main thread if args.verbose: print("ntploggps: creating poll thread") gpsp = GpsPoller() # create the thread try: # Create the logger instance Logger = logging_setup() # Create data layout Logger.info("# Time Device TDOP nSat") gpsp.start() # start it up last_time = 0 while gpsp.running: # It may take a second or two to get good data try: current_time = gpsp.time device = gpsp.device tdop = gpsp.tdop satellites_used = gpsp.satellites_used if current_time is not None and \ device is not None and \ satellites_used is not None and \ tdop is not None: if last_time != current_time: s = '%i %s %f %d' % (current_time, device, tdop, satellites_used) Logger.info(s) last_time = current_time if args.once: # just once break except AttributeError as e: print('parse error\n') # wait a bit before next log time.sleep(args.wait[0]) except (KeyboardInterrupt, SystemExit): # when you press ctrl+c args.once = True # stop the retry loop if args.verbose: print("\nKilling Thread...") else: # print a blank line to make bash happy print("") except Exception as e: # any error, signal print(e) # tell the thread to die gpsp.running = False # wait for the thread to finish what it's doing gpsp.join() if args.verbose: print("ntploggps: Done -- Exiting.") ntpsec-1.1.0+dfsg1/ntpclients/ntpq-man.txt0000644000175000017500000000062013252364117020302 0ustar rlaagerrlaager= ntpq(1) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpq - standard NTP query program include::../docs/includes/ntpq-body.txt[] == EXIT STATUS == One of the following exit values will be returned: 0 (EXIT_SUCCESS):: Successful program execution. 1 (EXIT_FAILURE):: The operation failed or the command syntax was not valid. // end ntpsec-1.1.0+dfsg1/ntpclients/ntploggps-man.txt0000644000175000017500000000062313252364117021340 0ustar rlaagerrlaager= ntploggps(1) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntploggps - log gpsd data include::../docs/includes/ntploggps-body.txt[] == EXIT STATUS == One of the following exit values will be returned: 0 (EXIT_SUCCESS):: Successful program execution. 1 (EXIT_FAILURE):: The operation failed or the command syntax was not valid. // end ntpsec-1.1.0+dfsg1/ntpclients/ntpkeygen.py0000644000175000017500000000600313252364117020365 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- # # ntpkeygen - generate cryptographic keys for NTP clients and servers # # All file names are like "ntpkey__.", where # is the file type, the generating host name and # the generation time in NTP seconds. The NTP programs # expect generic names such as "ntpkey__whimsy.udel.edu" with the # association maintained by soft links. Following is a list of file # types. # # ntpkey_MD5key_. # MD5 (128-bit) keys used to compute message digests in symmetric # key cryptography from __future__ import print_function import os import sys import socket import random import time import getopt import stat # # Cryptodefines # MD5KEYS = 10 # number of keys generated of each type MD5SIZE = 20 # maximum key size def gen_md5(id, groupname): "Generate semi-random MD5 and SHA1 keys compatible with NTPv3 and NTPv4." with fheader("MD5key", id, groupname) as wp: for i in range(1, MD5KEYS+1): md5key = "" for j in range(MD5SIZE): while True: r = randomizer.randint(0x21, 0x7e) if r != ord('#'): break md5key += chr(r) wp.write("%2d MD5 %s\n" % (i, md5key)) for i in range(1, MD5KEYS+1): sha1key = "" for j in range(MD5SIZE): sha1key += "%02x" % randomizer.randint(0x00, 0xff) wp.write("%2d SHA1 %s\n" % (i + MD5KEYS, sha1key)) # # Generate file header and link # def fheader(file, # file name id ulink, # linkname owner # owner name ): try: filename = "ntpkey_%s_%s.%u" % (file, owner, int(time.time())) orig_umask = os.umask(stat.S_IWGRP | stat.S_IRWXO) wp = open(filename, "w") os.umask(orig_umask) linkname = "ntp.keys" if os.path.exists(linkname): os.remove(linkname) # The symlink() line below matters os.symlink(filename, linkname) sys.stderr.write("Generating new %s file and link\n" % ulink) sys.stderr.write("%s->%s\n" % (linkname, filename)) wp.write("# %s\n# %s\n" % (filename, time.ctime())) return wp except IOError: sys.stderr.write("Key file creation or link failed.\n") raise SystemExit(1) if __name__ == '__main__': try: (options, arguments) = getopt.getopt(sys.argv[1:], "hM", ["help"]) except getopt.GetoptError as e: print(e) raise SystemExit(1) for (switch, val) in options: if switch == '-M': # dummy MD5 option for backwards compatibility pass elif switch in ("-h", "--help"): print("usage: ntpkeygen [-M]") raise SystemExit(0) # The seed is ignored by random.SystemRandom, # even though the docs do not say so. randomizer = random.SystemRandom() gen_md5("md5", socket.gethostname()) raise SystemExit(0) # end ntpsec-1.1.0+dfsg1/ntpclients/ntpsnmpd.py0000644000175000017500000014637313252364117020243 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function, division import sys import os import getopt import time import socket import subprocess import shlex try: import ntp.packet import ntp.util import ntp.agentx_packet ax = ntp.agentx_packet from ntp.agentx import PacketControl except ImportError as e: sys.stderr.write( "ntpsnmpd: can't find Python NTP library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) # TODO This is either necessary, or a different workaround is. ntp.util.deunicode_units() logfp = sys.stderr nofork = False debug = 0 defaultTimeout = 30 log = (lambda msg, msgdbg: ntp.util.dolog(logfp, msg, debug, msgdbg)) ntpRootOID = (1, 3, 6, 1, 2, 1, 197) # mib-2 . 197, aka: NTPv4-MIB snmpTrapOID = (1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0) snmpSysUptime = (1, 3, 6, 1, 2, 1, 1, 3, 0) DEFHOST = "localhost" DEFLOG = "ntpsnmpd.log" class DataSource(ntp.agentx.MIBControl): def __init__(self, hostname=DEFHOST, settingsFile=None, notifySpin=0.1): # This is defined as a dict tree because it is simpler, and avoids # certain edge cases # OIDs are relative from ntp root oidTree = { # ntpEntNotifications 0: {"subids": # ntpEntNotifModeChange {1: {}, # ntpEntNotifStratumChange 2: {}, # ntpEntNotifSyspeerChange 3: {}, # ntpEntNotifAddAssociation 4: {}, # ntpEntNotifRemoveAsociation 5: {}, # ntpEntNotifConfigChanged 6: {}, # ntpEntNotifLeapSecondAnnounced 7: {}, # ntpEntNotifHeartbeat 8: {}}}, # ntpSnmpMIBObjects 1: {"subids": # ntpEntInfo {1: {"subids": # ntpNetSoftwareName utf8str {1: {"subids": {0: {"reader": (lambda oid: self.cbr_systemInfo(oid, "name"))}}}, # ntpEntSoftwareVersion utf8str 2: {"subids": {0: {"reader": (lambda oid: self.cbr_systemInfo(oid, "version"))}}}, # ntpEntSoftwareVendor utf8str 3: {"subids": {0: {"reader": (lambda oid: self.cbr_systemInfo(oid, "vendor"))}}}, # ntpEntSystemType utf8str 4: {"subids": {0: {"reader": (lambda oid: self.cbr_systemInfo(oid, "system"))}}}, # ntpEntTimeResolution uint32 5: {"subids": {0: {"reader": self.cbr_timeResolution}}}, # ntpEntTimePrecision int32 6: {"subids": {0: {"reader": self.cbr_timePrecision}}}, # ntpEntTimeDistance DisplayString 7: {"subids": {0: {"reader": self.cbr_timeDistance}}}}}, # ntpEntStatus 2: {"subids": # ntpEntStatusCurrentMode INTEGER {...} {1: {"subids": {0: {"reader": self.cbr_statusCurrentMode}}}, # ntpEntStatusStratum NtpStratum 2: {"subids": {0: {"reader": self.cbr_statusStratum}}}, # ntpEntStatusActiveRefSourceId # uint32 (0..99999) 3: {"subids": {0: {"reader": self.cbr_statusActiveRefSourceID}}}, # ntpEntStatusActiveRefSourceName utf8str 4: {"subids": {0: {"reader": self.cbr_statusActiveRefSourceName}}}, # ntpEntStatusActiveOffset DisplayString 5: {"subids": {0: {"reader": self.cbr_statusActiveOffset}}}, # ntpEntStatusNumberOfRefSources # unit32 (0..99) 6: {"subids": {0: {"reader": self.cbr_statusNumRefSources}}}, # ntpEntStatusDispersion DisplayString 7: {"subids": {0: {"reader": self.cbr_statusDispersion}}}, # ntpEntStatusEntityUptime TimeTicks 8: {"subids": {0: {"reader": self.cbr_statusEntityUptime}}}, # ntpEntStatusDateTime NtpDateTime 9: {"subids": {0: {"reader": self.cbr_statusDateTime}}}, # ntpEntStatusLeapSecond NtpDateTime 10: {"subids": {0: {"reader": self.cbr_statusLeapSecond}}}, # ntpEntStatusLeapSecondDirection # int32 (-1..1) 11: {"subids": {0: {"reader": self.cbr_statusLeapSecDirection}}}, # ntpEntStatusInPkts Counter32 12: {"subids": {0: {"reader": self.cbr_statusInPkts}}}, # ntpEntStatusOutPkts Counter32 13: {"subids": {0: {"reader": self.cbr_statusOutPkts}}}, # ntpEntStatusBadVersion Counter32 14: {"subids": {0: {"reader": self.cbr_statusBadVersion}}}, # ntpEntStatusProtocolError Counter32 15: {"subids": {0: {"reader": self.cbr_statusProtocolError}}}, # ntpEntStatusNotifications Counter32 16: {"subids": {0: {"reader": self.cbr_statusNotifications}}}, # ntpEntStatPktModeTable # SEQUENCE of NtpEntStatPktModeEntry 17: {"subids": # ntpEntStatPktModeEntry SEQUENCE {...} {1: {"subids": # ntpEntStatPktMode INTEGER {...} {1: {}, # ntpEntStatPktSent Counter32 2: {}, # ntpEntStatPktRecived Counter32 3: {}}}}}}}, # ntpAssociation 3: {"subids": # ntpAssociationTable # SEQUENCE of NtpAssociationEntry {1: {"subids": # ntpAssociationEntry SEQUENCE {...} {1: {"subids": # ntpAssocId uint32 (1..99999) {1: {"subids": self.sub_assocID}, # ntpAssocName utf8str 2: {"subids": self.sub_assocName}, # ntpAssocRefId DisplayString 3: {"subids": self.sub_assocRefID}, # ntpAssocAddressType # InetAddressType 4: {"subids": self.sub_assocAddrType}, # ntpAssocAddress # InetAddress SIZE (4|8|16|20) 5: {"subids": self.sub_assocAddr}, # ntpAssocOffset DisplayString 6: {"subids": self.sub_assocOffset}, # ntpAssocStratum NtpStratum 7: {"subids": self.sub_assocStratum}, # ntpAssocStatusJitter # DisplayString 8: {"subids": self.sub_assocJitter}, # ntpAssocStatusDelay # DisplayString 9: {"subids": self.sub_assocDelay}, # ntpAssocStatusDispersion # DisplayString 10: {"subids": self.sub_assocDispersion}}}}}, # ntpAssociationStatisticsTable # SEQUENCE of ntpAssociationStatisticsEntry 2: {"subids": # ntpAssociationStatisticsEntry # SEQUENCE {...} {1: {"subids": # ntpAssocStatInPkts Counter32 {1: {"subids": self.sub_assocStatInPkts}, # ntpAssocStatOutPkts Counter32 2: {"subids": self.sub_assocStatOutPkts}, # ntpAssocStatProtocolError # Counter32 3: {"subids": self.sub_assocStatProtoErr}}}}}}}, # ntpEntControl 4: {"subids": # ntpEntHeartbeatInterval unit32 {1: {"subids": {0: {"reader": self.cbr_entHeartbeatInterval, "writer": self.cbw_entHeartbeatInterval}}}, # ntpEntNotifBits BITS {...} 2: {"subids": {0: {"reader": self.cbr_entNotifBits, "writer": self.cbw_entNotifBits}}}}}, # ntpEntNotifObjects 5: {"subids": # ntpEntNotifMessage utf8str {1: {"subids": {0: {"reader": self.cbr_entNotifMessage}}}}}}}, # ntpEntConformance 2: {"subids": # ntpEntCompliances {1: {"subids": # ntpEntNTPCompliance {1: {}, # ntpEntSNTPCompliance 2: {}}}, # ntpEntGroups 2: {"subids": # ntpEntObjectsGroup1 OBJECTS {...} {1: {}, # ntpEntObjectsGroup2 OBJECTS {...} 2: {}, # ntpEntNotifGroup NOTIFICATIONS {...} 3: {}}}}}} ntp.agentx.MIBControl.__init__(self, oidTree, mibRoot=ntpRootOID) self.session = ntp.packet.ControlSession() self.hostname = hostname if hostname else DEFHOST self.session.openhost(self.hostname) self.settingsFilename = settingsFile # Cache so we don't hammer ntpd, default 1 second timeout # Timeout default pulled from a hat: we don't want it to last for # long, just not flood ntpd with duplicatte requests during a walk. self.cache = ntp.util.Cache(1) self.oldValues = {} # Used by notifications to detect changes # spinGap so we don't spam ntpd with requests during notify checks self.notifySpinTime = notifySpin self.lastNotifyCheck = 0 self.lastHeartbeat = 0 # Timestamp used for heartbeat notifications self.heartbeatInterval = 0 # should save to disk self.sentNotifications = 0 # Notify bits, they control whether the daemon sends notifications. # these are saved to disk self.notifyModeChange = False # 1 self.notifyStratumChange = False # 2 self.notifySyspeerChange = False # 3 self.notifyAddAssociation = False # 4 self.notifyRMAssociation = False # 5 self.notifyConfigChange = False # 6 [This is not implemented] self.notifyLeapSecondAnnounced = False # 7 self.notifyHeartbeat = False # 8 self.misc_loadDynamicSettings() # ============================================================= # Data read callbacks start here # comment divider lines represent not yet implemented callbacks # ============================================================= # Blank: notification OIDs def cbr_systemInfo(self, oid, category=None): if category == "name": # The product name of the running NTP data = "NTPsec" elif category == "version": # version string data = ntp.util.stdversion() elif category == "vendor": # vendor/author name data = "Internet Civil Engineering Institute" elif category == "system": # system / hardware info proc = subprocess.Popen(["uname", "-srm"], stdout=subprocess.PIPE) data = proc.communicate()[0] vb = ax.Varbind(ax.VALUE_OCTET_STR, oid, data) return vb def cbr_timeResolution(self, oid): # Uinteger32 # Arrives in fractional milliseconds fuzz = self.safeReadvar(0, ["fuzz"]) if fuzz is None: return None fuzz = fuzz["fuzz"] # We want to emit fractions of seconds # Yes we are flooring instead of rounding: don't want to emit a # resolution value higher than ntpd actually produces. if fuzz != 0: fuzz = int(1 / fuzz) else: fuzz = 0 return ax.Varbind(ax.VALUE_GAUGE32, oid, fuzz) def cbr_timePrecision(self, oid): return self.readCallbackSkeletonSimple(oid, "precision", ax.VALUE_INTEGER) def cbr_timeDistance(self, oid): # Displaystring data = self.safeReadvar(0, ["rootdist"], raw=True) if data is None: return None data = ntp.util.unitifyvar(data["rootdist"][1], "rootdist", width=None, unitSpace=True) return ax.Varbind(ax.VALUE_OCTET_STR, oid, data) # Blank: ntpEntStatus def cbr_statusCurrentMode(self, oid): mode = self.misc_getMode() return ax.Varbind(ax.VALUE_INTEGER, oid, mode) def cbr_statusStratum(self, oid): # NTPstratum return self.readCallbackSkeletonSimple(oid, "stratum", ax.VALUE_GAUGE32) def cbr_statusActiveRefSourceID(self, oid): # range of uint32 syspeer = self.misc_getSyspeerID() return ax.Varbind(ax.VALUE_GAUGE32, oid, syspeer) def cbr_statusActiveRefSourceName(self, oid): # utf8 data = self.safeReadvar(0, ["peeradr"]) if data is None: return None data = ntp.util.canonicalize_dns(data["peeradr"]) return ax.Varbind(ax.VALUE_OCTET_STR, oid, data) def cbr_statusActiveOffset(self, oid): # DisplayString data = self.safeReadvar(0, ["koffset"], raw=True) if data is None: return None data = ntp.util.unitifyvar(data["koffset"][1], "koffset", width=None, unitSpace=True) return ax.Varbind(ax.VALUE_OCTET_STR, oid, data) def cbr_statusNumRefSources(self, oid): # range of uint32 try: data = self.session.readstat() return ax.Varbind(ax.VALUE_GAUGE32, oid, len(data)) except ntp.packet.ControlException: return None def cbr_statusDispersion(self, oid): # DisplayString data = self.safeReadvar(0, ["rootdisp"], raw=True) if data is None: return None return ax.Varbind(ax.VALUE_OCTET_STR, oid, data["rootdisp"][1]) def cbr_statusEntityUptime(self, oid): # TimeTicks # What the spec claims: # The uptime of the NTP entity, (i.e., the time since ntpd was # (re-)initialized not sysUptime!). The time is represented in # hundreds of seconds since Jan 1, 1970 (00:00:00.000) UTC. # # First problem: TimeTicks represents hundred*ths* of seconds, could # easily be a typo. # Second problem: snmpwalk will happily give you a display of # how long a period of time a value is, such as uptime since start. # That is the opposite of what the spec claims. # # I am abandoning the spec, and going with what makes a lick of sense uptime = self.safeReadvar(0, ["ss_reset"]) if uptime is None: return None uptime = uptime["ss_reset"] * 100 return ax.Varbind(ax.VALUE_TIME_TICKS, oid, uptime) def cbr_statusDateTime(self, oid): # NtpDateTime data = self.safeReadvar(0, ["reftime"]) if data is None: return None txt = data["reftime"] value = ntp.util.deformatNTPTime(txt) return ax.Varbind(ax.VALUE_OCTET_STR, oid, value) def cbr_statusLeapSecond(self, oid): # I am not confident in this yet # NtpDateTime DAY = 86400 fmt = "%.8x%.8x" data = self.safeReadvar(0, ["reftime"]) hasleap = self.safeReadvar(0, ["leap"]) if (data is None) or (hasleap is None): return None data = data["reftime"] hasleap = hasleap["leap"] if hasleap in (1, 2): seconds = int(data.split(".")[0], 0) days = seconds // DAY scheduled = (days * DAY) + (DAY - 1) # 23:59:59 of $CURRENT_DAY formatted = fmt % (scheduled, 0) else: formatted = fmt % (0, 0) value = ntp.util.hexstr2octets(formatted) return ax.Varbind(ax.VALUE_OCTET_STR, oid, value) def cbr_statusLeapSecDirection(self, oid): # range of int32 leap = self.safeReadvar(0, ["leap"]) if leap is None: return None leap = leap["leap"] if leap == 1: pass # leap 1 == forward elif leap == 2: leap = -1 # leap 2 == backward else: leap = 0 # leap 0 or 3 == no change return ax.Varbind(ax.VALUE_INTEGER, oid, leap) def cbr_statusInPkts(self, oid): return self.readCallbackSkeletonSimple(oid, "io_received", ax.VALUE_COUNTER32) def cbr_statusOutPkts(self, oid): return self.readCallbackSkeletonSimple(oid, "io_sent", ax.VALUE_COUNTER32) def cbr_statusBadVersion(self, oid): return self.readCallbackSkeletonSimple(oid, "ss_oldver", ax.VALUE_COUNTER32) def cbr_statusProtocolError(self, oid): data = self.safeReadvar(0, ["ss_badformat", "ss_badauth"]) if data is None: return None protoerr = 0 for key in data.keys(): protoerr += data[key] return ax.Varbind(ax.VALUE_COUNTER32, oid, protoerr) def cbr_statusNotifications(self, oid): return ax.Varbind(ax.VALUE_COUNTER32, oid, self.sentNotifications) ############################## # == Dynamics == # assocID # assocName # assocRefID # assocAddrType # assocAddr # assocOffset # assocStratum # assocJitter # assocDelay # assocDispersion # assocInPackets # assocOutPackets # assocProtocolErrors ######################### def cbr_entHeartbeatInterval(self, oid): # uint32 return ax.Varbind(ax.VALUE_GAUGE32, oid, self.heartbeatInterval) def cbr_entNotifBits(self, oid): # BITS data = ax.bools2Bits((False, # notUsed(0) self.notifyModeChange, self.notifyStratumChange, self.notifySyspeerChange, self.notifyAddAssociation, self.notifyRMAssociation, self.notifyConfigChange, self.notifyLeapSecondAnnounced, self.notifyHeartbeat)) return ax.Varbind(ax.VALUE_OCTET_STR, oid, data) ########################## def cbr_entNotifMessage(self, oid): # utf8str return ax.Varbind(ax.VALUE_OCTET_STR, oid, "no event") ######################### # ===================================== # Data write callbacks # Returns an error value (or noError) # Must check that the value is correct for the bind, this does not mean # the type: the master agent handles that # Actions: test, undo, commit, cleanup # ===================================== def cbw_entHeartbeatInterval(self, action, varbind, oldData=None): if action == "test": return ax.ERR_NOERROR elif action == "commit": self.heartbeatInterval = varbind.payload self.misc_storeDynamicSettings() return ax.ERR_NOERROR elif action == "undo": self.heartbeatInterval = oldData self.misc_storeDynamicSettings() return ax.ERR_NOERROR elif action == "cleanup": pass def cbw_entNotifBits(self, action, varbind, oldData=None): if action == "test": return ax.ERR_NOERROR elif action == "commit": (self.notifyModeChange, self.notifyStratumChange, self.notifySyspeerChange, self.notifyAddAssociation, self.notifyRMAssociation, self.notifyConfigChange, self.notifyLeapSecondAnnounced, self.notifyHeartbeat) = ax.bits2Bools(varbind.payload, 8) self.misc_storeDynamicSettings() return ax.ERR_NOERROR elif action == "undo": (self.notifyModeChange, self.notifyStratumChange, self.notifySyspeerChange, self.notifyAddAssociation, self.notifyRMAssociation, self.notifyConfigChange, self.notifyLeapSecondAnnounced, self.notifyHeartbeat) = ax.bits2Bools(oldData, 8) self.misc_storeDynamicSettings() return ax.ERR_NOERROR elif action == "cleanup": pass # ======================================================================== # Dynamic tree generator callbacks # # The structure of these callbacks is somewhat complicated because they # share code that is potentially finicky. # # The dynamicCallbackSkeleton() method handles the construction of the # MIB tree, and the placement of the handler() within it. It also provides # some useful data to the handler() via the readCallback() layer. # ======================================================================== # Packet Mode Table # These are left as stubs for now. Information is lacking on where the # data should come from. def sub_statPktMode(self): pass def sub_statPktSent(self): pass def sub_statPktRecv(self): pass # Association Table def sub_assocID(self): def handler(oid, associd): return ax.Varbind(ax.VALUE_GAUGE32, oid, associd) return self.dynamicCallbackSkeleton(handler) def sub_assocName(self): return self.dynamicCallbackPeerdata("srcadr", True, ax.VALUE_OCTET_STR) def sub_assocRefID(self): def handler(oid, associd): pdata = self.misc_getPeerData() if pdata is None: return None # elaborate code in util.py indicates this may not be stable try: refid = pdata[associd]["refid"][1] except IndexError: refid = "" return ax.Varbind(ax.VALUE_OCTET_STR, oid, refid) return self.dynamicCallbackSkeleton(handler) def sub_assocAddrType(self): def handler(oid, associd): pdata = self.misc_getPeerData() if pdata is None: return None srcadr = pdata[associd]["srcadr"][1] try: socklen = len(socket.getaddrinfo(srcadr, None)[0][-1]) except socket.gaierror: socklen = None if socklen == 2: # ipv4 addrtype = 1 elif socklen == 4: # ipv6 addrtype = 2 else: # there is also ipv4z and ipv6z..... don't know how to # detect those yet. Or if I even need to. addrtype = 0 # is this ok? or should it return a NULL? return ax.Varbind(ax.VALUE_INTEGER, oid, addrtype) return self.dynamicCallbackSkeleton(handler) def sub_assocAddr(self): def handler(oid, associd): pdata = self.misc_getPeerData() if pdata is None: return None srcadr = pdata[associd]["srcadr"][1] # WARNING: I am only guessing that this is correct # Discover what type of address we have try: sockinfo = socket.getaddrinfo(srcadr, None)[0][-1] addr = sockinfo[0] ipv6 = True if len(sockinfo) == 4 else False except socket.gaierror: addr = None # how to handle? ipv6 = None # Convert address string to octets srcadr = [] if ipv6 is False: pieces = addr.split(".") for piece in pieces: try: srcadr.append(int(piece)) # feed it a list of ints except ValueError: # Have gotten piece == "" before. Skip over that. # Still try to return data because it is potential # debugging information. continue elif ipv6 is True: pieces = addr.split(":") for piece in pieces: srcadr.append(ntp.util.hexstr2octets(piece)) srcadr = "".join(srcadr) # feed it an octet string # The octet string encoder can handle either chars or 0-255 # ints. We use both of those options. return ax.Varbind(ax.VALUE_OCTET_STR, oid, srcadr) return self.dynamicCallbackSkeleton(handler) def sub_assocOffset(self): def handler(oid, associd): pdata = self.misc_getPeerData() if pdata is None: return None offset = pdata[associd]["offset"][1] offset = ntp.util.unitifyvar(offset, "offset", width=None, unitSpace=True) return ax.Varbind(ax.VALUE_OCTET_STR, oid, offset) return self.dynamicCallbackSkeleton(handler) def sub_assocStratum(self): return self.dynamicCallbackPeerdata("stratum", False, ax.VALUE_GAUGE32) def sub_assocJitter(self): return self.dynamicCallbackPeerdata("jitter", True, ax.VALUE_OCTET_STR) def sub_assocDelay(self): return self.dynamicCallbackPeerdata("delay", True, ax.VALUE_OCTET_STR) def sub_assocDispersion(self): return self.dynamicCallbackPeerdata("rootdisp", True, ax.VALUE_OCTET_STR) def sub_assocStatInPkts(self): def handler(oid, associd): inpkts = self.safeReadvar(associd, ["received"]) if inpkts is None: return None inpkts = inpkts["received"] return ax.Varbind(ax.VALUE_COUNTER32, oid, inpkts) return self.dynamicCallbackSkeleton(handler) def sub_assocStatOutPkts(self): def handler(oid, associd): outpkts = self.safeReadvar(associd, ["sent"]) if outpkts is None: return None outpkts = outpkts["sent"] return ax.Varbind(ax.VALUE_COUNTER32, oid, outpkts) return self.dynamicCallbackSkeleton(handler) def sub_assocStatProtoErr(self): def handler(oid, associd): pvars = self.safeReadvar(associd, ["badauth", "bogusorg", "seldisp", "selbroken"]) if pvars is None: return None protoerr = 0 for key in pvars.keys(): protoerr += pvars[key] return ax.Varbind(ax.VALUE_COUNTER32, oid, protoerr) return self.dynamicCallbackSkeleton(handler) # ===================================== # Notification handlers # ===================================== def checkNotifications(self, control): currentTime = time.time() if (currentTime - self.lastNotifyCheck) < self.notifySpinTime: return self.lastNotifyCheck = currentTime if self.notifyModeChange is True: self.doNotifyModeChange(control) if self.notifyStratumChange is True: self.doNotifyStratumChange(control) if self.notifySyspeerChange is True: self.doNotifySyspeerChange(control) # Both add and remove have to look at the same data, don't want them # stepping on each other. Therefore the functions are combined. if (self.notifyAddAssociation is True) and \ (self.notifyRMAssociation is True): self.doNotifyChangeAssociation(control, "both") elif self.notifyAddAssociation is True: self.doNotifyChangeAssociation(control, "add") elif self.notifyRMAssociation is True: self.doNotifyChangeAssociation(control, "rm") if self.notifyConfigChange is True: self.doNotifyConfigChange(control) if self.notifyLeapSecondAnnounced is True: self.doNotifyLeapSecondAnnounced(control) if self.notifyHeartbeat is True: self.doNotifyHeartbeat(control) def doNotifyModeChange(self, control): oldMode = self.oldValues.get("mode") newMode = self.misc_getMode() # connection failure handled by method if oldMode is None: self.oldValues["mode"] = newMode return elif oldMode != newMode: self.oldValues["mode"] = newMode vl = [ax.Varbind(ax.VALUE_OID, snmpTrapOID, ax.OID(ntpRootOID + (0, 1))), ax.Varbind(ax.VALUE_INTEGER, ntpRootOID + (1, 2, 1), newMode)] control.sendNotify(vl) self.sentNotifications += 1 def doNotifyStratumChange(self, control): oldStratum = self.oldValues.get("stratum") newStratum = self.safeReadvar(0, ["stratum"]) if newStratum is None: return # couldn't read newStratum = newStratum["stratum"] if oldStratum is None: self.oldValues["stratum"] = newStratum return elif oldStratum != newStratum: self.oldValues["stratum"] = newStratum datetime = self.safeReadvar(0, ["reftime"]) if datetime is None: datetime = "" else: datetime = ntp.util.deformatNTPTime(datetime["reftime"]) vl = [ax.Varbind(ax.VALUE_OID, snmpTrapOID, ax.OID(ntpRootOID + (0, 2))), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 2, 9), datetime), ax.Varbind(ax.VALUE_GAUGE32, ntpRootOID + (1, 2, 2), newStratum), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 5, 1), "Stratum changed")] # Uh... what goes here? control.sendNotify(vl) self.sentNotifications += 1 def doNotifySyspeerChange(self, control): oldSyspeer = self.oldValues.get("syspeer") newSyspeer = self.safeReadvar(0, ["peeradr"]) if newSyspeer is None: return # couldn't read newSyspeer = newSyspeer["peeradr"] if oldSyspeer is None: self.oldValues["syspeer"] = newSyspeer return elif oldSyspeer != newSyspeer: self.oldValues["syspeer"] = newSyspeer datetime = self.safeReadvar(0, ["reftime"]) if datetime is None: datetime = "" else: datetime = ntp.util.deformatNTPTime(datetime["reftime"]) syspeer = self.misc_getSyspeerID() vl = [ax.Varbind(ax.VALUE_OID, snmpTrapOID, ax.OID(ntpRootOID + (0, 3))), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 2, 9), datetime), ax.Varbind(ax.VALUE_GAUGE32, ntpRootOID + (1, 2, 3), syspeer), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 5, 1), "SysPeer changed")] # Uh... what goes here? control.sendNotify(vl) self.sentNotifications += 1 def doNotifyChangeAssociation(self, control, which): # Add and remove are combined because they use the same data source # and it would be easy to have them stepping on each other. changes = self.misc_getAssocListChanges() if changes is None: return datetime = self.safeReadvar(0, ["reftime"]) if datetime is None: datetime = "" else: datetime = ntp.util.deformatNTPTime(datetime["reftime"]) adds, rms = changes if which in ("add", "both"): for name in adds: vl = [ax.Varbind(ax.VALUE_OID, snmpTrapOID, ax.OID(ntpRootOID + (0, 4))), # Add ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 2, 9), datetime), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 3, 1, 1, 2), name), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 5, 1), "Association added")] control.sendNotify(vl) self.sentNotifications += 1 if which in ("rm", "both"): for name in rms: vl = [ax.Varbind(ax.VALUE_OID, snmpTrapOID, ax.OID(ntpRootOID + (0, 5))), # Remove ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 2, 9), datetime), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 3, 1, 1, 2), name), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 5, 1), "Association removed")] control.sendNotify(vl) self.sentNotifications += 1 def doNotifyConfigChange(self, control): # This left unimplemented because the MIB wants something we can't # and/or shouldn't provide pass def doNotifyLeapSecondAnnounced(self, control): oldLeap = self.oldValues.get("leap") newLeap = self.safeReadvar(0, ["leap"]) if newLeap is None: return newLeap = newLeap["leap"] if oldLeap is None: self.oldValues["leap"] = newLeap return if oldLeap != newLeap: self.oldValues["leap"] = newLeap if (oldLeap in (0, 3)) and (newLeap in (1, 2)): # changed noleap or unsync to a leap announcement datetime = self.safeReadvar(0, ["reftime"]) if datetime is None: datetime = "" else: datetime = ntp.util.deformatNTPTime(datetime["reftime"]) vl = [ax.Varbind(ax.VALUE_OID, snmpTrapOID, ax.OID(ntpRootOID + (0, 7))), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 2, 9), datetime), ax.Varbind(ax.VALUE_OCTET_STR, ntpRootOID + (1, 5, 1), "Leap second announced")] control.sendNotify(vl) self.sentNotifications += 1 def doNotifyHeartbeat(self, control): # TODO: check if ntpd running? vl = [ax.Varbind(ax.VALUE_OID, snmpTrapOID, ax.OID(ntpRootOID + (0, 8))), ax.Varbind(ax.VALUE_GAUGE32, ntpRootOID + (0, 1, 4, 1), self.heartbeatInterval)] if self.heartbeatInterval == 0: # interval == 0 means send once self.notifyHeartbeat = False control.sendNotify(vl) self.sentNotifications += 1 else: current = ntp.util.monoclock() if (current - self.lastHeartbeat) > self.heartbeatInterval: self.lastHeartbeat = current control.sendNotify(vl) self.sentNotifications += 1 # ===================================== # Misc data helpers (not part of the MIB proper) # ===================================== def misc_loadDynamicSettings(self): if self.settingsFilename is None: return def boolify(d, k): return True if d[k] == "True" else False optionList = ("notify-mode-change", "notify-stratum-change", "notify-syspeer-change", "notify-add-association", "notify-rm-association", "notify-leap-announced", "notify-heartbeat", "heartbeat-interval") settings = loadSettings(self.settingsFilename, optionList) if settings is None: return for key in settings.keys(): if key == "notify-mode-change": self.notifyModeChange = boolify(settings, key) elif key == "notify-stratum-change": self.notifyStratumChange = boolify(settings, key) elif key == "notify-syspeer-change": self.notifySyspeerChange = boolify(settings, key) elif key == "notify-add-association": self.notifyAddAssociation = boolify(settings, key) elif key == "notify-rm-association": self.notifyRMAssociation = boolify(settings, key) elif key == "notify-leap-announced": self.notifyLeapSecondAnnounced = boolify(settings, key) elif key == "notify-heartbeat": self.notifyHeartbeat = boolify(settings, key) elif key == "heartbeat-interval": self.heartbeatInterval = int(settings[key]) def misc_storeDynamicSettings(self): if self.settingsFilename is None: return settings = {} settings["notify-mode-change"] = str(self.notifyModeChange) settings["notify-stratum-change"] = str(self.notifyStratumChange) settings["notify-syspeer-change"] = str(self.notifySyspeerChange) settings["notify-add-association"] = str(self.notifyAddAssociation) settings["notify-rm-association"] = str(self.notifyRMAssociation) settings["notify-leap-announced"] = str(self.notifyLeapSecondAnnounced) settings["notify-heartbeat"] = str(self.notifyHeartbeat) settings["heartbeat-interval"] = str(self.heartbeatInterval) storeSettings(self.settingsFilename, settings) def misc_getAssocListChanges(self): # We need to keep the names, because those won't be available # after they have been removed. oldAssoc = self.oldValues.get("assoc") newAssoc = {} # Yes, these are cached, for a very short time pdata = self.misc_getPeerData() if pdata is None: return ids = self.misc_getPeerIDs() if ids is None: return for associd in ids: addr = pdata[associd]["srcadr"][1] name = ntp.util.canonicalize_dns(addr) newAssoc[associd] = name if oldAssoc is None: self.oldValues["assoc"] = newAssoc return elif oldAssoc != newAssoc: oldIDs = oldAssoc.keys() newIDs = newAssoc.keys() adds = [] rms = [] for associd in oldIDs + newIDs: if associd not in newIDs: # removed rms.append(oldAssoc[associd]) if associd not in oldIDs: # added adds.append(newAssoc[associd]) return (adds, rms) return def misc_getMode(self): # FIXME: not fully implemented try: # Don't care about the data, this is a ploy to the the rstatus self.session.readvar(0, ["stratum"]) except ntp.packet.ControlException as e: if e.message == ntp.packet.SERR_SOCKET: # Can't connect, ntpd probably not running return 1 else: raise e rstatus = self.session.rstatus # a ploy to get the system status source = ntp.control.CTL_SYS_SOURCE(rstatus) if source == ntp.control.CTL_SST_TS_UNSPEC: mode = 2 # Not yet synced elif False: mode = 3 # No reference configured elif source == ntp.control.CTL_SST_TS_LOCAL: mode = 4 # Distributing local clock (low accuracy) elif source in (ntp.control.CTL_SST_TS_ATOM, ntp.control.CTL_SST_TS_LF, ntp.control.CTL_SST_TS_HF, ntp.control.CTL_SST_TS_UHF): # I am not sure if I should be including the radios in this mode = 5 # Synced to local refclock elif source == ntp.control.CTL_SST_TS_NTP: # Should this include "other"? That covers things like chrony... mode = 6 # Sync to remote NTP else: mode = 99 # Unknown return mode def misc_getSyspeerID(self): peers = self.misc_getPeerData() syspeer = 0 for associd in peers.keys(): rstatus = peers[associd]["peerstatus"] if (ntp.control.CTL_PEER_STATVAL(rstatus) & 0x7) == \ ntp.control.CTL_PST_SEL_SYSPEER: syspeer = associd break return syspeer def safeReadvar(self, associd, variables=None, raw=False): # Use this when we want to catch packet errors, but don't care # about what they are try: return self.session.readvar(associd, varlist=variables, raw=raw) except ntp.packet.ControlException: return None def dynamicCallbackPeerdata(self, variable, raw, valueType): rawindex = 1 if raw is True else 0 def handler(oid, associd): pdata = self.misc_getPeerData() if pdata is None: return None value = pdata[associd][variable][rawindex] return ax.Varbind(valueType, oid, value) return self.dynamicCallbackSkeleton(handler) def dynamicCallbackSkeleton(self, handler): # Build a dynamic MIB tree, installing the provided handler in it def readCallback(oid): # This function assumes that it is a leaf node and that the # last number in the OID is the index. index = oid.subids[-1] # if called properly this works (Ha!) index -= 1 # SNMP reserves index 0, effectively 1-based lists associd = self.misc_getPeerIDs()[index] return handler(oid, associd) subs = {} associds = self.misc_getPeerIDs() # need the peer count for i in range(len(associds)): subs[i+1] = {"reader": readCallback} return subs def readCallbackSkeletonSimple(self, oid, varname, dataType): # Used for entries that just need a simple variable retrevial # but do not need any processing. data = self.safeReadvar(0, [varname]) if data is None: return None else: return ax.Varbind(dataType, oid, data[varname]) def misc_getPeerIDs(self): peerids = self.cache.get("peerids") if peerids is None: try: peerids = [x.associd for x in self.session.readstat()] except ntp.packet.ControlException: peerids = [] peerids.sort() self.cache.set("peerids", peerids) return peerids def misc_getPeerData(self): peerdata = self.cache.get("peerdata") if peerdata is None: associds = self.misc_getPeerIDs() peerdata = {} for aid in associds: try: pdata = self.safeReadvar(aid, raw=True) pdata["peerstatus"] = self.session.rstatus except IOError: continue peerdata[aid] = pdata self.cache.set("peerdata", peerdata) return peerdata def connect(address): try: if type(address) is str: sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(address) else: host, port = address[0], address[1] af, _, _, _, _ = socket.getaddrinfo(host, port)[0] sock = socket.socket(af, socket.SOCK_STREAM) sock.connect((host, port)) except socket.error as msg: log("Connection to %s failure: %s" % (repr(address), repr(msg)), 1) sys.exit(1) log("connected to master agent at " + repr(address), 3) return sock def mainloop(snmpSocket, reconnectionAddr, host=None): log("initing loop", 3) dbase = DataSource(host, "/var/ntpsntpd/notify.conf") while True: # Loop reconnection attempts control = PacketControl(snmpSocket, dbase, logfp=logfp, debug=debug) control.loopCallback = dbase.checkNotifications control.initNewSession() if control.mainloop(True) is False: # disconnected snmpSocket.close() snmpSocket = connect(reconnectionAddr) log("disconnected from master, attempting reconnect", 2) else: # Something else happened break def daemonize(runfunc, *runArgs): pid = os.fork() if pid < 0: log("Forking error " + str(pid), 1) sys.exit(pid) elif pid > 0: # We are the parent log("Daemonization success, child pid: " + str(pid), 3) sys.exit(0) # We must be the child os.umask(0) sid = os.setsid() # chdir should be here, change to what? root? global logfp if logfp == sys.stderr: logfp = None sys.stdin.close() sys.stdin = None sys.stdout.close() sys.stdout = None sys.stderr.close() sys.stderr = None runfunc(*runArgs) def loadSettings(filename, optionList): log("Loading config file: %s" % filename, 3) if os.path.isfile(filename) is False: return None options = {} with open(filename) as f: data = f.read() parser = shlex.shlex(data) parser.wordchars += "-.:" data = [x for x in parser] i = 0 dataLen = len(data) while i < dataLen: if data[i] in optionList: arg = data[i+1] if arg[0] in "\"'": arg = arg[1:-1] options[data[i]] = arg i += 1 i += 1 return options def storeSettings(filename, settings): dirname = os.path.dirname(filename) if os.path.exists(dirname) is False: os.makedirs(dirname) data = [] for key in settings.keys(): data.append("%s %s\n" % (key, settings[key])) data = "".join(data) with open(filename, "w") as f: f.write(data) usage = """ USAGE: ntpsnmpd [-n] [ntp host] Flg Arg Option-Name Description -n no no-fork Do not fork and daemonize. -x Adr master-addr Specify address for connecting to the master agent - default /var/agentx/master -d no debug-level Increase output debug message level - may appear multiple times -l Str logfile Logs debug messages to the provided filename -D Int set-debug-level Set the output debug message level - may appear multiple times -h no help Print a usage message. -V no version Output version information and exit """ if __name__ == "__main__": try: (options, arguments) = getopt.getopt( sys.argv[1:], "nx:dD:Vhl:c:", ["no-fork", "master-address=", "debug-level", "set-debug-level=", "version", "help", "logfile=", "configfile="]) except getopt.GetoptError as e: sys.stderr.write("%s\n" % e) sys.stderr.write(usage) raise SystemExit(1) masterAddr = "/var/agentx/master" logfile = DEFLOG hostname = DEFHOST # Check for non-default config-file conffile = "/etc/ntpsnmpd.conf" for (switch, val) in options: if switch in ("-c", "--configfile"): conffile = val break # Load configuration file conf = loadSettings(conffile, ("master-addr", "logfile", "loglevel", "ntp-addr")) if conf is not None: for key in conf.keys(): if key == "master-addr": # Address of the SNMP master daemon val = conf[key] if ":" in val: host, port = val.split(":") port = int(port) masterAddr = (host, port) else: masterAddr = val elif key == "logfile": logfile = conf[key] elif key == "ntp-addr": # Address of the NTP daemon hostname = conf[key] elif key == "loglevel": errmsg = "Error: loglevel parameter '%s' not a number\n" debug = ntp.util.safeargcast(conf[key], int, errmsg, usage) fileLogging = False for (switch, val) in options: if switch in ("-n", "--no-fork"): nofork = True elif switch in ("-x", "--master-addr"): if ":" in val: host, port = val.split(":") port = int(port) masterAddr = (host, port) else: masterAddr = val elif switch in ("-d", "--debug-level"): debug += 1 elif switch in ("-D", "--set-debug-level"): errmsg = "Error: -D parameter '%s' not a number\n" debug = ntp.util.safeargcast(val, int, errmsg, usage) elif switch in ("-V", "--version"): print("ntpsnmpd %s" % ntp.util.stdversion()) raise SystemExit(0) elif switch in ("-h", "--help"): print(usage) raise SystemExit(0) elif switch in ("-l", "--logfile"): logfile = val fileLogging = True if nofork is False: fileLogging = True if fileLogging is True: if logfp != sys.stderr: logfp.close() logfp = open(logfile, "a", 1) # 1 => line buffered hostname = arguments[0] if arguments else DEFHOST # Connect here so it can always report a connection error sock = connect(masterAddr) if nofork is True: mainloop(sock, hostname) else: daemonize(mainloop, sock, hostname) ntpsec-1.1.0+dfsg1/ntpclients/ntpdig-man.txt0000644000175000017500000000066113252364117020612 0ustar rlaagerrlaager= ntpdig(1) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpdig - standard Simple Network Time Protocol client program include::../docs/includes/ntpdig-body.txt[] == EXIT STATUS == One of the following exit values will be returned: 0 (EXIT_SUCCESS):: Successful program execution. 1 (EXIT_FAILURE):: The operation failed or the command syntax was not valid. // end ntpsec-1.1.0+dfsg1/ntpclients/ntpmon.py0000644000175000017500000004673713252364117017716 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- # SPDX-License-Identifier: BSD-2-clause '''\ Any keystroke causes a poll and update. Keystroke commands: 'a': Change peer display to apeers mode, showing association IDs. 'd': Toggle detail mode (some peer will be reverse-video highlighted when on). 'h': Display helpscreen. 'j': Select next peer (in select mode); arrow down also works. 'k': Select previous peer (in select mode); arrow up also works. 'm': Disable peers display, showing only MRU list 'n': Toggle display of hostnames (vs. IP addresses). 'o': Change peer display to opeers mode, showing destination address. 'p': Change peer display to default mode, showing refid. 'q': Cleanly terminate the program. 's': Toggle display of only reachable hosts (default is all hosts). 'u': Toggle display of units. 'w': Toggle wide mode. 'x': Cleanly terminate the program. ' ': Rotate through a/n/o/p display modes. '+': Increase debugging level. Output goes to ntpmon.log '-': Decrease debugging level. '?': Display helpscreen. ''' from __future__ import print_function, division import getopt import sys import time try: import ntp.control import ntp.magic import ntp.ntpc import ntp.packet import ntp.util except ImportError as e: sys.stderr.write( "ntpmon: can't find Python NTP library -- check PYTHONPATH.\n") sys.stderr.write("%s\n" % e) sys.exit(1) # LANG=C or LANG=POSIX refuse unicode when combined with curses disableunicode = ntp.util.check_unicode() try: import locale except ImportError as e: sys.stderr.write( "ntpmon: can't find Python locale library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) try: import curses except ImportError as e: sys.stderr.write( "ntpmon: can't find Python curses library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) stdscr = None def iso8601(t): "ISO8601 string from Unix time, including fractional second." return time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(time.time())) def statline(_peerlist, _mrulist, nyquist): "Generate a status line" # We don't use stdversion here because the presence of a date is confusing leader = sysvars['version'][0] if span.entries: trailer = "Updated: %s (%s)" \ % (iso8601(span.entries[0].last), ntp.util.PeerSummary.prettyinterval(nyquist)) else: trailer = "" spacer = ((peer_report.termwidth - 1) - len(leader) - len(trailer)) * " " return leader + spacer + trailer def peer_detail(variables, showunits=False): "Show the things a peer summary doesn't, cooked slightly differently" # All of an rv display except refid, reach, delay, offset, jitter. # One of the goals here is to emit field values at fixed positions # on the 2D display, so that changes in the details are easier to spot. vcopy = {} vcopyraw = {} vcopy.update(variables) width = ntp.util.termsize().width - 2 # Need to separate the casted from the raw for key in vcopy.keys(): vcopyraw[key] = vcopy[key][1] vcopy[key] = vcopy[key][0] vcopy["leap"] = ("no-leap", "add-leap", "del-leap", "unsync")[vcopy["leap"]] for fld in ('xmt', 'rec', 'reftime'): if fld not in vcopy: vcopy[fld] = "***missing***" else: vcopy[fld] = ntp.util.rfc3339(ntp.ntpc.lfptofloat(vcopy[fld])) if showunits: for name in ntp.util.MS_VARS: if name in vcopy: vcopy[name] = ntp.util.unitify(vcopyraw[name], ntp.util.UNIT_MS, width=None) for name in ntp.util.PPM_VARS: if name in vcopy: vcopy[name] = ntp.util.unitify(vcopyraw[name], ntp.util.UNIT_PPM, width=None) for name in ntp.util.S_VARS: if name in vcopy: vcopy[name] = ntp.util.unitify(vcopyraw[name], ntp.util.UNIT_S, width=None) vcopy['filtdelay'] = ntp.util.stringfiltcooker(vcopyraw['filtdelay']) vcopy['filtoffset'] = ntp.util.stringfiltcooker(vcopyraw['filtoffset']) vcopy['filtdisp'] = ntp.util.stringfiltcooker(vcopyraw['filtdisp']) else: vcopy['filtdelay'] = ntp.util.stringfilt(vcopyraw['filtdelay']) vcopy['filtoffset'] = ntp.util.stringfilt(vcopyraw['filtoffset']) vcopy['filtdisp'] = ntp.util.stringfilt(vcopyraw['filtdisp']) # annotate IPv6, to stand out from :port if ':' in vcopy['srcadr']: vcopy['srcadr'] = '[' + vcopy['srcadr'] + ']' if ':' in vcopy['dstadr']: vcopy['dstadr'] = '[' + vcopy['dstadr'] + ']' vcopy['adr'] = "dstadr=%(dstadr)s:%(dstport)s " \ "srcadr=%(srcadr)s:%(srcport)d" % vcopy if len(vcopy['adr']) > width: # too long, break the line vcopy['adr'] = vcopy['adr'].replace(" ", "\n") peerfmt = """\ %(adr)s reftime=%(reftime)s\tleap=%(leap)s\trootdelay=%(rootdelay)s rec=%(rec)s\tstratum=%(stratum)2d\trootdisp=%(rootdisp)s xmt=%(xmt)s\tprecision=%(precision)3d\tdispersion=%(dispersion)s unreach=%(unreach)d hmode=%(hmode)d pmode=%(pmode)d hpoll=%(hpoll)d \ ppoll=%(ppoll)d headway=%(headway)s flash=%(flash)s keyid=%(keyid)s filtdelay = %(filtdelay)s filtoffset = %(filtoffset)s filtdisp = %(filtdisp)s """ str = peerfmt % vcopy return str.expandtabs() class Fatal(Exception): "Unrecoverable error." def __init__(self, msg): Exception.__init__(self) self.msg = msg def __str__(self): return self.msg class OutputContext: def __enter__(self): "Begin critical region." if (sys.version_info[0] < 3) and (disableunicode is False): # This appears to only be needed under python 2, it is only # activated when we already have UTF-8. Otherwise we drop # down to non-unicode versions. locale.setlocale(locale.LC_ALL, "") global stdscr stdscr = curses.initscr() try: curses.curs_set(0) except curses.error: # VT100 terminal emulations can barf here. pass curses.cbreak() curses.noecho() stdscr.keypad(True) # Design decision: The most important info is nearer the # top of the display. Therefore, prevent scrolling. stdscr.scrollok(False) def __exit__(self, extype_unused, value_unused, traceback_unused): curses.endwin() usage = ''' USAGE: ntpmon [-nudV] [-l logfile] [-D lvl] [host] Flg Arg Option-Name Description -n no numeric Numeric host addresses -u no units Display time with units. -d no debug-level Increase output debug message level - may appear multiple times -D Int set-debug-level Set the output debug message level - may appear multiple times -l Str logfile Logs debug messages to the provided filename -h no help Print a usage message. -V opt version Output version information and exit ''' if __name__ == '__main__': try: (options, arguments) = getopt.getopt(sys.argv[1:], "VhnudD:l:", ["version", "numeric", "units", "debug", "set-debug-level=", "logfile=", "help"]) except getopt.GetoptError as e: sys.stderr.write("%s\n" % e) sys.stderr.write(usage) raise SystemExit(1) progname = sys.argv[0] showhostnames = True wideremote = False showall = True showpeers = True showunits = False nyquist = 1 debug = 0 logfp = None defaultlog = "ntpmon.log" for (switch, val) in options: if switch in ("-V", "--version"): print("ntpmon %s" % ntp.util.stdversion()) raise SystemExit(0) elif switch in ("-h", "--help"): print(usage) raise SystemExit(0) elif switch in ("-n", "--numeric"): showhostnames = False elif switch in ("-u", "--units"): showunits = True elif switch in ("-d", "--debug"): debug += 1 elif switch in ("-D", "--set-debug-level"): errmsg = "Error: -D parameter '%s' not a number\n" debug = ntp.util.safeargcast(val, int, errmsg, usage) elif switch in ("-l", "--logfile"): if logfp is not None: logfp.close() logfp = open(val, "a", 1) # 1 => line buffered if (logfp is None) and (debug > 0): logfp = open(defaultlog, "a", 1) poll_interval = 1 helpmode = selectmode = detailmode = False selected = -1 peer_report = ntp.util.PeerSummary(displaymode="peers", pktversion=ntp.magic.NTP_VERSION, showhostnames=showhostnames, wideremote=wideremote, showunits=showunits, termwidth=80, debug=debug, logfp=logfp) mru_report = ntp.util.MRUSummary(showhostnames, wideremote=wideremote, debug=debug, logfp=logfp) try: session = ntp.packet.ControlSession() session.debug = debug session.logfp = logfp session.openhost(arguments[0] if arguments else "localhost") sysvars = session.readvar(raw=True) with OutputContext() as ctx: while True: stdscr.clear() stdscr.addstr(0, 0, u"".encode('UTF-8')) if helpmode: stdscr.addstr(__doc__.encode('UTF-8')) tempStr = u"\nPress any key to resume monitoring" stdscr.addstr(tempStr.encode('UTF-8')) stdscr.refresh() stdscr.timeout(-1) else: if showpeers: try: peers = session.readstat() except ntp.packet.ControlException as e: raise Fatal(e.message) except IOError as e: raise Fatal(e.strerror) strconvert = peer_report.header() + "\n" stdscr.addstr(strconvert.encode('UTF-8'), curses.A_BOLD) else: peer_report.polls = [1] # Kluge! peers = [] if showpeers and len(peers) == 0: raise Fatal("no peers reported") try: initphase = False for (i, peer) in enumerate(peers): if (not showall and not ( ntp.control.CTL_PEER_STATVAL(peer.status) & (ntp.control.CTL_PST_CONFIG | ntp.control.CTL_PST_REACH))): continue try: variables = session.readvar(peer.associd, raw=True) except ntp.packet.ControlException as e: if e.errorcode == ntp.control.CERR_BADASSOC: # Probable race condition due to pool # dropping an associaton during refresh; # ignore. (GitLab issue #374.) break raise Fatal(e.message + "\n") except IOError as e: raise Fatal(e.strerror) except IndexError: raise Fatal( "no 'hpoll' variable in peer response") if not variables: continue if selectmode and selected == i: retained = variables hilite = curses.A_REVERSE else: hilite = curses.A_NORMAL data = peer_report.summary(session.rstatus, variables, peer.associd) data = data.encode('UTF-8') stdscr.addstr(data, hilite) if (('refid' in variables and 'INIT' in variables['refid'])): initphase = True # Now the MRU report limit = stdscr.getmaxyx()[0] - len(peers) span = session.mrulist(variables={'recent': limit}) mru_report.now = time.time() # After init phase use Nyquist-interval # sampling - half the smallest poll interval # seen in the last cycle, rounded up to 1 # second. if not initphase: nyquist = int(min(peer_report.intervals()) / 2) nyquist = 1 if nyquist == 0 else nyquist ntp.util.dolog(logfp, "nyquist is %d\n" % nyquist, debug, 1) # The status line sl = statline(peer_report, mru_report, nyquist) strconvert = sl + "\n" stdscr.addstr(strconvert.encode('UTF-8'), curses.A_REVERSE | curses.A_DIM) if detailmode: if ntp.util.PeerSummary.is_clock(retained): dtype = ntp.ntpc.TYPE_CLOCK else: dtype = ntp.ntpc.TYPE_PEER sw = ntp.ntpc.statustoa(dtype, peers[selected].status) strconvert = "assoc=%d: %s\n" stdscr.addstr(strconvert % (peers[selected].associd, sw)) strconvert = peer_detail(retained, showunits) stdscr.addstr(strconvert.encode('UTF-8')) try: clockvars = session.readvar( peers[selected].associd, opcode=ntp.control.CTL_OP_READCLOCK, raw=True) strconvert = ntp.util.cook(clockvars, showunits, " ") stdscr.addstr(strconvert.encode('UTF-8')) except ntp.packet.ControlException as e: pass elif span.entries: strconvert = ntp.util.MRUSummary.header + "\n" stdscr.addstr(strconvert.encode('UTF-8'), curses.A_BOLD) for entry in reversed(span.entries): strcon = mru_report.summary(entry) + "\n" stdscr.addstr(strcon.encode('UTF-8')) except curses.error: # An addstr overran the screen, no worries pass # Display all stdscr.refresh() stdscr.timeout(nyquist * 1000) try: helpmode = False key = stdscr.getkey() if key == 'q' or key == 'x': raise SystemExit(0) elif key == 'a': peer_report.displaymode = 'apeers' elif key == 'd': if not selectmode: selected = 0 selectmode = not selectmode detailmode = not detailmode showpeers = True # detail + hide peers == crash elif key == 'm': showpeers = not showpeers detailmode = False # detail + hide peers == crash elif key == 'n': peer_report.showhostnames = \ not peer_report.showhostnames mru_report.showhostnames = not mru_report.showhostnames elif key == 'o': peer_report.displaymode = 'opeers' elif key == 'p': peer_report.displaymode = 'peers' elif key == 's': showall = not showall elif key == 'u': showunits = not showunits peer_report.showunits = showunits elif key == 'w': wideremote = not wideremote peer_report.wideremote = wideremote mru_report.wideremote = wideremote elif key == " ": if peer_report.displaymode == 'peers': peer_report.displaymode = 'apeers' elif peer_report.displaymode == 'apeers': peer_report.displaymode = 'opeers' else: peer_report.displaymode = 'peers' elif key == 'j' or key == "KEY_DOWN": if showpeers: selected += 1 selected %= len(peers) elif key == 'k' or key == "KEY_UP": if showpeers: selected += len(peers) - 1 selected %= len(peers) elif key == '+': if logfp is None: logfp = open(defaultlog, "a", 1) session.logfp = logfp peer_report.logfp = logfp mru_report.logfp = logfp debug += 1 session.debug = debug peer_report.debug = debug mru_report.debug = debug elif key == '-': debug -= 1 session.debug = debug peer_report.debug = debug mru_report.debug = debug elif key in ['?', 'h']: helpmode = True except curses.error: pass except KeyboardInterrupt: print("") except (Fatal, ntp.packet.ControlException) as e: print(e) except IOError: print("Bailing out...") # end ntpsec-1.1.0+dfsg1/ntpclients/ntpwait.py0000644000175000017500000001022013252364117020043 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- """\ ntpwait - Wait for ntpd to stabilize the system clock. 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 -h, --help Issue help Options are specified by doubled hyphens and their name or by a single hyphen and the flag character. A spurious 'not running' message can result from queries being disabled. """ # SPDX-License-Identifier: BSD-2-Clause # Because we've actually seen this fail on a ^C during import of ntp.packet. import signal import sys signal.signal(signal.SIGINT, lambda signal, frame: sys.exit(2)) import getopt import re import time import socket try: import ntp.magic import ntp.packet import ntp.util except ImportError as e: sys.stderr.write( "ntpwait: can't find Python NTP library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) 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 for (switch, val) in options: if switch in ("-n", "--tries"): errmsg = "Error: -n parameter '%s' not a number\n" tries = ntp.util.safeargcast(val, int, errmsg, __doc__) elif switch in ("-s", "--sleep"): errmsg = "Error: -s parameter '%s' not a number\n" sleep = ntp.util.safeargcast(val, int, errmsg, __doc__) elif switch in ("-v", "--verbose"): verbose += 1 elif switch in ("-h", "--help"): sys.stdout.write(__doc__) raise SystemExit(0) # Autoflush stdout sys.stdout = Unbuffered(sys.stdout) basetime = ntp.util.monoclock() if verbose: sys.stdout.write("Waiting for ntpd to synchronize... ") for i in range(1, tries): session = ntp.packet.ControlSession() # session.debug = 4 if not session.openhost("localhost"): if verbose: sys.stdout.write("\bntpd is not running!\n") continue msg = None try: msg = session.doquery(2) # Request system variables except ntp.packet.ControlException as e: sys.stderr.write("localhost: timed out, nothing received\n") sys.stderr.write(e.message) 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=([^,]*),", repr(session.response)) if m: leap = int(m.group(1)) else: sys.stdout.write("\bLeap status not available\n") sys.exit(1) if leap == ntp.magic.LEAP_NOTINSYNC: if verbose: sys.stdout.write("\b" + "*+:."[i % 4]) if i < tries: time.sleep(sleep) continue if leap in (ntp.magic.LEAP_NOWARNING, ntp.magic.LEAP_ADDSECOND, ntp.magic.LEAP_DELSECOND): # We could check "sync" here to make sure we like the source... if verbose: sys.stdout.write("\bOK! (%.1f seconds)\n" % (ntp.util.monoclock() - basetime)) 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 ntpsec-1.1.0+dfsg1/ntpclients/ntpleapfetch-man.txt0000644000175000017500000000077313252364117022006 0ustar rlaagerrlaager= ntpleapfetch(8) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpleapfetch - leap-seconds file manager/updater include::../docs/includes/ntpleapfetch-body.txt[] == EXIT STATUS == One of the following exit values will be returned: 0 (EXIT_SUCCESS):: Successful program execution. 1 (EXIT_FAILURE):: The operation failed or the command syntax was not valid. 2:: Internal error - restart command failed. == AUTHORS == Timothe Litt // end ntpsec-1.1.0+dfsg1/ntpclients/ntpsnmpd-man.txt0000644000175000017500000000041513252364117021165 0ustar rlaagerrlaager= ntpsnmpd(8) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpsnmpd - NTP Simple Network Management Protcol sub-sgent include::../docs/includes/ntpsnmpd-body.txt[] == EXIT STATUS == Always returns 0. // end ntpsec-1.1.0+dfsg1/ntpclients/ntpsweep-man.txt0000644000175000017500000000065413252364117021174 0ustar rlaagerrlaager= ntpsweep(1) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpsweep - print various information about given NTP servers include::../docs/includes/ntpsweep-body.txt[] == EXIT STATUS == One of the following exit values will be returned: 0 (EXIT_SUCCESS):: Successful program execution. 1 (EXIT_FAILURE):: The operation failed or the command syntax was not valid. ntpsec-1.1.0+dfsg1/ntpclients/ntpsweep.py0000644000175000017500000001654213252364117020237 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- """ ntpsweep - print various information about given NTP servers USAGE: ntpsweep [- [] | --[{=| }]]... [hostfile] -h, --host=str Host to execute actions on -l, --host-list=str Comma-delimited list of Hosts to execute actions on -p, --peers Recursively list all peers a host syncs 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-2-clause # # Python translation by ESR of a Perl script written long ago by # Hans Lambermont # # 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 import sys import getopt try: import ntp.packet import ntp.util except ImportError as e: sys.stderr.write( "ntpsweep: can't find Python NTP library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) def ntp_peers(host): """Return: a list of peer IP addrs for a specified host, empty list 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 = "" known_host = False if host in known_host_info: known_host = True else: session = ntp.packet.ControlSession() session.openhost(host) sysvars = session.readvar() # got answers ? If so, go on. if isinstance(sysvars, dict): 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": errmsg = "Error: -m parameter '%s' not a number\n" maxlevel = ntp.util.safeargcast(val, int, errmsg, __doc__) 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) try: if arguments: hostlist += [ln.strip() for ln in open(arguments[0]).readlines()] except IOError: sys.stderr.write("Host file not found.\n") raise SystemExit(1) 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: try: scan_host(host, 0) except ntp.packet.ControlException as e: sys.stderr.write(e.message + "\n") continue sys.exit(0) # end ntpsec-1.1.0+dfsg1/ntpclients/ntplogtemp.py0000644000175000017500000002152013252364117020553 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- """\ usage: ntplogtemp [-h] [-l LOGFILE] [-o] [-q] [-v] [-w WAIT] [-V] Program to log system temperatures optional arguments: -h, --help show this help message and exit -l LOGFILE, --logfile LOGFILE append log data to LOGFILE instead of stdout -o, --once Run the output once and exit -q, --quiet be quite -v, --verbose be verbose -w WAIT, --wait WAIT Set delay time in seconds, default is 60 -V, --version show program's version number and exit See the manual page for details. """ from __future__ import print_function, division import argparse import glob import logging import logging.handlers import os import re import subprocess import sys import time try: import ntp.util except ImportError as e: sys.stderr.write( "ntplogtemp: can't find Python NTP library -- check PYTHONPATH.\n") sys.stderr.write("%s\n" % e) sys.exit(1) def run_binary(cmd): """\ Run a binary Return output if good, None if bad """ try: # sadly subprocess.check_output() is not in Python 2.6 # so use Popen() # this throws an exception if not found proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) output = proc.communicate()[0].split("\n") if proc.returncode: # non-zero return code, fail return None except: if args.verbose: sys.stderr.write("Unable to run %s binary\n" % cmd[0]) # throws exception return None return output class CpuTemp: "Sensors on the CPU Core" has_sensors = False def __init__(self): # check for sensors binary ret = run_binary(["sensors", "-h"]) if ret is not None: self.has_sensors = True # pattern that matches the string that has the cpu temp self._pattern = re.compile('^\s+temp\d+_input:\s+([\d\.]+).*$') def get_data(self): "Collects the data and return the output as an array" if not self.has_sensors: return None _index = 0 _data = [] # grab the needed output output = run_binary(["sensors", "-u"]) if output is not None: for record in output: match = self._pattern.match(record) if match and match.group(1): _now = int(time.time()) _cpu_temprature = match.group(1) _data.append('%d LM%s %s' % (_now, _index, _cpu_temprature)) _index += 1 else: self.has_sensors = False if args.verbose: sys.stderr.write("No sensors returned temperatures. Have you run sensors-detect?") return _data class SmartCtl: "Sensor on the Hard Drive" _drives = [] has_smartctl = False def __init__(self): ret = run_binary(["smartctl", "-h"]) if ret is not None: self.has_smartctl = True if self.has_smartctl: # Which drive to watch for child in glob.glob('/dev/sd?'): self._drives.append(child) self._drives = sorted(self._drives) def get_data(self): "Collects the data and return the output as an array" if not self.has_smartctl: return None data = [] for _device in self._drives[:]: output = run_binary(["smartctl", "-A", _device]) if output is None: # do not keep trying on failure self._drives.remove(_device) else: for line in output: if line.startswith('194 '): now = int(time.time()) temp = line.split()[9] data.append('%d %s %s' % (now, _device, temp)) return data class Temper: """\ Reads 'temper-poll -c' for room temperature data. Before you can use this class you must have a TEMPer USB thermometer plugged in, and the temper-python package must be installed and configured. See their documentation for that procedure. """ has_temper = False def __init__(self): # check for sensors binary ret = run_binary(["temper-poll", "-h"]) if ret is not None: self.has_temper = True def get_data(self): "Collects the data and return the output as an array" if not self.has_temper: return None data = [] _device = 'TEMPER0' # only one device can read the TEMPer at a time # collisions will happen, so retry a few times for attempt in range(0, 3): # grab the needed output output = run_binary(["temper-poll", "-c"]) try: # make sure it is a temperature temp = float(output[0]) now = int(time.time()) data.append('%d %s %s' % (now, _device, temp)) break except: # bad data, ignore it, for a bit if args.verbose: sys.stderr.write("TEMPer-poll failed\n") if 0 == len(data): self.has_temper = False return data class ZoneTemp: "Zone sensors" def __init__(self): base_dir = '/sys/class/thermal/thermal_zone?/' self.zones = [] for child in glob.glob(base_dir): self.zones.append(child) def get_data(self): "Collects the data and return the output as an array" _zone = 0 _data = [] for zone in self.zones: _zone_data = open(os.path.join(zone, 'temp')) for line in _zone_data: temp = float(line) / 1000 _now = int(time.time()) _data.append('%d ZONE%s %s' % (_now, _zone, temp)) _zone += 1 _zone_data.close() return _data # Work with argvars parser = argparse.ArgumentParser(description="Temperature sensor daemon", epilog="""See the manual page for details.""") parser.add_argument('-l', '--logfile', dest='logfile', help="append log data to LOGFILE instead of stdout", nargs=1) parser.add_argument('-o', '--once', dest='once', help="Run the output once and exit", action='store_true') parser.add_argument('-q', '--quiet', action="store_true", dest='quiet', help="be quite") parser.add_argument('-v', '--verbose', action="store_true", dest='verbose', help="be verbose") parser.add_argument('-w', '--wait', default=[60], dest='wait', help="Set delay time in seconds, default is 60", nargs=1, type=int) parser.add_argument('-V', '--version', action="version", version="ntplogtemp %s" % ntp.util.stdversion()) args = parser.parse_args() def logging_setup(): "Create logging object" logFormat = logging.Formatter('%(message)s') # Create logger for cpuTemp tempLogger = logging.getLogger() tempLogger.setLevel(logging.INFO) # Create file handler if args.logfile: # log to logfile file = logging.handlers.TimedRotatingFileHandler( args.logfile[0], when='midnight', interval=1) else: # log to stdout file = logging.StreamHandler(sys.stdout) file.setLevel(logging.INFO) # Create the formatter and add it to the handler file.setFormatter(logFormat) # Add the handler to the logger tempLogger.addHandler(file) return tempLogger def logData(log, data): "log the data" if data is not None: for _item in data: log.info(_item) def log_data(): "Write all temperature readings to one file" # Create objects cpu = CpuTemp() zone = ZoneTemp() hdd = SmartCtl() temper = Temper() # Create the logger instance Logger = logging_setup() # Create data layout logData(Logger, ["# time, sensor, value"]) # Write data to their respective logs while True: logData(Logger, zone.get_data()) logData(Logger, cpu.get_data()) logData(Logger, hdd.get_data()) logData(Logger, temper.get_data()) if args.once: sys.exit(0) time.sleep(args.wait[0]) args = parser.parse_args() if os.getuid(): sys.stderr.write("You must be root!") sys.exit(1) try: log_data() except (KeyboardInterrupt, SystemExit): print("") # be nice to bash sys.exit(0) ntpsec-1.1.0+dfsg1/www/0000775000175000017500000000000013252650651014454 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/www/index.html0000644000175000017500000000141013252364117016442 0ustar rlaagerrlaager ntpviz
NTPsec

ntpviz






Top   Daily Stats   Weekly Stats  

You may customize this top level index file to your taste. You may wish to describe your system here, show contact information, or add a link to your running ntp.conf.
ntpsec-1.1.0+dfsg1/www/day/0000775000175000017500000000000013252650651015231 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/www/day/optionfile0000644000175000017500000000020013252364117017311 0ustar rlaagerrlaager# this is a comment --name=ntpvis-day # another comment --outdir day --period=1 # uncomment next line to clip the data #--clip ntpsec-1.1.0+dfsg1/www/day/favicon.ico0000644000175000017500000000217613252364117017355 0ustar rlaagerrlaager h(  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ÿÿÿÿÿŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ÿÿŸŸŸ`ÿÿŸŸŸ`ÿÿÿMMM`ÿŸŸŸ`ÿÿŸŸŸ`ÿÿÿŸŸŸ`ÿÿŸŸŸ`ÿMMM`ÿŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ÿÿÿÿÿŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€¿þßýÀïûìëëë+ëkëëìïûàßýÑÅÄntpsec-1.1.0+dfsg1/www/day/header0000644000175000017500000000027513252364117016405 0ustar rlaagerrlaager Top   Daily Stats   Weekly Stats  

ntpsec-1.1.0+dfsg1/www/day/footer0000644000175000017500000000006013252364117016443 0ustar rlaagerrlaager
ntpsec-1.1.0+dfsg1/www/week/0000775000175000017500000000000013252650651015407 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/www/week/header0000644000175000017500000000027413252364117016562 0ustar rlaagerrlaager Top   Daily Stats   Weekly Stats  

ntpsec-1.1.0+dfsg1/www/week/footer0000644000175000017500000000006013252364117016621 0ustar rlaagerrlaager
ntpsec-1.1.0+dfsg1/www/week/favicon.ico0000644000175000017500000000217613252364117017533 0ustar rlaagerrlaager h(  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ÿÿÿÿÿŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ÿÿŸŸŸ`ÿÿŸŸŸ`ÿÿÿMMM`ÿŸŸŸ`ÿÿŸŸŸ`ÿÿÿŸŸŸ`ÿÿŸŸŸ`ÿMMM`ÿŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ÿÿÿÿÿŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€¿þßýÀïûìëëë+ëkëëìïûàßýÑÅÄntpsec-1.1.0+dfsg1/www/week/optionfile0000644000175000017500000000020113252364117017470 0ustar rlaagerrlaager# this is a comment --name=ntpvis-week # another comment --outdir=week --period=7 # uncomment next line to clip the data #--clip ntpsec-1.1.0+dfsg1/www/ntpsec-logo.png0000644000175000017500000000305713252364117017416 0ustar rlaagerrlaager‰PNG  IHDR@J¡ûš3gAMA± üasRGB®Îé cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDþðˆü) pHYs  šœ(IDAThÞÍ™OLUÇ?3 (,,6 ‹‘ƒÒÄÛD]Óp©BáP!Mu{kìÁÅ/Jˆšª115¡1$MÚ¦DHL ^Š%z)Q8èÅDÔ¤ »ÿuÿ5´»ó<ìì2;;³;ow¶øÓì|÷÷¾ï÷ÞûýÞû=È`ˆ$ÂòY¢‰]4±dÄâ”d(CQuêê±Æaº oͤÄAš ¯Ý¶!Ös _€= ”äØ!@c)JMÞÛU– ’|œÀgù¯(ÓDM¿9ekô}jðžá­lMžaÓô[†½O ˆMQœ­Ú °F7ZÎ/>IYxŨxLgky³ÇÎ80”…bŸ„)6_¤*d;ò@ˆqNÓ@þ*$¯ ¥²•$ã"Tžø‚ïðüš&’rÌö¤[";ü]¦€4Û8‡%;UÂDé@Teä{À¸àªÓâ4 è–0å ²ýMÞ醳-f=pžC ç>þKÔõ¡ÐðqOîmŽóùÂŒb/?1Â6µ®5.€Þדs‚yF Ò¼Ù @ã+õ(Š‹ýâÒ!‚Š×δJ³úž%DP÷G¥hc‚m‚´²¡ ç,”âgRŸ q–ù¡b?htÒ$˜WÞnù=¦Ý,ÆWŒŠÝ|Šº¾Ä\m~E}ÆÚ—–¹ Ž[sb H!]ÁxhÅ,k_[,Üôìá9¶,c‚º4v9Eã.s1…¥vDi¶ùòg÷ÒÌÄ™çÙøI-å?'[²B£qœ Í܇‡4cÜ`† ü†&o¬ƒ|ȃþÔS}-ûçe.ò)!Ycò†9K ¿rbh¨ø8F}Lð(#V!¶ÁO¦ ä Peæ¶N“…êÜf>ÕÕ2™ ž]Ú6ƒnV¨Ô«.& Ævú9G+p“·øÅÕ*™@ð"§©68ÊF¡€VÎÑ@а©êïüúª›á•ÂÌ1æzYÊþ‰+¹äîLkÈʬî€WäNOæ@­›®q#øØ åÛ1Ý.Ø9³³)aQw‘{~c²çjhVÛîPë“Ý€ï®Ñ*ÏÌôÔ~{•¸Â+-x² ßõhB.ÀD‰¢ð£R•#ì*p'óË@߬eLó‘‹W4¥q›73›½]¸V`tÝãÿƒe˜Á¸35"wºç«@%¶·<Äxšº=ký? vgTl›ÜÍ%tEXtdate:create2015-06-29T18:30:07-04:00g#Û%tEXtdate:modify2015-06-29T18:30:07-04:00D›gIEND®B`‚ntpsec-1.1.0+dfsg1/www/favicon.ico0000644000175000017500000000217613252364117016600 0ustar rlaagerrlaager h(  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ÿÿÿÿÿŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ÿÿŸŸŸ`ÿÿŸŸŸ`ÿÿÿMMM`ÿŸŸŸ`ÿÿŸŸŸ`ÿÿÿŸŸŸ`ÿÿŸŸŸ`ÿMMM`ÿŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ÿÿÿÿÿŸŸŸ`ŸŸŸ`ÿÿŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ŸŸŸ`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€¿þßýÀïûìëëë+ëkëëìïûàßýÑÅÄntpsec-1.1.0+dfsg1/libparse/0000775000175000017500000000000013252650651015431 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/libparse/data_mbg.c0000644000175000017500000001504613252364117017336 0ustar rlaagerrlaager/* * Created: Sun Jul 20 12:08:14 1997 * * Copyright (c) 1997-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include #include "ntp_types.h" #include "ntp_stdlib.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "mbg_gps166.h" #include "binio.h" #include "ieee754io.h" static void get_mbg_cfg (unsigned char **, CFG *); static void get_mbg_health (unsigned char **, HEALTH *); static void get_mbg_tgps (unsigned char **, T_GPS *); static void get_mbg_tm (unsigned char **, TM_GPS *); static void mbg_time_status_str (char **, unsigned int, size_t); /* byte order for meinberg doubles */ static offsets_t mbg_double = { 1, 0, 3, 2, 5, 4, 7, 6 }; #define RAD2DEG 57.2957795131 /* 180/PI */ void put_mbg_header( unsigned char **bufpp, GPS_MSG_HDR *headerp ) { put_lsb_uint16(bufpp, headerp->cmd); put_lsb_uint16(bufpp, headerp->len); put_lsb_uint16(bufpp, headerp->data_csum); put_lsb_uint16(bufpp, headerp->hdr_csum); } void get_mbg_sw_rev( unsigned char **bufpp, SW_REV *sw_revp ) { sw_revp->code = get_lsb_uint16(bufpp); memcpy(sw_revp->name, *bufpp, sizeof(sw_revp->name)); *bufpp += sizeof(sw_revp->name); } void get_mbg_ascii_msg( unsigned char **bufpp, ASCII_MSG *ascii_msgp ) { ascii_msgp->csum = (CSUM) get_lsb_uint16(bufpp); ascii_msgp->valid = get_lsb_int16(bufpp); memcpy(ascii_msgp->s, *bufpp, sizeof(ascii_msgp->s)); *bufpp += sizeof(ascii_msgp->s); } static void get_mbg_health( unsigned char **bufpp, HEALTH *healthp ) { *healthp = (HEALTH) get_lsb_uint16(bufpp); } static void get_mbg_cfg( unsigned char **bufpp, CFG *cfgp ) { *cfgp = (CFG) get_lsb_uint16(bufpp); } static void get_mbg_tgps( unsigned char **bufpp, T_GPS *tgpsp ) { tgpsp->wn = get_lsb_uint16(bufpp); tgpsp->sec = get_lsb_uint32(bufpp); tgpsp->tick = get_lsb_uint32(bufpp); } static void get_mbg_tm( unsigned char **buffpp, TM_GPS *tmp ) { tmp->year = get_lsb_int16(buffpp); tmp->month = (int8_t)(*(*buffpp)++); tmp->mday = (int8_t)(*(*buffpp)++); tmp->yday = get_lsb_int16(buffpp); tmp->wday = (int8_t)(*(*buffpp)++); tmp->hour = (int8_t)(*(*buffpp)++); tmp->min = (int8_t)(*(*buffpp)++); tmp->sec = (int8_t)(*(*buffpp)++); tmp->frac = get_lsb_int32(buffpp); tmp->offs_from_utc = get_lsb_int32(buffpp); tmp->status = get_lsb_uint16(buffpp); } void get_mbg_antinfo( unsigned char **buffpp, ANT_INFO *antinfop ) { antinfop->status = get_lsb_int16(buffpp); get_mbg_tm(buffpp, &antinfop->tm_disconn); get_mbg_tm(buffpp, &antinfop->tm_reconn); antinfop->delta_t = get_lsb_int32(buffpp); } static void mbg_time_status_str( char **buffpp, unsigned int status, size_t size ) { static struct state { int flag; /* bit flag */ const char *string; /* bit name */ } states[] = { { TM_UTC, "UTC CORR" }, { TM_LOCAL, "LOCAL TIME" }, { TM_DL_ANN, "DST WARN" }, { TM_DL_ENB, "DST" }, { TM_LS_ANN, "LEAP WARN" }, { TM_LS_ENB, "LEAP SEC" }, { 0, "" } }; if (status) { char *start, *p; struct state *s; start = p = *buffpp; for (s = states; s->flag; s++) { if ( (unsigned int)s->flag & status) { if (p != *buffpp) { strlcpy(p, ", ", size - (size_t)(p - start)); p += 2; } strlcpy(p, s->string, size - (size_t)(p - start)); p += strlen(p); } } *buffpp = p; } } void mbg_tm_str( char **buffpp, TM_GPS *tmp, size_t size, int print_status ) { char *s = *buffpp; snprintf(*buffpp, size, "%04d-%02d-%02d %02d:%02d:%02d.%07ld (%c%02d%02d) ", tmp->year, tmp->month, tmp->mday, tmp->hour, tmp->min, tmp->sec, (long) tmp->frac, (tmp->offs_from_utc < 0) ? '-' : '+', abs((int)tmp->offs_from_utc) / 3600, (abs((int)tmp->offs_from_utc) / 60) % 60); *buffpp += strlen(*buffpp); if (print_status) mbg_time_status_str(buffpp, tmp->status, size - (size_t)(*buffpp - s)); } void mbg_tgps_str( char **buffpp, T_GPS *tgpsp, size_t size ) { snprintf(*buffpp, size, "week %d + %ld days + %ld.%07ld sec", tgpsp->wn, (long) tgpsp->sec / SECSPERDAY, (long) tgpsp->sec % SECSPERDAY, (long) tgpsp->tick); *buffpp += strlen(*buffpp); } void get_mbg_cfgh( unsigned char **buffpp, CFGH *cfghp ) { int i; cfghp->csum = (CSUM) get_lsb_uint16(buffpp); cfghp->valid = get_lsb_int16(buffpp); get_mbg_tgps(buffpp, &cfghp->tot_51); get_mbg_tgps(buffpp, &cfghp->tot_63); get_mbg_tgps(buffpp, &cfghp->t0a); for (i = 0; i < N_SVNO_GPS; i++) { get_mbg_cfg(buffpp, &cfghp->cfg[i]); } for (i = 0; i < N_SVNO_GPS; i++) { get_mbg_health(buffpp, &cfghp->health[i]); } } void get_mbg_utc( unsigned char **buffpp, UTC *utcp ) { utcp->csum = (CSUM) get_lsb_uint16(buffpp); utcp->valid = get_lsb_int16(buffpp); get_mbg_tgps(buffpp, &utcp->t0t); if (fetch_ieee754(buffpp, IEEE_DOUBLE, &utcp->A0, mbg_double) != IEEE_OK) { utcp->A0 = 0; } if (fetch_ieee754(buffpp, IEEE_DOUBLE, &utcp->A1, mbg_double) != IEEE_OK) { utcp->A1 = 0; } utcp->WNlsf = get_lsb_uint16(buffpp); utcp->DNt = get_lsb_int16(buffpp); utcp->delta_tls = (int8_t)(*(*buffpp)++); utcp->delta_tlsf = (int8_t)(*(*buffpp)++); } void get_mbg_lla( unsigned char **buffpp, LLA lla ) { int i; for (i = LAT; i <= ALT; i++) { if (fetch_ieee754(buffpp, IEEE_DOUBLE, &lla[i], mbg_double) != IEEE_OK) lla[i] = 0; else if (i != ALT) lla[i] *= RAD2DEG; /* convert to degrees (* 180/PI) */ } } void get_mbg_xyz( unsigned char **buffpp, XYZ xyz ) { int i; for (i = XP; i <= ZP; i++) { if (fetch_ieee754(buffpp, IEEE_DOUBLE, &xyz[i], mbg_double) != IEEE_OK) { xyz[i] = 0; } } } /* * data_mbg.c,v * Revision 4.8 2006/06/22 18:40:01 kardel * clean up signedness (gcc 4) * * Revision 4.7 2005/10/07 22:11:10 kardel * bounded buffer implementation * * Revision 4.6.2.1 2005/09/25 10:23:06 kardel * support bounded buffers * * Revision 4.6 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.5 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.3 1999/02/21 12:17:42 kardel * 4.91f reconciliation * * Revision 4.2 1998/06/14 21:09:39 kardel * Sun acc cleanup * * Revision 4.1 1998/05/24 08:02:06 kardel * trimmed version log * * Revision 4.0 1998/04/10 19:45:33 kardel * Start 4.0 release version numbering */ ntpsec-1.1.0+dfsg1/libparse/info_trimble.c0000644000175000017500000000722313252364117020247 0ustar rlaagerrlaager #include "config.h" #include "ntp_types.h" #include "ntpd.h" #include "trimble.h" cmd_info_t trimble_rcmds[] = { { CMD_RDATAA, "CMD_RDATAA", "data channel A configuration (0x3D)", "trimble_channelA", RO }, { CMD_RALMANAC, "CMD_RALMANAC", "almanac data for sat (0x40)", "gps_almanac", RO }, { CMD_RCURTIME, "CMD_RCURTIME", "GPS time (0x41)", "gps_time", RO }, { CMD_RSPOSXYZ, "CMD_RSPOSXYZ", "single precision XYZ position (0x42)", "gps_position(XYZ)", RO|DEF }, { CMD_RVELOXYZ, "CMD_RVELOXYZ", "velocity fix (XYZ ECEF) (0x43)", "gps_velocity(XYZ)", RO|DEF }, { CMD_RBEST4, "CMD_RBEST4", "best 4 satellite selection (0x44)", "trimble_best4", RO|DEF }, { CMD_RVERSION, "CMD_RVERSION", "software version (0x45)", "trimble_version", RO|DEF }, { CMD_RRECVHEALTH, "CMD_RRECVHEALTH", "receiver health (0x46)", "trimble_receiver_health", RO|DEF }, { CMD_RSIGNALLV, "CMD_RSIGNALLV", "signal levels of all satellites (0x47)", "trimble_signal_levels", RO }, { CMD_RMESSAGE, "CMD_RMESSAGE", "GPS system message (0x48)", "gps-message", RO|DEF }, { CMD_RALMAHEALTH, "CMD_RALMAHEALTH", "almanac health page for all satellites (0x49)", "gps_almanac_health", RO }, { CMD_RSLLAPOS, "CMD_RSLLAPOS", "single LLA position (0x4A)", "gps_position(LLA)", RO|DEF }, { CMD_RMACHSTAT, "CMD_RMACHSTAT", "machine code / status (0x4B)", "trimble_status", RO|DEF }, { CMD_ROPERPARAM, "CMD_ROPERPARAM", "operating parameters (0x4C)", "trimble_opparam", RO }, { CMD_ROSCOFFSET, "CMD_ROSCOFFSET", "oscillator offset (0x4D)", "trimble_oscoffset", RO }, { CMD_RSETGPSTIME, "CMD_RSETGPSTIME", "response to set GPS time (0x4E)", "trimble_setgpstime", RO }, { CMD_RUTCPARAM, "CMD_RUTCPARAM", "UTC parameters (0x4F)", "gps_utc_correction", RO|DEF }, { CMD_RANALOGDIG, "CMD_RANALOGDIG", "analog to digital (0x53)", "trimble_analogdigital", RO }, { CMD_RSAT1BIAS, "CMD_RSAT1BIAS", "one-satellite bias & bias rate (0x54)", "trimble_sat1bias", RO }, { CMD_RIOOPTIONS, "CMD_RIOOPTIONS", "I/O options (0x55)", "trimble_iooptions", RO }, { CMD_RSTATLSTFIX, "CMD_RSTATLSTFIX", "status and values of last pos. and vel. (0x57)", "trimble_status_lastpos", RO }, { CMD_RLOADSSATDT, "CMD_RLOADSSATDT", "response to load satellite system data (0x58)", "trimble_loaddata", RO }, { CMD_RSATDISABLE, "CMD_RSATDISABLE", "satellite disable (0x59)", "trimble_satdisble", RO }, { CMD_RLASTRAW, "CMD_RLASTRAW", "last raw measurement (0x5A)", "trimble_lastraw", RO }, { CMD_RSTATSATEPH, "CMD_RSTATSATEPH", "satellite ephemeris status (0x5B)", "trimble_ephstatus", RO }, { CMD_RSTATTRACK, "CMD_RSTATTRACK", "tracking status (0x5C)", "trimble_tracking_status", RO|DEF }, { CMD_RADDITFIX, "CMD_RADDITFIX", "additional fix data (0x5E)", "trimble_addfix", RO }, { CMD_RALLINVIEW, "CMD_RALLINVIEW", "all in view satellite selection (0x6D)", "trimble_satview", RO|DEF }, { CMD_RPOSFILT, "CMD_RPOSFILT", "position filter parameters (0x72)", "trimble_posfilt", RO }, { CMD_RHEIGHTFILT, "CMD_RHEIGHTFILT", "height filter control (0x74)", "trimble_heightfilt", RO }, { CMD_RHIGH8CNT, "CMD_RHIGH8CNT", "high-8 (best 4) / high-6 (overdetermined) control (0x76)", "trimble_high8control", RO }, { CMD_RMAXAGE, "CMD_RMAXAGE", "DC MaxAge (0x78)", "trimble_dgpsmaxage", RO }, { CMD_RDGPSFIX, "CMD_RDGPSFIX", "differential position fix mode (0x82)", "trimble_dgpsfixmode", RO }, { CMD_RDOUBLEXYZ, "CMD_RDOUBLEXYZ", "double precision XYZ (0x83)", "gps_position_ext(XYZ)", RO|DEF }, { CMD_RDOUBLELLA, "CMD_RDOUBLELLA", "double precision LLA (0x84)", "gps_position_ext(LLA)", RO|DEF }, { CMD_RDGPSSTAT, "CMD_RDGPSSTAT", "differential correction status (0x85)", "trimble_dgpsstatus", RO }, { CMD_RSUPER, "CMD_RSUPER", "super paket (0x8F)", "", 0 }, { 0xFF, "", "", "", 0 } }; ntpsec-1.1.0+dfsg1/libparse/clk_sel240x.c0000644000175000017500000000744513252364117017636 0ustar rlaagerrlaager////////////////////////////////////////////////////////////////////////////// // Copyright (c) 2009,2012 - // Schweitzer Engineering Laboratories, Inc. ////////////////////////////////////////////////////////////////////////////// // Need to have _XOPEN_SOURCE properly defined for time.h to give the // correct strptime signature. As per feature_test_macros(7), // define this before including any header files. #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #include "config.h" #include "ntp.h" /* only for the u_* typedefs from GCC; remove someday */ #include "ntp_syslog.h" #include "ntp_types.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "ntp_machine.h" #include "ntp_stdlib.h" #include "parse.h" #include #include ////////////////////////////////////////////////////////////////////////////// // The B8 output has the following format B8 = '\x01YYYY:ddd:hh:mm:ssq\r\n' // where q = ' ' locked // '.' <1 us // '*' <10 us // '#' <100 us // '?' >100 us // // Based on this we need to recored the stime when we receive the // character and end it when we see the \n. // // The q or quality character indicates satellite lock and sync. For the // purposes of NTP we are going to call it valid when we receive anything but // a '?'. But we are only going to call it synced when we receive a ' ' ////////////////////////////////////////////////////////////////////////////// static parse_inp_fnc_t inp_sel240x; static parse_cvt_fnc_t cvt_sel240x; // Parse clock format structure describing the message above static struct format sel240x_fmt = { { { 6, 3 }, { 0, 0 }, { 1, 4 }, { 10, 2 }, { 13, 2 }, { 16, 2 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, (const unsigned char *)"\x01 : : : : \x0d\x0a", 0 }; // Structure desctibing the parser clockformat_t clock_sel240x = { inp_sel240x, cvt_sel240x, pps_one, (void*)&sel240x_fmt, "SEL B8", 25, 0 }; ////////////////////////////////////////////////////////////////////////////// static unsigned long inp_sel240x( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned long rc; parseprintf(DD_PARSE, ("inp_sel240x(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch( ch ) { case '\x01': parseio->parse_index = 1; parseio->parse_data[0] = ch; parseio->parse_dtime.parse_stime = *tstamp; rc = PARSE_INP_SKIP; break; case '\n': if( (rc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP ) { rc = parse_end( parseio ); } break; default: rc = parse_addchar( parseio, ch ); } return rc; } ////////////////////////////////////////////////////////////////////////////// static unsigned long cvt_sel240x( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { unsigned long rc = CVT_NONE; UNUSED_ARG(size); UNUSED_ARG(local); if( Strok(buffer, format->fixed_string) ) { struct tm ptime; buffer++; buffer = (unsigned char *)strptime( (const char *)buffer, "%Y:%j:%H:%M:%S", &ptime ); if( *(buffer+1) != '\x0d' ) { rc = CVT_FAIL | CVT_BADFMT; } else { clock_time->day = ptime.tm_mday; clock_time->month = ptime.tm_mon + 1; clock_time->year = ptime.tm_year + 1900; clock_time->hour = ptime.tm_hour; clock_time->minute = ptime.tm_min; clock_time->second = ptime.tm_sec; clock_time->usecond = 0; clock_time->utcoffset = 0; clock_time->flags = PARSEB_UTC; if( *buffer == '?' ) { clock_time->flags |= PARSEB_POWERUP; } else if( *buffer != ' ' ) { clock_time->flags |= PARSEB_NOSYNC; } rc = CVT_OK; } } return rc; } ntpsec-1.1.0+dfsg1/libparse/parse.c0000644000175000017500000003617013252364117016713 0ustar rlaagerrlaager/* * Parser module for reference clock * * Copyright (c) 1989-2015 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause * */ #include "config.h" #include "ntp_fp.h" #include "timespecops.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include "ntp_machine.h" #include "ntp.h" /* (get Y2KFixes definitions) Y2KFixes */ #include "parse.h" # include /* * PPS edge info */ #define SYNC_ONE 0x01 static unsigned long timepacket (parse_t *); static unsigned int parse_restart (parse_t *, char); static parse_pps_fnc_t pps_simple; bool parse_timedout( parse_t *parseio, timestamp_t *tstamp, struct timespec *del ) { struct timespec delta; l_fp delt; delt = *tstamp; delt -= parseio->parse_lastchar; delta = lfp_uintv_to_tspec(delt); if (cmp_tspec(delta, *del) > 0) { parseprintf(DD_PARSE, ("parse: timedout: TRUE\n")); return true; } else { parseprintf(DD_PARSE, ("parse: timedout: FALSE\n")); return false; } } /*ARGSUSED*/ bool parse_ioinit( parse_t *parseio ) { parseprintf(DD_PARSE, ("parse_iostart\n")); parseio->parse_plen = 0; parseio->parse_pdata = (void *)0; parseio->parse_data = 0; parseio->parse_ldata = 0; parseio->parse_dsize = 0; parseio->parse_badformat = 0; parseio->parse_ioflags = PARSE_IO_CS7; /* usual unix default */ parseio->parse_index = 0; parseio->parse_ldsize = 0; return true; } /*ARGSUSED*/ void parse_ioend( parse_t *parseio ) { parseprintf(DD_PARSE, ("parse_ioend\n")); if (parseio->parse_pdata) free(parseio->parse_pdata); if (parseio->parse_data) free(parseio->parse_data); } static unsigned int parse_restart( parse_t *parseio, char ch ) { unsigned int updated = PARSE_INP_SKIP; /* * re-start packet - timeout - overflow - start symbol */ if (parseio->parse_index) { /* * filled buffer - thus not end character found * do processing now */ parseio->parse_data[parseio->parse_index] = '\0'; memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1)); parseio->parse_ldsize = parseio->parse_index; updated = PARSE_INP_TIME; } parseio->parse_index = 1; parseio->parse_data[0] = ch; parseprintf(DD_PARSE, ("parse: parse_restart: buffer start (updated = %x)\n", updated)); return updated; } unsigned int parse_addchar( parse_t *parseio, char ch ) { /* * add to buffer */ if (parseio->parse_index < parseio->parse_dsize) { /* * collect into buffer */ parseprintf(DD_PARSE, ("parse: parse_addchar: buffer[%d] = 0x%x\n", parseio->parse_index, (unsigned)ch)); parseio->parse_data[parseio->parse_index++] = (char)ch; return PARSE_INP_SKIP; } else /* * buffer overflow - attempt to make the best of it */ return parse_restart(parseio, ch); } unsigned int parse_end( parse_t *parseio ) { /* * message complete processing */ parseio->parse_data[parseio->parse_index] = '\0'; memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1)); parseio->parse_ldsize = parseio->parse_index; parseio->parse_index = 0; parseprintf(DD_PARSE, ("parse: parse_end: buffer end\n")); return PARSE_INP_TIME; } /*ARGSUSED*/ int parse_ioread( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int updated = CVT_NONE; /* * within STREAMS CSx (x < 8) chars still have the upper bits set * so we normalize the characters by masking unnecessary bits off. * * (ESR, 2015: Probably not necessary since STREAMS support has * been removed, but harmless.) */ switch (parseio->parse_ioflags & PARSE_IO_CSIZE) { case PARSE_IO_CS5: ch &= 0x1F; break; case PARSE_IO_CS6: ch &= 0x3F; break; case PARSE_IO_CS7: ch &= 0x7F; break; case PARSE_IO_CS8: ch &= (char) 0xFFU; break; default: /* huh? */ break; } parseprintf(DD_PARSE, ("parse_ioread(0x%lx, char=0x%x, ..., ...)\n", (unsigned long)parseio, (unsigned)(ch & 0xFF))); if (!clockformats[parseio->parse_lformat]->convert) { parseprintf(DD_PARSE, ("parse_ioread: input dropped.\n")); return CVT_NONE; } if (clockformats[parseio->parse_lformat]->input) { unsigned long input_status; input_status = clockformats[parseio->parse_lformat]->input(parseio, ch, tstamp); if (input_status & PARSE_INP_SYNTH) { updated = CVT_OK; } if (input_status & PARSE_INP_TIME) /* time sample is available */ { updated = (unsigned int) timepacket(parseio); } if (input_status & PARSE_INP_DATA) /* got additional data */ { updated |= CVT_ADDITIONAL; } } /* * remember last character time */ parseio->parse_lastchar = *tstamp; #ifdef DEBUG if ((updated & CVT_MASK) != CVT_NONE) { parseprintf(DD_PARSE, ("parse_ioread: time sample accumulated (status=0x%x)\n", updated)); } #endif parseio->parse_dtime.parse_status = updated; return (((updated & CVT_MASK) != CVT_NONE) || ((updated & CVT_ADDITIONAL) != 0)); } /* * parse_iodone * * clean up internal status for new round */ /*ARGSUSED*/ void parse_iodone( parse_t *parseio ) { /* * we need to clean up certain flags for the next round */ parseprintf(DD_PARSE, ("parse_iodone: DONE\n")); parseio->parse_dtime.parse_state = 0; /* no problems with ISRs */ } /*---------- conversion implementation --------------------*/ /* * convert a struct clock to UTC since Jan, 1st 1970 0:00 (the UNIX EPOCH) */ time_t parse_to_unixtime( clocktime_t *clock_time, unsigned long *cvtrtc ) { #define SETRTC(_X_) { if (cvtrtc) *cvtrtc = (_X_); } static int days_of_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int i; time_t t; if (clock_time->utctime) return clock_time->utctime; /* if the conversion routine gets it right away - why not */ if ( clock_time->year < YEAR_PIVOT ) /* Y2KFixes [ */ clock_time->year += 100; /* convert 20xx%100 to 20xx-1900 */ if ( clock_time->year < YEAR_BREAK ) /* expand to full four-digits */ clock_time->year += 1900; if (clock_time->year < 1970 ) /* Y2KFixes ] */ { SETRTC(CVT_FAIL|CVT_BADDATE); return -1; } /* * sorry, slow section here - but it's not time critical anyway */ t = julian0(clock_time->year) - julian0(1970); /* Y2kFixes */ /* month */ if (clock_time->month <= 0 || clock_time->month > 12) { SETRTC(CVT_FAIL|CVT_BADDATE); return -1; /* bad month */ } if ( clock_time->month >= 3 && is_leapyear(clock_time->year) ) t++; /* add one more if within leap year */ for (i = 1; i < clock_time->month; i++) { t += days_of_month[i]; } /* day */ if (clock_time->day < 1 || ((clock_time->month == 2 && days_per_year(clock_time->year) == 366) ? clock_time->day > 29 : clock_time->day > days_of_month[clock_time->month])) { SETRTC(CVT_FAIL|CVT_BADDATE); return -1; /* bad day */ } t += clock_time->day - 1; /* hour */ if (clock_time->hour < 0 || clock_time->hour >= 24) { SETRTC(CVT_FAIL|CVT_BADTIME); return -1; /* bad hour */ } t = t*24 + clock_time->hour; /* min */ if (clock_time->minute < 0 || clock_time->minute > 59) { SETRTC(CVT_FAIL|CVT_BADTIME); return -1; /* bad min */ } t = t*60 + clock_time->minute; /* sec */ if (clock_time->second < 0 || clock_time->second > 60) /* allow for LEAPs */ { SETRTC(CVT_FAIL|CVT_BADTIME); return -1; /* bad sec */ } t = t*60 + clock_time->second; t += clock_time->utcoffset; /* warp to UTC */ /* done */ clock_time->utctime = t; /* documentray only */ return t; } /*--------------- format conversion -----------------------------------*/ int Stoi( const unsigned char *s, long *zp, int cnt ) { char unsigned const *b = s; int f,z,v; char unsigned c; f=z=v=0; while(*s == ' ') s++; if (*s == '-') { s++; v = 1; } else if (*s == '+') s++; for(;;) { c = *s++; if (c == '\0' || c < '0' || c > '9' || (cnt && ((s-b) > cnt))) { if (f == 0) { return(-1); } if (v) z = -z; *zp = z; return(0); } z = (z << 3) + (z << 1) + ( c - '0' ); f=1; } } int Strok( const unsigned char *s, const unsigned char *m ) { if (!s || !m) return 0; while(*s && *m) { if ((*m == ' ') ? 1 : (*s == *m)) { s++; m++; } else { return 0; } } return !*m; } unsigned long updatetimeinfo( parse_t *parseio, unsigned long flags ) { parseio->parse_lstate = parseio->parse_dtime.parse_state | flags | PARSEB_TIMECODE; parseio->parse_dtime.parse_state = parseio->parse_lstate; parseprintf(DD_PARSE, ("updatetimeinfo status=0x%lx, time=%x\n", (unsigned long)parseio->parse_dtime.parse_state, lfpuint(parseio->parse_dtime.parse_time))); return CVT_OK; /* everything fine and dandy... */ } /* * parse_pps_fnc_t pps_simple * * handle a pps time stamp */ /*ARGSUSED*/ static unsigned long pps_simple( parse_t *parseio, int status, timestamp_t *ptime ) { UNUSED_ARG(status); parseio->parse_dtime.parse_ptime = *ptime; parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; return CVT_NONE; } /* * parse_pps_fnc_t pps_one * * handle a pps time stamp in ONE edge */ /*ARGSUSED*/ unsigned long pps_one( parse_t *parseio, int status, timestamp_t *ptime ) { if (status) return pps_simple(parseio, status, ptime); return CVT_NONE; } /* * timepacket * * process a data packet */ static unsigned long timepacket( parse_t *parseio ) { unsigned short format; time_t t; unsigned long cvtrtc; /* current conversion result */ clocktime_t clock_time; memset((char *)&clock_time, 0, sizeof clock_time); format = parseio->parse_lformat; if (format == (unsigned short)~0) return CVT_NONE; switch ((cvtrtc = clockformats[format]->convert ? clockformats[format]->convert((unsigned char *)parseio->parse_ldata, parseio->parse_ldsize, (struct format *)(clockformats[format]->data), &clock_time, parseio->parse_pdata) : CVT_NONE) & CVT_MASK) { case CVT_FAIL: parseio->parse_badformat++; return cvtrtc; case CVT_NONE: /* * too bad - pretend bad format */ parseio->parse_badformat++; return CVT_NONE; case CVT_OK: break; case CVT_SKIP: return CVT_NONE; default: /* shouldn't happen */ msyslog(LOG_WARNING, "ERR: parse: INTERNAL error: bad return code of convert routine \"%s\"", clockformats[format]->name); return CVT_FAIL|cvtrtc; } if ((t = parse_to_unixtime(&clock_time, &cvtrtc)) == -1) { return CVT_FAIL|cvtrtc; } /* * time stamp */ struct timespec ts = {t, clock_time.usecond * 1000}; parseio->parse_dtime.parse_time = tspec_stamp_to_lfp(ts); parseio->parse_dtime.parse_format = format; return updatetimeinfo(parseio, clock_time.flags); } /*ARGSUSED*/ bool parse_timecode( parsectl_t *dct, parse_t *parse ) { dct->parsegettc.parse_state = parse->parse_lstate; dct->parsegettc.parse_format = parse->parse_lformat; /* * move out current bad packet count * user program is expected to sum these up * this is not a problem, as "parse" module are * exclusive open only */ dct->parsegettc.parse_badformat = parse->parse_badformat; parse->parse_badformat = 0; if (parse->parse_ldsize <= PARSE_TCMAX) { dct->parsegettc.parse_count = parse->parse_ldsize; memcpy(dct->parsegettc.parse_buffer, parse->parse_ldata, dct->parsegettc.parse_count); return true; } else { return false; } } /*ARGSUSED*/ bool parse_setfmt( parsectl_t *dct, parse_t *parse ) { if (dct->parseformat.parse_count <= PARSE_TCMAX) { if (dct->parseformat.parse_count) { unsigned short i; for (i = 0; i < nformats; i++) { if (!strcmp(dct->parseformat.parse_buffer, clockformats[i]->name)) { if (parse->parse_pdata) free(parse->parse_pdata); parse->parse_pdata = 0; parse->parse_plen = clockformats[i]->plen; if (parse->parse_plen) { parse->parse_pdata = malloc(parse->parse_plen); if (!parse->parse_pdata) { parseprintf(DD_PARSE, ("set format failed: malloc for private data area failed\n")); return false; } memset((char *)parse->parse_pdata, 0, parse->parse_plen); } if (parse->parse_data) free(parse->parse_data); parse->parse_ldata = parse->parse_data = 0; parse->parse_dsize = clockformats[i]->length; if (parse->parse_dsize) { parse->parse_data = (char*)malloc((unsigned)(parse->parse_dsize * 2 + 2)); if (!parse->parse_data) { if (parse->parse_pdata) free(parse->parse_pdata); parse->parse_pdata = 0; parseprintf(DD_PARSE, ("init failed: malloc for data area failed\n")); return false; } } /* * leave room for '\0' */ parse->parse_ldata = parse->parse_data + parse->parse_dsize + 1; parse->parse_lformat = i; return true; } } } } return false; } /*ARGSUSED*/ int parse_getfmt( parsectl_t *dct, parse_t *parse ) { UNUSED_ARG(dct); UNUSED_ARG(parse); if (dct->parseformat.parse_format < nformats && strlen(clockformats[dct->parseformat.parse_format]->name) <= PARSE_TCMAX) { dct->parseformat.parse_count = (unsigned short) (strlen(clockformats[dct->parseformat.parse_format]->name) + 1); memcpy(dct->parseformat.parse_buffer, clockformats[dct->parseformat.parse_format]->name, dct->parseformat.parse_count); return 1; } else { return 0; } } /*ARGSUSED*/ bool parse_setcs( parsectl_t *dct, parse_t *parse ) { parse->parse_ioflags &= ~PARSE_IO_CSIZE; parse->parse_ioflags |= (int) (dct->parsesetcs.parse_cs & PARSE_IO_CSIZE); return true; } /* * History: * * parse.c,v * Revision 4.20 2005/08/06 17:39:40 kardel * cleanup size handling wrt/ to buffer boundaries * * Revision 4.19 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.18 2004/11/14 16:11:05 kardel * update Id tags * * Revision 4.17 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.14 1999/11/28 09:13:52 kardel * RECON_4_0_98F * * Revision 4.13 1999/02/28 11:50:20 kardel * (timepacket): removed unnecessary code * * Revision 4.12 1999/02/21 12:17:44 kardel * 4.91f reconciliation * * Revision 4.11 1999/02/21 11:09:47 kardel * unified debug output * * Revision 4.10 1998/12/20 23:45:30 kardel * fix types and warnings * * Revision 4.9 1998/08/09 22:26:06 kardel * Trimble TSIP support * * Revision 4.8 1998/06/14 21:09:39 kardel * Sun acc cleanup * * Revision 4.7 1998/06/13 15:19:13 kardel * fix mem*() to b*() function macro emulation * * Revision 4.6 1998/06/13 13:24:13 kardel * printf fmt * * Revision 4.5 1998/06/13 13:01:10 kardel * printf fmt * * Revision 4.4 1998/06/13 12:12:10 kardel * bcopy/memcpy cleanup * fix SVSV name clash * * Revision 4.3 1998/06/12 15:22:30 kardel * fix prototypes * * Revision 4.2 1998/06/12 09:13:27 kardel * conditional compile macros fixed * printf prototype * * Revision 4.1 1998/05/24 09:39:55 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:36 kardel * Start 4.0 release version numbering * * from V3 3.46 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/README0000644000175000017500000000542713252364117016316 0ustar rlaagerrlaager= PARSE reference clock driver = by Frank Kardel This directory contains the files making up the parser for the parse refclock driver. For reasonably sane clocks this refclock drivers allows a refclock implementation by just providing a conversion routine and the appropriate NTP parameters. Refclock support can run as low as 3k code with the parse refclock driver. The structure of the parse reference clock driver is as follows: ntpd contains the contains NTP implementation and calls a reference clock driver named "generic" which is implemented by refclock_parse.c, which contains a large number of refclock descriptions. The kind of clock is selected by the subtype parameter. This parameter selects the clock type which determines how I/O is done, the tty parameters and the NTP parameters. refclock_parse operates on an abstract reference clock that delivers time stamps and statuses. Offsets and sychronization information is derived from this data and passed on to refclock_receive of ntpd which uses that data for syncronisation. The abstract reference clock is generated by the parse* routines. They parse the incoming data stream from the clock and convert it to the appropriate time stamps. The data is also mapped into the abstract clock states POWERUP:: clock has no valid phase and time code information. NOSYNC:: Time code is not confirmed, phase is probably OK. SYNC:: Time code and phase are correct. A clock is trusted for a certain time (type parameter) when it leaves the SYNC state. This is derived from the observation that quite a few clocks can still generate good time code information when losing contact to their synchronisation source. When the clock does not reagain synchronisation in that trust period it will be deemed unsynchronised until it regains synchronisation. The same will happen if ntpd sees the clock unsynchronised at startup. The upper bit of x specifies that all samples delivered from the clock should be used to discipline the NTP loopfilter. For clock with accurate once a second time information this means big improvements for time keeping. A prerequisite for passing on the time stamps to the loopfilter is that the clock is in synchronised state. Here is how to read the files in this (libparse) directory: parse.c:: These are the general routines to parse the incoming data stream. Usually these routines should not require modification. clk_*.c:: These files hold the conversion code for the time stamps and the description how the time code can be parsed and where the time stamps are to be taken. If you want to add a new clock type, this is the file you need to write in addition to mentioning it in parse_conf.c and setting up the NTP and TTY parameters in refclock_parse.c. Further information can be found in the various source files. ntpsec-1.1.0+dfsg1/libparse/binio.c0000644000175000017500000000313213252364117016671 0ustar rlaagerrlaager/* * Created: Sun Jul 20 12:55:33 1997 * * Copyright (c) 1997-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "binio.h" int16_t get_lsb_int16( unsigned char **bufpp ) { int16_t retval; retval = *((*bufpp)++); retval |= *((*bufpp)++) << 8; return retval; } void put_lsb_uint16( unsigned char **bufpp, uint16_t val ) { *((*bufpp)++) = (unsigned char) (val & 0xFF); *((*bufpp)++) = (unsigned char) ((val >> 8) & 0xFF); } int32_t get_lsb_int32( unsigned char **bufpp ) { int32_t retval; retval = *((*bufpp)++); retval |= *((*bufpp)++) << 8; retval |= *((*bufpp)++) << 16; retval |= *((*bufpp)++) << 24; return retval; } unsigned short get_msb_ushort( unsigned char *p ) { return (unsigned short) get_msb_short(&p); } short getmsb_short( unsigned char *p ) { return (short) get_msb_short(&p); } long get_msb_short( unsigned char **bufpp ) { long retval; retval = *((*bufpp)++) << 8; retval |= *((*bufpp)++); return (retval & 0x8000) ? (~0xFFFF | retval) : retval; } /* * binio.c,v * Revision 4.2 1999/02/21 12:17:34 kardel * 4.91f reconciliation * * Revision 4.1 1998/06/28 16:47:50 kardel * added {get,put}_msb_{short,long} functions * * Revision 4.0 1998/04/10 19:46:16 kardel * Start 4.0 release version numbering * * Revision 1.1 1998/04/10 19:27:46 kardel * initial NTP VERSION 4 integration of PARSE with GPS166 binary support * * Revision 1.1 1997/10/06 21:05:46 kardel * new parse structure * */ ntpsec-1.1.0+dfsg1/libparse/clk_rawdcf.c0000644000175000017500000004336613252364117017705 0ustar rlaagerrlaager/* * Raw DCF77 pulse clock support * * Copyright (c) 1989-2015 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "timespecops.h" #include "ntp_calendar.h" #include "parse.h" #include "ntp_stdlib.h" /* * DCF77 raw time code * * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig * und Berlin, Maerz 1989 * * Timecode transmission: * AM: * time marks are send every second except for the second before the * next minute mark * time marks consist of a reduction of transmitter power to 25% * of the nominal level * the falling edge is the time indication (on time) * time marks of a 100ms duration constitute a logical 0 * time marks of a 200ms duration constitute a logical 1 * FM: * see the spec. (basically a (non-)inverted pseudo random phase shift) * * Encoding: * Second Contents * 0 - 10 AM: free, FM: 0 * 11 - 14 free * 15 R - "call bit" used to signalize irregularities in the control facilities * (until 2003 indicated transmission via alternate antenna) * 16 A1 - expect zone change (1 hour before) * 17 - 18 Z1,Z2 - time zone * 0 0 illegal * 0 1 MEZ (MET) * 1 0 MESZ (MED, MET DST) * 1 1 illegal * 19 A2 - expect leap insertion/deletion (1 hour before) * 20 S - start of time code (1) * 21 - 24 M1 - BCD (lsb first) Minutes * 25 - 27 M10 - BCD (lsb first) 10 Minutes * 28 P1 - Minute Parity (even) * 29 - 32 H1 - BCD (lsb first) Hours * 33 - 34 H10 - BCD (lsb first) 10 Hours * 35 P2 - Hour Parity (even) * 36 - 39 D1 - BCD (lsb first) Days * 40 - 41 D10 - BCD (lsb first) 10 Days * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday) * 45 - 49 MO - BCD (lsb first) Month * 50 MO0 - 10 Months * 51 - 53 Y1 - BCD (lsb first) Years * 54 - 57 Y10 - BCD (lsb first) 10 Years * 58 P3 - Date Parity (even) * 59 - usually missing (minute indication), except for leap insertion */ static parse_pps_fnc_t pps_rawdcf; static parse_cvt_fnc_t cvt_rawdcf; static parse_inp_fnc_t inp_rawdcf; typedef struct last_tcode { time_t tcode; /* last converted time code */ timestamp_t tminute; /* sample time for minute start */ timestamp_t timeout; /* last timeout timestamp */ } last_tcode_t; #define BUFFER_MAX 61 clockformat_t clock_rawdcf = { inp_rawdcf, /* DCF77 input handling */ cvt_rawdcf, /* raw dcf input conversion */ pps_rawdcf, /* examining PPS information */ 0, /* no private configuration data */ "RAW DCF77 Timecode", /* direct decoding / time synthesis */ BUFFER_MAX, /* bit buffer */ sizeof(last_tcode_t) }; static struct dcfparam { const unsigned char *onebits; const unsigned char *zerobits; } dcfparameter = { (const unsigned char *)"###############RADMLS1248124P124812P1248121241248112481248P??", /* 'ONE' representation */ (const unsigned char *)"--------------------s-------p------p----------------------p__" /* 'ZERO' representation */ }; static struct rawdcfcode { char offset; /* start bit */ } rawdcfcode[] = { { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 }, { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 } }; /* #define DCF_M 0 UNUSED */ #define DCF_R 1 #define DCF_A1 2 #define DCF_Z 3 #define DCF_A2 4 #define DCF_S 5 #define DCF_M1 6 #define DCF_M10 7 /* #define DCF_P1 8 UNUSED */ #define DCF_H1 9 #define DCF_H10 10 /* #define DCF_P2 11 UNUSED */ #define DCF_D1 12 #define DCF_D10 13 /* #define DCF_DW 14 UNUSED */ #define DCF_MO 15 #define DCF_MO0 16 #define DCF_Y1 17 #define DCF_Y10 18 /* #define DCF_P3 19 UNUSED */ static struct partab { char offset; /* start bit of parity field */ } partab[] = { { 21 }, { 29 }, { 36 }, { 59 } }; #define DCF_P_P1 0 #define DCF_P_P2 1 #define DCF_P_P3 2 #define DCF_Z_MET 0x2 #define DCF_Z_MED 0x1 static long ext_bf(unsigned char *, int, const unsigned char *) __attribute__((pure)); static long ext_bf( unsigned char *buf, int idx, const unsigned char *zero ) { long sum = 0; int i, first; first = rawdcfcode[idx].offset; for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--) { sum <<= 1; sum |= (buf[i] != zero[i]); } return sum; } static unsigned pcheck( unsigned char *buf, int idx, const unsigned char *zero ) { int i,last; unsigned psum = 1; last = partab[idx+1].offset; for (i = partab[idx].offset; i < last; i++) psum ^= (buf[i] != zero[i]); return psum; } static unsigned long convert_rawdcf( unsigned char *buffer, int size, struct dcfparam *dcfprm, clocktime_t *clock_time ) { unsigned char *s = buffer; const unsigned char *b = dcfprm->onebits; const unsigned char *c = dcfprm->zerobits; int i; parseprintf(DD_RAWDCF,("parse: convert_rawdcf: \"%.*s\"\n", size, buffer)); if (size < 57) { msyslog(LOG_ERR, "ERR: parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits", size); return CVT_FAIL|CVT_BADFMT; } for (i = 0; i < size; i++) { if ((*s != *b) && (*s != *c)) { /* * we only have two types of bytes (ones and zeros) */ msyslog(LOG_ERR, "ERR: parse: convert_rawdcf: BAD DATA - no conversion"); return CVT_NONE; } if (*b) b++; if (*c) c++; s++; } /* * check Start and Parity bits */ if ((ext_bf(buffer, DCF_S, dcfprm->zerobits) == 1) && pcheck(buffer, DCF_P_P1, dcfprm->zerobits) && pcheck(buffer, DCF_P_P2, dcfprm->zerobits) && pcheck(buffer, DCF_P_P3, dcfprm->zerobits)) { /* * buffer OK */ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n")); clock_time->flags = PARSEB_S_CALLBIT|PARSEB_S_LEAP; clock_time->utctime= 0; clock_time->usecond= 0; clock_time->second = 0; clock_time->minute = ext_bf(buffer, DCF_M10, dcfprm->zerobits); clock_time->minute = 10*(clock_time->minute) + ext_bf(buffer, DCF_M1, dcfprm->zerobits); clock_time->hour = ext_bf(buffer, DCF_H10, dcfprm->zerobits); clock_time->hour = 10*(clock_time->hour) + ext_bf(buffer, DCF_H1, dcfprm->zerobits); clock_time->day = ext_bf(buffer, DCF_D10, dcfprm->zerobits); clock_time->day = 10*(clock_time->day) + ext_bf(buffer, DCF_D1, dcfprm->zerobits); clock_time->month = ext_bf(buffer, DCF_MO0, dcfprm->zerobits); clock_time->month = 10*(clock_time->month) + ext_bf(buffer, DCF_MO, dcfprm->zerobits); clock_time->year = ext_bf(buffer, DCF_Y10, dcfprm->zerobits); clock_time->year = 10*(clock_time->year) + ext_bf(buffer, DCF_Y1, dcfprm->zerobits); switch (ext_bf(buffer, DCF_Z, dcfprm->zerobits)) { case DCF_Z_MET: clock_time->utcoffset = -1*60*60; break; case DCF_Z_MED: clock_time->flags |= PARSEB_DST; clock_time->utcoffset = -2*60*60; break; default: parseprintf(DD_RAWDCF,("parse: convert_rawdcf: BAD TIME ZONE\n")); return CVT_FAIL|CVT_BADFMT; } if (ext_bf(buffer, DCF_A1, dcfprm->zerobits)) clock_time->flags |= PARSEB_ANNOUNCE; if (ext_bf(buffer, DCF_A2, dcfprm->zerobits)) clock_time->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */ if (ext_bf(buffer, DCF_R, dcfprm->zerobits)) clock_time->flags |= PARSEB_CALLBIT; parseprintf(DD_RAWDCF,("parse: convert_rawdcf: TIME CODE OK: %02d:%02d, %02d.%02d.%02d, flags 0x%lx\n", (int)clock_time->hour, (int)clock_time->minute, (int)clock_time->day, (int)clock_time->month,(int) clock_time->year, (unsigned long)clock_time->flags)); return CVT_OK; } else { /* * bad format - not for us */ msyslog(LOG_ERR, "ERR: parse: convert_rawdcf: parity check FAILED for \"%.*s\"", size, buffer); return CVT_FAIL|CVT_BADFMT; } } /* * parse_cvt_fnc_t cvt_rawdcf * raw dcf input routine - needs to fix up 50 baud * characters for 1/0 decision */ static unsigned long cvt_rawdcf( unsigned char *buffer, int size, struct format *param, clocktime_t *clock_time, void *local ) { last_tcode_t *t = (last_tcode_t *)local; unsigned char *s = (unsigned char *)buffer; unsigned char *e = s + size; const unsigned char *b = dcfparameter.onebits; const unsigned char *c = dcfparameter.zerobits; unsigned long rtc = CVT_NONE; unsigned int i, lowmax, highmax, cutoff, span; #define BITS 9 unsigned char histbuf[BITS]; /* * the input buffer contains characters with runs of consecutive * bits set. These set bits are an indication of the DCF77 pulse * length. We assume that we receive the pulse at 50 Baud. Thus * a 100ms pulse would generate a 4 bit train (20ms per bit and * start bit) * a 200ms pulse would create all zeroes (and probably a frame error) */ UNUSED_ARG(param); for (i = 0; i < BITS; i++) { histbuf[i] = 0; } cutoff = 0; lowmax = 0; while (s < e) { unsigned int ch = *s ^ 0xFF; /* * these lines are left as an exercise to the reader 8-) */ if (!((ch+1) & ch) || !*s) { for (i = 0; ch; i++) { ch >>= 1; } *s = (unsigned char) i; histbuf[i]++; cutoff += i; lowmax++; } else { parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, (int)(s - (unsigned char *)buffer))); *s = (unsigned char)~0; rtc = CVT_FAIL|CVT_BADFMT; } s++; } if (lowmax) { cutoff /= lowmax; } else { cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */ } parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: average bit count: %u\n", cutoff)); lowmax = 0; highmax = 0; parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: histogram:")); for (i = 0; i <= cutoff; i++) { lowmax+=histbuf[i] * i; highmax += histbuf[i]; parseprintf(DD_RAWDCF,(" %d", histbuf[i])); } parseprintf(DD_RAWDCF, (" ")); lowmax += highmax / 2; if (highmax) { lowmax /= highmax; } else { lowmax = 0; } highmax = 0; cutoff = 0; for (; i < BITS; i++) { highmax+=histbuf[i] * i; cutoff +=histbuf[i]; parseprintf(DD_RAWDCF,(" %d", histbuf[i])); } parseprintf(DD_RAWDCF,("\n")); if (cutoff) { highmax /= cutoff; } else { highmax = BITS-1; } span = cutoff = lowmax; for (i = lowmax; i <= highmax; i++) { if (histbuf[cutoff] > histbuf[i]) { cutoff = i; span = i; } else if (histbuf[cutoff] == histbuf[i]) { span = i; } } cutoff = (cutoff + span) / 2; parseprintf(DD_RAWDCF, ("parse: cvt_rawdcf: " "lower maximum %u, higher maximum %u, cutoff %u\n", lowmax, highmax, cutoff)); s = (unsigned char *)buffer; while (s < e) { if (*s == (unsigned char)~0) { *s = '?'; } else { *s = (*s >= cutoff) ? *b : *c; } s++; if (*b) b++; if (*c) c++; } *s = '\0'; if (rtc == CVT_NONE) { rtc = convert_rawdcf(buffer, size, &dcfparameter, clock_time); if (rtc == CVT_OK) { time_t newtime; newtime = parse_to_unixtime(clock_time, &rtc); if ((rtc == CVT_OK) && t) { if ((newtime - t->tcode) <= 600) /* require a successful telegram within last 10 minutes */ { parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: recent timestamp check OK\n")); clock_time->utctime = newtime; } else { parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: recent timestamp check FAIL - ignore timestamp\n")); rtc = CVT_SKIP; } t->tcode = newtime; } } } return rtc; } /* * parse_pps_fnc_t pps_rawdcf * * currently a very stupid version - should be extended to decode * also ones and zeros (which is easy) */ /*ARGSUSED*/ static unsigned long pps_rawdcf( parse_t *parseio, int status, timestamp_t *ptime ) { if (!status) /* negative edge for simpler wiring (Rx->DCD) */ { parseio->parse_dtime.parse_ptime = *ptime; parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; } return CVT_NONE; } static long calc_usecdiff( timestamp_t *ref, timestamp_t *base, long offset ) { struct timespec delta; long delta_usec = 0; l_fp delt; delt = *ref; bumplfpsint(delt, -offset); delt -= *base; delta = lfp_uintv_to_tspec(delt); delta_usec = US_PER_S * (int32_t)delta.tv_sec + delta.tv_nsec / 1000; return delta_usec; } static unsigned long snt_rawdcf( parse_t *parseio, timestamp_t *ptime ) { /* * only synthesize if all of following conditions are met: * - CVT_OK parse_status (we have a time stamp base) * - ABS(ptime - tminute - (parse_index - 1) sec) < 500ms (spaced by 1 sec +- 500ms) * - minute marker is available (confirms minute raster as base) */ last_tcode_t *t = (last_tcode_t *)parseio->parse_pdata; long delta_usec = -1; /* * 2017-01-07: Emergency repair. This guard used to test * t->tminute.tv.tv_sec, but that cannot have been right as * that tv_sec field was never set anywhere outside the * in-kernel Sun module (long discarded) while this code was * reached in userspace. It is unknown whether replacing that * test with the analogous one on an l_fp is correct. This * problem was discovered when reducing the timestamp_t union * - this was one of two references to the UNIX timestamp * member. */ if (t != NULL && lfpuint(t->tminute) != 0) { delta_usec = calc_usecdiff(ptime, &t->tminute, parseio->parse_index - 1); if (delta_usec < 0) delta_usec = -delta_usec; } parseprintf(DD_RAWDCF,("parse: snt_rawdcf: synth for offset %d seconds - absolute usec error %ld\n", parseio->parse_index - 1, delta_usec)); if (((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK) && (delta_usec < (NS_PER_S/2000) && delta_usec >= 0)) { /* only if minute marker is available */ parseio->parse_dtime.parse_stime = *ptime; bumplfpuint(parseio->parse_dtime.parse_time, 1); parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio->parse_index - 1)); return updatetimeinfo(parseio, parseio->parse_lstate); } return CVT_NONE; } /* * parse_inp_fnc_t inp_rawdcf * * grab DCF77 data from input stream */ static unsigned long inp_rawdcf( parse_t *parseio, char ch, timestamp_t *tstamp ) { /* 1.5 seconds denote second #60 */ static struct timespec timeout = { 1, (NS_PER_S/2) }; parseprintf(DD_PARSE, ("inp_rawdcf(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ if (parse_timedout(parseio, tstamp, &timeout)) { last_tcode_t *t = (last_tcode_t *)parseio->parse_pdata; long delta_usec; parseprintf(DD_RAWDCF, ("inp_rawdcf: time out seen\n")); /* finish collection */ (void) parse_end(parseio); if (t != NULL) { /* remember minute start sample time if timeouts occur in minute raster */ /* * 2017-01-07: Emergency repair. This used to test * t->timeout.tv.tv_sec != 0, but that cannot have been * right as the tv.tv_sec part of (what used to be) the * timestamp_t union was only set in the long-discarded * Sun kernel driver, and this could always be called * from userspace. Problem discovered while eliminating * the timestamp_t union; this was one of only two * referebces to the timrspec member. It is unknown * whether this change actually corrects the code. */ if (lfpuint(t->timeout) != 0) { delta_usec = calc_usecdiff(tstamp, &t->timeout, 60); if (delta_usec < 0) delta_usec = -delta_usec; } else { delta_usec = -1; } if (delta_usec < (NS_PER_S/2000) && delta_usec >= 0) { parseprintf(DD_RAWDCF, ("inp_rawdcf: timeout time difference %ld usec - minute marker set\n", delta_usec)); /* collect minute markers only if spaced by 60 seconds */ t->tminute = *tstamp; } else { parseprintf(DD_RAWDCF, ("inp_rawdcf: timeout time difference %ld usec - minute marker cleared\n", delta_usec)); memset((char *)&t->tminute, 0, sizeof(t->tminute)); } t->timeout = *tstamp; } (void) parse_addchar(parseio, ch); /* pass up to higher layers */ return PARSE_INP_TIME; } else { unsigned int rtc; rtc = parse_addchar(parseio, ch); if (rtc == PARSE_INP_SKIP) { if (snt_rawdcf(parseio, tstamp) == CVT_OK) return PARSE_INP_SYNTH; } return rtc; } } /* * History: * * clk_rawdcf.c,v * Revision 4.18 2006/06/22 18:40:01 kardel * clean up signedness (gcc 4) * * Revision 4.17 2006/01/22 16:01:55 kardel * update version information * * Revision 4.16 2006/01/22 15:51:22 kardel * generate reasonable timecode output on invalid input * * Revision 4.15 2005/08/06 19:17:06 kardel * clean log output * * Revision 4.14 2005/08/06 17:39:40 kardel * cleanup size handling wrt/ to buffer boundaries * * Revision 4.13 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.12 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.9 1999/12/06 13:42:23 kardel * transfer correctly converted time codes always into tcode * * Revision 4.8 1999/11/28 09:13:50 kardel * RECON_4_0_98F * * Revision 4.7 1999/04/01 20:07:20 kardel * added checking for minutie increment of timestamps in clk_rawdcf.c * * Revision 4.6 1998/06/14 21:09:37 kardel * Sun acc cleanup * * Revision 4.5 1998/06/13 12:04:16 kardel * fix SYSV clock name clash * * Revision 4.4 1998/06/12 15:22:28 kardel * fix prototypes * * Revision 4.3 1998/06/06 18:33:36 kardel * simplified condidional compile expression * * Revision 4.2 1998/05/24 11:04:18 kardel * triggering PPS on negative edge for simpler wiring (Rx->DCD) * * Revision 4.1 1998/05/24 09:39:53 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:30 kardel * Start 4.0 release version numbering * * from V3 3.24 log info deleted 1998/04/11 kardel * */ ntpsec-1.1.0+dfsg1/libparse/trim_info.c0000644000175000017500000000144513252364117017564 0ustar rlaagerrlaager/* * Created: Sun Aug 2 20:20:34 1998 * * Copyright (c) 1989-2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_types.h" #include "trimble.h" cmd_info_t * trimble_convert( unsigned int cmd, cmd_info_t *tbl ) { int i; for (i = 0; tbl[i].cmd != 0xFF; i++) { if (tbl[i].cmd == cmd) return &tbl[i]; } return 0; } /* * trim_info.c,v * Revision 4.5 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.4 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.2 1998/12/20 23:45:31 kardel * fix types and warnings * * Revision 4.1 1998/08/09 22:27:48 kardel * Trimble TSIP support * */ ntpsec-1.1.0+dfsg1/libparse/parse_conf.c0000644000175000017500000000335413252364117017716 0ustar rlaagerrlaager/* * Parser configuration module for reference clocks * * Copyright (c) 1989-2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "parse.h" /* * format definitions */ clockformat_t *clockformats[] = { #ifdef CLOCK_MEINBERG &clock_meinberg[0], &clock_meinberg[1], &clock_meinberg[2], #endif #ifdef CLOCK_DCF7000 &clock_dcf7000, #endif #ifdef CLOCK_SCHMID &clock_schmid, #endif #ifdef CLOCK_RAWDCF &clock_rawdcf, #endif #ifdef CLOCK_TRIMTAIP &clock_trimtaip, #endif #ifdef CLOCK_TRIMTSIP &clock_trimtsip, #endif #ifdef CLOCK_RCC8000 &clock_rcc8000, #endif #ifdef CLOCK_HOPF6021 &clock_hopf6021, #endif #ifdef CLOCK_COMPUTIME &clock_computime, #endif #ifdef CLOCK_WHARTON_400A &clock_wharton_400a, #endif #ifdef CLOCK_VARITEXT &clock_varitext, #endif #ifdef CLOCK_SEL240X &clock_sel240x, #endif 0}; unsigned short nformats = sizeof(clockformats) / sizeof(clockformats[0]) - 1; /* * History: * * parse_conf.c,v * Revision 4.9 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.8 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.5 1999/11/28 09:13:53 kardel * RECON_4_0_98F * * Revision 4.4 1999/02/28 15:27:25 kardel * wharton clock integration * * Revision 4.3 1998/08/16 18:52:15 kardel * (clockformats): Trimble TSIP driver now also * available for kernel operation * * Revision 4.2 1998/06/12 09:13:48 kardel * conditional compile macros fixed * * Revision 4.1 1998/05/24 09:40:49 kardel * adjustments of log messages * * * from V3 3.24 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/clk_rcc8000.c0000644000175000017500000001151013252364117017500 0ustar rlaagerrlaager/* * /src/NTP/ntp4-dev/libparse/clk_rcc8000.c,v 4.9 2004/11/14 15:29:41 kardel RELEASE_20050508_A * * clk_rcc8000.c,v 4.9 2004/11/14 15:29:41 kardel RELEASE_20050508_A * * Radiocode Clocks Ltd RCC 8000 Intelligent Off-Air Master Clock support * * Created by R.E.Broughton from clk_trimtaip.c * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "parse.h" #include "ntp_stdlib.h" #include /* Type II Serial Output format * * 0000000000111111111122222222223 / char * 0123456789012345678901234567890 \ posn * HH:MM:SS.XYZ DD/MM/YY DDD W Prn Actual * 33 44 55 666 00 11 22 7 Parse * : : . / / rn Check * "15:50:36.534 30/09/94 273 5 A\x0d\x0a" * * DDD - Day of year number * W - Day of week number (Sunday is 0) * P is the Status. See comment below for details. */ #define O_USEC O_WDAY static struct format rcc8000_fmt = { { { 13, 2 }, {16, 2}, { 19, 2}, /* Day, Month, Year */ { 0, 2 }, { 3, 2}, { 6, 2}, /* Hour, Minute, Second */ { 9, 3 }, {28, 1}, { 0, 0}, /* uSec, Status (Valid,Reject,BST,Leapyear) */ }, (const unsigned char *)" : : . / / \r\n", /*"15:50:36.534 30/09/94 273 5 A\x0d\x0a" */ 0 }; static parse_cvt_fnc_t cvt_rcc8000; static parse_inp_fnc_t inp_rcc8000; clockformat_t clock_rcc8000 = { inp_rcc8000, /* no input handling */ cvt_rcc8000, /* Radiocode clock conversion */ 0, /* no direct PPS monitoring */ (void *)&rcc8000_fmt, /* conversion configuration */ "Radiocode RCC8000", 31, /* string buffer */ 0 /* no private data */ }; /* parse_cvt_fnc_t cvt_rcc8000 */ static unsigned long cvt_rcc8000( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { UNUSED_ARG(size); UNUSED_ARG(local); if (!Strok(buffer, format->fixed_string)) return CVT_NONE; #define OFFS(x) format->field_offsets[(x)].offset #define STOI(x, y) Stoi(&buffer[OFFS(x)], y, format->field_offsets[(x)].length) if ( STOI(O_DAY, &clock_time->day) || STOI(O_MONTH, &clock_time->month) || STOI(O_YEAR, &clock_time->year) || STOI(O_HOUR, &clock_time->hour) || STOI(O_MIN, &clock_time->minute) || STOI(O_SEC, &clock_time->second) || STOI(O_USEC, &clock_time->usecond) ) return CVT_FAIL|CVT_BADFMT; clock_time->usecond *= 1000; clock_time->utcoffset = 0; #define RCCP buffer[28] /* * buffer[28] is the ASCII representation of a hex character ( 0 through F ) * The four bits correspond to: * 8 - Valid Time * 4 - Reject Code * 2 - British Summer Time (receiver set to emit GMT all year.) * 1 - Leap year */ #define RCC8000_VALID 0x8 /* #define RCC8000_REJECT 0x4 UNUSED */ /* #define RCC8000_BST 0x2 UNUSED */ /* #define RCC8000_LEAPY 0x1 UNUSED */ clock_time->flags = 0; if ( (RCCP >= '0' && RCCP <= '9') || (RCCP >= 'A' && RCCP <= 'F') ) { int flag; flag = (RCCP >= '0' && RCCP <= '9' ) ? RCCP - '0' : RCCP - 'A' + 10; if (!(flag & RCC8000_VALID)) clock_time->flags |= PARSEB_POWERUP; clock_time->flags |= PARSEB_UTC; /* British special - guess why 8-) */ /* other flags not used */ } return CVT_OK; } /* * parse_inp_fnc_t inp_rcc8000 * * grab data from input stream */ static unsigned long inp_rcc8000( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; parseprintf(DD_PARSE, ("inp_rcc8000(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch (ch) { case '\n': parseprintf(DD_PARSE, ("inp_rcc8000: EOL seen\n")); if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: if (parseio->parse_index == 0) /* take sample at start of message */ { parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ } return parse_addchar(parseio, ch); } } /* * History: * * clk_rcc8000.c,v * Revision 4.9 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.6 1999/11/28 09:13:51 kardel * RECON_4_0_98F * * Revision 4.5 1998/06/14 21:09:38 kardel * Sun acc cleanup * * Revision 4.4 1998/06/13 12:05:02 kardel * fix SYSV clock name clash * * Revision 4.3 1998/06/12 15:22:29 kardel * fix prototypes * * Revision 4.2 1998/06/12 09:13:25 kardel * conditional compile macros fixed * printf prototype * * Revision 4.1 1998/05/24 09:39:53 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:30 kardel * Start 4.0 release version numbering * * from V3 3.5 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/clk_hopf6021.c0000644000175000017500000001522213252364117017672 0ustar rlaagerrlaager/* * /src/NTP/ntp4-dev/libparse/clk_hopf6021.c,v 4.10 2004/11/14 15:29:41 kardel RELEASE_20050508_A * * clk_hopf6021.c,v 4.10 2004/11/14 15:29:41 kardel RELEASE_20050508_A * * Radiocode Clocks HOPF Funkuhr 6021 mit serieller Schnittstelle * base code version from 24th Nov 1995 - history at end * * Created by F.Schnekenbuehl from clk_rcc8000.c * Nortel DASA Network Systems GmbH, Department: ND250 * A Joint venture of Daimler-Benz Aerospace and Nortel * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "ascii.h" #include "parse.h" #include "ntp_stdlib.h" #include /* * hopf Funkuhr 6021 * used with 9600,8N1, * UTC ueber serielle Schnittstelle * Sekundenvorlauf ON * ETX zum Sekundenvorlauf ON * Datenstring 6021 * Ausgabe Uhrzeit und Datum * Senden mit Steuerzeichen * Senden sekuendlich */ /* * Type 6021 Serial Output format * * 000000000011111111 / char * 012345678901234567 \ position * sABHHMMSSDDMMYYnre Actual * C4110046231195 Parse * s enr Check * * s = STX (0x02), e = ETX (0x03) * n = NL (0x0A), r = CR (0x0D) * * A B - Status and weekday * * A - Status * * 8 4 2 1 * x x x 0 - no announcement * x x x 1 - Summertime - wintertime - summertime announcement * x x 0 x - Wintertime * x x 1 x - Summertime * 0 0 x x - Time/Date invalid * 0 1 x x - Internal clock used * 1 0 x x - Radio clock * 1 1 x x - Radio clock highprecision * * B - 8 4 2 1 * 0 x x x - MESZ/MEZ * 1 x x x - UTC * x 0 0 1 - Monday * x 0 1 0 - Tuesday * x 0 1 1 - Wednesday * x 1 0 0 - Thursday * x 1 0 1 - Friday * x 1 1 0 - Saturday * x 1 1 1 - Sunday */ #define HOPF_DSTWARN 0x01 /* DST switch warning */ #define HOPF_DST 0x02 /* DST in effect */ #define HOPF_MODE 0x0C /* operation mode mask */ #define HOPF_INVALID 0x00 /* no time code available */ #define HOPF_INTERNAL 0x04 /* internal clock */ #define HOPF_RADIO 0x08 /* radio clock */ #define HOPF_RADIOHP 0x0C /* high precision radio clock */ #define HOPF_UTC 0x08 /* time code in UTC */ /* #define HOPF_WMASK 0x07 * mask for weekday code UNUSED */ static struct format hopf6021_fmt = { { { 9, 2 }, {11, 2}, { 13, 2}, /* Day, Month, Year */ { 3, 2 }, { 5, 2}, { 7, 2}, /* Hour, Minute, Second */ { 2, 1 }, { 1, 1}, { 0, 0}, /* Weekday, Flags, Zone */ /* ... */ }, (const unsigned char *)"\002 \n\r\003", 0 }; #define OFFS(x) format->field_offsets[(x)].offset #define STOI(x, y) Stoi(&buffer[OFFS(x)], y, format->field_offsets[(x)].length) #define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \ ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \ ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \ -1) static parse_cvt_fnc_t cvt_hopf6021; static parse_inp_fnc_t inp_hopf6021; clockformat_t clock_hopf6021 = { inp_hopf6021, /* HOPF 6021 input handling */ cvt_hopf6021, /* Radiocode clock conversion */ 0, /* no direct PPS monitoring */ (void *)&hopf6021_fmt, /* conversion configuration */ "hopf Funkuhr 6021", /* clock format name */ 19, /* string buffer */ 0 /* private data length, no private data */ }; /* parse_cvt_fnc_t cvt_hopf6021 */ static unsigned long cvt_hopf6021( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { unsigned char status,weekday; UNUSED_ARG(size); UNUSED_ARG(local); if (!Strok(buffer, format->fixed_string)) { return CVT_NONE; } if ( STOI(O_DAY, &clock_time->day) || STOI(O_MONTH, &clock_time->month) || STOI(O_YEAR, &clock_time->year) || STOI(O_HOUR, &clock_time->hour) || STOI(O_MIN, &clock_time->minute) || STOI(O_SEC, &clock_time->second) ) { return CVT_FAIL|CVT_BADFMT; } clock_time->usecond = 0; clock_time->utcoffset = 0; status = (uint8_t) hexval(buffer[OFFS(O_FLAGS)]); weekday= (uint8_t) hexval(buffer[OFFS(O_WDAY)]); if ((status == 0xFF) || (weekday == 0xFF)) { return CVT_FAIL|CVT_BADFMT; } clock_time->flags = 0; if (weekday & HOPF_UTC) { clock_time->flags |= PARSEB_UTC; } else { if (status & HOPF_DST) { clock_time->flags |= PARSEB_DST; clock_time->utcoffset = -2*60*60; /* MET DST */ } else { clock_time->utcoffset = -1*60*60; /* MET */ } } clock_time->flags |= (status & HOPF_DSTWARN) ? PARSEB_ANNOUNCE : 0; switch (status & HOPF_MODE) { case HOPF_INVALID: /* Time/Date invalid */ clock_time->flags |= PARSEB_POWERUP; break; case HOPF_INTERNAL: /* internal clock */ clock_time->flags |= PARSEB_NOSYNC; break; case HOPF_RADIO: /* Radio clock */ case HOPF_RADIOHP: /* Radio clock high precision */ break; default: return CVT_FAIL|CVT_BADFMT; } return CVT_OK; } /* * parse_inp_fnc_t inp_hopf6021 * * grab data from input stream */ static unsigned long inp_hopf6021( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; parseprintf(DD_PARSE, ("inp_hopf6021(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch (ch) { case ETX: parseprintf(DD_PARSE, ("inp_hopf6021: EOL seen\n")); parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: return parse_addchar(parseio, ch); } } /* * History: * * clk_hopf6021.c,v * Revision 4.10 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.7 1999/11/28 09:13:49 kardel * RECON_4_0_98F * * Revision 4.6 1998/11/15 20:27:57 kardel * Release 4.0.73e13 reconciliation * * Revision 4.5 1998/06/14 21:09:35 kardel * Sun acc cleanup * * Revision 4.4 1998/06/13 12:02:38 kardel * fix SYSV clock name clash * * Revision 4.3 1998/06/12 15:22:27 kardel * fix prototypes * * Revision 4.2 1998/06/12 09:13:25 kardel * conditional compile macros fixed * printf prototype * * Revision 4.1 1998/05/24 09:39:52 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:29 kardel * Start 4.0 release version numbering * * from V3 3.6 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/clk_schmid.c0000644000175000017500000001246213252364117017677 0ustar rlaagerrlaager/* * /src/NTP/ntp4-dev/libparse/clk_schmid.c,v 4.9 2005/04/16 17:32:10 kardel RELEASE_20050508_A * * clk_schmid.c,v 4.9 2005/04/16 17:32:10 kardel RELEASE_20050508_A * * Schmid clock support * based on information and testing from Adam W. Feigin et. al (Swisstime iis.ethz.ch) * * Copyright (c) 1989-2015 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "parse.h" #include "ntp_stdlib.h" #include /* * Description courtesy of Adam W. Feigin et. al (Swisstime iis.ethz.ch) * * The command to Schmid's DCF77 clock is a single byte; each bit * allows the user to select some part of the time string, as follows (the * output for the lsb is sent first). * * Bit 0: time in MEZ, 4 bytes *binary, not BCD*; hh.mm.ss.tenths * Bit 1: date 3 bytes *binary, not BCD: dd.mm.yy * Bit 2: week day, 1 byte (unused here) * Bit 3: time zone, 1 byte, 0=MET, 1=MEST. (unused here) * Bit 4: clock status, 1 byte, 0=time invalid, * 1=time from crystal backup, * 3=time from DCF77 * Bit 5: transmitter status, 1 byte, * bit 0: backup antenna * bit 1: time zone change within 1h * bit 3,2: TZ 01=MEST, 10=MET * bit 4: leap second will be * added within one hour * bits 5-7: Zero * Bit 6: time in backup mode, units of 5 minutes (unused here) * */ #define WS_TIME 0x01 #define WS_SIGNAL 0x02 #define WS_CALLBIT 0x01 /* "call bit" used to signalize irregularities in the control facilities */ #define WS_ANNOUNCE 0x02 #define WS_TZ 0x0c #define WS_MET 0x08 #define WS_MEST 0x04 #define WS_LEAP 0x10 static parse_cvt_fnc_t cvt_schmid; static parse_inp_fnc_t inp_schmid; clockformat_t clock_schmid = { inp_schmid, /* no input handling */ cvt_schmid, /* Schmid conversion */ 0, /* not direct PPS monitoring */ 0, /* conversion configuration */ "Schmid", /* Schmid receiver */ 12, /* binary data buffer */ 0, /* no private data (complete messages) */ }; /* parse_cvt_fnc_t */ static unsigned long cvt_schmid( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { UNUSED_ARG(format); UNUSED_ARG(local); if ((size != 11) || (buffer[10] != (unsigned char)'\375')) { return CVT_NONE; } else { if (buffer[0] > 23 || buffer[1] > 59 || buffer[2] > 59 || buffer[3] > 9) /* Time */ { return CVT_FAIL|CVT_BADTIME; } else if (buffer[4] < 1 || buffer[4] > 31 || buffer[5] < 1 || buffer[5] > 12 || buffer[6] > 99) { return CVT_FAIL|CVT_BADDATE; } else { clock_time->hour = buffer[0]; clock_time->minute = buffer[1]; clock_time->second = buffer[2]; clock_time->usecond = buffer[3] * 100000; clock_time->day = buffer[4]; clock_time->month = buffer[5]; clock_time->year = buffer[6]; clock_time->flags = 0; switch (buffer[8] & WS_TZ) { case WS_MET: clock_time->utcoffset = -1*60*60; break; case WS_MEST: clock_time->utcoffset = -2*60*60; clock_time->flags |= PARSEB_DST; break; default: return CVT_FAIL|CVT_BADFMT; } if (!(buffer[7] & WS_TIME)) { clock_time->flags |= PARSEB_POWERUP; } if (!(buffer[7] & WS_SIGNAL)) { clock_time->flags |= PARSEB_NOSYNC; } if (buffer[7] & WS_SIGNAL) { if (buffer[8] & WS_CALLBIT) { clock_time->flags |= PARSEB_CALLBIT; } if (buffer[8] & WS_ANNOUNCE) { clock_time->flags |= PARSEB_ANNOUNCE; } if (buffer[8] & WS_LEAP) { clock_time->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */ } } clock_time->flags |= PARSEB_S_LEAP|PARSEB_S_CALLBIT; return CVT_OK; } } } /* * parse_inp_fnc_t inp_schmid * * grab data from input stream */ static unsigned long inp_schmid( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; UNUSED_ARG(tstamp); parseprintf(DD_PARSE, ("inp_schmid(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch ((uint8_t)ch) { case 0xFD: /* */ parseprintf(DD_PARSE, ("inp_schmid: 0xFD seen\n")); if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: return parse_addchar(parseio, ch); } } /* * History: * * clk_schmid.c,v * Revision 4.9 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.8 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.5 1999/11/28 09:13:51 kardel * RECON_4_0_98F * * Revision 4.4 1998/06/13 12:06:03 kardel * fix SYSV clock name clash * * Revision 4.3 1998/06/12 15:22:29 kardel * fix prototypes * * Revision 4.2 1998/06/12 09:13:26 kardel * conditional compile macros fixed * printf prototype * * Revision 4.1 1998/05/24 09:39:53 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:31 kardel * Start 4.0 release version numbering * * from V3 3.22 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/clk_dcf7000.c0000644000175000017500000001000413252364117017461 0ustar rlaagerrlaager/* * ELV DCF7000 module * * Copyright (c) 1989-2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "parse.h" #include "ntp_stdlib.h" #include static struct format dcf7000_fmt = { /* ELV DCF7000 */ { { 6, 2}, { 3, 2}, { 0, 2}, { 12, 2}, { 15, 2}, { 18, 2}, { 9, 2}, { 21, 2}, }, (const unsigned char *)" - - - - - - - \r", 0 }; static parse_cvt_fnc_t cvt_dcf7000; static parse_inp_fnc_t inp_dcf7000; clockformat_t clock_dcf7000 = { inp_dcf7000, /* DCF7000 input handling */ cvt_dcf7000, /* ELV DCF77 conversion */ 0, /* no direct PPS monitoring */ (void *)&dcf7000_fmt, /* conversion configuration */ "ELV DCF7000", /* ELV clock */ 24, /* string buffer */ 0 /* no private data (complete packets) */ }; /* * parse_cvt_fnc_t cvt_dcf7000 * * convert dcf7000 type format */ static unsigned long cvt_dcf7000( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { UNUSED_ARG(size); UNUSED_ARG(local); if (!Strok(buffer, format->fixed_string)) { return CVT_NONE; } else { if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day, format->field_offsets[O_DAY].length) || Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month, format->field_offsets[O_MONTH].length) || Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year, format->field_offsets[O_YEAR].length) || Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour, format->field_offsets[O_HOUR].length) || Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute, format->field_offsets[O_MIN].length) || Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second, format->field_offsets[O_SEC].length)) { return CVT_FAIL|CVT_BADFMT; } else { unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset]; long flags; clock_time->flags = 0; clock_time->usecond = 0; if (Stoi(f, &flags, format->field_offsets[O_FLAGS].length)) { return CVT_FAIL|CVT_BADFMT; } else { if (flags & 0x1) clock_time->utcoffset = -2*60*60; else clock_time->utcoffset = -1*60*60; if (flags & 0x2) clock_time->flags |= PARSEB_ANNOUNCE; if (flags & 0x4) clock_time->flags |= PARSEB_NOSYNC; } return CVT_OK; } } } /* * parse_inp_fnc_t inp_dcf700 * * grab data from input stream */ static unsigned long inp_dcf7000( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; parseprintf(DD_PARSE, ("inp_dcf7000(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch (ch) { case '\r': parseprintf(DD_PARSE, ("inp_dcf7000: EOL seen\n")); parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: return parse_addchar(parseio, ch); } } /* * History: * * clk_dcf7000.c,v * Revision 4.10 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.9 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.6 1999/11/28 09:13:49 kardel * RECON_4_0_98F * * Revision 4.5 1998/06/14 21:09:34 kardel * Sun acc cleanup * * Revision 4.4 1998/06/13 12:01:59 kardel * fix SYSV clock name clash * * Revision 4.3 1998/06/12 15:22:27 kardel * fix prototypes * * Revision 4.2 1998/06/12 09:13:24 kardel * conditional compile macros fixed * printf prototype * * Revision 4.1 1998/05/24 09:39:51 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:28 kardel * Start 4.0 release version numbering * * from V3 3.18 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/gpstolfp.c0000644000175000017500000000370613252364117017436 0ustar rlaagerrlaager/* * Created: Sun Jun 28 16:30:38 1998 * * Copyright (c) 1998-2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "gpstolfp.h" #define GPSORIGIN 2524953600u /* GPS origin - NTP origin in seconds */ void gpstolfp( int weeks, int days, unsigned long seconds, l_fp * lfp ) { if (weeks < GPSWRAP) { weeks += GPSWEEKS; } /* convert to NTP time, note no fractional seconds */ *lfp = lfptouint((uint64_t)weeks * SECSPERWEEK + (uint64_t)days * SECSPERDAY + (uint64_t)seconds + GPSORIGIN); setlfpfrac(*lfp, 0); } void gpsweekadj( unsigned int * week, unsigned int build_week ) { /* adjust for rollover */ while (*week < build_week) *week += GPSWEEKS; } void gpstocal( unsigned int week, unsigned int TOW, int UTC_offset, struct calendar * out ) { time64_t t; t = (time64_t)((int64_t)GPSORIGIN - UTC_offset); t += (time64_t)week * SECSPERWEEK; t += TOW; ntpcal_ntp64_to_date(out, t); } void caltogps( const struct calendar * in, int UTC_offset, unsigned int * week, unsigned int * TOW ) { time64_t t; t = ntpcal_dayjoin(ntpcal_date_to_rd(in) - DAY_NTP_STARTS, ntpcal_date_to_daysec(in)); t -= (uint64_t)((int64_t)GPSORIGIN - UTC_offset); *week = t / SECSPERWEEK; if (NULL != TOW) *TOW = t % SECSPERWEEK; } /* * History: * * gpstolfp.c,v * Revision 4.8 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.7 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.3 1999/02/28 11:42:44 kardel * (GPSWRAP): update GPS rollover to 990 weeks * * Revision 4.2 1998/07/11 10:05:25 kardel * Release 4.0.73d reconciliation * * Revision 4.1 1998/06/28 16:47:15 kardel * added gpstolfp() function */ ntpsec-1.1.0+dfsg1/libparse/clk_meinberg.c0000644000175000017500000005164513252364117020226 0ustar rlaagerrlaager/* * Meinberg clock support * * Copyright (c) 1989-2015 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "ntp_machine.h" #include "parse.h" #include #include "ntp_stdlib.h" #include "mbg_gps166.h" #include "binio.h" #include "ascii.h" #define MBG_EXTENDED 0x00000001 /* * The Meinberg receiver every second sends a datagram of the following form * (Standard Format) * * D:
..;T:;U:::; * pos: 0 00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2 2 3 3 3 * 1 23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8 9 0 1 2 * = '\002' ASCII start of text * = '\003' ASCII end of text *
,, = day, month, year(2 digits!!) * = day of week (sunday= 0) * ,, = hour, minute, second * = '#' if never synced since powerup for DCF C51 * = '#' if not PZF sychronisation available for PZF 535/509 * = ' ' if ok * = '*' if time comes from internal quartz * = ' ' if completely synched * = 'S' if daylight saving time is active * = 'U' if time is represented in UTC * = ' ' if no special condition exists * = '!' during the hour preceding a daylight saving time * start/end change * = 'A' leap second insert warning * = ' ' if no special condition exists * * Extended data format (PZFUERL for PZF type clocks) * *
..; ; ::; * pos: 0 00 0 00 0 00 11 1 11 11 1 11 2 22 22 2 2 2 2 2 3 3 3 * 1 23 4 56 7 89 01 2 34 56 7 89 0 12 34 5 6 7 8 9 0 1 2 * = '\002' ASCII start of text * = '\003' ASCII end of text *
,, = day, month, year(2 digits!!) * = day of week (sunday= 0) * ,, = hour, minute, second * = 'U' UTC time display * = '#' if never synced since powerup else ' ' for DCF C51 * '#' if not PZF sychronisation available else ' ' for PZF 535/509 * = '*' if time comes from internal quartz else ' ' * = 'S' if daylight saving time is active else ' ' * = '!' during the hour preceding a daylight saving time * start/end change * = 'A' LEAP second announcement * = 'R' "call bit" used to signalize irregularities in the control facilities, * usually ' ', until 2003 indicated transmission via alternate antenna * * Meinberg GPS receivers * * For very old devices you must get the Uni-Erlangen firmware for the GPS receiver support * to work to full satisfaction ! * With newer GPS receiver types the Uni Erlangen string format can be configured at the device. * *
..; ; ::; <+/-><00:00>; ; * * 000000000111111111122222222223333333333444444444455555555556666666 * 123456789012345678901234567890123456789012345678901234567890123456 * \x0209.07.93; 5; 08:48:26; +00:00; #*S!A L; 49.5736N 11.0280E 373m\x03 * * * = '\002' ASCII start of text * = '\003' ASCII end of text *
,, = day, month, year(2 digits!!) * = day of week (sunday= 0) * ,, = hour, minute, second * <+/->,<00:00> = offset to UTC * = '#' if never synced since powerup else ' ' * = '*' if position is not confirmed else ' ' * = 'S' if daylight saving time is active else ' ' * = '!' during the hour preceding a daylight saving time * start/end change * = 'A' LEAP second announcement * = 'R' "call bit" used to signalize irregularities in the control facilities, * usually ' ', until 2003 indicated transmission via alternate antenna * (reminiscent of PZF receivers) * = 'L' on 23:59:60 * * Binary messages have a lead in for a fixed header of SOH */ /*--------------------------------------------------------------*/ /* Name: csum() */ /* */ /* Purpose: Compute a checksum about a number of bytes */ /* */ /* Input: uchar *p address of the first byte */ /* short n the number of bytes */ /* */ /* Output: -- */ /* */ /* Ret val: the checksum */ /*+-------------------------------------------------------------*/ CSUM mbg_csum( unsigned char *p, unsigned int n ) { unsigned int sum = 0; unsigned int i; for ( i = 0; i < n; i++ ) sum += *p++; return (CSUM) sum; } /* csum */ void get_mbg_header( unsigned char **bufpp, GPS_MSG_HDR *headerp ) { headerp->cmd = (GPS_CMD) get_lsb_uint16(bufpp); headerp->len = get_lsb_uint16(bufpp); headerp->data_csum = (CSUM) get_lsb_uint16(bufpp); headerp->hdr_csum = (CSUM) get_lsb_uint16(bufpp); } static struct format meinberg_fmt[] = { { { { 3, 2}, { 6, 2}, { 9, 2}, { 18, 2}, { 21, 2}, { 24, 2}, { 14, 1}, { 27, 4}, { 29, 1}, }, (const unsigned char *)"\2D: . . ;T: ;U: . . ; \3", 0 }, { /* special extended FAU Erlangen extended format */ { { 1, 2}, { 4, 2}, { 7, 2}, { 14, 2}, { 17, 2}, { 20, 2}, { 11, 1}, { 25, 4}, { 27, 1}, }, (const unsigned char *)"\2 . . ; ; : : ; \3", MBG_EXTENDED }, { /* special extended FAU Erlangen GPS format */ { { 1, 2}, { 4, 2}, { 7, 2}, { 14, 2}, { 17, 2}, { 20, 2}, { 11, 1}, { 32, 7}, { 35, 1}, { 25, 2}, { 28, 2}, { 24, 1} }, (const unsigned char *)"\2 . . ; ; : : ; : ; ; . . ", 0 } }; static parse_cvt_fnc_t cvt_meinberg; static parse_cvt_fnc_t cvt_mgps; static parse_inp_fnc_t mbg_input; static parse_inp_fnc_t gps_input; struct msg_buf { unsigned short len; /* len to fill */ unsigned short phase; /* current input phase */ }; #define MBG_NONE 0 /* no data input */ #define MBG_HEADER 1 /* receiving header */ #define MBG_DATA 2 /* receiving data */ #define MBG_STRING 3 /* receiving standard data message */ clockformat_t clock_meinberg[] = { { mbg_input, /* normal input handling */ cvt_meinberg, /* Meinberg conversion */ pps_one, /* easy PPS monitoring */ 0, /* conversion configuration */ "Meinberg Standard", /* Meinberg simple format - beware */ 32, /* string buffer */ 0 /* no private data (complete packets) */ }, { mbg_input, /* normal input handling */ cvt_meinberg, /* Meinberg conversion */ pps_one, /* easy PPS monitoring */ 0, /* conversion configuration */ "Meinberg Extended", /* Meinberg enhanced format */ 32, /* string buffer */ 0 /* no private data (complete packets) */ }, { gps_input, /* no input handling */ cvt_mgps, /* Meinberg GPS receiver conversion */ pps_one, /* easy PPS monitoring */ (void *)&meinberg_fmt[2], /* conversion configuration */ "Meinberg GPS Extended", /* Meinberg FAU GPS format */ 512, /* string buffer */ sizeof(struct msg_buf) /* no private data (complete packets) */ } }; /* * parse_cvt_fnc_t cvt_meinberg * * convert simple type format */ static unsigned long cvt_meinberg( unsigned char *buffer, int size, struct format *unused, clocktime_t *clock_time, void *local ) { struct format *format; UNUSED_ARG(size); UNUSED_ARG(unused); UNUSED_ARG(local); /* * select automagically correct data format */ if (Strok(buffer, meinberg_fmt[0].fixed_string)) { format = &meinberg_fmt[0]; } else { if (Strok(buffer, meinberg_fmt[1].fixed_string)) { format = &meinberg_fmt[1]; } else { return CVT_FAIL|CVT_BADFMT; } } /* * collect data */ if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day, format->field_offsets[O_DAY].length) || Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month, format->field_offsets[O_MONTH].length) || Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year, format->field_offsets[O_YEAR].length) || Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour, format->field_offsets[O_HOUR].length) || Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute, format->field_offsets[O_MIN].length) || Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second, format->field_offsets[O_SEC].length)) { return CVT_FAIL|CVT_BADFMT; } else { unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset]; clock_time->usecond = 0; clock_time->flags = PARSEB_S_LEAP; if (clock_time->second == 60) clock_time->flags |= PARSEB_LEAPSECOND; /* * in the extended timecode format we have also the * indication that the timecode is in UTC * for compatibility reasons we start at the USUAL * offset (POWERUP flag) and know that the UTC indication * is the character before the powerup flag */ if ((format->flags & MBG_EXTENDED) && (f[-1] == 'U')) { /* * timecode is in UTC */ clock_time->utcoffset = 0; /* UTC */ clock_time->flags |= PARSEB_UTC; } else { /* * only calculate UTC offset if MET/MED is in time code * or we have the old time code format, where we do not * know whether it is UTC time or MET/MED * pray that nobody switches to UTC in the *old* standard time code * ROMS !!!! The new ROMS have 'U' at the ZONE field - good. */ switch (buffer[format->field_offsets[O_ZONE].offset]) { case ' ': clock_time->utcoffset = -1*60*60; /* MET */ break; case 'S': clock_time->utcoffset = -2*60*60; /* MED */ break; case 'U': /* * timecode is in UTC */ clock_time->utcoffset = 0; /* UTC */ clock_time->flags |= PARSEB_UTC; break; default: return CVT_FAIL|CVT_BADFMT; } } /* * gather status flags */ if (buffer[format->field_offsets[O_ZONE].offset] == 'S') clock_time->flags |= PARSEB_DST; if (f[0] == '#') clock_time->flags |= PARSEB_POWERUP; if (f[1] == '*') clock_time->flags |= PARSEB_NOSYNC; if (f[3] == '!') clock_time->flags |= PARSEB_ANNOUNCE; /* * oncoming leap second * 'a' code not confirmed - earth is not * expected to speed up */ if (f[3] == 'A') clock_time->flags |= PARSEB_LEAPADD; if (f[3] == 'a') clock_time->flags |= PARSEB_LEAPDEL; if (format->flags & MBG_EXTENDED) { clock_time->flags |= PARSEB_S_CALLBIT; /* * DCF77 does not encode the direction - * so we take the current default - * earth slowing down */ clock_time->flags &= ~PARSEB_LEAPDEL; if (f[4] == 'A') clock_time->flags |= PARSEB_LEAPADD; if (f[5] == 'R') clock_time->flags |= PARSEB_CALLBIT; } return CVT_OK; } } /* * parse_inp_fnc_t mbg_input * * grab data from input stream */ static unsigned long mbg_input( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; parseprintf(DD_PARSE, ("mbg_input(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch (ch) { case STX: parseprintf(DD_PARSE, ("mbg_input: STX seen\n")); parseio->parse_index = 1; parseio->parse_data[0] = ch; parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ return PARSE_INP_SKIP; case ETX: parseprintf(DD_PARSE, ("mbg_input: ETX seen\n")); if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: return parse_addchar(parseio, ch); } } /* * parse_cvt_fnc_t cvt_mgps * * convert Meinberg GPS format */ static unsigned long cvt_mgps( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { if (!Strok(buffer, format->fixed_string)) { return cvt_meinberg(buffer, size, format, clock_time, local); } else { if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day, format->field_offsets[O_DAY].length) || Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month, format->field_offsets[O_MONTH].length) || Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year, format->field_offsets[O_YEAR].length) || Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour, format->field_offsets[O_HOUR].length) || Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute, format->field_offsets[O_MIN].length) || Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second, format->field_offsets[O_SEC].length)) { return CVT_FAIL|CVT_BADFMT; } else { long h; unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset]; clock_time->flags = PARSEB_S_LEAP|PARSEB_S_POSITION; clock_time->usecond = 0; /* * calculate UTC offset */ if (Stoi(&buffer[format->field_offsets[O_UTCHOFFSET].offset], &h, format->field_offsets[O_UTCHOFFSET].length)) { return CVT_FAIL|CVT_BADFMT; } else { if (Stoi(&buffer[format->field_offsets[O_UTCMOFFSET].offset], &clock_time->utcoffset, format->field_offsets[O_UTCMOFFSET].length)) { return CVT_FAIL|CVT_BADFMT; } clock_time->utcoffset += h * 60; clock_time->utcoffset = clock_time->utcoffset * 60; if (buffer[format->field_offsets[O_UTCSOFFSET].offset] != '-') { clock_time->utcoffset = -clock_time->utcoffset; } } /* * gather status flags */ if (buffer[format->field_offsets[O_ZONE].offset] == 'S') clock_time->flags |= PARSEB_DST; if (clock_time->utcoffset == 0) clock_time->flags |= PARSEB_UTC; /* * no sv's seen - no time & position */ if (f[0] == '#') clock_time->flags |= PARSEB_POWERUP; /* * at least one sv seen - time (for last position) */ if (f[1] == '*') clock_time->flags |= PARSEB_NOSYNC; else if (!(clock_time->flags & PARSEB_POWERUP)) clock_time->flags |= PARSEB_POSITION; /* * oncoming zone switch */ if (f[3] == '!') clock_time->flags |= PARSEB_ANNOUNCE; /* * oncoming leap second * 'a' code not confirmed - earth is not * expected to speed up */ if (f[4] == 'A') clock_time->flags |= PARSEB_LEAPADD; if (f[4] == 'a') clock_time->flags |= PARSEB_LEAPDEL; /* * f[5] == ' ' */ /* * this is the leap second */ if ((f[6] == 'L') || (clock_time->second == 60)) clock_time->flags |= PARSEB_LEAPSECOND; return CVT_OK; } } } /* * parse_inp_fnc_t gps_input * * grep binary data from input stream */ static unsigned long gps_input( parse_t *parseio, char ch, timestamp_t *tstamp ) { CSUM calc_csum; /* used to compare the incoming csums */ GPS_MSG_HDR header; struct msg_buf *msg_buf; msg_buf = (struct msg_buf *)parseio->parse_pdata; parseprintf(DD_PARSE, ("gps_input(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); if (!msg_buf) return PARSE_INP_SKIP; if ( msg_buf->phase == MBG_NONE ) { /* not receiving yet */ switch (ch) { case SOH: parseprintf(DD_PARSE, ("gps_input: SOH seen\n")); msg_buf->len = sizeof( header ); /* prepare to receive msg header */ msg_buf->phase = MBG_HEADER; /* receiving header */ break; case STX: parseprintf(DD_PARSE, ("gps_input: STX seen\n")); msg_buf->len = 0; msg_buf->phase = MBG_STRING; /* prepare to receive ASCII ETX delimited message */ parseio->parse_index = 1; parseio->parse_data[0] = ch; break; default: return PARSE_INP_SKIP; /* keep searching */ } parseio->parse_dtime.parse_msglen = 1; /* reset buffer pointer */ /* fill in first character */ parseio->parse_dtime.parse_msg[0] = (unsigned char)ch; parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ return PARSE_INP_SKIP; } /* SOH/STX has already been received */ /* save incoming character in both buffers if needbe */ if ((msg_buf->phase == MBG_STRING) && (parseio->parse_index < parseio->parse_dsize)) parseio->parse_data[parseio->parse_index++] = ch; parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] \ = (unsigned char)ch; if (parseio->parse_dtime.parse_msglen > sizeof(parseio->parse_dtime.parse_msg)) { msg_buf->phase = MBG_NONE; /* buffer overflow - discard */ parseio->parse_data[parseio->parse_index] = '\0'; memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1)); parseio->parse_ldsize = parseio->parse_index; return PARSE_INP_DATA; } switch (msg_buf->phase) { case MBG_HEADER: case MBG_DATA: msg_buf->len--; if ( msg_buf->len ) /* transfer not complete */ return PARSE_INP_SKIP; parseprintf(DD_PARSE, ("gps_input: %s complete\n", (msg_buf->phase == MBG_DATA) ? "data" : "header")); break; case MBG_STRING: if ((ch == ETX) || (parseio->parse_index >= parseio->parse_dsize)) { msg_buf->phase = MBG_NONE; parseprintf(DD_PARSE, ("gps_input: string complete\n")); parseio->parse_data[parseio->parse_index] = '\0'; memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1)); parseio->parse_ldsize = parseio->parse_index; parseio->parse_index = 0; return PARSE_INP_TIME; } return PARSE_INP_SKIP; default: /* huh? */ break; } /* cnt == 0, so the header or the whole message is complete */ if ( msg_buf->phase == MBG_HEADER ) { /* header complete now */ unsigned char *datap = parseio->parse_dtime.parse_msg + 1; get_mbg_header(&datap, &header); parseprintf(DD_PARSE, ("gps_input: header: cmd 0x%x, len %d, dcsum 0x%x, hcsum 0x%x\n", (int)header.cmd, (int)header.len, (int)header.data_csum, (int)header.hdr_csum)); calc_csum = mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg + 1, (unsigned short)6 ); if ( calc_csum != header.hdr_csum ) { parseprintf(DD_PARSE, ("gps_input: header checksum mismatch expected 0x%x, got 0x%x\n", (int)calc_csum, (int)mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg, (unsigned short)6 ))); msg_buf->phase = MBG_NONE; /* back to hunting mode */ return PARSE_INP_DATA; /* invalid header checksum received - pass up for detection */ } if ((header.len == 0) || /* no data to wait for */ (header.len >= (sizeof (parseio->parse_dtime.parse_msg) - sizeof(header) - 1))) /* blows anything we have space for */ { msg_buf->phase = MBG_NONE; /* back to hunting mode */ return (header.len == 0) ? PARSE_INP_DATA : PARSE_INP_SKIP; /* message complete/throwaway */ } parseprintf(DD_PARSE, ("gps_input: expecting %d bytes of data message\n", (int)header.len)); msg_buf->len = header.len;/* save number of bytes to wait for */ msg_buf->phase = MBG_DATA; /* flag header already complete */ return PARSE_INP_SKIP; } parseprintf(DD_PARSE, ("gps_input: message data complete\n")); /* Header and data have been received. The header checksum has been */ /* checked */ msg_buf->phase = MBG_NONE; /* back to hunting mode */ return PARSE_INP_DATA; /* message complete, must be evaluated */ } /* * History: * * clk_meinberg.c,v * Revision 4.12.2.1 2005/09/25 10:22:35 kardel * cleanup buffer bounds * * Revision 4.12 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.11 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.8 1999/11/28 09:13:50 kardel * RECON_4_0_98F * * Revision 4.7 1999/02/21 11:09:14 kardel * cleanup * * Revision 4.6 1998/06/14 21:09:36 kardel * Sun acc cleanup * * Revision 4.5 1998/06/13 15:18:54 kardel * fix mem*() to b*() function macro emulation * * Revision 4.4 1998/06/13 12:03:23 kardel * fix SYSV clock name clash * * Revision 4.3 1998/06/12 15:22:28 kardel * fix prototypes * * Revision 4.2 1998/05/24 16:14:42 kardel * support current Meinberg standard data formats * * Revision 4.1 1998/05/24 09:39:52 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:29 kardel * Start 4.0 release version numbering * * from V3 3.23 - log info deleted 1998/04/11 kardel * */ ntpsec-1.1.0+dfsg1/libparse/wscript0000644000175000017500000000121613252364117017044 0ustar rlaagerrlaagerdef build(ctx): libparse_source = [ "binio.c", "clk_computime.c", "clk_dcf7000.c", "clk_hopf6021.c", "clk_meinberg.c", "clk_rawdcf.c", "clk_rcc8000.c", "clk_schmid.c", "clk_sel240x.c", "clk_trimtaip.c", "clk_trimtsip.c", "clk_varitext.c", "clk_wharton.c", "data_mbg.c", "gpstolfp.c", "ieee754io.c", "info_trimble.c", "parse.c", "parse_conf.c", "trim_info.c", ] ctx( target="parse", features="c cstlib bld_include src_include", source=libparse_source, ) ntpsec-1.1.0+dfsg1/libparse/ieee754io.c0000644000175000017500000001410013252364117017265 0ustar rlaagerrlaager/* * Created: Sun Jul 13 09:12:02 1997 * * Copyright (c) 1997-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include #include "ntp_stdlib.h" #include "ntp_fp.h" #include "ieee754io.h" static uint64_t get_byte (unsigned char *, offsets_t, int *); #ifdef DEBUG_PARSELIB #include "lib_strbuf.h" static char * fmt_blong( uint64_t val, int cnt ) { char *buf, *s; int i = cnt; val <<= 32 - cnt; buf = lib_getbuf(); s = buf; while (i--) { if (val & 0x80000000) { *s++ = '1'; } else { *s++ = '0'; } val <<= 1; } *s = '\0'; return buf; } static char * fmt_flt( bool sign, uint64_t ml, uint64_t ch, int length ) { char *buf; buf = lib_getbuf(); if ( 8 == length ) { snprintf(buf, LIB_BUFLENGTH, "%c %s %s %s", sign ? '-' : '+', fmt_blong(ch, 11), fmt_blong(ml >> 32, 20), fmt_blong(ml & 0x0FFFFFFFFULL, 32)); } else { snprintf(buf, LIB_BUFLENGTH, "%c %s %s", sign ? '-' : '+', fmt_blong(ch, 8), fmt_blong(ml, 23)); } return buf; } static char * fmt_hex( unsigned char *bufp, int length ) { char * buf; char hex[4]; int i; buf = lib_getbuf(); buf[0] = '\0'; for (i = 0; i < length; i++) { snprintf(hex, sizeof(hex), "%02x", bufp[i]); strlcat(buf, hex, LIB_BUFLENGTH); } return buf; } #endif static uint64_t get_byte( unsigned char *bufp, offsets_t offset, int *fieldindex ) { unsigned char val; val = *(bufp + offset[*fieldindex]); (*fieldindex)++; return val; } /* * make conversions to and from external IEEE754 formats and internal * NTP FP format. */ int fetch_ieee754( unsigned char **buffpp, int size, l_fp *lfpp, offsets_t offsets ) { unsigned char *bufp = *buffpp; bool sign; int bias; /* bias 127 or 1023 */ int maxexp; int mbits; /* length of mantissa, 23 or 52 */ uint64_t mantissa; /* mantissa, 23 or 52 bits used, +1 */ int characteristic; /* biased exponent, 0 to 255 or 2047 */ int exponent; /* unbiased exponent */ int maxexp_lfp; /* maximum exponent that fits in an l_fp */ unsigned char val; int fieldindex = 0; /* index into bufp */ int fudge; /* shift difference of l_fp and IEEE */ int shift; /* amount to shift IEEE to get l_fp */ *lfpp = 0; /* return zero for all errors: NAN, +INF, -INF, etc. */ /* fetch sign byte & first part of characteristic */ val = (unsigned char)get_byte(bufp, offsets, &fieldindex); sign = (val & 0x80) != 0; characteristic = (val & 0x7F); /* fetch rest of characteristic and start of mantissa */ val = (unsigned char)get_byte(bufp, offsets, &fieldindex); switch (size) { case IEEE_DOUBLE: fudge = -20; maxexp_lfp = 31; mbits = 52; bias = 1023; maxexp = 2047; characteristic <<= 4; /* grab lower characteristic bits */ characteristic |= (val & 0xF0) >> 4; mantissa = (val & 0x0FULL) << 48; mantissa |= get_byte(bufp, offsets, &fieldindex) << 40; mantissa |= get_byte(bufp, offsets, &fieldindex) << 32; mantissa |= get_byte(bufp, offsets, &fieldindex) << 24; mantissa |= get_byte(bufp, offsets, &fieldindex) << 16; mantissa |= get_byte(bufp, offsets, &fieldindex) << 8; mantissa |= get_byte(bufp, offsets, &fieldindex); break; case IEEE_SINGLE: fudge = 9; maxexp_lfp = 30; mbits = 23; bias = 127; maxexp = 255; characteristic <<= 1; /* grab last characteristic bit from 2nd byte */ characteristic |= (val & 0x80) ? 1 : 0 ; mantissa = (val & 0x7FU) << 16; mantissa |= get_byte(bufp, offsets, &fieldindex) << 8; mantissa |= get_byte(bufp, offsets, &fieldindex); break; default: return IEEE_BADCALL; } exponent = characteristic - bias; shift = exponent + fudge; #ifdef DEBUG_PARSELIB if ( debug > 4) { /* SPECIAL DEBUG */ int length = 8; if ( IEEE_SINGLE == size ) { length = 4; } printf("\nfetchieee754: FP: %s -> %s\n", fmt_hex(*buffpp, length), fmt_flt(sign, mantissa, characteristic, length)); printf("fetchieee754: Char: %d, Exp: %d, mbits %d, shift %d\n", characteristic, exponent, mbits, shift); } #endif *buffpp += fieldindex; /* detect funny numbers */ if (characteristic == maxexp) { /* NaN or Infinity */ if (mantissa) { /* NaN */ return IEEE_NAN; } /* +Inf or -Inf */ return sign ? IEEE_NEGINFINITY : IEEE_POSINFINITY; } /* check for overflows */ if (exponent > maxexp_lfp) { /* * sorry an l_fp only so long * overflow only in respect to NTP-FP representation */ return sign ? IEEE_NEGOVERFLOW : IEEE_POSOVERFLOW; } if (characteristic == 0) { /* de-normalized or tiny number - fits only as 0 */ return IEEE_OK; } /* build the real number */ /* add in implied 1 */ mantissa |= 1ULL << mbits; if ( 0 == shift ) { /* no shift */ *lfpp = mantissa; } else if ( 0 > shift ) { /* right shift */ *lfpp = mantissa >> -shift; } else { /* left shift */ *lfpp = mantissa << shift; } /* adjust for sign */ if (sign) { L_NEG(*lfpp); } return IEEE_OK; } ntpsec-1.1.0+dfsg1/libparse/clk_trimtaip.c0000644000175000017500000001123513252364117020256 0ustar rlaagerrlaager/* * Trimble SV6 clock support - several collected codepieces * * Copyright (c) 1989-2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "parse.h" #include "ntp_stdlib.h" /* 0000000000111111111122222222223333333 / char * 0123456789012345678901234567890123456 \ posn * >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual * ----33445566600112222BB7__-_____--99- Parse * >RTM 1 ;* <", Check */ #define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \ ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \ ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \ -1) #define O_USEC O_WDAY #define O_GPSFIX O_FLAGS #define O_CHKSUM O_UTCHOFFSET static struct format trimsv6_fmt = { { { 13, 2 }, {15, 2}, { 17, 4}, /* Day, Month, Year */ { 4, 2 }, { 6, 2}, { 8, 2}, /* Hour, Minute, Second */ { 10, 3 }, {23, 1}, { 0, 0}, /* uSec, FIXes (WeekDAY, FLAGS, ZONE) */ { 34, 2 }, { 0, 0}, { 21, 2}, /* cksum, -, utcS (UTC[HMS]OFFSET) */ }, (const unsigned char *)">RTM 1 ;* <", 0 }; static parse_cvt_fnc_t cvt_trimtaip; static parse_inp_fnc_t inp_trimtaip; clockformat_t clock_trimtaip = { inp_trimtaip, /* no input handling */ cvt_trimtaip, /* Trimble conversion */ pps_one, /* easy PPS monitoring */ (void *)&trimsv6_fmt, /* conversion configuration */ "Trimble TAIP", 37, /* string buffer */ 0 /* no private data */ }; /* parse_cvt_fnc_t cvt_trimtaip */ static unsigned long cvt_trimtaip( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { long gpsfix; uint8_t calc_csum = 0; long recv_csum; int i; UNUSED_ARG(size); UNUSED_ARG(local); if (!Strok(buffer, format->fixed_string)) return CVT_NONE; #define OFFS(x) format->field_offsets[(x)].offset #define STOI(x, y) \ Stoi(&buffer[OFFS(x)], y, \ format->field_offsets[(x)].length) if ( STOI(O_DAY, &clock_time->day) || STOI(O_MONTH, &clock_time->month) || STOI(O_YEAR, &clock_time->year) || STOI(O_HOUR, &clock_time->hour) || STOI(O_MIN, &clock_time->minute) || STOI(O_SEC, &clock_time->second) || STOI(O_USEC, &clock_time->usecond)|| STOI(O_GPSFIX, &gpsfix) ) return CVT_FAIL|CVT_BADFMT; clock_time->usecond *= 1000; /* Check that the checksum is right */ for (i=OFFS(O_CHKSUM)-1; i >= 0; i--) calc_csum ^= buffer[i]; recv_csum = (hexval(buffer[OFFS(O_CHKSUM)]) << 4) | hexval(buffer[OFFS(O_CHKSUM)+1]); if (recv_csum < 0) return CVT_FAIL|CVT_BADTIME; if (((uint8_t) recv_csum) != calc_csum) return CVT_FAIL|CVT_BADTIME; clock_time->utcoffset = 0; /* What should flags be set to ? */ clock_time->flags = PARSEB_UTC; /* if the current GPS fix is 9 (unknown), reject */ if (0 > gpsfix || gpsfix > 9) clock_time->flags |= PARSEB_POWERUP; return CVT_OK; } /* * parse_inp_fnc_t inp_trimtaip * * grab data from input stream */ static unsigned long inp_trimtaip( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; parseprintf(DD_PARSE, ("inp_trimtaip(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch (ch) { case '>': parseprintf(DD_PARSE, ("inp_trimptaip: START seen\n")); parseio->parse_index = 1; parseio->parse_data[0] = ch; parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ return PARSE_INP_SKIP; case '<': parseprintf(DD_PARSE, ("inp_trimtaip: END seen\n")); if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: return parse_addchar(parseio, ch); } } /* * History: * * clk_trimtaip.c,v * Revision 4.11 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.10 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.7 1999/11/28 09:13:51 kardel * RECON_4_0_98F * * Revision 4.6 1998/08/16 18:46:27 kardel * (clock_trimtaip =): changed format name * * Revision 4.5 1998/06/14 21:09:38 kardel * Sun acc cleanup * * Revision 4.4 1998/06/13 12:06:57 kardel * fix SYSV clock name clash * * Revision 4.3 1998/06/12 15:22:29 kardel * fix prototypes * * Revision 4.2 1998/06/12 09:13:26 kardel * conditional compile macros fixed * printf prototype * * Revision 4.1 1998/05/24 09:39:54 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:31 kardel * Start 4.0 release version numbering * * from V3 1.4 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/clk_varitext.c0000644000175000017500000001436413252364117020301 0ustar rlaagerrlaager/* * Varitext code variant by A.McConnell 1997/01/19 * * Supports Varitext's Radio Clock * * Used the Meinberg/Computime clock as a template for Varitext Radio Clock * * Copyright (c) 1989-2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "parse.h" # include "ntp_stdlib.h" # include /* Not used. Comment out to avoid warnings static const uint8_t VT_INITIALISED = 0x01; static const uint8_t VT_SYNCHRONISED = 0x02; static const uint8_t VT_ALARM_STATE = 0x04; static const uint8_t VT_SEASON_CHANGE = 0x10; static const uint8_t VT_LAST_TELEGRAM_OK = 0x20; */ static const uint8_t VT_BST = 0x08; /* * The Varitext receiver sends a datagram in the following format every minute * * Timestamp T:YY:MM:MD:WD:HH:MM:SSCRLFSTXXX * Pos 0123456789012345678901 2 3 4567 * 0000000000111111111122 2 2 2222 * Parse T: : : : : : : \r\n * * T Startcharacter "T" specifies start of the timestamp * YY Year MM Month 1-12 * MD Day of the month * WD Day of week * HH Hour * MM Minute * SS Second * CR Carriage return * LF Linefeed * ST Status character * Bit 0 - Set= Initialised; Reset=Time Invalid (DO NOT USE) * Bit 1 - Set= Synchronised; Reset= Unsynchronised * Bit 2 - Set= Alarm state; Reset= No alarm * Bit 3 - Set= BST; Reset= GMT * Bit 4 - Set= Seasonal change in approx hour; Reset= No seasonal change expected * Bit 5 - Set= Last MSF telegram was OK; Reset= Last telegram was in error; * Bit 6 - Always set * Bit 7 - Unused * XXX Checksum calculated using Fletcher's method (ignored for now). */ static struct format varitext_fmt = { { {8, 2}, {5, 2}, {2, 2}, /* day, month, year */ {14, 2}, {17, 2}, {20, 2}, /* hour, minute, second */ {11, 2}, {24, 1} /* dayofweek, status */ }, (const unsigned char*)"T: : : : : : : \r\n ", 0 }; static parse_cvt_fnc_t cvt_varitext; static parse_inp_fnc_t inp_varitext; struct varitext { unsigned char start_found; unsigned char end_found; unsigned char end_count; unsigned char previous_ch; timestamp_t tstamp; }; clockformat_t clock_varitext = { inp_varitext, /* Because of the strange format we need to parse it ourselves */ cvt_varitext, /* Varitext conversion */ 0, /* no PPS monitoring */ (void *)&varitext_fmt, /* conversion configuration */ "Varitext Radio Clock", /* Varitext Radio Clock */ 30, /* string buffer */ sizeof(struct varitext), /* Private data size required to hold current parse state */ }; /* * parse_cvt_fnc_t cvt_varitext * * convert simple type format */ static unsigned long cvt_varitext( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { UNUSED_ARG(size); UNUSED_ARG(local); if (!Strok(buffer, format->fixed_string)) { return CVT_NONE; } else { if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day, format->field_offsets[O_DAY].length) || Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month, format->field_offsets[O_MONTH].length) || Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year, format->field_offsets[O_YEAR].length) || Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour, format->field_offsets[O_HOUR].length) || Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute, format->field_offsets[O_MIN].length) || Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second, format->field_offsets[O_SEC].length)) { return CVT_FAIL | CVT_BADFMT; } else { uint8_t *f = (uint8_t*) &buffer[format->field_offsets[O_FLAGS].offset]; clock_time->flags = 0; clock_time->utcoffset = 0; if (((*f) & VT_BST)) /* BST flag is set so set to indicate daylight saving time is active and utc offset */ { clock_time->utcoffset = -1*60*60; clock_time->flags |= PARSEB_DST; } /* if (!((*f) & VT_INITIALISED)) Clock not initialised clock_time->flags |= PARSEB_POWERUP; if (!((*f) & VT_SYNCHRONISED)) Clock not synchronised clock_time->flags |= PARSEB_NOSYNC; if (((*f) & VT_SEASON_CHANGE)) Seasonal change expected in the next hour clock_time->flags |= PARSEB_ANNOUNCE; */ return CVT_OK; } } } /* parse_inp_fnc_t inp_varitext */ static unsigned long inp_varitext( parse_t *parseio, char ch, timestamp_t *tstamp ) { struct varitext *t = (struct varitext *)parseio->parse_pdata; int rtc; parseprintf(DD_PARSE, ("inp_varitext(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); if (!t) return PARSE_INP_SKIP; /* local data not allocated - sigh! */ if (ch == 'T') t->tstamp = *tstamp; if ((t->previous_ch == 'T') && (ch == ':')) { parseprintf(DD_PARSE, ("inp_varitext: START seen\n")); parseio->parse_data[0] = 'T'; parseio->parse_index=1; parseio->parse_dtime.parse_stime = t->tstamp; /* Time stamp at packet start */ t->start_found = 1; t->end_found = 0; t->end_count = 0; } if (t->start_found) { if ((rtc = (int)parse_addchar(parseio, ch)) != PARSE_INP_SKIP) { parseprintf(DD_PARSE, ("inp_varitext: ABORTED due to too many characters\n")); memset(t, 0, sizeof(struct varitext)); return (unsigned long)rtc; } if (t->end_found) { if (++(t->end_count) == 4) /* Finally found the end of the message */ { parseprintf(DD_PARSE, ("inp_varitext: END seen\n")); memset(t, 0, sizeof(struct varitext)); if ((rtc = (int)parse_addchar(parseio, 0)) == PARSE_INP_SKIP) return parse_end(parseio); else return (unsigned long)rtc; } } if ((t->previous_ch == '\r') && (ch == '\n')) { t->end_found = 1; } } t->previous_ch = (unsigned char)ch; return PARSE_INP_SKIP; } /* * History: * * clk_varitext.c,v * Revision 1.5 2005/04/16 17:32:10 kardel * update copyright * * Revision 1.4 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * * Revision 1.0 1997/06/02 13:16:30 McConnell * File created * */ ntpsec-1.1.0+dfsg1/libparse/clk_computime.c0000644000175000017500000001064013252364117020426 0ustar rlaagerrlaager/* * Supports Diem's Computime Radio Clock * * Used the Meinberg clock as a template for Diem's Computime Radio Clock * * adapted by Alois Camenzind * * Copyright (c) 1995-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_fp.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include "parse.h" #include /* * The Computime receiver sends a datagram in the following format every minute * * Timestamp T:YY:MM:MD:WD:HH:MM:SSCRLF * Pos 0123456789012345678901 2 3 * 0000000000111111111122 2 2 * Parse T: : : : : : : rn * * T Startcharacter "T" specifies start of the timestamp * YY Year * MM Month 1-12 * MD Day of the month * WD Day of week * HH Hour * MM Minute * SS Second * CR Carriage return * LF Linefeed * */ static struct format computime_fmt = { { {8, 2}, {5, 2}, {2, 2}, /* day, month, year */ {14, 2}, {17, 2}, {20, 2}, /* hour, minute, second */ {11, 2}, /* dayofweek, */ }, (const unsigned char *)"T: : : : : : : \r\n", 0 }; static parse_cvt_fnc_t cvt_computime; static parse_inp_fnc_t inp_computime; clockformat_t clock_computime = { inp_computime, /* Computime input handling */ cvt_computime, /* Computime conversion */ 0, /* no PPS monitoring */ (void *)&computime_fmt, /* conversion configuration */ "Diem's Computime Radio Clock", /* Computime Radio Clock */ 24, /* string buffer */ 0 /* no private data (complete packets) */ }; /* * parse_cvt_fnc_t cvt_computime * * convert simple type format */ static unsigned long cvt_computime( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { UNUSED_ARG(size); UNUSED_ARG(local); if (!Strok(buffer, format->fixed_string)) { return CVT_NONE; } else { if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day, format->field_offsets[O_DAY].length) || Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month, format->field_offsets[O_MONTH].length) || Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year, format->field_offsets[O_YEAR].length) || Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour, format->field_offsets[O_HOUR].length) || Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute, format->field_offsets[O_MIN].length) || Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second, format->field_offsets[O_SEC].length)) { return CVT_FAIL | CVT_BADFMT; } else { clock_time->flags = 0; clock_time->utcoffset = 0; /* We have UTC time */ return CVT_OK; } } } /* * parse_inp_fnc_t inp_computime * * grab data from input stream */ static unsigned long inp_computime( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; parseprintf(DD_PARSE, ("inp_computime(0x%lx, 0x%x, ...)\n", (long unsigned)parseio, (unsigned int)ch)); switch (ch) { case 'T': parseprintf(DD_PARSE, ("inp_computime: START seen\n")); parseio->parse_index = 1; parseio->parse_data[0] = ch; parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ return PARSE_INP_SKIP; case '\n': parseprintf(DD_PARSE, ("inp_computime: END seen\n")); if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: return parse_addchar(parseio, ch); } } /* * clk_computime.c,v * Revision 4.10 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.9 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.6 1999/11/28 09:13:49 kardel * RECON_4_0_98F * * Revision 4.5 1998/06/14 21:09:34 kardel * Sun acc cleanup * * Revision 4.4 1998/06/13 12:00:38 kardel * fix SYSV clock name clash * * Revision 4.3 1998/06/12 15:22:26 kardel * fix prototypes * * Revision 4.2 1998/06/12 09:13:24 kardel * conditional compile macros fixed * printf prototype * * Revision 4.1 1998/05/24 09:39:51 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:27 kardel * Start 4.0 release version numbering * * from V3 1.8 log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/clk_trimtsip.c0000644000175000017500000002745213252364117020310 0ustar rlaagerrlaager/* * Trimble TSIP support * Thanks to Sven Dietrich for providing test hardware * * Copyright (c) 1995-2009 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause */ #include "config.h" #include "ntp_syslog.h" #include "ntp_types.h" #include "ntp_fp.h" #include "timespecops.h" #include "ntp_calendar.h" #include "ntp_machine.h" #include "ntp_stdlib.h" #include "parse.h" #include #include "ascii.h" #include "binio.h" #include "ieee754io.h" #include "trimble.h" #include "gpstolfp.h" /* * Trimble low level TSIP parser / time converter * * The receiver uses a serial message protocol called Trimble Standard * Interface Protocol (it can support others but this driver only supports * TSIP). Messages in this protocol have the following form: * * ... ... * * Any bytes within the portion of value 10 hex () are doubled * on transmission and compressed back to one on reception. Otherwise * the values of data bytes can be anything. The serial interface is RS-422 * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits * in total!), and 1 stop bit. The protocol supports byte, integer, single, * and double datatypes. Integers are two bytes, sent most significant first. * Singles are IEEE754 single precision floating point numbers (4 byte) sent * sign & exponent first. Doubles are IEEE754 double precision floating point * numbers (8 byte) sent sign & exponent first. * The receiver supports a large set of messages, only a very small subset of * which is used here. * * From this module the following are recognised: * * ID Description * * 41 GPS Time * 46 Receiver health * 4F UTC correction data (used to get leap second warnings) * * All others are accepted but ignored for time conversion - they are passed up to higher layers. * */ static offsets_t trim_offsets = { 0, 1, 2, 3, 4, 5, 6, 7 }; struct trimble { uint8_t t_in_pkt; /* first DLE received */ uint8_t t_dle; /* subsequent DLE received */ unsigned short t_week; /* GPS week */ unsigned short t_weekleap; /* GPS week of next/last week */ unsigned short t_dayleap; /* day in week */ unsigned short t_gpsutc; /* GPS - UTC offset */ unsigned short t_gpsutcleap; /* offset at next/last leap */ uint8_t t_operable; /* receiver feels OK */ uint8_t t_mode; /* actual operating mode */ uint8_t t_leap; /* possible leap warning */ uint8_t t_utcknown; /* utc offset known */ }; #define STATUS_BAD 0 /* BAD or UNINITIALIZED receiver status */ #define STATUS_UNSAFE 1 /* not enough receivers for full precision */ #define STATUS_SYNC 2 /* enough information for good operation */ static unsigned long inp_tsip (parse_t *, char, timestamp_t *); static unsigned long cvt_trimtsip (unsigned char *, int, struct format *, clocktime_t *, void *); struct clockformat clock_trimtsip = { inp_tsip, /* Trimble TSIP input handler */ cvt_trimtsip, /* Trimble TSIP conversion */ pps_one, /* easy PPS monitoring */ 0, /* no configuration data */ "Trimble TSIP", 400, /* input buffer */ sizeof(struct trimble) /* private data */ }; #define ADDSECOND 0x01 #define DELSECOND 0x02 static unsigned long inp_tsip( parse_t *parseio, char ch, timestamp_t *tstamp ) { struct trimble *t = (struct trimble *)parseio->parse_pdata; if (!t) return PARSE_INP_SKIP; /* local data not allocated - sigh! */ if (!t->t_in_pkt && ch != DLE) { /* wait for start of packet */ return PARSE_INP_SKIP; } if ((parseio->parse_index >= (parseio->parse_dsize - 2)) || (parseio->parse_dtime.parse_msglen >= (sizeof(parseio->parse_dtime.parse_msg) - 2))) { /* OVERFLOW - DROP! */ t->t_in_pkt = t->t_dle = 0; parseio->parse_index = 0; parseio->parse_dtime.parse_msglen = 0; return PARSE_INP_SKIP; } switch (ch) { case DLE: if (!t->t_in_pkt) { t->t_dle = 0; t->t_in_pkt = 1; parseio->parse_index = 0; parseio->parse_data[parseio->parse_index++] = ch; parseio->parse_dtime.parse_msglen = 0; parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = (unsigned char)ch; parseio->parse_dtime.parse_stime = *tstamp; /* pick up time stamp at packet start */ } else if (t->t_dle) { /* Double DLE -> insert a DLE */ t->t_dle = 0; parseio->parse_data[parseio->parse_index++] = DLE; parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE; } else t->t_dle = 1; break; case ETX: if (t->t_dle) { /* DLE,ETX -> end of packet */ parseio->parse_data[parseio->parse_index++] = DLE; parseio->parse_data[parseio->parse_index] = ch; parseio->parse_ldsize = (unsigned short) (parseio->parse_index + 1); memcpy(parseio->parse_ldata, parseio->parse_data, parseio->parse_ldsize); parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE; parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = (unsigned char)ch; t->t_in_pkt = t->t_dle = 0; return PARSE_INP_TIME|PARSE_INP_DATA; } /*FALLTHROUGH*/ default: /* collect data */ t->t_dle = 0; parseio->parse_data[parseio->parse_index++] = ch; parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = (unsigned char)ch; } return PARSE_INP_SKIP; } /* * cvt_trimtsip * * convert TSIP type format */ static unsigned long cvt_trimtsip( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { struct trimble *t = (struct trimble *)local; /* get local data space */ #define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */ uint8_t cmd; UNUSED_ARG(format); clock_time->flags = 0; if (!t) { return CVT_NONE; /* local data not allocated - sigh! */ } if ((size < 4) || (buffer[0] != DLE) || (buffer[size-1] != ETX) || (buffer[size-2] != DLE)) { printf("TRIMBLE BAD packet, size %d:\n", size); return CVT_NONE; } else { unsigned char *bp; cmd = buffer[1]; switch(cmd) { case CMD_RCURTIME: { /* GPS time */ l_fp secs; int week = getmsb_short(&mb(4)); l_fp utcoffset; l_fp gpstime; bp = &mb(0); if (fetch_ieee754(&bp, IEEE_SINGLE, &secs, trim_offsets) != IEEE_OK) return CVT_FAIL|CVT_BADFMT; if ((lfpsint(secs) <= 0) || (t->t_utcknown == 0)) { clock_time->flags = PARSEB_POWERUP; return CVT_OK; } if (week < GPSWRAP) { week += GPSWEEKS; } /* time OK */ /* fetch UTC offset */ bp = &mb(6); if (fetch_ieee754(&bp, IEEE_SINGLE, &utcoffset, trim_offsets) != IEEE_OK) return CVT_FAIL|CVT_BADFMT; secs -= utcoffset; /* adjust GPS time to UTC time */ gpstolfp((unsigned short)week, (unsigned short)0, lfpuint(secs), &gpstime); setlfpfrac(gpstime, lfpfrac(secs)); clock_time->utctime = (time_t)(lfpuint(gpstime) - JAN_1970); clock_time->usecond = lfp_intv_to_tspec(gpstime).tv_nsec / 1000; if (t->t_leap == ADDSECOND) clock_time->flags |= PARSEB_LEAPADD; if (t->t_leap == DELSECOND) clock_time->flags |= PARSEB_LEAPDEL; switch (t->t_operable) { case STATUS_SYNC: clock_time->flags &= ~(PARSEB_POWERUP|PARSEB_NOSYNC); break; case STATUS_UNSAFE: clock_time->flags |= PARSEB_NOSYNC; break; case STATUS_BAD: clock_time->flags |= PARSEB_NOSYNC|PARSEB_POWERUP; break; default: /* huh? */ break; } if (t->t_mode == 0) clock_time->flags |= PARSEB_POSITION; clock_time->flags |= PARSEB_S_LEAP|PARSEB_S_POSITION; return CVT_OK; } /* case 0x41 */ case CMD_RRECVHEALTH: { /* TRIMBLE health */ uint8_t status = mb(0); switch (status) { case 0x00: /* position fixes */ t->t_operable = STATUS_SYNC; break; case 0x09: /* 1 satellite */ case 0x0A: /* 2 satellites */ case 0x0B: /* 3 satellites */ t->t_operable = STATUS_UNSAFE; break; default: t->t_operable = STATUS_BAD; break; } t->t_mode = status; } break; case CMD_RUTCPARAM: { l_fp t0t; unsigned char *lbp; /* UTC correction data - derive a leap warning */ /* current leap correction (GPS-UTC) */ int tls = t->t_gpsutc = get_msb_ushort(&mb(12)); /* new leap correction */ int tlsf = t->t_gpsutcleap = get_msb_ushort(&mb(24)); /* week no of leap correction */ t->t_weekleap = get_msb_ushort(&mb(20)); if (t->t_weekleap < GPSWRAP) t->t_weekleap = (unsigned short)(t->t_weekleap + GPSWEEKS); /* day in week of leap correction */ t->t_dayleap = get_msb_ushort(&mb(22)); /* current week no */ t->t_week = get_msb_ushort(&mb(18)); if (t->t_week < GPSWRAP) /* coverity[copy_paste_error] */ t->t_week = (unsigned short)(t->t_weekleap + GPSWEEKS); lbp = (unsigned char *)&mb(14); /* last update time */ if (fetch_ieee754(&lbp, IEEE_SINGLE, &t0t, trim_offsets) != IEEE_OK) return CVT_FAIL|CVT_BADFMT; t->t_utcknown = lfpuint(t0t) != 0; if ((t->t_utcknown) && /* got UTC information */ (tlsf != tls) && /* something will change */ ((t->t_weekleap - t->t_week) < 5)) /* and close in the future */ { /* generate a leap warning */ if (tlsf > tls) t->t_leap = ADDSECOND; else t->t_leap = DELSECOND; } else { t->t_leap = 0; } } break; default: /* it's validly formed, but we don't care about it! */ break; } } return CVT_SKIP; } /* * History: * * clk_trimtsip.c,v * Revision 4.19 2009/11/01 10:47:49 kardel * de-P() * * Revision 4.18 2009/11/01 08:46:46 kardel * clarify case FALLTHROUGH * * Revision 4.17 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.16 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.13 1999/11/28 09:13:51 kardel * RECON_4_0_98F * * Revision 4.12 1999/02/28 13:00:08 kardel * *** empty log message *** * * Revision 4.11 1999/02/28 11:47:54 kardel * (struct trimble): new member t_utcknown * (cvt_trimtsip): fixed status monitoring, bad receiver states are * now recognized * * Revision 4.10 1999/02/27 15:57:15 kardel * use mmemcpy instead of bcopy * * Revision 4.9 1999/02/21 12:17:42 kardel * 4.91f reconciliation * * Revision 4.8 1998/11/15 20:27:58 kardel * Release 4.0.73e13 reconciliation * * Revision 4.7 1998/08/16 18:49:20 kardel * (cvt_trimtsip): initial kernel capable version (no more floats) * (clock_trimtsip =): new format name * * Revision 4.6 1998/08/09 22:26:05 kardel * Trimble TSIP support * * Revision 4.5 1998/08/02 10:37:05 kardel * working TSIP parser * * Revision 4.4 1998/06/28 16:50:40 kardel * (getflt): fixed ENDIAN issue * (getdbl): fixed ENDIAN issue * (getint): use get_msb_short() * (cvt_trimtsip): use gpstolfp() for conversion * * Revision 4.3 1998/06/13 12:07:31 kardel * fix SYSV clock name clash * * Revision 4.2 1998/06/12 15:22:30 kardel * fix prototypes * * Revision 4.1 1998/05/24 09:39:54 kardel * implementation of the new IO handling model * * Revision 4.0 1998/04/10 19:45:32 kardel * Start 4.0 release version numbering * * from V3 1.8 loginfo deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/libparse/clk_wharton.c0000644000175000017500000001075313252364117020113 0ustar rlaagerrlaager/* * Support for WHARTON 400A Series clock + 404.2 serial interface. * * Copyright (C) 1999, 2000 by Philippe De Muyter * * SPDX-License-Identifier: BSD-2-clause * */ #include "config.h" #include "ntp_fp.h" #include "ascii.h" #include "parse.h" #include "ntp_stdlib.h" #include /* * In private e-mail alastair@wharton.co.uk said : * "If you are going to use the 400A and 404.2 system [for ntp] I recommend * that you set the 400A to output the message every second. The start of * transmission of the first byte of the message is synchronised to the * second edge." * The WHARTON 400A Series is able to send date/time serial messages * in 7 output formats. We use format 1 here because it is the shortest. * For use with this driver, the WHARTON 400A Series clock must be set-up * as follows : * Programmable Selected * Option No Option * BST or CET display 3 9 or 11 * No external controller 7 0 * Serial Output Format 1 9 1 * Baud rate 9600 bps 10 96 * Bit length 8 bits 11 8 * Parity even 12 E * * WHARTON 400A Series output format 1 is as follows : * * Timestamp STXssmmhhDDMMYYSETX * Pos 0 12345678901234 * 0 00000000011111 * * STX start transmission (ASCII 0x02) * ETX end transmission (ASCII 0x03) * ss Second expressed in reversed decimal (units then tens) * mm Minute expressed in reversed decimal * hh Hour expressed in reversed decimal * DD Day of month expressed in reversed decimal * MM Month expressed in reversed decimal (January is 1) * YY Year (without century) expressed in reversed decimal * S Status byte : 0x30 + * bit 0 0 = MSF source 1 = DCF source * bit 1 0 = Winter time 1 = Summer time * bit 2 0 = not synchronised 1 = synchronised * bit 3 0 = no early warning 1 = early warning * */ static parse_cvt_fnc_t cvt_wharton_400a; static parse_inp_fnc_t inp_wharton_400a; /* * parse_cvt_fnc_t cvt_wharton_400a * * convert simple type format */ static unsigned long cvt_wharton_400a( unsigned char *buffer, int size, struct format *format, clocktime_t *clock_time, void *local ) { int i; UNUSED_ARG(format); UNUSED_ARG(local); /* The given `size' includes a terminating null-character. */ if (size != 15 || buffer[0] != STX || buffer[14] != ETX || buffer[13] < '0' || buffer[13] > ('0' + 0xf)) return CVT_NONE; for (i = 1; i < 13; i += 1) if (buffer[i] < '0' || buffer[i] > '9') return CVT_NONE; clock_time->second = (buffer[2] - '0') * 10 + buffer[1] - '0'; clock_time->minute = (buffer[4] - '0') * 10 + buffer[3] - '0'; clock_time->hour = (buffer[6] - '0') * 10 + buffer[5] - '0'; clock_time->day = (buffer[8] - '0') * 10 + buffer[7] - '0'; clock_time->month = (buffer[10] - '0') * 10 + buffer[9] - '0'; clock_time->year = (buffer[12] - '0') * 10 + buffer[11] - '0'; clock_time->usecond = 0; if (buffer[13] & 0x1) /* We have CET time */ clock_time->utcoffset = -1*60*60; else /* We have BST time */ clock_time->utcoffset = 0; if (buffer[13] & 0x2) { clock_time->flags |= PARSEB_DST; clock_time->utcoffset += -1*60*60; } if (!(buffer[13] & 0x4)) clock_time->flags |= PARSEB_NOSYNC; if (buffer[13] & 0x8) clock_time->flags |= PARSEB_ANNOUNCE; return CVT_OK; } /* * parse_inp_fnc_t inp_wharton_400a * * grab data from input stream */ static unsigned long inp_wharton_400a( parse_t *parseio, char ch, timestamp_t *tstamp ) { unsigned int rtc; parseprintf(DD_PARSE, ("inp_wharton_400a(0x%lx, 0x%x, ...)\n", (unsigned long)parseio, (unsigned)ch)); switch (ch) { case STX: parseprintf(DD_PARSE, ("inp_wharton_400a: STX seen\n")); parseio->parse_index = 1; parseio->parse_data[0] = ch; parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ return PARSE_INP_SKIP; case ETX: parseprintf(DD_PARSE, ("inp_wharton_400a: ETX seen\n")); if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) return parse_end(parseio); else return rtc; default: return parse_addchar(parseio, ch); } } clockformat_t clock_wharton_400a = { inp_wharton_400a, /* input handling function */ cvt_wharton_400a, /* conversion function */ 0, /* no PPS monitoring */ 0, /* conversion configuration */ "WHARTON 400A Series clock Output Format 1", /* String format name */ 15, /* string buffer */ 0 /* no private data (complete packets) */ }; /* * clk_wharton.c,v * Revision 4.1 1999/02/28 15:27:24 kardel * wharton clock integration * */ ntpsec-1.1.0+dfsg1/contrib/0000775000175000017500000000000013252650651015270 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/contrib/temper-temp-log0000755000175000017500000000240213252364117020227 0ustar rlaagerrlaager#!/usr/bin/env python # coding: utf-8 """\ Usage: temper-temper-log Reads 'temper-poll -c' for room temperature data. Writes the temperature found to stdout on one line, preceded by the Unix UTC time in seconds and the Log source ID. Before you can use this utility you must have a TEMPer USB thermometer plugged in, and the temper-python package must be installed and configured. See their documentation for that procedure. Sample log from a TEMPer: 1471573103 TEMPER 37.000 1471573104 TEMPER 37.000 1471573105 TEMPER 37.000 Field 1: Unix UTC time in seconds Field 1: Log source (TEMPER) Field 3: CPU temperature in degrees C Sample crontab usage: # take and log CPU temp every 5 minutes */5 * * * * /usr/local/sbin/temper-temp-log >> /var/log/ntpstats/temps """ from __future__ import print_function import time import subprocess # sadly subprocess.check_output() is not in Python 2.6 proc = subprocess.Popen(["temper-poll", "-c"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) output = proc.communicate()[0] now = int(time.time()) try: temp = float(output) print(str(now) + ' TEMPER ' + str(temp)) except: # bad data, ignore it raise SystemExit(1) ntpsec-1.1.0+dfsg1/contrib/make-leap-seconds.py0000755000175000017500000000363413252364117021140 0ustar rlaagerrlaager#!/usr/bin/env python """\ make-leap-seconds.py - make leap second file for testing Optional args are date of leap second: YYYY-MM-DD and expiration date of file. Defaults are start of tomorrow (UTC), and 28 days after the leap. "Start of tomorow" is as soon as possible for testing. """ # SPDX-License-Identifier: BSD-2-Clause from __future__ import print_function, division import datetime import sha import sys import time JAN_1970 = 2208988800 # convert Unix/POSIX epoch to NTP epoch epoch = datetime.datetime.utcfromtimestamp(0) args = sys.argv[1:] leap = time.time() days = int(leap/86400) leap = (days+1)*86400 if len(args) > 0: leapdate = datetime.datetime.strptime(args[0], "%Y-%m-%d") leap = (leapdate - epoch).total_seconds() leap = int(leap) args = args[1:] expire = leap + 28*86400 if len(args) > 0: expiredate = datetime.datetime.strptime(args[0], "%Y-%m-%d") expire = (expiredate - epoch).total_seconds() expire = int(expire) args = args[1:] leap_txt = time.asctime(time.gmtime(leap)) leap = str(leap+JAN_1970) expire_txt = time.asctime(time.gmtime(expire)) expire = str(expire+JAN_1970) update = int(time.time()) update_txt = time.asctime(time.gmtime(update)) update = str(update+JAN_1970) tai = "40" # hardwired # File format # # # is comment # #$ xxx Update Date # #@ xxx Expiration Date # #h SHA1 hash of payload # # #$ 3676924800 # #@ 3707596800 # 2272060800 10 # 1 Jan 1972 # #h dacf2c42 2c4765d6 3c797af8 2cf630eb 699c8c67 # # All dates use NTP epoch of 1900-01-01 sha1 = sha.new() print("%s %s # %s" % (leap, tai, leap_txt)) sha1.update(leap) sha1.update(tai) print("#@ %s # %s" % (expire, expire_txt)) sha1.update(expire) print("#$ %s # %s" % (update, update_txt)) sha1.update(update) digest = sha1.hexdigest() print("#h %s %s %s %s %s" % (digest[0:8], digest[8:16], digest[16:24], digest[24:32], digest[32:40])) # end ntpsec-1.1.0+dfsg1/contrib/smartctl-temp-log0000755000175000017500000000434413252364117020573 0ustar rlaagerrlaager#!/usr/bin/env python # coding: utf-8 """\ Usage: smartctl-temp-log [device] Reads 'smartctl -a device' for temperature data. If a device is not specified on the commline line then /dev/sda is used. Writes the temperature found to stdout. Each line contains the Unix time in seconds since the epoch, the identifier, and the temperature in Celsius. Before you can use this utility smartctl must be installed and configured. See their documentation for that procedure. Sample log from a typical hard disk. 1471573103 SMART 37.000 Field 1: Unix time in seconds since the star of the epoch Field 2: Log source (SMART) Field 3: temperature in degrees C Sample crontab usage: # take and log disk temp every 5 minutes */5 * * * * /usr/local/sbin/smart-temp-log >> /var/log/ntpstats/temps Note, many distributions put smartctl in /usr/sbin, and do not put /usr/sbin in the PATH of programs executed by crontab. Not all hard drives support SMART. Not all of SMART drives are supported by smartctl. Not all smartctl compatible drives report temperature. Not all reported temperatures are valid. This file may only be useful as a template. The way to read your disk temperatures will be hardware specific. """ import subprocess import sys import time # check for device on command line, otherwise use /dev/sda device = '/dev/sda' if 1 < len(sys.argv): # process device device = sys.argv[1] try: # sadly subprocess.check_output() is not in Python 2.6 proc = subprocess.Popen(["smartctl", "-a", device], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) output = proc.communicate()[0] except subprocess.CalledProcessError as e: sys.stderr.write("ERROR: 'smartctl -a %s' failed\n" % device) sys.stderr.write(e.output) sys.stderr.write("Return code: %s\n" % e.returncode) raise SystemExit(2) except OSError as e: sys.stderr.write("ERROR: Could not start smartctl: %s\n" % e.strerror) raise SystemExit(2) lines = output.split('\n') line = '' for line in lines: if line.startswith('194 '): now = int(time.time()) temp = line.split()[9] sys.stdout.write('%d %s %s\n' % (now, device, temp)) ntpsec-1.1.0+dfsg1/contrib/zone-temp-log0000755000175000017500000000177013252364117017715 0ustar rlaagerrlaager#!/usr/bin/env python # coding: utf-8 """\ Usage: zone-temper-log Reads /sys/class/thermal/thermal_zone*/temp to find the CPU temperature. Writes all temperatures found to stdout on one line, preceded by the Unix UTC time in seconds. Sample log: 1471582083 ZONE0 56.92 1471582084 ZONE0 57.458 1471582085 ZONE0 56.92 1471582086 ZONE0 56.92 Field 1: Unix UTC time in seconds Field 2: Log source (ZONE) Field 3: CPU Temperature ntpviz will read all file temps* by default. Do not step on temps used by temper-temp-log. Sample crontab usage: # take and log CPU temp every 5 minutes */5 * * * * /usr/local/sbin/zone-temp-log >> /var/log/ntpstats/tempsz """ from __future__ import print_function import time now = int(time.time()) zone = 0 while True: try: f = open('/sys/class/thermal/thermal_zone{0}/temp'.format(zone), 'r') except: break for line in f: temp = float(line) / 1000 print('{0} ZONE{1} {2}'.format(now, zone, temp)) f.close() zone = zone + 1 ntpsec-1.1.0+dfsg1/contrib/ntpheat0000755000175000017500000000542513252364117016664 0ustar rlaagerrlaager#!/usr/bin/env python # # generate some heat! # # Wrap your RasPi in bubble wrap. Then run makeheat in the background. # It will try to stabilize the CPU temperature at 60C. # sometimes one copy of ntpheat can use 100% of one CPU and # still not heat up your RasPi as much as you want. The temptation # is to add more insulation to your RasPi, but then it will overshoot # your target temperature if your load factor goes high. # # The solution is to run more than one copy of ntpheat. This is # easy to do with the -c option. # # to run 3 copies of ntpheat; ntpheat -c 3 import argparse import hashlib import os import sys import time try: import ntp.util except ImportError as e: sys.stderr.write("ntpheat: can't find Python NTP modules. " "-- check PYTHONPATH.\n%s\n" % e) sys.exit(1) # Work with argvars parser = argparse.ArgumentParser(description="make heat") parser.add_argument('-c', '--copies', default=[1], dest='copies', help="Number of copies to run. Default is 1", nargs=1, type=int) parser.add_argument('-t', '--temp', default=[65.0], dest='target_temp', help="Temperature to hold. Default is 65.0", nargs=1, type=float) parser.add_argument('-w', '--wait', default=[0.001], dest='wait', help="Set delay time in seconds, default is 0.1", nargs=1, type=float) parser.add_argument('-V', '--version', action="version", version="ntpheat %s" % ntp.util.stdversion()) args = parser.parse_args() args.copies[0] -= 1 while args.copies[0]: args.copies[0] -= 1 pid = os.fork() if pid: # I am the fork break zone0 = '/sys/class/thermal/thermal_zone0/temp' cnt = 0 m = hashlib.md5() temp = 0 max_cnt = args.wait[0] * 200000 # on a RasPi 3 the temp steps seem to be about 0.537 to 0.539C temp_gate = args.target_temp[0] while True: # on a RasPi 3, 200,000 of the m.update() can be one second delta = temp_gate - temp if 0 < delta: # heat it up m.update("Nobody inspects the spammish repetition") else: cnt = max_cnt # cools off slower than it heats up. # undocumented Python 'feature', no sleep less than 1 milli Sec sleep = args.wait[0] * 10.0 * -delta if 0.001 > sleep: sleep = 0.001 time.sleep(sleep) cnt += 1 # read the temperature every max_cnt if max_cnt < cnt: cnt = 0 zone_data = open(zone0, 'r') for line in zone_data: temp = float(line) / 1000 zone_data.close() ntpsec-1.1.0+dfsg1/contrib/README0000644000175000017500000000342313252364117016147 0ustar rlaagerrlaagerThe following tools are not production-ready. They are included only as conveniences, examples or rudimentary starting points for other development efforts. ntpconfigtest: Check your linux configuration for the proper entries. ntpheat is a program to exercise a CPU until the CPU reachs a certain temperature. The idea is to get the temperature around the system oscillator to be near the zero TC point. Tested on RasPi wrapped in bubble wrap. Formerly known as makeheat. ntpheatusb is a program to control an external USB relay to maintain a stable temperature. See the blog post "More Heat" for details: https://blog.ntpsec.org/2017/03/21/More_Heat.html The next 4 tools cpu-temp-log, smartctl-temp-log, temper-temp-log and zone-temp-log have been replaced by ntplogtemp. Those programs will stay in contrib for a while to give users a chance to migrate. cpu-temp-log is a tool to use the output of 'sensors -u' and write the motherboard temperatures to stdout. Useful to create a log that can be used by 'ntpviz --local-temps' smartctl-temp-log for hard drives. It is a tool to read a hard drive's SMART data to get the disk temperature and write the temperature to stdout. Useful to create a log that can be used by 'ntpviz --local-temps' temper-temp-log for TEMPer USB thermometer. Useful for logging room temperature. This reads the thermometer using the temper-python command line utility and writes the temperatures to stdout. Useful to create a log that can be used by 'ntpviz --local-temps' zone-temp-log reads /sys/class/thermal/thermal_zone*/temp to find the CPU temperature. Writes all temperatures found to stdout on one line, preceded by the Unix UTC time in seconds. This is useful on any Linux system that supports the /sys/class/thermal/thermal_zone*/temp interface. ntpsec-1.1.0+dfsg1/contrib/cpu-temp-log0000755000175000017500000000352513252364117017531 0ustar rlaagerrlaager#!/usr/bin/env python # coding: utf-8 """\ Usage: cpu-temper-log Reads 'sensors -u' for temperature data. Writes all temperatures found to stdout. One temp per line, preceded by the unix UTC time in seconds, and an identifier. Before you can use this utility lm_sensors must be installed and configured. See their documentation for that procedure. How many temperatures, which temeratures and in which order will depend on your lm_sensors configuration and your motherboard. Sample log from an Supermicro Quad Core Xeon: 1471573103 LM0 37.000 1471573103 LM1 35.000 1471573103 LM2 31.000 1471573103 LM3 31.000 1471573103 LM4 30.000 1471573104 LM0 37.000 Field 1: unix time in seconds since the star of the epoch Field 2: Log source (LM) Field 3: temperature in degrees C Sample crontab usage: # take and log cpu temp every 5 mins */5 * * * * /usr/local/sbin/cpu-temp-log >> /var/log/ntpstats/temps This file may only be useful as a template. The way to read your system temperatures will be hardware specific. """ from __future__ import print_function, division import sys import re import time import subprocess try: # sadly subprocess.check_output() is not in Python 2.6 proc = subprocess.Popen(["sensors", "-u"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) output = proc.communicate()[0] except: print("Unable to run 'sensors -u'") exit(0) lines = output.split('\n') # this regex matches temperature output lines from 'sensors -u' pat = re.compile('^\s+temp\d+_input:\s+([\d\.]+).*$') now = int(time.time()) line = '' index = 0 for line in lines: match = pat.match(line) if match and match.group(1): temp = match.group(1) sys.stdout.write('%d LM%d %s\n' % (now, index, temp)) index += 1 ntpsec-1.1.0+dfsg1/contrib/logrotate-ntpd0000644000175000017500000000100213252364117020144 0ustar rlaagerrlaager# Adjust for your needs and install as /etc/logrotate.d/ntpd # This rotates monthly and kicks ntpd to start using the new file. # It inherits dateext and compress from /etc/logrotate.conf # If you are using SELinux, you will have to do something like: # cd /etc/logrotate.d/ # sudo chcon --ref syslog ntpd # That may not work if selinux is not active. ???? # ls -Z will show the results /var/log/ntpstats/ntpd.log { monthly rotate 9999 postrotate /usr/bin/killall -HUP ntpd endscript } ntpsec-1.1.0+dfsg1/contrib/ntpheatusb0000755000175000017500000002023013252364117017365 0ustar rlaagerrlaager#!/usr/bin/env python # # generate some heat! # # Wrap your RasPi in a closed box. Get a usbrelay1 to control # an incadescent light bulb in the box. Heat to 45C. profit. # # This code depends on the program 'usbrelay' to manage your usbrelay # connected device. # Get it here: git@github.com:darrylb123/usbrelay.git # Update the usbrelay_on/off variables below with your device ID # # This code depends on the program 'temper-poll' to read the box temp # from an attached TEMPer device. # Get it here: git@github.com:padelt/temper-python.git # # ntpheatusb will use a lot less CPU than ntpheat, and more directly # heats the XTAL rather than the CPU. # # Avoid the desire to decrease the wait time. The relay clocks twice # per cycle, and those cycles add up. Minimize wear on your relay. # # Try the simple P controller (the default) before trying the PID controller. # The PID controller may take some fiddling with the constants to get # it working better than the simple P controller. # # More info on the blog post: https://blog.ntpsec.org/2017/03/21/More_Heat.html from __future__ import print_function, division import argparse import atexit import subprocess import sys import time try: import ntp.util except ImportError as e: sys.stderr.write("ntpheatusb: can't find Python NTP modules. " "-- check PYTHONPATH.\n%s\n" % e) sys.exit(1) def run_binary(cmd): """\ Run a binary Return its output if good, None if bad """ try: # sadly subprocess.check_output() is not in Python 2.6 # so use Popen() # this throws an exception if not found proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) output = proc.communicate()[0].split("\n") if proc.returncode: # non-zero return code, fail print("Return %s" % proc.returncode) return None except ImportError as e: sys.stderr.write("Unable to run %s binary\n" % cmd[0]) print(cmd) sys.stderr.write("%s\n" % e) return None return output class PID: """\ Discrete PID control """ def __init__(self, setpoint=0.0, P=2.0, I=0.0, D=1.0, Derivator=0, Integrator=0, Integrator_max=100, Integrator_min=-100): self.Kp = P self.Ki = I self.Kd = D self.Derivator = Derivator self.Integrator = Integrator self.Integrator_max = Integrator_max self.Integrator_min = Integrator_min self.set_point = setpoint self.error = 0.0 def __repr__(self): return "D_value=%s, I_value=%s" % (self.D_value, self.I_value) def setPoint(self, set_point): """ Initialize the setpoint of PID """ self.set_point = set_point def update(self, current_value): """ Calculate PID output value for given reference input and feedback """ self.error = self.set_point - current_value self.P_value = self.Kp * self.error self.D_value = self.Kd * (self.error - self.Derivator) self.Derivator = self.error self.Integrator = self.Integrator + self.error if self.Integrator > self.Integrator_max: self.Integrator = self.Integrator_max elif self.Integrator < self.Integrator_min: self.Integrator = self.Integrator_min self.I_value = self.Integrator * self.Ki PID = self.P_value + self.I_value + self.D_value return PID # Work with argvars parser = argparse.ArgumentParser(description="make heat with USB relay") parser.add_argument('-p', '--pid', action="store_true", dest='pid', help="Use PID controller instead of simple P controller.") parser.add_argument('-s', '--step', action="store_true", dest='step', help="Step up 1C every 2 hours for 20 hours, " "then back down.") parser.add_argument('-t', '--temp', default=[45.0], dest='target_temp', help="Temperature to hold in C. Default is 45.0C", nargs=1, type=float) parser.add_argument('-w', '--wait', default=[60], dest='wait', help="Set delay time in seconds, default is 60", nargs=1, type=float) parser.add_argument('-v', '--verbose', action="store_true", dest='verbose', help="be verbose") parser.add_argument('-V', '--version', action="version", version="ntpheatusb %s" % ntp.util.stdversion()) args = parser.parse_args() zone0 = '/sys/class/thermal/thermal_zone0/temp' cnt = 0 temp_gate = args.target_temp[0] start_temp_gate = temp_gate period = float(args.wait[0]) # you will need to personalize these to your relay ID: usbrelay_on = ['usbrelay', '959BI_1=1'] usbrelay_off = ['usbrelay', '959BI_1=0'] # turn off the usb relay on exit. no need to cook.. atexit.register(run_binary, usbrelay_off) # to adjust the PID variables # set I and D to zero # # increase P until you get a small overshoot, and mostly damped response, # to a large temp change # # then increase I until the persistent error goes away. # # if the temp oscillates then increase D # pid = PID(setpoint=temp_gate, P=35.0, I=10.0, D=10.0) start_time = time.time() step_time = start_time step = 0 start_time = time.time() step_time = start_time step = 0 try: while True: if args.step: now = time.time() if 7200 < (now - step_time): # time to step step_time = now step += 1 if 0 <= step: # step up temp_gate += 1.0 else: # step down temp_gate -= 1.0 if 9 < step: step = -11 pid.setPoint(temp_gate) # only one device can read the TEMPer at a time # collisions will happen, so retry a few times for attempt in range(0, 3): # grab the needed output fail = False output = run_binary(["temper-poll", "-c"]) try: # make sure it is a temperature temp = float(output[0]) break except: # bad data, try aagin fail = True if args.verbose: print("temper read failed: %s" % output) if fail: # give up print("temper fatal error") sys.exit(1) # the +20 is to create an 80/20 band around the setpoint p_val = pid.update(temp) + 20 p_val1 = p_val if p_val > 100: p_val1 = 100 elif p_val < 0: p_val1 = 0 if temp > temp_gate: perc_t = 0 elif temp < (temp_gate - 3): perc_t = 100 else: perc_t = ((temp_gate - temp) / 3) * 100 if args.pid: # use PID controller perc = p_val1 else: # use P controller perc = perc_t if perc > 0: output = run_binary(usbrelay_on) time_on = period * (perc / 100) time.sleep(time_on) else: time_on = 0 time_off = period - time_on output = run_binary(usbrelay_off) if args.verbose: print("Temp %s, perc %.2f, p_val %s/%s" % (temp, perc_t, p_val, p_val1)) print("on %s, off %s" % (time_on, time_off)) print(pid) if 0 < time_off: time.sleep(time_off) except KeyboardInterrupt: print("\nCaught ^C") run_binary(usbrelay_off) sys.exit(1) # can't fall through ??? except IOError: # exception catcher # turn off the heat! run_binary(usbrelay_off) print("\nCaught exception, exiting\n") sys.exit(1) ntpsec-1.1.0+dfsg1/contrib/ntpconfigtest0000755000175000017500000000235113252364117020103 0ustar rlaagerrlaager#!/usr/bin/env python # On Linux systems, read /proc/config.gz and check for the # needed kernel options. from __future__ import print_function import gzip import sys config_file_name = '/proc/config.gz' try: with gzip.open(config_file_name, 'rb') as f: file_content = f.read() except: print("ERROR: can not read your %s" % (config_file_name)) sys.exit(1) tests = ( ('CONFIG_CPU_FREQ_GOV_PERFORMANCE', 'For best performance'), ('CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE', 'For best performance'), ('CONFIG_SECCOMP', 'For extra security'), ('CONFIG_THERMAL', 'For thermal monitoring'), ('CONFIG_PPS', 'Needed for PPS support'), ('CONFIG_PPS_CLIENT_LDISC', 'For PPS support on serial lines'), ('CONFIG_PPS_CLIENT_GPIO', 'For PPS support on GPIO lines'), ('CONFIG_USB_SERIAL_GARMIN', 'For Garmin USB GPS support'), ('CONFIG_USB_SERIAL_PL2303', 'For PL2303 USB GPS support'), ) # when this figures out how to test for platform, add these # for Pi: # CONFIG_THERMAL_BCM2835 for test in tests: print("Checking: %s, %s: " % (test[0], test[1]), end='') index = file_content.find("\n%s" % (test[0])) if 0 <= index: print("OK") else: print("Missing") sys.exit(0) ntpsec-1.1.0+dfsg1/docs/0000775000175000017500000000000013252650651014560 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/docs/driver_arbiter.txt0000644000175000017500000001303713252364117020325 0ustar rlaagerrlaager= Arbiter 1088A/B GPS Receiver = == Synopsis == ["verse",subs="normal"] Name: arbiter Reference ID: GPS Driver ID: GPS_ARBITER Serial Port: /dev/gps__u__; 9600bps 8N1 Features: tty_clk == Deprecation warning == This refclock is deprecated and obsolete. The NTPsec maintainers plan to remove it in a future release. If you have a requirement for it, please make this known to us. This driver reports only two-digit years, and is thus reliant on the system clock to be near correct before samples will be processed properly. You will not be able to use it to run autonomously, nor will it reliably recover from a trashed or zeroed system clock. == Description == This driver supports the Arbiter 1088A/B Satellite Controlled Clock. The claimed accuracy of this clock is 100 ns relative to the PPS output when receiving four or more satellites. The receiver should be configured before starting the NTP daemon, in order to establish reliable position and operating conditions. It does not initiate surveying or hold mode. For use with NTP, the daylight savings time feature should be disables (+D0+ command) and the broadcast mode set to operate in UTC (+BU+ command). The timecode format supported by this driver is selected by the poll sequence +B5+, which initiates a line in the following format to be repeated once per second until turned off by the +B0+ command. Format +B5+ (24 ASCII printing characters): ------------------------------------------------------- i yy ddd hh:mm:ss.000bbb on-time = i = synchronization flag (' ' = locked, '?' = unlocked) yy = year of century ddd = day of year hh:mm:ss = hours, minutes, seconds .000 = fraction of second (not used) bbb = tailing spaces for fill ------------------------------------------------------- The alarm condition is indicated by a \'?' at i, which indicates the receiver is not synchronized. In normal operation, a line consisting of the timecode followed by the time quality character (TQ) followed by the receiver status string (SR) is written to the clockstats file. The time quality character is encoded in IEEE P1344 standard: Format +TQ+ (IEEE P1344 estimated worst-case time quality) ----------------------------------------- 0 clock locked, maximum accuracy F clock failure, time not reliable 4 clock unlocked, accuracy < 1 us 5 clock unlocked, accuracy < 10 us 6 clock unlocked, accuracy < 100 us 7 clock unlocked, accuracy < 1 ms 8 clock unlocked, accuracy < 10 ms 9 clock unlocked, accuracy < 100 ms A clock unlocked, accuracy < 1 s B clock unlocked, accuracy < 10 s ----------------------------------------- The status string is encoded as follows: Format +SR+ (25 ASCII printing characters) ---------------------------------------------- V=vv S=ss T=t P=pdop E=ee vv = satellites visible ss = relative signal strength t = satellites tracked pdop = position dilution of precision (meters) ee = hardware errors ---------------------------------------------- A three-stage median filter is used to reduce jitter and provide a dispersion measure. The driver makes no attempt to correct for the intrinsic jitter of the radio itself. == Monitor Data == When enabled by the +flag4+ option, an additional line containing the latitude, longitude, elevation and optional deviation data is written to the +clockstats+ file. The deviation data operates with an external pulse-per-second (PPS) input, such as a cesium oscillator or another radio clock. The PPS input should be connected to the B event channel and the radio initialized for deviation data on that channel. The deviation data consists of the mean offset and standard deviation of the external PPS signal relative the GPS signal, both in microseconds over the last 16 seconds. == Driver Options == +unit+ 'number':: The driver unit number, defaulting to 0. Used as a distinguishing suffix in the driver device name. +time1+ 'time':: Specifies the time offset calibration factor, in seconds and fraction, with default 0.0. +time2+ 'time':: Not used by this driver. +stratum+ 'number':: Specifies the driver stratum, in decimal from 0 to 15, with default 0. +refid+ 'string':: Specifies the driver reference identifier, an ASCII string from one to four characters, with default +GPS+. +flag1 {0 | 1}+:: Not used by this driver. +flag2 {0 | 1}+:: Not used by this driver. +flag3 {0 | 1}+:: Not used by this driver. +flag4 {0 | 1}+:: Enable verbose +clockstats+ recording if set. +subtype+:: Not used by this driver. +mode+:: Not used by this driver. +path+ 'filename':: Overrides the default device path. +ppspath+ 'filename':: Not used by this driver. +baud+ 'number':: Overrides the default baud rate. == Configuration Example == ---------------------------------------------------------------------------- refclock arbiter ---------------------------------------------------------------------------- == Author == David L. Mills == Additional Information == link:refclock.html[Reference Clock Drivers] == Known bugs == If your Arbiter has firmware made more than 1024 weeks (19 years and 36 weeks) in the past, its internal date counter may wrap around and generate spurious timestamps. This problem is fundamental and cannot be compensated for in code without relying on the accuracy of the local system clock, which is exactly what an NTP implementation may not do without risking perverse failure modes (especially at startup time). The only sure remedy is to be sure the Arbiter's firmware has been updated within the current GPS era. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/leap.txt0000644000175000017500000001023613252364117016241 0ustar rlaagerrlaager= Leap Second Processing = Every six months the International Earth Rotation Service (IERS) issues a bulletin announcing if there will be a leap second in the Universal Coordinated Time (UTC) timescale. Historically, only insertions have occurred, about every eighteen months. The changes have been at the end of the last day of June or December; but, in principle, it could happen at the end of any month. While these bulletins are available on the Internet at https://www.iers.org[www.iers.org], advance notice of leap seconds is also available in signals broadcast from national time and frequency stations, in GPS signals and in telephone modem services. Many, but not all, reference clocks recognize these signals and many, but not all, drivers for them can decode the signals and set the leap bits in the timecode accordingly. This means that many, but not all, primary servers can pass on these bits in the NTP packet heard to dependent secondary servers and clients. Secondary servers can pass these bits to their dependents and so on throughout the NTP subnet. A leap second is inserted following second 59 of the last minute of the day and becomes second 60 of that day. A leap second is deleted by omitting second 59 of the last minute of the day, although this has never happened and is highly unlikely to happen in future. So far as is known, there are no provisions in the Unix libraries to account for this occasion other than to affect the conversion of an NTP datestamp or timestamp to conventional civil time. When an update is received from a reference clock or downstratum server, the leap bits are inspected for all survivors of the cluster algorithm. If the number of survivors showing a leap bit is greater than half the total number of survivors, a pending leap condition exists until the end of the current month. When no means are available to determine the leap bits from a reference clock or downstratum server, a leapseconds file can be downloaded from the https://www.ietf.org/timezones/data/leap-seconds.list[IETF] and installed using the link:miscopt.html#leapfile[leapfile] command. The file includes a list of historic leap seconds and the NTP time of insertion. It is parsed by the +ntpd+ daemon at startup and the latest leap time saved for future reference. Each time the clock is set, the current time is compared with the last leap time. If the current time is later than the last leap time, nothing further is done. If earlier, the leap timer is initialized with the time in seconds until the leap time and counts down from there. When the leap timer is less than one month, a pending leap condition exists until the end of the current month. If the leapseconds file is present, the leap bits for reference clocks and downstratum servers are ignored. If the precision time kernel support is available and enabled, at the beginning of the day of the leap event, the leap bits are set by the Unix +ntp_adjtime()+ system call to arm the kernel for the leap at the end of the day. The kernel automatically inserts one second exactly at the time of the leap, after which the leap bits are turned off. If the kernel support is not availed or disabled, the leap is implemented as a crude hack by setting the clock back one second using the Unix +clock_settime()+ system call, which effectively repeats the last second. Note however that in any case setting the time backwards by one second does not actually set the system clock backwards, but effectively stalls the clock for one second. These points are expanded in the white paper {millshome}leap.html[The NTP Timescale and Leap Seconds]. If the leap timer is less than one day, the leap bits are set for dependent servers and clients. As an additional feature when the NIST leap seconds file is installed, it is possible to determine the number of leap seconds inserted in UTC since UTC began on 1 January 1972. This represents the offset between International Atomic Time (TAI) and UTC. If the precision time kernel modifications are available and enabled, the TAI offset is available to application programs using the +antipasti()+ system call. You may also wish to read the page on link:leapsmear.html[leap smearing]. include::includes/footer.txt[] ''''' ntpsec-1.1.0+dfsg1/docs/discipline.txt0000644000175000017500000002435613252364117017453 0ustar rlaagerrlaager= Clock Discipline Algorithm = == Table of Contents == * link:#intro[General Overview] * link:#pll[Phase-Lock Loop Operations] * link:#loop[Loop Dynamics] * link:#house[Clock Initialization and Management] ''''' [[intro]] == General Overview == At the heart of the NTP specification and reference implementation is the clock discipline algorithm, which is best described as an adaptive parameter, hybrid phase/frequency-lock feedback loop. It is an intricately crafted algorithm that automatically adapts for optimum performance while minimizing network overhead. Operation is in two modes, phase-lock loop (PLL), which is used at poll intervals below the Allan intercept, by default 2048 s, and frequency-lock loop (FLL), which is used above that. image::pic/discipline.gif[align="center"] Figure 1. Clock Discipline Algorithm [[pll]] == Clock Discipline Operations == A block diagram of the clock discipline is shown in Figure 1. The timestamp of a reference clock or remote server is compared with the timestamp of the system clock, represented as a variable frequency oscillator (VFO), to produce a raw offset sample _V~d~_. Offset samples are processed by the clock filter to produce a filtered update _V~s~_. The loop filter implements a type-2 proportional-integrator controller (PIC). The PIC can minimize errors in both time and frequency using predictors _x_ and _y_, respectively. The clock adjust process samples these predictors once each second for the daemon discipline or once each tick interrupt for the kernel discipline to produce the system clock update _V~c~_. In PLL mode the frequency predictor is an integral of the offset over past updates, while the phase predictor is the offset amortized over time in order to avoid setting the clock backward. In FLL mode the phase predictor is not used, while the frequency predictor is similar to the NIST _lockclock_ algorithm. In this algorithm, the frequency predictor is computed as a fraction of the current offset divided by the time since the last update in order to minimize the offset at the next update. The discipline response in PLL mode is determined by the _time constant_, which results in a "stiffness" depending on the jitter of the available sources and the wander of the system clock oscillator. The scaled time constant is also used as the poll interval described on the link:poll.html[Poll Program] page. However, in NTP symmetric mode, each peer manages its own poll interval and the two might not be the same. In such cases either peer uses the minimum of its own poll interval and that of the other peer, which is included in the NTP packet header. [[loop]] == Loop Dynamics == It is necessary to verify that the clock discipline algorithm is stable and satisfies the Nyquist criterion, which requires that the sampling rate be at least twice the bandwidth. In this case the bandwidth can be approximated by the reciprocal of the time constant. In the NTP specification and reference implementation, time constants and poll intervals are expressed as exponents of 2. By construction, the time constant exponent is five times the poll interval exponent. Thus, the default poll exponent of 6 corresponds to a poll interval of 64 s and a time constant of 2048 s. A change in the poll interval changes the time constant by a corresponding amount.. The Nyquist criterion requires the sample interval to be not more than half the time constant or 1024 s. The clock filter guarantees at least one sample in eight poll intervals, so the sample interval is not more than 512 s. This would be described as oversampling by a factor of two. Finally, the PLL parameters have been chosen for a damping factor of 2, which results in a much faster risetime than with critical damping, but results in modest overshoot of 6 percent. It is important to understand how the dynamics of the PLL are affected by the time constant and poll interval. At the default poll interval of 64 s and a step offset change of 100 ms, the time response crosses zero in about 50 min and overshoots about 6 ms, as per design. Ordinarily, a step correction would causes a temporary frequency surge of about 5 PPM, which along with the overshoot slowly dissipates over a few hours. However, the clock state machine used with the discipline algorithm avoids this transient at startup. It does this using a previously saved frequency file, if present, or by measuring the oscillator frequency, if not. It then quickly amortizes the residual offset at startup without affecting the oscillator frequency. In this way the offset error is less than 0.5 ms within 5 min, if the file is present, and within 10 min if not. See the link:clock.html[Clock State Machine] page for further details. Since the PLL is linear, the response with different offset step amplitudes and poll intervals has the same characteristic shape, but scaled differently in amplitude and time. The response scales exactly with step amplitude, so that the response to a 10-ms step has the same shape as at 64 s, but with amplitude compressed by one-tenth. The response scales exactly with poll interval, so that response at a poll interval of 8 s has the same shape as at 64 s, but with time compressed by one-eighth. The optimum time constant, and thus the poll interval, depends on the network time jitter and the oscillator frequency wander. Errors due to jitter decrease as the time constant increases, while errors due to wander decrease as the time constant decreases. For typical Internet paths, the two error characteristics intersect at a point called the _Allan intercept_, which represents the optimum time constant. With a compromise Allan intercept of 2048 s, the optimum poll interval is about 64 s, which corresponds to a compromise poll exponent of 6. For fast LANs with modern computers, the Allan intercept is somewhat lower at around 512 s, so a compromise poll exponent of 4 (16 s) is appropriate. An intricate, heuristic algorithm is used to manage the actual poll interval within a specified range. Details are on the link:poll.html[Poll Program] page. In the NTPv4 specification and reference implementation a state machine is used to manage the system clock under exceptional conditions, as when the daemon is first started or when encountering severe network congestion. In extreme cases not likely to be encountered in normal operation, the system time can be stepped forward or backward more than 128 ms. Further details are on the link:clock.html[Clock State Machine] page. [[house]] == Clock Initialization and Management == If left running continuously, an NTP client on a fast LAN in a home or office environment can maintain synchronization nominally within one millisecond. When the ambient temperature variations are less than a degree Celsius, the clock oscillator frequency is disciplined to within one part per million (PPM), even when the clock oscillator native frequency offset is 100 PPM or more. For laptops and portable devices when the power is turned off, the battery backup clock offset error can increase as much as one second per day. When power is restored after several hours or days, the clock offset and oscillator frequency errors must be resolved by the clock discipline algorithm, but this can take several hours without specific provisions. The provisions described in this section insure that, in all but pathological situations, the startup transient is suppressed to within nominal levels in no more than five minutes after a warm start or ten minutes after a cold start. Following is a summary of these provisions. A detailed discussion of these provisions is on the link:clock.html[Clock State Machine] page. The reference implementation measures the clock oscillator frequency and updates a frequency file at intervals of one hour or more, depending on the measured frequency wander. This design is intended to minimize write cycles in NVRAM that might be used in a laptop or portable device. In a warm start, the frequency is initialized from this file, which avoids a possibly lengthy convergence time. In a cold start when no frequency file is available, the reference implementation first measures the oscillator frequency over a five-min interval. This generally results in a residual frequency error less than 1 PPM. The measurement interval can be changed using the +stepout+ option of the link:miscopt.html#tinker[+tinker+] command. In order to reduce the clock offset error at restart, the reference implementation next disables oscillator frequency discipline and enables clock offset discipline with a small time constant. This is designed to quickly reduce the clock offset error without causing a frequency surge. This configuration is continued for an interval of five-min, after which the clock offset error is usually no more than a millisecond. The measurement interval can be changed using the +stepout+ option of the link:miscopt.html#tinker[+tinker+] command. Another concern at restart is the time necessary for the select and cluster algorithms to refine and validate the initial clock offset estimate. Normally, this takes several updates before setting the system clock. As the default minimum poll interval in most configurations is about one minute, it can take several minutes before setting the system clock. The +iburst+ option of the link:assoc.html#burst[+server+] command changes the behavior at restart and is recommended for client/server configurations. When this option is enabled, the client sends a volley of six requests at intervals of two seconds. This usually insures a reliable estimate is available in about ten seconds before setting the clock. Once this initial volley is complete, the procedures described above are executed. As a result of the above considerations, when a backup source, such as the local clock driver, ACTS modem driver or orphan mode is included in the system configuration, it may happen that one or more of them are selectable before one or more of the regular sources are selectable. When backup sources are included in the configuration, the reference implementation waits an interval of several minutes without regular sources before switching to backup sources. This is generally enough to avoid startup transients due to premature switching to backup sources. The interval can be changed using the +orphanwait+ option of the link:miscopt.html#tos[+tos+] command. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntplogtemp.txt0000644000175000017500000000126213252364117017510 0ustar rlaagerrlaager= ntplogtemp - log system temperature data for use by ntpviz = //FIXME: Image duplicates the one used for panda.txt [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/panda.gif[]| {millshome}pictures.html[from the DARPA Atlantic SATNET Program (1979)] P is for patching. |============================== == Related Links == include::includes/manual.txt[] == Table of Contents == * link:#_synopsis[SYNOPSIS] * link:#_description[DESCRIPTION] * link:#_options[OPTIONS] * link:#_usage[USAGE] * link:#_exit_status[EXIT STATUS] * link:#_authors[AUTHORS] ''''' include::includes/ntplogtemp-body.txt[] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/cluster.txt0000644000175000017500000000751213252364117017004 0ustar rlaagerrlaager= Clock Cluster Algorithm = The clock cluster algorithm processes the truechimers produced by the clock select algorithm to produce a list of _survivors_. These survivors are used by the mitigation algorithms to discipline the system clock. The cluster algorithm operates in a series of rounds, where at each round the truechimer furthest from the offset centroid is pruned from the population. The rounds are continued until a specified termination condition is met. This page discusses the algorithm in detail. First, the truechimer associations are saved on an unordered list with each candidate entry identified with index __i__ (__i__ = 1, ..., __n)__, where __n__ is the number of candidates. Let θ(__i__), be the offset and λ(__i__) be the root distance of the __i__th entry. Recall that the root distance is equal to the root dispersion plus half the root delay. For the __i__th candidate on the list, a statistic called the _select jitter_ relative to the __i__th candidate is calculated as follows. Let __d~i~__(__j__) = |θ(__j__) − θ(__i__)| λ(__i__), where θ(__i)__ is the peer offset of the __i__th entry and θ(__j__) is the peer offset of the __j__th entry, both produced by the clock filter algorithm. The metric used by the cluster algorithm is the select jitter φ~S~(__i__) computed as the root mean square (RMS) of the __d~i~__(__j__) as __j__ ranges from 1 to __n__. For the purpose of notation in the example to follow, let φ~R~(__i__) be the peer jitter computed by the clock filter algorithm for the __i__th candidate. The object at each round is to prune the entry with the largest metric until the termination condition is met. Note that the select jitter must be recomputed at each round, but the peer jitter does not change. At each round the remaining entries on the list represent the survivors of that round. If the candidate to be pruned is preemptable and the number of candidates is greater than the _maxclock_ _threshold_, the association is demobilized. This is useful in the schemes described on the link:discover.html[Automatic Server Discovery Schemes] page. The maxclock threshold default is 10, but it can be changed using the +maxclock+ option of the link:miscopt.html#tos[+tos+] command. Further pruning is subject to the following termination conditions, but no associations will be automatically demobilized. The termination condition has two parts. First, if the number of survivors is not greater than the _minclock_ _threshold_ set by the +minclock+ option of the link:miscopt.html#tos[+tos+] command, the pruning process terminates. The _minclock_ default is 3, but can be changed to fit special conditions, as described on the link:prefer.html[Mitigation Rules and the prefer Keyword] page. image:pic/flt7.gif[] Figure 1. Cluster Algorithm The second termination condition is more intricate. Figure 1 shows a round where a candidate of (a) is pruned to yield the candidates of (b). Let φ~max~ be the maximum select jitter and φ~min~ be the minimum peer jitter over all candidates on the list. In (a), candidate 1 has the highest select jitter, so φ~_max_~ = φ~S~(1). Candidate 4 has the lowest peer jitter, so φ~min~ = φ~R~(4). Since φ~max~ > φ~min~, select jitter dominates peer jitter, the algorithm prunes candidate 1. In (b), φ~max~ = φ~S~(3) and φ~min~ =φ~R~(4). Since φ~max~ < φ~min~, pruning additional candidates does not reduce select jitter, the algorithm terminates with candidates 2, 3 and 4 as survivors. The survivor list is passed on to the mitigation algorithms, which combine the survivors, select a system peer, and compute the system statistics passed on to dependent clients. Note the use of root distance λ as a weight factor at each round in the clock cluster algorithm. This is to favor the survivors with the lowest root distance and thus the smallest maximum error. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/oncore-shmem.txt0000644000175000017500000002364713252364117017726 0ustar rlaagerrlaager= Motorola ONCORE - The Shared Memory Interface = == Introduction == In NMEA mode, the Oncore GPS receiver provides the user with the same information as other GPS receivers. In BINARY mode, it can provide a lot of additional information. In particular, you can ask for satellite positions, satellite health, signal levels, the ephemeris and the almanac, and you can set many operational parameters. In the case of the VP, you can get the pseudorange corrections necessary to act as a DGPS base station, and you can see the raw satellite data messages themselves. When using the Oncore GPS receiver with NTP, this additional information is usually not available since the receiver is only talking to the oncore driver in _ntpd_. To make this information available for use in other programs, (say graphic displays of satellites positions, plots of SA, etc.), a shared memory interface (SHMEM) has been added to the refclock_oncore driver on those operating systems that support shared memory. To make use of this information you will need an Oncore Reference Manual for the Oncore GPS receiver that you have. The Manual for the VP only exists as a paper document, the UT+/GT+/M12 manuals are available as a pdf documents at http://www.synergy-gps.com/index.php?option=com_content&task=view&id=35&Itemid=60[Synergy] . This interface was written by Poul-Henning Kamp (phk@FreeBSD.org), and modified by Reg Clemens (reg@dwf.com). The interface is known to work in FreeBSD, Linux, and Solaris. == Activating the Interface == Although the Shared Memory Interface will be compiled into the Oncore driver on those systems where Shared Memory is supported, to activate this interface you must include a *STATUS* or *SHMEM* line in the +/etc/ntp.oncore+ data file that looks like ---------------------------- STATUS < file_name > or SHMEM < file_name > ---------------------------- Thus a line like -------------------------------------- SHMEM /var/adm/ntpstats/ONCORE -------------------------------------- would be acceptable. This file name will be used to access the Shared Memory. In addition, one the two keywords *Posn2D* and *Posn3D* can be added to see @@Ea records containing the 2D or 3D position of the station (see below). Thus to activate the interface, and see 3D positions, something like -------------------------------------- SHMEM /var/adm/ntpstats/ONCORE Posn3D -------------------------------------- would be required. == Storage of Messages in Shared Memory == With the shared memory interface, the oncore driver (refclock_oncore) allocates space for all of the messages that it is configured to receive, and then puts each message in the appropriate slot in shared memory as it arrives from the receiver. Since there is no easy way for a client program to know when the shared memory has been updated, a sequence number is associated with each message, and is incremented when a new message arrives. With the sequence number it is easy to check through the shared memory segment for messages that have changed. The Oncore binary messages are kept in their full length, as described in the Reference manual, that is everything from the @@ prefix through the . The data starts at location ONE of SHMEM (NOT location ZERO). The messages are stacked in a series of variable length structures, that look like ---------------------------------------- struct message { unsigned int length; unsigned char sequence; unsigned char message[length]; } ---------------------------------------- if something like that were legal. That is, there are two bytes (caution, these may NOT be aligned with word boundaries, so the field needs to be treated as a pair of unsigned char), that contains the length of the next message. This is followed by a unsigned char sequence number, that is incremented whenever a new message of this type is received. This is followed by \'length' characters of the actual message. The next structure starts immediately following the last char of the previous message (no alignment). Thus, each structure starts a distance of \'length+3' from the previous structure. Following the last structure, is an unsigned int containing a zero length to indicate the end of the data. The messages are recognized by reading the headers in the data itself, viz @@Ea or whatever. There are two special cases. (1) The almanac takes a total of 34 submessages all starting with @@Cb. + 35 slots are allocated in shared memory. Each @@Cb message is initially placed in the first of these locations, and then later it is moved to the appropriate location for that submessage. The submessages can be distinguished by the first two characters following the @@Cb header, and new data is received only when the almanac changes. (2) The @@Ea message contains the calculated location of the antenna, and is received once per second. However, when in timekeeping mode, the receiver is normally put in 0D mode, with the position fixed, to get better accuracy. In 0D mode no position is calculated. When the SHMEM option is active, and if one of *Posn2D* or *Posn3D* is specified, one @@Ea record is hijacked each 15s, and the receiver is put back in 2D/3D mode so the current location can be determined (for position determination, or for tracking SA). The timekeeping code is careful NOT to use the time associated with this (less accurate) 2D/3D tick in its timekeeping functions. Following the initial @@Ea message are 3 additional slots for a total of four. As with the almanac, the first gets filled each time a new record becomes available, later in the code, the message is distributed to the appropriate slot. The additional slots are for messages containing 0D, 2D and 3D positions. These messages can be distinguished by different bit patterns in the last data byte of the record. == Opening the Shared Memory File == The shared memory segment is accessed through a file name given on a *SHMEM* card in the +/etc/ntp.oncore+ input file. The following code could be used to open the Shared Memory Segment: --------------------------------------------------------------------------------- char *Buf, *file; int size, fd; struct stat statbuf; file = "/var/adm/ntpstats/ONCORE"; /* the file name on my ACCESS card */ if ((fd=open(file, O_RDONLY)) < 0) { fprintf(stderr, "Can't open %s\n", file); exit(1); } if (stat(file, &statbuf) < 0) { fprintf(stderr, "Can't stat %s\n", file); exit(1); } size = statbuf.st_size; if ((Buf=mmap(0, size, PROT_READ, MAP_SHARED, fd, (off_t) 0)) < 0) { fprintf(stderr, "MMAP failed\n"); exit(1); } --------------------------------------------------------------------------------- == Accessing the data == The following code shows how to get to the individual records. -------------------------------------------------------------------------------------------- void oncore_msg_Ea(), oncore_msg_As(), oncore_msg_Bb(); struct Msg { char c[5]; unsigned int seq; void (*go_to)(uchar *); }; struct Msg Hdr[] = { {"@@Bb", 0, &oncore_msg_Bb}, {"@@Ea", 0, &oncore_msg_Ea}, {"@@As", 0, &oncore_msg_As}}; void read_data() { int i, j, k, n, iseq, jseq; uchar *cp, *cp1; for(cp=Buf+1; (n = 256*(*cp) + *(cp+1)) != 0; cp+=(n+3)) { for (k=0; k < sizeof(Hdr)/sizeof(Hdr[0]); k++) { if (!strncmp(cp+3, Hdr[k].c, 4)) { /* am I interested? */ iseq = *(cp+2); jseq = Hdr[k].seq; Hdr[k].seq = iseq; if (iseq > jseq) { /* has it changed? */ /* verify checksum */ j = 0; cp1 = cp+3; /* points to start of oncore response */ for (i=2; i < n-3; i++) j ^= cp1[i]; if (j == cp1[n-3]) { /* good checksum */ Hdr[k].go_to(cp1); } else { fprintf(stderr, "Bad Checksum for %s\n", Hdr[k].c); break; } } } } if (!strncmp(cp+3, "@@Ea", 4)) cp += 3*(n+3); if (!strncmp(cp+3, "@@Cb", 4)) cp += 34*(n+3); } } oncore_msg_Bb(uchar *buf) { /* process Bb messages */ } oncore_msg_Ea(uchar *buf) { /* process Ea messages */ } oncore_msg_As(uchar *buf) { /* process As messages */ } -------------------------------------------------------------------------------------------- The structure Hdr contains the Identifying string for each of the messages that we want to examine, and the name of a program to call when a new message of that type is arrives. The loop can be run every few seconds to check for new data. == Examples == There are two complete examples available. The first plots satellite positions and the station position as affected by SA, and keeps track of the mean station position, so you can run it for periods of days to get a better station position. The second shows the effective horizon by watching satellite tracks. The examples will be found in the GNU-zipped tar file https://www.eecis.udel.edu/~ntp/ntp_spool/software/OncorePlot.tar.gz . Try the new interface, enjoy. ''''' Reg.Clemens (reg@dwf.com), Poul-Henning Kamp (phk@FreeBSD.org) ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/driver_nmea.txt0000644000175000017500000002670613252364117017624 0ustar rlaagerrlaager= Generic NMEA GPS Receiver = == Synopsis == ["verse",subs="normal"] Name: nmea Reference ID: GPS Serial Port: /dev/gps__u__; 4800-115200bps, 8N1 Serial Port: /dev/gpspps__u__; for PPS Features: +tty_clk+ == Description == This driver supports GPS receivers with the +$GPRMC+, +$GPGLL+, +$GPGGA+, +$GPZDA+ and +$GPZDG+ NMEA sentences by default. Note that Accord's custom NMEA sentence +$GPZDG+ reports using the GPS timescale, while the rest of the sentences report UTC. The difference between the two is a whole number of seconds which increases with each leap second insertion in UTC. To avoid problems mixing UTC and GPS timescales, the driver disables processing of UTC sentences once +$GPZDG+ is received. The driver expects the receiver to be set up to transmit at least one supported sentence every second. The accuracy depends on the receiver used. Inexpensive GPS models are available with a claimed PPS signal accuracy of 1 μs or better relative to the broadcast signal. However, in most cases the actual accuracy is limited by the precision of the timecode and the latencies of the serial interface and operating system. If the operating system supports PPSAPI (http://www.ietf.org/rfc/rfc2783.txt[RFC 2783]), the +flag1 1+ option enables its use. If there is a /dev/gpspps__u__ it is tried first for PPS, before /dev/gps__u__. The various GPS sentences that this driver recognises look like this (others are quietly ignored): .Accepted NMEA sentences [options="header"] |============================================================================= |Sentence |Vendor |$GPRMC,UTC,POS_STAT,LAT,LAT_REF,LON,LON_REF,SPD,HDG,DATE,MAG_VAR,MAG_REF*CS| |$GPGLL,LAT,LAT_REF,LON,LON_REF,UTC,POS_STAT*CS| |$GPGGA,UTC,LAT,LAT_REF,LON,LON_REF,FIX_MODE,SAT_USED,HDOP,ALT,ALT_UNIT,GEO,G_UNIT,D_AGE,D_REF*CS| |$GPZDA,UTC,DD,MM,YYYY,TH,TM,*CS| |$GPZDG,GPSTIME,DD,MM,YYYY,AA.BB,V*CS|Accord |============================================================================= .NMEA data items [cols="15%,85%",options="header"] [options="header"] |============================================================================= |Symbol | Meaning and Format |UTC |Time of day on UTC timescale. Hours, minutes and seconds [fraction (opt.)]. (hhmmss[.fff]) |POS_STAT|Position status. (A = Data valid, V = Data invalid) |LAT |Latitude (llll.ll) |LAT_REF |Latitude direction. (N = North, S = South) |LON |Longitude (yyyyy.yy) |LON_REF |Longitude direction (E = East, W = West) |SPD |Speed over ground. (knots) (x.x) |HDG |Heading/track made good (degrees True) (x.x) |DATE |Date (ddmmyy) |MAG_VAR |Magnetic variation (degrees) (x.x) |MAG_REF |Magnetic variation (E = East, W = West) |FIX_MODE|Position Fix Mode (0 = Invalid, >0 = Valid) |SAT_USED|Number of Satellites used in solution |HDOP |Horizontal Dilution of Precision |ALT |Antenna Altitude |ALT_UNIT|Altitude Units (Metres/Feet) |GEO |Geoid/Elipsoid separation |G_UNIT |Geoid units (M/F) |D_AGE |Age of last DGPS Fix |D_REF |Reference ID of DGPS station |GPSTIME |Time of day on GPS timescale. Hours, minutes and seconds [fraction (opt.)]. (hhmmss[.f]) |DD |Day of the month (1-31) |MM |Month of the year (1-12) |YYYY |Year |AA.BB |Denotes the signal strength (should be < 05.00) |V |GPS sync status '0' => INVALID time, '1' => accuracy of +/- 20ms, '2' => accuracy of +/- 100ns |CS |Checksum ||Sentence terminator. |============================================================================= == The \'mode' byte == Specific GPS sentences and bitrates may be selected by setting bits of the \'mode' in the refclock configuration line: .mode byte bits and bit groups [cols="10%,10%,10%,70%",options="header"] |============================================================================= |Bit | Decimal | Hex | Meaning |0 |1 |1 |process +$GPRMC+ |1 |2 |2 |process +$GPGGA+ |2 |4 |4 |process +$GPGLL+ |3 |8 |8 |process +$GPZDA+ or +$GPZDG+ |7 |128 |0x80 | Write the sub-second fraction of the receive time stamp to the clockstat file for all recognized NMEA sentences. This can be used to get a useful value for fudge time2 *Caveat:* This will fill your clockstat file rather fast. Use it only temporarily to get the numbers for the NMEA sentence of your choice. |8 |256 |0x100|process +$PGRMF+ |9-15 | |0xFE00 |reserved - leave 0 |16 |65536 |0x10000 | Append extra statistics to the clockstats line. Details below. |============================================================================= The default (mode 0) is to process all supported sentences at a linespeed of 9600 bps, which results in the first one received and recognized in each cycle being used. If only specific sentences should be recognized, then the mode byte must be chosen to enable only the selected ones. Multiple sentences may be selected by adding their mode bit values, but of those enabled still only the first received sentence in a cycle will be used. Using more than one sentence per cycle is impossible, because * there is only the link:#fudgetime2[time2] option available to compensate for transmission delays but every sentence would need a different one and * using more than one sentence per cycle overstuffs the internal data filters. The driver uses 9600 bits per second by default, unless NTPsec was built in Classic strict-compatibility mode, in which case the old default of 4800bps is used. Faster bitrates can be selected using the +baud+ option. *Caveat:* Using higher line speeds does not necessarily increase the precision of the timing device. 9600 baud is useful to accommodate an amount of data that does not fit into a 1-second cycle at 4800 bps; some GPSes (especially when emitting skyview sentences) emit more than this. Any device that is exclusively used for time synchronisation purposes should be configured to transmit the relevant data only, e.g. one +$GPRMC+ or +$GPZDA+ per second; this will slightly reduce jitter by reducing variation in transmission times. == Monitor Data == The last GPS sentence that is accepted or rejected is written to the clockstats file and available with +ntpq -c clockvar+. (Logging the rejected sentences lets you see/debug why they were rejected.) Filtered sentences are not logged. If the 0x10000 mode bit is on and clockstats is enabled, several extra counters will be appended to the NMEA sentence that gets logged. For example: ---------------------------------------------------------------------------- 56299 76876.691 nmea(0) $GPGGA,212116.000,3726.0785,N,12212.2605,W,1,05,2.0,17.0,M,-25.7,M,,0000*5C 228 64 0 0 64 0 ---------------------------------------------------------------------------- .Clockstats [cols="10%,20%,70%",options="header"] |============================================================================= |Column|Sample |Meaning |1 |56299 |MJD |2 |76876.691 |Time of day in seconds |3 |NMEA(0) |Driver type and unit. |4 |$GPGGA,...0*5C |NMEA Sentence |5 |228 |Number of sentences received |6 |64 |Number of sentences accepted and used for timekeeping |7 |0 |Number of sentences rejected because they were marked invalid (poor signal) |8 |0 |Number of sentences rejected because of bad checksum or invalid date/time |9 |64 |Number of sentences filtered by mode bits or same second |10 |0 |Number of PPS pulses used, overrides NMEA sentences |============================================================================= The clock identification in field 3 is normally the driver type and unit, but if your ntpd was built in strict Classic compatibility mode it will be a magic clock address expressing the same information in a more opaque way. Sentences like $GPGSV that don't contain the time will get counted in the total but otherwise ignored. http://support.ntp.org/bin/view/Support/ConfiguringNMEARefclocks[Configuring NMEA Refclocks] might give further useful hints for specific hardware devices that exhibit strange or curious behaviour. Note that it describes NTP Classic and the old refclock configuration syntax, and assumes the old default speed of 4800 baud. To make a specific setting, select the corresponding decimal values from the mode byte table, add them all together and enter the resulting decimal value into the clock configuration line. == Setting up the Garmin GPS-25XL == Switch off all output with by sending it the following string. ------------------- "$PGRMO,,2" ------------------- Now switch only $GPRMC on by sending it the following string. ------------------------ "$PGRMO,GPRMC,1" ------------------------ On some systems the PPS signal isn't switched on by default. It can be switched on by sending the following string. ----------------------------- "$PGRMC,,,,,,,,,,,,2" ----------------------------- == Driver Options == +unit+ 'number':: The driver unit number, defaulting to 0. Used as a distinguishing suffix in the driver device name. +time1+ 'time':: Specifies the PPS time offset calibration factor, in seconds and fraction, with default 0.0. +time2+ 'time':: Specifies the serial end of line time offset calibration factor, in seconds and fraction, with default 0.0. +stratum+ 'number':: Specifies the driver stratum, in decimal from 0 to 15, with default 0. +refid+ 'string':: Specifies the driver reference identifier, an ASCII string from one to four characters, with default +GPS+. +flag1 {0 | 1}+:: Disable PPS signal processing if 0 (default); enable PPS signal processing if 1. +flag2 {0 | 1}+:: If PPS signal processing is enabled, capture the pulse on the rising edge if 0 (default); capture on the falling edge if 1. +flag3 {0 | 1}+:: If PPS signal processing is enabled, use the +ntpd+ clock discipline if 0 (default); use the kernel discipline if 1. +flag4 {0 | 1}+:: Obscures location in timecode: 0 for disable (default), 1 for enable. +subtype+:: Not used by this driver. +mode+:: Used to control GPS sentence selection and logging. +path+ 'filename':: Overrides the default device path. +ppspath+ 'filename':: Overrides the default PPS-device path. +baud+ 'number':: Overrides the default baud rate. == Configuration Example == ------------------------------------------------------------------------------ refclock nmea baud 19200 # All sentences from /dev/gps0 at 19200 baud refclock nmea path /dev/ttyUSB0 # All sentences from /dev/ttyUSB0 at 9600 baud ------------------------------------------------------------------------------ link:refclock.html[Reference Clock Drivers] ''''' == Known bugs == If your GPS has firmware made more than 1024 weeks (19 years and 36 weeks) in the past, its internal date counter will almost certainly wrap around and generate spurious timestamps. Beginning in January 2018, newer GPSes may have a longer wraparound (8192 weeks, or 157 years and 28 weeks) but it is not safe to bet that any given receiver will have firmware updated to take advantage of this. This problem is fundamental and cannot be compensated for in code without relying on the accuracy of the local system clock, which is exactly what an NTP implementation may not do without risking perverse failure modes (especially at startup time). The only remedy is not to use ancient GPS hardware. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/accopt.txt0000644000175000017500000000107413252364117016571 0ustar rlaagerrlaager= Access Control Commands and Options = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/pogo6.gif[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] The skunk watches for intruders and sprays. |============================== == Related Links == include::includes/accopt.txt[] ''''' == Commands and Options == Unless noted otherwise, further information about these commands is on the link:access.html[Access Control Support] page. include::includes/access-commands.txt[] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntp_config.txt0000644000175000017500000003042213252364117017445 0ustar rlaagerrlaager= Configuration File Definition (Advanced) = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/pogo7.gif[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] Racoon is shooting configuration bugs. |============================== == Table of Contents == * link:#synopsis[Synopsis] * link:#files[Files] * link:#high-level[High-Level Description] * link:#detailed[Detailed Description] * link:#guidelines[Guidelines for Adding Configuration Commands] ''''' [[synopsis]] == Synopsis == The NTP configuration process is driven by a phrase-structure grammar which is used to specify the format of the configuration commands and the actions needed to build an abstract syntax tree (AST). The grammar is fed to a parser generator (Bison) which produces a parser for the configuration file. The generated parser is used to parse an NTP configuration file and check it for syntax and semantic errors. The result of the parse is an AST, which contains a representation of the various commands and options. This AST is then traversed to set up the NTP daemon to the correct configuration. This document is intended for developers who wish to modify the configuration code and/or add configuration commands and options. It contains a description of the files used in the configuration process as well as guidelines on how to construct them. [[files]] == Files == A brief description of the files used by the configuration code is given below: [cols="<40%,<60%",options="header"] |============================================================================== | File | Description | *ntp_config.y* | This file is a Bison source file that contains the phrase-structure grammar and the actions that need to be performed to generate an AST. |*ntp_config.c* | This file contains the major chunk of the configuration code. It contains all the functions that are called for building the AST as well as the functions that are needed for traversing the AST. | *ntp_config.h* | This file is the header file for *ntp_config.c*. It mainly contains the structure definitions needed to build the AST. | *ntp_scanner.c* | This file contains the code for a simple lexical analyzer. This file is directly included into the *ntp_config.c* file since this code is only used by the configuration code. The most important function in this file is +yylex+, which is called by the generated parser to get the next token on the input line. | *ntp_config.tab.c* | This file is generated by Bison from the *ntp_config.y* file. This file is also included directly into the configuration code. |============================================================================== [[high-level]] == High-Level Description == A high-level description of the configuration process showing where all the files fit in is given below: image:pic/description.jpg[JPEG] The scanner reads in an NTP configuration file and converts it into tokens. The Bison generated parser reads these tokens and converts them into an AST. The AST traverser consists of a set of functions that configure parts of NTP on the basis of what is on the tree. A more detailed description of these parts and the files used is given below: [[detailed]] == Detailed Description == *ntp_scanner.c*:: This file contains the scanner. The scanner is a small program that converts an input NTP configuration file into a set of *tokens* that correspond to *lexemes* in the input. Lexemes are strings in the input, delimited by whitespace and/or special characters. Tokens are basically unique integers that represent these lexemes. A different token is generated for each reserved word and special character in the input. There are two main functions in the public interface of this file: +int yylex+():: This function is called +yylex+ for historical reasons; +lex+ is a program that takes a set of regular expressions and generates a scanner that returns tokens corresponding to those regular expressions. The name of the generated function is called +yylex+. We aren't using +lex+ because it requires linking against an external library and we didn't want to increase the compile-time requirements of NTP. + History lessons aside, this function basically checks to see if the next input character is a special character as defined in the array +char special_char[]+. (The function +int is_special(char ch)+, can be used for this.) If yes, the special character is returned as the token. If not, a set of characters is read until the next whitespace or special character is encountered. This set of characters forms the lexeme; +yylex+ then checks whether this lexeme is an integer, a double, an IP address or a reserved word. If yes, the corresponding token is returned. If not, a token for a string is returned as the default token. + +struct state *create_keyword_scanner(struct key_tok *keyword_list)+:: This function takes a list of (_keyword, token_) pairs and converts them into a tree that can recognize the keywords (reserved words). Every time the scanner reads a lexeme, it compares it against the list of reserved words. If it finds a match, it returns the corresponding token for that keyword. *ntp_config.y*:: This file is structured as a standard Bison file and consists of three main parts, separated by +%%+: 1. The prologue and bison declarations: This section contains a list of the terminal symbols, the non-terminal symbols and the types of these symbols. 2. The rules section: This section contains a description of the actual phrase-structure rules that are used to parse the configuration commands. Each rule consists of a left-hand side (LHS), a right-hand side (RHS) and an optional action. As is standard with phrase-structure grammars, the LHS consists of a single non-terminal symbol. The RHS can contain both terminal and non-terminal symbols, while the optional action can consist of any arbitrary C code. 3. The epilogue: This section is left empty on purpose. It is traditionally used to code the support functions needed to build the ASTs. Since we have moved all the support functions to *ntp_config.c*, this section is left empty. == Prologue and Bison Declarations == All the terminal symbols (also known as tokens) have to be declared in the prologue section. Note that terminals and non-terminals may have values associated with them and these values have types. (More on this later). An unnamed union has to be declared with all the possible types at the start of the prologue section. For example, we declare the following union at the start of the *ntp_config.y* file: ----------------------- %union { char *String; double Double; int Integer; void *VoidPtr; queue *Queue; struct attr_val *Attr_val; struct address_node *Address_node; struct setvar_node *Set_var; /* Simulation types */ server_info *Sim_server; script_info *Sim_script; } ----------------------- Some tokens may not have any types. For example, tokens that correspond to reserved words do not usually have types as they simply indicate that a reserved word has been read in the input file. Such tokens have to be declared as follows: ----------------------- %token T_Discard %token T_Dispersion -------------------- Other tokens do have types. For example, a +T_Double+ token is returned by the scanner whenever it sees a floating-point double in the configuration file. The value associated with the token is the actual number that was read in the configuration file and its type (after conversion) is double. Hence, the token +T_Double+ will have to be declared as follows in the prologue of *ntp_config.y* file: ----------------------- %token T_Double ----------------------- Note that the declaration given in the angled brackets is not +double+ but +Double+, which is the name of the variable given in the +%union {}+ declaration above. Finally, non-terminal symbols may also have values associated with them, which have types. This is because Bison allows non-terminal symbols to have actions associated with them. Actions may be thought of as small functions which get executed whenever the RHS of a non-terminal is detected. The return values of these functions are the values associated with the non-terminals. The types of the non-terminals are specified with a +%type+ declaration as shown below: ----------------------- %type address_list %type boolean ----------------------- The +%type+ declaration may be omitted for non-terminals that do not return any value and do not have type information associated with them. == The Rules Section == The rule section only consists of phrase-structure grammar rules. Each rule typically has the following format: ----------------------- LHS : RHS [{ Actions }] ; ----------------------- where LHS consists of a single non-terminal symbol and the RHS consists of one or more terminal and non-terminal symbols. The +Actions+ are optional and may consist of any number of arbitrary C statements. Note that Bison can only process LALR(1) grammars, which imposes additional restrictions on the kind of rules that can be specified. Examples of rules are shown below: ----------------------- orphan_mode_command : T_Tos tos_option_list { append_queue(my_config.orphan_cmds, $2); } ; tos_option_list : tos_option_list tos_option { $$ = enqueue($1, $2); } | tos_option { $$ = enqueue_in_new_queue($1); } ; ----------------------- The +$n+ notation, where +n+ is an integer, is used to refer to the value of a terminal or non-terminal symbol. All terminals and non-terminal symbols within a particular rule are numbered (starting from 1) according to the order in which they appear within the RHS of a rule. +$$+ is used to refer to the value of the LHS terminal symbol - it is used to return a value for the non-terminal symbol specified in the LHS of the rule. == Invoking Bison == Bison needs to be invoked in order to convert the *ntp_config.y* file into a C source file. To invoke Bison, simply enter the command: ----------------------- bison ntp_config.y ----------------------- at the command prompt. If no errors are detected, an *ntp_config.tab.c* file will be generated by default. This generated file can be directly included into the *ntp_config.c* file. If Bison report shift-reduce errors or reduce-reduce errors, it means that the grammar specified using the rules in not LALR(1). To debug such a grammar, invoke Bison with a +-v+ switch, as shown below. This will generate a *ntp_config.output* file, which will contain a description of the generated state machine, together with a list of states that have shift-reduce/reduce-reduce conflicts. You can then change the rules to remove such conflicts. ----------------------- bison -v ntp_config.y ----------------------- For more information, refer to the http://www.gnu.org/software/bison/manual/[Bison manual]. *ntp_config.c* This file contains the major chunk of the configuration code including all the support functions needed for building and traversing the ASTs. As such, most of the functions in this file can be divided into two groups: 1. Functions that have a +create_+ prefix. These functions are used to build a node of the AST. 2. Functions that have a +config_+ prefix. These functions are used to traverse the AST and configure NTP according to the nodes present on the tree. [[guidelines]] == Guidelines for Adding Configuration Commands == The following steps may be used to add a new configuration command to the NTP reference implementation: 1. Write phrase-structure grammar rules for the syntax of the new command. Add these rules to the rules section of the *ntp_config.y* file. 2. Write the action to be performed on recognizing the rules. These actions will be used to build the AST. 3. If new reserved words are needed, add these to the +struct key_tok ntp_keywords[]+ structure in the *keyword-gen.c* file. This will allow the scanner to recognize these reserved words and generate the desired tokens on recognizing them. 4. Specify the types of all the terminals and non-terminal symbols in the prologue section of the *ntp_config.c* file. 5. Write a function with a +config_+ prefix that will be executed for this new command. Make sure this function is called in the +config_ntpd()+ function. ''''' mailto:skamboj@udel.edu[Sachin Kamboj] include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/mode6.txt0000644000175000017500000005137713252364117016345 0ustar rlaagerrlaager= Mode 6 Protocol = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/which-way-is.up.jpg[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] Keeping control of the situation. |============================== == Related Links == include::includes/hand.txt[] == Table of Contents == * link:#intro[Introduction] * link:#packet[Mode 6 packet structure] * link:#varlists[Variable-Value Lists] * link:#requests[Mode 6 Requests] * link:#authentication[Authentication] ''''' [[intro]] == Introduction == This page describes the Mode 6 protocol used to get status information from a running ntpd and configure some of its behaviors on the fly. The protocol is normally used by the 'ntpq' and 'ntpmon' program distributed with the suite. It is fully documented here so that other clients can be written. (Note that the most efficient way to write a new client is to use the Python Mode 6 libraries included in the distribution. Both 'ntpq' and 'ntpmon' use these.) [[packet]] == Mode 6 packet structure == The protocol uses UDP packets transmitted and received over port 123. They use the same structure (header, plus extension, plus optional MAC) as time synchronization messages, but the layout and semantics of the header fields are different. They are distinguished from time synchronization packets by their Mode field, which has the value 6 (110). .Mode 6 packet structure ------------------------------------------------------------------------- 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |LI | VN |Mode |R|E|M| Opcode | Sequence | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Status | Association ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Offset | Count | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | . . . Payload (variable length) . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key Identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | MAC (128) | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ------------------------------------------------------------------------- In mode 6 packets, the leap indicator (LI) is ignored and normally zero. The version (VN) is the NTP protocol major version, currently 4. Mode is 6. The following field interpretations are specific to mode 6: |===================================================================== | Response bit | 1 in a response, 0 in a request | Error bit | 1 in an error response, 0 otherwise | More | 1 if payload is continued in next packet, 0 otherwise | Sequence | Sequence number for multi-packet reassembly | Status | System status word | Association ID | Association ID of peer, or 0 for the ntpd host | Offset | Octet offset of this fragment in the response | Count | Octet count of fragment payload |===================================================================== Requests to ntpd are single UDP packets; ntpd expects them to be padded to a 4-octet boundary. Responses may be multiple UDP packets; they may arrive out of order, and the client is responsible for reassembling the payloads. All multibyte numeric fields are interpreted as big-endian 2s-complement integers. [[varlists]] == Variable-Value Lists == Several requests and responses (in fact, all but one) use a common textual payload format consisting of a comma-separated list of items. An item may be textual (ASCII) variable name, or a textual variable name followed by an equals sign followed by a textual value. Following any comma the format may insert a newline; these are not significant to the meaning of the payload, but are placed so that if the payload is dumped to an 80-character terminal window the lines will be folded in a way convenient for visibility. Values may be decimal numeric literals, decimal float literals, hex numeric literals led with "0x", binary literals consisting of exactly two of digits 0 and 1, NTP date stamps, or string literals enclosed in double quotes. NTP date stamps are represented by hexadecimal fixed-point literals led with "0x", distinguished from hexadecimal integers by the embedded radix point ("."). They represent seconds (and fractional seconds) since the epoch of the current NTP era. NTP era zero began at 00:00 of January 1st 1900. String literals never contain newlines or other control characters. One quirk of the format is that a bare variable name without a following "=" may be interpreted in context as an instruction to set a string-valued variable to the empty string. Textual responses may end with padding NULs; clients should ignore these. In RFC 5234 ABNF: ----------------------------------------------------------- varlist = item [itemtail] LF *%x00 itemtail = "," [1LF] item [itemtail] item = name / name "=" value value = 1*DIGIT / 1*DIGIT "." 1*DIGIT / "0x" 1*HEXDIG / 2BIT / quoted-string quoted-string = %x22 *(%21 | %x23-7E) %x22 ----------------------------------------------------------- [[requests]] == Mode 6 Requests == Request-response types are distinguished by operation codes. The table below lists them all. The "Auth?" column tells whether a request requires authentication from the client. [options="header"] |===================================================================== |Name |Value | Auth? | Use |CTL_OP_READSTAT | 1 | No | read system or peer status |CTL_OP_READVAR | 2 | No | read system or peer variables |CTL_OP_WRITEVAR | 3 | Yes | write variables |CTL_OP_READCLOCK | 4 | No | read clock variables |CTL_OP_WRITECLOCK | 5 | - | write clock variables |CTL_OP_SETTRAP | 6 | - | set trap address (obsolete, unused) |CTL_OP_ASYNCMSG | 7 | - | asynchronous message (unused) |CTL_OP_CONFIGURE | 8 | Yes | runtime configuration |CTL_OP_READ_MRU | 10 | No | retrieve MRU (mrulist) |CTL_OP_READ_ORDLIST_A | 11 | Yes | ordered list req. auth. |CTL_OP_REQ_NONCE | 12 | No | request a client nonce |CTL_OP_UNSETTRAP | 31 | - | unset trap (obsolete, unused) |===================================================================== The CTL_OP_SETTRAP and CTL_OP_UNSETTRAP opcodes relate to an obsolete notification facility supported in some older versions of the software. CTL_OP_WRITECLOCK is unimplemented and will throw a permission error. CTL_OP_ASYNCMSG is reserved for expansion. The remaining opcodes are as follows: === CTL_OP_READSTAT === This requests ntpd to ship up a list of association IDs and status words for all peers currently associated with the ntpd instance. It does not require authentication. The normal use case for this request is to discover the current list of associations preparatory to querying peer variables for each association. There is no request payload. The response payload is not textual. It consists of a sequence of pairs, each consisting of 16-bit association ID followed by 16-bit status word, both unsigned in network (big-endian) byte order. There is no padding in the response. The number of IDs is implicitly given by the payload length in octets, divided by 4. Interpretation of the peer status word is described link:decode.html#peer[here]. === CTL_OP_READVAR === This requests ntpd to ship up a list of peer variable settings for an association specified by ID, or system variables if the ID is zero. It does not require authentication. The request payload may be empty or a textual variable list of variables to be reported in the response. An empty request payload calls for a report on all known variables. The response payload is a textual varlist. === CTL_OP_WRITEVAR === Some system variable are defined as being settable from a mode 6 client. This request provides a general way to do that. It requires authentication. The request payload is a textual list of variable settings. Any variable name that is unknown or not settable immediately terminates processing of the payload. This request is only valid with an association ID of 0. There is no response payload. No system variables are currently settable. This may change in a future release. === CTL_OP_READCLOCK === This requests ntpd to ship up a list of peer variable settings for a reference clock association specified by ID. It does not require authentication. The request payload may be empty or a textual variable list of variables to be reported in the response. An empty request payload calls for a report on all known variables. The response payload is a textual varlist. === CTL_OP_CONFIGURE === This request is used to change the configuration of ntpd without restarting the daemon. It requires authentication. The request payload should be a line of text in the syntax of the ntp.conf configuration file. The response payload will begin with either an error message or the string "Config Succeeded", followed by a NUL. Note: Due to an implementation error, the response packet may and typically will contain trailing garbage. Implementations should expect this and truncate it at the first NUL. === CTL_OP_READ_MRU === This request is used to retrieve information about recent traffic between ntpd and its clients and peers; in NTP-speak this traffic summary is called the "MRU list", where MRU stands for "most recently used". It does not require authentication. //Keep this in sync with the big comment in ntpd/ntp_control.c, //from which it is derived. A finite and variable number of entries are retrieved per request, to avoid having responses with such large numbers of packets that socket buffers are overflowed and packets lost. The entries are retrieved oldest-first, taking into account that the MRU list will be changing between each request. We can expect to see duplicate entries for addresses updated in the MRU list during the fetch operation. In the end, the client can assemble a close approximation of the MRU list at the point in time the last response was sent by ntpd. The only difference is it may be longer, containing some number of oldest entries which have since been reclaimed. If necessary, the protocol could be extended to zap those from the client snapshot at the end, but so far that doesn't seem useful. To accommodate the changing MRU list, the starting point for requests after the first request is supplied as a series of last seen timestamps and associated addresses, the newest ones the client has received. As long as at least one of those entries hasn't been bumped to the head of the MRU list, ntpd can pick up at that point. Otherwise, the request is failed and it is up to ntpq to back up and provide the next newest entry's timestamps and addresses, conceivably backing up all the way to the starting point. The request payload is a textual varlist that must include some of the following variables and may include others: nonce:: Regurgitated nonce retrieved by the client previously using CTL_OP_REQ_NONCE, demonstrating ability to receive traffic sent to its address. frags:: Limit on datagrams (fragments) in response. Used by newer ntpq versions instead of limit= when retrieving multiple entries. 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. mincount:: (decimal) Return entries with packet count >= mincount. maxlstint:: (decimal) Return entries with lstint <= maxlstint. (lstint is now-time of most recent packet) laddr:: Return entries associated with the server's IP address given. No port specification is needed, and any supplied is ignored. recent:: Set the reporting start point to retrieve roughly a specified number of most recent entries 'Roughly' because the logic cannot anticipate update volume. Use this to volume-limit the response when you are monitoring something like a pool server with a very long MRU list. resall:: 0x-prefixed hex restrict bits, which must all be lit for an MRU entry to be included. Has precedence over any resany=. resany:: 0x-prefixed hex restrict bits, at least one of which must be list for an MRU entry to be included. last.0:: 0x-prefixed hex l_fp timestamp of newest entry which client previously received. addr.0:: text of newest entry's IP address and port, IPv6 addresses in bracketed form: [::]:123 last.1:: timestamp of 2nd newest entry client has. addr.1:: address of 2nd newest entry. More entries may follow; ntpq provides as many last/addr pairs as will fit in a single request packet, except for the first request in a MRU fetch operation. The response begins with a new nonce value to be used for any followup request. Following the nonce is the next newer entry than referred to by last.0 and addr.0, if the "0" entry has not been bumped to the front. If it has, the first entry returned will be the next entry newer than referred to by last.1 and addr.1, and so on. If none of the referenced entries remain unchanged, the request fails and ntpq backs up to the next earlier set of entries to resync. Except for the first response, each response begins with confirmation of the entry that precedes the first additional entry provided: last.older:: hex l_fp timestamp matching one of the input .last timestamps, which entry now precedes the response 0. entry in the MRU list. addr.older:: text of address corresponding to older.last. And in any case, a successful response contains sets of values comprising entries, with the oldest numbered 0 and incrementing from there: addr.#:: text of IPv4 or IPv6 address and port last.#:: hex l_fp timestamp of last receipt first.#:: hex l_fp timestamp of first receipt ct.#:: count of packets received mv.#:: mode and version rs.#:: restriction mask (RES_* bits) The client should accept the values in any order, and ignore .# values which it does not understand, to allow a smooth path to future changes without requiring a new opcode. To ensure this, ntpd occasionally issues a randomly-generated tag=value pair. All such noise tags are three letters long. Clients can rely on all *.0 values preceding any *.1 values, that is all values for a given index number are together in the response. The end of the response list is noted with one or two tag=value pairs. Unconditionally: now:: 0x-prefixed l_fp timestamp at the server marking the end of the operation. If any entries were returned, now= is followed by: last.newest:: hex l_fp identical to last.# of the prior entry. Portions of the response side of the protocol (specifically the last.older, addr.older, and last.newest attributes) can be ignored by a client that is willing to accumulate an entire set of MRU list fragments and then perform stale-record elimination of its own before displaying or passing on the report (that is, as opposed to incremental display with an attempt to suppress stale records on the fly). === CTL_OP_READ_ORDLIST_A === This request is used for two purposes: to retrieve restriction lists and to retrieve interface statistics. For the former use, the request payload should be the string "addr_restrictions"; for the latter case, the request payload should be "ifstats" or empty. Both uses require authentication. The response payload is, in both cases, a textual varlist. A response payload consists of a list of attribute stanzas. Each stanza consists of the attributes with tags of the form "name.#', with # being replaced by a zero-origin integer literal that is the index of the stanza. Attributes within each stanza are deliberately issued in a random order, and ntpd occasionally issues an attribute with a randomly-generated name and value. This is an attempt to prevent Mode 6 clients from making brittle assumptions about the inventory of attributes and their transmission order. Clients can rely on all *.0 values preceding any *.1 values, that is all values for a given index number are together in the response. In a reslist stanza, elicited by "addr_restrictions", the elements are as follows: addr.#:: Address to which the restriction applies. May be IPV4 or IPV6. Has no port suffix flags.#:: Space-separated list of flag names applying to the address. These flag names are the same as those used in the "restrict" directive of the configuration syntax. hits.#:: The number of times this rule has been matched. Not updated for default rules. mask.#:: Subnet mask qualifying the address to express a range. In an ifstats stanza, elicited by "ifstats" or an empty string, attributes are as follows: addr.#:: Address of the interface. May be IPV4 or IPV6. Has a port suffix. May be a wildcard; extreme cases are 0.0.0.0 and [::]. bcast.#:: Either a broadcast address associated with the interface or empty. en.#:: Integer literal. 1 if packets on this interface are processed, 0 if they are to be ignored. flags.#:: A hex literal that is a mask of flag bits on. Flag mask values are described in a following table. name.#:: The interface name, such as would occur in an ifconfig listing. pc.#:: Count of peers using this interface. rx.#:: Packet reception count. tl.#:: Last time-to-live specified on a send. tx.#:: Packet transmission count. txerr.#:: Packet transmission error count. up.#:: Uptime in seconds. .Interface flag bits in the flags.# attribute |========================================================================== |INT_UP | 0x001 | Interface is up |INT_PPP | 0x002 | Point-to-point interface |INT_LOOPBACK | 0x004 | the loopback interface |INT_BROADCAST | 0x008 | can broadcast out this interface |INT_MULTICAST | 0x010 | can multicast out this interface (not used) |INT_BCASTOPEN | 0x020 | broadcast receive socket is open |INT_MCASTOPEN | 0x040 | multicasting enabled (not used) |INT_WILDCARD | 0x080 | wildcard interface - usually skipped |INT_MCASTIF | 0x100 | bound directly to MCAST address |INT_PRIVACY | 0x200 | RFC 4941 IPv6 privacy address |INT_BCASTXMIT | 0x400 | socket setup to allow broadcasts |========================================================================== === CTL_OP_REQ_NONCE === This request is used to initialize an MRU-list conversation. It informs ntpd that the Mode 6 client is ready to receive. It does not require authentication. The request retrieves a nonce specific to this client, which will be played back to ntpd to demonstrate that the client is capable of receiving responses at the source IP address that requested the nonce, and is thereby unlikely to be forging the source address. This check prevents CTL_OP_READ_MRU from being used for flooding attacks. The request has no payload. The response will be a textual varlist containing one string-valued variable, "nonce". The value need not by interpreted by the client, only replayed as part of a following MRU-list request. Each nonce becomes invalid 16 seconds after the request for it was received by ntpd. While the issue time is encoded in the nonce, it is safer practice not to rely on the nonce format but instead to track the last nonce transmission time in your client and re-request based on that. [[auth]] == Authentication == Authenticated requests require a MAC (message authentication code) trailer following the payload data, if any. Such requests must be padded to an 8-octet boundary, with those bytes not included in the header count field. The contents of the MAC trailer consists of: 1. The 32-bit identifier of the signing key in network byte order. 2. A cryptographic hash of the following octet spans, in order. First, the password entered to use the signing key, then the request header fields, then the payload. The cryptographic hash is 16 octets for MD5 and 20 octets for SHA1. Longer digests don't work yet. 2018-Jan-06 FIXME: long digests == Compatibility Notes == The "recent" parameter of CTL_OP_READ_MRU is not supported in versions prior to NTPsec 0.9.6. Export of the count of control requests (ss_numctlreq) is new in NTPsec. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/leapsmear.txt0000644000175000017500000003116013252364117017270 0ustar rlaagerrlaager= Leap Second Smearing with NTP = By Martin Burnicki with some edits by Harlan Stenn The NTP software protocol and its reference implementation, ntpd, were originally designed to distribute UTC time over a network as accurately as possible. Unfortunately, leap seconds are scheduled to be inserted into or deleted from the UTC time scale in irregular intervals to keep the UTC time scale synchronized with the Earth rotation. Deletions haven't happened, yet, but insertions have happened over 30 times. The problem is that POSIX requires 86400 seconds in a day, and there is no prescribed way to handle leap seconds in POSIX. Whenever a leap second is to be handled ntpd either: * passes the leap second announcement down to the OS kernel (if the OS supports this) and the kernel handles the leap second automatically, or * applies the leap second correction itself. NTP servers also pass a leap second warning flag down to their clients via the normal NTP packet exchange, so clients also become aware of an approaching leap second, and can handle the leap second appropriately. == The Problem on Unix-like Systems == If a leap second is to be inserted then in most Unix-like systems the OS kernel just steps the time back by 1 second at the beginning of the leap second, so the last second of the UTC day is repeated and thus duplicate timestamps can occur. Unfortunately there are lots of applications which get confused it the system time is stepped back, e.g. due to a leap second insertion. Thus, many users have been looking for ways to avoid this, and tried to introduce workarounds which may work properly, or not. So even though these Unix kernels normally can handle leap seconds, the way they do this is not optimal for applications. One good way to handle the leap second is to use ntp_gettime() instead of the usual calls, because ntp_gettime() includes a "clock state" variable that will actually tell you if the time you are receiving is OK or not, and if it is OK, if the current second is an in-progress leap second. But even though this mechanism has been available for about 20 years' time, almost nobody uses it. == The Leap Smear Approach == Due to the reasons mentioned above some support for leap smearing has recently been implemented in ntpd. This means that to insert a leap second an NTP server adds a certain increasing "smear" offset to the real UTC time sent to its clients, so that after some predefined interval the leap second offset is compensated. The smear interval should be long enough, e.g. several hours, so that NTP clients can easily follow the clock drift caused by the smeared time. During the period while the leap smear is being performed, ntpd will include a specially-formatted 'refid' in time packets that contain "smeared" time. This refid is of the form 254.x.y.z, where x.y.z are 24 encoded bits of the smear value. With this approach the time an NTP server sends to its clients still matches UTC before the leap second, up to the beginning of the smear interval, and again corresponds to UTC after the insertion of the leap second has finished, at the end of the smear interval. By examining the first byte of the refid, one can also determine if the server is offering smeared time or not. Of course, clients which receive the "smeared" time from an NTP server don't have to (and even must not) care about the leap second anymore. Smearing is just transparent to the clients, and the clients don't even notice there's a leap second. == Pros and Cons of the Smearing Approach == The disadvantages of this approach are: * During the smear interval the time provided by smearing NTP servers differs significantly from UTC, and thus from the time provided by normal, non-smearing NTP servers. The difference can be up to 1 second, depending on the smear algorithm. * Since smeared time differs from true UTC, and many applications require correct legal time (UTC), there may be legal consequences to using smeared time. Make sure you check to see if this requirement affects you. However, for applications where it's only important that all computers have the same time and a temporary offset of up to 1 s to UTC is acceptable, a better approach may be to slew the time in a well defined way, over a certain interval, which is what we call smearing the leap second. == The Motivation to Implement Leap Smearing == Here is some historical background for ntpd, related to smearing/slewing time. Up to ntpd 4.2.4, if kernel support for leap seconds was either not available or was not enabled, ntpd didn't care about the leap second at all. So if ntpd was run with -x and thus kernel support wasn't used, ntpd saw a sudden 1 s offset after the leap second and normally would have stepped the time by -1 s a few minutes later. However, 'ntpd -x' does not step the time but "slews" the 1-second correction, which takes 33 minutes and 20 seconds to complete. This could be considered a bug, but certainly this was only an accidental behavior. However, as we learned in the discussion in http://bugs.ntp.org/2745, this behavior was very much appreciated since indeed the time was never stepped back, and even though the start of the slewing was somewhat undefined and depended on the poll interval. The system time was off by 1 second for several minutes before slewing even started. In ntpd 4.2.6 some code was added which let ntpd step the time at UTC midnight to insert a leap second, if kernel support was not used. Unfortunately this also happened if ntpd was started with -x, so the folks who expected that the time was never stepped when ntpd was run with -x found this wasn't true anymore, and again from the discussion in NTP bug 2745 we learn that there were even some folks who patched ntpd to get the 4.2.4 behavior back. In 4.2.8 the leap second code was rewritten and some enhancements were introduced, but the resulting code still showed the behavior of 4.2.6, i.e. ntpd with -x would still step the time. This has only recently been fixed in the current ntpd stable code, but this fix is only available with a certain patch level of ntpd 4.2.8. So a possible solution for users who were looking for a way to come over the leap second without the time being stepped could have been to check the version of ntpd installed on each of their systems. If it's still 4.2.4 be sure to start the client ntpd with -x. If it's 4.2.6 or 4.2.8 it won't work anyway except if you had a patched ntpd version instead of the original version. So you'd need to upgrade to the current -stable code to be able to run ntpd with -x and get the desired result, so you'd still have the requirement to check/update/configure every single machine in your network that runs ntpd. Google's leap smear approach is a very efficient solution for this, for sites that do not require correct timestamps for legal purposes. You just have to take care that your NTP servers support leap smearing and configure those few servers accordingly. If the smear interval is long enough so that NTP clients can follow the smeared time it doesn't matter at all which version of ntpd is installed on a client machine, it just works, and it even works around kernel bugs due to the leap second. Since all clients follow the same smeared time the time difference between the clients during the smear interval is as small as possible, compared to the -x approach. The current leap second code in ntpd determines the point in system time when the leap second is to be inserted, and given a particular smear interval it's easy to determine the start point of the smearing, and the smearing is finished when the leap second ends, i.e. the next UTC day begins. The maximum error doesn't exceed what you'd get with the old smearing caused by -x in ntpd 4.2.4, so if users could accept the old behavior they would even accept the smearing at the server side. In order to affect the local timekeeping as little as possible the leap smear support currently implemented in ntpd does not affect the internal system time at all. Only the timestamps and refid in outgoing reply packets *to clients* are modified by the smear offset, so this makes sure the basic functionality of ntpd is not accidentally broken. Also peer packets exchanged with other NTP servers are based on the real UTC system time and the normal refid, as usual. The leap smear implementation is optionally available in ntp-4.2.8p3 and later, and the changes can be tracked via http://bugs.ntp.org/2855. == Using NTP's Leap Second Smearing == * Leap Second Smearing MUST NOT be used for public servers, e.g. servers provided by metrology institutes, or servers participating in the NTP pool project. There would be a high risk that NTP clients get the time from a mixture of smearing and non-smearing NTP servers which could result in undefined client behavior. Instead, leap second smearing should only be configured on time servers providing dedicated clients with time, if all those clients can accept smeared time. * Leap Second Smearing is NOT configured by default. The only way to get this behavior is to invoke the +./waf configure+ script from the NTP source code package with the +--enable-leap-smear+ parameter before the executables are built. * Even if ntpd has been compiled to enable leap smearing support, leap smearing is only done if explicitly configured. * The leap smear interval should be at least several hours' long, and up to 1 day (86400s). If the interval is too short then the applied smear offset is applied too quickly for clients to follow. 86400s (1 day) is a good choice. * If several NTP servers are set up for leap smearing then the *same* smear interval should be configured on each server. * Smearing NTP servers DO NOT send a leap second warning flag to client time requests. Since the leap second is applied gradually the clients don't even notice there's a leap second being inserted, and thus there will be no log message or similar related to the leap second be visible on the clients. * Since clients don't (and must not) become aware of the leap second at all, clients getting the time from a smearing NTP server MUST NOT be configured to use a leap second file. If they had a leap second file they would apply the leap second twice: the smeared one from the server, plus another one inserted by themselves due to the leap second file. As a result, the additional correction would soon be detected and corrected/adjusted. * Clients MUST NOT be configured to poll both smearing and non-smearing NTP servers at the same time. During the smear interval they would get different times from different servers and wouldn't know which server(s) to accept. == Setting Up A Smearing NTP Server == If an NTP server should perform leap smearing then the leap smear interval (in seconds) needs to be specified in the NTP configuration file ntp.conf, e.g.: -------------------------------- leapsmearinterval 86400 -------------------------------- Please keep in mind the leap smear interval should be between several and 24 hours' long. With shorter values clients may not be able to follow the drift caused by the smeared time, and with longer values the discrepancy between system time and UTC will cause more problems when reconciling timestamp differences. When ntpd starts and a smear interval has been specified then a log message is generated, e.g.: ---------------------------------------------------------------- ntpd[31120]: config: leap smear interval 86400 s ---------------------------------------------------------------- While ntpd is running with a leap smear interval specified the command: -------------------------------- ntpq -c rv -------------------------------- reports the smear status, e.g.: -------------------------------- # ntpq -c rv associd=0 status=4419 leap_add_sec, sync_uhf_radio, 1 event, leap_armed, version="ntpd 4.2.8p3-RC1@1.3349-o Mon Jun 22 14:24:09 UTC 2015 (26)", processor="i586", system="Linux/3.7.1", leap=01, stratum=1, precision=-18, rootdelay=0.000, rootdisp=1.075, refid=MRS, reftime=d93dab96.09666671 Tue, Jun 30 2015 23:58:14.036, clock=d93dab9b.3386a8d5 Tue, Jun 30 2015 23:58:19.201, peer=2335, tc=3, mintc=3, offset=-0.097015, frequency=44.627, sys_jitter=0.003815, clk_jitter=0.451, clk_wander=0.035, tai=35, leapsec=201507010000, expire=201512280000, leapsmearinterval=86400, leapsmearoffset=-932.087 -------------------------------- In the example above 'leapsmearinterval' reports the configured leap smear interval all the time, while the 'leapsmearoffset' value is 0 outside the interval and increases from 0 to -1000 ms over the interval. So this can be used to monitor if and how the time sent to clients is smeared. With a leapsmearoffset of -.932087, the refid reported in smeared packets would be 254.196.88.176. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/tf582_4.txt0000644000175000017500000000343713252364117016420 0ustar rlaagerrlaager= European Automated Computer Time Services = The National Laboratories of many European countries provide time information in a standardised format, an ITU-R Recommendation (ITU-R TF583.4). Information is provided over a dial-up modem, with one 78-char segment every second. A description of the time code is available at https://www.ptb.de/cms/en/ptb/fachabteilungen/abt4/fb-44/ag-442/dissemination-of-legal-time/dissemination-of-time-via-the-public-telephone-network/the-european-telephone-time-code.html The following countries have traditionally provided this service: Austria, Belgium, Germany, Italy, The Netherlands, Poland, Portugal, Romania, Spain, Sweden, Switzerland, Turkey and United Kingdom. Note that the service is usually available from outside the country as well, as long as you are willing to make a long-distance phone call. Some examples are: * In Germany, Physikalisch-Technische Bundesanstalt (PTB)'s timecode service. Phone number: +49 5 31 51 20 38. + For more detail, see https://www.ptb.de/cms/index.php?id=1786&L=1 * In the UK, National Physical Laboratory (NPL)'s Telephone Time Service. Phone number: 020 8943 6333 + For more detail, see https://www.npl.co.uk/science-technology/time-frequency/products-and-services/time/time-synchronisation-of-computers-to-utc(npl) * In Italy, L'Istituto Nazionale di Ricerca Metrologica (INRIM)'s CTD service. Phone numbers: 011 39 19 263 and 011 39 19 264 + For more detail, see https://www.inrim.it/res/tf/ctd_i.shtml * In Sweden, SP Swedish National Testing and Research Institute's timecode service. Phone number: +46 33 415783. + For more detail, see https://www.sp.se/en/index/services/time_sync/modem_time/Sidor/default.aspx == Additional Information == link:refclock.html[Reference Clock Drivers] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntpmon.txt0000644000175000017500000000100313252364117016623 0ustar rlaagerrlaager= ntpmon - real-time NTP status monitor = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/looking.jpg[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] Words of wisdom. |============================== == More Help == include::includes/manual.txt[] ''''' include::includes/ntpmon-body.txt[] == Mode 6 Protocol == The Mode 6 protocol used by ntpmon to communicate with {ntpdman} is described link:mode6.html[here]. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/asciidoc.js0000644000175000017500000001330213252364117016670 0ustar rlaagerrlaagervar asciidoc = { // Namespace. ///////////////////////////////////////////////////////////////////// // Table Of Contents generator ///////////////////////////////////////////////////////////////////// /* Author: Mihai Bazon, September 2002 * http://students.infoiasi.ro/~mishoo * * Table Of Content generator * Version: 0.4 * * Feel free to use this script under the terms of the GNU General Public * License, as long as you do not remove or alter this notice. */ /* modified by Troy D. Hanson, September 2006. License: GPL */ /* modified by Stuart Rackham, 2006, 2009. License: GPL */ // toclevels = 1..4. toc: function (toclevels) { function getText(el) { var text = ""; for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants. text += i.data; else if (i.firstChild != null) text += getText(i); } return text; } function TocEntry(el, text, toclevel) { this.element = el; this.text = text; this.toclevel = toclevel; } function tocEntries(el, toclevels) { var result = new Array; var re = new RegExp('[hH]([1-'+(toclevels+1)+'])'); // Function that scans the DOM tree for header elements (the DOM2 // nodeIterator API would be a better technique but not supported by all // browsers). var iterate = function (el) { for (var i = el.firstChild; i != null; i = i.nextSibling) { if (i.nodeType == 1 /* Node.ELEMENT_NODE */) { var mo = re.exec(i.tagName); if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") { result[result.length] = new TocEntry(i, getText(i), mo[1]-1); } iterate(i); } } } iterate(el); return result; } var toc = document.getElementById("toc"); if (!toc) { return; } // Delete existing TOC entries in case we're reloading the TOC. var tocEntriesToRemove = []; var i; for (i = 0; i < toc.childNodes.length; i++) { var entry = toc.childNodes[i]; if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") && entry.getAttribute("class").match(/^toclevel/)) tocEntriesToRemove.push(entry); } for (i = 0; i < tocEntriesToRemove.length; i++) { toc.removeChild(tocEntriesToRemove[i]); } // Rebuild TOC entries. var entries = tocEntries(document.getElementById("content"), toclevels); for (var i = 0; i < entries.length; ++i) { var entry = entries[i]; if (entry.element.id == "") entry.element.id = "_toc_" + i; var a = document.createElement("a"); a.href = "#" + entry.element.id; a.appendChild(document.createTextNode(entry.text)); var div = document.createElement("div"); div.appendChild(a); div.className = "toclevel" + entry.toclevel; toc.appendChild(div); } if (entries.length == 0) toc.parentNode.removeChild(toc); }, ///////////////////////////////////////////////////////////////////// // Footnotes generator ///////////////////////////////////////////////////////////////////// /* Based on footnote generation code from: * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html */ footnotes: function () { // Delete existing footnote entries in case we're reloading the footnodes. var i; var noteholder = document.getElementById("footnotes"); if (!noteholder) { return; } var entriesToRemove = []; for (i = 0; i < noteholder.childNodes.length; i++) { var entry = noteholder.childNodes[i]; if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote") entriesToRemove.push(entry); } for (i = 0; i < entriesToRemove.length; i++) { noteholder.removeChild(entriesToRemove[i]); } // Rebuild footnote entries. var cont = document.getElementById("content"); var spans = cont.getElementsByTagName("span"); var refs = {}; var n = 0; for (i=0; i" + n + "]"; spans[i].setAttribute("data-note", note); } noteholder.innerHTML += "
" + "" + n + ". " + note + "
"; var id =spans[i].getAttribute("id"); if (id != null) refs["#"+id] = n; } } if (n == 0) noteholder.parentNode.removeChild(noteholder); else { // Process footnoterefs. for (i=0; i" + n + "]"; } } } }, install: function(toclevels) { var timerId; function reinstall() { asciidoc.footnotes(); if (toclevels) { asciidoc.toc(toclevels); } } function reinstallAndRemoveTimer() { clearInterval(timerId); reinstall(); } timerId = setInterval(reinstall, 500); if (document.addEventListener) document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false); else window.onload = reinstallAndRemoveTimer; } } ntpsec-1.1.0+dfsg1/docs/sitemap.txt0000644000175000017500000000122213252364117016755 0ustar rlaagerrlaager= Site Map = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice15.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] Welcome to the tea party. |============================== == Links == include::includes/install.txt[] include::includes/manual.txt[] xref:_links[Back to Top] include::includes/hand.txt[] include::includes/refclock.txt[] xref:_links[Back to Top] include::includes/misc.txt[] include::includes/special.txt[] xref:_links[Back to Top] include::includes/external.txt[] ''''' image::pic/tribeb.gif[align="center"] include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/select.txt0000644000175000017500000001525113252364117016601 0ustar rlaagerrlaager= Clock Select Algorithm = The clock select algorithm determines from a set of sources, which are correct (_truechimers_) and which are not (_falsetickers_) according to a set of formal correctness assertions. The principles are based on the observation that the maximum error in determining the offset of a candidate cannot exceed one-half the roundtrip delay to the primary reference clock at the time of measurement. This must be increased by the maximum error that can accumulate since then. The selection metric, called the _root distance,_, is one-half the roundtrip root delay plus the root dispersion plus minor error contributions not considered here. First, a number of sanity checks is performed to sift the selectable candidate from among the source population. The sanity checks are summarized as follows: 1. A _stratum error_ occurs if (1) the source had never been synchronized or (2) the stratum of the source is below the +floor+ option or not below the +ceiling+ option of the link:miscopt.html#tos[+tos+] command. The default values for these options are 0 and 15, respectively. Note that 15 is a valid stratum, but a server operating at that stratum cannot synchronize clients. 2. A _distance error_ occurs for a source if the root distance (also known as synchronization distance) of the source is not below the distance threshold +maxdist+ option of the link:miscopt.html#tos[+tos+] command. The default value for this option is 1.5 s for networks including only the Earth, but this should be increased to 2.5 s for networks including the Moon. 3. A _loop_ _error_ occurs if the source is synchronized to the client. This can occur if two peers are configured with each other in symmetric modes. 4. An _unreachable_ _error_ occurs if the source is unreachable or if the +server+ or +peer+ command for the source includes the +noselect+ option. Sources showing one or more of these errors are considered nonselectable; only the selectable candidates are considered in the following algorithm. Given the measured offset θ~0~ and root distance λ, this defines a _correctness interval_ [θ~0~ − λ, θ~0~ + λ] of points where the true value of θ lies somewhere on the interval. The given problem is to determine from a set of correctness intervals, which represent truechimers and which represent falsetickers. The principles must be given a precise definition. The _intersection interval_ is the _smallest interval containing points from the largest number of correctness intervals._ An algorithm that finds the intersection interval was devised by Keith Marzullo in his doctoral dissertation. It was first implemented in the Digital Time Synchronization Service (DTSS) for the VMS operating system for the VAX. While the NTP algorithm is based on DTSS, it remains to establish which point in the correctness interval represents the best estimate of the offset for each candidate. The best point is at the midpoint θ~0~ of the correctness interval; however, the midpoint might not be within the intersection interval. A candidate with a correctness interval that contains points in the intersection interval is a truechimer and the best offset estimate is the midpoint of its correctness interval. A candidate with a correctness interval that contains no points in the intersection interval is a falseticker. image:pic/flt3.gif[] Figure 1. Intersection Interval Figure 1 shows correctness intervals for each of four candidates A, B, C and D. We need to find the maximum number of candidates that contain points in common. The result is the interval labeled DTSS. In the figure there are three truechimers A, B and C, and one falseticker D. In DTSS any point in the intersection interval can represent the true time; however, as shown below, this may throw away valuable statistical information. In any case, the clock is considered correct if the number of truechimers found in this way are greater than half the total number of candidates. The question remains, which is the best point to represent the true time of each correctness interval? Fortunately, we already have the maximum likelihood estimate at the midpoint of each correctness interval. But, while the midpoint of candidate C is outside the intersection interval, its correctness interval contains points in common with the intersection interval, so the candidate is a truechimer and the midpoint is chosen to represent its time. The DTSS correctness assertions do not consider how best to represent the truechimer time. To support the midpoint choice, consider the selection algorithm as a method to reject correctness intervals that cannot contribute to the final outcome; that is, they are falsetickers. The remaining correctness intervals can contribute to the final outcome; that is, they are truechimers. Samples in the intersection interval are usually of very low probability and thus poor estimates for truechimer time. On the other hand, the midpoint sample produced by the clock filter algorithm is the maximum likelihood estimate and thus best represents the truechimer time. image:pic/flt6.gif[] Figure 2. Clock Select Algorithm The algorithm operates as shown in Figure 2. Let _m_ be the number of candidates and _f_ the number of falsetickers, initially zero. Move a pointer from the leftmost endpoint towards the rightmost endpoint in Figure 1 and count the number of candidates, stopping when that number reaches _m_ − _f_; this is the left endpoint of the intersection interval. Then, do the same, but moving from the rightmost endpoint towards the leftmost endpoint; this is the right endpoint of the intersection interval. If the left endpoint is less than the right endpoint, the intersection interval has been found. Otherwise, increase _f_ by 1. If _f_ is less than _n_ / 2, try again; otherwise, the algorithm fails and no truechimers could be found. The clock select algorithm again scans the correctness intervals. If the right endpoint of the correctness interval for a candidate is greater than the left endpoint of the intersection interval, or if the left endpoint of the correctness interval is less than the right endpoint of the intersection interval, the candidate is a truechimer; otherwise, it is a falseticker. In practice, with fast LANs and modern computers, the correctness interval can be quite small, especially when the candidates are multiple reference clocks. In such cases the intersection interval might be empty, due to insignificant differences in the reference clock offsets. To avoid this, the size of the correctness interval is padded to the value of +mindist+, with default 1 ms. This value can be changed using the +mindist+ option of the link:miscopt.html#tos[+tos+] command. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/stats.txt0000644000175000017500000002444513252364117016465 0ustar rlaagerrlaager= Performance Metrics = == Related Links == include::includes/special.txt[] include::includes/external.txt[] == Table of Contents == * link:#intro[Introduction] * link:#budget[Statistics Summary] * link:#quality[Quality of Service] ''''' [[intro]] == Introduction == This page describes several statistics provided in the NTP specification and reference implementation and how they determine the accuracy and error measured during routine and exceptional operation. These statistics provide the following information. * Nominal estimate of the server clock time relative to the client clock time. This is called _clock offset_ symbolized by the Greek letter θ. * Roundtrip system and network delay measured by the on-wire protocol. This is call _roundtrip delay_ symbolized by the Greek letter δ. * Potential clock offset error due to the maximum uncorrected system clock frequency error. This is called _dispersion_ symbolized by the Greek letter ε. * Expected error, consisting of the root mean square (RMS) nominal clock offset sample differences in a sliding window of several samples. This is called _jitter_ symbolized by the Greek letter φ. Figure 1 shows how the various measured statistics are collected and compiled to calibrate NTP performance. image:pic/stats.gif[] Figure 1. Statistics Budget The data represented in boxes labeled Server are contained in fields in packet received from the server. The data represented in boxes labeled Peer are computed by the on-wire protocol, as described below. The algorithms of the box labeled Selection and Combining Algorithms process the peer data to select a system peer. The System box represents summary data inherited from the system peer. These data are available to application programs and dependent downstream clients. [[budget]] == Statistics Summary == Each NTP synchronization source is characterized by the offset θ and delay δ samples measured by the on-wire protocol, as described on the link:warp.html[How NTP Works] page. In addition, the dispersion ε sample is initialized with the sum of the source precision Ï~R~ and the client precision Ï (not shown) as each source packet is received. The dispersion increases at a rate of 15 μs/s after that. For this purpose, the precision is equal to the latency to read the system clock. The offset, delay and dispersion are called the sample statistics. [NOTE] In very fast networks where the client clock frequency is not within 1 PPM or so of the server clock frequency, the roundtrip delay may have small negative values. This is usually a temporary condition when the client is first started. When using the roundtrip delay in calculations, negative values are assumed zero. In a window of eight (offset, delay, dispersion) samples, the algorithm described on the link:filter.html[Clock Filter Algorithm] page selects the sample with minimum delay, which generally represents the most accurate offset statistic. The selected offset sample determines the _peer offset_ and _peer delay_ statistics. The _peer dispersion_ is a weighted average of the dispersion samples in the window. These quantities are recalculated as each update is received from the source. Between updates, both the sample dispersion and peer dispersion continue to grow at the same rate, 15 μs/s. Finally, the _peer jitter_ φ is determined as the RMS differences between the offset samples in the window relative to the selected offset sample. The peer statistics are recorded by the +peerstats+ option of the link:monopt.html#filegen[+filegen+] command. Peer variables are displayed by the +rv+ command of the link:ntpq.html#peer[+ntpq+] program. The clock filter algorithm continues to process updates in this way until the source is no longer reachable. Reachability is determined by an eight-bit shift register, which is shifted left by one bit as each poll packet is sent, with 0 replacing the vacated rightmost bit. Each time a valid update is received, the rightmost bit is set to 1. The source is considered reachable if any bit is set to 1 in the register; otherwise, it is considered unreachable. When a source becomes unreachable, a dummy sample with "infinite" dispersion is inserted in the filter window at each poll, thus displacing old samples. This causes the peer dispersion to increase eventually to infinity. The composition of the source population and the system peer selection is redetermined as each update from each source is received. The system peer and system variables are determined as described on the link:prefer.html[Mitigation Rules and the +prefer+ Keyword] page. The system variables Θ, Δ, Ε and Φ are updated from the system peer variables of the same name and the system stratum set one greater than the system peer stratum. The system statistics are recorded by the +loopstats+ option of the link:monopt.html#filegen[+filegen+] command. System variables are displayed by the +rv+ command of the link:ntpq.html#system[+ntpq+] program. Although it might seem counterintuitive, a cardinal rule in the selection process is, once a sample has been selected by the clock filter algorithm, older samples are no longer selectable. This applies also to the clock select algorithm. Once the peer variables for a source have been selected, older variables of the same or other sources are no longer selectable. The reason for these rules is to limit the time delay in the clock discipline algorithm. This is necessary to preserve the optimum impulse response and thus the risetime and overshoot. This means that not every sample can be used to update the peer variables, and up to seven samples can be ignored between selected samples. This fact has been carefully considered in the discipline algorithm design with due consideration for feedback loop delay and minimum sampling rate. In engineering terms, even if only one sample in eight survives, the resulting sample rate is twice the Nyquist rate at any time constant and poll interval. [[quality]] == Quality of Service == This section discusses how an NTP client determines the system performance using a peer population including reference clocks and remote servers. This is determined for each peer from two statistics, _peer jitter_ and _root distance._ Peer jitter is determined from various jitter components as described above. It represents the expected error in determining the clock offset estimate. Root distance represents the maximum error of the estimate due to all causes. The root distance statistic is computed as one-half the _root delay_ of the primary source of time; i.e., the reference clock, plus the _root dispersion_ of that source. The root variables are included in the NTP packet header received from each source. At each update the root delay is recomputed as the sum of the root delay in the packet plus the peer delay, while the root dispersion is recomputed as the sum of the root dispersion in the packet plus the peer dispersion. [NOTE] In order to avoid timing loops, the root distance is adjusted to the maximum of the above computation and a _minimum threshold._ The minimum threshold defaults to 1 ms, but can be changed according to client preference using the +mindist+ option of the link:miscopt.html#tos[+tos+] command. A source is considered selectable only if its root distance is less than the _select threshold_, by default 1.5 s, but can be changed according to client preference using the +maxdist+ option of the link:miscopt.html#tos[+tos+] command. When an upstream server loses all sources, its root distance apparent to dependent clients continues to increase. The clients are not aware of this condition and continue to accept synchronization as long as the root distance is less than the select threshold. The root distance statistic is used by the select, cluster and mitigation algorithms. In this respect, it is sometimes called the _synchronization distance_ often shortened simply to _distance_. The root distance is also used in the following ways. * Root distance defines the maximum error of the clock offset estimate due to all causes as long as the source remains reachable. * Root distance defines the upper and lower limits of the correctness interval. This interval represents the maximum clock offset for each of possibly several sources. The clock select algorithm computes the intersection of the correctness intervals to determine the truechimers from the selectable source population. * Root distance is used by the clock cluster algorithm as a weight factor when pruning outliers from the truechimer population. * The (normalized) reciprocal of the root distance is used as a weight factor by the combine algorithm when computing the system clock offset and system jitter. * Root distance is used by the mitigation algorithm to select the system peer from among the cluster algorithm survivors. The root distance thus functions as a metric in the selection and weighting of the various available sources. The strategy is to select the system peer as the source with the minimum root distance and thus the minimum maximum error. The reference implementation uses the Bellman-Ford algorithm described in the literature, where the goal is to minimize the root distance. The algorithm selects the _system peer_, from which the system root delay and system root dispersion are inherited. The algorithms described on the link:prefer.html[Mitigation Rules and the +prefer+ Keyword] page deliver several important statistics. The _system offset_ and _system jitter_ are weighted averages computed by the clock combine algorithm. System offset is best interpreted as the maximum-likelihood estimate of the system clock offset, while system jitter, also called estimated error, is best interpreted as the expected error of this estimate. _System delay_ is the root delay inherited from the system peer, while _system dispersion_ is the root dispersion plus contributions due to jitter and the absolute value of the system offset. The maximum system error, or _system distance_, is computed as one-half the system delay plus the system dispersion. In order to simplify discussion, certain minor contributions to the maximum error statistic are ignored. If the precision time kernel support is available, both the estimated error and maximum error are reported to user programs via the +ntp_adjtime()+ kernel system call. See the link:kern.html[Kernel Model for Precision Timekeeping] page for further information. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/huffpuff.txt0000644000175000017500000000435313252364117017134 0ustar rlaagerrlaager= The Huff-n'-Puff Filter = In scenarios where a considerable amount of data are downloaded or uploaded using DSL or telephone modem lines, timekeeping quality can be seriously degraded. This occurs because the traffic volume, and thus the queuing delays, on the upload and download directions of transmission can be very different. In many cases the apparent time errors are so large as to exceed the step threshold and a step correction can occur during and after the data transfer. The huff-n'-puff filter is designed to correct the apparent time offset in these cases. It depends on knowledge of the propagation delay when no other traffic is present, such as during other than work hours. The filter remembers the minimum delay over the most recent interval measured usually in hours. Under conditions of large delay, the filter corrects the apparent offset using the sign of the offset and the difference between the apparent delay and minimum delay. The name of the filter reflects the negative (huff) and positive (puff) correction, which depends on the sign of the offset. The filter is activated by the +tinker huffpuff+ command, as described in the link:miscopt.html[Miscellaneous Options] page. ''''' image:pic/flt4.gif[] Figure 1. Huff-n'-Puff Wedge Scattergram Figure 1 shows how the huff-n'-puff filter works. Recall from the link:filter.html[Clock Filter Algorithm] page that the wedge scattergram plots sample points ('x', 'y') corresponding to the measured delay and offset, and that the limb lines are at slope ±0.5. Note in the figure that the samples are clustered close to the upper limb line, representing heavy traffic in the download direction. The apparent offset 'y'~0~ is near zero at the minimum delay 'x'~0~, which is near 0.1s. + Thus, for a point ('x', 'y'), the true offset is:: θ = 'y' - ('x' - 'x'~0~) / 2 for 'y' > 'y'~0~ at or near the upper limb line or + θ = 'y' + ('x' - 'x'~0~) / 2 for 'y' < 'y'~0~ at or near the lower limb line. In either case the associated delay is δ = 'x'. In the interior of the wedge scattergram far from the limb lines, the corrections are less effective and can lead to significant errors if the area between the limb lines is heavily populated. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/extern.txt0000644000175000017500000001160113252364117016622 0ustar rlaagerrlaager= External Clock Discipline and the Local Clock Driver = The NTPv4 implementation includes provisions for an external clock, where the system clock is implemented by some external hardware device. One implementation might take the form of a bus peripheral with a high resolution counter disciplined by a GPS receiver, for example. Another implementation might involve another synchronization protocol, such as the Digital Time Synchronization Service (DTSS), where the system time is disciplined to this protocol and NTP clients of the server obtain synchronization indirectly via the server. A third implementation might be a completely separate clock discipline algorithm and synchronization protocol, such as the +Lockclock+ algorithm used with NIST Automated Computer Time Service (ACTS) modem synchronized time. When external clocks are used in conjunction with NTP service, some way needs to be provided for the external clock driver and NTP daemon +ntpd+ to communicate and determine which discipline is in control. This is necessary in order to provide backup, for instance if the external clock or protocol were to fail and synchronization service fall back to other means, such as a local reference clock or another NTP server. In addition, when the external clock and driver are in control, some means needs to be provided for the clock driver to pass on status information and error statistics to the NTP daemon. Control and monitoring functions for the external clock and driver are implemented using the link:driver_local.html[Local Clockdriver] and the +ntp_adjtime()+ system call. This system call is implemented by special kernel provisions included in the kernel of several operating systems, including Solaris, Tru64, FreeBSD and Linux, and possibly others. When the external clock is disabled or not implemented, the system call is used to pass time and frequency information, as well as error statistics, to the kernel. Besides disciplining the system time, the same interface can be used by other applications to determine the operating parameters of the discipline. When the external clock is enabled, +ntpd+ does not discipline the system clock, nor does it maintain the error statistics. In this case, the external clock and driver do this using mechanisms unknown to +ntpd+; however, in this case the kernel state variables are retrieved at 64-s intervals by the Local Clock driver and used by the clock selection and mitigation algorithms to determine the system variables presented to other NTP clients and peers. In this way, downstream clients and servers in the NTP subnet can make an intelligent choice when more than one server is available. In order to implement a reliable mitigation between ordinary NTP sources and the external clock source, a protocol is necessary between the local clock driver and the external clock driver. This is implemented using Boolean variables and certain bits in the kernel clock status word. The Boolean variables include the following: +ntp_enable+:: Set/reset by the +enable+ command. Enables ntpd clock discipline +ntp_control+:: Set during initial configuration if kernel support is available +kern_enable+:: Set/reset by the +enable+ command If the +kern_enable+ switch is set, the daemon computes the offset, frequency, maximum error, estimated error, time constant and status bits, then provides them to the kernel via +ntp_adjtime()+. If this switch is not set, these values are not passed to the kernel; however, the daemon retrieves their present values and uses them in place of the values computed by the daemon. The +pps_update+ bit set in the protocol routine if the prefer peer has survived and has offset less than 128 ms; otherwise set to zero. The +PPS control+ Updated to the current time by kernel support if the PPS signal is enabled and working correctly. Set to zero in the adjust routine if the interval since the last update exceeds 120 s. The +ntp_enable+ and +kern_enable+ are set by the configuration module. Normally, both switches default on, so the daemon can control the time and the kernel discipline can be used, if available. The +pps_update+ switch is set by the protocol module when it believes the PPS provider source is legitimate and operating nominally. The +ntp_control+ switch is set during configuration by interrogating the kernel. If both the +kern_enable+ and +ntp_control+ switches are set, the daemon disciplines the clock via the kernel and the internal daemon discipline is disabled. The external clock driver controls the system time and clock selection in the following way. Normally, the driver adjusts the kernel time using the +ntp_adjtime()+ system call in the same way as the daemon. In the case where the kernel discipline is to be used intact, the clock offset is provided in this call and the loop operates as specified. In the case where the driver steers only the frequency, the offset is specified as zero. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/comdex.txt0000644000175000017500000000102613252364117016574 0ustar rlaagerrlaager= Command Index = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice38.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carrol] The Mad Hatter says "Bring it on". |============================== == Related Links == include::includes/accopt.txt[] include::includes/authopt.txt[] include::includes/confopt.txt[] include::includes/monopt.txt[] include::includes/clockopt.txt[] include::includes/miscopt.txt[] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntp_keys.txt0000644000175000017500000000137113252364117017154 0ustar rlaagerrlaager= ntp.keys - NTP symmetric key file format = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice35.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] Three can keep a secret, if two are dead. |============================== == More Help == include::includes/manual.txt[] == Table of Contents == * link:#_description[DESCRIPTION] * link:#_usage[USAGE] * link:#_files[FILES] * link:#_see_also[SEE ALSO] ''''' include::includes/ntp.keys-body.txt[] ''''' == SEE ALSO == link:ntp_conf.html[{ntpdconfman}], link:ntpd.html[{ntpdman}], link:ntpq.html[{ntpqman}], link:ntpkeygen.html[{ntpkeygenman}], link:ntpdig.html[{ntpdigman}]. include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/authentic.txt0000644000175000017500000002155013252364117017305 0ustar rlaagerrlaager= Authentication Support = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice44.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] Our resident cryptographer; now you see him, now you don't. |============================== == Related Links == include::includes/hand.txt[] include::includes/authopt.txt[] == Table of Contents == * link:#auth[Introduction] * link:#symm[Symmetric Key Cryptography] * link:#operation[Operation] * link:#keys[Key Management] * link:#algorithms[Algorithms] * link:#formats[Data Formats] //* link:#windows[Microsoft Windows Authentication] ''''' == Introduction == Authentication support allows the NTP client to verify that the server is in fact known and trusted and not an intruder intending accidentally or on purpose to masquerade as that server. NTP performs authentication via message digests. It computes a one-way hash, which verifies that the server has the correct private key and key identifier. A detailed discussion of the NTP multi-layer security model and vulnerability analysis is in the white paper {millshome}security.html[NTP Security Analysis]. Authentication is configured separately for each association using the +key+ subcommand on the +server+ configuration commands. The authentication options described below specify the locations of the key files and which symmetric keys are trusted. Authentication is always enabled, although ineffective if not configured as described below. If a NTP packet arrives including a message authentication code (MAC), it is accepted only if it passes all cryptographic checks. The checks require correct key ID, key value and message digest. If the packet has been modified in any way by an intruder, it will fail one or more of these checks and be discarded. Authentication doesn't prevent replays. [[symm]] === Symmetric-Key Cryptography === NTP allows any one of possibly 65,534 keys, each distinguished by a 32-bit key identifier, to authenticate an association. The servers and clients involved must agree on the key and key identifier to authenticate NTP packets. Keys and related information are specified in a key file. More info in {ntpkeysman}. It must be distributed and stored using secure means beyond the scope of the NTP protocol itself. Besides the keys used for ordinary NTP associations, additional keys can be used as passwords for the {ntpqman} utility program. When {ntpdman} is first started, it reads the key file specified in the keys configuration command and installs the keys in the key cache. However, individual keys must be activated with the +trustedkey+ command before use. This allows, for instance, the installation of possibly several batches of keys and then activating or deactivating each batch remotely using {ntpqman}. This also provides a revocation capability that can be used if a key becomes compromised. The +controlkey+ command selects the key used as the password for the {ntpqman} utility. [[operation]] == Operation == A server receiving an unauthenticated packet will respond with an unauthenticated packet, while the same server receiving a packet of a cryptotype it supports will respond with packets of that cryptotype. Some examples may help to reduce confusion. Client Alice has no keys file. Server Bob has a symmetric key file. Alice's unauthenticated messages arrive at Bob, who replies with unauthenticated messages. Cathy has a copy of Bob's symmetric key file and has selected key ID 4 in messages to Bob. Bob verifies the message with his key ID 4. If it's the same key and the message is verified, Bob sends Cathy a reply authenticated with that key. If verification fails, Bob sends Cathy a crypto-NAK, which tells her something broke. She can see the evidence using the {ntpqman} program. It should be clear from the above that Bob can support all the girls at the same time, as long as he has compatible authentication and identity credentials. Now, Bob can act just like the girls in his own choice of servers; he can run multiple configured associations with multiple different servers (or the same server, although that might not be useful). But, wise security policy might preclude some combinations; for instance, running authentication with one server and no authentication with another might not be wise. [[keys]] == Key Management == Shared keys used for authentication are incorporated keys files generated by the {ntpkeygenman} utility program. [[algorithms]] == Algorithms == The NTP standards include symmetric (private-key) authentication using any message digest algorithm supported by the OpenSSL package. Digests longer than 20 bytes will be truncated. This algorithm computes a message digest or one-way hash which can be used to verify the client has the same message digest as the server. Authentication is configured separately for each association using the +key+ option of the +server+ configuration command, as described in the link:confopt.html[Server Options] page. The link:ntpkeygen.html[ntpkeygen] page describes the files required for the various authentication schemes. By default, the client sends non-authenticated packets and the server responds with non-authenticated packets. If the client sends authenticated packets, the server responds with authenticated packets if correct, or a crypto-NAK packet if not. The +notrust+ flag, described on the link:authopt.html[Access Control Options] page, can be used to disable access to all but correctly authenticated clients. [[formats]] == Data Formats == The NTPv4 specification (RFC 5905) allows any one of possibly 65,534 message digest keys (excluding zero), each distinguished by a 32-bit key ID, to authenticate an association. The servers and clients involved must agree on the key ID, key type and key to authenticate NTP packets. The message digest is a cryptographic hash computed by an algorithm such as MD5 or SHA1. When authentication is specified, a message authentication code (MAC) is appended to the NTP packet header. The MAC consists of a 32-bit key identifier (key ID) followed by a 128- or 160-bit message digest. The algorithm computes the digest as the hash of the key concatenated with the NTP packet header fields and the key ID. On transmit, the message digest is computed and inserted in the MAC. On receive, the message digest is computed and compared with the MAC. The packet is accepted only if the two MACs are identical. If a discrepancy is found by the client, the client ignores the packet, but raises an alarm. If this happens at the server, the server returns a special message called a _crypto-NAK_. Since the crypto-NAK is protected by the loopback test, an intruder cannot disrupt the protocol by sending a bogus crypto-NAK. MD5 digests are 16 bytes. SHA1 digests are 20 bytes. Longer digests don't work yet. 2018-Jan-07 FIXME: long digests Keys and related information are specified in a keys file, which must be distributed and stored using secure means beyond the scope of the NTP protocol itself. Besides the keys used for ordinary NTP associations, additional keys can be used as passwords for the +ntpq+ utility program. See {ntpkeysman} for details. .Figure 1. Typical Symmetric Key File image:pic/sx5.gif["Typical Symmetric Key File",align="center"] Figure 1 shows a typical keys file. In this figure, for key IDs in he range 1-10, the key is interpreted as a printable ASCII string. For key IDs in the range 11-20, the key is a 40-character hex digit string. Any line can be edited to change any field or new lines can be added. Note that two or more keys files can be combined in any order as long as the key IDs are distinct. When +ntpd+ is started, it reads the keys file specified by the +keys+ command and installs the keys in the key cache. However, individual keys must be activated with the +trustedkey+ configuration command before use. This allows, for instance, the installation of possibly several batches of keys and then activating a key remotely using +ntpq+. The +controlkey+ command selects the key ID used as the password for the +ntpq+ utility. [[windows]] == Microsoft Windows Authentication == In addition to the above means, +ntpd+ supports Microsoft Windows MS-SNTP authentication using Active Directory services. This support was contributed by the Samba Team and is still in development. It requires the +--enable-mssntp+ option to +waf configure+. At run time, it is enabled using the +mssntp+ flag of the +restrict+ command described on the link:accopt.html#restrict[Access Control Options] page. *Note: Potential users should be aware that these services involve a TCP connection to another process that could potentially block, denying services to other users. Therefore, this flag should be used only for a dedicated server with no clients other than MS-SNTP.* == History == Old versions of NTP supported Autokey. It is described in RFC 5906. It used key ids greater than 64K. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/pic/0000775000175000017500000000000013252650651015333 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/docs/pic/alice44.gif0000644000175000017500000004667113252364117017262 0ustar rlaagerrlaagerGIF89aù´Õöõö윻»»[H&ЭsFFFº™i3/-©©©Ÿf "””” Œ0„„„gggŒsOwww¤7 t' W"I4¹>£†\DÞÞÞwcBË¥n$!hW;&VWV{„„1N7b‰l;cH 3~¯ˆ‚–ˆÆÆÆÎÎÎÖÖÖ ÆB^ç½{ƽÆÖÎÖÎÆÆµ­­œœœ¥œœÎÎÖÆœcèè猌ŒÎÖÖ"q<­¥œÿÿÿ!ù?,ù´ÿÀŸpH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°xœR©ȬJmËða¹Á¨†f÷xÏïû‹)>,…!h)zV)+ ‹?e!g>)>mz‹› £¤¥N…ª…„mX …+v²;-…+z+*>ÀBŒ¦ÊËÈ+6+_i±6e+;°! Ó*Ñm…… Ž;,Bf+ÓÌüOežÿ"½b’Aˆ ¤È0‰¤ElôIœ˜lÒ$Ò9Ô(›™HŽ6IX¡Ð uB¤k¡Rl‹Vø€Ô¯&6 h !„ ÿ-8Ñ7ì€%„X ¡Áq' ë …ÊUêµ0ªéÈ¢HrIŒõ,QÃ*šj9@ÕÁ# O¹1³ÊG­;6óÙ¹‚jÆ„ ÇF…ƒq‘v°k@‡cyÎ ¡U@"#$Ä À`  ¼ÀÐov@`IEåUA³¸ù·‰˜ ·Ap wˆ:JѬ×KT%*Xظ³¢²7+lˆ¨!`d-o’BCä-IA¸Œ5(„aBèÎ.\D`Ñàv¬õRõWž.ŽðJèÊÉA t°N)@ P ,lRœ5‘¥B7x1Xœ`¾¥Âÿ F‡L ‚B@Õƒ|À( =öu‡Ï3À”ãÀ Ь0˜Šù°À&*´ÀŽ'?´¡ÁšØ¸à!‘ß}D¸v€>"–‘S TbL7*ÐÓB1,"p†R ´^l a;D£S¿a¥ J®EK/÷ ƒ€îaŸ #KH…`C!…6Ù€S³ç”E¨àÃ3nüXP€³qÊ ´Ò¦ ÓB$>ì N¸9‚蟃1 –‚ þ€@…"Ê1Ì%-@R=1 @ˆ*+Ú°`*LèC“¤ÆnÀ4§-?°Š‡…0ÙdeP]FhI®SjšÿÂŽèc,—F ;Ȉv ÐÞKi„¢Má$nÁAƒN ‡„#¥•øö:"Dþµ AèÍbÅ®D•8ÁÇl€è Ìf DCÆ¢••…SÉlÇ”e„ƒ€5´P@(¶ƒ¦ „@|É]I`´¹õ,Á •4¡ž…´–G–ggDú°³gO:ÌðV‚© ‹ï\XHA ì ”p|‚€4ÿHÆ“rÅ;£ ¥:ÜÏNá[§>6Øñƒv„jLuŸ¸²ˆöñàCnÔ²s»`Eð¹Ë%³O“€tuÛF¢Ãj°2 ¤ÒB•[R‰eoã¶ yÿ†«Gs±^³ܷYZåLܸæÓ ¦kn„{ƒäjLÊ ©Îo=h@àÌ»érPëQ÷×V*u‡}ÇX X8ðŒ L  X ¿-d\²†F« (ÜœÇ.ü ¡; œ+pPd Ec—Þò‚>#œáH‚ñ€J¦¦Î¦g+èÙ…f²'K°ªÐÀŽŠ’IeÝ+t#¸dDõ³"@óTe8+ƃð}Œ„B…®+ £²°Ì«v˜SF@žQ5ûl1m :¾vA`]X¡R,#ŸŒÜáGàr†p1(ÿF<8 ^@˜@¤üd$P€Ç¢l–â¾—g]háF´€·EàS ‹`‚E'Øg2^|Dv¹44¬'ÏøZ!äQ•´jªx†w=jzDI1åÍ´ž`BGo>è•ÀOªX4°³ÀÅ-)`¥ {3ÐV 8ªc•D äq" €b.T±\:N¨T.u; ‚wÄM%»(•z• E‚D²† ¦Ã>»ÐG¶äe½ÂWœR$`"ÈÁ?èè”tFû|EO;£¸"W³° µE™ÿ!è q¸…H¯ùi'2 d\€–~9@ vÏì‚qTÍI‰!€<îˆCàÄиQ¹­ÈeÙåÁwr 9 Ó˜SŽîúòÓ(Ùÿd”û(ЉìÀz{¢3OµÁ?÷̧Cñ9ϤUOµk0#ð²1À ,+0VŒDÿÑ€ *q€ìÌgÈXI+.¯†vºKçü [.gÑ%ùÀõƒnH…p¬IÍ0raüUÓÆF¨8ÛÃâļF [§rOU q4_UÜò‰A@¾˜ÑWqÓ›4âÓ—Ô@ÿ±šÊPN8³%ÉQðâLo¾¹ 3æ•7Wæ+Y#NÞ!„S‚< ëbQK–"™Ú„7,úY³•+™¢ ìdˆjPR•KLz ±¨]/øÔP>UŽ*È£VW! Ô õÈV|2eÐnTù/X‡Ï¬ðæ/h`H6.q¡Týõ 'Ã`ÏÇ^ØxÆÚ+i –¯Ü¤`3;,dK䥥É'@1Ûpóa§Å#X×ЗP…q q 4Âr•U¦À2À P4@2¬tÕž¦Y§ ƒm€©èMpʸI Ê%q(CR$å°c!g(—ŠÖAªG°ž<ÐL0pÿÈ‚)÷Š óà–°À×^UÍþ‰q=#Ñ;›ØTjUÜB¦9_•(ëæI=dhN‰F¢|Y{®â¼yÒ䱃}®{•ˆ\… æ.±B‹Pl1D÷81¾M3/xÁpž(ÄàÆmzéwä ø@O‰Ó[‹ÀƒA fÅÌ(H¯~³À… f²›/θ v¸ïž†)„4-÷¡>‰i"Ĩÿ»'BlãF,êʪ²›R…ÓW5$r(Ý“å+AB.Aë¿rD9×xd-üÜv)àÞÿ/&™â.„òƒBMvñ©24é€s‡`cÿ–G/ïÀJ½!5O²EIõUo’Å þÔPõ¶ €T:Ç‚§å‚€ Pð=p¥Â ¶Šà2 !«ÀHuWM2 ÷^4"yFè#8þµ°ìÔx½ÑGdÒ6(«¢ ÝÀ U´h• 1;@0æâ4-&bCt«`p, jY±'†Kv¸ .² §•OyøG PL Lî3!j`µp5€À 04Cñ‚qš²:‰ˆÂÞ„ Ñ)°B[”´Xøç de„àNä¼QBÿ%iû‘GÕÖœK•sCÜPð`òÀ»yÿ)ù4#¬5öEH…`@Të1?,ÐdŸÖ‡Çˆ6U5S€ÁÕƒDÀnÁ%þÇ%w .ä¸òpŠ#Ôý¢) |¤ô±6Öq÷“€`6°^à3¬Ã´‹P3jùt|e40•1,0pðjPo$w‡… 1² &~¬`_ÁÈ&ÐŒ&À‚ð2r馘¡ƒí°vWý×9ŒÐ@=´D7*1?v!yãöÑ1´$ òÁp¦„ «@ ùi´?®r‚A¥t@ÐPeÕ‡0’EÈ Ëd‘ª ÿÆHõEH-€£æ><‚/eBy8GØT3<è+Ù‘;Y’?£7?a-T{a T†ƒÁŸPµ´*@U“7FR ƒ%à"TçU9ÓÄ”zâKç•‚YµZí“ ¸¸`‘'¸?Ôg‘øšÄˆ–ÇBs·ò˜—° >A+C"¹a%äóiÀ9ä›4“kÖfž]yæ0ž·28ˆ/ °6^Ñ!µ ñ1!t5Éw^óô0ŒÃmf™O` ‡'XøC_ë©'BÄ'ãKJA‹k ƒi)´3óteC:åqH,á>À°ŒC˜®Ñ áP\ÿ^ñKØM$j])ŽÈ ÑŒ´‰Ut„VR ®ž2àˆdd6¼¸ •°S»õ)~tÀ)r,n¨•©ðiZó WǸ†- n7jC‰p@l!}ÔŸ.R: 3Á#ã!oVVÐ%'&.&B*(&vìP¡MÐbøP¡n ‰pŽ ˆ²@H´9PMÑ+¬‚-þ’<ð(P©B©8% ôVvSŸh"÷ç¿×¶ø†è ,r^Ÿ6#(CIº :&X”“:ˆÔ™ÇHA¤-P:õödkù_° ÚWZ÷Wµ .É€q"W{å¢Ð  ÿUÑe߈1ºÓÌh—62ÈaJîóyµuÛ@/@†ÄZ:s1Åð}°Ôi©,ŽR,P9–èñ©©ei‘:6j'ô©uÀƒÂ¥txÑz‘8‡HиT"@oðÔM !ø˜OÙ™S*¤IMV_ÁˆrG"”k`f%.ûPõÕ P­Ð=4I%¯©©ro¨áO~zðs¤i‡#6õ©"B×=;§‡êH pHĘF8·'=tZªáMñv o,9“‹€[©D7?=»¯ŸV´WÚƒ¡e@.oC/RN†ž%#0r£I¥â‡ÿªp )D10ŒX,#· El¾$jÔUBA¶\‹U#ÙŸÔW¬„€‘ªŠÂd械fꉈ2‰gú¡pXfðùEHÅø©Z* KÐLγp¥cpL¬À#`¸/àGÂÄskñ@[ÓD©0»P°?Ö–©7*t­›¤æñ†û_@ZÂdHEi©;4\¢%J™~6-úÀ€ŒsZ¤QQXÌŠÈÒÊ‘gɇ{È10§2hz«'í£$û“Z:#@·ºË“r îSoçµJ+t)K~d¸]Û³1(#¥ÃKØ{ZÆh¹.0ÿÊ!sä l P´v4ðj‘À p,~ä"!‰H2’Á"“« t ±C± œ±‘›±_ø ý« ßV£©0õÖI ‰¤÷[~ 3*¡ˆ …6°5ã!eç¥hùPvÙ³6|‘™æÁX‘ilY¯£;zV¦=n£ú`Å4àÀ -z"pi¤~„±YúU?¢¡ã44•Ç8,Lòv ½´}lˆ?t0PzR@L9j‹ÃCŒTæÄÓ´¨…¸Ÿ©hŸ9{Œ§å"ÍÈéÇuøZ_P+­üš,D‚ƒ7òà¯IGõ ÿÇ’XÈ+ÀuWÅŒTÌ ‘÷$­!ÿÖ°¡€ qìÁ@T­»ð–æ‘–µ£¹²9p†¬@®÷[ÐmrX)ò–ec :’æ"O’·ƒH„Ëq|‡£¼¯V&#´iyð°~4!Ò€@è½Và Å€bú£/28 )0¢ÉíGm©žàfÆ?0Bxpa pBXa¹‡Hеij–Ý£3ÿq0Ъ pÒ Ä‹,¿Ø,1°¸‡¥¿Kaét2•!…3¦&øˆ\—¯Å5õÊ 0’"¬ N&/à/0CM4ý|'á 2Þ -ðŸhùG1ÒŒNVͨ|BÑ- lÿe* "À àP,бÔZ­%PÐÌ +‚Bªuk tR à'^i‡ e.kÒ 0 ð@_ÑÔ!@o#ˆUlª±¿§‚§cËöûÕVV©B4Ô„ô:„? QÁ±n·j/³÷ˆé‡øFÝÐ)D$y½hi›˜œŒ`”i`Ç‹ó¤1@Z Ã= &@3P 5FáKÚ¼Z U,ppàn °ØÒae1µ;Þã e ‰Í ‰Ýð8ÓKKéd‚YVFBš¬8ÐkÉ‚öI}O{Ú! ·¡§Ð€ ž„ÿ Ð §áy 3[d• ƒ‘gHC}Àǘi~ G1™NÙ ¢ÒÊ(?"àø¸•ãPß=PΗ"ÍÝ0pý] Þ‡­¶pÒØ<ÞéÈãíÚ!ä9ž„­¢F0#`"P8ô¬øb$LO–~|LÚlYðÂ;lá¸JÀ ;²b]$ B²|š Hþ_2RùlÞÅŽq'°'p5 Ö PÁî[¦áP Ò‰¤*"è½ãµ‹ã'Þ1ÀÞ@Þšê)ýãJžä'½I±m ¾¨)Ò:)2!O> ÷·–ÿ ¬'N6¯^]_†DÔˆ”ëxî‹y¨³û¡u´`1|. ^TÇvà2˜Ìôc¹v K wª“e€x$¡éHGÂÀ: a.{X2cn Ý9°éÐxy;§Vœ‘z2’|8º;d?sÆ@ ‚«“é<žp8K¼ §CH†»–s|‡ pçŠÿ°Ä̆šQÞ–˜[ñ`˜Ïq‡V/¿« e<®ØŸ>Þ<®¶ã䈄Þ!ð1ÒëU¡3eôg>0M(8õ çËåψHÍ¥s¯‡/H0b¨SX%Ðü­=f§P-îË 1óm†Ÿ©×[|¤1Œâ”ãÐw1@®Î ©Ué@¿kÙ>¯Ù‚}ØÍslúT‘&mÒà„mÍUƒÊ !)@ e§plØBIH €ìvÿ׆#t`I¿Ãá•JX”‰Ôô‘|LïÈ<ò•~_æûùP0Î uTR~W%'S"«~*SRT$>XìÎLOM(öXLM!*R4 VV@Q¥ø¤(â$Z XXrO+*$˜™ˆbb@*Xb˜cô˜M«ÁL |¶¶24¤ZZtOvJQ~ÑÎ>0öMÔLL&L@öMÂ/1„H‘FN“$©X¡â"N—*.R!Ù?{Ú¢ïŒ*)kð ÷0…ƒ‘§òìh. QPÒ Il €>k†o$D@Ä ¥B€„€ÿàÀT‡2§X¬#¹U/0ÿôèˆÅ Þa}òÅ t£˜‘Q"Œ“ø˜{·®E;_0(È£«MIT"_JQQeE†´ÌÔ3fBÛ`^Úõ× ¤ ú,Z…´%uf:³ÓDz¤CíʸžÑ &ªƒB|@ “ V¢¾0ˆÀ`ç)?ªP`ðâÀ ¬à Q@í˜,qZ‘q¢¢ –"¥+pIiÏÄI,A{§ëRbtеÞX( 2f³cNY…žÛè¡„ˆÍƒ>@­ƒÖ ¡PŠ< ‚©¸(€¤÷‚ ·X  ˆ¡§z(xC0 ÿ$vÒc§}Œy¡Åž`‚Åèb8èR‘ô@"Š:Qä€&7ÚCŠeœï RVDò„*<ÜÊ=0ˆ…Ì03àÃL¡l‚{’JÑša- ª‰-¶"€"¢ÐðÀƒÍHÂMƒ:È 1Lqc¤@Ò` ^yc‚²z©±Æ²0XãdH”C2È`® QÕ(MRÈNÉ 8)‘ZÀƒ 4¾o$õû` +ë"·”vù@4m ³ž‚Løbšbx!<­Z× )§ªMPSXû° h ýÀ8tÐíTR Z” E¦Œ7” ŘQÿ*ke‚æÀ€OV˜AKR¥«NÈ¥º¨B@ô$ð`CÁBIŸ+[ÉìŽÎïBÍ€*+ùXï#l²)3+GÂÆ4§hvªÏ>a«¹ælwJ Ò* r¹AY ´aGƒ¥ÑeØ2EÙt” ìÝøÖÚ#øú+0 Àûƒt†ˆa 0…Ü\¨’ƒ@!e€QÖkA°S\Yo FAņ[JN¹L©‚½7˜–éVçšK{†çÇ`¡o*¸ó £ßhÂ:Aø z³.¸ˆì @ ÂõÊSêƒÒ;X·g§å•‚ (£L²Ë–Ä´ÓÄ ÿ4 ÇYÖÙ@ZGºzZø·KxCºTWç¾ZCæÿ|¥œ ø\C'^$ ‚CÞ¦1‘ÀlÐðÀØÀ¡îÉ ´À¨À`죓[I¦ ¶¢‘¸Y˜Š)öõ…ÊäRxªF¶0¹´`[§Ù™SÞê ÑDˆAˆ ð°}¸Å+¦hT?ׄ¡‘aCh8€ ÐÌ’k™TéÀ ˆ8l‰qà—:`€\E>ØÐv𣠯bkò€DبƒÚ¼ª:pPƒE¾ò& ¯ =Ô *0‘´z  ÃÆ0𱫫õ£LR œþ€ŒöZöËÆj¸Šð!0€èÔ)Þñ˜K*SiÀŒ†ÿ_Iò"*¯…@4– 0ìã¬@^ Q@+ @…¸Èl%‘‚Ôæb"2è„ €ˆíù ‘¨‰ àó=Àª×ˆÆƒÐ-6øAÛàÑœNe)3ŠÜ€$_—J©"›c²ÙäpÞùY¢c’Ÿw<׬”¸º•4¡V­xÆQô`‰uˆ½’|‡´J€$¨!a¡ÅÂŒùNTŽñ°Êœ?@x@‘Š,@pce¦4Ä`AmÐ -TtBˆX¹šƒ¦ 8w?zXE{d 9¼ÊRꄯµH:y¸ã°Ý®N™8j@TB ÊÿL'_Ä …A¤p„œÑ9~I„£¶¹ƒ)XATc È–™…6 ±š.p5&;8ò!€¶h:ÉÆÌ˜§´ÀUæn¡q SÜÇéð& Z6Û“ª‘¹é…9(N£ ò_%$A$ˆ‚*äjGuù/%­£™£5I8 Hñ ¬Å#6:S Iƒã#N(€8ðŒ|4Ôç°`R¯…Kh™0¾²ÀWøâJ¥Í¶µ³m™ÓV|"x {Ǭ aêe®‘¼|]èh\}'’®H`€^PƒqÈ —µÅÅg«ƒ(pü$&ñù>®kH[ÿM»Aù”V˜÷ðáή¤îØ•ZDMÙôºYà-s«ÛA>#'õ†²4G#š/è?WŒr?x8uÔþ€~ÀèÙs`U’{ WvÎ vˆXxŠMï€û6€Ï/wÇ ƒb¼y®È±=J;í4qÀRÍÈKA©QÄïï:“Ms äÇ ˆQÊ䪓;ÜwLƒ`n§Ö‚†jý¨7ð#h @¬eÐÀI†@B]à Ða½UŸ°Npë ‰Òº›ÜòúÈÀ†\ UT@@ )ÁT~€Ð` ‘¦’(é6 ?@¡ÑàaZ V@lÊW*&@pB+àRþeì%G i:ÀùEÕ Éh†¦ f£6M6BÁW²ÿ©»z":ºBRàªý#10à,r°Bñ À¬ïéР´Œ ¥Ò!¦6Aa”DA"|`% ˆÌ§ã0ÚFj+Zà",¦$wjpÌWþ)0úÃ!j¼væ©Ü v`1T ‹%°aÝÚ!4`L(à°d Ú vr€t!]OÒÇŽâžC:,ƒ `~LåT*bØf ÿ" ÀxŒ@×ÀJ‘Àª £a)`ô†+baÿT€ˆ`2×Ð„ÅÆÏuòCVcÓ ÃØ!+Z€£ùŠ4¦lÊ’€\b@•”GÀÒ0+yÏQøFá†ÿkq¬O *#:0À²¨t 0 ¡ ‰+AžR¶Al ™*¡bˆrÀC*Iø„¬<$–.ˆóPd©yZ‰®ªHW :£‹Âk)F↦HÈra¾v Þq™$ Ö úÀ'%PR!£'P¡^‰yˆé[ Eìá`*1kF þÅÙQTÀˆ¤™D,pŒ$/¤Ç£âIôÀª6TQl f "@\‚o¤ $Óe¾mJFb0ìh-8ñ£°€Áßê ªjÒ~£þàÎóg àˆDϸj¨†åˆÜ/…<€BCî À5ACýò ^"ÿå_Úb•ÚS^–ÃL‰.îÁê¢>XÀØT ¶4ìR`h°à#?†38°ð¹¶"<Ža|–Ó$¥|¬+д9òAÜÌð8xa›@Àçþ36b``ÅàñDþ+ <¤i¦C6`ä”G‡þjŒìMÙ@N‘ `) 4‰(îh¦ä± l(íȉw’iEo¡05Á:H|€F5Vj€`¥°Å„¡ìBéOÀÀJ@"G 'ªIkFAFÓPãJî¨ì%w¤d‰^â¢zŽÝš¨6È¥ B ¾DÀ ÞA ¼êm„÷pï|H3ä0D œQ'… ´–biÿƒñÞ h0:À¦Y'à,Äé,¨“Ø<Á'ݬŒ$='ÂØhLD(îdqølÒRHY< s2Ë ÚÂ^øÌý„Ï&T'Kšcñ‡ƒUu&£FÕ…Îà7:Ä và†jcC‘”Wï±Æ¦í2ELÖAÑÎH+¿,j„,` *0l®OˆgE¿3Âð¯`eV~ÄTfJ<hL=L <Ï|I­àScÿ@: @ÌÇe^ nHÈNËî ÃÃtíDnäÞ¦ì\#Š@_áAÕ¾@PuRK‘Êî2‰AMH÷”‹÷"¨0R!‚ì´+¾¯š°iœl ¶dœæ€Tàªÿs@ ³FIÄLÅQA²@/š ˆs0#‰ŒÀYæÐžÄ>ÀÖÀöE|Б¥^ ÐL3wsÓ„ã¦ðºV×-7H7”â6¤ ê’ ™Òd¡îîñÃV £ÜGØ@"‚v@;Æòf õÚ¬âJåfƒ ÀÀh Á,Ik=@?š†$&‚2!&$€‘¬ TÑŽso‚|ÓäºÊîmxô&ÚÊs7å4Ñtc+DL@tã{„sûTIwl„phúSÖƒzKl2͉P•°{” êâ":áŒD©Ží©(ª1”`iCiX xRÿà ÌpOí­EbvW×—sÛjÙ¬ ¬&ÍÚ<àlÂXË—aD+š’÷8fêðúpÃVrVñ¦‘sðÊ‚"ÀN5?òWÉ€1 3Ö‰aS=A²U;þ¸õ|€©C(5Ø®?‰$PÄ.ä…^ ´¸ä6Єg1wÙÔs3ƒRÇôÏ*àB/t€ )Ik' ¬H¬%qxû&ŃŒ$lñczøš£ W8¥LøéR{¥^ ›L 2 T8ÿ&{i#”j„fxé<  D€|.“’kQk+¢+ 0À²¯ŠÈÙÒ4ŽÅðó8ÿNTHä() šÖ¹’A)«W3çrL§VÉcb`‘! 4 ™¤îRíóÜ@ fd^ÐYebZ' tàM×€xÛL"âQ¶Ú6à`¥& `šiV[ ÜÈWÝÔ;ÔíÐ`7:€[À €´w—møx4'2I E7DzŠh‘E*iL¯W€€aE¨Ù¤!%¯Ä8Ô*‚Z‰\ÓF‡ŒŠã-{"‚ʈJKJB & *ôÒâªólA”W DmŽä"ævÄm- îë\ "¬£!â\R˜gûŒ4åÊcP2J¯ê.––(Ãííl7\ÿ0wà¡ÈÓüÒ¸ÖÎhWÄ‘ Òcu"&ipFÀ:J™ŠÕÈÀ*l ©šxŒÄ:w  îË–9÷&“$x„«ÆÞq‚ie˜©>Co3|…ˆs‰rÀJ*#’ޤ®†+˜CoLàšƒnLÁt°Õƒ§n&øÓåRôA:Œï,``ê/R@hjRjà[‚1_ê"&BVj0kÂD¡wCÞÑJ«u@@ I¯ËeFN¡ .­’aÚ'­Ý K0®t»$¬‡Yáª&q§Î¼iz€¡ù#òÅ£ƒ2Ö¸=;`º ¹íB„/Œ$º,=üÿ͚əôê–Ùªä—$ô;@Ï á‡ù]f3å˜T2¹ÜRBÞ£b·2´r®Ne³ìT¤štìëÛN‹›tÊœn ú¡'¸‹{§C4±I4! 5º€\ÎF©úVÀUbhÀHlP¾%1HÇ* P äFó&*v+¯$$‡A?^“n‚ûàJJKÞã˜P!‚æ ê`ŽÓ¢…N(nJ"÷[n¿ { 3öa”ÀÃÓ¡à…zàGtàÊ=¸oí Kµc~Ày! ,᥼€ ”dʈ¤4€¸.éÏ^‚F #%ÀðyCIAÿu¬KºNhˆð»oŠˆ)o oê» ”çQôpRfÛã/Öš§r0$`¨Ö–j<Æô€¾´€ö¢`ª`t2ÿ€ƒ‚c‡á r ä´ÿ‚¸åwH^KN c*O‡]äÉD!±>Æ . C×" VÁˆ‚)`²È@'1d¬´xš{15àTV`´û<æëßàE­³>8AX€Á8¡¦¡“†!.ËÖÊQ êé{+3À HSàŠ ð\œË⊈ÒÔК‰b}JªDëÓ*FÂjÚ󘪎ψoQ9Ú³Ò~ÚèV\K?0‹›“ž‰ ‚ë" ¶` 6Ä4`Ø·^6ÅÒ’Ý%á@ +Èš‚žÄ‚R ÄU0CPÉÑt CÙ&ÁWl`À…’D"°ÀTS»5³…:¥pC d°Ã0¶Ó†`lÃ<àÿ+MD&lC†L|` 0À…[õ°p¶ ÅŽc}%qAPÀÅxkP—{Pð’H˜à‚-€ÃŽû1°¨ I&³´G(¢' ‚2™,àá²d9ÎxpÁ¾H“PùT4S<½ÀÀ¢S©QcÀ‘M-XyÄ!r) F[0<ðœL?²y¤ ”)Бٹ! ˜÷‚˜ˆ4·„p€d2*4¹iRÆ‘ÜäÃB=bu+ß eáÅ€ët+A$.Ag>„u"Ç*¡£èÐ@ðâT`§¨1üÿÔP5S¡9£jðp›VpŦ6£jw ànµ+d0 !$ßÈ&dÄ™Ï õ…„,/ ç•ê®+>ø@Cw†Ü'°ò° B Âd¶Lbºê©Ã]6oÓgrMàB=8`”²$º@K h Ÿý06¾¢àB ÿvÒ™-Ô1bL]ÐBÈLqÌ0§Í|¨ TxÇæx€‹P)žÄư:¨‚áàƒ Ô…­Y€E9dDLù™‰êDðlbÄHp.!è0wÝ+ø°Ãd+ìNê:—nláÖ‘6Ë‘N#É1ÿÁ:Ø ‚&HÂpC- U¡Y& ÿhƒ!\CÅÎ0 ÔoÇOéF@DÌø¯LœáVt*@ ;B¢B<d àP>º³&}ë`àÑ(,äç’(Ÿ`ƒòŒ l`ƒ¸O úÛ~04¤$É û‰€ ôðˆáWÌb@»$À€äƒDÖ`‹Ûé€ZP¡X`¦ 3T‰ äE7A©€ÃVã~‚Ä "ÒÙ…©š•A 1åàØ¦œ²0,°`(àPfЀŠdÈ ›ìÐA«mg _P@íð×%pÄ`C’À¯Hèÿ2pYÀXÀú: O /ž±Aæ4=7¼ÅexKcj7‡U2ÝbA%,Á¦$ÄY&€<‰ ±­@X€UBI·•xA—¹‰‚? `(“3„ Ö?ÄT“uÌ8÷w*¨&CFÀR Œ<Ù <ˆit40ë-y!ƒ'µ±à °€ ,ІV‚n0´{ôµ S)HÞ&°'èpM I%¢ ;n¨® NݬÂàåØ  (ľd§¶Õð˜œ$¨õ3 à§{‰8*XÓVx€áž1^ÌÑTutƨjÕ™ %²È“,t‚ÿ‚nF{‡òŽº<åÂrèš”%Jñ -€Ë¹~E%n %ºŒ\p„+€RÒ°—ÁUu}ÑVøä)ðÀIö©Ð£ÔJhµˆªÁê×175bÉ ·?g0¥T“‚¤o ›˜å @uNöxåM÷\µ@♂ -JVgEš@Nø˜}‘f}ŸÈÓFаò°‡&`Vï"€®2x‡Y^H2ݲ4À!;N6èIņjådʘFðàhMe”ƒS Û,r¸6úÏëޤ5óÀ¡ÿ ­´š2$àas:â8’°ƒuJ<ò´ƒê &‡v€_¢ã @¯p|÷#ÙÎFš…7yg„»ýœWŽäÚ=äÂâ½€fnðÊ@K²Ø‚-rQ>ˆoL!û®(:Ü쩪iåZˆjÈP¤"Á%v9Ïç¢D… t@K†B VÛLC,]øBYXð$~^b…Æ”D,@‚ÐAvx2òÖN m`BšV¹h‚H ]ƒ>¶ ƒ+ v+>öŒ{?€¶M ˆ‚RÀƒ”¦Ä#¨‚L¤³)Dl:ýo5 ¸ p¾5Û2¯;$»‘1Ò;ÿ¹CÂ@D£Te¢U8jp€Dàû±q`­!9Šx¢Á ”Û>Ó j&üÀö<2 c„ùh•ÈÝéN ;p—ŽÄ%C ä€l‚)ðA”ë›ušcs@Å8Ðɺ"ú5Ç6æ¿X³€ùQmzíñuk µ‚Q›ÐÜhÄ1 S&ìwî‚€Ô@\ &d×-y¹·™È"Îp ¶‘@L ó`â‘odÔ.J\Â:æÀbZšÖ˜î½+J¨}"ë]P¿#?ÁuŒfÚÀ2Â?ýÙzS›•ÕÇën›øLvòeÿ àÛŒÁ¿Àå"dš&ptÀx³•ݼ쀡0TgÒì =R¯ÄÔÞ:üÊ?=Œ³thB´àÀ B ~fÛë @@Þ@=àSŽâWER êÄß~es8¥Dv_>Pa- lÄZÝ!íÖå”A×(!v…$.Û5Š&Þ  À±¶X@• öÓ`xC ÀcãeL°e8ä“[1t¬À:ä‡ ¬ÀØHÀy©” á€iÈ„¤€“uÊÐL•ìFSøÏÂ\–­YÁØËl”\1_.hGtÂ|ŽCUÂuxEÿ{8“ÄF Â$!€Ü %­”@ð{ ˆÎtA^tëLÁ[ØC*ÙÌW…D 1€ ¼@ ˜GÏ@€ ) Hy K,,H,ìK,àÀd•ê劸‚‡ôB @DXÜ­Q‘Èà<+XO—½›x¼wyܳ edÀNH„îèÀ´€{|x,!DR|‡ ÔCW|€‰R´ÈJ „Ô œ‰ ÐŽ ÈÃçHÅ8@@æáÙÖ€-ñ”ØÁ¼±À½˜ß^Ø€·Bƒ’!A4@ ˆ—¤<€MÜF, À ‚ÄåÔÅ1Ãÿä¸ ^ÍzT’¶0‰@Й[ðÚ”JŒ0Gj‚IÀ[`ÏTÈA|ˆ³¨I è#w½ ¿©@¿‰Ò…*S ¹> ‰tÁ¸`ÀXɃ@ÀýÙŒve˜ø íÈôSoÝÞˆùبUŽ0†ÝÓp† Œ\¢˜ˆÎWDHFpÈo° ¬†¸Qnè!á@Ám`@ATØP‰ ô‰hYÀ½œ$Ü€ë}@ Ì@'¬(Ë ÄÁ  DBÒ¨IâK h€Ù'D @»”ÅëA Ëh²°Bº‰ºå ðÐ%R”˜ð”7€žèØ™;exDSÝ8Ö è”À:0]8€ 4@V¢€ Ô€$4€ \F-ˆåŽ×d|i´@¼@ ì‡ ˜9Š‹ZÆG­À•$O?A×p}Á>ɇ]\¡<ÄÀd*\ ,„ù5zÝBƒHä8,…R”ÃÄ|p„ˆþèÔ †JJ–,éLOüJ*µƒ?0GD‰ê„ÄFòš8@HeÀ;YTþ€ÚÀ¬Œ%©I•8ÀŸ´„$ ‡-(– 8€( O#õ“ÿ-ဲ€³Ð‚L'âŬ’Ê5 µ)Ñ8Æ(@ JiœìÕ ª \’ÊÝ äÀ ÅP‡LI5ƒ5¾Â4FòiVPG—pÉ[$C½¸tÉÒŠU*@ªè@ØÔ6Š =›D U@3v@ ¬æÐ¹ ÂV‡€ peÍ™Øâ1•AÞÉÙŸ°Ü q@ÛÃph•º äôCmA¨K=ä-xEèÀˆIŠÂÏ Ä4@dEXËh9XA„ÌÄÁ"pé7Q„ºÜ_å*fÐü51„AÔ•Í”ÀNÄ‚dÝmÙM4(¤Rº¹ix@.‘ÿ³º²Ej! æB Ø\R!€"š<ÉEJJÀ8•^Ÿ_¦Ëv¢ zðL”ð³Ð€(ÔHIfÝÊBùM4€q¨äªhÀS,Å€@Å5cñá¡~é×\ÇòAOOr¬¸„< ¨ÈM'€óAÊI·ØThMèĈ-Œ¡g8ÀÍ ƒ*è,4D‚@ƒNp°¸ ´Ä~a Lg›X'‚ÞŒ×F\(Q_ö¼;¸@G.À¿N† hTgìbt-M¤Æ4B+|€BÄFê†á™)Ýe„Cl°Ä Ì©ð̈«ÿ¯q- |HgRÁ+Ú€È"ih¤ÀLV…û€ròS”$MÝÐÂ@ ŠG_^%ˆa ÁÔjgœTÂÔÀí(T-$ܾä ÉyÓ –È2`SÁt€®FÈô‚QœÆÞgÛMÃàúE¬É‹±’HÁć˜ ¹íY0儾0Üou´1¢µÝ¬6ELçö’'ýË-LÒìÀ뽃ê¨Ï܇tF…nº¬˜˜Ì{(Ѻȇ|(ØÀž%)¬–'žX… ¼iÚ Û9Ã+à Ô ØDNìÂàüTÌ×Ý6ÄXÂ,Vœ”AÛ~(Ÿ Yæ$ÿÏ/@!(â‹…IZ´À¡ÈD`ˆÈ (½MÝøpÝ,™rDZ´ÔË¡Œ*gŒMNÈ 'XqÙ™ºä… õ”D›¨:@*¤¦yþ°(EBÝÔM" |‰N ô‚ÿ4…\ÀoÀäTÉ“q § Y ¼l‡ÄhÍèpPVØv ’@À”ô]ÑðÀ©»¢I%.$A™à q¡¨/6@T@„IRø@¿éK[}o€ß(/@:!Á‡v³¦…ñ€‘"@j™oɹÁ>Q›ó€ÈP '|ûŠ$pµk0¦)ƒ,BŠti4ɪ4 Âm]\~žFt´-ÿË4]y#èOX:´lÔƒ1„=n,0‰|t•ˆ,gð)æ`DSwœ&R(¹ ˆrγ .àæ +Þò¡ùèv®žø@Il1ý ì)YºËzÑ/+(.€€ÀoÄÑä †‡ÐÁ4N}V„ã\A¨‡6Gòv lQÞEDI~Ì´ @à‚1p’,4ÈRrl¤Ý°&Ôž 8¢&@@ äŠvøo}ïSè(HµìÚ·E@£Äq€@l½(ÖÔàíàÂ͆4P†©¦Z!¬y¨€ ìÀL¯$Áï‰}VÓ†tÀYXœ–úµJƒEÿ,eÁ`XBc¨ÛZäлÁ“RÒØ€ ¬® šZÞ%AX›a…”,lÄ@Và ìÀkmØá l@(@h›„’BnÝœšx1ñÏñl‡uþÊí!ÈŸ¾«ÜXýe¼HÆØ( U8+!(39(ÿ<™å8=O9Ü_¼C©EׄG[.¬“P@ ¬[8€Î\AÊ=0"À Ô@ àö -´E€Ušó®¹P hÈ´‹‚QÁ†Í‚Bá‚$–Üì\°ôÀˆ‰ßxA$TáÞ€ É(HÈvLÙB0‚¯RÇÅ,wÆŽ7‘H3ìBCøÿÕ€°XM™ ÉÕíiÒR>ˆt Îä‡@ Ñø;bÀƒ¸tHÐi)…N,$e deϦò"›‚ˆ€o ¥¡æ‰$`9–[ cºW,@Áz'Ôšjäw<†ýD?Ì €ñ„ <‰ L€¨*ÈÉ®:mÎînÁ40,RpÈL#Å SL8©|7A\ð%‡PPÛE—ýSÀS ­¬D)K© >½C8@pÀØyŸµM Ìæ 8€öRåaN‰+tŸ÷}k×Ò‚H¦…ÒÜðƒuý]ÔUG?X±ê´€( =èe´é hœW€ø2òÀÜ4¸,d2)ÄN,Åÿ…èTÆQ„3(N€“zÜbcÈA@ÔñÂAY Õmd+É_×D›ìô;¼Ö:tÀ ü’(¥¯b¾´ x Š(+ûtØ©Ð+·Ä0–1X×çðÓ‹ÙÙ¾³D@XÉhKµ4²ÍÉCAûPd„4=ß"ÌÆ}íχhé9Ø7LâÀØœP‰7(‡G=zHØŒ,™ô%Œ¶øÍü%/j…±'”Ç9ê4) ,èK`6ˆä)”-ÐBÒñÏd1‘^ôS^4¡ÙFc, M×4ÆCng Çtœà éHEÁPT€5ýOÂÃÌ Ý’7ŒgG7\Çÿ_ØÃØ{ÇCé¥-!ÒF:†¼€uÆûˆ‘‘h¡¬À¨u }ã tÇ…(…q¼ R '+ô…Ž«%^7{Œ®¨•1‰¿Š Àw¾Ã+ÔÌPÀ'1…¼Ñ,ƒ\{7AäA±œ):ˆ#Jï£èPR’à…UšA™«ŽÌF.Ä, fÁŸ¤9éìÈ9@m8ÑÀŠ'ÅpˆÆ*#§Ù€ªú˜oÞk=,K$® ½ÑòmƒÇ=h™ÔOÕ¸lX¦¤²ÔHÑ T`i*ƒÃƒÀs¾Ã%Ì1tšk&KÁ–˜(’–£²Áá,wAXIYh€9hiÑÿ`Áˆú0yûXœû Ä°d°xQñX@Pø%-M™I9hPI-…•%MYXSl9pØi8(.Ð88è„PŰ``‘ûÀ``ˆp‰ À˜ `À0 Q™ýI(@(úYHÉèTéØ(zxиXzj‘ˆ©`égA¬L/gÈC`€&&˜ˆÆ@Ÿn8#ƒGÐØ`@Á‹!2„K‘2¥;|¸ŠÒ†#†‰þÞ˜ˆáa±†R´÷c ª$h°"èÒXä1`A§2ЍÀ‡©î2P!#D‹%MLH³'B7‡'¤‘R\  Ð*ÿ@B8<Ø0eO+xˆÑ¡Â ÑpñFU = | `ÁÐv¤˜ n´|<ò‘ð&NáJ è¹@€ jr|„ÁÍÆìdÝAħPLe¢aÁ ×LgÕ½—U¨Ÿ°ä -¢b…ƒ :¼xÁ l ¼˜À`†l©M˜( 4hÊæIC¬'€…â4 ˆ &ô9*@ :¸‹@a€àã7ãšh­Ί† JP!ƒQj ¤6Ž ÀóDâÁ”t2Ø.ÊR£„£5€è½ èÀ?ÀblPǹd0‚ c8aÅ:t®SÁÿ%Hê*#SŠq…j[ %‚(ö^ `‘–@tæºN¨nÀíô“à‚{4¨Ç¨&*ÀB‚.Àƒ€د;2€C à 4‡:@@>p88dÄjá®6p£kB`g¡ìZ³)@…’ØŒà„U’"!ÀXªšf‰ Êq6è@žŸ’ã§Ä`e8–B < 1x@%÷Ã… pÿA€ÁšPœ5.C¤ #X`¡‚¦µ² #ÙØH ‡a’tLÈÉTCÓ@Þ¥ ‹uH€;w¡Ö^ˆ! &˜#)sŒRà^È.¾8‰<2âÈQ£ SÊÒ~ÈÀê Ròh€Èã}iW¼)è‘ %›@4ÚRLtÐ2E(l €dÎ ‘k€]"JDr•ŽÓÚã8éæi1ÏN7M–’"à˜1€5Љ 7#ÆÄò(û4rÑ+ "À ¬Aƒgöq”ÿüâ>Q´ È£´ ô$À@(/„’1ÿˆņHP ¡9ÉþLH¿ÖÀq|—P«½&H)‰‚­ ±;Ðd${e3†F6ü¨-f(Œ%5tÍ‹ãPÁ ˆ‚€ðÊÀˆqŽ–iç/P€ïÚÙRØÜ‚ ’@.,È¢Ã;RHÜ•Ê)ˆõLû=| ÍA$ËX˜nìPYÐñ}qª+-xÙ§…nŒŠ5àH[‚ÆÛ„d"ˆ"bµ Y5!¹#‚§[Ó‘À¢©« Ì«ZwòR 0Bd°€ÂÖ¢‘ÅŒq zÐcôÜ£èÆzÄ~g‘•:‘§-&,xãRBA.qØ` 7eÿslà€*®A7¢"ɇ_°V o·¥D31b[g.ÀIÑZVYtGptÅ tðUt€lß"(âAX,â.ð‡Hü–€â•f9*«Š€ püšzàgnã@ŸIq˜Å,Gj5çPGJ2` 22PsXQ$S„†0@4`à¯e98[bt ¡’ñ29‚áÀeG²Â©d)Ú¤  ¼zsî\8fNwÚ:hÎÏ°Ó 0a isŠÓHÈ®CÈ‚Óò©€Áò˜p@b’2†ù¨Äˆ= y=È7@ÀaP@IZÿ €È€œôt¶µí P¯X‚h©”@#kάøÃöø£/¨@u˜U² s­x¢ Ô°öȪÙM±ŽE4T ˆÒÊxˆÑjÛáaþJ®<-"gHÎqã 4ií» àîn`fMhA¬ ¼`³ÑÎäô؇×Üæ7'ñ$°ƒeÑbãÐÀ Ì{‹¤lš)ê¥5»6p'HH»ATuðKL{æÕ¶ø]œwÝëcÞ ¨cWØ1°ñT£´ç}7# `œÕ¬•Ú4yHoµ¡®ÝïŸf'$ð$ð“*ùQ Ñ—BÿÐíÛX Ю‰íƒ2@‡ „þµ'V¸X ÒxÒ—^š`Á ¦rR¯I.CQ5£Ÿ³æ¯–áh*e.  ¶c{Dú ÏCÈ§àŽ™`ô¦G~òƒ’:+N|…7sÖMõ½&E¸xÆ`rœ¤­t€“  že¤– ÙÀ•¿þõ§@ëhWo»ˆ‚P—g°g5 !õ(˜~7ë¨ñ+“̘•pa¿T¾V˜Ž' N8—Ïp VúX¯–иµ/H”éa3PƒÊ“ø6íÐp¹8XФALuŠqò@c‘ZØ«ÖX—À¡Cv»= Aƒª¤Uø6Q¨‰z¤(¬Á+T>u€™ß ‘žú½”S¬Ú€€Ø…Vðà Áø/k:€{á¤øÃÂ;¼Âù±8¸: â"‡d€B@3È* ‡•9 ÄéD( éC´uTÏ$ÜÔÍQg„zŒðø¢¡r ÿ4€Â¢,œˆsÀ¢Š?r†Ëh€£Ä%o¼â$²Ô“™‰aVƒƒsIRÏx5a Í#t§ pEàä.šcæw“ÀœÑÝ4c ÿÝaR—ê  ŠÈ00ôÖÃŽ ̽(Œ‡‰¢'<ä¢f,¼Ì"#™ éP@aæ)AkE’ñ]gÞy¯‘’A3Þ…·¦0RSO%æ 쀀.Þ50’6Ú4p€Ål1ÃrM(šÇdÎ&Á#Xt@E‚ô7¦0¢É)ª°‹0Î< ÷Å€ËBTœ“© Ô(ÈôÉÙ.ïP¶O}Åè¢/L :ŠÖ˜Ô€á /êâ  àâŒäÁà ¥ ßáBÔR¼ŠˆÄÉßðÛ@ÆM9©T“~³‰¹¿ÀÐ8Ç ÌæŠ ~Â( ‰xCŠ+>F¹z0âÿ 3/0îäxBAw~Àr`Ý«6¥8Y‡“ÀIàâ(«ºô:CÔ€Ø?”‹¦iw!AÓr´šŽ5 Ÿä" §~,{#ƒÀ‚âŠJ`úCo@:½-¾!Ì:û úeÊuˆ¤‡slbAîxç.4€`/XàÓx—§0)êN/`øXÐ:½`â~`L¤ˆ0€ðzÿ à½÷1L¨ŽM/(ß\Ô`MdT„ÔÊŽE© PüÒ ¹“-Ìô*(6a\ö²W1ó쬀-ü†7¿$âo£a7ó²Ö@|/,@ qpYWÖõýB ˜øz)ÿÃ[\¬È›p6<Á3¢‡z¸oQز€ ^öˆQœ1Ý B¬C¶Éë"óEÅ0$9U¤ycyqAæ4I5XËຉ„,HnèBT ‚Ìíg³C´s!\èòB“¤qŒôkC8LPêÖBn± RIÇ 4ã¼ómkZ‚‡ p¬Ðà/œvŠÔ,ØûÁÅäe½Ø *ÅÄPÅx%„×à,¥cÓɰ¦œSAú¾ÑšE¼ 2Éc /Ô1˜ãœø0M¯6q§œè›D¯øÇ5Ì#ŽtR»FN.,¨s,ðèºÐ…¼DbUÿÎy‘4×x‚¯<²“4’Á+½¨Ëõàš4pe½ñcPÁ(œéOh„óè蜩ô\hŠN¨‹NÁƆ²`¹:tQÓ3(7˜Æ&ŠòKiñ#,À-¡-FÀt üÆlðà.„ :ÀA™þ6€‚©€ É>za¦ID¢\׋€ÃñÆ^Hb4l¥¯ø »`Âov8¡0³Ã!jU¬†Là (/°H§’SèòGÓ×P iᇣFÙúDÊñãU>$í9L¤Èk…³[p¡5Âè6« =tR‘;Ljg |aKL‘ _Õ34ÿÙZ_6N! "ðbu¶¦–꜈ôÇÔåQpNë€ÃT¬½[1YÄ\Å+åhL™9ƒfé@3Iìe¿üÁÏîeÚEÅ—ƒÌ¹‹H]Ö¹¸ä³I…`‹b+ «»GH$6žaÎ¥ÎVaa 8 †€‚`¶t(µF2 ”&®5E÷//PQú ¾ ¿þªI7 Ρï\P‰Êt‹rÚ€W U#•£:SggØßFÕú‡òƒÄâÌ buv‚¥¤"a(£“´ÀÐõð·n pØapPƒE™‡´qÂÑm1«I›¦bAñ1…¡S=`nr¦“)ÿ& ‘p¾ÏIªÀsM›ƒÀÎ}-R¨€Æ\°d8ì¡`b ãP=3¹ñî^ P‡k(Ó/U °1S@ƒ ãÌ©]Œý¦¾òo,ègð¼ñÀò‘¢¥}eÏa¢ÌÏÈ%N)’¹ãÈkñ”áªhîÙ/Pç~½äZèX¶2èô_CitR¬ì›íi$x%ÆÖgÝ0wà‹iˆéTp3$H¦Ï‰=‚Âi›†KŒáÓ0æ”~a‰´˜º0pØF“ÄjçPÁ3½mG"¥¯„2‘F°¬X¹[P‚7l(û)B¹©¹.Ao6À:êRÕ¾p½mÒ4;ckÏxEÿ8h0`Mëx:&XÛE¡sÞ'Æ¿1ÁE€ÉþàÖ:"Õ‡$S.{ï­Pål_MÍ·øÒ øâkO Šº€iïR/Êc.èN¢<§€ÎQC[ÓœKëD¶@u-¢Ù†¿;— ¤9¥C§â‹. Ú„xʨKÁ,Îà’;psL£Ãøƒ(")OSõÜÐe<`^‹×v5T§–‹PîE²NŠöÐIv}áE[VÞñ õežVòË¡wÐû&Pö$Ë‹¼,}¨á°P¾t¸`¤@.£À¹½‹wép ½ð {w¿ (*`èð&ó“Äõ°F»·ÿÆW²§ErT[pI²#"£8~mç|†5r@S.23°2?(À/UÓ‚¡KH°âÃ[‹b"r8mpÝå&Jñj©ôôa 8Ãéðkûãqž…C¨ ÀÀã$¥3"Pç‚9¡ƒs-ØbÝq|'À±(p±1O¤3@ Ñ¢ ûã"(r¡*fòD®²EaA'à¶ä7‘¢P.ÂmÉ+Ô`Ky5(¬‘#™Ð ìfð“@SeR7ð ¡t¼V °,_dŽ! /Ó­ š%@l`).Üõ STXÓ>Äöxî¢Eÿ«@R491†5h‹2õ¤æ1cBÜÑ ˜€¬q ‹‚’ÑQ¾1åðFš€&""(/÷fè9  mZTˆóP1ƒÉàauÔ€U|¡g¨mð°H•È Ï€!L‘l®r—`xÇà2"Xèàh–ZÜRyÁÃ*Ã&ÕÖj¼˜¯f$9NN³dö!)ˆp/‚¬WHá2ÞaPõ2©)-7EyÓå¥=*žD#‡áŒoFZ¬ÒÀ¡vêž §Í€‡Ã «0G¡:¦‹»2<@Ñ$§ @âù|ÁaŒa9¦âI¡¤`2 –yOÁ9`§vZ6’!äh" dÞP‡¼…¢ìašè€Š" ¿`›—j‘ü`Œÿ¦<ÕÖŒj©p iY$ãe™9ÀÀ¬v:ùø ”’L–Â/|Ål1"6`"ú1Qy9OgðNé6¬ žÚvÌÁ<Û©“À*¬°¦î⬠{l4˜PPÊ:ýʬB†¢ª‰&p8¼5Gâ—¡°nD/Õ• pÐ<ºsRÓ®lUš¦+Ðúa!tNæÀʺ¬Ê:„Ÿ1ãÁF+'Ü#0 5ÑF®"î“Ö:w{¬Ãj$|$©‹è$¢P©ÂŠ©«PNîw4sB ¡º¬90üÚ¡°¤oÈ )ª @ÿão'Eˆ! mTax Þ Ëá± ð˜ê£QM¸æj˜pðJ¬—U9ñzBÀm˜Ð²IZÀvà 2#Œó(¼Å+´’uØ ´ø‹àp/F˨²v» RŽiM~óˆ!Û£#; ïà.„˜LùZ¹ã¸¢Ê¬ÐBôÚê k±Q_ƒfÝ0ŠF¥I ƒü°°°€û¥èðû³&'‘l"-ðz±ÐC ô‚ˆë¯[[ µ¢jÐ ÿ¼.Ð#@-sR 8Æb°?½PHå«®Àð{`ÀjZ 6‘Jˆ)<£¸ÿ»Zqo¦¤ º;_Š›?8¿Ð Ð:–ãÑï;ó$ Eá ŸrbPgQd´` ܨ$›#öÀ–zºÕ@4h.ÝQ§Œ«»Ïª¸p@ Ð ÐÐ’¡œD0õdk8x‘sg»€›Œ±wžÔ»T'2&P”¹ƒ´=*¸‘+:V§îiÁ훤?<À"0ÐÃËÇ.€L5y´E¤‹3l›h*¯g¼(/Px ƒ×k©¼# w<ïÕµ«¸ZqywO$³@Âd›ã4³ wƒ¬•ÐðŒÔ{ ×;ÆßÀ‚6K2 ¯Û£ÿxÃi¼6‚ýªÉ. §‚0R}Ñã‘®ÇĽ©Œ/p €µ¹ðŹšÊj »PI“@ÃÆtÚ¬-Ëa;#e«àõÛìÜÌ-´¦Ö°ÁŠ·Œ±çžàü1  Óº`Ð`±¶É9êÛUæŒ(ÀÛWÃ;ÖàÎÆWE¸l´,aL£ú\º ¯ñúSGq"zS¯ÁE+¯´é¥ ¬Ï0.0-€Ì³(Ñw6 ]z!°  Àª±‹<¬Ó¸9º]j©àÓ3 ¬±FËÒkÀ<½µyÏÐÁž€)Åã$œG‡ÓÿÀJ¡=íÓK¡œ´À`´ð¦0>]×!°ÑáùÅÒ+¾£P›Jhš×€,­uC¿šsÑ$Àƒ\› pÖ@ ¶)ÐQ-¯ËÊÀÊ·)¡Ü K]× à™žkJÖ] 0À´€ÐžÀm àÊùQò'»À29}+p g=Ú:m›)= ŒŠÙkÊ£âYw ¿]×@l±¬|“Ù¬,ÆWf~!c­´¿2 ìmaò4€; P×@È™}Õä ?}¡-Ú?MÚxË×· ¬3¼ý4òÒN@ÌI¦.ÑWO ” ="½ƒ} ßÿ?ß‘¤íËpŸ-ßüààn±À€ÜP#©Ê|5 îP< àsѺZ.0LÌà˜ÐÜþÞ¡jÐ\k§9ߢޣÍ-äpá£ý¡ƒÜ>ŠÐ v ˆ >ÀàÄ-Âp¼ó ‹Å @Š-µçÍÔFîÜ Àö‡ Ž;6þãAŽÞÞwýÛM}›ì-¡°uîUÎpA"‚$¬c&œpáÄÐÅ´¡” ™ÁâÌíÓ@îÙF:(ªKšÞaþÓ!ßéíp阰ṡÈFçúH]üÐ#ðL<¨LLÀ@è)®¾`ºæÿ‘íËíé@ݸ)$uª®7^×—î/_þÞ«m©~ûó(·àŸª.ÐÄO ÐÓ®0 è˜`½3\žkjÀ}ç½Üæ½ uŠ þ:ÇÎÔ£í㢭ÐÔæÍè—MÍÂ]ß<šE •uæßG Z²K óÅ@ ?ÀÛ×å î·9º _ÙÐðÙaÕÐÚîñ>ìó½ëLý·[ò¤p›<úÂõÔ †”ÃÌÌ…£±ÊðÄ Åä}ÈH^±ðŒÔAÚæ^ã  ïþê¸Êíé ¿˜ïmßËÝÒ— ž+ Í»g gÐ ˜Ä ¼•¢ƒщMÊÿ5ÏÎ  ™ ÝßÀ¯è«¤ ` BÏéÀÚfþ ŠôýØðnï˜àññ.Ù¯ÓÖ ÅPði•¤ö¢¼©œÀâ€N¶"¬Î OÃz@Ç£º¤¯ëñþÙpŸf>?ç äoñŽ~ã‘Ú^§òº ‹Ð ¬Áõ`:ÖYìÀ;åöã´­`ÏóÐ`ТjÁîyëEŽÞŸîùºî/<éÉò Ú¨? ¾\ýá!m=B"L´ÂÝ6Á+ü>Ì$»Ò~Ê! Å¯ØŠŽ Ü;D¬ñ<úžýã ½ÜMŽÆH -š¤ç˜,‘Q ñ²Ú`ÿÙDžʽH~?T¤†û h‘Tª!ÿR–ÚÑH½ñù|½¨hxF\@FZ<¨ˆ& r * &( 8690 ”¢4HBžŠ<¤4V‘„Šš¢†.dV°‡ ‡ÆjîR"Þâ‚„õ¦Ã@в³;@ ]Z:Qÿ8 8, r&.ÖÛs@BOÑÑe“D÷5ð§!`­?ü¼\ðBÀ0œ9ÅÞŒÁÂjÓøôÒöă½C- ðછک«Ä®;,Nà0"DÀ^~¡Ë' `ˆP£ˆ<À¢tà §D%ÿð ªÂeÃ`ðà S€ 317Í>¾ÐD "…Þº%¢`Ĉ”0B—cž¥ðÔå ™C·'f!ÀѪTw-2”è’"¶²DÕRõP 6(ü!AØd£Í*C‚ C<øq ˆŠÔRðAH®‡–:,â¡…‹{áµ{y)¹¦‰¡G¯íôT¨µDÆ*e*  N1„Wš¹ZCÂÈm 5Ö(¬Õ@!…‚hpDHëµH‡!Û°AÇÿ°IÀÀlž€‰v:¡©¢‹1ü¸@—<ˆ*4~8l€€¥¦Úl :ëì—‰†‰à*ˆ’fµ¬ÿ@ î¸è6j.j ‘ì²'@Q î Là7"4©é$ðÀƒ 8@Â)SHBôЖ]:Då®/3…‰6õ¸&,ÓÔËj n@!Í7îû€l\/Éœ†´—F(²H ‰èDÉL$h €Pè<ªd ³p6ì²ñĤªÄS !j`ެÔH!ÞT†¶7"Ú¯GȦ…¶È’áBI@."rx‡ƒ'ëiÀ¶ BÊJc¡O 8á¢dè̃O3ÌŽÃñ”Â`¨H,ñ‰˜AVk. >× 5…ÿÄ&­0hÐзâœá8Dâ ô„ž¡ÙžH‰ˆ¸Â*_Pá°;‰8€$ø¥3Ç,̸Cд€ìÔÎÊËX%˜™UjN¡˜ækH7,ºG@õl@XBˆû#¢ýìÀsÊÁ¦e³[§ÚˆU ¨-`Êl ³´±©(o H ·ËZDl×Ý'ˆà¢Áx•é™}¨A6‹ÿH€!–]D¥^²ëž¼ê†‚£œ! ˜‘¸x*l0ð€¥³àÃŒ=ø|*Ø& ;tòm©#dƒ×1ÐÛh‹ã‡hÅ0(|šµÿ Nxd7·–€3%"wzGà™ÄÚ"䆤SÁc à0ÆåìC, pÊB©ŠP¹»W¨fZ6ýT©©¾JE þ€Ž”/x)PÀàT¤+A”ÄX(ÇÂR°·(ÀàDJÖˆ„¢|ÂFüƒ ŒeÌs²«¥Ô `Ê*< ܲ°”-„‡2°Å¨•M…DXÀ€¤ *™±ÅMÉ >Í àPІ0ÍNäóþà¤o©€2Ç\`9Ù§`‘“À"( a *Cø`¶1D 5˜#ÿBã˜ë` 5ü$œ§6ª´ŽA$ÿSÚØ¨¡ `~P⽂熆p4ÿ‚C[|Óu ŽX˜ ¸,baŒ¼IDQ9¿ ¶±|8A ˜6¦“ÀݰÀ2¥°L3¸ßyþ ‹-¤­<«ÜBgdH¢A`V¶šMá0‡ßY€]¡I{í?ÐdJÆa4#u¤ÏA# a£üֱ̧q…'8Ü ^¡»Ì†l´!$F„Ž!B†•HüL§Ø˜Q) ‘ÍZôЀ­Ló_t¤Ë84Yb5¯ƒØ(¡¤‡,êuÐH”%¼A4ü=áC\+‚ølÁº]0€(ArȺM±n 2ð…»ÿ€r™~È0Õ"IT¡*¹aMGò`‡)Z’ $į0^ü§r“‹ÕÖ Æ^„ Ôä¯Hòª°t¨ëxj¡¦´®ˆ‹d†~ؾ í°sp[èpP¿-àºðP. °-y0Þ¿Ø09eeQOd‹7SâÁʹÅ.æHJ59„q8,B•Ÿ¸0 ­®­åÔëç:Ö]!fXadV§À>f~šYÁaÀÇ –Ü‚ãÙm°8…Èdvò2¹@áe`Üt`/ÀñT¢YÛZc†† o-"3µf!¶»¥Šð·/!1CÈ™qñËÿãX6«ƒ°˜}ô2§¡.ŽŽPrnr¨*©àÃ^¸jA†ÖéZB¶+]â…QIÞý‘x¬cñ‹6À0¥9úÔ(ËK°#¾ñ0”æ¥Àâuä—]tîCp]c §RÔØb)fAßzXHªöSÕI¨ëz -k¹'þ×E&',(—.ˆ@D^ÀHA²r8×iŸ6foð)…D ?€ì9ϵ֠ädºW©€ê· €Êg6PÔ\25€¸œT’Ä ¨@&]i#Å„}Lú°„) ¥•¥‹&±èÙý ôF#±ã“ÿls’9°/T)Þú 2[H$nW¿„Ê`¡5L›TîVÔ,¨-È‚>@ˆÕ¶6> é `Ã˸a€“Fay£P…xšJQ?msW” Ng…ÕY ä¬^2- /´¯µXbËœª\ƒwuSù–°Ph×i nÀ,mm‰C@¨€ÊzÑmý¡W»:Ú¼¥g9 º¥XÍj`òE|Œ)¯ËÍuçò¬ÔD†.ì„«ðø!Ѩ†–i`?´‚†&°ÉöQO—ï Ôž8Å€(-1ˆM§v @]ŽH†r YQjˆ¯hÞK‰=6¨°”´Àÿ.£åPPKtì…fá i ²Ÿ¾®‡>8=§ìýAü ¨t >ð©K]ñR¯t(Pm t­ÓÂà–lèÆC=lGª ˆKûé=¦&¬Ïª¤ÎñëB;5#Þ Ò¢Ð¹Öõ<ÇóÛOáͬ½@€ø d£Ñ•–ø´EPÄëIãÔ8œz75^‘}ÆŠ@°møÒS¼âðþRlpÞ¢RoBîٽؗì[Û”O}g©´p]ý<€éd£Ò–ÆtÏñb@ùH-¤!-x' "@4Ï.òBY ÇDàø¤ø2€Ò/ñOñ°é  ``aØÿHô-È<Äฤ<”I¶ZËTŽ Ààoeê~VképÛ2­é¤mÚ&®| R¢ù ¹Š„¢«ŠC>(°š®¤°ê*Íé¶O û Q†à34ŒDÖ%½há1X®rA™2#ah9Åõ–ΦΠoû @¢x>¸Ì¸îªïy¥.4)ù! à /ޝâ”/D@ •‹yðbsBc—ö­}b™VG ­ãv°`]8q38C\@Ê6ž ñb`¼Ï KϤð}`5ˆÐ"Ô‚z¼ní@¨P Ú&ï? ø¯ê,-òŽì&Ç«Ê!çX+*ž¢òÿJA€`fF$ ,L£!äExÖ¿RË ·m˜Ž Q«é,.âœ-WއÓà`,àÝŠãW¦Ž,®Ù,ôä0¨ƒìíT‚3,  0? âL„a¶lË:͸øDOE À! 2 {‘ò¨!?òà@ÐëÌ®hÈAP,ãpû> M b$+ŠgÞäò&-ɆÃñ(`ê°¡Úøùðò.M$8P =oD"€ó¼Î8@ˆÎ6ŠÒòO ÍQ@€HÀ| 5b'q$ÅÔãM$ |2ܯ$ôD$4Ê ò$.îÀNJ@ÿ`l¤&aR"F Mæ‰ÀÒ Òà²ô¼Oò¤PùàVÀRRµl.Åò- øÐ-û/Ñû‚IOâ2`„f”á+€>ShƒïËqâÌ2#³'—¦Û„AÒ€4PÏâ<3/Ò,’ñ_h xd±t£žM£&3½O -®ôz²+Gà5­­#ƒò<³ôœí ±ï$3#&Bµ€Q1²=o<>Î3µÒJ!@ = %‡ Ù¬°'w ®ÓNj ÿðÑé’/Üò+½Ï }©$øón@-æ#zS÷Ñÿ8S85:‰²@·ÿ¯"@B-@4§z1c¸Óð4ðÐà+;M^c`&RÒ8Ð0ñdG  ÏéàFÀ&=Å3ê¨P8ï/íÒúïI sâz²±œú&¢ r-ÓIi Î$@‡,²õê°7as.U¥Ñ¬Mù²&í3N1pñÌ´@­í¨)2#“5R`ÄÔ¸¬a7–.0Niš‡­ ä%>€«mÛ°Ï=1•û~“*”⦎ 5TÓb ÐéÂ1Û°«ðv“;%Ç+ø”3ÕƒÓñ6Ý4ñ‚5DµWÿef¾C8´V¥.PÜcpP@ÿ…RòGgõé±/Pê,M´L@F}µW…,à$€ôd\Æf¥p–á 2ÀZ…“ £óCò¼ïN5s?³ÚñÑz¡D·µ_qò1mÒ±èªÕZ]óN«Ž7)ÍÒœÕ@3tGoêŽÁ_)¶bñ€ Ìo8œŽU7¶qªòÒ-Nÿ@”ÁbQeë#cÍ/ñÞ"2ÌZÌ‚!òâ$iï 4½ìjW£g”µÉ ,b¤oy¦WƒbkÈvËp3F^DYwG6`5èc>ºQ-®Fh ÊÂlÀ,æ#pÇ’!N´a".—j}5µZd6Ä–Æ$À Wú2+FS`tÓ$&#÷j!sMòCx³â¸1s¯q"Øk—7þu"’¶ikåmCµM€!/VMcf×MT_^d.Ó$MÊÂpç#yÉBšvt±6_b$&Éàh›v"3@g›ü€{ûu Ì5\À,ô…k×`t÷6\¹etéC= o5¢ik@KA"27¬ö~™ÿ×M”¡¸.öE_kocbí–px‡w¶–4iå«!o‡tKõŠàz7?tí"mt÷c2NÖfñKŠèèwW¿úVåÄÀö2ðrÊ7Êbt³B€Ë´ÑÇ6+¨éd !àƒM,"¾t%·4ù7?¡ pVx’ËÔ£BGW‹±s$ó" êkq¦$úÖ"úöiõVo,"P|¦#SV®xoÓd>dÀÞEAWЇw>n@q䀀 þØkUÅ‚r£&bN4"tÆ,`ÄI”wÂÕŽƒÆÒ"h yyÇ4 ˜5†ÁUýEh'„¥Uv-j õö¥} ¦ÔÃhZ„ffrƒ[y‰ÉP܃ F×d çX¹‘eCF"€Pñ€u¡V›Õ¨œ,Ճ鹞íùžñ9ŸõyŸù¹ŸýÙ’‚;ntpsec-1.1.0+dfsg1/docs/pic/fig_3_1.gif0000644000175000017500000002427413252364117017237 0ustar rlaagerrlaagerGIF89a5ÔÄÁÂÂááᢣ£ðððÑÑÑ$&'sttcde466²²²SUU’““DEF‚ƒ„ÿÿÿ!ù,5Ôÿ $Ždižhª®, <ð ¾ñ ?2Dãv­ß¹]Øãý|ÂâÐ7"ÉçêÜDƒÛ’…m»¯;æ–Áb/Ymf£×ax:Þ¦¿ëó¼|‡ D7ƒ…€7ƒ-‹ŒŽ‘’“”•,™X™›\Ÿ˜šœ¤ ¦£ž¥ª§¬©¢¡«°¦# ®±­³¸¨¹©9ÀÂ"Á™ÃÇÅÄÆÈÎÊÉÌËÍÏÖÑÐÓÒÕØ×ÚÙÜßÞáÍ"æèêìçê–òóôõö÷ø$[ùý–ü H° Áƒ*\8oC„JœH±¢Å‹3¶ˆ¨ÑÇŽ CŠ|à@¤É“?ÿ¢”ôQåÊ—0I“˜@8sêÜɳ§ÏŠ\Lqó§Ñ£HšhÉïDÒ§P£JÍÙ´fÅ™V³¢p`ª×¯7T}ĬY©LÔ7VßÙ·p:x Õ"Öºx×µ=é`/£¾5Ó®p©//$†% ˆ—Ø*`šS¦`k%7n„x³çÏþ*¿½´iȤQ^®å÷ÄjÐ(:ÞM{Rj¾­!¨|ðm“¯uç.¼6"ãï"ù[ä\Ÿºâ @ÝØÀ¥Ùª(n\ör|Ê¿klÎ<ºóèÑQï}]:Ã]<8Ý)AP% |ÒL¼édÜÕæÝDÀ°@Lቇÿy 1ho¾å¶ `À}‹$Ð PøÂ(¤@IÛ©5€­HÛ€5p $°ñ%G¢‚ãÕ˜‘ƒ$œÆy«!@—æ=0 d“`0" ‚€:Ü" ÿé†#!tUdÀ‰0Öcf‘ fæ>‹m™cM@6uâ9’ !/`0‡tE·Àb >9B˜(°€…Wˆh¥k,¬ )Ž,2d@U 0€…ŒàÀ…¼í#À ÜPIà⧺9;JØ7$pÈ(B“ ä0ªnC5° /Âà€¦Mnª¥¡üiÇˆŠ³UºÐÄx ÿBÀÀ´¡^Ú6 âp … éêr°.(«{P¨ë¸uZžs ±7\{Ù“Ž–ݳ[J»¹ @@wv»Î´Óh­% f¨Á•(·ƒ ˆÀ`H"sÌ¥Ç!LòÈ"‚²É)ƒ¼rÉ0¿,s " 2ÍØ|¸½›&;ÂÛ/? Á—Xhp‰ý9{%¥]ND@<8 îŠj ® @`b‘ôqàY)€;ëÀóN&í¸Ý6Üo§SwÜtÏm·ÞxïÍößrž·à$Ò7x'"nIñ¹€û±9¼+HjÐ%p©_ À3 –éÿ J'( ÃÓͲ&õ¤ b|æ'" îÒ È À' ”X”,šïÅUۓຠ°öpþ±vñõpË"힀y PíµЛ9‚iHº $ðx—¬OM‚¤YB ›ì% è‡ƒ˜?ˆ $*. °äØÜÙ¸”ã!o! ’ Âf¤  w¸]ð @’Ø'g H@ÿFð¿=µ z—ÓFÜ÷‘ÞñÃ} ßA¤ Š€k# @è`³+èÌ5 ‚Ѧ&(í€a g?j"Ö šÄ?4àK@r85ýÐz>ÛÊ-â>Ë…lƒžFr‡ ÿ+±sqPïw:ôªf rÀµ° ®ýJ½â ÌX‚8QŽ&û˜Žtx‚€õ8…ŸQ!CЛ“ô|<ã=Ò(ÂÞùî^iyL@¤¡(Þ+ WdÖ üø³-¦$™°EzСC"Ò3Š”dl¬dÀà C@«È'¦¡yˆWk.O0L .ˆq, Q ÓR´šñ ºôŒ&ÊõE’2YÄ"v¢æ:|±Qa”¥$”óHFô©+ 0€9TXÍ&x*~âÓäP0*’1;&ƒ°°*€ Ÿ#„ïøÁO¯@\£¢P(¯I§ÊÍ)„ýT>‚ذáçaº‚ö@ÿ %錚‚⤀´m|ÊV Í4¦°”€%xé z9å9(€P@˜á=»E†‘98Íùª´Gê™?GmÁ“Š%,¥(£ñ$up­—Ôr½‹)-ô©OØêSxRŸìéa=IsÖÙJUª Š:•búÂ!xnbT¥rÀC Â#51Õ ìã_S@zÄ©P‹¬!îU$弪S‹u~-L›ôÒÏ{oDOz€ZeÔ¯m 1R¼lÆF¡ü¡Ì¼¹XŠR‹àÉÚ`[7ƒ9YŽ8P}ˆ(û2€þ³{Ñ ÂO¡RµÔùD!0Ï´9¢Rÿ^¸ÎfpèʳÖ|RsÊ v(4£ëÒR¶x0ˆÈìÙ`fµÛRNt`‹xåfbéfT·BØçÊ'#Ó¥4uâê¡°Ö7ØFŽEÝʬíê@lÉïCé§»ºïeÒ«PŒ¶×ð­íDn«Í|¨¸©ÜÌop2ÄN¨ ¦ÍüïÒr¹I‘KsùÌäh*åH–|*M©[ÿ;Ee5g*IiK <•ø[Ѐº¸ ¤@mAŽs-1•€'<ƒPNUyh–^Ì6'¶›(zówøÛy½]Š {×gtÈ SÕú 3\rYúS¢ Ø)’üšsÉÿ‰äxÏçtJFOúŸhìÝÒ@7/ÊÕ‹ÑÿÑqsfŽoL~š€êþnæ­p^·”/¤y#Ÿ@”á)‚è…º¼Âr!ìüÉ„£•3Ï”t°…Ø3 6 ªŸ0ŸÚ C‘!Ç(¤›é º¤b—n¨X—ۆ滕#Ì[|ð©T£®»4Ä*€.‰ZÔ…ŽU¡ q¸®¨cÃF¿¶­/msµžw®r•¨Æ‡1ÝXY#‡ÎùÐ ê9$eCJÂ&ɲ-™’³‰1G~ ÂbS…#Äæ£NþÙêÜDX¹a0®Q§ »ç’8³|‰T-5Ów›´X¼ÿ>‰”3=誆 «'·f—„¢Mùma–þôùç]o„ÐWMˆ¿³¶xwö½‡\O·”ÆAÆåî:±9YìŽ÷¾ÃëIߺbÛΧÃz Ãv“òŠâw¿ïÒ&FãÙxŒrð³$A9R¦’˜Ç\Σ÷>Ü6n²Zæ8ýN1­ä¿^HûlÍúÞ‘q¯n°'q QÀ VÝ(£Ù Íø®®ý$²ÔÿÀž‘”ë©tyåÛþí¶¹ENÕ-˜i£´×"Zð?7:c(æÀc9æpªq¿àÅÃsFûAßLÈ¥Êä_Ó)b€ÿÚCy1ígu‹à|Ô}ý –7 ·Ô7q xhóPâôuf6‚qGÚ’+AÀa9€+\0i:wxp èu\¡R¥vŒ€'èdY-¶ )ç(K†ÖµvðÑ=jâ'\ó'FÄ2ÂNôOPÈOÎa€Öw‚3W5¨>‘ 4M3a…M5#)cHT1%&25NVȃ“÷†fAVø'A†1dAƒàqA$Fò5æqAåÃ[x…+q{÷0{“PZ{eÖa€Ñ=Ö!kÅVHÓbE*ž¢&Ú9”„ü‡ ï%P„ˆyÓ'†èð& kÕÿ(SöQšÒ™•R²8.WO2[áE¯µ‚MІ˜1‡u!J÷WŠ’ < ¸©H §X„Θ(Ï·ƒ”I/ð°ZÖDTÿÒëՉ˜'eãIÈ -hpñ'BÀXEdÔHb$Q•!^ÓqÞ‘à‰Ä¨åHŠçHáhëh<ÿøqŠïèFU£c[¶;zñdnD[V\ø˜eŰYüxéR i¢Ä¹n8Š}7ˆYÂxpa“‡vˆ˜Œ‹C~ª 9]ñ"àr $Q6éic•i“NNòM÷‘j’51’?§3(Ù”Fñ‚”aANùz·n|7•ÿSxp;’°;Òð53ôƒT¤mc)¾A±@€Ž“³¡”ð‰XY—7Ž.•Áfìç…sf”1…”4!—Cƒpù>‚IzÉy¹!)Õ—W÷—\˜Žà[„yU‰™B‹iN˃fóÕ#¨Ñ˜þÏ\ÕWx.Ô™Ÿ±™|äš°ô™Ôu£Qvr’hcƒç­d²¹ƈr±™™0„mf›+Á—JÚ§ÕQ•'¡•½%gË{ϹJÚSÈù†iŽâø…H›Äœ(<9aHÔI°lð†#ˆ[!OJ÷:1i?„êyaãÿ¨…+å<$5ˆ¼Ô_´ôsÃÙÊ9J艛Ï"b!z”Dr%pii˜òFž"˜Á9”o‰*6€§L× @ý‰á©T$zÚf¦‰F;ä#døfêB$sq$ DL‚¢#%˜°léS™®±Šþ`‘™p Ó;  ™B.7ªaŒà^yHŒ"ãroH•9fH 0µ>gòW5ºyƒI{p·£yéP¯æ…w’'{ò“d*(„‚†»R ŠÂ(ËIÖa:D4b+œ£o[Œo ¦Sú5e32Ôñi\eb1‰‡pÔJ»¦Sz¥æA*‘ªœù¨!‘£ÿ)Ðr¢¹G·µr+¹r(Dâ+ â05,Å"raµÚ¤ÐØqë'¢øP‚I{i¡k˜æÙ ƒ)4™‚aF@–[äç5Y…»([å"‰ï)|”ªp*c'ue'Xƒ!/„€Iör/ù²ûÒ«þ¢ƒšŸ[é›þÐ;Яb¦%­yª’0"›äF3„o¬Å%Ìþ³“0öz*Vœ•6ŒåŠ{j,fv²†9£-Uõ‚4ãSLÃNã§"ºÅ¯ý 9 P^ÿÊL×B‘ýà§& ¢°à’]Øô?3Rt!±ãC±IU£äºšôp®)à8œÈ&˜¡«®ÿØ©9WàXž:ù<^ÓN¦ƒ:ªs¯ØÙ…NzµØòv´Vªš¦ °öj: B­ Lö5—BiE´Dxt;íT>CE¶²f¶jFÓÕ@Ì"Ýã]o«³[ @]„ü£3Ìpo p)‰â^}öQI$»A™-H»«˜ YbYë·…fžFUxUÖzˆ$A @AxˆA4§‘¤ÃÁ—rJq£ÉAz–±â: l¹mYkCã·1ªdi3`9li3ØÖ–X¡EåCÿš'&]q$øKŠxÝ‹0 5wIU²6€q½\Ð;Ó¶k_•CD=bDdšS’aLO¤&R䀗… äqŠ»Iƒ”©-–³–i¼¡Î+¦46®ò_‹cEÊKeº9ü†„C£à ÁfÂÁc“D*½+àª,°FÞÅ0¸*¯<Ÿ{yôhš|e «uªr¨´ÐJÕ±¼V»1š[U·Ög×]èÑ$=LÀjZÌ„¶ªðz¯ˆ¡k¿Â·úŸP±…N»–´˜Ä3ÛI˜ªú]£”®_ìÙÓh\j,té^3g,½zf\Çÿ¬šURa’\¡ETsÕȵUS——ó¸ÅL6k4‘M‹Áź2ø–ü£˜Œv^$«¾•ó¶¡´¢9;,·¯‘²,ä}«G"@ÃóG%¡EͲµlž“ÈAÓOX P\ûLµ+%Õ´V&ŒÁ’«ö@n櫟M‘AŠðŽ2” ûË–ÂTx9û¬Ð¬5zùCÀw<a›:2pPœã$ÓH…ß È›°µ¤XÎEéº\ }¦Sê€\>Å@•8‡´8,ûQ”°ÂŠ@nL ºŸ—©†$ÿäš3dЄÐMg%+÷$ÔÒuF¸Œ`Í÷æÃÒ|rHÏUWÿßÃÊ%^ÞŹUíÑlÓ-àWA¾÷@F*»Ï÷l¨9ÎpVƒLf ‰¦óf™8/à$_£vÈ! Ž(bX&’x"$%®¨â‡(ÂØbŠ3ƸV0¤rÍMúÜGuï $mR´ .Þå’ ½¶¤hJþf¤cBî`œKÕͦ Y:™Ã…]>Ò$˜BÙFåif¶0.øt%o\®0!œcB%wâÉC™R.†& =›nÞÆ”š]ä©Ã—‰2zçž?jò¤ À¤Y€ –ÑqÖè ‹zšDn¡>ô(oMû Es|~ÇŸ?<ðw XÉ d&lÉi,¡-EjÂR"&±ÙÉà¬yù'`­ºÒl(d `ÿ’f0¬˜0iél¯ÇrHn#Æš{n²å-k—´†ùn7ḁ̈ɤÀ€>åÊ'-rÎÈŠË·Á†ʸ䂚îè:Lˆ©·½«ƒ¼,˜ñÍe¤´R¶1¨º"ŸÒß ,P-`ü§r¦ÛpÄAì6³º·[×»‡6Ägú$G*ü›@WŸ@Ê`58'üûU ,×çËRΙ¨Ì6s½Ðº„Ò&¯”òêÓ Y`aÚ`˜âÀLëó6…@Pƒ¡À.|ìÖ]ã0°{öþ·àð ^_áùÝ'âÞø ¶v‡#wûq'@ÅÌú<¶Ï., È=ÿ<àÑX/_=]wÞ [ó×|ç0ª „(cµ¿h»î¹óŽ»ï,öüï.c‡ýÏ­Ë6p@9"€&àݳݚÛí‚GTÐË \sŒ <ÝÆ   ÀªS—p ÕÉðë°Ó1ýG¤¶¼Ùê¨Ú ùplbcÂæ\°ç@n0@Ðã3ä¢(xžØ'_±N¡ÉZžöF?¹Èî~JV* ±äk__ÁZV°€De:Z@`ðŠB|ÂåìB¤?IHÄ“a§ª‰ð$Ô_º§¨„% ˆ´5¨ €²j@/ôЀ6Å‚9ÜÿazX—¬UZ+s]i²ûµ±~_»œž +Ÿp:L°ðuËhÙ’„ÆHƒÁl«Ñ¤$U)I)ÀUCtÔüâø)$ZÒKsÌD0J¡ÍMø¶Ö¶1$òg×#c¡Ô²B ¤UŸ™$ŠØF8f²‘™LÅž¨A0€n×ÒL"aš+9œØqX9$ ΀ªº-ÇdË®…0—ø[bÅ Rüß ß'EáÍòà @0°Éà)Îäö‘•\qᇿb )SM0]“k¸Ô槸¹,„Dý Ѥ+øä?L¦."™@fnžUp"Bÿñ}fl$(ÍXM’Á < ŒbFIp²ð ½Rm¬NXµÙQ•ž*¦Ö‹= F¬Ô§y#&ƒåÀ@‰$Ó&Ä'žEÉrÀT-sµœ’j§l*4vI0€úB÷$Uu•‡*Öˆ»uJ~J}Y§¡$`‰€'òÑÕ¾$‹—t •6XOBl(~„¸éË’ºWaʳ.”D`€Ã%/}ËC`È –5üŠî M¬ºÌ\bØ@‡A¼3 P@fKTŠQAz‚r¬F£@€Ý6§·ù„&Ìd[IK‚P¹ËèÞk£ÿª2‰Ë¦é]î`±;(€x徿êõ²K_Q-0>A%ñmvS[·- ¨Ê‰¦ÞôÕö¥W½ì]/4ª%]Q)`¾Lªj\£b€Ó±¶ÊÚ”\/è\ׯ½Œ00#Ž«àì0¸P)û†Z(HØÂư.5 Ù8 o2vi¹ò>d€s¥0óª·çÆñ¤Ëp' Q/¸¾«z“ÒßRŒmÉMž2?nšâ:P9ËLößekÛ-¥µÐÈTµœeŠ‹ef²½´Ì\)™9ÎÂ1ߢ;]WB ­ñY<ƒ %CBtö³Ö˜º .ëÏê3¡MŠ…"ÿcBvXO“d8`Ϙ2®üˆ`¡[™t&½ü„´&ã€ Ž˜¤q…,@šž&õýLÒ–OÞ4&èV3BœðD¤MëD y(_2ÒØìþÚØu%A-Ð<Ùmø­_᯲Ýmno;ßNMÁ-Nî”›Üè@÷ºÕ °ûÝñ†·»é­By×{ÞúÎ7¿ïmïtû{ßï7À ÞnÃ9nqÍqq! 3]Q»†Ñyö¹þ„l¸òi)Û8^g|ã0XƒrD¾±¯èƒä)GùÈY~rq®æ-—ùË•£ò™ÛÜå7¯yÌsNó]çBÿùЕC£ÝýÿŒÉnÄB“’k.äïØÔ´´¥Þ([t8X€[lhïTh¡‚Ò´3ªþ0‰“ëV·ºÚ‰0Hx$Â{”æ+Ét"¦[1ÛÛÌZ«_;áÛ1¨MR¤]¡b-9b¥$1¶§ÇDq¿>O'|Ì2o„«ôíî&`òˆ1²¿`c ê%–Xk/éò…(ƒœ÷¡ªÚã>÷ºß=ïw¿ XkÙåA¨™“­-ùÊW¾¥¾+©|@»;Š*쥳»]÷·ï=÷»ïýÉÁâÕÇ®¬² ¦nÔQ÷÷Ã?þÛÂIýƒ¦•¤ú:øÏõI%{ΓËÒÍÿ^SRÜ 4À‡5]AEÞã%„ýÍ€ ÔTVøLƼ XP†Zø„7=X`Ÿÿ… v‰áõÀÛåÀnåPcåaR* S¬ 8G™>40€iŽ'Aký• ¤ÂÉ "ayžpÐ ÙB‘BáVuÑ@QñF÷pÇpÐC t:h„¸)¸ €ÞnÔæ%áºÆÁ®Ayéß?ÁE¶AÞ¡wXJ‚ÐÁÈ·€—±ý_…ê”)C8½…¯ èÅÓ´yÝ@’Á<@¼ŸBÙdÜËAYì[pZ!"ÿb£}Ï2˜\Ð Š ª}Îab¹\É$~ƒ%JÆL|(6“…ìôÖý ”Þ,Á?±b+úéè!L"i=—  Ú(íÖ¿ð¡¸Bµà– \A nUÞ0&‰0À "Ì­PÅ1"–‹3Ž 4Þ†Û9"Á@c=—²A¾Á$<ê)°eœVDVhE‚Öáà05AÞ-³AмĀ¥p‹ 4c<£>Z“"º¢å™ > ?†•'’qá”ÀöB'®@ÔLµü MFȤR™ Ìâ $"býha*°E¥‚G&%n¹Òšäa*x"$(ÿKHþ$J¥Ù4>Á7¨¤Ô¤.QÝ@'~" tSÃ`åá cV– I¼a‚]Ú$NŽÕÄÞÁÀ(U 8ô¡Ü0`“Uâ£\®áq±äŒ„W*ÃP¦b\*ÃXZe h£B]C9 ˆUjd­gRƒO²F>*fE‘#¨DßYãI¦Âu9†e*d*šTd)¤AÖÀ œ)(¤PYæiV?>ÂÀ¦‘µ »YÓydÞoçmle)g¨ÄÊ"%Í ÀdPÊEíÂ|„ÜH 0Ð¥Cû’ LÞs²'“Ð%Ä¼× ”Ñø‚žÁÓIÀ˜€ÞL‚¥ÿÁÌ…ÃuNe{"(c…8sº kvIF8 ŒRÝXK¤Œ„›h‚Ùä“iÖ!e&èjDç­!Ô½Aµ0€‰&ƒ^>°DH ˜Á(µBóÝ;€'ü̈Šà‰&¡ ˆW@^Ô™<—ŽþDXt‹ÈÐÁ(Ñ‹é%©›¤¢ãá¡Í’Æ&‰©•Á4@Œq…4ÝÛjH& $˜àC"ÌSåà"dÊ5tKô ›lx“ÎètŒb˜†)n¬gB$)8¸I΃†ƒÞA¨ ÈM Ì. à|¥‹"ÀS € A„ ¨Á Tkå×yÆ;IÿԒʯ´ª P%Âm=OX†T¡–¤c°TÆ-@DbÇš Œ·W€ ¤@¤€J¤Ï0+`×È@´®e<Í1á*m½ç¶bÂÆ‚âÖB0À:BØÈ`hxD§bÅG<¶Ö]$»z+·Þ+i°¢’)`¢Õt²ùÊ5ì¿¢zxSñ¸æ«c¤¨6Éèìkc‚ééè}Q”Â#FXxlZ 85ä\Àá–>lÊB°b ]} ÜéIÊT‹åÈJÍvO¯¢LÎf‹` ˆZ|ÂÉÀÍ!¡ªìÑÖ_c>+bjr´I µÉÀŒ¥®ÿ'ù„ÕF¨R[ÁtÑ"í#'­±ì‰:ç×.D·¦‰°‘œþ‰Å €„Š<Úªè@ÃØšíÝÖEÄ~›ÈéÛ\â}Ž˜õLÛæÀ:›Ãâíêí7là­B…=ÆAô–åh?Ò^øoêH‰™×KÙVâ&.Dè­ðYV‘  ÕõêpØ ”…&Nxf¢›À€;Ä2ì2L,ˆîÒ‚néB¬’ˆ!„¹ èñ© Ä.›úAƒžk†¼ÃÈ}S'ù–F݉Fx[¸iÛ¹‰øŽ[ø~¯øš¯¹ïúªoû/ñVQøáØH¸ªŠ9‡¶ÑÁüânGîÿOh/p/Šš\w ðÇ!ð_®7pÈ1pC°‹¤ÂoB„íŒ .:Üà u>FêÃ:îïíV¯ìÂ;0)íÃp˜ íM†NQº/ö‡aŠp-lð:aÌLâçRïÜ–½´p½p 3±ÄÌp€ˆä] 8BÏ»…D„ÆÁâÅ]ÅH4;‰DµîqYÊLÀõ6qwÞ#$éZÂvž€¯îæ xª(Àq,hæ²”èíÅl¶T ª 2t>1n<³åÅïò"+ÕÀêÊ.1#K2‚ð$[2dð¤Uò%sr†!â&wr(Xd¢ Šò)—ÿZ!c”¡*»ré- hB,@¬Î‡ØñÊ©úAz.”/?rŒZð+sdŠ‚M²ž†48}Óq À]ƒ²Þ"3›ZgŽEr1s3#TQS΄(o™ë5tèЮcˆ’‚:ëÌ6wsgr!dZ%ZÍeº„|óäü¨óÄÀ>ik¾s@ë†B˜‰Œ¬I˜ŸZéd©8½A˳; 4 Çò*#Œ;mŒ¾ÔÀJüQâi€zbŸîiŸNtI¿EE뀻þ†0Ù ¾èKZè ‹~êÝÍô0›ôM;1"*2Nó´&KtOu×€rPo<ÚPuâ¢t!uRßí!\T+9\Â)NU'ÎU3ŽT[uVOuTSØqçä85*»ÒÑñOZ£õZ›õð´µZ¿5[›HòLEýB@;ntpsec-1.1.0+dfsg1/docs/pic/c51.jpg0000644000175000017500000004005513252364117016426 0ustar rlaagerrlaagerÿØÿàJFIFddÿìDucky<ÿîAdobedÀÿÛ„       ÿÀïJÿÄ£ !1AQa"q2BRb#‘¡±rÁ‚Cc$’3Ssƒ%ðÑá“Ã4DTdñ²£³5ÕV!1AQaq2ðñ"ÿÚ ?úf¼A@ P( @ P( @ P(ZЄ¬„¡ •(›M;=êf& -cQú“àÙKB¶°Ÿö¶Vãü ûksJÅÞ9ÿTúÉÕ£¥ä¥¸íÅJa°BR‘sæ8Ë?UÑ^»H{*ˆÒäƒ&ëBKnXn‡P7€/±\{ªÝoc¹cò0²1— ä¿Át­?ÀŽ Ž`ê+•v•“@ P( @ P( @ P( @ P( @ P \ð ŠfýDÄBÜÎ?ýå,if™IúÝÔ“·4µ‹¼Ž-×þµ@l©œ”ï¿5oFA<Å\Ž?2‰®ºêå¶ö¹Ö#Ö¼—ë7ËÇiG¬‚à %Lö.üWßû«XcÔ-!2#)¹˜éIºU¢Ð¤«‘*XÔÙ‰ÃⱨSxømDB”V¤´£qçjÍk-ÿNu.G0¿ùŒ8GÜÄQ²¿áPä¯ÛqRÌ’áÙ0yìfj—ÝéàãjÑÆÕouiäwer³Ò屨¥@ P( @ P( @ P( @ P( Ök¯°˜å)ˆää&§O!ƒt¤ýn{©ïµÏujik7y¿¨:ÿ)™’ä¥Ïy>þ'£Mö}ˤ„ë«Ø+¬Ó;ok\ÏNΚ/œ|»Š„¥!€;wÂã§´ ©î5¼3…®¬ôÿ§ºœzØnñF6[ ,®Ý©¶æÉ÷“ý5ZËçn¢éÜÇNå‹Ë3åI@ÜÚÒnÛ­ž4¯‰'÷s¡cyÑaŸéFÑ-qÞ‘ÓS.¤¦í‡t*òTtÜ9ŽÛQ›¯·m;–ǵ’Æ<†è¸Rx¤ó E¹Þ¥†»eì‹q¬´ÊÄeò8‰É›wËth´Pâ~E§˜ýã•K\;Luf7?{Ê–Øq gƃÚ>dö(~ã¥rºáÛ]²ÝÔR@ P( @ P( @ P( @ P(#ÙÞ¸Ââ–¸áFdôéö¬jB»¿uüû«SKY»Hæ]Mê6F|¥cÔã‹yÁta1Þ' ;^ré²{JÊSÝ]f‘Ëmíi™Áe§õW„6Ód,ÿ¯’,£ü­Ûùo 7páÃ1¡°Ühé÷Zi!"ç™·Þuª¬‹PSøPj:§¥0ÝO‹V;(ÕíuE–<æù›WñOE•Â¥FÎú}—wcõ,ä/|`ÃËE¬h¨+Êq*±U¼B‰g¸÷&çDOg1Ó¹ò=;‘Q ‚·EÔ‡E*Eìt±›‹ãË®à󘎢Ƨ#Šs{|dèãKµÊTžU›lÉ Ž5–—"L• [s!º¦%4nÛ©åÚ:yƒ¡¡—\鸅Bc?hùT¦ëcárÜVÑy 4Jû¸]•Ïm]uÝ7¬6P( @ P( @ P( A§Îõf 6ÊwÌ’}ØŒÙnŸêÜŽõVkj]¤s^«õ:r€mÇÎ5—tb ]ÎLxé Ø7Ÿêß]f’8íÙj4Î;=‘HóèPTn¦Räç䥸›fýە쮘fÆçˆÆcZ ÇK>a»®\­×kŽ(©k>ÓTeû(¹íî òµÐ¥í+ [R.£ml‘ÌÐkQ7#9 ÁdÅl(ÞCãÞ‘áA²´¢.ÅÃÄaÓ!w‘*ÆòÖÂäøA$$ +Ãù¦¼ÒÌ6—:G ­{‰Vº8¿ƒ‡e-ˆ¹‘µD\¦÷·uè¡ãA’ÆãòpÇ䣢TkÌ,\¨r#QC.Õ])Ô¾›e›Êb¤­ì3Î&F»N¦ÌKHÒö$%\ùXг<­;.Dw®ú)§.9\ZŠRZ*:)±¸ïi)ü.hž|ºODõÞ3«¢Y1s,ñÉÑcçnüGðçR¤¸o–‚’A¶–ì¬XÚÚ’±¨:FzŠRZÆç]¸6D|ŠàñÿŸþWmgm]5ßõÒ+›¡@ P( @ P( @ P(5y¾¦Ãá‘y¯8‹·'—ì@þ&þ¬ÖÔ»HæýUꄵ7³Îý&+§kM¶Jæ¼~T]W=ˆ﮳Hå·b&Ì^ É¤ƒƒˆåÊrÎäžÓ¹¶oõnWp®’0Ûb°˜¼XR¡³i‹?1Ò\çúÇUugê£<Þj"¶¢µG 37¨øiã4­ û‚ ºï"ãTß´Q,daãd™ÆGg#$MœÒ6È”‘´-W6Ð÷i~tÎå…µÚ¤êž6¦ lsÞ»ôã!Ñò™ê>yR°ah‘Jl銫îoÌ÷’ãZøûj5Åœ1„ÕÔý:ÆuF=W›ŽŽ ’ò€¿ÜÇM÷¡ï&Ö<=¤Îx©÷BzˆÇPÒ3 z‰‹ ¥cb^(Ѐ“ªV-âO*–&lò•:Ò¢µ¹VpÞ^[‘¨&×ïb|¼~QJ{p–Ÿ7RØ,y©±ûS˲³¶¹o]ðë :Ó­¡Ö–—p!Ĥ©'PAk“«Õ@ P( @ P( »1ÔŒ;Asä%¥(]¶‡‰Åÿ*Ô}¼*É”»Hç]Oꌯ¶Zâ0¸óáû‡T …ßK'’IìMÏa®šõ¹m؆2sÙE-Èm+Ãuäç$®[·â¦˜UˆþgHþS]daµÅ`q¸Å˜Jžœàüì„“æI]ùo#Ÿ¥ 'º¨Ø!b(+mh¬YÙX0œŠÌ‡ÜMsʉ´©Ç\PÕD! ¨©GDŽ4JË:\#J)nt.¬é&:’4v›"Û-KB£·âN«Wò ]Jü$Ê[#Ÿuª2‹.!F&}ù²J|Ëw$øûë¬ërÛ±n^k.òÆ0¦šwÄæo"JïÍ–TRëŸÌ¢”÷šé#ÖÇÓxøR>õÕ/!“á÷ò¬¥§¹¤ÐþAí&´6¤“ru4F.O+‹ÄÅû¼œ¦âG&ÉS‡Ä³ò¶u­]É…h“ê?MùƒîQ6U>Tr†Î…d)±Þ´ÛU2”ð‚Iàƒ¨ öŠÒg²RØÉáq¬ÉLro:—§©)Q»( DfÊýÍŠ¹$ÛZ u>#2TYxÙÿ¦åmèûI /ÄyHSÍ%{V¶•vÓgP’SA—‹9´© ʉ$&@\—%jM‚J–¤ ZÜIº‰¡†ÊÔ·e¦ÎtoNg¤1+-Ïz"v¡Íê@ …oÚ» o­0e«Ëz‡ .*>”æ%Rä ­Y#æx ºGÊÕý¢®Ê+532’Q+7$älÝ–J|¸îã‚S©{•ßW³/$ÓK-%*zHÐ2x‘}M¸Zª.°§ÔÝÞl6»›$øo¥ºƒÃ¯4Ã*y÷Ó([‹!)¼š¢î3ÔôŸÓÙ8üjÓc—˜…$¨+BcG;Vá·*ÉöÔ»~5%tL\8Ld \Uùq⶘Ñ|Õ ëOá¹GRmYiË}Mô„;ççº]“ç’\‡lh«ê§#Ï™oövQ|¸Á‚8\( ê<-Dv?Iú¯©òN:|Gfc#¦ÑòÊðì¶µ-_ç;­¯mFqg‡Av r(}¤-•qC© ú¦õœ7•X3!¶ZKm Y(@HÀRjeܺ] ×MâÛÖŠÍÇyl\6ò﯆΢” @ P( &K-Æ1çÏ’ˆíëb³ª­É)%à)&Kp€u©òTÛŸ¦%a ]yV Ú8©(W…#½Wö é:ÿ\¶ìsïÕrÙ©üK&Ytþnv~àņ—h8÷vË'ꮳW>k:LBnCsr/+‘lÝ$€i]¬°?-¿nªú«X#rTN§SÌš"ÌÉ Å\ÉÒ‰½\}å w\óî¢Ô?%×Óe(µÓ±ÃQÈ×/9 ý„SµJþg,;\3j>˜»æòrvMCj§H;ÜÛò Ú~”*ᕉ¹h¬Æ”´$Ï\}¨‘8ó\üÃk‹ò¹"ƒgÒ\Ɔ#¿'î:J@´º©PN8}ï·¾—:¶t> Ë£L…dW"Ma¹Q{¶Ô8ƒÌw‚+-e‡ŽéÜ5õHƒ ÉZv)òVã›8í p­A=ÃJ$l¯§}T‚¢<€¢#™®¹Ããœv$«-–oEBŠ Ú¿óŸËoÙª»¨eÊÉËçFÜ丄…'sqµüÍ|ÇÈúÎߦµ„Ê©P€”¥"ͤ $Xh,9Q2Öa‡Q]ßÕËVòü«{×ÖÛ~vëCúlõüN—ª)n$›$jIÐßz‡ pQ”̨·Óñ>ñ ís"é-BnÜ6×t•°}¢–ÂD«ÐØLao'’œœÖT’܉A-DadØy,°ðRÊ•Y­I†Ðç&äKŸ¢¶Ü†Ñ½ ˜ùRQ椛xÒEõ¿cEU8¨1²2:eìÓï€BO­4‘aýAg/›K.8àš@WÚÆ rQIí¹`Â8öÕW½:ÂåóŠÎæa6$:”ï‚ÒÔYRÇö¯Ãç:~-¡)=‡fÕÊZÛm2Úi!-¶[B@JR”0ŠêMεUáÐJ,?(;ëhKm¥´‹%%#¸ W•èz P( @ Pcd2P1Ñ̙ґñ¸@¹µì9“§­$ÉnLç©Ï9¹¬#!¶À;§IÐ_VÛ?¶ëÿ&ºM?\¶ìqN®õ‹RŠd«7•$%r.VËBúîPÒɽö6?eu‘Ë6¥8¼3"Äl´Ù£¨ðEp°‡bšŽ MÇk›”ekTˆ®ö¾¼¨.­–™[ï8–Xhnuç„ÍJU€¢¡ù/P’è-tÌq4“ee%· #µ¤èãçÙdýUpÎQ§Ùzl¤MÊÉs'5³v\|ÛDÿ e6m¿h»êá2·;&Ä@½ÁÉ2ûVæ;¨$]#ÝiÕVF¾{™ Ññ“ñæB±¯'Ì”ÄE¡©$8[RT­Ãï&õUã€_Ü1’u&„¼¹Eh•THKÜ@Þ’ ‰HÕUá§ÃŠêÞ Æ†J”ùXJ%~þáÀ•sí«„Ê[逞zM x¨ã\ê°>aܱÐ4=»·lP›VkZĪÚÛ•Ejs½U„Â,G”êžÈ­;šÆFd•À” 'êY†PÌžw¨³AH–éÅã• ÇBYóVŸüÄ¡eóKv浄µ‡ˆñ˜LxÍ¥–î¶ØÚ‘ø 3ír¨¥@'A©¡…†¤».b bc/'FŽ2Ńm_ý;çòÚÓ~ê–µ"Kôõ…6%uK蜤 êÆ´J1í¯Žö[öí_‡é©•ÄHåOÀ ÏmH¥·´Gi]"É  ˆ9ÔU”b7¹—&­aHSj3å«RÚtPîá~Ú‚™<ä|qe¥¥-6°7©ÁòFe°Vâ­Ø,9š [HÍKRϘî::í¹eIT÷ùÖ›µw7u}B¦L3!Àƒ¢Ôf‚¥®×%K-¡å2<*µýõî5¬2®B{CÒwÙÅ¥–[BJ–·î¡ ͨ^ Ž ‰Èå˜RÀLp¤„¸¥JƒÅdyJ@ÅTÃ>>ˆyTM²:ÛLd$’öå¡EDßrU~<´¤V\X‘aEòXBXŠÖåZöB‰RµQÐ\еÍú‡Ž‹¹œZDÙ#O8Ü0“íâ¿ÃJ™Á&PLž_'”{ÍÈHSÄ¡Gò h+9jD»ÒÿR&tÄäã%%Ù˜‹ñElaÅjÂyßâG>oQ?öqO»ˆ„çæ(vI”›êµoæ53VDŒÍÁàYkˆ†ÅœT'u•ñ¯ªQ¿:ž–ž^iIj3£Á‹jÊ- T·“©Zmð›P\•’‡ˆØm¨ …¬7´Ü‚úÝwà.h5*›–È”ˆ)S,\Ôf´¤vÆ‹¦Ûüï“S#".7÷‰én =)å_XìS‡‚~”Ù=Õ’THìO`áAJ U*Û‰ìÚUïpùGôš ×G0—z§‚<)qK·ú¶Ô¡ûÅgo kåÙkÎìP( @ ˆz’ÉCÇÅj#Ša©.)A²ìÒ€F£v·#]+§\•ϲ×Í=IÖy89Y,4]rLwT•c J›a íp¯i~‡nlçó˜—ô“™÷à.^]m‘'c›l•) )7²ÔBwûWLåœ6Ï2—I÷¬XÓ‹úéò±n9˜Å5þíWŠTtboªÒ>CÌ|>Êšíêˆö ¦áfñ2SQI©äc]ږߌ”ÞÌ+ŠâH?ò×LHý+õ9}3 a³+Qé÷T  R¡º£â;N¾Y>ú9qí«9DÛ©:}¾š*ÈÁVþ’—e(4oúzà¶È½ã8Up~ôÓ>©fPÖ::Z¦¡©ŠÃiÅ8äï9Âì¦Ô74‹ø\mZù€û+Q%I1ø¸°üï$-Ç_Xq÷ÞYuÅ©)Ú ”®ÄéQ–›5×8liS,¿˜<¶å¤ýnpüé’D3ÔyŒÂÏÞ"…ÉÊ”¥•¥›‹›ìi $ž®æµ5Ã9H ‰ü8UdƒÕ$’x 0;/!Qp1¾ýh;^š¥l‚ÉìqûÊ#`ŸeK…“)4^ÁáÇê½Q1)l„¸Ÿ9;!±u„3ê½” <Ç/nê–µ&ÿõRõÔGé¸Dj4„}ÜŒ›Km*mµ”¼Þä©RoËZSsµ[‡ T\WHãñòÿP–ïÜɳicÎ!IcËqn4”¸BVê›.© ¸¿G†ŠËËf¾Ô6„ªt~PJ ¯*Ö·’À¶á¯¼«$s ×}¶BiqO(ãã:w=;‡ÎwK~|m£èjßÌjZa’Ê"Da,Di ´uHJ<ÂGñ©ƒ/ Z”«¨ÜÐy½½Ô2¦ëÿÃJaUïçA[ÐzJ²Eîl/¦½”ËÍ…)A+QÏöPP:ÈôAѽ(bÐòR~wÐ×ý7ÖW.Ç^´î¹¶P( @ ³22eD~2ýÇÛSjö-%'øÒ\8ÃE@Ü÷Àµûm¡ükÛ—‰íh NÓÃøTkl¡D²ҔP€E¨¬ –6,È®D”Ú]ŒòJ…‹‚#Y°pÌö-Ð]FÆF ”¸{ÿÃ< RªŽõÁI¸áq¯"®›þ–Y†Åä°‘úä4‰î©¬ž)N¡N7&À¨´‘e(yB××u€­Y„L½/õVè®­"C2›1 KxèëjžWÍþ‡V¼žóñsúk$Ö#"âä‘táò® ÿ»H<èÆ5ãIsçÊX÷ýYTÍh‹ ™ºWØÁaÌ–Kÿ)æyfÈiRȦp­ÃÚ™]U-µÇ¸ÛŽaKD=ÚŽ,YÙj°>„£ÛY·-a)Âå¢<€ÌÔ–˜ ÃØ–ѱ—<¥ùHE’‚•[ÃÌ)'€ÏDce"J²’žÉ!é/?òµ6êbÈ@K‘]q6S¨]*IФ'@EA·“–ŠÃëe-´ •-d¢:MìRw/éN´È×yÓ¦-E­èmGÂü”¥K°&Å–ÚßyÍ~š–«Û¢DÞSwsW]RŠÜYíqÃâUEuKãÀp…lžÚ$ÐWøÕ öþÊ ÜvQ^üjÈõƒ |ÁÃ0r³Ç…^Y´v÷Ž‹‹ý)¹©v‘dÊ57- 9ˆHËä\ž[èLH0¿ÌGqDî@;l/­ÉQ©.×ø‹q<¤ð`fÂÒ™¶áJÊœ~ÊÜP„÷º”iÊ7iº––Ð’·v¶ÚR”£À%#RkH›t禓¥„ÉÍ-PØ6)ˆ‚<å­Z„{ϰ×=»?&ŽŽÆÀÆÅLX,&; Ô!‰áuªyÖ¹[—I0É P( @ PrÌpÞZ{$mÛ!›q¬©$~½zó#É·–I¹J´Xýã´V™m+MF¦ XjII±ÖpÑEP€A…¯5……”‚ì ­c<6q‘Pk4á“ñùïOz¹QˆZHZc>âû*ÑÆ– +n‡öŠÞ»%‹½[Ò±ŸÅ±Õ˜å8ié • yˆ÷ö€J¶þÎ<8[0‘<ôï®ñý]ŠWEõqJu¸rTv™)F¨ñ|2µÐ¯‹ÛÇ^±›#§ú·ú¢/#5ÑrQ<¢·ð‡ÚRÐPàà¢<'&߬ÝWàtžk!ueÜ8˜€ÙXø®!Rˆ?ø‰Z´À?*7/¾¦Wå¶j~+Ò0½=€WÉQBR|‡”•eÇÝ)JÕwÃÃQQ^¢b²Ù u©>sQÜC¸Cí,<†î¦Àm^¥ yq¡†á¦qÐ’ópØi¥*î»…‚/cç>xö_€µ1ËÉ“và%·›P±AÑȆDžr? 'êåRÕÀÆ5–\Cò_’„ímklOÊÓcÀÒ”_´šŠ¾· sŸ´ÕÂ,›TÔª£É$ÐRô¾”ÝEk2ÝIÆ(2â•"rÇåA`oy]—;Õj–˜Eº‡0ê£oêYƒ bíáb(—žŽ(Yjýɬ}[ᯙ<¡²z³7™PÃtä5Bбµ£ ¼¡ÏÌp;mjÔÒNiwõΈôÔâ%3—Ë>“5›­¨mêÛk"Û–¿‰Bü´­]˜Ã¯ôçEf³¥.¥&=Bÿxê}áýÚ. ýº'¾±¶Ò75˨ôïHá°(&#erV6»-Ó¹Õ Þ×Ð$w$ ã¶ÖºÍdnª)@ P( @ Ps.²dµÔ’‰¥·ìØüPkÕÕþ//gù4«FëÙCT«²¶Â‰7‹(q]h8Ÿ¨p©bÊÄ) EˆåYiJ )!BÆ Òu'N@Íã\ÇÎFæ×ª4RT=Õ¤òP¬ØÓ‰½'ªzT¬8)LYkóêPïµµM’Ó«JüµlZ“Àí&·®Òı‰Ô¸LD6 åúzbÄLð°—‘*<–@+Ajü:û@¹Ò¯×½2õ$u<›é.HuGš«›÷ªæ¬Óõ.øð¹ôã1–tÎÏ:äFœ;ÔwJrúëºû?Ù[Ïã×[èîŽR“úoMcü=áî‚~'žW>î=‚³nÖ:÷LúeŒÇådÔ2Óe‘þµ}(>õ¾e{l+–ÛåÖhšV( @ P( ‚êrœ”9ÑæTÝ»ÚUÿë+ÑÓxÃÏÝ9E볋ÊÐIÜ8Äj ‹ð¶„ ÕW‡™Þ.Ÿx~úÍ‹.„vñ¬´PQH ?… £]Òpz‡¸r“µÔÝQßHñ6»h¤ÿHæ+s–Ÿ>eðÓðÙ OkË’Þ»†©ZOº´i5¹rŒfy‡Û‘Å3!…‡y³µhZMÒ¤‘ÀƒTw¾ƒõDõ±,¥¬ô$m˜¦˜.¹!$€‡ÈNí|À¢:8[úD™ O”æ-Qc+E4Û„¼´Úß#Ù¡Kvï&±i†K-GŒÒYa ChBR6¡>ÄŠ`UJ$Ü›šªòMW[R …!Z¥c¨«ÚöÔ~áL Uˆ¹¹ýÔ”àè-­ô§Ü“Ú.º»!>%Û’{M-\9Øê¬tL.k#:æuC3ü¨SVåㆢÚÚ>êP”Ý%ÄNµ.™%ÅGbâú¯¬%¦\‡¦¢e¼62„Ÿ…–Çêþ5©$ð—k]¥º/‹}¶ññÜ•sDºR\yGŸ”„ƒ·ð¥L;7KúG%â™]F²Ëw8öWux:êx{­\¶ìüuš~º| En$&3BÈi°‘û9žf¹[—Iè @ P( @ õÆ"Dì{R##Ìv”µ6=å6¡emȰ6®½[b¹öë˜çÉ €A¸:ŠôáåVŠò¤w'Þæ;E@) Ž"ÓìîÒ<\ÇmKV-e¥u¡^VÝÓ¨·a¦ ¢=uÑqz‹Q`ÞE€UEµJ¸Øö¥\ÇãX²Îb Ý/è¶FS‰¨ûVÿ°ÇPSËæsT¡'ñ>ÊÖr:Æ †ÂÃñ±[ŒÀâÛcU™ÅŸÏ´Ó ÍRŠ­~€Ty ¥è<)@iÄöQ^Pâ‚ÜX'T¤ð¿: IóâÁާž]›N‰<Ôy+9ÂÉ”b~"ˆÈ–òÄ(Ï•†Få+e¯ÛÛʹýÛáÓç X¬Ë’uó‹È7 QÐÜq+s,Ü3ܒ⪲ÖÊnkÌÈi·|¿8•RÔ{E.¤£˜ÜkŒ­2£´êWµOR4*¿eªáΓôǨs¨içúV(±÷Sg‹i䳦…VÅë7yik²tßGà:u’Œlp—–6½-ý÷8{Ë<´¾Ñd÷W¶µÚk#uQJ 7Qu‡Môë!̼æã©Bí1ªÞ_/Hܵ óµªÍmð—io«½xÎLuQú}¯Òápû§R‡%/Ø“½¶ÇùG¼W}:§·-·¾›K=bvT”ázšPqÇ”$´„’²l{m“sð*Úð:ÚùìëÇ1tßõÙk‹©@ P( @ P( =aÓ?fµäá'ü"Íä²?³QøÓô“ǰ÷pôuož+ÏÛ×î# ×Wh<(’¡ÃãOôûh+¡ƒqÄ£ö~4ÿXVlkZ‡u¾~V1-!§Ñ€Ê¤:úȵ,·~}µÏm°é¬ËWÑýSÔY¬‚iÿw´¯.Z$-%ࢷ$% „ÛÆµ7féí=q´[rÍ»âj`[$Ûj|)æ?媯6AB°8éAáJ'é¾ ¶V *Ëï„ ¨ðÔ0;.nf%wûXò˜Žû§vù R‚–R “¨Ov¼ëLÜF§®dúrfXGP0¥fX Ö;m í»gÝP$ã\lÚW½n­sýòä¼ò–Ó‡ÂHa:m6NÍ+¶»8ØØ)Ð×F.žé\ïPÉ,bcù‰A³Ò–v0ß}v:ëî¦êî¥ÚBK]‡¤=)Áa<¹S€ÉåSâ:ŸÊm_Ý4n¾e]]–á\vìµ×]$N+ ”9¬æ# y ´¶áÃoE:éµÏ”Ž*Q¶‰H¹¤™-Êõ‡¯YËT>–mPbj’} 2þ©³t¶;ÕsÜ“]õêÇ—ÙøæädH”¹–ãò] ºûëSŽ®Újµ\ŸÛ]¯¶$ ßº­׃¬¨©:Ž'•2aÜ}"õ™©kÔr]*KP2.êI:%—Õó|«>÷=u<·ÓÜuÓUÚë‹¡@ P( @ PQIJÒR • )'PAäh9·TtéÃɱ®9õY¯îÔuòÉìùOáíõuï™ü¼½šcúi…ts=”ØIøãÛAêÖ¨5ù,ás©He^ciu!EµðÜ›ðü+k–¥±á¨° ¥A†mÅØ¨! ^Ú Ç‰µI0ß—‚T¢JÉçTWÛöPZ+'Dpù¹~ì¾îÚƒÙ$íRÀW1}h¬uMO*f 9Ï—[`‘¹$_²âÕÇDXCeÔÄt¦Ü4 xǰÖgòצÊ÷BÄ<Ó®/½ê‰S…’¢Utn×j”}Ú^J¸ó.(ÙGü>°®JE‹^VÏk-§¢ït×WuœÜæå¼Ô(¿y(ym-)Z[R\XW™oJµõ¿aévã1™¯8¯¦bD‹:#Deã´,Û-$! l›\]Whž¼õ"?O/ôè-¦^aI RO–ÂT<*rÚ¨žH†¤/Û«§ëŸN]¿?ۉ瓟˜&椽6Jo啬„6Ûi²?”kνS¯Yáæ»Ûí¤—ŽTd• ïhsæ=µ´ÃSl°ÂÖáÚ„Ü$ð¬ðÚò‚ÐSÀq©Ç¡fD`êJ’<\ÓÈû(5.G[NooK褞 †ƒ´zOë)ŽÂu+êTK„DÉ:w)’t ¾£©oåY÷~/©ç¿_¸é¦ÿ®ì n Šàê­@ P( AjTXòã¹BŒ:6­—™s æü4ß·Y.0à*ŒùÜ‘Å'–äóýµì×®^=ôù¬Ó%ªá·ÂxwÊ*âF¦ˆÖLxÖ"Ç:(V°Õ]ƒúM·"ê7îÀ’«$ ŸâM\&^iM¬¥Ñe€I¨e©Í98á wåH¹öžÊÆÑek™y'r¸vÕk/e*U …´#Ä«Bõp‰—OzK˜Î4—òjV&Â%|®XÚÞ±§sßh鮵ÎzàýDÆu;"Æ2vW°_bl6ÔYXq{v­Ã¢vÝHR´&ú‹6²Ï+­ÅwOH½4=!vNI¸ëê †ÏHd(–ãøJ#•elP*¸O:ç/—Ë¡Q ɽcŸê ܼÎK9’,ã\ŽS C(JW²á(m(ìÝkUÛ^Îtâ<ßårÀÆeÌX»¦þ¡kQr§möՙ;tJ‰÷ñp>.=4ß<_.{iŽci##+{澈퓳s¤%$žZÖðÄYšÛ„eI@¢Ö ¤ó¸åßYÛ^×f©°êÔáÓ’\}Æö¥¶÷-Wñ™Õ¸ÆÞg;s¨0Ë%µÞÖ<ý•a];ÒÿTeôûŒâ²ªTŒ ªJY7\B£`RO~düV:käÌF©XÑ+Aà´ß‘þ5ì›K=µ²ábªpuAD+j‚ÏÝ=½ÞÚ£!dº çÀs5κF'‰^÷„|£âhªè‘aòƒÉ4ؤ¼§N©d^ßQÐV£žÏ9dÉò[;|´…<àâ7{©ç,%ÃNôùó¹OG·¾°Ü2£“pq"ɶ€Ëþû%j2zs¥rùù biJZ IéÚÛwÔ_â$Û@¶Ôû’,™u¾–ô÷€)Þä‡ýñà.žß)†Çïï®[ok®ºH”V( ‹‹zÇ: ™;Ðä¼k–„Ù!Ä%›Ii<Ô‚›×¿~d¯· GLô³Ã—‚ı&e¶ü§£”&×>j· $¨tk^{³´öWÎ;¹3~P,¡~%(pÐqüMkïm™ø’=ô˜’ˆ’™u¥3©DJż®Àü;ïj劉±ž“"%³æ Y 6R{üU6× ëX«BV‚žÞÚ­5¹:~™QÖÒ½-¯* %€†Ö…Ÿ …­í¦GÕ==Éý…–êüdžÉyÂnK,žýÀÞ¼›N^|7u P( @ P(5}A‚c/ ÊU‘!»ª3ÜÒ®Ãô«˜þ›V´ßæ³¾ŸQÌÞaøï®4†ËR;\lñúAâ zç>K1p¥T, ±Žýh5Ù%   öV+¦¬2ªŠò¥PZRͨ1òEÌO‰'E£’“ÙZ•›˜§uÉO/T­Ýà÷‘ÃðKRb‰ °ÿ8=Óý,jVˆê¶*°VZfô~VF#ªá<§ŠcÌpF–€<*Kžü®oÈ^¹ÙÃzÜWq®NÅ@ P(8Gª] ó_2S+mœtØß~¿1Ô°êÚ£â6)p¤jwï;6ùÄôãzç×(tŒ•x˜fPëxþ–ùxGÅjºuå7Û ³i·–ó«[òÔ|r\Ôªü‡Ê=•é×Y}¶µ–Ö€V¢.V‹ßpiVVj)%*â ð®.Ë*zÔÀõ©ÇBFª'J`g½añëØjÝYúmú3Ô,ßFÍ ÈÃ<±÷pÔ|:ãG‚ðW>DrßLºk¾N4ëO4‡ZZ\iÄ…¶âRT• ‚Ђ+Ìîõ@ P( @ P(4UÓƒ)ώȰŸÊ<‰ãå¨ÿîžGñ®{ãχ>Í3ý¹ç‰%HZJ‚R´(X¥@Ø‚9^—•èrª5Ù] McfµkT¾U””º¢Ð S‰m +qjÚ†Ò ”¥)’j¥O:SÓlÓ®VIß°mÄþ\K¸£o œÖÈ·gÛW=»dðÜ굂¸2âÊTyM–d ÙHW?©=£°×Yü9\Æ£ª2ÍãZCpÐ2A(ŒÊÏ4‹©júR54šäúÄD:rT–ºË'=—qœgÜ%S Hò´ïG„ ¤¤$ªúsMç.›æòú¡*J’’• ¥CPA¯Ö­@ P(!¾¬ãzvoFÊ^ns8ÄDüøsß^ĶúRvÕoÕ;$ßA{Vúî+;ÌÇÉÓ'IÉ6Û{U&‹ºSn•vÝXW®ö~<óT‡¦g?:ÍÈsÎzÅ…>x¸¡iR­ñ«Þ·1Ïi†Ùg`Ó[eé*ž¢×”àÃK8ÿŒy#’¿ŽµÆùv×Á…Äç:‚z `1îää5c%MmKLƒ{yެ¥´“mUsRí'•Åô•d}5êî™Ç7–ÊFiQÜÑãÂê£^Öó¬<7$©7âx^éÙ­¸Œöi´å ê Œæq{á2^|†ÈÚUÌŠëlÞ¬X°dÇÇ%3WµE;ŸÜTI¬Û•Ö%Þ–uJºS¨Y%z gn RBZpî¨.É&ÖÚuäGÝ3º÷}‹Ëc2°Ñ7)©‘HK̨-$ƒb.9ŠòÙ‡¢V]@ P( @ ŠuL*Nì”KHpÊx¸-¹#çHý£¾ÕÛ«³ÕqíëÏ1B޽;/îYÙ­Ze(ÔmáJ¶¤Úƒ{Ó½œÏ8Ú>Ó­~õàl¡ýÒ4+öèžúÎÛÈÔÖ×SéÎÁàº#EÉjN×&;âuC° ɾ¸m½®ºé#{Yiƒ”Âã²m„Kh)H¿–ê|+EþUáµ®Öxgmeòƒå}Ääò±'HȾ"¥GJCƒzv¬o÷HPâ +¤ï³–?Õ<9ß\ôdŒ&Iq r+·T)$hâ9¤‘¦ôðPüxõi¼Þ/>úüßá¼ô›Ôgqrélã£ôõY¼LÇ ‹J½“j?þÌŸwÝákyûzýÇn¾ÏNÝ^wr@ PD=Bëôô¬V› sò’Ðâã0.–’–ʧXjBœHÓN4ãÚZùë) ®½AÏ´¬·™’ž’U̓1’¢5Û}'€+Y¿jw–Håeµ3›ÿó¶iŒ;cËnn\,}ÆÒËÈ’·*Ú5yÌ„ìlfßçø…¬›ß…†·W#\f¶\»}K0›tw£þ¨|äz€=‚Áºw¢ ²g¾ ×ÈG´n=ƒsìîž›ÓKŽ]ãÓØNŸÆ·ÃBj õK- ]DT¢n¥¨ÛU(’y×–Û|»É†zЇ¤-!hX)RT.:A ãþ¤zks5€fE¼ð©pÐBØe‚ƒ½hE·¥!`© ¹à‘§¯§ºÛŠóvõc˜ä=A #•XŒò[ˆWºI禣Ú+½qÏá”ÛŠºÞ!-¤Ÿ3ì,µeÄJ½7ë¶ú7-)r<ùXiþlxá¥ô‘±Ð•” íºM”/ßa\;zºûBôÿPbsø¶²x·Ãñ]ÒãE!cÞBÒuJ“ÌóY‡¢\¶5@ P( @ ƒõMê^R?!DªcIø Ô¸‘Ø~.Î=µèêßjNÅYMº›omÁî­óÃJÖ›Ýnc;ë6˜¯ú¯¥¥ã濞՟l\(­â;•ûŽœkÝ,Úf.J5äíëÇ/W^ùtúäèP(~¥éxA Q¤ºüe) kî"¹å;å¸RVÝì R­‰¸#«®ØK:¦p? í1Q’ ‹®{κ¡ñ8áñ,ëÏð©já´ Óõ'I`:Ž2XËEKÅ»ù/¤”<Ù˜tÖ Be¤9:j,[~QJ¼²9¶„¥)Iï±#¶¹ïݶÍëÕ"]\ ƒ•zé{a•é˜m´XIû¼\fÒ€âw—B»‚ú§â5Ðúz{½WÞ¯qÅÂÄŸ•k$ëËRPØ dÜyhl@<Åzn^l¯I‹³—‡‘åY±¹[KºŸ7ŠëltR÷ÆÊIm‰ñ”.…´o¹À8…6›¨(vk¥qìÖaÛ[Šúм®å@ P( AE$(¨]'B("Ò}9ÁÈrœ}1Š·**Tm{í ¶ðŸÆýõÓýµÏýS) ||dÅ„Â#ÇGºÛ`${tâO3\íˤ˜dP( AëN’ÔxÏ&éf{\)$_j¹¥V×bø+öò®}Ÿ7øc³O¨ùç1Œjˆ ‡’‚î¥ËeæÍÒ¤¨wêã^Û%™$Ì®íé§^µÕx‚¬Ì–ò, $x^oèrÇNFã¼øwÓìÓlİÑ@ P( @ P(ëÕNåfœFg „“HòåF6Aî£aæ#‡‹ˆÒú ïÓÛóÅðãÛמc„ef¥·ŒO”úUå¼Ë—JÒ»íØ¤Aˆ¯ey¦b¸ySús39ŠR2*Š›S‰Ü…¥I)Z;•é¨å\÷Ó3½6åô¿CõÞ«ñ†T"Y–Í“:†î2³ÛÃro Ƈ¸Üú]^½vÊIYR@ P( @ P( @ Pbå%».[-yïGeÇ[bÎêB ‚,Ëo»â"Þ֮ĨéAóç¨jöFH’æˆYV!‹g<Çš>àRÂÇH)â•pÓ]-ëé¶O7l–ÿ(gMugVÁêV'ôæ-Ù3ã‚·¢Fo"év>àmÄ ìV‚öÑVg–v_ûMÞ_»ô÷UçÂq—ÎÞ¦æñç².â£CŽìWßfS œ²þåm¸RœXŽÛ†×QKÊAù¼}}{m&+ͼ–ð†`z‹«XÎ3+¦aHQ„:Da… ]ˆá@nèO\`‡Ý$⑨ÒW ¤¨"IqpÄr/Vô\tÖÉ8b¥§£ƒ$Z$´5iaƒbEY›/$À]N"ªPel8‚Ó¥&¾Ga¢ð¥kk’0@LÌHÐhÐ- m¢–§Lr Ñg=£…µ€Ÿì ÚžNEóç>é¤@VxÞ†è\À¹Wƒ>äiŒ €’)ÊÙ¤@$ Äš‹RB@ $ ªYPª'XÚ©±+¨Ö€PÖ ©8@ǘ)l)´Êe+3°#®>`ÄÿšÒÒ`­ äwº›¦x*Žé!®dÊDžË€€–Žƒ²<¤ËÌfÌÝÍ‘y£H#î*…8ùqÿ´u1\—@@ f†.{ÑÂRDg‰;Ý+HÑ0x-«MŸZ 1"c[$Àmp+” T$WœªïÖ2™ürÅÛq+šu^¤Ê'è²ié³¾è˜oË7q[”)‹Xâ‡ÉÙ˜Sàß)KA}u ÿGÄU‹?w8Ðݰ–n'ÀÛ¬mÉÄ ¹±®ÌæÓ,maèC‚F¬‹¤åPÝíÒg»Ôh`ÙOcÖìh´ ½@`d+²@@ãÞš£.F´Câ†P˹¾h@—•h_K£´iÑ3$V-_–Û‘6‘Ø‘ì°4û1Þ^7µB3b£,ÞòÇ…ŒïdìÏýþµ’^ž¼D?¸@Nâ5L)Ü$‚@ãøüp|D¤÷¿+>Qeðs¯8Þ£Þ¼Šâ$‡‘R§„³\å.9»c.su¬¼æÃ¸9ÎeMóë»ç>‡¸‡kôkè¼èg8:Òÿ¿ ô¥sèNWÔ£¾Œ¦S V¿:Kˆþl­K}èÓö:`¦.vžÆpÙ¥!’%La_»¶³ƒÀ]î-7LâÀ©/“€Fx†Ç ÓG-Áë?ÊgVf@‡'¾d<™À.ùœG¾ò ¸<æ±®ùÍo]pžçHçC¿įŠôŠ7ý½Q?yÕ£œõUÈ:ìSMùÙ?½ö¶Ï<îsÏùÝóþóŒù}ï]ïxáçÃ÷Æ7 ò“Á gfþ+ó©_úsïâàxìaðZÞ»Ô\êýæ/þ[ÁŠ{îE‚ñë—P ?\F/£-¿þºŽ?þ‡ ûý—‘cþ—Á€ÿ¥BØkúw€=ÐÈ€þç€ûø'õGògï‡å§Þg"¯çÖ&!#è%(€,@Sx{i ~]—z÷Ð °‚žð8(z½` GPSåòB8„DX„Fx„H˜„Jøp’ }´2 `o Xn-PƒSˆ¬0,ƒ°2R(ðe‰“'ø}_¦…×ah@hX†Tø¯B~+g@ƒpr^€‡rh‡]@3ZXDr{1tàl] qnwPƒ2RI^ðˆ4d'¿Õ Sõ£ˆ¥çð4œh!¢…hPƒˆ(ló5?’ˆ‰É Ð…ëŠ6Ôh ®RŠÓ±k5ÈliÈõRzhˆ1ðKîÕ¿Ô‚rp;8b¢ˆC͈ŒW¨{¤‘F•jº‚,n!«= PD—Èhaq9Ј®¡#@Ð[0Ñ  àW à lp'I²Ž;Ÿ(#+Ó*~ÇhG3`±Ð±¡#aa,áÄihG ‰Ëè.è)H© % àW)#Vq%ûÑŠ$Pƒò'“ƒ;ntpsec-1.1.0+dfsg1/docs/pic/flt5.gif0000644000175000017500000002456113252364117016701 0ustar rlaagerrlaagerGIF89a@±ÄƒƒïïïÀÁÁBEE°±±355qssßààÏÐР¢¢bdd#&&RTT‘’’ÿÿÿÿÿÿ!ù,@±ÿ`$Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀà B,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°xL.›Ïè´zÍn»ßð¸|N¯Ûïø¼~Ïïûÿ€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉžF ÊÑÒÓSE ÔßàßÚEÙáëìÆ×DÌíøù¼ññ ÔG° ¬ p!„"JœH±¢Å‹%2Èȱ£Çˆ?Š ’¤ÉŽÿœ\y1%Ë—C„)s&Ëš6OâÌIr¡lq耣qÂQêf½7N“*µ*!Û²ÎK ‘ Š0u3¶MY6gÕD…³öMZ5oÓÄE3÷L]= Zà­APˆ¥º”pÓ§‡­º5L–q¡¢8˜7XqcËlÚ&.Œ­cωÜKÀâ»e|ÆQ ‡µÐàÀ^-ǵÛmpƒ¶„Ú ïßQz'¼¸q#Ä%?ÎÜ÷òBÏ›KÇ}PõéØ¿]´=»÷dÝÿ„ÿN~Øø>çË«ä =¦ôëãëÿKý<÷å럓ÀÀß=ùÝà~®2`¨`ÿ) ÎÑà‚~ò`gV¨Ë„œY¨á..¶á‡¸txˆ$Î"¢Y%¦ˉ»©è¢*,®ã‹4ø^82xcŽ<‚2£\=ÙÉt id&DÚuä’•$i†“LFé”dP)å•XX)†–Xv)—`€éå˜Lˆ9%™hr·cšlÚx‰™m¢ 'sÆéeIp@œC„ðÉžv^Iè °À‘ÁXÃÚ¡…Zx‡@=D @ÏTVi0¨£ ¥G$ÀhPD„ꨴwêp M«EÔ„¬¼~±E‹ðãLC®öj,†$ ë< Ð^ˆzì´[R*ÿ\Aã) JKí·]¼ £ Ѐ`õ4”!.¸)¾[€¸fiÈÁ«ïï~¹ï)  k>ý÷¯){þîh· …Ãû  0‡AÓ±Û0Ç ³‘±Ækœl##q@7Ùu\¦É0üfÌ47áò7c9€WA朄ÏQ €B}„ÑL01ŽH{[óÓî– 5ÔMG=5$ý-m\Õõ\ }ØqÍ5Ì)o-µ×1‹6Õg¯}°ÚnÓ wÜ0ÏM÷Ûmß ®Ýz#áL¯|÷mÄže³¸àD(0v4‡#î–Öò5î¸ÈCC(ùäkìlyÞ˜Ãz¹ÛYOûùÿÚ`;:¼JCQx¡§+4Ñl²½ð,À½ /œæ±’èFÀ¤z Pñk(®¢R¶-ÝZ]3ïG¤ äf|¼¡¸ÊblέӿžD4߯ò¢ôÇs“‹Ð+ÔPiêöÿ¦¾{»PÀþP0aÝï€úsÊü:‡ŒÕ…f€ïÒ“xÖ¬gEËz£R€t#8ð5À›^õºÆ«Ð1P\0À½ÁJ }ÔràÓ^Õ,­PP‚Ê–p¬ì m­«Ñ8B……à-pmA"©Äƒ€ƒ¢ãÔ2>pj=Mü— ëp±-šmf¼òâ#ÄXœÿ,F©t'”¢…€2/˜0`LW@Å%†ÁŒú€â¸gÇ+à±Qú# —$Hæhðˆƒ¬’×ã&²Zq”Ï.õ§G)’뚟,)£Eb‡/œl%t÷ 26Co…\ÇêøgÇ•Hô¤oÐxõ%¡×1"tI·T‚CO”R­õðØ&x¸’äÄFœ …@{a¢ÏžÜ²÷Àß?9²}:fuZOéÊWÜ}j[+ht?,Á{§ó詈°›_¸0õÊWK>-0ØÜâwº‰°+ŒVVžññ@L€o"Pƒ†K²[ñ#Fq#½Úï™&GB«š•F»L•„€ÕÒÉn-Í´‰ÀCP×’/e]C³ûÐmAÌzE­Q×È ðŽ)m øÊxœ=†·Ó>Õÿ%p¸üaß‚÷amõÄh«TíŒ6l¬e~Qžoeu1…}Ì:uvä…›aæiUN˜}‡_E¼jÓ4¸Â'>Év“XaJÅ|X8 #Üoîï£ÝZR´@DѰ’ÂAWmOÜ < 0€ ':­) ^6.Λ;\ Žçm¬Õ°ø´Éaê@€`0Ø*TM ˆ5Û…‰"ž|åC™0“Ïpø'¤Þ »g¦pø>ÝX™íb^Ë Ó}¾ €º”À'„× ¸w*Û:V!Ën/ Ô¬ % 0? ·qGH¾ŽŽ7"@@ @‰_‚«|ªsÿY“0ífÀÕNu!~]qpð|7r<e–GJó+óCXqQ±QÀ6þð}»dt&1`³yB}ë£jð…‚-¨u>ö3ôÆ3 ˜Y‚vF¡Iš&NY1ô –*Ôj±µGYHÑZÞÀF¡“|K „sñyÙÇ~v …v’$ä83`æ6ì’{ OXQM?ØjÚ·Z^Q6{2tæÓò „Kð€|s怠o•B$¨0ÈDêW¨c|âeœ¥CUIJcDþ‡-°}iÕ<ðÐêâdØÀ|¥S#ešÔjªV/Äç‰\Psƒ(ÿÙ@ƒE…Ÿ7LØ"‡èd‘å;CMEè[±1LãÔ½W_“atÌ÷p¥^<…Lˆ§íÁ(±áV4ÅÙ$@rÖ‹Ð†ÎØ9?¢AO‚6M¹•BS‡ÚÀpðÀU9¯óLt(ý +ð 8RðÀ؇(íâ “Á3Õ(nVuÂÅR"6U‰DˆÚxGyКÃ;‚U1b‡TC#@oä¨Ï…†ü(BeF”t@÷\ÌÐyiQi|¥S\.c1^÷VvXõýˆ%D‚y†hnàT¹¥'Ác?ãP>O(yMçt²gPÛ`{t@(^ G4¼szó7qBSÿ(帇õ±'‡Èrã1|`_AiqÀ Ð…_¸óxhMJƒUþÝçt°ÓØè’©u¶¢)óv‰À. ,&ØTFàh?31XLÔ4DãFUÜÈ“{3Ù =Åð”ƒYo.2Šç…Ì7ŽfÀ?ö`BtWCym<EgQE#0‰’¯Y†ØROþ¡8>¥|Â0B£‘œÙæ(í‘0k…SGti%ˆçw…5szD¢›iˉ¶2µ‚tƒy` •ãb5cÛð©ƒA!Y²Ò,ÐRÌ v|A‹?£ #^Ý%1à‰‹2´S ›ÿål C~Gàe¬P.8Cs–^0™2SØsX>TO·34-ÔEÀs13V˜iEE¶4I|VOeS‚‡i÷x4”ÑuF‡°Œ¥<ÇÃÖNý)úH‘W—i{"Ó Ã -Qù~èæP+’xsX&!ƒt÷ 'ìÒ'ã'^¹ _ÔU–jÍ0ì‚haàj2™‰=Zƒz¦NE'_€fè 015k“1c ¤‘`s•Añ®Á' ZÖÉn Ç-ÛÃsðXU eU‚ùtrU x˜†€5#yEYʦ (ÿá•{ƒ90ü  çPvÖ7à ¥#1²ÕBÿ“ái—R"É`1µ†3;<ÆGC)xª¸%ÿÁ™Ç'5Fqp '}bgÊäáâ{"œ £‘•ª{Y(>ÁwKbÑä_P¥,£©c j>xfrd¥ÊìF)n1,'s`\æ B5Þ€$¾3{¿4:gO¸ÝÇ.„)0@\‚SC2‡ÛÓ>ÓжD– m²ÅGª¶(SQù}4 Ø‚PßcbTã°IÑHa„NÃbk­d›L-ém Ya!…ÿšÃ¢1X±ŒréMÍÒ´RZ 'ÀGÖ¢+‹¦6(?ì[¸Í“oGÙH L Üs1† û©dåhKGë'D-DtY2ZÂÕøL<• ƒ¦Ò{ÕØëŒ9FÚz­îIÙ©Úì5DòAÕ ýì_îæ3Î3F4%èÕÅ)mt™ÕÑ @OüÌâ%eÑã'}èCÎúÌ+¸»wüPë)åÆ[æ5ó#‡¸ÜþÀóÁkÕÀ“qÒC|Êè¬ÒuìÏÉ´7Äñø2~Ï¢)O^KLUÙÈ^ƒ¹N /žAOÈý4¼®õ§,„(îh\Ãpÿ{²¶ðt~3ê_¸)”L»J°¬Î6\ôãHé¦V>™œÖ¬…M DÒ'u$[gÊ1÷tÒi+±7}VáçhÏ5±CP@8ÎÊdnÙ<µþïÜ5( ÓA¯1¹ ¨UO}®‹°{ÊqÀsîˆ.àq0”Ú—ê*úhí,JsÓ4•:Ö[‚—f€|ró!*©·oAqëÕ‡—§•s¿-oŠÚö€êpùÐ'šAj\¯Y¶ }ï ÎÞ€UóÃ‘Ï ¢Í@¡B?]‡ð£§JUªL™Å†›É¦žo±\Q~2å«߈U¯1ÒYó –ò^.,k[ÿôÉÃ,F€<‡(¾C…ñj C2HJµ‚î@x»™tª«ªº„¡ PQu3‘Õ¢Ój^P¬*kñ¼>Eìú½•¯s0ù Bœ! @Ý<,î(J¦j 4äø¼ ,%44==Ú0 Ø 9ÜxÀm’(”¥$õ¼ñàMuJ)¤ÞV…fýòà „PªYSWVWî9‹“—ëa›k)̤'ºëëànuȽDþ‘´0@¦"€,:;^”A’cF–Ð;ˆµJ¹xà…7Õ.éøäQ¢ byêÿ0#DË\0µŒ)s¦”‰-+ž4g“fo ¢˜ sÑ/Fd)$PD (ŨCŒK NV3™f/RH)€¦F“4ZKòL«v-Û¶uvÂ#à Ô€ºv;‚ëv‡Š)½0aƒšÒŠ<úcÐ ;`GjBÐåë9®<ŽÈIíÞtTñv-žÞt \  zµ_I¥÷Š=È¢€¦Ä h€M[°M |c¸¦ J^“ºæl¦+K"(8%ÊhqM«Gㆨhì4_»ÓL|õ'9 h÷‚WuÈG ˆ¥ ¥5ÕÝkò›ƒ—ŠFB5äÔd™w†Ü'ZHÿÇ€n‘gŽuµfÝ‚u@SÃ=H¥?š4 Æ ™W!Š’¡vû‰C@Y!¨ÆgMc[0 £w ŽCŠ#›ø ŽÕ©@,4à6ð—m. Ë‘x¡íX²Ï-ÒèØeÃT1M‹~<  cJ¡"™£ýØ zÔµÀSúxfWY8À—:¼P#IãW5i§Å \zÕ“GÆø!Wg‘e‰xUĆFpòµ¢œ—öéŒj=! ºHšØ=AE•ÅsFæWŘè×)·]4ðЗ<ÌÊÃp Â?H!¦Ç"Ë©ZPÊC=Ššìd\ÿJ‡Q¦f¤ ¥E(ÒTG¤ ‰ ­ÝªŸåDŠÆ®"*­¼ó¾“–•]Žj ½0Jœ*,ª…¿Rôú‚˜¥†"Ùª±ê¾ø>1¦ù"ò¬ÌÆIïus8¬Ãž¢AUÚýdÓFNf‡H»Ó.Ë/Cñ!Jê»vÈ\ÝÊeŠÙ“rézÌD Pe•I¢“¬ps39c*,ÌOWÂ4!_DWWoJu1—ÜGÅ:;üô_¿)Ú³¯¡.YláÌó(Ü•$ƒ±ä ÷MJPã­Z:Þ`qÖZ÷ç­’Éàï Š @‹ÙÑUoÌD ´‚¬†Ë*£@9ÌåÝ9šOK=ÿf¡UøCË)gÂÄ4ËU*<²pÔóbÄSÎ s{Þ;Ô¡<™än‰*ÚüŠqƒÛC¸»J'ài¼¾³Ô1õÒ O*õÁÜôHGB|"W‘Õ tRÀ 7#Ê!Aì1úõ~Ü9¿¼Ù—w}ãH?@h¶"qE² )†¤‚Õac‹«Ûæà¼–ÔYJ³ß‚ð'“dež² ŒêT´šÜ‹pÞª$G€Â¦úÞ ~b©å C&ì¡ ¤).ägphN5¿î aÝQ$E€ì'š %ñµC<èL¬!£†)P…Ðú‚t8ˆ¬‹Ä4!ÿA¸ÔŒ*p-†q3£Ž  é¢WUœãÄ.5€ß`CE‚À,åE Ö@7<ûTpçÍ"€_‰ÂÇa»©Ñ1’<ù£ƒðÄPØŠJõ’¤¢ÅPe X ÚlÅF;0ÀmÀÊtÜ&ÉWŽçXyl£&1é”{Ü£UT ƒ3¨p9?’¥ò‘+à>úqP~°¬ápy5Íš¥139‰eêhjS!³ˆúA uä(cüj…Í9R²ßAø!¤7,àZ›<çfRà´ÆàŠž8¾u¥œy3 ¦~Ô¸7z~NSÔ„€òñk"ô2îóÁ4÷ª&gŠhØ!Tÿê0ƒF£˜~P&…Ê“wÍ0¢6Th~.²C #X™ˆàÍDˆ¢†HáZ’âéx‚éI ªΩÁ9*Ý‹LÍŸËàã#Kõh{¤ã%šº.µœ835’nŠÁ¨ÜKIÆ™ª`éÐn÷µðúï”/V£¿a.ÑüŒDÊxÑh}ZŸ¼ÆayÊkaR˜?6¹7¯©Œ{¾a½œR,±a÷òú"½‚Ça×±%*ð®êÑPûÍ~fZæUG[²n•žõ2¢6"º&ôHß§^z'Ÿüš,ßw°8>ÓŸ¿øè{ŽÓž·¾ß±ï}B[þû J¹ø±üò§ôè·ýù×?“º³žûv?ý%¹×?ÿÃo¿þ…­Q«ÝýÝÿßä Úr´Zò_ÿ­ÅM9P„Ì˜×  îËÓQ^J ½<àßYàr`BW‚ ¼%`’`,…_ ÞX´1Ø¢ pq×±` NVóMÕÊ Ý}šµÅà *[·QÖò ±©``¡˜á*¡ª±Ì.á»1\ DǼ@oÍßb¡J\Ê•Oo¥ÒŒ¥8a*ÛµiQÛèðÀ þ‘ኦ$@ \ÈOðtê!ti äJ`&™ÒíáF.ÙH¿iPMÐÎÎåa!B⯽€’`Ð샆^!$râ&Ž ˆä€^AŽa'jÚµ=Á«ìÿχ„á#¢âfÛ¸À(„•\@Ë)Æ¢£Ùà.êa/ú¢,a0‚ 0#ã1.a2*ã2c3á3Bã Jã4¶`5Zc bc6†à6rczã7ŠW £8BMŸaS8š£ï Z±•ã:ŠŸ:ÂcýÉã<º_=Ú#úác>Æã;ò£ðíã?b_@ äòdAÆŸ?"äæäBnŸB:¤óAdD2^CR¤D&áEÒãDjäÜYdGj G‚$Æ% @T]ŸZ4™L¬dL´$K(Ây†L:[¼$KÜ$<ä$ÑiJ ÐÊKÅ[×P®E]åÒI—Z åR*åAÁ4?À,BS*ÿ‹Z%V"%iXå_u¥]Êàø¤‘<ÉQ6åU¦EV®åVò¤ZzeZÆå™|ØR ReRÂ%X¶åQÊ%_Îe`æ˜$ÁƒP ^šÔ)æb2fc:æcBfdJædRfeZæebffjæfrfgzæg‚fhŠæhæ’„á>¦5dGR,°B†íQµfmJÔL¥ÈÙænÄ¥üíf3FÛ¿gq 4@kœBjÐd ibH,´lÚ–8”™ ›G<ÅŠ!Âu^H§‡¬þ<“ÐÂNEâOvî€wòÂ&P_ØåtÜÀ,'ËIÂäÐyRÊr†§YÿÊ$*Ï<€gsêÁàð@%-ÀIrC˜†ˆŠ:R™Ó™rœÙýÆš^Ê -аÇý@•š@TIŠÖðC £f óÁÑ…Å^)…Hgë ³€èÉ»¾1¦Ph„A¤’-–/°Aó’j訌Âñ4ËUÌœuŠáÃì7œO‚ò$Þæ¾Í€Q…%'ÄvE0@kðq'§}$ž( ÿ²ÌÁ^3Ô"ü2ؤR(ÛŠ òj†Ð2%³Žqòr/ûò/s0 ó0³=*²j̧º¨. Ä*…g1k (Ü-¼ä×fÖÏ¢Q-C³Aú*, C¿án~ÔÅ®€1ØøE»,â¨n³ÂBrœ-ØœX‰2ÜÀ…Po¶ ˜© 0À lÂÖ:‚ÄQ„6¯sèµ³¯ä¨D‚•T©vâa–`H5Ä-ÕÈEO…0‚ œèoAÓã²dzØÅTZI%®O;Ø 0›2@ äœÅBE |(§–‚´>.,¢¶+䕨 ,~oSÄ%iÒÑfLâJmNë4´îÝî©fÕ¬& 4NSÓÑ)5“ ‚ÜBuT›çÇõHó l(P±B@Ø2€p5ᆜ\„Ç´XXdj¨Æ|ùÅà˜A` ¬IÁb¼bÔŒž¼5Âò°s gKÛõú$.µÕe¸€,'Bàòñ‘_$A&hbL¥[nGÔu’Ë»9¸‘«²ÑÒ ƒþqA®¾„h¢ ü °.ÿëm·ÿºzéô …Á ›‹`ÄS\ñ6‡hºW,C#HÇë+pÂ&Ó ž@"oÐ1‚&wüðºiµñò6ÿsÀÛ|1/ü³רsñÊ7ãÜÓM³ uËG‹1ËJ£ uÕ<ÔLÉYG0×Þ^M3ÍZGMvÓ_vÂpWÍtÛí,vÐGO 7Öi÷œòÓ|C aÔƒ‹³ÞK~vѶÒ;uxõ€í1ã—ëwxÿ‚Kþ0åI{9äu»=yÝœª´Šn‹¾yè+›®3æ¬OÎ9“•ãò:Ü8ÊNú赃K2ØÀÏ^ø«·&`î«Ç´¨ŽGÞïÞÃïm"À9飶ƒOB}¿Övv_—*÷ÖN` ¨½åujÉ£ùòzNÜktnë'»ˆ®ÇœüóË#þ™ âŸÿ_¹òßšžD?‚ÄÉYŒ*`²Ô·§ÚN1÷º_úƒŒÎ¹HLµâÌrüâ€òü h¦z£TuF 8qš¹Òµ¾y˜è7ü¡¡Bs%f}aU¯‚”Yœ£†Ó‚Îzf nçU(W@oD°P.lWjqŠ*þp‰ñÚLŽÿ*•®(Šá)³èŽóÁLX¯TÔHa¦1"q;’i@•ˆF*-8Å~C<TïX²(Ë ÇÞ=*X¬ªE4Øx=ddŒ£|HHK”’¬b™’ÄíÆ:ð›“+ªB“˜Ì¤& 4•_…²{ZÔIMi +Òr&nd$:¼@Ç"*æ8"¹e P©[¾q(5BH/aq28ôä–A"óFY–ÌômÔ•%6J3•ïˆ '¯É,ÔK<“5ÝLz\†œ_4§ÿn¡–§ðeð|Â&ä5ÏØF—Ô À"ÉÇ!쉥Á’?MÄhÖ²ÉÄø|Tÿ›B$Ž€@å4‘©-3>cPŒƒçPÍi ’£áb‚:…C+¡Ñ¤å,×.jÊ|-åŒ.ò1C™2²¦†DÌd‰Î¾"šì'P/ ÁO2Ƙ ë‚O ~K.c9»Ó¦wZUa2Ó «¨]ì5_íèÈT5«nòo§âJ«9ÔP\¹µP(Tåú·k­´­P4+ùø*É Ü°›» K TBà °YõÔ^É ÆåH}"ªdŽÀ¡®-j¯¥-8äì¦dªèK!R tUQ£ ÂÎxš…™Ä¯OŠ*°FXF™ù5´Üš,BGxÛŠÐ¥®`6•Ð#ûC¡u¶t⬧ո¶¢\îO $[¦b÷†¼(^&s©Ýb‹Ð5oGkÞή&¢“Ðgó7& —™5Jб€™`ŠjÝZ´IL €i¦¨™–ÂR]Ë–¡Õ™ƒÉ+É_Y®ZÑl‰9’  0Élý"AÛÈ.À)ÑÜÇçXÁ«•L }¯PNCF¦€Ä Ù˜)²iÓp ¡\&fÖÍh¬ÈìºöÇ+rúût •ïSª.ù”èôi7_({õ§ZyaÛÐ1ŽÎ Ê·õëØ³£2…IË"ȇ`Úÿlc+d´ÀÔ²:ú/_Ðt‰i˜cµsèrDˆ À9KHÀØL.DQPºÍd‰' ô¦Ýƒ¢ÂILº·‹LÁã‹kh4ñE`À'›N ST9cÅ9 (Ó9 èÐ_ RðÁ8æ¨ãŽp0LšŒ‚Ý)D^GŠ'¾ÉrË/À#S/¿ÔSE¡1ÆYþĸ  °Mè aÂ~逃Î6/IPD&ÂT Œ½•`›UÅP|Ñ\¨—`N~ú“I'¨YŠ'¤„âÉ„þØòË…þäŒ-þp!gFlþ¤£  Î* 3˜ª©ç¸À šàB ¤ÒÿÀ4ÿ$€Éÿ,p p>Ô@›9Y±V‚ác.j¡…Z¸èaŒqF{u¦ñE HÀ&RÊ„‹.ö“‡þÀZꨣšŠ.©¦ªp7À‹ƒ;,èÀ‚?közÑ€å$ˆ"ò'L‚ÒÈ!ƒ"ˆ Œ,p#ß'ñ\w( XêV 1È «¦ŠjjÈê–¬C9 ²ÊѰÓ꺣¦JúV4ÖÀ 2!„Â3!†’#ŒLlô\‹ bˆ Ì™€Ä ñB o;@€® . A.ø˜ *´ A¿¸À ¬Â-¬Ã½þd#MÍ -•SÔÖ‰†Y€ÿ±„ 8ÕÒ y„n¸áàæõ Èø3„ÔQ§ ðBíL4ÜðNàˆƒ€UÒ\eLhús ,  àlL;ôp9hI·A äd<êÈAûØ3pøñd‹Db‡%–`Ò­?µØbK-ÂÐ2KNE¨ÀŽè9 6Ô&ˆ>©í¨ó :çôëÏ›1ÀÌ6ÍPCM8Ü pÀRÛ Ý „„.P_L(` ܨÀÇÚ€‡ãí4¨, t R€‚¯›‰R ä`- Û̦¸Í?X VÀÂø€ Ê?Èq»‰ àuÀQìñ@8°‰Xéÿ°á ÚÔf @ Zp&f#8‚ à¶“4ƒ9ý¼AƒÌ„t[ÌŸ²µÔP%èð F4t@&(:£çHÇ:ÚñŽxÌ£kÓµœØÎ"¿’ ¯öHHL'&^HCØfL¤zŠgÌXÈ:¢`&]¨–„™†,¥ Di +YG»ÅÄ`x†R-NÁWÉ?@Y”MÊd¤øƒóô þ 0018À .¶ L‚Z0qgWòÉä ‡Ñ@CÑpP i¡rv`ºQ§ÆüÀúÿÀF˜À ¥@†…T£Àƒ³T ¹#Mb0Š’dYñfÙ°~ À 8± 1Ei tÃv†1aræ;˜Pó$T×Q µp À% ¨uL—‚g"(t‡ÄR7“b[h½ç…W34’ˆ¹Ø€– $W¨ § þà x‚ZP%PÛ§U± :°>Xèe[.Ð"è0+ito¨wt"'üb{1@`ÀÀ;¹ØP ª° @ § À ¹6WãZµ€ŒãLgPcÀ¹Áä0úØa& *ýh,ÿ *°.Cà/>%WHZ§dFY%ip—ª ¥ ª  à ™ ¾€ZN6.—"Êâ(°.µU“çÅBY#*?p6±20CcAuv´91GY—”¡‚â Ÿ€ sÅ ˜€!º0=¾W“XP2¢"*²%2ê3Ü/Hð +]“{$VótŽ Šp†ð3ƒà3Ò54FÓ|ÀŒ¡IPÈG^ 9º .¸°!þô –Y“%#[1³ ðYæéÐ5SXIp0¡Q@7„P„ ö† NÖ’ Dÿðí‘1º U–›”CÄÙa0*CðuÑà:àР..€Üo;)ÛàÓPIˆN@„3Ø Ò5ƒà‚ ßyvÀ\\à, è"–祘CÀ "¤˜90+Îà) 4öåb¢˜*03Ìô}2¡3Ò¥ö 8*10 p° + 50`_ Øì–耓a#@$ ë2ð6+Àç‘37Ž”c«# N8£Mb›Ä²7LIÐ^W9q ¹À ¾P )5È)h•óäÐ*,ÀZ¤>ÎYcƒýrÖ „Xsº’ \ ‚Gÿ™V 8x©Q"3P z¶ XF§Cpe7€œ! ü Wˆ.$÷ŸG2Ãm0 @‹5ÔCɦ1¡DP ÐÙ#€ ‘Ú«À@^Ce#à&97þ ÇY9OƒÙ¥Öølæ‰V`M°°xäL1QÙ€jø`l·°W¾Z8½W2A8ð’àB /ñÙmÙ0`9Ù÷’‘v)† Ö Ö` þà Ôp #Ñ? Û 媷3·Ø;Àû / }ð׆<‡@|€ {°—ð @uk© 9¡!@¦âEÌÿ‘&¶ÌÁã–кsu?²@¨vÂ#pàC¦  ­PkrŽ£–  /i““$`6øF9a{[²¥&P[C€ç`йÔÏySH‹f…#©Fö` c` #«‚›Dçpe‹o*{ÚP¶$F–ìæ62j‰/ qiy„"w:Ò0rµØ¦BÄÛÀU>Á\Ãn&5,ÄLĨêJt/è/±:p2)Gi SÀD=Uµau9£ 31Ö`±m±€Ï XÔä0 ´BßPf ")v&êÿÀ¤êc}´·ãl ’ `¼•ô*êµ ÂFÛtFü×SÏP¯xwC§w¶CkÆÀ<À\À|ÀœÀ ¼À ÜÀlt €–ùÀÁÏ€ÙÀ¾n!:ÍIÁñr1‘±ìÁ¡”б¿0q}&œ› ºá¥1‘¿/<1¬D*K—ñƒ0Q¡9 4¬è›b8&Ä’*xkêœ+þ@!_åÄÑEë¥WKk ·báÉá,%ÌÅ!£t©h¾nCa€Ï¨Æ ±züA}yÁNp\ÆÇ”o AlÇ †a¼ÃËÿ§dÆAæ"ƒLÈ«³ÔY‰,È !K‚!›2’Œ 쉱‰ñÃ5zÌÁOwèVÄ\,£q!zŒ«¼lbôÇDQÊ0QÅÌ·­×©t{u’1ó»Æ,˜.‚ß,£ð'að›•ÅþàÉa7EIǧüÉ,úrà£\o<…Çxi@Ç^hÇ èÿ“àµ|Lñf¸\—ì‘,É”ü̦Qó|N’f$K¯¡« —’ÜÍÀ,~LQ'¥Ì>H ³LÍëóÉÇ1ñÎÆlpÈ\qɡÚÇ}ÝÎø41€°¸hà'ÛdÒ›ÿáw—á¼ ¼Ã%h{ Í”QÊJÐJÚÄË Ì 9q£Gó\Ð#Íèdv]¾ƒÔG [ ÒZá'[0ÎéGÅŸ¼Ä;£ún ÕDAÍ/½ŽgÊ’,£1ÐãÒ³œÎ1Q”¸'iüÀ-› ¢à 8±=1“Ð\aÓù<ÏÁÅ;쾘Á`Õ§‹mwÝvÃ.‘™0†Úa›{°Ò§Q2pN#’ÙiÝÙ&üÃ1!Ž!Ș°Ór±^0cÀxn7Ð~E,BÆ3Ø%@f.°×v´6È‹@E†§ Ї4ÛV ÀBvŠ­Ï[ð›"IöÜÿ° óoÕ)D-#7”®@‰Ø‘ §  D8ƒS¦!J(eVÀ2‡}¼/à°ûÌ:’Vi• P¦ÖyäźÙê}©`  N•†Uߎ¬¦zÒcðÆy˜ÑB° ΋Y;Gî÷à!­p†îµ†E^=•fà!Q%¦¢WÙ *×@HôwôØ0Á ¬à‘-þ fx„TK HµvâdUÓÁá~0Ãó8á=u°¾8 ' .ö0Ή¥ ÚU$¨ Œâa!2Q G•Ì2`"~¢åa7ì3 € €ìÿ° @cÝSpæµ!—3g:âÒÆ®0‰qž¡’bŒ11âÍØ—"Š|¼Î 1 uÞV mÊxXG}-w+g`0Q ?¥Þ¦ ©õ«¦5),(Åb@QXMª¾v“Á! I# ÃÛL7D,@®9’ëG¾‘âV¾a ¥ ªP ê8êÐ#ì¬L,f ‡b Õ~¹Ñ>_ñ ÷3‹.7 ƒN7È+”®í:Ò¦ú cH š¾¤îþP!¼ 2åal%ÉTÂ…Œ!?X,ô)S°¹±ìÑ{!N7Fî ã ðiÅ8Á Öa ªð¨ÿ óç( ³@Œšè(KÅ_ÀOñøG €O:ì0û+& ¢4v¸ù#ë뫃ÜÐQòJÀIÀFÄpë*ÿëàš ó3?TçÈ ® þ€d1Á«HeP¥8± 0ô3ÄÆa²å€0T**ÀHnRÞpR¿3ÁÚ%Mz ¢°ü‘0‘[Ï;ýÀ€íò¨ð ªÐ £  ®àyW!¥ö³Ô«QÒ\¤³“縉²5Š{˜î *ãÀ.p)nGµŽHvdÌhfÀ’£Øç`'•\`D H ™® ªP ¬ kþ Æ(ê,ÿØW¦þðŠU[í)ûý8*ìFœ:€ËÐl‰R_3R'Qãü›å‘ ›þF¡b¥ÉŸ?LþnýªåÏ0\þ´ ùBæ „44ΠÁq# 3LÐ## þZhÔ8¤Ö‚ 8ðïßÑlîäÙÓçO A…êâàÑ£̤aÚÔéS¨Q¥Fu²Æ_'V jÒÄÊÒZ»l)ôu0Œ2VüýÉRãÈáj$Áî CFæð·l§6,h@Ôðaĉm"@Ú8 T4hÊ#æË—&_¸xñÒ… —.™Áˆ9ƒfêÔ1WC%l|”W0ºvŒbÑÿ˶pÛ‚Ä­1݃Áq”œÁÂ_ÅÉ•/ÿɸõÁ.eÀ¨õg…:y=Ú¾ýQ#ðŒ_ä(Ò¡@|Ü8j¥‰˜2¦§‚9T'ö}å•Ë_€/nãM.”‹%tHnA²Qa…㘓pÂÃxî(>æp‘CäBA)ÄBÄCoEYôÃŽ7´¨"ùš:c‹ÿ2ù¤5[ö«¥—±´¨ç-·Š,’#vܹ‡à‚À‡È1 Â(´òÊ(àB ï.[¤Ci¤BiDÍÛ\ñ@è°Â 1j4#¿ ÷˘±Pa£‘tã ®‘RàБÿà mš‘‡! €l"KL—[àœ Y3Ì£ iÄIL‘4ÅssU0L£Ë\t¹…b¢£·8*P$`5rgŠ-¶tN¢A‡5úÁ ªÌtÚÃÀ)AÔüDQQDÅDAD‘qUdõÜ7aƒÊp|ŽŒ¥@L§cp`›t8ç6†(2öü¢ö`øö¨CAµ1AÄýÙVAY„3ÑõxEGäÐâ½&´Èï™xÝ ”£•sX…ZЫ¥ðàŸmBp!€Hp…$ á$¼²‘a¤ƒr®1BÌýøi¨Ý|ä?æ ÿ 'R€a‘VÆ-`+t%ÁåŒ#·â@$X!…!Vp!l|á ä’Ö;¨*5DC A$EØŒúð6!$»\R+ΞË"pá_X`Á ¬x!…† ÁÆÀ…E«ƒÛ8PfoØZ€½sÀoP[[ø =J$ED‚~x9 Ä>Rw(˜rXR¸ L!‚ŒÚ"+\p£¾¹Ÿ“`•†ðA‚sÔÚ¦˜bWŸï-à €‹/J;Í)ÉØ 斆¬¥\‚Ñ¥`ÃÙøã è¸CÑ g4Øêr÷ι@Áú,”€KMˆÂü¢Bÿ‘s #€#$a G8þCyyžp–=áø@A X¢‚g=°>)0JR²1€, 0Œ4Ø+ŒÁƒOAà`äâ&¤b #2$`c6*8 Îa‚ÁöÈ9pðŽ)¬¡‹b†?о I€=àT˜ÐA ""ϸÌ…3òc ¤šà+h2ÈG?À*F¼ø“‚Sp1Œ7€™ ƒóàøÀäOК C@ €¼õDÊC €àXyÌ”…Z£`û¸Àˆ‰~ôƒø@3ùøC’”dYüqŽÿÐày#° ,é$ãà†7h<ä1ðÅ’BË ”v` Hä?­ãå•PÐ 0ó™ûä§3;`Ô‚š‘R{¼9‚œãJ²$*'EóÐÇŒò  (à@Ç3\ðŒÚ¥¯'Øè Pʘ0€ p†ŽVÏå(¬1è@?ûé`êÃV˜Ã$¼£Ü"»à…/~qT`õ¾Øl"Œ ±Â Hƒ&á þ`9Èñø@#S`Ï~Ò€(J €¢• j#䨣7ðÓƒ 5°©3ƒiyÈ €øº*“Å¢ž…(H‘ŠQd¢ÿ14€@:(w”¬a7øA~@¤xÍ ;ˆY‡"­8cÜÀ6´¡fà•ZhEÇL5°Oðc2 ‚â‡7­jrÃ&>! S¤"¨`nsQ¡ ƒ4&$H– 2²<ƒÛÚGXæ‘úò•­–&Øš l ™ÈÀne@@ |ÐD%J çæ§'.Är¤`-ÈfTàé`C‚²äż‘W}Ub€4 PÛ™^€™8åGæÁ‚;LípˆpÃ*”«_çš"’åÒ6V ±Àª$hÜH 9¸€ÀBÂrãç/•0µà·>‡ÿÈ@M?à T`mÈCaY5žSï#ÑIJŠ/ƒÌœ`ÍIàŽäx9‚æV°‚DÀe.øA ~,7´ù{à €H‹L-`ø KÖç>7€y´"n‘"QˆméPÒ  ¼áêm5n " àerËÁœ°UŒ€D ²Q×ýY1 ôAøŠ[~ØÃÀm“£+v‘m# Xaplå ËF@\@€@„Ù‰^Ý`9”Ú(,¸ñËXƒl¢Nv¾¦¦qœ£Òbg8F°³ƒøYÖAÑkcnÝÌT””€‹aˆm€Ò"ù^—ѵD®BˆÀ§§JUäÐlÀ5$c'§, t£Í[(Ø?*L LÀª¶%lÈ»È x¸ûÐa€ÀT#l*˜Á¸€cß3³C€7Fh)‚‹¼' (y˜$@È\é=ŒO ”p*ÁÛð7h0…¥[= •¦NuÜQÚ`õÙ%ÔhÑ Ç:€pc.~”sH]ép¼Ð9 €¶»ýJ|Bçruº½|ÔÝ3tÁ#Œ´yÔ`€ƒd‘çɯ9ƒq>v xÎxE/”bŸ>¸øõ®zØÇ^ö³§}ím{Ã;ntpsec-1.1.0+dfsg1/docs/pic/pogo4.gif0000644000175000017500000000621513252364117017053 0ustar rlaagerrlaagerGIF89aH´÷ÿÿÿÿÿÌÿÿ™ÿÿfÿÿ3ÿÿÿÌÿÿÌÌÿÌ™ÿÌfÿÌ3ÿÌÿ™ÿÿ™Ìÿ™™ÿ™fÿ™3ÿ™ÿfÿÿfÌÿf™ÿffÿf3ÿfÿ3ÿÿ3Ìÿ3™ÿ3fÿ33ÿ3ÿÿÿÌÿ™ÿfÿ3ÿÌÿÿÌÿÌÌÿ™ÌÿfÌÿ3ÌÿÌÌÿÌÌÌÌÌ™ÌÌfÌÌ3ÌÌÌ™ÿÌ™ÌÌ™™Ì™fÌ™3Ì™ÌfÿÌfÌÌf™ÌffÌf3ÌfÌ3ÿÌ3ÌÌ3™Ì3fÌ33Ì3ÌÿÌÌÌ™ÌfÌ3Ì™ÿÿ™ÿÌ™ÿ™™ÿf™ÿ3™ÿ™Ìÿ™Ì̙̙™Ìf™Ì3™Ì™™ÿ™™Ì™™™™™f™™3™™™fÿ™fÌ™f™™ff™f3™f™3ÿ™3Ì™3™™3f™33™3™ÿ™Ì™™™f™3™fÿÿfÿÌfÿ™fÿffÿ3fÿfÌÿfÌÌfÌ™fÌffÌ3fÌf™ÿf™Ìf™™f™ff™3f™ffÿffÌff™fffff3fff3ÿf3Ìf3™f3ff33f3fÿfÌf™fff3f3ÿÿ3ÿÌ3ÿ™3ÿf3ÿ33ÿ3Ìÿ3ÌÌ3Ì™3Ìf3Ì33Ì3™ÿ3™Ì3™™3™f3™33™3fÿ3fÌ3f™3ff3f33f33ÿ33Ì33™33f333333ÿ3Ì3™3f333ÿÿÿÌÿ™ÿfÿ3ÿÌÿÌÌÌ™ÌfÌ3Ì™ÿ™Ì™™™f™3™fÿfÌf™fff3f3ÿ3Ì3™3f333ÿÌ™f3ÿÿÿ!ùØ,H´ÿ± H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Çkkȸ¼øh™aŽÀ™sÖ,ž¯J´¨Ñ£H“ªtáµ§O›"|ºkS©©Ò«À§Âhí‚ÖÕ«V®e¥>…F‹Ù´J¡†E 7©\ZÉ ^{÷m]£z¯…õ‹5pU½M?Mf*_Y éÍûwèS4<R\™§¬k˜= á¼p£Ì£9Ç•5$s#Õv¯µÎ,zÈ×¥‹>M5îÕ½E£ùÜ™$+†—{ãvÌ’ÕŒ@Ne+Ì2 3>=-:3ê+¯yÿÁ®ýš¬Ì²Î§.>òÚõW¦Bµý”5ñÜ'ÝcPo£ïõÝ×Ò5ˆ`'C¤ÇÒ5­ì‡`‚Ì-xÍä-!bÍyA v¥]ˆáJ  ‡EsM4&^ØÒ{‚PxÜ@O-SŒ^Ñ“Ë2ªävؽã2¿œ(c.Dâ˜#J³PÈ#tYE“Ë/ÅD¹L‘ ¦¤üÁxbŠOý’‹b¾7à YqædU¢+f‰ iSΘf~l¾÷“–ŠÙhä‘ù5ˆ]e‘¦'˜ *)H œyùË2Ëp™Ð5¬øøÒŽW0Gš—DfšK4ý!Bá!Mˆ]+©)‘ ,åé~’bô»xÿ"”˜ˆxŠ2þâ#¤JΠáFžì"VU´”²!|¶’† +¢²ŠEa±Q +dA³‹±ïš¬b;bw(GÉÐBBAd )‡n •˜érÄ -HD–±]´Ð£ºâÁ'+­°‚GÂ\‚±Éô‹]­¶’š¯¾JB±+×> ¿•–ð0¥ø)"‚tÁãÇ3©4Â(dì%<Ë0ë5È3Ø»ŸÈASŠÉÖ&S ³@ºïZd]!³Ìú¶º‘0z(Ôs2—°²J \1Ë~d%cJ’Ë_!%C³Aï¢G ”½+Ï C^ÂÌR ÌXÝQ2rø $+”­w+bÎÿpÍÝÂGÒ #[sä­7úÉ|è.-~LR2žÄBL ÉØ¬Xaö,iï'˜:Ø^É R@kÌì5ž`E³<^3Ì\²‡‘`7 4H ò7ÓP‘ ";} 2mí"ÌóÉ$MôÉôΑµÇ±’ð/ ¬Oy¾B+½b7;/[=/LUê3ÖVÒ]óۿz4t‘-Cߨõ¬Ë%³ˆTËÖ"¬¯a„1­øý 0=ž•<"š2  A¼¬B[hác)Ð~Ô"Ëp˜ÁÇZz\v "ZÌÁ#­hÅW·&+DG{` &®Ä£ƒënYÁ¡ÿêÇ=…=…/»B J!¬KKfnÊJ¸4ØKC­ " 93=aíAüà ëVÌBLR{Ê ^ð‚¼@/ê[-šv Mf"‰†®P¾+¼nM]ÐÐßXQŠiQh7Ä D©…}ìg<¼‚ f•–Ï@’ ÕaÆ,6ì ]¸$Ìð*A ;§œ¤¨ÐfÂÙë” íì¥5Tb R3 Ÿ-cÆËSbÉq$|(äËýxì 4a-?Ö2ZäÌô$È|Ù4òh™C å œ™ 5S˜VüǼ€/°"ò})ˆEQ#¡/û÷”qza_Îá۟Ĥÿ<çp3'O¡PÇfÐLIòÍ ½'B[ÑLJuk™=«¦HøÆ¯ªȳ ÊÑC)I—‰$×yÄhس£Õ¨¥KV¼ç”€D{®1 Œ&t£åhBk•¤Z‰ÒoÏŠŸJuŠS…*Ô‚F¤©’^¹MF¶‚¨FÕiTÊ/Ždÿ ŽÕ¢ætª)5ç1PYT§Hü*TÃU„½ R²DÄMýå…¢´¨líªÔŒ0§‚HÇj%Wµ¢4¯_­UHˆP䔯î9ìPzR¶~ †aED"#JVªåW;šÕˆ0v¡zéähïÊQ›ÞÔ¨2僦úÇÙÿ~öµ¬Õd;*ÛÓÚórÕk^oJɯN6¶»{ªFO«0ã¾Ö²)¥énŠ<¿·®œåìS¦ûÜ©Öê©ÃkrÉŠˆÎvV9U©s-KªÓ–£»«'gˋѧ„·»Ï}ÝPÓ‹ÐE>ó)Oåß 2U¼†5§ß½îaï9ÒŠüq¾ô•îp ÜQH±·ñUv½€0þ‚Ö«®kN ‘¿¦4·rEXe½šWä…Á¥ÕNz;Ùty´™ï‚ÛÙׇpæ¬*†.‹oZã®"¬Ç©$FqjÄÊâ7ª4%jº"táÔŒ0tתÐö¶ÀåE®EHEÉJºôu³ø0b‰œf¶&ÿ‰íˆ^bXæí®ö½lõ2n&Ë#~e« Æ1‡í{ešÊµOÆ«zëja²R’oûê—)œÒ¤ê.˜i>®š;:_üˆ$‘U³¦¥*f-O²=Ê=ð¦÷‹P¨Ø”Ç BDƒå|Æ ß´Ì ®]+S„ʽÂUôhûuOœY“”¾í¦wºd/Ô¹­õZ§k\P… ¼j%ë\ »cƒÚÂw¦®ªW›ÛHÛ¼氠E|Ïû*Áõðwl¥Ô&”®Ú&õhßùíú>Åm»å.ÇáîøÅ:†f+t[á’56ž÷Œ·e‘LRa´°}4¿ +kK£ýRëë$žS‡‡AüÿzµQë©oÿ6œ¾¢}­M¦™¸çÍo¶§vC-U—¨¼g2OU­Q“‡§¾í,öwÁqŽZ' "Ð8ilêºJœÄ'‡4‡¸QDÔºœµÌV ï{‚», ©ÖÞ½íB6´Ïz--žËåmtã^hú:»Øïé•Õþ÷­‘ß<áWá "à 'Š·ŸcÛø,´$Rj<ðÖ<,0ˆ{AÊ¥!âêAdaƒˆa …§ù²Nó/ÈÂéQ/ûÚ³‘X€góîÆÙg÷3´Ÿð´üØ£>õ¦oãã“Ìù°~í«þ IEü¿Ó}ÎbžˆÛÎùà{?x­÷þjékOþÔ~*y'•ô¿o|êÿý3ìÞ3·Çº¾¿íLQãñi¿þ÷?Ý"¿#<ýg}Ïòc»ðnxÇ~Âw`¦÷}ñS}§w~ò‘@¿g?>We­p{è€Iæj/Á'^Q‚&x‚(È;ntpsec-1.1.0+dfsg1/docs/pic/alice15.gif0000644000175000017500000006333013252364117017247 0ustar rlaagerrlaagerGIF89aé´÷ÿÿÿ!!!)))111999BBBZZZcccsss{{{ŒŒŒ¥¥¥­­­½½½ÆÆÆÎÎÎÞÞÞïïï÷÷÷µ­­Æ½½”ŒŒ{ss{kk911)!!B119))„!!B)çÿcZRB91ZRJRJB{kZν¥ÆµœÖÆ­„{k{scçÖµ”Œ{Œ„sskZ¥œ„½µœµ­”­¥ŒŒ„k91cZRJ„{!{ssk­¥!µ­ÎÆÆÆ½BB9­­„!!ÿÿµ{{B))BBJJkk!99BBŒŒ!­­){{ŒŒccœœ{{ŒŒÖÖœœµµÆÆÖÖïïÿÿÞç½{„c½Æµ!BkŒZRkJkskÆÖÆRZR{Œ{csc9Z9Z!{!J{!­!)÷)1­ŒJ眭ÆÎçÿÿœ­Æs„sÿ!cc9{!J!)B1B1kRZRk{{9BB)99))BB!ZZ9­­99!„„ccJJss„„ŒŒ¥¥99„„JJœœ¥¥ZZÆÆss””­­½½ÖÖïïÿÿµ½ck{„)s{9s{)1!)Jck!)1RsJ)„9)9Rc„œµÆÞÿc9{)œ!)19{B„RZBJ”!œ”Œ”œ”œ{s{cZcRJR9191)1)!)1!1¥k¥Z)ZZZBB))ŒŒ1199ss„„œœRRµµccÎÎss{{ŒŒœœµµÎÎÞÞ÷÷µµÞÞÿÿÎÆ÷ïµ!­œ”{s{s91B1)!k)1Bck„”¥ÎÀÀÀ!ùÿ,é´@ÿÿ H° Áƒ*Tá‚ ÿEHÁÁ‰Ëʈ cG"D(Ðà‚ΈÀ € 8°ìÁC,PÀ0„e J´ -Ê´iQþ4XÖ âD,ð41qât¦¬™?18ú#à/PýÉ­h`gþ=@ÐÖ¡Èø;áÏ%+d0àáBŸË(f*W. " 0Á2 p ²²ÉCu¨< ‚†šY˜ØÂ‘)áAÛ‘.4XÀ €2ypò³‡¾WP¹r€´–9¨ÎÝr j#ÿ²Àß2 øs¡:ƒƒŒ´,á2 Ò£6a€Üh'ˆñ€L $ •H 8s`ËœàL *À€3,PÚiû9äOcÿœ Ÿ2þxÁn øãÀk)žU3 ¬æOŒ«9`ÂAb@M',Ѐ3>‰QY" $•CŒÈ˜w°-£#àÕ2eüÓHÊ,¢"AÕ!V@D  Œ<°Œ>É@}e ‘$PTEþuWÌœÀÀ ÐÀ.@@LEA¡„G¨ZY`(p‚2ÑД”ÿ(ÃÒQ¹ý“ß{­µœ2¤u€zb&3Û ÿ@]£¥ ×ku`[ (SÀ2º$×™DÀ>MA4’IÄ ”&WxApQn8ÀµWXàOU%‹ÓPD¢LeX°@#Ê,óÞAù³ÀÔP€H€lùcÀ € ,SÀ „HÁ™”Rj“m\©¹@ƒÎ@ÝlÕ µ€ ø¤@‘½>À+‰|}ÈÖ Ï`i#Ì,P€ž•±%€ ¢­õ€«4;SÙ ¥)T°p®@É@yr]ªÀÌþX03î € (³t>»ÅV¸G¤ œÿ4 '2`é¹õ€;IP˜3P˜ <ÐðÞÿªÿ'k€ù9 V`¶YÐÀpê–H;°Ì?U¥+CÿÌæ“H8q™&‰â óM;è°#úè¢Ãk•ÑQ22RØëþDòLž4ûóš¸<UKPÝUI…‘Á5“b4Au pµëN8‰¯GAäk " ª0âTDr¬z|o§÷Þúc‡ðÇïÆÖQwX3 €Ì¯B‚‹ZFäÔsäþ¡5eo âòG7DçŽÐä8G:°± t\ãàÈÆ6ÌQ ttëAB%`ÁRÈ:Ro°Ö¨uä"¨eH'šKGØä6Hû¬ÿR0ƒÔ±z€ä› D 8ÀÖü!ç‘ÈÌ8 @,`§tk@`"@ -M, ¦ ¸‘oˆKâ¿<´!!@ƒ†e¤ÂD±‹¸MÒƒ°ü 8@ŠÔF,BD#“éFnø£ $‡;ØQŽtˆãßG76ønøƒIÀB€„*! W8‚Züƒ*d¡¶ÀBz€l#.”ae°ê$à®[F£@­lGSÑCti ‰þh°Œîü a𕪅:ÀÀ5¨AuN`ÄÀV§ä€\`œÀUh¨ ?FÀÿO~Š üЇ><Ò‘{쳟#èG?öa+9 ¢hÃŒö5À0ÚOf2…µç8ÑB °¤LRtþÇ:ÖÑp\CàÀ&w¦…Žp')À¿(Ð"¹,^€Ú5¢ŽtÄC˜@&—‡Š@Œ–“ä… àÉlè&¿Ü£ŸýXM>ø€~ð³!0ëö±—$ꋌó\d%„Ä5˜ d „Ô`2ðG TPêp eþHA>ô “f´ÛáÔÄ!q8KÖ`MÆEÀ†o $œ—ÿ(cψÀ$ÓaIb€â°†:ÊŽvÿ˜#Øq0D3*渼$‰T)ê°®Yõ¤¢KG7¾ayøc¿øtyÑ‹_à"v·F/rÁ‹_øƒ¹ÓõÅs¡ ]T'»`Í¢”€ãò­@$0 ì ÿbÀRÝ„W þPF"A€B:sÀ‡:Þauè‡M€иƒÄ šD#vs ~¶¹pÀÃþØ€ H ƒ,2ä°‡9ÄasÈCþÀ‰P âÖ§0E*TÑAìkÐ! AˆAì|ØÃ… 8§ˆê ¡¼I@hæKGÌkUˆÀÈ¢»Fe°7ÔU'¼¨rp¡å\ôâ‘à…/|Ñ ^ä"ÀØ…-Ò-LÈǰ8Èfc¢‡œ¡ŽÑ•˜@o–a>»;Á¬=â4Ðaƒ ´ŒÐнAÀ ‹ÿHw&! U䚟…(2አԾöïð »H¨¢öÕœÝWìH@AŠ[ãšxD&aûÛñްíäâ R„"ž…ø´ãu8rFMùY€2Ò”¹ ü$š?Èÿòõ¤ tæhFüõõ ¢p…4o|P Œ˜¥žê‘ö×ÿÌR!¨cUþ·7™a&ÒòX H3Óр؀  5ùq5 ‚~`ˆrh´|ÜÂTƒ•Æ2'Uz#:ÑÊ -Éer Eh3%x'@€ ‰b{@ΰ(lvàu'P„òG5h #c{¶G° #^ s 5Dˆ ‰² ·GRtÏÖˆ5ê¡ðxì#“Òi2 Ñiz²M0ðlpv+Á&ò¢O„=°b€(!q Ë B/ÂÿñÓhÀœuëB(€2 ÀS ‚ñRi<‚}TÎ †bnàXµ&TÓa0Q%ÕƒàC-ñ3ìÃ$ÂqVÇT@)dÀX!Œ€`Pà0s"Ÿ’,Ñ_Ø#;BE @'â MX ÂèD?Å‚°„rqG+ÈSAâ_W}LØ»h~ 0b„·WÎ0ˆò°CQålç}ŽC÷6c³C8ÿ1Ëðsõ-ýU‹B"RE"`c×h)º‡ ³pr*T´m¶7‹ÂLO¤6K“]°ƒ‘Db(oóàÇ”7"¢EéLRA$Õ’'@ P p¦pTb ø’M ㆀâ3Pcf}P˜`W~õç¶G!fˆZ+±fÓc7÷–I(Laù0Z° Lå>ó™h#‘àp6‰T"RØu»±s ¸2,C‘üâ•S]Ç2¥'ëÄYœÆyœÈÉ€s–œÌI3p…6°./ñÐdcj²€ (<£ÕÓœnœàÿ9žàIÀÚåPÓØØÎ`Œ'.;‡p³áTr¾b‘)‘´CÙ#¤L\1h3áñ Â%qL6 Œ#Q±.bÀæö:ñ1ò¨ Å"@$à T± à p:ò6,¹%6‡Pq);X¢•£Ì02Ï01óŠiryb•Q^ËÀ‚Ióû(·ñ˜ƒ¥R:¥™ÀF;òY9"?Ñ@!8\2$TÙ( šEUÑ 0ü¢ Qd9S§r:§tZ§QŠ9Ɇh“"‡S”Ñ -h éALOTQ>Yµž‘0¿ÿãºâÝòd0>@@+k¨D|¡©v:$Â_'0Uøá <Ã!ŸÏ@!­ñBÁî2y&e^t¤§Å“§¡g…ùá*ËQ‘QgËà76õ%>1°Qpõ¡¹#.@ãaUhà `…ÁlYsrÁ ± P”11§`n×Á‚$²7fPæ3£c)rUŸêL§^pF6!EØ£ @ „¡5~Tï¸Ëá ðžÌÖ°û: á€C*¦{î…¯YYZ. ;Z gÖƒ4ܱ“FÑu‘2š·q6°?Y%µÿ£HÑ<ΰFp\ D°{³}è¡à6አ— ‚Ì“!ë‚66gY“g{±³ÍÐÖIŒ`Âgõ£¤EUô>ÆáaDcó¡ÝQk#%‘Ÿ—#ž%Q%€!çb,2×3Œ)#Cð&ØY«HqÐ"@'+éª'ÂÓF$ŽÚuö7Fèl C’^4£Õ³Û‡6“Ò¸9œJ=ܪ5ÆÒ w{êI+{¡Z‘•H;{˜' Ç0âj­Hƒ6y3ïÁ6$ƒä|A.¡þ@åÁ áÎÚT¨,á—¡EvQAÿR@`t0Å‹0@“q„‘{C'5Р}bp'­X:)kJ=&0Fê±/Z¤Uñ¡/‹ Ð ¯h§2>¡+Ðú ÿ€%AäÅL†ˆC³žZ"çù!ã¶ÁÂäjþà5¡KÀ"»áS‘0m#dð ¼ay“S-õaýjZ9'`‰¥5Âqþç3Kìˆ ÝD-Œ¡¹FlÀƒoƒ& pͰ&Zã8¨Šå‘Ïà@à&Aœäß0í0çßd逿`é@:¥ä êÙoØtƒ£ñrÑ.\˜&\% ÿ‘隢'Ô18”{à2š'W @ Ñ+l‰ © ë)  p‘ |ØJe°bˆ™R™Dx@GñÓÆÊÎà¬64RàóŠ8AZÓ),ž²F;)=2À^p íÀ@{ŒÔ çà£$îîà ¢TÐÌìpØ4JHJ [ÎèœÎì¼=À¦ô:'±zb>¶Cô¥%ZÔHNÁž 0÷tÐ̶q)ØtLÍ¢D°*À% U `ãƒ6" æFð•‹Y™'på%tà 0ú T`%P±ùÿTfb@þÀGA£ Ä‚C’Ìd´‘ŽqRéP Ú€àP¢´IÈ@3)ðh@Ôñ[þ€JÀ¼b|Ô0AÄ~\3«‘Ên"0iݸÀ|6fÄ-ô’bYÀŒ²ìH‹Àfys8~X Üq$À}¥+À* -* 20D þ„ P °°O µ÷µ°#ÀÀýOÕ”M|kƒ@¢pö$…­“1œÕØ‘ž¶Cy±Ç£J 0%íðà 1T?•A«!K:àL`+|ƒ˜^à ¢S þ@ Éÿð¤ÏÜqMÖ±LEó겕üÂ1ÿbñ¶ÖAÚþ”÷øP €ù€!P!€,bÀNÕ6žF-@ø\djM204@Î'h`z°Óq¬í}@Ó.Ža2hql hñ“lðÅnñÌá 0eU`>7cnì°ÝAÛ  ·  v· ¼°eÏå ÑUD0ÝQ@N&LëÖ Àd·° «±@ð\Í3n\GE¹\›` rQ ‡ç{–ð ÙŽ:9#Y£yph<Ë@ËÐfŒ0ÐØ'44~Ùž@'À%ÿÀöP{Pâ4}a€@þ@Ó/€t`ðP°}°æk~ lÀ¦GÙp0Ò±3þ^à“©AÓ(=â.àBì°I¤CþàP@/(¶p ¿` ¶oWönWf¹°eS¶ a¦ ûfìY®“]×P ä@â@ Ðv›Ç;è: {„•->~zo°&kk°¬r0u ŒÆ|‡Ò°‘0 ±àÀ Pg ¨pZà‰%ÂÞG£Wc¤îv0oÐÚÁv ™  ¶–k™0 ŸP ©€ §  ª0 Ÿ°w§Pæ$ ž –€ «ÿÐyÔ ³a?ж£šg‡s•¡Q0ä 7Iü…¾ äÓ¥'¶Ð\¹  «1 é&åS.eõ ¸ ¡À …a Žàˆàk š€ð¨P ² ¢såÀ Í€F^àÏTù~q“žDƒ,¬½êq0Y×±fx™‚?øçF‡IoDøgv¨ àð ØÌëþÀlGܰIí°æî`Ý~ݾãæPæ€0 žpò¡ð “0 p·7Ð$LS—€ò¥°ò¢@òŸ` Ši|G±°à…ñÂgö¨` œ€uœà À/)´­ ® ‘  ¶f ž !? yÿ7 ã85/F{Z>ã<‚ßY˜ ŸÀû‘Àžb¬¡cÓQ03ѵÚ÷¹Ù¡‹þlÁÁË@X&€Á2F'ˆQ ìÂü9{À„ L@AYƒ (L˜@Âgˆ9 … &X Ð@€zq–@B…‘İprY… p6!B…¥ &$ˆà ƒ ˜Ààç n–aÐàƒhÊ6 `à]¼ü P†€bo:`ð@LΊN$€à^1 ð <1Y 0 ÆŸ þiŽ¡Œf ¬ðSŒƒ©Ë LýWÛöÿ¿ì 4° ÷× N0 ÀlÁ„L80öè€ÄXFÆ#lH0!ë2 *HP áeÊÊs—€ÀVK%ð´9…©*§JZAhƒ›(X >æ0hª›ÔzJ½@p $p€¯œQà °"èI/ËÒí.ðg¤ h@ÂÈk€ HÀ’D0ýqé1üÑ«…,ÀÀ,x`€ˆ ²(C;É`ã‚ÎXÈÛn‹À±¢«¶Ç ‘ D,€ðBŒŸœñO‚  NXÆÙ”¯>$`à–ÁÁ *¨àO\`)e^ÿЬBE/1À3À¼Ø ®Õ´/"PÆExÀæD,1"h@€ŠrÙ ‰h@Ce¥Ñ–íxG<æQ{äcû(`Lƒæ' \LLöT€(cèZ€À•B 8ã` x¿Œv€ëén5f šÚ²š¯E€ ðc+Íå™ à'PV CéJ\rÉm³À3†#}„Êpít¸Eš\‘ È& ð´H ÑßþQkÿþcea‘L-u|ð‡2x¨=cælàЗá“fP¤nhʕԩC:ž³}²§6+òfh‹Qå ÐÌS+nÝ“âeF÷31©Fƒù\ à…†D!ÔYˆ&åwNÔ¡nOŒøÐo ¶\h÷´ ç<„¸Ã­d%ÁŠ€ètô>DBÚÈ8§*\Ë0§îÎuCĪ4Ñ8ê0U™fX2 IÀÅ€€T-`ç›óú×@d°[±@Xà!þ`†íp³ŒFhÍ0ùSdm¦h °‰Ê\'î,PdFŸ3ªET@‹˜ KÒ ÿ¼'ÿhe8AbºyÒVN•.l®üé›°@ÀbÐÖ\ƒ ¤Zx`Ó@††™Bµ@ÚÚ€pµœIšGöⵈEÝTP%´9À™üJ_ ;“B œUˆopWšº©,(­ o‡Óר…Š¿ÚLÜã(%Y¥o{˜”1(ÀCT@°Ý<~³¬Âí -2J×% Mª@Øé¡2 'XÀÊPCƒàÆ@©"3ÂE€º uzA•­F÷\#IØS«Q€Ù“ű²h»y±îmóÕ(w†:tF¤ðÎZp2l%šB ´‡:SéÛM³¢%ÿ°I|  DBÍï´D›Š¥¤!ƒ£!&ŽšÂžêùgx°ú«ÂÛ Ъ j ¸‘xèÕq€gèAò6¹¹s"~a†Œ¸ˆ=œÄx©j@røs¨Åv0r(vØE^ìÅ^¼†o@t(‡u0Qƒ±Â‹ð¦ÉÈ6#¢Á¶X.X L‘ ¨hÀ pq!pÝ «¥» ˜–=AÛ¡Ü‹¡A`Ù Ù "X³¸«Añ0:ÐüG=ȃ5ˆ`žNz†¥¹ž(û@m1€Ú‚Hk±¦×‹ÛQ%ÿ@†m(‡tðÅ]ltHsøwv8‡nø†o8‡`Lr¨‹kÐEv ‡n‡âɘó㊥û P¸)g {šÀzy@—ëŸß!Gð‰€ P ø þ1,­9®zÁ¬¢ØFÒ!5*!X:8»à‘eX ‘QŸ:È”Þ  ¦ð-‚–‹Hº¦Â¡šˆ©Š (3'1°’l0‡ŽÜÅt(n`‡•d‡qø†n‡mèŸsè¿s0Ì»€#X‚(€‚-ÈLÍÔ̈q ‡i¹þñ?¼€¡ã-j„eø+ÑÈž0›—ã »ˆSÉ"q!ˆ‹-Î8 èðŒØò‡n³ÿ ‚øˆ(¼FÑ(ƒ9¡‹Ï  ŸðYOsVÒ±°Zº€lƒØ}H{è~˜ŒŒp€àB–³8;ßPð „(€ÈÀ`RÁ!qJ‡ÀäÅm˜¸¨‡Ï¬Åm sp‡p‡ÝEeã$°ÌÜ‚+à'¸, $¨-¸‚%€*ð‡vèHrБø;¼H€Ãi™ºþy9(Û’;Ê`§ÚÀ8p2ñ°À ÞZ„ÈšŠpéS:˜ P«â  0¨à0˜áð4xH(1vÂÈ.~¸‡.½ÿ˜Ÿ}{@9„7h†øAlú ÜâÑJŒRñf(ƒz•ɘ#9»ÚðHv(r0LtØÅo ‡m‡u‡Bði *è[ð‡+Ð%h‚¸Ã€É(‚ à‚!0ètÀ†ohLà™²Z"¾h¥©ˆ([è§ÎªQ²–j#㬒ü€‚» ‚Xñ!°ÿ»hؼ4ð‡~БȇèЇ{|}}àF×{¸ ?@ƒ5„>̰Π¥aY†2`!a‹Ž&/Ð ÙÀ­-QTt¸nØmè†tpoÀÔ‰‚ÿ»˜‚8Ð ð·š©€v(Pwpt Xi&¢c ÿ›•®®6-¯(*Ù˜p9ˆì·âq-/À¥cV"˜X$]!0¨(€0(¼àRñ܇¨¼~~Ы 9•ƒ8ȃ€C(¾Í8‰¬Ëœ©"J¤æÉ´ØÏ]|Iq`Ô€‡txLÒ$P𑪈И ¡˜0ÿ Ÿnp‡] I^ü†®í·B :€eT«½, †\†¨À"¹tҰ…ðìð‡r­‘¸~èØ !©øc R°V00ÿi%‚¨ !Ú¡åˆYƒ7 ƒ;ÐÒ@ƒA° 6˜\èËŸÉÖ7ÈÖ5˜ƒ¼ƒ¢h‹áÀ ¯É´‡p€“ð:»ÀØOtÜð†rPt8†r8m€I‚(¨‹È‚+ÐØ°XÊ\™A/ƒRVo@k¸Œpx‡dx^4ÙÞá’{>á$|šq±mªëkÆXBü­²©x²s€Hð‡Fpeq€Ê€0@Zp@`øY  ½àð‡7Ð Bxƒ7(ºÉœ×5¸ƒ<¸4„lg¿pÑ‘5¨‹8À̃7 ’Íè`e`ÿºùRq1 I²¹åż5p@‡qkðO¸…\Ø^Ð…\Ð…^]ð˜+ è/íŸ5ä]^øã>ä_Ð`À…xØÅn Hp晚)³ ŽA„T8…S@…RHpD…T@LSG0›²!‰@é@I¸÷Øg  Ë¸6°òä=½€xæ˜ðhxƒ9ÀVé]6ˆ6„xƒ:è-„AH>àƒ%&?àƒ=hBhƒ?ÈVhØ8Ø8°1öG;¸9¢eP½Ù®™“ªTE…Ôs(ÌqЋ[Ð…ÿJ¾…@æ…_`è…^Ð^Ø‹Àõư…[ˆ“Ö…]\ˆ\8iP–pØEo°Z€Ñ»X€ÄF ˆð„LKT@S¸Ðh€ è.Pg<¨ƒ:à;@ƒñ‰€ ˆjü ‰3˜F%Hì`ˆ"Ø#fæPV»X>¸B„9 bB@9}b9 :ˆ!BXƒ@„=XƒBèƒ9øƒx€5èÞ?9%hÓß0r›TÈh0m±††f‡pðœÌ…_è\È…\àhÈ…¶…(ä[ø…>nià†ÿŒîd0é[ð«M# !‰ý˜D± 9èƒ5zxƒ8B؃;€6Àƒ*îƒ<àƒA¸ƒ8¸ƒ€ Èéê3X“ b€*BEFxúfp$%»àpf!¨VÚPb>ƒÈƒI¸UR0…£F…S(„7p„7ÿ«‹H¨ ÚžL“C˜íx« Tb!ÿpo¹@e{LÌñ‡Òfn¸ øQ [ðí>öcðß¾…èd\èm"¿\\øèÞ™r¨†rèDÇåExm rÀT8ûl€G®ìjCp:È;øâ8xbÿ=¸ S¼Ø‰fx¹É ˆ…HˆUÈ„ ?êJ8K±È(h•Ä‘ä¹ñ»€JX…UÈ„O0…VFU0êÞ…^dÿ[ „Jà„QÐO°DàLˆô<ÏscÏ„Jƒ8Š{‡dœL;˜ƒWgmÇïUp·@À&ÿ²~œñÈ â)3ÙØ÷Æ!€LP…H ïIˆ{¿÷’i;Y´)úý|ÜlöŠu`p¸wiaKîïm\ ‡aP„LÀôKˆp¤îsˆ…úÆÉ¨ §ôP Ox„TX®°œ ˆ@ø‰M!‰+:P`…QheVþ„LPPPRXËáw‡€Qð„THP°Rù9Ù÷ sšô@_Xú2°ŒHЄ<÷„ …Ž; 3¶H¬ÿê?;õ÷è sY9ˆUsL {gX‡g/yj(šqO¯ò¡†}¤&…G¨QàÁ@dyfˆLÈOXK¸ó…§¯5â­»ˆ…ˆKHt weHpœ‰&"Ïø+hÁ£S§F `v €Áˆ'FŒ%ª“%Q–,E2Á¡™`‘bdþEx l™ƒe þѬbA @˜à'П|VJAè ÊÊ4X°€Aƒ&x™AŒX€ „œ`ð`™S1  –e &Á  ``` `°€€³‚&€D`€Äÿ xö01ÜÎLîzÀÄÃÃNâ”( ˜,,3à …À(—‰ðЙ2ƒ# HÀ¹sD dP@3 @ˆà,Á²<ˆ ¬B…AÔ¬®“@D ÿ@3ÑÔ &, O!€Cð~¸˜ (HAô3<£~Bœ@  L{ 02ìõ€Ê@  ŠÈ¡WÊ8À" §PPpQÐÎPPÁP ÖWAl‰á€¸ù#RDÜ1`ÁtäMàÀ L>ú(ùCÀ10OÎD`’Ëüã@?-ÐÿLË@ƒahTgSØ€3ÿ7äfC* Õ‘-&ðÈÉZœ^Q˜¦ÊÀ2p)¨€HP{ ŠÁÀ‰>@ãœrz.ó@©˜ ÁQÊpÊLú”ÌHpBbpɧŠ"تÓýd›^%ÀA©ù•¨°à(†DVÔ}@J¤S @vpj œT,¸žX_D@Y02ckÒ p€2,E°ŒÎt(AÎLpÂÎH`àË€+ úÚY×w TàÅ2°c†V5êð\‹`8 {éIà°ÂÎ8ÿëta-HêÊ8³YPè=ÐÓ|½J0wÞ&·L`WWAp[DL2›.cÁ$pÀùMr Û€2 8³¯«S€] ”î^Î0 ,£L‘q©T"hwß.x‚KöÚÔãÍB0@héå€2'ø£¥ŒÞM@34x@†@@2¤G9{ZVÁ%Æ©NAf @¨œVÞk³?‰áˆÊvTÖè‡ Ð¬¥€í(³t`–´~T,1?"°›69ƒˆb˜’¸–±gp'Æ^@d9)´UrŸírì¤ì«Bþpœ ìM‰i¡¯› â¦HÏGNÓ±¸²…_!SojCpÇ€ðQk )Èý¤´ËuW'x_ŽPeÈÅ—°@sê÷t²¤u´2¨á-AÀxWqWÔ òˆ$ÉšVÛª©µˆ¨ùmâJs…Äð€¼Ôz«ð€hèz/¸«+v àì`ÎNÈ‚GÜæ$DÜÌGÄe€,IÞ3 ÿÒ£‰xMÂÆ\"…®uUBȺð”Ä=¹-ã N8ê}ó»ßöúú#!`ÚN Ë•A€e ps—fAÇk$®¥J¥af° €¨±Žô l¦¿å*…ÊCWú‘O ¼¸©&ƒÉ ™ÑXïJÚÁ—YÚXú…µÖ«ß§ý-ô¡½èÿ¶ÐéVN!µñBZâÏ?‚p!†»5[Ú"—°®™ryÿ¾–‘ïà¥%TÆÄÀ’4Þ«ŽºÑã.÷¹Óè[Ò7MšøÇ&P†RSyºÿ*ÐŒ÷º3¾ñŽ<ä#ßÙú€¶žœÜùÿmâäÛ(Ó)’Ä ×qp=Õ>³&çôˆÓ‰G°š‡,y¡C;ù=óìŸx×ϱŒÛ Üÿf€þ'ÀfCšCÞ'€@%o‰%Luuêö4;Ukzèj `îC}q³Îxò”–~~[`Êy\ ôÏ@€Žw)HðíÕ ÷©Ùt†tEØA8V‰øƒGíÝËpÜ®ID• ÑÅÍÅâåç @#œ\ĀѸÅœÄúñ[<ƒ4Ÿý°Ép¹ŠR!€ûÅØN,CA$ %n- @„^pGŽAjIE ­Týá—\€^äß¾µ‰®ÿ PUäy‹AøYÀEÉ£…à›<À‚ššRp¸4Ã}ÌÎ}8…¿ô^S$n¹†@—mF“T“?È`µÈN¨ e0nMi±¡apË 9Å⚂ūYàKäŸ?0‚ lÑ™1 yä·qYA¨ßã\ 5¡uˆTDÕ‘$Àý @qˆ(Ãh[ý$aMFl†GØÞGäj ÀŸ D:¹Ê ¤“ À×U“ŒýÆ ôÈjÔ˜Axâ}F8^íÄ 4w<ÎQŒ’!e ¼á‘Ä™3’`üáŸ/ýC/£œ4a´©!@38VD)òÆ¿üËãüÿˆTüƒ[´cÍZA €EòÕ~AÏÓÐÄ„ É8@$B:ƒ3à9™KáúƒÚA˜Õ#Ö›ÔØÄÆŠ˜EsxŒÚØh<„»ÌsTÑ1ýŽ]ȦµäÁ­IòÍÍ}€–Ì1Bi9ƒíðÓ…­Ùì4™¾à…ŠDØ[NüFS|Ú¿(@(ÕjÀIMDD!þCÁÀ]Nteu¬E¬Ý ͤŒ‹OäPïDŠÀ“P<Ð8C0ß¼[8’ HhÝ3ÜEŠ.ߨL2˜iÄ„?(C  …·SpÅÆð(—,KÜ‘lèKŠqˆº šüTÿ9Éch±D“4À B€ß¦I„à@À ®É?ŠåÉ :ã8ÊÖà­‰œD‡òÀ_-ÈÐȱÔÎhLH@3”E¨èF"ê, Íõ¤WF@#p0ƒ*@>v܉À €¡ÍÆšÎ3ºh›¨‰ÜO„V÷ÑcpL£8!AÄÆcýÖ§]¤AâM¼QMœ‘pDd/i¢½ÐDÀ‰v\äo’Áæøƒ<Ë[`kñÍL PXe4­ÌBÀñ OãÝÄÑ\3ÔJ<Þòü‹‰3|Ä“@`j‘l ]Xå ÏÿTÐ8«ÙE$°Äûà‘…þÿ‹cÑÄLlÉL¬^iå ô¦&J;U‡ðœÀš¨ juÔhNJ„£køˆ\l#øÌš-ÃQLÍOÉG8ÖW`È)ͯ0CýÔÐÉ`”ÿ L\ÉÆ‰ö¢Øýúµ^ÕaLLPœ0Î „F¡Ò˜ @¸Ë4EQÉÓ8¢ûØÑ"Ø… F©×¹PºøƒT‡DËñÇ_l[Ö8ê™'ZMÉedŒ¡ n|F¤\Z}DÀbÌ ‡œR1ˆšm›åL aštÇ“•Љ]p@ôa^¾,@/VH• ò—]Æ^`Í_¼Ð jÕÄü3üÃUƆ)Å£/DÁ´Qs$ÀlÿÊþ!P|£k˜èÿ˜£>ŠD”Ò„Að]øý««¬^“ #iÈBšå¾=Nl(#€MPx[x ’(Ê)½iJxšË5ƒ2˜À í¥‚Æ¥E@ Qô™¢x´DC´ØÏ ñÍ EÄu)CŽ-ÉGˆý<#ãÄØ(°FDÀQžlà+H]ŸD àeþˆ¾AM+YO½ÔDŽ)ÍûÏšäKƒ:ƒÖÄPÍSøRK"A¨1QD/¶AT ]ºÍOXÀ"è•°A€¼DY¨„<ÀðÈ]Š‚8‹™¼r^îÌ<±W½EgŒÝÉ?¥ÆÍ?D$‰Á“9À30À}„ÄOñx Aƒ³y„Ùð#,¦ RÍ‚ÁÀ{ìïÜȾÒM½±„˜IDЭ™õ]å.ôNUÚBXü¡NõHüu#@:…TlPðVPÜÅÝÅ/ZÌSÍS–K¸¨½>ÖÞv–K<ß}0›o²dÒPÙþ:ÿTÉa)é#FóE“ÜÏ ”óA¯q|ZoƸ֙é[ïE$´Æ,ôˆej©ÔŒy¢ÁK`„ßr@x?lÚòÕ߬pހĸ„‹’T¯ÇØÁµÁ=@³r‰a]—2š’l¯M i‰e6ÌÎ!.@›@„½Y•H)®±œÔ™¡˜ÀžÃýÃæ2À©h #q àŽ ô#K9 Ã7ÀCµ»ƒ6Ôhƒ8|C:X{7øƒ 4 ¹ŸD—8P‹„?(P‚ÿüsèPÃb 4p F`F `°1D*à`€€Dƒð_ˇ38øWÀ‹Ζ!@@ €Àþ€*ƒÑ¥K™V ÐŽ©SóÄѨ1€³L,‹ àÿACŠ˜åŒðàŸ Ë(–y°ìA±3Êõ— ;½{õ†c÷­·sÜ£ g®Ûà¿ÅIò8%‡ŒyõZ»zÙ߀  ,p°ñ­À œý{  -e3-@¨{b—epB  X(``‚ÒuÆ@µ3ebX ëPLé@Pà[´Ù4 ØLÉ+ýè/êT©s2õçl¨?“4Ðé9e¼UC ÈYh*ÒMLøÇ¹™ àqÖÑËœpÜÑ«œqþòÆm°qÇ–öâag›ŒŽ8B‰,ŽÀb‹SÌb Rð‡vTÀ€ÀíªTòÇÿ 1Hj@ "pÆÕV{ ³à„…þÁŠ#‘vlÄ„d`àN0À€ÊI‡Z ³ ˜þ1àXf¨LxS£P(‚(Y€ ÎK¯@èè#ø¹L714c€Qec5PMŒ  O *…€1šl'#0aB¾Ü tЧ›pÀÙ&vçæ1‡2o°b≤øAÅ`S´b Ò©Ç(š§0[f€"É(àm™†ž³`'X¤×žlO ÔlÎÒ$R€Ș€xF  ÐöŸ XA…bH!× pÁÿ„æ†98`gë„ñKÓ9ô€ƒƒ~úa„~øé瞌Þ@CßþsFÈe ã,pI 8î䬰p \q«YÇ/vÚ‡u¸æTqÊA§tþê†]ëÆd2bŠ—èA «P « ±Šºa‡œÍ àÎË”‘l7ji6XFܺît(– ðr$ €gp†£bŠ (@‚; €Û‡@…ÎmHAóeœµQ b !d¸MŽÞHŸ"EhH¢þ‰!ã/BΓ€~Fا€CúÙçAÞ Ä—xàgNHó€9W ÿ‚†F¡ –yþg ¢1ó¾ö‚Ç=À‘êjºi5õ×w§±'€Èá -€@ &HÂ)ªBJˆ‚t0…øcßpG:2b¸ÕËññÇ" %b`€¨ ÷À!hCð´€$`%q–@,…@"¸˜@,P 4bL3y €np"ÁH¡?F²¤€÷(@>v~àCühâ@6~ ËGÐ0ˆ>ô!°€Ä Œ‚9ce‹œá¶ 8‹Ãc&e€ÔPjâkÕ6¸AŽp ÃÝHÇÓÀÑk£Þÿ‡6üƒ)\a þ˜‚–à¤À6`„4€, à <‚?$éw3W92r†'X f–z@\T0›8„b\`Ïh¤@ p bFYa‚ˆ?Z@Áp ‚@Lp8ì7"8 4¬ hð‡÷¡vÞ#ü€>ð1<}lÄVÜ¢Üó9‚||¸4ÝRÈ–2Ô´à&P€Šã@ÀôYH ù2p £Õ aÎñªòá):HAp°‘(øƒüSÀäÊSžf¬ÃíàF:®¡ \E™;O,`eÔD‹hÿ’þj9Õ 8RXhÔœtôUEÃÔ\Å%à LÈ@4@ T°m‚¤WA> À›}ŒúèGì}èõÊX§ò!‚}¼¨=)Їº(’af !„ÿ!ø6#èIq Ä €K p†O|"/h¯J;Âñ mpÃæ «ºqR¤ X¥?ŽƒU ˜œS-¯ò@ îm2Ê7Tp@šÁSF–§Jø hNT€,``ÑÍ'õQ2ÖúãüЇõ>21$ÍÆ1LW C~Õ`$¨A dà"Ö@a’ш@‚|Üóʭͳâ°<äA=xH@Ä@åZÀR[Àš‡3g°dŸøÐq ƒÃé0‡6ÑÓˤôDP‰p?¹'^þJ@‚efØðË;ÞŽntW#Îè®`ÿ5 6Έ™àX‚„é(1B °jß91 ëG|×|ò®# ”#3,Ë¢í1ÁNpƒ¸@$p f`:袂ýñ:9b&ÃŒX;8på³ ïðæ4€–å[*Á*0¢™*¡c¸6lXæX‡9Ä1uÀR#DðÁŠ+éžUF 8ŽR&0Q (%fþHF:Ò1dHh/ßÀ5HR ý0%PvÀ $,@Ò€KzwN þz,óÀ®¬TÊŒFÀÞ#°@ä 0nrfÆ$è½ÛCˆ8¨S hpÝU ü‡4ÿâ26‰@Z×ub€– ðè„#¥ðÈÔÈГøœ`ˆ_rld€óî0Ç1ÚañÆ@!“Gøý€®òE¨é€3 -ÈF6À‘ Êè%Ïù©fhhÙF\GM¸´õêñƒø¨Wt®r„*eЊ†îŒG@$«kÁ päp %D º´”ÀÍ&HÁPüÞ€ûcüÎ!ð‡5ľ{€»?˜ì¾Ö€OòÍ<ã40ÆL lGüafÖdf&/Â!#T‚ÌaÆÀa6bœlãÜC3:$ DMVŽ<(`sÿü!\!ráˆá’!ÞéöŒ^~è Ðd†¢Š0.Ò"_|èÊ€>@öF”ì@È@‚Ñ KºÄÆÊ6l$ÊÉÖª”)<`l@È@„` GïÀã ŠÇÄ*îðdÚÏ="¡% @#Ò` æp¬b:&ËŽà3B  œ#”#hAK/²ÁœÖ@ óê!“laz€¡nÁ–À Nd¥^D)ÇY``F~A:D„ÁBô"DdI "òÜ(g,+B"á[,aÐB%pH#€PZQ¬äÑ¢ÿvb#øH^ðãŠCÕ”ÁpÖꇎH|È„àNÀnÀlÎ8@¸„`GáŸì Ö€ ß‚bÞÐê àÀþ`aðŽÿ"Oè€õР‚¼À  fƱ&è„~¨&‚jà ¤pEVÅt¡~%iñxAP²C˜&ýAÐIÈÀ=n!`na%Wx!¸B‚dGJZ)3ÂbÁbÁDaLáNA¸À€" f«€) RÔâ–â˜C†(à«‚'$ÈÄV‚ìf~‘<€:bÊêòðä@ÐéÿþÀ6¡ê` @ èÀ6ÖÀ!Á Pa§³úqû 0ç øÏ*Rz£gpÄ*h—Î"À(‘ÒØöðvÁváh±[²&“@ R@¥xXK7"%}¡|°âKÞƒ#ð„;¨ëN,ÁÁ0ÁPÁ:Q!$ L@Ö$Ò€†‚è€Ö@Цôc”a æD°4âZ2„,–Â`€À@¸/(ˆ H`#î Þ Ö€î€R`ÝnèÀ0ɨ Ö@±ö` æ á ’ Aö!Ó‰Œ^'Y"ÝÈzƒ>ÿü#\øf!‚-€5K뺡bxÁ6}!L2}!zzÁL±Hk³s:$#x!4‚h ¬‹ $k7N‚(ଫz0H &!>*Áà@ ªƒÊgå è`ö€<åÏf$&à ð@@–ÁTM%ì/Ãf `½ðK#Tbö ì€æàñ  6Á8!8!ÜôHô¤Ð N÷€ û@2ÿ@ð4O)`Hmf `-¥VH"Tâ>’Îá´AWjóp6cQGkÑ'ý¡$cql“ÿ~Á:Äná~³^¬ZZ·š¢"œ6 `2ÂЀXÎ2Õ6Ò€áþú Rãø€ìU_ †‚˜¡Žbf#;~ÈYÄ ˜Aƒ†¨¾ pcHàÀZ`d€KaàÂ‚â ø`/ó€>!T¡.A!LAwC!LwS8€¦`ƒÃ?œA( 3 l+`†TSL€9&kÑ´X È4 x¿ªY§E‚BGuáH¡Ö6iÑoÁ$sÏàà¡„/Î! ÁLÀ€  €82b5î2ÕŒ'ä`òàôyâÎ*ðT 5Ä2ü¡S1Áu±Ó"¡;‚£þ”(š ÿ)`Ê(@ ¬â*.áS+TÁRÁN¡uC¡>ø:y¸\Hð4«ì'&òj„)X°Q$k`Ëà%U/´¦^`U)â3l¡$EñikóàZ§6}—5¶5Ï®¡vëÆàáiÊ!È€†ÒA€1.΃öO#§ñ ‚ý æX†â "@ "ñèÀ2Âug÷¤ó~@³øOØn–ŽVýð*Tძ’"•]W:–a:áH!N*!(¼Ølƒ¹¦ àa/ÊNKWºX¹î2޵%{ÿÀNtnQšu´&#ÁÚƒFv2A^!Ê¡\éÊA²áÌárNJE/Üp='€¹°Ò @Qæþâ€UM—þ!ì€ ÀV¸|`—Q¡*OÁ:MÁ"ÁX*`À'œá…ð×ø„¼ …ùl-@vä #¯º1`yQš¦Íp¡ö"ÊAÄ2¢,Kx¥óLtza®–qáXOÒs!Y§å, .a¦%4A(x&D´Ö&iàÓ¬Á-`õO€Sÿb‰cxÒßÖ å.à@P¸ œÁß6šÿ$ì–|M;kÚ°±·fB ˆÁ•8ê‚–¡x`[es˜4G†:j9˜tajÁ8!N¡2¢£S:™FÛRÁuI…!@£™bvÎð‘TßrDß× þæ@þü!¤Û¸›)–ALà‰'e£¡¶‘{)fBñü!hN/Ü㥙â” %³yƒrš! Ö6 ¡T!$.a4á‡Ø>a¡˜ ¥+€.@h›åv„7ðoÁîàÁù€èq ú±,ÏV¡a`Á›^¹Yï&¡*aF¡PÊ{8ÿ4ù0·6!@Áª3£[…q‰OZ)èb;(,9—"B raH¡&Ók#>ØTárKv#"á ¡.`…išàb'8á´|?˜â#à `r* &s b,ÆM]Áˆ½ü:SA\,ûârºÔËA nK) Óö‚£¤fÔa¨ÀÔAÕ9¡Ë_w—¦»<&@ ºäX¨ô>xÁÉGA ;Í GªPO*^¨l¦5`,€¿)€p ÓÝHR´ý¯# Õüfã¿ýõpV²ˆï£AúŸÀˆpàÁƒþH1¢D„(ôW¡€ƒ8Ðà*ð/¥Ê$.€æÿ<° l€ ,` €gˆ)ãL  $@ Æ_4h ìL`Ñ™Àbš%ð·À@”%˜€À”5PPóÀ ,[†ÀŸ N<¨ ÌB – =°Ì€þ”1XVA,°ÀȰ0œ˜0@œÕmÀÀ¬?¥#$Øi ÀA'"h :ᚉq6 À]ÂÂŒà¥A„eË”d xñÉ•*É&TöO¹˜ &ôm0!C„Eœ8°øe L0Ñ < ‹@H ~Ùƒ(H0O ¬W{(cžIPÖ€ @|x§TÿóÕAe<èÌ 0áIó PÁ„ |¸ TÀ"t0³ž ÄAŸå6€BÑ$âI#§Œ€T@n !p#A ÅB0ðOpAðü£×Ibœô@ﴀ D%3ÿ$ DÀ@#,€œ…}0ÁœÞYT Á^(PËœà&P`@L  Æ–x1 xa^±Þƒ XÁà[00¨#-êŠ'T-ã…™P †ˆˆ8kÅÚ#°m- `k€Eøc¶P Aà@÷ÕI×WÊDÐÛÍXà.Ðàæð¯žÒà-À¦dr¡·Å`è‰ aîã¨\‰¡Dë!•B·"Œ-£ ЩÌXå(¨‡ØdØ¥¥Ð%kê›ÊêÔ7ôˆh@ À uZqdRb–a‚1iED …¡WLtç <+p‹: ôŽ~ŠÍ™àe€.%`NØ–` —s}¥$Ü›ÏK„éÏ]Ë–? ˜”X g&ÿøà 8CA¢¶džEX` ã2ަ€e½€@;œ´G[šSjJu® @qFÝ ¿”¬ €j@•“À†L#ýå@ ÃdÀd Ø/ðBÐ òš(xA#^Ä»ïQy#ºT˜F<qД´B›(c8ˆý p‚˜D0bPÎ lñ€eˆ!À|¶y¸+J ¨ ÄÄùh<ÀÎ €P³ œ%Ë“À«èÓ€&bÓ–E·,ôë)Å@:Io.‹Y\4ø‘M ¢+šˆ¶ÙÁ´bxÀ© ú¨uGsÊX†óÿF˜3b h@‘°Œ¹EäLaùˆ@‚c£ ÀÀKÞøð­ äU £Ìø†ÐÒ/‰€š¢»Ç< þ •8@€ ) &ÚqÀ8£ ! äJÙ±3"€D.l R†LÉ.tŽúâ§èD³,Ãÿd¢Hµ<]yÈ/:ѪiÑ“LìU XB“‹"®N{̦cRƒF”!Œñ‡ƒP¤„üÆÎXFœ±úÒQ™baz$+5 8¶'›ˆÈ•{PÆ1I³™Š(Ì?ܦ—('oxÛ8cФ†47JÀ‹˜Zòª8L[ê¼ð¯ÿ“4di)zÀ?éD*."EÉ ¬Îð*¼H ?DK¬ÊB§½Üì («³DM€s (ƒ<ßd? n¸1@¢Ñ6‹ÔfÏâ^Z@†>®ÆeYÐö b‘ÇèNþ¨¦2äzÄð7HhVböyåÜ–Љ85‡¿éŽ3<åètÿe-›ðsnnFeÚ‘d_jŤsÍkAh²¢3¨‚ÊJ2wšoe™X @J'1ÁƒP€œ`E'Á‰[iÑåŇ9/Á]¥ø‹g€,XÂ]À'aË‘1€ÐàY˜˜®ü³Îu§&^p‹”áÜEÿJžh“œ;JœqE)| ƒWljÅðPÇr€3îXÙ„1"‰ ¥(ð±€ñËøù¡ÔÀI X„{‹&˜¢QfBˆ°¬;¨“8`tÈ:ÉrÌælQþ1t]‰IÛ>“‘Äž øêá$+pi^Mgœ€›I@¸ËàË FC‰xjñGLœêW‰Á<¶1L¬§ûèØ]Êð sT5ÑÜ:/c{ÐHQ›Lh{U>UB¿éU`Š’%Ô±E$ª5Û'`oZ%¯QMÖ¨šÊ¶ˆ,PÌÑýkTMÎ"F•"L*#þH@º¢`rEãŽÜÝÈCñNñPÿ wvꌈ-Àµ©b¤üaì x);€€ ¸Î¾28UdÐùê? Wg°º#Å·?ø–„Eèt$0£LLda4I3"@§@ 8/ZßãfÑ~QÆæÈ€–|ë…FÇGDGsŒÍ{´Fš·w`W½Ð"i“͉MTIo ±œÉèÛÓ‘, ïhÅ{F7gæï¸šª‘¢‹ö3Õg½ ~_m.Ü‹£ã9twíW_QŠaw;ˆ °Õ9@ hgÛØT4¿)À»˜DYu2já®ÿyÁvËq¶Œ?SŸÇ¤5‰båp:A£ÿL1!‘À'!F±6ÃdS7ÿÀ§6&Χ룽ΰ<ÛAPFÓWœ'Ñð*Ûs“3Ò& yQYçx‹P~˜dQ:èlÈ:àIÅ1¿/“I¨}:Ek1  d !m”]AÀ«1PÅÂ8Tñò b*ëSc)a/nG%S.4‘Óc-îµx 3@u‘37r7Y\ £e™doJèlGkäYx«ƒˆI8h´>Õ–V¢9£rb¢±=ATnEg=z¡ e á[@ccÊ1èvÎå9±>ÓÕ;]$ÿ"Ëà ˜È^ÉÂ'±=Ó& ‡0ªÄN‰‘¨‡ARbx)YãpvŒE£D A(<˜m|çPEqY‡ 0E¸è ¼q|QQv’C%Ç}1±F±HV¨Ö:Í‚¬Ô6#+s³Såg[þ•$1n#DÁÁ÷á_9¢á_p[Vñ ±¡¥C :â_ÜõÃ1'Ba "(Ì3pIe‘Y~ÁQÎ`frÐÐhYƒ0GxÔ‚nÇMë³F`’¹e$ôs’A)”CI”Ei”GÁÜõ,Ó6Š•Nµp;†;)SB 5H©•[É•]©•´tÿS±AÞhý•.qX³j¯•}ÄCB”‘^I—ui—Aé“[: Šr|T3*N¥s¦òplùU÷Õs@Kd’w)™“™”EyAÔLâÀ2‘ss_±4e¸R±JÇacSåŽàÒˆ?ƒq’Ð Á 7”{FÖ"-A=ŒI™B Ž ±CQ?_Á²±Ð! Гms=²AaŒW[ÀaQ~?!:k%ô> ·Ž®É–TElOÕà÷J¡;Y@.p[¤¥Ts3DñañömK¡{ÊTƒ$&IõV‘fp=gÿœy9”q-Ñ:Àa-zÐáö²AbÙGFg-åFŽ©r³ž7Tüõ³8•æé¢/JNRB éÿ4ÎSUÍõ£b^1›= bA pàŽSpdJZÁQ`@ƒZg¡ ±1EÀÓNÎÐRâ”üS´–©AŒ À'ÿ>Y4Qñ·:Œ'wÐ0VpÇ·-ñ‘À¨R²>oHWbªX$žížå™¨Õj­æéÌPBÈ Æ"2t4§ï“7b`[ )ÛWNb.Kap àv: pxŠ‚Uܤô úQRãã ?÷ r CM« òãyÓ[‘vG1:g2~±8“kת±˱;Öã23넬"k3C¥ÊANØ©` $6r»ñþÐ[ºç‹H¥Õ¦_‘dk=#x_Ó¦9ÐAp¤áPÀá%™‹!ü’™5ë(•äôj1ñNÛ±Y«µ[ }jȵ\p2‚±^ÐQàoÓ ï´ªd²ÚÔ —A1Â1' ˰ÇU† ³¨ð ¥é8oèW2‹[k¸‡‹¸‡ÊcÌðáfÜÈOPµöCVb•pBŽ¡Ì!9Ó–"sx'0*0¬pTtp|ᙸ«Ëº­Ë±ü„à%½å @u΀ª-õNõ%ùTYZ1ÀÌ0ž¸sp®«¼Ë˼‡Këó¼Í+½ÓK½;ntpsec-1.1.0+dfsg1/docs/pic/oncore_utplusbig.gif0000644000175000017500000002360513252364117021410 0ustar rlaagerrlaagerGIF89aÒ|Õÿ'LI.G6t“nˆrO”n£„\ÑØÎµM# .(º¦â—XÌɵ’¹­V¤”²ÒÊTEqkNWK-ɹ¥JTMSkh$3.ïïì9xUisc¶¸£×çÞ_0 2‰n° -†woob{~žª””ƒ”ƽ!l¥~gSF­ ˜$G:,ÿÿÿ!)cB?8÷÷÷1kR5_BJsRkZ!sZ)cRççÞ{nG„aáÞÖ”{„9=jkRc!B{ÿÿÿ!ÿ ADOBE:IR1.0Þí!ù?,Ò|ÿÀ•pH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°xL.›Ïè´øµHh9µ|Nçæ4‰‚>u‚ƒ„G z#!-! …—˜g/#y‰ y2-4- –™®¯Y"6C ‹!&&§1-8&«°ÆÇM ¢–/ !) 8§8Â&ÈäÈ8~#:B/6 !x1 ¾10˜XЪœABÜ #Äu\xAñÅ $ˆ€!€9îˆé_ªUªÔdb!.ÀÈ b¢EŠHȉ₠ÿ D8°A!…#p†a˜†/VJã·\ ˆi`Å›+¸[ ÁÅ œÃ> Ä ãj*•¢NÝ‹…U@`kAÄ ‹9 ØÈqaŠZmÐÁ€0`Ì¡ó 0ˆ¦AƒÞ”|S71`õ› a€X´!Ï Û#nG^ s‚…lëâ€1ᇠ# ø¦ÛR¤ªX©žŽä ØÀÀ@j‚%Ÿ6.h@1Þ€Ú ð–Ðy€BNÓ(Âê€ %,äÏ:Ã¥5ÀÀ¨Q·× `÷€v1À+Ø áW㤛E¼µA>:$ÿ•MòÈ2Àˆm!PBn@ üWCœ¦—RyÀ.È%ƒ Œ@QF Ø Á4бb‘zfP$`ÑWÀØ‘ÀЊnEA%¬Ø` Ü#J4äv ð .Èv ©¥—áNA_ù‡b_Yt_>ùL0\œXB  ð€f•i k ¶ðwtÑVÑ æMt˜M9ð–€N @àB}^°A‘áõIOs&.êB\¾=`hgô"ƒL8ЂiÎhé -Ô„ò+„EFÞù¹F `t~5O”ÊÚG`T`®¹ÍfuÿÙ½šg—PC 3Ìp 8°±ÇªáÁÔ€ÀÀ°8Âa]DÞNF¦wmN Œ`Á RZ´ÁbŒõiÀ €”{n (ÉŽŠ0 pé·m9g¯i•ô›0â ÕV[ "EÓȃ £™ž¹uj”¶:B÷!`œ[A #ø†Ÿþ¹@ÛÆëÛ¶–µôÞ `Í6‹áèÌ, ¸C Ñ^0tHͶž9H~†º!‚Dƪr`Ç ¦ ° ¹¡%P0Џ`‚&R Õ-zâ¶¢‰Vï ¬ÂoÛX$ÔÁ 3ÀH€ÿpÞ“aÙœsæ¤Àû4ÓˆðF¶ ùÀ^ÿ D¿hÝ8tW!t<€Gúå#òô0—”®Îz/4ÐÁ4<à 8X€€uÖiäí Þû'²ÐÞQ" Yˆ÷´˜`bsÁ†­P Óctd/"-;Ñ FÀ"°À\ùÁhšó†x`@盂àÖ¾Ò-¶ˆÖt€ÈLÄn’þ€ƒœmÉÁb&3ž`8Ag˜ƒ­ lùÑOXp(˜a¯cÜÖ¢´æµ×À€K‹ÒÙZ„™–/ãHáЀÜ`„8x‰š‚¢ðÿ$g‘yÚ`8Dý†D äÀÒ˜DQÁ©€#b@¹)– û9”縷(œ " ÀÀPÁ‚¸d-Ê‘V^³"ÑÔ@lSc ÐÆ¸O4þi`0‚7H ,·k YN5@°%’ –Ù^æ†ÛºO8À‚T¯1˜“É€DÑí§n\ú[2ðBEÛÁ@®“.Ã}8I¥dyZÒ@všBÀL0L#CpˆNæ¤õhÀè! €¦\…›€À[_`€è—\ ‚d€Ym’梮¶¨¨ ^ÒJs¡b;ÿéTgsÀ,`%&Î SêÒØ6À«l§vBð›. JŠYŒ„"vª!ý;sY÷…Ô\%üZcL` l+)¸•è Q#푞ðRH‘kê”i\Ñ¾š¾†¥¨Á L@žNG@ðuƒUÊ‘xB @¤†g9p€X8‘‘Ô01hÖtÒ‚“åˆKÕDÀÓZCÚmp~È €€C(èòó–ËÀ'ª@LçŠëœB¦ÍigLƘÓWžF%IÉ<ÂÜ °³‹-"€G 8 J9 PbÈr¸ÖLJ§Z N0Õ¨¨ˆÿ‹*)´¦)MÑÅÓdPÀ&}¢ˆl<G™*âz%d¥9q­v¬t%÷Ñ´,ÚÊh`Y L\àù9|‚˜q/.ê}À ËÔ¾ü¥d¢$ÙÃÍ) ÂNZƒØmeTdî}î ¤âF`*ðä‘'°€úU+PÐHo`Úi®Íµªõˆ¦ 4)fêL"'9 ÷RÝ+„„ï`b„â¡`6Ö’á`:ð LF1@VJ€Ö@ÜP€í(06¶@(Z@ôЬAŽ2:HÛT4kq € P0üV+*áVÿÆ[0ð@¦èHÈnΕZ@‰-y8£&*óÉ$<>ùµ¢òÊ!;ÌCÁ q!`@Ä„Dª”.¶.—‰À\v ƒAE„`k7ƒ2ÄÒƒÁ\Jxä7¿Ž<Ñɶ¥|˜Z¦æAs†c€#PAäPq™`/`7Ì®Æ\š’Yr‘«ª û*„É#Z çB±k9ÈÀ mH §äØ'ŽY1‘Rlؼ'µÀ”¹ƒ‚À Ð@Ò á, 9 ¿:( ˆl’‘˜ëƒRáx@à;FðÿÀi‡ˆ§HfÉû黪q3»R8Á.R† p ( ŒãƒŽÃŒÞ;A`¼’Cª£`@RÏÍ)©;¡‡Ã¥¦•? 8RSÑÀ¢8]´Ü޶Ò@yˆû ’.R ¬f\xœS >àÁ¢¶â\Ðd°oŽÖcà (>[ÙŸ.k­L¾ÌSŒ¹ÝN Š $ uwÁôtüÉ MýîÈt‹¡ªÇ–a˜`(‰Q¨6P@þTÒxÙ”"fÍblÒ§NDi7þô[µìÖsÌr Ç*°§÷ý•4”#µ$Ì ”oÿ<à&²Žb" k$¤y#ƒu#e5*#jÇ,™Õýöp(ÅcÃÅqâ0/py==@4ˆ•ñ20Cc;?! 1%!µx·U+Ì‚LšBs¬…m3^SeS8òJ Ôt}óþv'°wwaˆzq—úÑ%³Æ%€J'’nHR,0` 6;PÍ¡yRd5WSM'¢! J—ÁN³v%Y<3K3* 8p\_0@"€‡gz0=ð2'itA V!Ëô’Å àbëGí´"Z*°A8¢/A2‘uȉ!ð?'ð1 {¨%gÿ®Vt IUf\S)@R^Ç, F`Û iÂO˧‡çb(ˆÕ`KÑ"8SW¢ÞX;Egc1×ð7OZ°‘h<# ”d(‚§Ÿ´-9bh²Líex8àOŠQ¯b=S†8 HŠTUcÄP+£@4õ¡A’£pÈ$ŽEw àtKRJq(Z‘@<`"–´"êv+Òv8S0óAO7÷«¶"ò¥3ˆ;Ft‹b€\}Ai.@ ¡/°@Á „èRc­¦Ì"#°~øè^FÖA Cè€dðÒ>°y[ÿ³£¿2;lå ¹U;©h@|Þ¸!xÇcp€'àt0k‹B|‘C6àÀ‹bf¥€¦*¦à¯2y.ä_°jX!kYK1^Ú¦"‹RÞ(9#Å$Ÿa[àÁ "òa*•¢ãã1€6Ä%72"Ãá˶'Uðõ5Pp``IÆVýÜ”[«Ö<1Pr&ŽCš ­Ævù0V\b"0 /‹žÍ‘1…±¦‘kY{梜P.>,bU ÈÔ"<Æ$0³‚ÕÄcw÷èuÒ:vMI6w'Ž¢ÿ91ôDp[‘ÐB4 ¬5F DzBd1_ÿàl·‚MÞ )Z‘[&,ò54•Y“t2â8^¡Æ‡â§H°·$@ŠÈ‡|¢³„&ñ¤p 4`%Á0J 6SjâÑ×%úvSæÞ¨išv‹*p[]4ŸõÆ ÉA Và 0#šŠf“'•ˆ#Q8 `U4Ð1í 0[ùØ)½ÐdÏÀË‘!P:l…>B>6F ò IAc@|Ͳ!y"9Z´‘“(C˜š„Þ|h1v§ )à5é)kVBf5u2V…ÀŸq¡)  iW‚k ðÿWD39—äUaOÀp3ðšdCt'u†ù‘Y@Cò(:!ËvgÒÄdr–:94gQ—4ðG‡- #B2ð‡IâvHõ°32*§r‹ÌÒ¦+20/‘‡²d êM(‚7ù[š²8‰ë¤YfŸ…¦{wü9«3 ÷ô»:Ÿ¶3wP:@¦SàÀB*cl‹›Ó#p‹ØXcð‡.@¹/ˆ @!HÛS.€J¼dûR*â‘u,%}¢á5{²P3ê(¼„²—õ0§–ASVª#õ™Ü°`¿Å ©ªeÿϸ ÔØƒ ËŸ%ÀŸ4'#Fv P·Zn#0P'ÄÚõ¤>cDv²]Fü9©hA¡áé2ñ‚! Gc…[ ä2¯ðsxOñƒú2-bUí s 5m"% p[Öƒ&@TJšª¼]Ûo0™õƒrk-&«z‡’©‡¦Jú><¶ŸVU·Ðv$u.¢ÕTð >óö Y< ¡PE ›`f[÷„€±‚ç5ÀQÀg5.ê››£¡ð’x5@Sûø?a·, íè4 8B!·ååoÿ ‡0‰·ŠkŠ©˜ö»Ðh‡§ .Pzb5, pLR;€†.0qýfu+™e9(>R8³>?ç^V¢'§§@ 5IWR71Ð<ã5€¾9 „#â° EÓãèÐ^÷¤Š¡™A¨9rJÁp%_Ô+YA¡v5Þ4ÁÔˆs~ÖŒ¨ô[ æ¥óžÜ4Á·$Ø%û440/¬Ç'*T`²0R0"!€ÛYÉ•œŸDczÀ ùWUk˜œ#pd(‹GÓ£ú‘ù¡Êò³-k8;æxLÂ0  –&ÿ:¹ ð3p‹tƒ¢>æµ1ÉÆ“¡"¿uÁ¨`ª¨`‡ð”¸RM{Ìv’›Y¡DqÑ>† …Í¡eF P`½3IÜ€–üΉ sw¹%\c”#Ò5ƒFU¿Â<.S) {d3Åø@ZóÒºÀ„”Ê€¥ã¾8B—;€m4æ(Îêµò{d<[2`¦ÐŒÌ h2fÈÄŸ1ÇŒtN ·ˆ3À™…Ò à q1g:ðkO°&7gÓ¢à=¼ÏÛ©-TTÁ^30†’Xþas!J´r†RŒu™šy¨"Ô¸‘Ål>–ctœÿÜq5€w4¥Ñ= ‡»‡Œ"˜ [ ¶µ;Po¡UJøáo!ÛšÓ²@“’3e¦ƒål~Á§‰%Bs1Ô–\Ôï,†5»P# 4E6–£R_D|+º¬Z45Tƒ¢£E¬,ÁRÆé^ÐKð¨Üs¡ÃpN%1{«Ö¡z5#ã‘ͱ˜¨>Hzª`;6(Bšn!o4Y±Z1cß@·Îâìo¦Óæó0"À+uƒiÔð|bh;b"×@ ê ÑZô~Aë'Ð.³¬!@˜Ç·-ª,ۇÕgƒ•šÁ°lÒN% b7=ªÖÏÅŠ"jµº´ÿÆwÈ € SöAH‘W£H.õp¹!íS·7м˷k6õÓË.pBýfãÞ ‘-Þà‡u`žÑƒ=`´™õ”R|†8>$ðÚ^i=ªì†VEPÅx lÒm>†X±@}˜¢SMOwàÔ{'ˆÄq îç„sVfã4‘[7ŒB·73®¸«ÃkIc:3`×½ç\dîcº{Nq!Ù–ìÎ1^ɰbÈ Ú¢ÙêVÐÉ?îMªÜÚ‹å•„ùÚØÓoI~ß |nA&0«ô+Ó:Vk¬åÊ¨Û»ÝÆ”i‚ y&f¸‡ÿOÇÄ20Ì")qàL·ßæZñÃ"½QPO¯SÉ¥ÌUt ÞÛ¹F-Þ‹Ð_#0Ådƒ|0”Õ”BÎÊ^/[rß" PäPbSÌå ¼í%B¡ÑN0Çaþ¼êÆ<‚ Nþžˆ(ãà_ä°ÈØ ±uû4Ý"Îv2÷öâT`½7`ޏ¶ ްŠ.íÛ)ãÖ^Ô(r!×LN6ÉÈv¯Ñoð]J« 5 x[r¢aX}ߊò2x›þà(¨ 6yÊK¢Ñ Îå¶ëï1ùå]‚l‡0ó­°Ç=7@· põ à0p@ÓO·kÿK"ñ  ÓLÀp¦B¥°Üñ‰Nãb(Ù#?]Rh³A%0[V-j-âµ!` Ñc9Ñ“Þ8ŸåIŽUœ£K·‰ÇÉ",Ô%T…Û[®(‘.ÁL?™]»kjF1À žöà5i_’#~Óõò‰Ž,­Hzº"ßÚñŠîñ‰îñ/ÞI–^ƒA ~% %ÀâU†˜Bnþº`(‡Òé"iEÑãI~†(â_ u ÄW%slpáµÖ¼Æ#£ÛKßô_¾ùSrAšç;Ó ÿ:`ñ‘#ñjó·I€1 (-ÀÕ  šÓ©p8ìvÿVk–S²0Ä@Ð ƒÒàTaY\K¥”*•ŽÍdâS,î¼\&(BîXî**FÐXZZ0pNT*ZF˜b&82XIIéJèXWÝXUU]e]lCY0||@|}^sJf@>V˜›Ÿ54D 2& ((`\0 {t$z®O®°²v8T N\d\(4DìG&ˆ$ßRL á‡D6>†2È©ãE/øX›`R #¼ð‰áâ±9¥JÑy% Õ,“²b‘du‹E 7)`tèèF Æh̘áZOh $¨Æ.…\À! G  œ˜ÿàÄ€ '"pX×B Ȱõà8`« †;‚Y°Ð'DÁS &QQ¨Jh  7F 80<Àc€†›I[„9êï¢FŠJâ™ äÉU)=72F6T¨ d¤€U,€ :AØà“öŠ‚1šT rÁ P"„$JX€â],œ€vÁ&²ee€ƒ“5&ÒI‘B®\;sKP`0ÀÏ!.¬`!„ öÆ ƒ?¥ZpAÀx8+a‘¿@êì”ù(˜à(@Pṳ́X@û,ž”xÀµ\D©—±tú€Ú|ÊA‚ °¡™ ™ž;ÿlc à4ÈÀ… œ+ †¦Á` €£$≬ áºì­;ì /€0*›øš´@ƒ‰ˆƒJÀ¡ª| &è(Aük• NAÈ@ÂÐNŠ 4lŽZ°¥–\ày ™f#šÀí‚flØj)†È¨ŒÇAT`B°è,+Žæ{̵M4  ƒÏI(qè *Uƒ • ô@/q €€h€a#²8B³5Aº¬ ° ‚qh™¥B•*ˆ`„Hˆ@¶¡#IrqÍhø€E{z!(4X` FÈFŒP, iÿʸ¥5˜  Ø€mˆÖ(Ððq`½°@‘NyÒ!ÕL)a À°0;óÌ7²ˆ†šeV$FDšë®9òÄÓ•:È%².ÃÐ…bàÐdhðàQv J‚iÀ/b(ƒa„¹` £¤ø`‚H48˜ûåÀ … ¶‘Á"0Pð0(ëD×Fx/Ávåm.”q³Í¹Qc›Y©´ [„‰Ì–!P8¸)f¸á† @º'Qj舘b€a €€x!§Aq PhX@„²X ÀNÑÈà˧ÿLÏ?!‚Z%.]ïÈ@Ÿ© VÌî´–!Œ/ ügî—ɘÍÅoOl䪠‰I&‘!†FP!’›ŽIwDÑÛµÁÜØs¤Î\¶¨‡6Â0 "ñ\@€<‚qBAŽ÷Ñ`@G@‹Ç¾ çi(!ÌÞ2Ÿ-Ýe8°„õ|‹,€%úÂ,6u¦|‰Kž*t<|!2ð+L•,À‚¨s @ÿHä?È® oQ !Á3ÒT<€ ð!Xn˜Ž„QCr àè(ÊKØÀ LMV—9 6Z€+wa ÿKi°¬Ä-2™Q`Tõ5Š0€"JÂl†ÁÁàñ_ˆŠìzÁÀ4`Xð…†JSˆè…R)°…¥.Œl-«‚Ì—ÊX q‰"@€ä0œ@VÉÝ8#—à"ð€°pð0 ÉO)„ëáøÄ÷²Yñ0³Ö9ËiŸl1@ðž»44Ü G+%Ò.`¬’•¸4´ªÕ t,FÉË<’’M¶£5@L h¦ðe0 B>ˆ`‚˜^ð‚œÒ øÒ.•°Ëî¬)æk¤³Zz™Hb 4×:‰>¾0Ÿ±@ïÿZs.t`÷,å ô©JVZ1uñ¾Š!œ ÀÀæ‚(4傆žLw3¨ *ÔÀÛ, ƒáD®$Á l`ƒ là"@«ƒˆÖ`u¥° 8%Y’¿V’’Õ¢ƒ5* ‡Â°ÌÑŠD DÁQÒàF3êe_°T®RÄÊ ø¼gÙÐf*yd“,!HLêtwî n—@B8*×`™«êzC1_Â@ø´è¦ù°‘Š#¬gT€‰D ±“x ÜI¨Ò”€sÈ @è.Þ‘Jq­¼Á `y!p! kî k! à@ ¨™f+ÿ4~‰ƒ£ hÄÞzÊ 8¤6ȧdÀîÌq@ž£” 1tçL†Ã˜IKôáz `¤(ñ÷ZjÊ ìY²m ã,†dà @,ÀKÀs°%2û4‹äÖí?Àì„ÅÍÈc;Æh – =(¬aàâEK†#‰°D.úÊ|PÁ22­Eà‚ràç[ȆJ "¾$Xà³4„ Ž yæ‹)™2.a@,¬‚ø„ª ZÐÿ À@VHðì æ”¼å†T.RtÍF'(ëÜ´F‰lŠrÍÚ,<·ÆEÊñG1~Ó­¾€*JàªJ€ÒÀT`Ãd’M,à=@¼À¸&1…Kg”úê©FÅôÌ"÷©Š>½´¦—’)»ŠeO€¸=–  Æ^ 9JnAP`*ÀE~$æèa,‚ÅäACN‘¨TsL!u´Ï"]ퟭ?dÑîÀÎÂ$2âˆ~°k`Ôà(dÍ„¨|\$‰²[8``¼¤N„î’áä0Rq•"ÍÔÕ\éRùCLÜ N`‚\(€2´Ž°ÿR,À(,` UP¤"@N€„3Í…#’¡Š*WÅUìp”ä6ëÕÐ¥õø#d §fŒí$„™J * !Ó` µ3²ž&ÀNàÛ8àn¡ëD$;Çua3ËIe•†Åö?á[† †îne–€aæô(ó^•à7Á  ™2AD€h‚s¾uó6f´Õú Åf ‚fè×(LV,ŒÌ&üέ4ƒÒ†î_š åZÎÀ \Öì¶Qf¡öÒŒÖÍ^ ½È¢¬Æ†ã[”¢,„nFà&! 懴ãB5ajDá!“aõ¢vn¡ÿaR½óİöl〴fd¾Är²# @QÍÅÎVùp4g\`žÌ.\éöqÉu³ öÄØ ŒŽà˜B N L¬óEnA¾D·ùÖ¢¨ÏVrU÷ï6M+7«d8 a†é–nÁúfè ãì4Ou·]P Mn•zÕTn×cÑTãv«Ð ŒNnZ€<îVƒ÷z=/³À‘x«ÈfM Ø")žfÜ“" Â&Ié f±·}½±"‰·¬(Å—b`”óU¿­zêê‹ uÝw€µÏ_ÑŸrb@º5i9`TÀA¬"¢b Po€-Ø'V/¹ï;‘…,sP#@¢7°­·.ø„…·"ðjÑ‹3î¤+h…iø}“ŠûZ ÈãD DÊ$Àz$€}k¸ˆ³×©¶kŽ îc7  ÂA؈©8Í@^4àDvEˆ©ø‹ñò·äe^` Q¦ŒÕ®r@^‚âÈr@å¸6ØøQçøŽñ8õxc6;ntpsec-1.1.0+dfsg1/docs/pic/driver_palisade.gif0000644000175000017500000002671313252364117021165 0ustar rlaagerrlaagerGIF89a¤dÕÿ*$Ò÷éÓ-.¶™J½½¼&HQÆÆ½ñÍF…‡™Ê®ešh!+±‰)ÑÒÏ÷Î0íËWQ?Þ¦²xfkz?8FGGþúçlKѺpxˆ/ر%æ¾,ïïïGWhïÆaª¯Èž¥¶®®¬ŒU€|sÛÊ—ã¶–˜¨Zco8=ïÞ»EH.€c “z&jc-ßÕ­nhO·ÆÍÿÿÿˆ†ÿï繩~+8$XRáâàÆ½Þè×sÿïµÿÿÿ!ÿ ADOBE:IR1.0Þí!ù?,¤dÿ@…pH,ȤrÉl:ŸÐ¨t eX¯ØkǺet¿Ü°WÜÍ‚³Z²zÌ.·ðóúL_³ßx|§¸£ÿ€‚ƒ€T†‡ˆ‰Š‹ŒSzb„‘X{{~fdpy`mau›n^”^”ª^§o¢j«ªqŸq²·¸±z¼½¾¿Àˆiµ³¯Ÿ®]Ëx¥`¥VѥϩiÇ[ÑÆsy }Ù¬™§²Ö¨¯Ç“cæµèì’uB5÷ø÷*9Qù6 0äÃìß!øAX£Ã|>Ì—¡À Á!ÛËÖFx캴ò d“(©ñQ¹ç›78Óúè1&Jݵ-.\0 —©ÿLºZ¶™E¨ºUÃl*¥…FȈ@Éw"àÀ‚øªzX`!¾¡Ö+ߎÉ„)-ݨ›©”*Ë„’@-µ¤6ÍÛ;ln7z¶GœNž=MÉ*çMR:(¨dEA7OKåZóR¯,>€NòqPã*0чJ›&Ï¡çÖa¡bLK…Ø]¿•kùÂq’K¾’½äÀ°âD ìØ¢ùr%TØàC-)ã˜Ìíªû˜h  ïáéó'®ïGãŠWL.Ó$9ò"uÆ'½DÄÙL >$xúóAˆ] ÁP9PÀ€ÀC;·CT´òEI¤ÈŠ&àQÆÿan” ' Ükží.¸!åÍ´b¹)6Î ˜s c<æ2“z”ô4@5~iXÌ-ÝÙ‚Ê'N™Düå—U…CPõD!ØZöà¥.äs€ t©•RদK›°ùÛJ(­$'J^`A ¢¨'D;ÀÁu9.ÓP26Œ š×AO@éHã95ÕXc)̆Å7uà5£ÌwO”´ !ªá•¤6Z` êaí å=cñ”—^m)Ä“ù ™f6qj£Rv{Áˆè±¥L–4d°ç´b@8z³c.æÄÂ]·êQjÊ8âŒ×^yJæXÿ)²pdÌ3Úd,d’îái  Z}ø€`~¨íG¦ yÖà/~ûæs09ð[‚j÷€öë„÷ø»D˜­¦¯C˜‰o¯÷¨€ [E[ÌÆ„è^/ÁKRKŠ1ÃÔÖŒb +Ê]ŒÉj3%ÝŽ·¨9°‡®-ËìxЉ! Œ/¡õ‡Bõ&™»~z+¯ÀFÁlV ¬@¨^ÏÖ5D_ á@Ái¥€ P• Dl¶ÈåjV}Õq>"›ÜÄ]ÙWÒ²+ ï9œ@±ÍŒ{Ú é£O‰Ž{ã¢M3ºèÑ…-myN:]@IIvÑ9çvƒ¿!xJÍ*àq} DÁ%ð{ÿÏh èw• ø@`ƒ™ Ñn{ ¸Ã]ñï¯ví¯™#[ 60”ëY×ÐÏž`Ãâ·ûín0.~á£++ †.Üøú(î`ÃuåèÈ.ÀÑßùüÝæ@nÐÞÖ?æ<¹À¸Hèpýƒ™cnq/±dÉ%˜„@¶†!`W)X–`ß)‚¤ ¯¤u ŠjqYb€YV՚Šk}sÞž· ï=á;ô: ùÐ%3õ±ï‡¯ñ—èö»€¸ÄJŠe%x×Uí”J»âŠø?ÆŒc'pÂt,š’i Û bðç–iÂÙÈåKxŠp T )Ç,˜;ÿlx5ˆ0±'L)`Áº >FÍÖ ¨`h,‚ôè‘é•QkØË>6¨È¿9-Ó_årƒêYòe:P´¡ù„üCbÒÊAJ,ê–{˜AþÅ€€O\,A‡˜ý•2ÿ[*Äw,DUtf1»÷~8€’B2Ê7p š…žxç·isKÿ±‰¡Ç\2b–3 /KÅI”/–Äb8,…§P_£‚&’2ËÜ-JRDE¹ò”D›e2°r`à.y6‡˜tñŠù4OÐÈ!¹Š~ócHÈAÁT‚ ²qJbc€ H˜©¨•>mÕÛ Pj¥½zwF°79Ø/=Ä›7}BEWæ¨Å6Ã$¸Û†JY¨´`‹ê§?‰@)MúËb»Šøà ˆ äW4'p\lèþh¿-¬K4ÅE‘`»™)@Ñã—°’ÔW¿])_§ùL”6Á¹2äXŠP½/¹Ñ¦‰ýžGjµŸo% keÇ+~ Ÿæ¨ÿ(êÂH´¸×•ús¯þB×dVØÁNYÄzÚÅ0²”eT½h®ñä¨i¹)Uå±/™Ê?Xó•îx¥€¨@UÕü”¨²Ö•³á1¤zôcüxÁ¾M£{£ðÖ°›]sR®‘žìC@JދɽŒâ‰.ÆÎ‘‡‰ÿ”í(A+dÜhÙ«@›Eñ4T–³’bþäŽVÈ:¾OÎJâßC–YÜ[ïQ•Žb€„iSaBòfˆùƒùH¡¾*ææÖ m%æ˜P|/¶Å7d‰+ãÙ–\§~µ±¢ã–.:Z'@Œåx\4Ç7Ê:íky"ƒènˆA“ÿwTÄ×ÊòÒV.W“:Ú&ÞhÕ[.l2 ñ—¹sán°%ZÛÚS¾ãîÎ ÆdÞL€˜¨;Ã’Ú è¢Æ`Êч†-úÚP™l·HÀX^À^Í –³œå¢Ø«3Á f0¸`Ç™ÞQ€Zj"Ÿ’iX÷Є¤Pýíög«˜„ýs]‚Ü5" èGÀ æ«°LbÆC["6HÐU¦xÓ㱟}.PnÚ|ÈA)h€í’/È0[„4”Ÿ|¬|Ó9!%ޏà@'éË@ \0˸`:€­ŽÏ«?zŽr\T(WÓ_zÚh´gup8P0íõ #§\ÙVÿÔ!P=+AÁlV§„ÀL.q¿$æ<ž4dwS±§Ëq&Tƒè=œRåjò¾× TžùÑéù^šÇPöêaH+nÜ<ïfÀªØ€$(€ Ä-U÷¾´8æjÒ«|Þ¥©‹ ¬šø†#ua@õ é2b%ûº×}%•¢6E—EC¿“ðõt2ŒSjz‹›¾‹? σ `ø¹ F€”`$Á p¯föø Íw¾o$ßþÊü¼H·½úן‘—¬ç=Æ0}¯hà›¾ñ½4C»Jß耋:¤xdI $ Ù·€Ù§};\nÿ4:8JØTïÕ_LÖO›³Lì‚"ˆ¦P4I•^§³“E6–Ë:„e.·x¦66€-`sà‚±4y+`ƒÓ‘€Bx ¨Ó·}ÿ—rŠn¸rù‡<Z8Ñ_ä2+#x…Xø¦4ôDh+#4Ƃ䥡³EðhÈGƒ.0°7°< - _ !+`Ї}8„ 8}˜§}¡æmŽF@Ø„+÷rP(dpÚÓ*Y8‰#¨.<28›µàNbXcÈmg(@èm1'p 9@pÈ4P$6 ‡yh ‡µø‡~(„FH×§}ÿÔ HÉeCÑ Óh%¸…•²ÇA¸h0ÂXŒÅx1pDÔ·êx‡xˆ‡êh+p‹òø‡D8ˆ¾ÈŽn㘈£Ø}hx^år±&;­ YƒX²Œp*ÉqíenŠ1>ÔÐsÕŠÜ6kh6 ²r.ç+@¼¨€`íèŽ/¹îøŽ¶8„FxÚ§}×p†Â8€úø“Þ¶„D :^çb‡&Õh2KyúÂMWN%_ÔÆSã³ ¡‘”Ž:`ƒÅ2ÒÁ+yxÿ-Ù’,à’!Ð’mÉŽ0I“ê8‹ ȸ‹ 6ГgØ—>”Ù“ã:ঈ¡3.d‡ÃviÑ”JIXNÉ\ž•å4`¡d>[h *p¼Æ•ãµ6É‘€¼*°-é,À’.ÙšmÉ–®9“ë8‹zH‹u©‹ 8ˆ4@ø¨¾ù›&à›ô›ÄÉ—Â9Ž.Àm*Œ zDN¡{¿‘”˜§Â~2ã^?!(ÚX EÃŽ—âå™ Sž{R.ð+~’¨É9Àô™–öé–øù–w8—s‰‡µ)6‰—¾80Ài&оœÄ¹  ˜”„ÿAÙœs§Ø3‡ZVH©%uјZÏXE*NÀ*‹+­÷”K€¢N0ÔI€D³¤$`-@žrøwPÉ–ŒÉ˜üÁ#LŒÂý¢LÅ¥Ü+œÊª¼ì†Y1rÂ=ìö̤õy»l¹Ÿ³ÙËH ˆw¹€F¨}$À-ðRŒÌË|¦ÛúÌ- ®\ê¼TLÔF½­³×Œ÷€ç¾|wàkA‰šnLÎXýMÓãì¬Îù •D0<ä´ÏÿaÏ’ Ö]MÖ‡¼Ö‹ néyüGÉ û¸¶êÐŒÉ4ÌÉ|ÝÉ'|Ñ0Ê0`Ô-/ ÊŸŒ«lŽnxq×Á&Òp¤=lË?¬–>+Ä»|‡ðØ»3MÓ7©´öx`¼€ÌÊÜÓO[Å=ÅR<ÊÇœÌÇ,ÅE€0§5AÅ/~üÍv+° W}Õíœ\x¬Ãm\A·}h‚\GÃMÜÁRp#wŠA–|œç‘ìª mÁ íЗ¬×½ì×ÑÔ¥ § '@*°Ê­ì† Û ’³,@˔ͳIÊÒù —EìŸñè»Hü»HK„ÇyÙw}(pÌÿPÚQlÚ±mÚ=ÚÅ^^iT¨_‚¿Z#]~ºm¦A{è|ÆÈ5H$À1fB^}Gj ÖðšÀ$\l}Ï[r¯G Õ“ȉ`–Ö4ømÛ ¹ÝM«¹ªÁ­×"LÞämÂpÞé=ÊX,Ô+,ì?*ª£m!Ù“-æ+m»ýŸ/»ÏZàž­€ 8Â<}<°ðàv®ážçz¾ç¥}ç}žá@¾½CiÍ=èûƈ®G°5m¼âu”ÕÐ ÕB@WUÑÕù¬¿‰Êv]ýϯÖQ*Žš4õ4U±MxxEùhsä‘ëœ ÞÉšå& Ê¡œÑÿAå0`‰]ÅÖ†¼–îahP¬#BË*–õyæ?«Ÿ0íŸGœ‹ÑºÄWj}Pç~®áv¾íxèÛèzžá‚Þ;Õ‘¨€zbôAºMãoÆA£¿¿=Hó+ó«Ã]܃œtœÇ ãhË‘\Çø 1?~¸‡@ZåG”x œÉê:ê.›Áì¹yÉ PnÞ€ ÊüÌÖlØYß }ºZ!àÑ$1æ*ͳ.Ýši®æ´i´4]¥ ΋×w8 íÚîíB?ôßþíA/ ³ýL$ƒcMâìKlt¶Få,ÕCð¶6·k¼ÎÑ”\Ù[4p6ÿ¾ %“4¸‚‹Ò äÕM˜ò…s‰H†乑œÐäIñ”|ñ¿ÁÏÉå]ë½ÂíÚ¥ìÌ0ÉÐË!Y- E¢ ȾßËî³Ì¾¬/à¼+¥›õ¨à9)Eoô£_úEßíîœ+X1ٔƇŽèS-2«³qÕë¾î×›°xfé]¿%ž1&$Å6ü1û4Žî­ª*O݇„Enœÿw8ü¸u²ß]ÉvÉßñµnë€Ñ‚-ò-ŒÊªÌÊ'²Ì5îù/Oæûíß?+´™ýŽÐNàÒ>í Ÿ“;@a ",‘I岨p*¥Ú”ZÈ=ŸÿTVQ œÞi‚Pk*ÆS\#+Ug|ÌU0 *ȶvžÐþá¾ÐÝ Ú rÐNœÚ¦§Àž8vÐJž0Ю<©®ìHK¹&44&VYSM4tV/V[22lsm}2n|€Z†‡eŒQP6–— <¢£+`¨^²5R_b``pJdpkJÖÙ;Ú;:ÖXîY:ð÷ï Xþù è/„ T¸âàŠ Zà0‘C‹P±‘‰? °„dI %‡Øq€ÁƉœ¸0°A%„)›XVb¸¹óf x幨M0À¼¹Ò…P§Ahxÿ¡Cj 0bhÓpaÖ¬t…µåkÇ`-„;†lƒ2e ĉ“ Ü5lÙ^HÅ ÎÆ82 ìØ‘¡O»uÜÍc øž>~üþ8P`åƒ †`è⊉)b´¸QEGiðØ€’5I '‘Pp*©ÆŽ§·qçÖ½›wïÜ.XgÅõ† :¸â¸!vW/_Á€k¡–-3fq¥UÓ~ o·lß`<W!Ø-¦—Ø]½ÇïñU–o9!fÍïGM±"FŽ?*á#phmˆ“`K0€×„P0%¡è˜„Q|«Ð 1ÌÐ).p« f‘esøpZ¨¡—°¸¡…ÿÌ‚nºbŽqË:gäš‹pºã¯f€¡/pØ@†ÌÀöÔ[Ï=ì±Ç1È"›ÏJ̺o3Î"⯿‹Hû¯„ ¡%`;°Á5—HS‰Ùª PÃ9é¬ÓΧ¸N¸XV‰…ªTlx1uÆ_Î’N݇”! ™§Åp°H@­‚%çQ,±zØy2ø$“ìÊ‚ŠU³†zh?/E @Sƒ` Qr%§rºSÙe™]¶Dâ:슨š€ªPkÐ\|xNFbÔjÔÑ·&…&»ª±ëÇu…¯HcScÉT×Q¬zì’Õ~ÿ&»R „d5?ý>ƒ(4ÑH(SÀ X-………˜Á×(®¸b‰ÑlVã9n–Ãg¡5±UVÁP±Z„.:E“iËÆë(¥+ÝK×õæpÄ)aPÍ;¡ÉôšœÕwöݧÊ~­TȾY-p¨"ˆx¡3 ,ú’4Ó<ú¨…"Öâ¯Á–Øâ:.Ûì³}qDà>ì°ÏiY±¡Ûns!Ô,DWžŽ­¶ µ.ì¨à,ýÑ»ïÄ“ÔHn¨zk1ày§ž+ÚŸ~‘¶L3ƒ°œ•ƒ8X¡Â;L‹°Žú€ÖVM°i·xX´qÏ]wSØî³w¸­E…Ú fxÑÅ[ÿpÉ€—^d\ RÈýǹo »îºæ››O €LЩÁö*§üÞôð½'ߢ‘ît0x?,8†vµ„Ø„ ’€/‰%ÈÚZ@Š-(MÀr øµa½& hЉ€2 Á»óàÓ&¸ £ֵÀ è )y¶† Z€Œ½ñíF€£ 5*`\*<áh‹ l†t 'ÈA© S/õØK}ì{Ì« b@`Ø XR‚þùO0ÈDTà™‰ø'#YXdð+ÙhbmlPÁ6 >A â„#*X`@p¨UBá¡"„LÿE!'ƒ ¦D%Ws—1ÀjóÀ¢ªAf"ar`?L€˜ ˜5DÐk´cfí(–Ç)\0Sà€ÐpMJü²Ýìæ­Å'U2TùÓŸHÆŠà/šÔR Iö`Q3´Ñ[” côÀÇ€âcg`@ zñ88@hAkO¾ò5¥XƧþX „à~3±Á¾¸ƒýíïqÂ.éG5Ñ”FL 9€&¨¦ Úš“M>á\&›Tè NÿõèM¡~Ðä4ªQÏYÎàð¸&YO—¹…†×)N ³˜ ;:Ø‹¼W€È Gè–dʱ'Uu"¿ôPÊ)\Æû;@òw¤ã—ùóLˆ š\u¤4#0ÍhÀµ‡UlXôciרhnÓq XÁ”BŠ6¼a¨Å]TV1ÎC¢Bx‡D§ˆXáÚç*=˜™k:¼Ë+¤¢d# (4`pä &PŒÐŽ;/ˆ6Ñ1Í5ÚæFP› ÀGI½b€‚™¢ дHäVª;æ1 + Ò €ÃJÁc)ð°¸wXð/Åæ{ÿßøNVÔ\o!Kx–Àf+¤hÇY-v%ZâʤÅÌÀB%Q½èÙð†Õ¸™].µCKÍR¹†Óµgrle¢”Þ*™ËžÀ'( è,PƒÔ Æ%(ýÄX0þ°"oKc-.`‚ôͯ~_S_÷R¦QŽ 47“)@€ ŽÐ2".X`1/ëˆ$Y:¬¶gm†0›!œ• q²o7‚K4 šy#+ZÑÁ¬• ëYïzˆ™À µ.&‰õJ_æÖ`cR v?ƒì@€°º.CÈ šòaInÝ<€ìwÊSŽò°>êÇÿ´½ñ}˜©PÍ)ø¡§Ö"*;faßI„²€Ö›Û|»YÙnv¶@DgCDË‘ Á¡KÐÕm6®÷‚^Ú/¸^Ñ-\DÛrkMb©èáV|`nUO¼GilPG¼ ´"Áp5Æ‹à¼Qa7ðØ÷æ—øÃmípLœâ§€±¬4¬ ްÃ`P`Pa» ú°|ÍU t¿1OÐÎïõE ‘ŽÏæ ‚”Z~}!ùáŒCÂΟb®4í#HcÜã>_»Ç:Öx8õ'®wŠãØ*ê8Tn&žüZw²›]ìÁyÙ5w½ëÊPø+ÉòО<´‘Îh\à"€nçÿÉÃznl®Dd{*€¡8ÏIm‰ÐÇ|(§1œËÞ‚4 t.#`´$ìô£¼íLÿCJ íÜîÉb ïènâVmÕXâ"¦" ïÜ j¢×H‡û8«üxP7ràsÀÚ‚#ÖlÚä¡. 5¯þƒ“€Zf€6@· ^@x¦BÙÀZ”MlÑdOP¯^òïö*„FUê ëfi–þE—fVÏ`¼DàŒ©?6¢Œ>âù"@îêŽúªo絛áøNü¦ B þÎ|Jšv°)Q(†ËòXÎäØl8,ë/ÚäA‰B1 'ï¸ä!“.H&b gÀäÞ/Ù@ 7@érèc£îöÔJ_Üà +j2ú!æ`b¥Vl…˜˜ÿqȈÌj«? þð§1±±î¬±ú1Bjc þËûp0üÚ`À*ñy"Qñý`ýÞLß/ óo‰à­TFq1\`0À«b`‡bñ/ò +4Eú)árˆÐ  ÷íë^ØßãýEAçº2ÃA0àÐŽ¥®íèÎíªñ©1­ï­Ïá$Àá°ì÷Š&pP›"dÑñ&í`Ý‚0Úr æTpÚ01þB‘|DQ‰HÅ:ÄÂpSb`° 9bÎD|žª6`$†ÝÀéÎ!&¹&p}„9çJâ'`¶¤KÈÎìÀäjF@g$®%éòZÿ2ê²%¯1]R/¨µj³p²0K¡ÙdÎýï'{þ°2×Ððeòr *càl ­D¸p QာReÕ`Ý@•«¡Ë!Ê÷$­¢äÃ}<‡V¸¤Kîð?òàJ ÃÀ.?€êÎÚ®.ƒó.YR8ûÒÃLˆÆQš2€£ ³:ïæÈm'ÒÙ´óõæ2]l.Uâa0ÛZqÂÒÕ¬‡zF`¸ÆŠ ` öÞáTÐçÛŠÊ’_€ïhä#VbK<£3(bùF#D  Z28í²%¡a%éRþNR/?@>ÿËÂ¬Ž¦à ²É H4׬Ó0!ï'[n'ÕÑòŽHGÑTLåòJÞ8ˆ:D8Ó*ÃCÐÄ-<ˆ2ÀD€M¶R¸j•¢ÎöèA_¨”}â,¡¨øê`žuÅAÉ> XR/% /&ÔáD@ÒTDô .‹ §à§êtŽVô&× Ú0ñ{rPeé@ñ1íÅF‘22åAN±CòÑG©¥CJd'©M*g²EEZ@ˆ%M<År”I¦ÎI –bì¹lV4ã~è0Ô@ИNL&‰%àM?Nã4M…³MyW唲(„ œ`먠)úÿô&ƒRaÔE°æðÁÓTæå0‚†r.ó„€9.à‰ƒ¬§ád@…z`$ˆX1êQG%²r„†r@6­´6‘ BKŒìx3WÀ+É´HBñòMÓ´DÀ`á”MuõL?ôVáôB„oYqròÞ¯1òê/c=Q«õ(í±)§T(ï8$õ„:ÄÙB«ô¨0>}Õö‹I) ”!°rÒc yo"÷å‰1i?ޝ›ÑB½Ð+.e€LÉ4N+tWÙTØ”M? @¢Vb±v7,O'c.ò¢­Pgôˆ@ñcïÑ¡êöFÖb@m[Ñ\`ßÌ=+€Þÿ´àëS‡ t`^¬5†æétg{o7f?–o° k6€j—öiw5rÓôi«6N k1÷6ÜF2òäali´l‘2¹]ŸNåŽÛ° (ƒrºá£á$+XÖ$ÑÒ_lö!%Ò=tVp[¥J¾nqó)‚hI-l@#Xª# ¨Öj•6zß”`ÓN©jƒ5sµw'ÚìYaN=¯'¥•F=ÖÅÌСìñ1µUZǶÍÉän'†M(àôó|n"^ß*2Ô_4ÒøËÝ’Œ ë·×qöM ¶z§WWu5{··‚s’òTPÕ1‘ò:ÿÏó¨Õl“k„w±FÕ']·ô’±ž‰±œî|ÈÓlj¹ø—Uô“ù²¤iôVŒ÷-ÿcTÀ j«Öjj­7N—{/ׂ¡˜ hÎR¿þPñCøcGw±52Ípc‘0nCKõT“Aë(` =to‰Ð°­" Æ|—¢^…V†K4£'‚)m#n"®kÝŠXi©¶Ëô•Xrã4ŠÙ ‚²{ãñ'?X|CWG­u‹IWi”Ù>ÄÆpv‚(&?ò?Ó0ÞxSõwýE!4R‡‰7Ô˜²È‹£Ø±ÿ¸LxzxzYW™‘=±b+Ö1ÑP½ÿó˶(iÖ0À³>×Ù\÷^€Œ Œå<@Ýîљ߆Wø~¯s8ùVoEìhIþtà$@sYaëö`öV™8‚Ó4˜¡x¸J¤k•-F{Òƒ=˜ZuôZu™#3d)G);ÙLÀš‘à>2¢Djˆìe­Öãvs¶†œYÕŽt#bVæu@·ŠØrÉÔq•Öa#¸žwÕóYŸ-xñhÎõÔ1ƒ§µƒóÏɶ|’)gHGæ¡—•z©Y3BÀÚŠ?Mw›¨†]%ënV0#-?m`BøŽhIøz£·¬£w¦«÷ž vÖ¦+˜ClÙ^ÿ|—ð˜A÷§šf-Ù §’*:ö®±g°¹£!‡ Uì¥?ÍgJ1‡Þ¬ú¹(ÃsàÇŽ¢i„JH¥À¨±¬#€C;û¥gÚjA¦%€ìÒ­+ØñPw§»W AXu¶šT‹™­Z‹î¸ê¡›«4¸ËIIˆ³Qý`챇1²ã°>ìç t  þÖóò¥+ l•½û»½;Cߦ Và`ÙNT[{×íØ^îE_Tlø’éQ„5 3ϯ‡{·Õ·pë¨Æ‰´ °TÂ:ªah\©J5‡•­äºÒrïa†i61. ¶±³?¼Çô¥cÿš¼¯W½×;sá ZžEæbîˆ*‹é» M‘óBvce±ª©[èÐLý³Íî…¹7ÚrwæõTwv@gI«ß's®길­& ÜË»MRÃ7ÜÃ#¸¼9ij®—­ÙâšÙbq Atk–^€š¯åϨSo¸÷›šGk8<ÙtZÚlîó0­˜—Kâ¹çƒì!­Ô*"íœr¶§±¡Ü$¡Vž%W‚éË1ב å’Í'mnÎsô§)“2¡ÙP£9·‹j¸gü¿ÑŒÒsÚóðÌorv|±uo|ω|íf%3±éaRzn)à$£/=[aí¹‰­6r!ÿ=kG|ñØoÚSó:ïõ&gÓŸôÓ99eß·Æ*8Þ‘§ÃÜÙŸæ0Z‰ ùgú3Uªz"-’{¯Å×Jh.Ú?ÉÓó lTí$¡¼ÍúqŸv‚±×r¡öØ%öü”Ë)=ÇeÎ17VZ3oÚLÙF毚œ– §ão®ÙókïÜKÕ•„œ¢>º‰RÅZõé¤tGÛ{ÌÍ:Ôáîý³=;´—¶ß±·i)à­sÛ­P„1XšC$´Œ ЉÔÛ¼è%= ½–}‰èYÝï˜×ÁߘÑ|_æõoEñò.³Îq]<MÜÄ#MñÒVA¼óCÏr!7/q¾Oµÿ\Ùužýr<ê+5Óï̲]8\Dº›Íç›= »Ö®á¯¡ ‡FŽá£Ó÷±9ÂÞ¶öÜí¸¢ÐÜâkƒ³rí’M¿›La´E?z;_¼+´Bß¾Oãzà…CáñCMd†PÍû~‹íÍåšåÜìÄ}^÷óµ¿3¹oÖ|ò¡±ÕçÅóžåm™Ù00ÑZ'%弄=íÁÛ»—¶óOŸWo>õý”‡~è'^ôäöŒÔI]ÀÛüß±¿ü½Ý,FßÏÒÿ´åÊ<f«ŽoÃ:£ãä\£Y|2—Éëáù >‘-áEH¾ßˆ6‚à¢ÓŸ5:K–`áÖŠ9½n¿ãóú=¿ïÿ  NjL$&6"*:ê(jTZV¾h˜¼d¾˜h.:^ŒN ˜^˜J¥®¦æ¬ºš! É"Ášæ =1ù2,15íæä8u #Óö7Q5X]uID„‰õœ¥…‹«m•±E¦«¯³·»¿2d^.ZR†ÒWêäWš|úûË' Õ(‚¨T<¸J!BV²Ú2ed`Dx9VÌ Ç"–!["¤#.hxÙ-Y$lëÆMŒmà\’ g®L9Z;ntpsec-1.1.0+dfsg1/docs/pic/SAppr.jpg0000644000175000017500000002704613252364117017070 0ustar rlaagerrlaagerÿØÿàJFIFddÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀà,ÿÄÿÄ?!1AQaq‘"2¡B±Á#RÑbráð%3‚¢ñ45C²âÿÄÿÄ*!1A"2Qa#qB‘ÿÚ ?ó0p®Ë¨™ô ±“2#[%cA±FæŠ*dÊ'l ѪÙNᓲ;{)r†aôJÀ©Ì Æ’4âhZBVŒåØøa<>%6À>,>•UÕgcAΑ)pR%h¡1œ0¿ìªÜ1~ÑK{¾ë¢7/Ãqry"ÆŽZ^a5*®€ôÿU{“¯ƒ‹Jû%h£†5 ÛkKpê…ø`ëùEª± øpÝšS»7aåItnÃ*©TX v[‰$h²7€tEغ Â9«@øˆ'C M03}7û¡€œ%Æ‹v:¦1†w\´C%ŠØõ"µòT„1ÑQ¥dŽluµ –odÔu7¨û :‰½PP±Ç­ÊK"7¶Rlt>Ê[ drY9 (aþP÷S¸dÌ„¥$ä?a" ´¥È¦‘Y›ASBhý$-±³)¤3Ì(jªL…Ñc þë6ËH,Aµ$Ø×d‡ÛÉM—C„<’±Òáÿ*jAG|=rÜ-¢ˆ:µ …ÜBŽ•ì=Ј`ðÕ‚Îpyjˆc°ôvNÄ0À7æÀ0Á©ºOs`F`³Í4èC §éµ{¬TFøtÛT ¡¡T<ÕÇ  t'™ÕUˆH‰×®”LD]Û™¯ ˆ†Ž£dÐ ,¢M LsXlpî®"€Ý‘è¤!ŽÒÅ& N†"2/ª‰Ä5ÔP; †=|W#:ŠcôYÉð6ƒcƒb³r Ž6Y¹XÉÙ‡×P¡±Ñ(±QZK¨h¥I¢Z$îé§C®ëEЈ$ Ð$Á\OTYT<ÂkA☄~‡’v'„ýÐÔ•y‡R賂.t³›¸â4«‚ãgšVí®ü”ØÉÚÃ~(+Y®É 6Œœë‘î´Æù"eV¿6át>ŒQiv7X¶h‚£oʳl¨öNÖñSeŽ åÉ0Â+¢pfˆ°‚ù"À^ìH¶wI[ 7½•Ü Š=S DkIˆc™J“$í°‹ºÓd‰ñÕš]Œ쳨Z'Á,X®ÕÄ9"']<Õ.ÀVlˆHÅ@9…®ºT™2Z@2µDKFôLiͨÐè<c<À¾a2YÁ¤ƒÏÍ4.°k„XðL 41é@侎¨–P²©`û,26l¡€KYI{CYcm2F³Ý0%cx‡’@ ™6òùG‡î®“.Š|(ÚÞG?²Î ²f¨)6³‘¢è!­¦©@´Æ8·]PpÞÛ¢Æ86Ð!Ü%0;†ÐpÞIÂ3['¸àáuëª,á't 挫q Ão÷ Ù¥Öª¬w2Ö¨@òÅʵê›9b'^JÉ’"$Œáq«­*O€s Yû&š:¸E'’ÕµDR–ë§š¡½­æm𨤠_1èU"X;ƒƒ&¶’F‹É“:âXÆÍBÁ”6¥ˆ!­ “°' l€A˜ ÀÊ<ƒ{‰—E6´÷]Lç]–r²fˆ-€rY³UÑ õª­Ê(fsÚ|D“¼eøXŸ vt„ÛüG@¡ÉG†R†î ̳3 ™¬,p%¯a7Âá¸B’`Õp‰’;…(º¢Àp°;‡[´Ð ¢y à$U sj áÙ1-Ñ0¤FöRh‡ÙÑh„öôUl@Ò4–sV˜0Y#'aËU¢dÐ;ãÞý°•¿á¯º¤ G´ ÕièË&†ª¢KZ\Ò %¡43NÛ ­‘‘AÛTÆ8 uêÉdNŽÏú  |1ly¯Lë\ÆÒ7Y¶Pd{iJDNÆÝ$Ío.ˆl #j›þñ¤XcÅà¥> ñ«dK¢žæºè‹(ZIYI‚Ú4Y3XgR²L[šiÅœ#ÔúE%É“Á™Î ¡Z.,¹Õ.̵͓Ñô™áæZoô[Ç£'٣РЅ¢˜áÒýø á×dœ6P蠚ׇDœ @–ŠñNÀk›@^é¦*²74ú-$ÃÁRä’+O²Ð@f†‚hdŽÉÝZ7°`y*±I½j•ß{MPÑ\Y,­>jì@Ò³êÑ:g4i^z¢€Q[ŸÕX™:Ý‘i#k EáaMe *;c"Ë1ìÅÍ+HîϸêžH¸¡°n³N— HÔ%cÖŠ×d€H<ËL„øºÓä‰XaÔ®¶èÆË‡E”ÙI°5Y6jŠÎуüP6ãŒÿÞ»E®Ìö£¸k\rüY}Ù€^Ìt„3¶1ÿKÿö]-TQ‹ìÑ´j˜ [A;à6±i€â X Ãך`w óN€à,&ØñI]‡Î²¼lîƒ ˜a¦•¤‚ÖÈ7ðëè·zl±WF_4¶Øaß§ì±j²7FW®·ªi~ÀiuUÑ=Œs-Rd´@ö+¢Õ1P,‘ŽeUŠ$`£¡ó È<4UB|f¶'ª±ÈÂO_EhTCÃÂH¼¢h†vYV®esÑ: ‡nI‰¢25úmFî&غä¼+àë kMä†Pd€Ãœ=›qq7Ø®œÉ8ˆØF4Õq0%­$1Ü#¢p )›ÿèâ@Wò"EfV%×.Œ=–P³j±f‹ ¶ÖòY3EÑQÚ‡äŽØHÁ÷¿Ùõ*=”Xf v#™è¹*äm#IÙxCr8¥âÿŸ$“ëÌ9ƾÀ.œž‘‹/[¶©R,&†(n *`:¬¨œ&Ψ܆?UiØ E €×r°kžœ¸wý„•Å£ÅñX€ÆMƒp§Á#˜O=‡ÚŠû},ñå­z>s=âÊè¿É»_‹ÁpÃÅ@4ÿähóüËÏÕx¥?¶§¹ÅýÆ0ÂfP‰p³²Vî@ݾn¨ÓeÅ/º=XfŽNSqµÙ¯C9õT‰‘ i¦‹DÉ•¢Ísä´3Ø8uòÑZè@’°W‚´&#w „öøÒ´îm‚­@kéû«$„´€I †ˆ¸~bu(Ehh äMùvä¼?Gb kt*g˜ß‡íÀKú®›¸¯nˉª`HÖØÙѦˆ`87]uHE~pÐ0Zóp[b왕¸q§EÓ#ÙeÑc#DÁz¬¤i?Û+ƒ‹œ“^¿þ‚ŸýK‡e<îv-´[Ã4IÛïK JçF™:7˜+p˜(0ài mØ·—f!!–T°¹è­t·TØÅæ GWTÜ@â4òN+€8iº`s€«E•k¦a»m“žñ™¤ Q9ü+éwí콯­øþ“èóuº]üÄŸâ ¥¯¥Å$׋5·†> æÂÊ&ÃÊø¥n¡í4TæÁŽiîeœZÚmò^Ö³ñ…ÇÇ14ÙFŸB¾[U¤Û&â{º}Bq[6¶~_Eçóg[i«Don÷­rZ"HGpU!ÈÂFôµB•ƒ^^ ÐÊÂ~¡Í_ {45¢´H+›Dê¨diÛÕ\{!ƒ½”}U2H õMÞͶz fî!Z/ Mh+7ÐÊŽÞf\<º±ó¸Çº€Wí#E;dà+’D•ÙÈ?M~`¶ÅÙ ¼/+ ¦F(¶„oÒÖ24]°V¥dÍFCµ¯ïsŒ“ÝÇÄG‹ýš¢n‘®!˜x¾+9˰•l2÷¯á`âý@ p.Øæø£xÑòê~ÊŒÇÑꀯŠhÒö*„)º@5AGW­ Lâ:„iÕGˆ†)¢t30>9 šE‚éÛTÐûàñüï2Œÿ{¢iŒ»›\,ká¨ô_Wãç“.?øxZȨJ¨Ýt+ÒÉ–?ðáRJ\„45Âï‘^Tá)qG[j*Ó6=“í#ñf5ü H”ž½·@ã’'n›T§ôf¼ƒKʾhïÚBöõhW0i­ÞÇEhYô DR©w ò¥h@rV„ ðn«ÝPˆ/roÁR%JÞ‡u¢;Û¥mÒ43€ž¥2ômÐh¾}¾N¸Æ›)—C)sÈË1¾þ¦ÒèÂø¡ ¸°P¸Þ¬®y¯°¢‘Øœ$”Ç{¤Wç 꽸»3ÈŠ¼ åZ®™¤[ÄÝ/Ác&k>›«Y>YhÄæNø®Òbäݱ¿»o“[_¯Ï7×à°ì¼›;ÆâÈ%¸x›|ãnû4{§ŠÔ?écd:&=£å´Ò°òN„!äÄ;wWÝ+¡ê˜ìC@ G £Ñ!’fñ/ØlbÆÜÍ”aݯÔ µò#šq˶Ï;ÈAl³Öð´k¡_R•¦ çÙw’z©ø•S›:Ë\ kÁ°æš õž5(m*q•žf£7Ê™15ˆgÉ3z8 ýF«ä5˜~ ßÑô|ŠqE‹…jG¢çY»‘ºk¨µiò*‘¤·Åh˜€¥f½ ˜m²y«LT #xNÔ©1ºúªD´(½jÓ$Ípwå­J¤' ù U›ÈÛ`/®N Æ6Ç¢²«´  ”l>½ÂÛOà,2R$Ê¡7µºœñ©dÑCC~‹&¨ Hcƒt×ìì7ðçô°µÅÙžB«6¥Ó#$‹x𰑬B Ûn•ú20dw“EŸÑEr3ϰt®2?ëpãqñq³ú®|ß‘´xF¿²°òfÍTì\œùMû4{­ºTfß6_i²6ŽÇ´xZ}qÑ#ŽþHcBê¤(muµ@Ä#õLBÖ›„8]ž@Ò`d8ƒáê×=åêø™Gæäâ×+Äyùh è¾—çûTO Ť0õ]J\r„>iØé—]•Ì:Ž8ˆq_Êvº~S離ò|®OåÙèøü›]H¶íoð˜x¶3‹Ç–ßr×pˆÁÛˆòò çðârVÏS&T¸F¿ŒšäÊð¦/ékÜºéø¢`²´ÍîAÚi2ÿŠÂ’×´†ËÌÇxùò+)Ci¼f¥Ðl­?Ù ¢¬Fªˆt #NþÖ­!:2kM!0g²¼• ðíH*1œ> $ ß@ 6v^:ÂÛ°­”ôyÜ|Yc´úH+l nÞθ¿#å’ý­E)»„RåNÀP4Ù:cÁ*›íÏ{ \]‘‘ðVà†”º$Ñœz."×EÏ7ɤ@;I9‡"ž0þâ€ówý¡Èõe.Ì— Ÿ ÝF?ˆxŠ:êãCû®Uö‘³á‡°C1€#‰‚6×@¢Ý™†ÕŸÐ…h<”±ŽpM 8&â(ÝꦘÎõT&r'‡ì€æ³¢ít³@!º3 ø‹ˆ fYmŽòG»À÷/[ÅáùehóõóQ 1°L¹{3Uôxp8ž<å|²b§pùXÆJêDqìù®»Äˆž×p’Eú« @ë èK‰›¯$Æ•½€ºÖ z-Ñ«Á‘ÔѲ†y¶'¹Ãpßz V¸ãÉE.W;p™‹^çS!ÄòµÑ8\DÍtOl‘¶Fæ8X!pÊ4Ä?è¤béÈ \úCxxFæÜV¸‘œÆ`Úh-e!l’&‰÷†±ƒ‰æþ–ï~Àû,zf©\4îÌ3,V:F‘Þ¼ÈäÒ=\Ù¾Ò£xªF§²x`p˜Œs·ÅÌx?ÈÏ•¿~#êõµ$eìÑVŠJѺ¢G7SÍ1 hµ´ Cª :¾è€ „Š`!64.Ðùæía˜ö«5n ðÍm¾qÐ௠ÓÝ}Ÿ‹w(>OŸÖÆ[¾èÁöƒ‰Ã˜ádÎhx$´WÕg«Í(ýG‚Àü®W4xª×|˜¥º2&Xã%MCø•Ø<Wƒn?/–n(ÝF)\^dã.útå[/CªyßÜäÉA}Iÿ'l¿Äð07)‡‹å•š4±ÃÌ‚ЬÐÞ‰Á-¬ö† ”š¡År}E%9Ï#›{¯;Ùß|¿F«9v64+D "Ûi<ú¦ ÐyôT„Ñë‡@ V WD ´Äãªb!}nëðW`z ]ÒðÙÖÑ`)Yž5¿ Äu!Ël.˜ìΛª<—bàFŸ³ïoðþýAæ×UÈ ñXÖââ8G UÈßF6g³lC‰$w„E׎Y;dy¬¬„àÞó\N{hù]ý¾ë—Sν={/²Ìqà©à;½§;MÇ%Éû*o’I2ø_‰lõÂZÒÑ@jé¹÷Z$Hü“…ÑÏ,MhÃÉ%ÄáùÀFwZU— Ó¢°±ãmÒHB–ëºl®¨êW.¨!`.¾gš ±Ô@A'_TÀKÕ4Ùå¿DRô2:î¡Æeøébl‚Å0‹%ÔxHȳî¶Á=’%Ȳ™>G‘çYŽi‚ÂKÆŽÅšÀî-E€ãZø.øåOÙË,/ѦíšLKâÅÞü¦ªÍ ¯_Ýræ|ðubM*f+µ¿†XÕfï̧ÇO‡”ÄÈÃccKi·©½yª†¢QTLðFNÏí§a1ÝŽÅEßJÜFkâÒ#v‘È®Ìy7£“&'ËÏÂ^ÍK‹ÎŽw3+ ƒ±#GÊEiþPIó¥9f’ Ã Ìö—7NZ.4ù:’àfé¡*쥂Ò"{O  x7¥H ƒS¦¡Y Òoè˜^,×ꘈË‚¶†o⪯ôu5Õ¢šnÃ&J:™i°3ÕvyÙ©×Íü΢/Ásçˆ= èBîIØ O õGš¿4H÷a˜‰ï~]ù¢s΢®$ðw³›ÏÅݾ~î:chw·5Ïóïìßb‰mj§ÁaÙ†š8ûÆ ãy- 9m©PÓ¾Áq³Ðacsðx™nY[Õ­ªõ>ËhÆ»£I-‚âˆÇC ºPךtI8l¦*!F¿ê€€8Y(æŠNÈÀVè '@!ät`?s,Ç.ìs—Ë,]ö)±Ï,g…Íeò·ì»4‘Œ›LÃRÜR£Ë2Äìÿ(ž#‰”f0D8C1'ækyÓ·åÎ×\ôð’9ádzèÌ-˜ü· ¥¬ÄBÉZ¸h5÷^|–ǵð’š °>ˆlª!q`sCäŽvxåhÚØ{øŸ‹Áâòøò 2wìšr51´Eˆß²Ñeø•ºO-l·K„‰»#œå­Ê°ÙcxYpí62ÞýtpêNçÅB–÷lÏY㧦—Õqû4’?®÷Z«Q89Åæ<(áÄb#‰×³ÞUtˆ™?¿þŒváÜâ"y;¼-"Ó éòǸ³œÓJ½˜ƒÊÀ.ÏÝhïó×ÍR3ÅóL@ò]Ý]¦‘4D÷€v GØÍì;ΗˆÎ†ÑÝ@Êìê~ë ci²ý6ä¶Â­ˆÎ–K±€fTãgºº½ÖyW66¸eú3]¨ÌžÓ[ ÿæ7G^Mð½}§H¸FÌÌx7â±l‚6êãC§þåVÍÚ¤n°|X3aŒmõ;›UuG<¤>| RQ1F^5â-­²S$Ë‹š&…â¸ßòž^à­ñÝ ±h4l…`:ÓDpЦ„=¢Î¨ÄòýP4'0™CÀÐUÉ"«LbziÕq6€V ŽÀas, ¸Ÿ&g·3ù—⥇‡.ÁàÞón¯£Gî¡åGµƒÁI´óº3Ù–sŠÇÌÉñx|,˜˜…F÷DIh»ëK%™¶zKÂᯩW.&ldòâ&•ÏÄHë’WjIðè”äÙè鴱űØâ$ºì:ÍßU mäÓÂj¥Ñ6/ÄHÙ%ÅÎé)¡ò(r¥´s7Áåj|&žPrƹ‘¦goùé÷õDÜš'Äe”“ÄêÐUh(Yäy¬c&Ÿg©“<œL¾É³7¼Œ4î/'èqßNEwé²¹v|™ñ«Ý‹gøRî>|ã}<Ó@ !;שTIõ벸˜Ûùµ)˰7ÐèÀ¼ià^œÖcÍbiÀJêÍnþ«\]›±®».ÑÉ rµàý.).mZðXÓ£……æMrZ1£þ'´˜Ç†Žˆ‡Oðïk‹3¾ £Ñ}’eͬÅ;ës4ðÖÿ²K¢e+.Ûà¨C¸m1Á™€v’"=Zoô%o‡µhþP«Ð D ÏOTòtÿT ì¦6p¯TÄq?âHÝ$Ú-!=7BC‡ZõN›áåEcÌì;›•Â$“a4Ķ :Ýkö[ÇO&¬œyq9ÖN8Π̢Äó+|³_ Åáíx†¸i§M6\y#É÷+S¤šQÃÀ>]“bó¬GÂà™¸ä‡è‰½IëÐnTcƒo“#ä1iáRvÁû÷=’aß}än,y"¶4£,*V´¹>X)¯d-Œ6ùË¢[½ëZKžKF”ÐÚIr(?ÏkN£{U×DÊ”ŽÆ9±ÃƒqoÏÝÝ Mh·­ÈùŸÔ52Éû̇g¡XJžôrÆO¢vÊè$l­:Æàïe¦ T¨Ã]‰eÀÍ‹Ü× ù‚½¥Ê?9’ÚÚ•Ö­ÕôBèü[¥¤!„ eR¥¡ÆÂ©vÚ¤W‘# -¢ég@ ›é–Í­mú­±v3,d`ºèDr¸ï©ñM~„Í 7‚<·ÆûN‹&[¡©£1ñ|YÎ8ÕqL÷Qñ7úågƒŒ¹:aÌM¦O;gÀEÃùG ®¡ÎI&ÅRµ§dèœ %â°æfŽ˜äiâcÇå?ÙitŠ,c˜æÅŠkcyØ¥ÞG’Ò±?è3¼BUªVO"÷š”'¶…b‰?š1{ëü¥=€Ý2³úuñK`n±DÌ:ðøl`)•¤lQµ…ïãì¬JHq•„ ý’ÚË´4ÊÍh‘懿`x‰Û6$@çµ±¡ò—⻦ý¬ú.Í>$ùlÃ,Ûè’L×/…¤»†h[•ºuÙÇìç§Ý~ÒÿÌòœHÂæxñ½ãEüp/—…uYePqììÐ垟&õÀ8ÏrŽÌöv6·Š1‚Ø™»ä­^þ‚ï^c@¼þ"ÏN3ë3njÑ“4“Ɉœ9\^÷ ,I\ó¹>¶ÓGãÇqÁ˜†´l’Å|“’‚äAÞJCï†#ÈnQ&— )9ó|"PIkx@oÖîMjJ.¹85ÚÈcƒÅr~ŒþyÚ'Ã,µ¡Á [†¡¼‡‚ô´úe(Û>K/”žérÄÊûN&{bÆ×AãoU9ôuÌGÆÿ)Kãι~Í)!ÃM«çG‰S$§=šL«1‚™òÜ:ÏÔj½œ3¸Ÿë±<9¤¤‰åp:RÕHâqsì›bdv·èª6Ø q¡)¾ÀÙÀòj——$n˜ldóQCΞ•Nop÷W…}‚ÌL¸ÆÆ7$Ž‹¿i.H|T’-o‚ÑB‰m°f x/«¢á°*\à¸dür'Ë{ÇfÎqy{^ÐO‡%ãyGvK“e“âÛƒ2G¯ÌàuÙ¢µ+‚à'Ù§k„l•¡ÔuWVø2.a„ÃÙñFNÁÏ•jJÙ`\\Ãåþ¡AÞWºªDrgX0÷Í{E|­|dq–ßÐÒ 9à €Ìߺ«¯’µ¸T‡²<ÞYàlñÀ\,Ï ÑÔ–ê|Ñiý±Éª4}tòZ&f• WÌXEx¡6ÐeV-x¢,(«Ï3¬>I†ŽIÚé+‹#c4. YÔíCöM¾,ÛK¥zŒ›äÇâælŽîòøCG7Hçì)žòÿqŽéÊŽojólËO&<ècÌ>çE2ÉÏŸ!-±äÅs¤”:W»ê’WêV&OF* v…k!v‚6z´R—9þDZað/ÅLØ0qNûùF®dôk†3”¹"sŒb[ÉØ,$ЖËë€â‘¸§5ÇO@xl½XàrsC_ŸÛ pyÆoÙ,FGÚ'2Y±2`žÎ({×]žbÆô¦pŠ]§ŠË—$÷ä›!îƒ' ¹¤V¸³­µGØé²|ŸV&¥±@x€HÓÑa)ÇôCÑåoé:,€³…ÀW&0¶üºù§)>ô˜ôÿ|Žädsüºhñ¯Ä\ÇÑ$Q£ÑzØ›ŒiŸ å0JYžHóePÃâR6@ßZï‹àóœê¦k{;œ¿…Ä÷ƒVž¾ ÎÕ`®QöžɼŸêÊùôh¢ÄKƒÄ÷ÑNÑíÇ÷Yé³(ºg¡å¼ò1î]¢Èc+xãW§ÑðÙ!(6¤†D”~op¶I37Úâ$â:ØM(Ø·WcN&BNµè†‡fúÕ^‹Ëq:&8Æ2À6î-¢ÜF÷»Þ€ZOÒœU¸‹ø> ú¿ Ïe+\D3ök;+»,=Xi/äL¸•Nì–7 Âc#‘„ßìýÂŴݲԚžÍ-ÐḈÔ1å§ŽH)öR› Ãeù”2³¼Á†‘Â{ÆÐ=OUÍð¤Vô_džÇÈÐ÷ã;“ý,hyÌóôZF¶‚ Ë0q¿½1 %ââ2H8ÜO[<ÖŠ(Vh´iµ‹¤¶'©OjB³€±­!PX¿(è¨C…rH¾»!ÛÐΰtLuN€6ÊpYÖá±±w‘ßHqk˜zƒ¸(¾ 1dÉŽWLÂç‰Áå8‹‹‹|bV0±å´jjÿò¥®8=Gæs¸lŸ$,Ã@ÀØXE}+‘Úg[žæÄsZËlwGÙ ÆÚ_GAj¢œ N£fû!Ê]…/˜‰óý#“}>ëÛÓâQ'•—+“6(÷yŽ%¿L2AêAoÝÄz-ïѕݥʿ‹døø¸š $N#˜ÚxBœ‘N<ZïU/F ØìÒ|@sû¼,NºBþ7W€ºòÞ;ìú̾saþ¸ò7bñ‘ƒì9f™c<@xÖ…nÌ!þC’+ñ"gðD@ã+1ÇK¤lpáczšýÖ¿\HàÕyÚ§ú"Ãäx(ä2œ4Eîm4yË“>I> )¥M‡ ,A§‚6·ÈRÒ\Ù +åÍ–`æ²pÑ 9HÖGª$úlÛ X²)Ĥ{e†GFáó·qÉÃÁd×6}®—V²ãLPO× ËoêÓB|—F=Dñ˜j¼fSê‰{é@A¯òÑuc×[¦xzñÍ«èÉ£•’6Û¸ät!zXòFjÏÔéç§–Ù¡]Wª§Ù¦be—Ÿè¼ú4°Ø[`J™|B†¤íÍee$ÑÔRL¢fo­mÌ)eDÐè³öP ÕZcSÉMX‡N†:ÆÈ €nÒ¸¬YÑ:‹“ 8Ià|ÕP ÕFwT„šrB¸¬nª„wHg„Pì†vE‰ðÎÆÉÅ9®N„geìŽÒç†3¯vÙÈHRà˜÷5ÀÓÙ “‰˜ìÁ€ŽS÷!=¨›dÐöW-i©ŠÄ°–rG ¢’v'o‚÷(ŸßÏ!Áå¬alƒón.¹îÇ’ÕY1ívË(áŠ(ȉsœòÜ’I>d’VífØÆAƒ’–»+K#eêIÒÏ@,ŸO ÒeÁ[*øƒ"kZI ¶| \3¹EQÜ?ÙN‰wìÉcœ]Ú)øÝôÄÞ{6¹zÚåÔAjˆÌNy¢æ&’3lÁD¶a¼ê»oÓÙEà}CÎI‹‘‘&d‡qý0lvíñiEÞR*ávqVž¹ò4ªUœrQó¿,‘.K$ª«&ô3mŠÝ÷¦ÅÍs8± ù ùmøb\Ÿ`Ö†õ³Ùu7³mÛ\(ÕôF“J²ÓõÔ-”Ê2扯µak\]~/´Ñy÷3òÆk½äwt'§EÚ Óú´ÍÄ'«è*£{r«×¹£Ò¬¶^{ê/-Y2çà“MÒ7 TØœçÔ>qòå’6t¸>Óoº™U¿v²S™ŽÆ™J¸8þ£Êv°š}ÚÜXž–î¿}¼ß$é9=Û€6>šiôhŸ¼–©£°¥Y÷!Ìó¦Uoœ0l¾YbäµþúýCÍe°2ž,Èn©²O·¨Ûknm%“®¥(ÕÜ.¦“J³\«ù¸ås•‘æ vº‹î½¼;G_²¤Êú—›‚_/æd6ñ‡H ’M½o¦–sÔµívìáš5*à8ß©9™$MK-‚‚í¸:Ý@«›ë/¨Uˆù Ö6¿––û¨+&ÝÄ6ã²­lô¼ÇÀÔ)VkÕܤѱž{: 6“ïb-½æŠê~V@³Ë+@„-4òµ¼®w=´ì-vŸÚYêh«Äú7õçÊ}±cŽÔw3ƒå…PŠGKú©˜YeOùñˆؤR|¹`¿a¬¹MÃÁ¶¹Œ*á3>ªç!œØÇJw8e-k~ |]•?+Îòv‡#m˜óF%‹`¶Œ?ïðŸ Ê$+Ìhv”«?ÿ¼¹x•ƒì{ck6:SøßªyŒ¿1<¶Ú£É‡s™ŸÓ |é­ë;¤¥ô£Nö•qy|¿Ô1D¹Ÿ2«†­úꈭ,@¥u'¿Óè©"ú“‘L2§e2çÌ¢m¢DMºõ>º_zšs/n;›cþN•pÚÀ4ZU™ÇõO8ÒílöDì"$f=Ãm¿m‡õ_2XÉ2±, tm¾Ûõî¨ÏÕÜØ±.½šmÐëZPÛ,ht«;êîqÆã*…E%¼[ >°çö ò)ðÝŽÐE5¤Û4ɤR¬Ñ>²çÝ—lñêß A¢÷³t­&2LjN¤­R¥J±…Y/Ô[_˜ÍF@dHA7[ ìÕ­VOõeÛ•õ¿›(þëšÁJgÀ¯ˆ.â»Ü3‰J†·Åøµ¶Þ´vax0à•G˜£K5ì¦Hÿ}UHöDF{¤¡YËÄjÝ ×©5mˈð„’#•øÊ m·§Å¥JéÊiIÑÇoWm²”HÎ+r4æÖ•€Ž3v$—"ÔÞ_kG$D³³96½”|7¾÷¢Û$I)EòØ .»B­ø¼:ZÔ¹\/›‡ÉuóA+åÿüwìÕ%oý‰¼)ý‘Ðëÿ§eráiݲ’Û)“A¸!oáSk;]…_âᬬ)ëfï=ô±pcÄ@‘êÇãsÕS EgKvž‚‡'+³„?ãª-Ö‰êá¤ÍHý(üN½‡NÚvtñÄ[’»”ÛZ§Ž2ªLšÈÿ~Áè4¸pÈG™,²áEÆ€ö|4‰¦ÔéR¶­•lÖ·ïÓÀ±U.æàj{£Ø;?gmTÍç’÷ñYW·m»« \|AñFe&æòÚžºt©Qü¿0YbX}šUkÉ·J·ô9ù8á;ª¯‹(±¡É…—&kH²’@¿ãÓ¥»ªÔÚÇÇ £‹‹Õ˜ÑB`_eÊÉÛ‚­ì¿_e 4rc;æc¦íÂÒÅßükûh;»<¨}¯ã®µ».°LÑ*3džm•7¶ŠŠ½-zLhr›vÙòXt+áØZÀ{*|Lxš5˜¿Ì;ûÏÂ÷)éj8¸îlRN•ÕÇøØVµŸÀó¹¿5K­8ÔO÷ç>@Pà*HŒbêFàÂI ì»ÐPíÄ\62†ÚÛˆÚÆß—ÑÛV‹,nHF Wâ£ÙR$rHHE-mMºéî«×Љcç2rß›’ÏÕÓ % |01ò1¡Èêʤí~ÍhµM € 5' eB­²!óö,gÁ~íúßû7¨LÂC»1ž5Rql)¥‹\ßÛYÝ}µõ?¢ó`\Oî¿¥kâü‘2”•D±ê 6p:öo›¶O()vêÀ~ÞjSL6£2À…ˆÜ;ö¥ÇÙI!ŠB®Ûõ'©>’zše>K¿„‘0i6v)Ư²‡ù«²°mêà.àÃá7·eaM*¢êž¹³Z8›dçDˆ¸;‰UÜÚõo6ײÔ6;7#‘LÃæ!¾ÉÚÃi«‚æ$gE.Àh«Ôú¯\üÑM³ wŸ4xÜßp-¨7^»­Ò¹ùxKg¥÷;x?*Í¿s×W­tüx óe…må,q«mn§ÄMØP«òn£ @!–Ìö›½}U?ŒðÇ*Êa·Qµ…êR |:8v„¾¡´ÜNÞÎêåʽ«6•ç{iñRñE[7>[ºAã^)›ÊÈø”Y·ŸÁf#_mO.ËâǾ9Mžc»?Fa$õvvÔY0´ÿŠÛµ:z;;…©ø'ŽÆy²A|™M¤ ZÖ éí>Ê/)7ê}*—SžÜnºiÜ"ã2ù81Ûz™œ™w0mä+lo„kÓ­KŸ“—Ç$DQIJ˜DÚ|»Ý—òü=”ã±0L“—<”Œ<”Å5ÅßßÝV§295…¬¸œd=º—nÒÖþÖ”–MZR³Ù¦ïÞs§>Å|3†ˆG+û!É#j¾š¡Ý­»¾‹¹n6vò$•Q{=˜ úïQ¶cIfC‘Ç4ÝE€ó‘WºçÂ}T —M†î«·Ë‹y‰Ð?ÙLìš{~%8œ´­Ó@ ãÖd]LvÔvØ‹ÿ–‹âòøÈTÇŽI&‘Ž›Ø,ÖîA§®…F)+; hÝâڴײ`âÎw+Û¶ÇM}l“PþáunÕt |å3æor˜Ùíuƒnæ m#£0=;½5r>žÎÉÆ8bÃL}¥|âd•¶@ðß`ô^«~ŸÅ?QŹËÇ œ‚ŸYŠ;\ž‚ÕÞŠjqWõØ…›M¦sKô„†9c|”D–O4ùq]•¯{+»^ÔOý§‚Øé4óIt[ª¥¿ újö¼ªm¯e¬‹,§O¥8Eì,7HçOaD_Oð±|qiÚÃyÿèÌ™Z yfT24hÎ#^¬T_hõм_3…Éã™ám†1úѾíÝèôÑ4…Å"ÐÄ‘àP¿p©*þ£•%òÆ Ó™‡™Š‹³C}¢I/ðî==;sSC‡&fn ØÊ¬‰D«É#9°°5¬bÒ•®,uÕF¼÷"ªfÈâf‡ºRë¹T›\¡µ^t¢\Ž3Èÿ_)/ÚP_Þ( >–ᙃE@Ên¦' k_knþCŸ‡ âcA.vZk$0 ùcøÛ²çñbã×?=;”Xd•=Š+¬ŸèÈÉ ÇQÚ$Eòí¨[è¹;3ŸLdtõ=Õ¼|³¬F)âŠB'‘,—mûªÇ’ÄÏ2Œw»Àæ9PèÊTÛ§qï¡;­ÜåÏÑœ‚Ç$k‘ Þ ã«kýÈ?Yà÷ o×ÑÒºø2#ÉŒÉö†dÔ[TböŠy­Ûž‡È};ŸÇEó2Eر ̰±ÐŽŒû†â/×öVŠþf42h7¢¶5Õg)áã3­ “Oìš/‡$ñ$õ8ñ_û‹XPÚT©V0«'ú˜ïÍÊk|9s©öØþÊÖ+,ú–¹œÂe«÷U5zùÉA)¼Q V×íY¿}I+åЧf÷?p¦È âDã°²Ÿ}êx¢WL%:‚\²Ž¦Í{{h7(“xî‘j©q xÙÈ{£-ïéGF‹&÷g7vïîö VwsºYMØŽž…VÂû¾Ô÷Þ¸9éñ¨¬½D‚-7‚b%™@Ò8¼GÒß„{:Ô¤{…{°ÖàÒ,d£K¸3±'¸~ÊnGa_6[ùHÚ°¾ëuÛé6ô^¥™=…Fù‚"Ÿ€ø›Ð½j²<ÄÊæâŽ"Z÷ª¹ø¤m¤4ë삯ÃUgàŽ^~Fšªyµ1›? Æ v%Ô±Tzk§º›…ó™y ¢ikâ“ÄÀ_mMÃHñɽWÌ’=ʉÝq¡cØ5«Lwis¥ûÅOÅ‹`JßbÔÿîuûjê·ªÓW¡Ï~N.KnTÛu÷>Ÿ¥Í–WܰÅ$€ˆ0/" _íPj6&£|\¬›œÐ° #‹D ÂâÁtõT3!ÇÍŽh‰WRA7dÖÿ––FNN,ÑE¤€Iä•j›[¶Úu§­bÍY»$FöÝUj%Vúõ,"DH„l¿R:ŸY§j/=L‚1!=•z57öQ«}}µÐ£¡ÊærJäJ8Ñ¥b7Óâ¦ãÓo´×­År ïN¶„û‹ÕÏÓ`&)ÿIiXúØ…÷(­j‘Î é\J3/âqb’¯‡¾¨÷ðQµ—ãWO룯޵ÛÒ¹ï£î°{5ñ8S‘Œ¿Ⱦ³Pæ¾ ð,g"2 VѵþêïLhß©õ€i§›ù1ß¿bþêÏ•¼B2áK2Îæð‘˜ ”†"Ûnl…éè @ǘȭ*®ævÞÝã 5¥¤.‹(ô(²åÆ:"`¨l[§6:½×í׎SC3 ­`nÅcØ{…$ÅVmë ’J* nÂ÷ÓB:V›ktÒ–½õ½µÝ…󷪫2ù8.Vgi6D²3Ò4eoé×Z–>šývl)‹L»IÛmw+~ÊÒéSA,Ÿ“ÄÆlyá’;Ç‘˜|Æ2y€^ÖÐ ^¬³>¡8ßOÇÌ,KçNªRoÌHëÔLæ~¡“ãq¥Cgd¢Ëåõ €‘£°P g› ËdËÉ®1 8@‰ û›¿oZ¼ÃÈlœXr M21“r»µ±ªY9Ü\ì¼,Žø”;·›‚dݤzw³Srþ¨8Ùùаù¸(«#%ƒyÌÁlKv{(€¡iðq§ÉÅÈÎÎw9’,uXÃÈM‰Ý¸“í« H²8ø±ùìY²q/.Èd>tØÊûJ=›¿n½ÔFÔy¹¹øèœzcA3ÓHFò¶¿‡á¹>Ú»å¹øÌ ³ª F§ñH~÷Ö1NܳsËü¿Q Œž|ó© müZiLL͉%Ç~%“: “Íså¸íôQ õF0Æã$6Ýžád^ÈÀðÈ}Ò£äþ¤Ëƒ—ÀÂóçˆîíµ öi¯i¬Þ'–1ÅƒŽøÒ 3äšMÝ=Ò;Iîµ_æÔxØÑcÏÊ/"¾iDÆ*Ñ c¶Ûƒ½…Âs9\³Èí†ØØCE+w›ÛC`(#ž%x\ãÿ ÿh£ø‘n+èEþA@}AÿDÏ?ú/÷U6ñ¸ƒºÇøcR¥J±…Yw:grÈ:†ÞÞµ™‡ÜkQ¬¯–{óü¢ÚÂO=Hïü4PÔû—Œ¯™OﱽǶ­8Ü3 JòÔ±°ü þÓÛCñ8…•²$ø7~švˆúªâ$ÜÚôk››“Tºjw~?+g«Xþ¡X˜áW{P©¦6ŽÝ­^Æ6€;è>O'bYM˜øWÑùr)µŽ–ã=*&vìÅ1ô'´±«]«é4,jÁÇÄÚ›÷ŸÜ(é%Hã2B‹ßÜ=´÷ŒV¢ñî—kÑe3&Öçqí®ŽÆvsgâr~Oæl¢T[w8’·yVL§6‘¥_Šú™1 ÷Õ—Œ$eî\HïÓmœ ÓÝL“1™¼èdÆÆ_ É ;Þ[±('á7¢¸îXò37“‹,x¡I\™FÐìݪ¾ú‰Ì3’Ë‘1ÜÄÒy‘äÈ›TmDŠË»[’ ;=8î ²²þ›Í¶?ÄÌe}«í¬œ®_.<)\üÌ£rƪ[ÃÞÄh®«ò>³á s¼“í;KD—_c/ìª ¨ãHÌœËdG™ÉÚid…á >+uíÑDý?ƒ’ÜŒM‚Ù ÄÅv—æB…÷"j:Ö1Öœ¸R(¥}ȳ”T ¤Ò|!‡e{‘“:«I{;¬Ka«@ó£!ñ`‚d>L"'a¹U®MÈöPy˜Yx§'7>\™(e[ˆ( Û¶¨ìµc²r8ÑçÅÇ»["dgAÙeì¿yÖÞªY¼§Çó“¬E…ÕN¬Àw(Ö¹é°yþL4‚ñKMó0Ï#=J‹DªO•칟#.>^$O”1?Qgùl$e%X]ŠÖ1k‹õ'”òªJPD»ÚITÆ…o¶à·¤Òo©¸…:Jî ø¤HÜ¢ŽòÖéTq1lL©9£+f‰ Ç"mTÆ©´atÚOZ¶þa/#âñX¥ pc|©“dj§Cµ:±·J/ ÁÔÝHÓ"ŒED¿ j_¹E©ô@gòq9r\ŒxΘðâäï3 î¬=B£*ñÒ•¤ÃÆ}±Ftiüï,1ÓáÝÙë¡y,ˆßêL£‘) ço:×!Ö3m»G[ÚÕëóI—ÈB—òp~cÍ‘~UTûß\ܵ¾÷}Ú~²R­GÐ?1_–|3.IÆ’1ð ÖÂ?á±ö×z+/^RLÞn]1ÎTMtÚ¨B/ÙZ‡mW‰YUn×_/lÔàå9¾òyø<«®/!fË§èøšþ±kzkÞ…|AÊM0,Ð,¸¸·ìŒrW׺ººæ¹«_eh°$tŠF„Í!ÛpHìý*‚œÏ…¸¨G“›‘©'qH5ømµoö×mÏ£¿•!.b"‚OUÐPqs¦>N|1‰óäº,gà ,:µÉsrbr¸t1«üÛ òn‚ûM€í cÌúgÊúz¼¹dä¥1¯–nJ¿€/eMÎò9Y‚˜Ñù d•‚ ¶ëR¿Õü–Gš0àÇÇD%<܉@ñàJßÝ]Îòø¯ŸkM²+lÐ=—qÛ~úÁ9eà³ÓŠÎÃxøÙDø2­Ák®çÛè"Ç×Rçñ‡è×C¬óCr8ó¢™pZGX­þ¤QêÝÚ-¯¾ˆé¼h_“Äh¸éˤʑ€`§Ä¢Ê¢ç§Zºå°eå¹Ül)þ_ÈÆÌJ…>ëz¯C·Ö2Ž> ¡ˆL©dŠ·Ø›@,mÚZ‰â9ŒÉó%‹’lh6ǽ"‰Ã°ÚnÌì¬ÖP1HÿJÉäò²Y‰Æb0×_ƒæ1Ù6õÐ|„F^Y~s\©[ÑÂJ±sêNÖö×CÕ𿟙aº vbÆqúw÷iß÷ºÕ§Óˆñðxi"•aªEޤ‘P:3óÉÅâ¢Í#¹¡[²Ç¦¾º¹ b·ê3n;ÿhym„ÃÇ øES}Oÿ@Íþ ÿ2ÕÖ ¶,#º5`¬bjT©V0«+ä¡yþ¦ÍXŽÑæË½b›«}úV©YÞT_%ÈH~9r$Ù iy-¶­­tEx)¿‘.‹/à@¨ªª‘‹"è;ª%Ê•¥Q´`ø¯¿Zʲpá%‘HSÖ×Ò„*Ðø.‚×+­Žš{«‘fgëûžåDiáû³çC¡žÝ§¥UäIóz²=/~ßÅj|™^bÊ/V·ˆ(>)A†“áìNÿ] ¥U/P-×p²›‘é&ùÜÝ Ø=¥rUŒ§ ™»·~ìëî¨DÊYÕnAÄ?1Þ‹ýÔɧ\X,RS¹öެF¬ÇÐ+mmàgeTç=~ æËŽ5»/@ÄǹER6d™S³0Ù[vGÜw¨¿®¤Ä”äd¬“ÎèÅtKöhV%ÉoÊÃOíÞº¸ø•sÔãååµáÎ3‚ï"#~Iü/e}6?좣–8qaó((¶¹êl:Só£ßóJ.̱Ýb×ý´9Æ2.+/Xœ«UIýÕÓÆÞ`æü䓪é—ó,.f<ê…ˆÝdÐ6Ä]»µôšœeFÎQC¹M¤0÷ÔXçÌÎÝ×Á!÷¸ýõç"¤ÉŠ@ÿÆ‘Ü{)ë0߉Çd›KýC!%µ#o õ¡2ð¥3üÎ#”Ø:/·PÀ÷Ñq Žáz{wSÅ®/NÒj•³«”xÓådYÕc…l^0ۼƇ¥–úÛ¶œ\±¹êk×am£¥5ÍjÕU`6½¬òÇZš¦Æœõ4E.~–nKÓ:Ÿ|kW⨾—:OÍ‘·û‘ «á\¶Õù³¶¿jòB ääc“Ž‘J´Ñ™"ü,Ê|H=6ÖŒª¯¨q&Ÿdb‹æá8ŸÂ䕸—Ú(—•ÅÌ“21áù) H[PTŒz4>ê€s±Œ¬,i#Ùó€’Äéaº?Äã[vPY|DñK…,ŠÉËò š—@¾aö’ ûj8.w‘…ãÌšhääÑwH$üqÓh²èkèW-sam;Ö!6ý-bÅ-ëÒ>BE$2–9Pw +>¿Ý®~O3*,‰ð¦Ÿ*|8ÐÇþ4‘Á,OÃ~´FD<. ‹;°b36ß-ï O)îY‡K“jÆ <Þ#e>68lƒ »äK±Å±Km-б·Jd<ææEˆÀÅ.D1Ë00 qø¨ L.GˆŪ ®>xå1L +FÛíp:îì§âñ ”²ãfFËbaªIk’5kí=êk?;“Lg–Žù"#""îÞÒžW­X!bŠXmb+Üm¨ªž‘‡'0rÈè"†9ÿóQ7°o_‹Z¸Œ*l’£i[áE,}J/N yÉ|®:KÚÐ8²6þÚÆ3“ŽrÈiÉÞíuð‡o3íÝÖ‚—õ²6i ,ö=‡i°­†‹] ön?²¥¸öxdž9•Øž¦ãQï5Îùrú¸,èœGøÿBŸ‰ãäp·©]ÒÆÂý£x×ì­qX6£¥qXz™ñò]CÄ­#h«´–÷!5ØddË&TͶ»7  éëªÕ·'‚j¢ú¿#+‡6–GÈO ô ©¨¾›ç2332póü9 Þ~:Èœ°Ti¼OÔþgÏceÙ8~t‘vy±ÆXÛºëjp ç8ɹ{Ž„#‰ac$ÑÞébeúT#éñ‰õòqHØð¯›<Îw ×{\ô½&úŸ”l\ M•É´†0×ÚŠb/eýfáùNMù\Ž+“òÞh£«Ä,íu?Þ c—ãðrœMåñ?91‘¶Ë)`Š/kmA×Ó]–TsGôܱHf\B­c­²ÅT è(y9ÓÔàäI0Aæm 1 F§×ÐUyú§$åòyŸ 50 C–uMÅ­»´Ñ1WÃqYmäÄ+øÇ›•‘ºÛwuTb« õÑßöÖ^Sò™;[%¦fÃbm½ðëê`j\o¨9Ió±~n|\h%tÿ—Œù’¸…NÝööÚ­þ£åfâxã• +È\F»ïa¸}:ô¬cËãòWŽâ ly%*Ó´°¨;¿Ô–àt¢ãâó ¡‡ŽÍIBY¥19b÷.zxEôiËýI• ÑqüxæÚ5y時ÇàÞ"è¿§ósò„ë><í]‡•´7¾íµŒÿiÀy BÆ$JÛ4€yvÿŠ…*Æ ús†^'+€r¦³NÞžÄõ-[×ÿróó"äD˜˜ð¹#’DÞAíñ8?ev n Ü¨é@%_Õ_ô Ïê¯ùÖ¯1¿øÑQ~ê£ú£^7úƒüËW˜ßüh¨¿p¬ZT©V0«†åLG”œCðoc§æ¿üW®æ³ì‰ryKÛLªG­Ë¾¥Î½£ñm·“Ͷ2<‰9$2kaÐÚöûè6v&ĂҬøªÜ¥”ƒéÊÇ&K†øñý"úOØ+š–ÖNîJèëñ†;)'t6€?ìSê£æ‘b^ 1`ôz•B\íP (*¯:RòÈÙ•v'¾ÍöÖ^»GDýº7ÖÄü|^s1±;>öîÕŸöP9jÉÈ{ùŽå”þÈÌz…^áÆ°dùKжþÁ*~ñT¹Ê_ê&OÍáØíTá´ò¾Ûd‡<íªÿxã]S7_]áPàìÛ¾ÃL•‰Öc}û[×sº™»ËÉÅ+}è"Óí«.qY’:¨»GÛôÔ²ë9£ÐÿÕþè´ïážž\dÛÐV¼Åkb†=›‰ö3XáO¿C©>Û^˜‚ø2mèƒÞÇ÷Õ8ž¤ÿ97íø¤G€¤d¹=KokýÔkH¸½Ç¯¾‡ÅÑÜöEËÔàÕ8þÔpòýïÀDÚ¼–´Úrd‹â65* B¦ÔðÖ¬Òt¨‡ZqkÔSÉåC$¿‘I÷ ÆÕ/Óqø¤~ÙÞIOöœÛìn(N2#ŽÅ‡µ"@}{EþÚ,W!Ü &À ’{ªÍs¿‰ÀŠ —"vØ$ø”i¥^Ë’7‰´WR¦ÝÌ-\ìOõW àE‡ZÂ6C”^À øw @¬bI~¤ÈÄüþL˜åDš(Îá庳 cïøN•fÜ–9ÂLØš) a6ÿQ•5î¶ê­Çâ¹/2Ìâ²æËÛ"†4‘/ÅQeð¹K'¦LѼ^ÄØáüÄ÷k@ÅÇ%ȶ¡\iržV*r,/v qù^[-˦ Á ²d4’ Ø.À(µ]vš¯Â†h¢äwÆA“"gŒƬ£iº&'ÃË\Œ(s$™B‹.á{n6 âæâ1yù #…qþeävžcFÛÚ„Âú_âÀÙÆ\™Dkúr¹Ø‡oÀwPRñ٠ƈfÇ”ˆ°a"4ð³IÌÛ/c­ë8òÿPH«‘‘ã±Vi6ÊÊ{mqj¾ůü„~V7,L× •›3°B]7ìq øñc‚XDŠS´Zæ€I*«ê{ÿ!Í·äæZµ ù|c•Åeã¯Å$NúÀ\}¢ˆ-0ìV)2ö5i#6÷Õî …$fò? Uí! A§²«b&|\”Ro&ÖÔîÜôõÕï1°-·t²ÞßýFê{+’ø¿¦5IOFZͨòH^Hxü;l’y¤3¯âXÐ>ëûÞÚŸêŒ>G‘\n?†RÏ3yv*Ñ­ŒQþ ÉA`11ãX×ÿx—vû«j颅I~_Óܾ.^')OÍf‰£G!l¨¿éø{)°ý/—?™+©ƒ=§’\}lZ2 ”=Á¯]yLˆ~›hxvÂ…–|HI,ÖPŽdcc»·Zèx>¸á.FLž~~N³ËÔ[®Õ«jT sOÀœï©²ró`ß‚¨¢2ÆÁÜ*kjGZ®Ëá³!‡›òq™"žH£ÆE‰/áQÙ]µ*&8î…åcËÆ™°`ÂÇŒ©“p3ÔÝ÷IîµY}]…—Ÿ6$M+´ÊX¶§ÑWƼ5ŒqÈ·'9N2w5£÷•*Ptp½•oô¿7òåm$|(«{.žº¾¯+äù%æàú’|ž3ÌÏ F””Âæ÷úwÐ-Æs4ÊÏÃ2Í7”6-ÀvVRvl7ÐÊîyXÆÊKKÇ/ަòLwî#·YçØ+±ã22¤’|iq4¤GŽìoæ*ép£éP [õ ÝÁgý"}ĹÃ7Ä€÷Ƈü"ª¹¥Äf©í‚O±I«$“Åá×ÈŠÿÜZÀ ¥J•c ³,×0ýMŸé$®G®û‡ßZmfÄwúŸ1Æ‚9wnÕ?}-þËHü}c¸NLÍáŽ/õeø„‰Ïªš‘…Q^ôõ$õcé4Ø™S‹ôÑü>þ´L`*–>¡\OýIê§9ý@ÉC ?b _ûê©V̧½…Ϣ⬳îqH·VAoFáU®£lÿR×Aü]E7“݉Ìþ•’æFØ`Éìˆí“úá>ãcUsÆGÕ ~ÖVÜÿe[@VU 'Ã*j;ÃöÐDǓ”›Éø¥=æ%$í)½‘à×òO‘J«ÿj¿–$Ú0C–ÕwÏ‚òãÉk«FYÁèUHÛ@C‚ù!dÚ2Ê©+–U#s(knzöUÇ1 ü´+ñ´Aã>YÝÑ5Ôz«·©ËL®EÞ4d-½6 _Qaj‹t˜¬¢Ö3m#´Ü‡e…?ÈcüÑ)";Øjv€BÞN•*nãðÂ#̘n(|^D}\þ6ö )Â~ ü•/S “Çæ7•+† †¹Ò.£«$€\£©F±èlÖ¦aà Œ¹C»G¥Ëub6(7ضÖî|6éÝÒŠÊÈ|¼Ü™&M“Bæ —¾ÈÖÌ‹ëÔÞ©Çw*½>^4“´¹"þŠ÷oJðm ëÐÕcœ[@¦Þ½&¼ ÛXkP¹÷8â!Öi#ûÌ(¦ ù éS¨¸Ç•%a×§Zû_Ôû×™¡€ƒ ÓÝNÅ‘O "”nG‚ J+”íÊò)Æá¶Kíp‘!!C;(,zóFU5‹.VG1dLZ8榇^¾ŠÆ9ùy~Ci–Ns GLx£óPz ÄÑù<Ç5åù¸Xð¾&ä2݉—*›ÕÚn5âÃõ ÃÊ‹ ŒŒèeÒVõت-ï«8íœl8Ú¬/ %®±º»·¬Úô7^S.\ùcÈl5÷F¥,a, zÉ©9BL7²¨p1çžÇBL;,=GuCËAË>^.G".‹*;L|($Ûf°ëðÐMÄsçÍ—”™R¾,ÐFŠ6*<›mm[u¢bL|¯¨§Éƒæ›oíÓ2v­ÉÖ¢“’çògÇ“ )^5šfñ-Š“f'»ºÀúwŠÂ’<„‚ù)cæ»`ÖÔmB`ý5„RlœìQ.[Ë3…f6±v)ÐÛQ@ůò?ɹnËvÈ]ÍΣAEÐ|Q›ä"c 7PWåÔÜ ÚÞº&YR(ÞYÔŒcܵŒ:•r¼ï5Ë*¨ÃqŽXF…’úÛ•®v÷tªé¹«8ø’yf¾…YVââöðÙ¯ê¤\´q]µö.¸œxâÂ`‹}¹S„a©ÚŽÁvÑÉ>a¦m–¾Ô½ØÜî7öÕõ~xQâÍ Â(»c”^Xïù›ñ “ÖŽ•q^t¾BM#,ÌEÀö÷W?5R¶÷êDi`Ü€ËÊÍ^W‹o#.%ØC|3'ånÊ&«<µÛÉáM"è싾?XèjËØ<#±EÍ4d±·í§â·.·…_öÔr}SÀ¸¹ËT?•Õ•‡³m<ýIÀ€ÏEcëýÕQÄÊ{6,DvZFõ iQ§Æßqň“Þ ÿ²µ¿*‰á; ŸêÞM†Iê#Ÿøi£ê¬4ÄÇÊÊ=ž\FÞöµ]c…# ~ʰ‰hhu?-®þ×Z÷fiÖÎp |NI¿æ1¡ûZ¥ÃåNTØrÂq² ÌŽèćvhëÚ€ÎÀã¹ú¸âYÚ%VQýqcTµ•u¿#»˜Øwš]EÇJ¡o¦ñ€¹™kn‘¤Äê½ê9>ž‡êåeäXiœI>Žê›æIçåÔÐ^I<«*'õ˜/ÞiG<3 ÑH²ÚŒ}•ÿÑ\¤òo(æâ7‘Æ;MÍÆbÇÁgä¤-óYQÀ×áŒ1ýGgü¨Š½·°ª€ë2r±ñb3dȱF4ÜÆÚžÁ^G“)´S$„v++}ƳLœ¬¾c(ÏŸ8X—âs}‘¯r í=ƒ­C”¿+$¦6¼*¡ŽšôgR½¡Ljú÷W‡N½+9Ë9÷DÈȈî ÚV*-Ð{ê<ˆ¥h×ÌËžD=HHµ®M&ôWÙ¶tÁÓýOõ$s`c:Í—:2¤Hñn—·A]?Á¸ÜFÀÁ»À+&ÉX—Gíy-½¿ ÍØžëÖ­Ãñ&Ö¾(“[«Ѡ;-ü=W÷TÓ¯ó‰âe³{éð“èat>±CäÈÑ(ƒeqï”þúkN¥´®ê@@n5Òì{;•—q-U±Ñ¼í$fSÄ …Uç‹uÎÕº9&úëÒ›²‚fºmŒŸÜU‰KuQm§`Áòh©°)]Äî6¬6,jÉ[‹+²ªŽiÇü!¤½Ã“kÀ¹õõª+Ùßm4g%¸­Ä•ŸÓ¿`ž; .y.DÜ‹vÄR¥ÿuƒvþPzu£3p¯èj³/‘[1¶õBÑq‘’ÊA 9þÙè¹íÓYUI¹}HZÎͶ{ƒ‚‹0Ãê +‘˜GÂÓŸô¡¿tc_uP}M‹4Yù¹0¹ùv(fPmiš6 {—í«Ü|Õâxy2rØ3‹É¯Å,®N¿Ún•Æ'!‘'Ÿ,²iKÍnŒ\°Ôz‡geiH±/!XQm›)[k@Ìv‹t°kßE¤›•†E ½ªn7hÝ)˜QÄL Ȳ(B,n ­­D´Q¶.N·PcºŸB)+ù¦åNŒ7üzÝ&½.5#7½©àØZ¡€²n7Ù,ˆ ëµZÂôû×mlšO¹Ájº¶ŸL2è°ÔwKSÕ´±éM°&˜XÉqR™xÉ…ÍßR|¿ì÷UÔTå‘iøâÚ˜å[èÝU¡Ai·mMñÕø\¶K£-ÿî‰N>Ol±Òÿ¹¥?ÿ‡®dý€ÕFÓÙH€ ³ª;Moj½ØÞõû"ÜýN#]óãl[…d’ÆÀjª>Ú½]@#¡îÖ¸¡JÈ‹*1øX]MVÉ!‰LY$Cå¤r8UK›/RåJûà·î]Ë­TšN½Õ“C¼²*Ô³ûë9ŠIç“lÙº•¾Ó+ÛOmˆ³XƒbIc þ"j>☃£Ø´nmk†üß÷fÃuØ:“§ª‹†h§‰&…ƒÅ Œ:k.ÜÁ‰@µì;š¬¸Îs7ŠvXm&) ÏŽÝÀ,cî&Žð>'Ðì&ú‹‰ÇyS'!bh\ÆA¹$­¯¢ƒUùŸP`òF>?s,’0y.¥Wd~2­ºìÁC°-Öýã³²¡ËŸ>9Œr–%o/cøÀ¹×öfÓ쩱32réeb\Xê _©ý”Ø["L¬‰¤Ü>JòI{|NìÊÝM×éQÊyU„°l¥LIÒI¢Vr˜´!ˆÑ‹·x>Á £ÉI³ÍÀ1Ž!´ª¯Àö®Ó§‹_áÒ”nü¤¥);ìFˆø¬*÷’iИ²¤ÄÆœr$bZ3ÚN÷÷w <’ý«ó-ð~­x1’,ØÌ²Qd”¹ {VÛs§gZ¶9|[þ±Ì¹Â_8O#™OA!ñŸþœ[ßßjzrÎ<"y¸ÝÑ­Ë›°»w;h¨¸Ü±Çe†U ‰Á&Ev6YG¢ú0ö×SON=R'i«ˆÀÍÇÏÇ\ˆ„èÊ~$n»Z¦2‹íA½½¶©`0¹ÜˆD"_-~Û€·¼µZ± ÿ¦§¢/SC“‘ÖRÄjÿXBÁëMœïoȽ(Yù"Y|É,ó? o…I§º†æóçãq£š(Ã]üPþ&Œ+3÷‹UêÒM+³y…&!»e)âoeì*{lÓoÓ?7úý!ë]Îb}E<óÏ„yvUÜÁEΧÌa{[¸\׸|·,“Ë$ð‰ hâ@±]»îäµ½tر‘U¶.ÑrÆÝ¤›“NlwµÃt즬UÍR+íV2›êlh~l/Ž? ’>€¦õ&?&+¼dkÈ1“-åÑð£I.“D®:x€?}3|¾?.1òEÅÜ÷~²Æ=øþ‚«NIÃ'~(ÊЃêî3Ë“ŽÂÀ„$N#z “ßnÓU<öøÇŽSç."ì½7Lň‹è+ºÏÃÉÉ– †Xd(½´*TÛß^Ž;$3Of•€Ѷöÿ1 ùlŸÛ ½°N#*ó¢È‚äíp¾±þÚŽpR8Л¶Ð§¸m}¦Õ*!=P¼dúc6ûª,¯õŸ…€mÒäþÊ`ëY¬÷H¯È—ˆ…šGô[ís†ÿ¤`öËŧö²,ùI@b·%@kÚO¦µî$â°ƒ ¬ ˆzƒ±j«C›“î ¥J•YÞqŸùŽk${Òf&ß`Ìû¬zŽ—µh•eËÿæ¹d$G渰fÜúmëlVüžÜÛ:t#äàN>%Ɉ—””7ãݹ÷zŠccÞ%”ãÄ…Œ$¡Îºû«Ÿgù5)•âü‡ZÙ=Ö™ˆèÏ~uãHÚDÚÒ(a®ŒKùfǸ™áŠr2!m²[à Ô0îaÚ(Y°¢i^"»–0 Ž„³)½¼'PÖíöÔh`lZogvï Ú캖ÜQšáêtSò\ªò¬[¯ò×& „ÚöøNªÃ½Oh¯Y£‰ØG‹µ…†”ÎàåIiQ·n²Ø,¬Æê*i¸èž,Í2ZÈuBOM_¶§ 5ºk%÷Ù§±Öñ¦sñ1¥ÈòsC èÎs!w^ˆc¹·Kè)ØÒ®HŽRd@÷€p®½Uê )C‰`3B¢8·+4d˜À`ã_iÓ[iPM.dR:†U ‚¦õ¿Sjªª|{¥&­äå­¹-¸ï_MªímÎò:l7ÂÈÅeÞ˜Sþ~îLò*’Åuµ•Î¥‡[ÕnVvGšs¥Ç´`‰|>Tks¶ÝÿÜ((¦Y¬“F»Êø%hîGÂOì©b–KÆÚ´q1ý®õí“[íêõÓ{¶ê¢5ÿŽçÐÓ—£Ô8â7%2e‚‘©/åuÞÇD-p,z]Tr«„ß-Š/+±²ø¤20¿Mû: {ªÇ7––¾2·œzÆgþÉ]Àzζè+Î;åqAŸ É.KèXC.ØÁ×bn[úÏm ^Õ[šoµP‘_µ|Yä|HÅãâ9s,1” mɹ®@Ú~-O·ÕBñÓŒŒôÛúqƤ!âm¾ºô¸:ÜQ®lò™Y‚ª7ËDQÔ1±°%”½{ýB¤â!D‘E1`gSe`Jøï~Ò@½%¡»eÛè6%tH¦—ÉÆÌÉR… 9×^ñ­ÉbÎ4kÛj?ÙLÌÇxòæ•vƒ;Fj¸õwÐÍ‹ †ÒÆ<Ænzw(ûÅuW™Ôä¿mgŒÏBÝHo„†º•ìu¡1áò°¥$y¶)Ú††„|;´©à•Y|½¬¬›WkjĶƒ¥õ5~>eyZAÏ~=ºd˜5{××L'km‘]ñ+/ÚE©™2ªyƒ“¸°[\(ºß¾¨î’™5ik4¢<ÂYÖLŽ5-±GmÍ<³N$BÁ5ó¨Ýmʧ[i¥Or —¼&ô\Þ;È›Ê:¨“:mí0y¢k²¬ž~ŒÚê}Cº¹Ÿ5ão¦šþ:ÚÞå)þ°Bg¶2yw"pEØxšÇMµRªÅå¨Ñ@ ìÐQsüËʲL£´`.à±³ƒ#mW×[koP¡$Žf„ÎßîÖÔYëƒÐ맺¡ÉÉk:îJ»z\§™|ŸDˆXåZþBO¬)Ù6ó4k“ê©¡Çhe‘Y[Ηõ@'jp õSñ$Çsæ´~dªBå@B@k*šFòßdZÖŠ*¥¾ÍÊH<<™nÑFJµ¬ç½k~Ê'ù\¯½¤xãܹ6 zê;#!£‰iS b¥Ž–Sbwz;j¹óØ4ДœnÜX _§ï¬·<èr[›–]RÛ·U3£>\‘Ÿ“Ã"‚lW^„\tªœy9–Sàœ:º·ö—²¬q±o8˜äIæ6I,„¯]nVÕ;q鹑cÜÈAð‹Øöxª•uR~K5W|¾èXùS®Axö¯ä 6 Ödz¥z™Y2M™FòÍš±¢•[†6ØnVÛi&Á–I+ì?>íäW¸êÉ‘Vß"ºW°{ŸPõÚ—m^cãõ ³è3—gÅTåÙZââ$†¿´øGî«ha‹!@íêÌugcÕ˜ö“@|ØÂ@$´ò°D;T€·ßmþ=¤š<^ô¶PÙ~4£Ä“Êí=µ*&ÀSou½9Ž´=’4`T€C z]IÆ}E$…1sH„±d!¼71èÊ÷Òú|C¯¢£-ÚjNäò›;)VF‰Æ<ƒuìôŠ¥TâZžÄyV$è Hm¾2 €†¿´TÖ¦Gq®ØÕQ{”áOªª¤¡(Dkšyåçñâ)ú8𳇦K­´RÇÊ$I#0ÍѬt:«+ 5cÍáÍ:E‘Œ»²!, ¥$R§ÜÖj­‡"M›e°• ŽP:oCµ­èí©ò¬É~'ˆ^d¢;%éä  S…š5·SAO æ[‰¶l|½×µ·zôõÔऒdD…¯ÐÕfcʉàÝ¡ñ„±fB`^½*Öf´ 3Áùge¸e³]>;)»m¿m«WS?µÉÆ1[ì£s k¹ÄH6&F/6S"/ÌF½ÆèË«€ÍÛ¿]jº||ܧ\œG¼1¨hÈ`aU_q¹³^ç×zdc6bêƒ Huce±Ó×è4cqùiŠÏ C*,¯e ï»,–¿aº\T•3)Í­Ü}Ý#‘ ˆb7Ì2Ç»@è`31f¼%íÙÖ½ŒyûÓ9ìpªQ£Òú±û¯UÒdäJÞlèžs© Q„2Ûu ô " ñC$’'êÊíkzbtSVUÇp6¡§¤Èà¸Ò+.TDˆÝ”„ í6±òÔ©¦˜£ÉW0™­bÆå´ìÝٽ¥'ò]dg`@«å¡Uê<7gÙ¯[ÑrÁ !h@ŒäÈL²ʯP=uR×­[OláK†D˜Òb¶“$%æNݨºƒcV8OÅñóäÊeGL’—]ÛC^ë%»j»Ò,¦ÆbÒ©/ 4nÇÄwƒÖÝGJ±Ç€åC‘‡“ÈT2E3€Î¤ *­Ô_B+—–ª©¦¼ã´àµ¹]ÞÔç ç¼d¯“+9DªèÊd’IYXÌX0@ÍÑl/R‰¤Ÿ)š@؈ì«(ÙðŸ Å­Ò«~CÉáâ™U¾jw– ôÚ­Ðìלv42E'Ì i"mÂÝà«~Œ[í{tøŠ]’uOè{kâåÉÌþT¦Ä†ml.—Ûë¢ ð9ÓÌköþ¥ɨ†h&ˆU6²è.¦õdóxºkYL<å~Å)‡j¸ô¾ÝÆ©Aÿ†ý×¹ÿõPø¹døÎä+Y–àe î³î¢<ÁÚ5 å}¼¤2tÜ„ê‚S)®ƒZÖž–_\ÎP4{HpćmaÚ-´IéEÄ>Kæ˜ÞKxÿº‹è¡&ÉWQ:´›“`ö`ÃZ~hlíÑÆHH€v#¸Š}­ö^„7¶¯ ©¢•µùmóÈ›içÉyR1öîV7 §²÷û)é8I™Z9Òt-ŽÛZäH·- ->ÛԱ˛ç@ku“j„MkG£­ ‰\jŒÛHwuäMÞH`ê¡\Ü0`}õLת¯Óàr^ž¦ßª\‹”Ηšq%dŽæÚ ’G Ôù?PeEœ†&0ûX})§§¶ž9¼šÂðÆ@;e·ˆ&Ú‹îéÙÛLšL4åcñI˜ÆB¬Òà„#íªÊy𜔃?H¹menŒî r[KwT0É$8²$1šiªÖ‹}¯þê±\,hd|ˆ!Q¿ SdÚ/ª£vëÛ­K$ÓX*!Ú¥Š_Ø R:%…_7ä+½“”ž4+ïÈrvÈ%A¼cw„)'T@ÚtXW¹Cò²ùplXÒF@~%ñ"®§øE˜ÙrL*v,[ÝFµ¯fÚ #×QÆ™9R¡ŒÏ¥ö¡ÝöôÖ·k5KàeÉ)͇ÆJñ¹YÖè„— l='q¨²&thðñõcá¸èX »ŸUMÇá¶^YÄiD¨ñyŠÞÄ*t¿´UÎ7Ór4rLß:Ì[̶Àè6©é§JËŽo¼‹÷mOJࡉÕb“!«ÓbFÜreøß¢ö"Û¥!_’÷Ò¤ã¸×ÍLœÈÉÎÇÉ?+7a؈¬Þ­C®­~δoÒÜ€Xbš/)ç2M ›®%%‹‘nÃcîªqêO›DtqhÔÈ¡$ Pn[P Êæ6>R€^5ðÓs«i¢… Êáœî>|U;^Eý3Üêw/Ú*Ç9Éår9Œ'C‘+ £xìZêÌV×Ûøuüµ&’È ”ZS (=w´`úHÐ o1%9Wp’",É)ì:wûêÚAÛµÜý䀾êç³q èª[¥tŒZ%ïª^W‡IRÁvȳV·”Š;ƒt«eb - ‘Œ’º<€1ŒÝI×iï©ÀíHØ™„JdÐØ_×Q¼Ò™£†$_nÒmpœØö·JSÈ›  #ÕQñ8ÙY“Ú›–òÜIáØ«a³¿qkSUg ½¡8žd.;ÌZ&HTº1`4OYé@ä36 ¤ˆB<†1\¸;÷j²)Qrtø½=h¾vXfÆò4i¤Á<{¶«X•òü^Š¥†LÐbÂR9·yel@ .tg£ÓBÖ¦]aÏWüä|ÔÑ6ZZãc4„ènò£îÒ©`wT^ѱïvEr&DÙ@¼’¬`w¬*{\Ô/¶8Ä*oswaÞN¤Ó-ë‚üZy óDÞI!.Àµõózý'ºµNþ‘ƒÿùâÿ"ÖPeó!xGÄÌü*Ö l Úµž,ÆbÐC½U*O–%4J•*bB®#’ç ã¢ž8Ø>až{F5Ûú¯«÷WoY5ÂgÏÊæ41Ü´Ò5‰væ%vû*|´¥ÒWxV«:èT§'"»³"I¿V݈ÌÚvžÚ·Y$\8[~ÈåO˜H>\NŽ»J H½íë¥ÂG òŸÔ”,–ØÅH^½j«/3ŒÈ±ÌÎáC/p°½ÇשּׂFÚì7ÒnT–3ã´í<Âf;»*ÛEŽâËØ×¦C¥ÌM¤U%µÚ¢ß˜žžóN‡ÁþXð<Iä, ä–ûõë{5¦ÿ0Äò&w²@¡ºùŠÛŠéê§Wô¸[]p¼Q+&ìû6þI‰<ùŒñXo4·}M•òå†%S&0vXúy ý—SÐQæÇ‹€®I”IcÒ¼±hš§ ’3êÛfû 6ƺ›Ý´Î>D¾W–™ FoÃ#8é×Ä|4ìLuÈ™0•ņ&fºÝIìõÜ›Qnf-Ç èÛ@b= £kh©09šIqÛÃ##yÕanŽ4ëB?Ruã GdI²(B ÆŒS[iøMy‹ƒ‘’ÈT–• 9GR ÚãÂã ]*?1†ÇvÜv›ŸÍ6÷§ÝVRJðº|³&,rÊ÷*ƒ¡ê}·á­µœv7·G.^5ž€y)6cäù3]ˆòÃXm?õÖ§p63I¾LÜh‚–‘¶fmâ½Áø¯^E<ø’/ÔÜ@$¼Ñ9 ™( hØ/VŽþ5ýÔÊŠ½ß¦"E·Ñûn-¹8ê2lwäÒY£‰„©!e,l¡Uv»YMÂÔ¸V?)Icfo0mººÖ]¤öEÈ&<ü|Å1T0ñ[þv^ÛPÑ 7dò¤…ãŠ;©g7ü`ôSnÊœrÇ:ã„i™•<¹/µWáó,, ö÷Ú¨÷'.>b½ßÜ¡¬|ƒpxüoåXΙ“I¸*¼¬¡Úämô²0òb›W„´CYRâíÜ pÃîõЙ‚1äÌ”˜Õì.alº \\ú…?ù¯œ$L|É7Âà릾úÛ“RŒ”å1ÒŒfÉ2E™ŒJì.Î-ÖÀ‡éè¡qÞ.9Þ<,©áŠF¹F‰¸ÀÝ”F/?” C$BMNã ñZúëè¢çæ"R< cÐýôÊÐgVºÒ_‘ÈI“!‘jȸ̷ó_­M+ M䳓bWH É_NîÛ*.Z(–„"ºÌ¤îÔiöÕb["&ñÆãhf¸g²“FgàgFµZ,sâ‰J*¼¤ke‡MzÕ‚›€lEõ±ëíªß#)œŒ·IÍ–FS·¥ïq¥è˜Ú[mIÖcüVÿvŒ®ßQ2rœþV#‘:£\‚fÔBôk|:®†Í Ç –ñ°ð{íQ¯}uÉL‚)¤„;AÓ\ÿÔ8׿0v±òæÝ,±v€Y_üV¥½S[»ã»Xï€ćɶR¬²·ŠBÀì^à:Tˆ¨ˆ#QdQe°wSçŒî½´¤AT¨*&yq›rå0éù÷÷7J|9Žð™£—Öhø:¯´Z–xüèÞ3¦îÞã@DZThÈãÛfoØÔëê%—É£@ÆÈ*ò"ÿNU ·î"ôùfŽ1©×¸uªNiS“>¸²Y{m Kù­Vqã3xåÓ´ŽÚ7ä¼ì¥fÑ2ô9 ªçñᛳ£V‡8F@23tI/£ ¡‹+“’ÊÎC–@´fÌ[] Wc‰™Šø’ÜFÝt(ÃUe=à×-™$Ül>_!n<ø×Á(µ÷õ¸$jE ””®K/º q¸™p $ÐO+æB¡ '–Þl Äi§PjÒ9–xĈw# ƒÓßÝUè"þY2DlB¸`E÷hOÛ@ Õ^8âÆOÌJIÉaÑ©7év](DøSç: ä§…²GËÂò ;²H6+Ûn5Ûá®#œù®:)pG“²I­šV[DGOÞÝ+Ž‘Úá²ÔU%vƽºQ¿Ó,Ó&g†e‘²C+~£©¶â÷ÛØ{:^Žy1|ç_&(îÃÅ»´_­Ïwm[ãHVLfWI‘òN—SâÜOª¨ù<ß>Bäþ„zÄ…›ÿ4WÃ~Íj +?·Oîþ„«WgªD¦ö%Dw?½~ÊÒk€ÍæWù–TR 4sHš5‰ Ä ß}¤j¸ŸÅðŸ(8q´]÷±I¿²Õä‹ “ÁºFÇäÆ8a#hÒ@zè.jfÈóò£Š/Ð*¾sJ£ËØí §Oi¡eXór Ñã±e]„ »lX’㥼W5/mæm%¹ùW&×Z.;W­H²s‰ˆÁ›¤_²Ü‰Yn>ʧȓ&ÈWs’ûZÀ V¼¤xÀE!†Ë´R>ék+¹dbTõ¨¤‘dT™£e,éÚSk-šš”ªÑÆtñü·¶,·8Ö ìy£lˆÛ,<±‹+> : ·ÓNê´LÈ_$´ÎP¥‚™”Än H¸Þú7IË6'•,ñ±Y™™‰t ãPm®½-ê¨ò ÇÊÀß$ëæ…#0 ‘36Özë´ØVµ–ï§d¶¬nÔtt³¼9O¸­BŸ€«½ öš’\qñüÜq¢Æn¿Ç¸°=mz¦‚5Ž6o5r1£³d¬7W ÇnŽê/©«>"I·àdùØÊ—(âÌ·$lek€t¾†–Öª–Þ6¶Â–ã´>)Úb¤™%Lj³"uŽ¡më %ÃÊÆšL(æDHC|9Õ‡j±ï®2‘H­*9î¾b6Ó`¤Ù¶oR4üKUY—<¸ÎUÔI°°,‘0eïÖÍF—­—¦ ê×Bä9m"ÉìqÖÒ0%pdê=•4¼v.À32aYñH€+ŸA±쪉¦Ê6ù‡î®æ:‹ÛïÌi!"9%]ñ+ê;E<>ÿ!K'†|È‹Í'aR×÷øEI‹šsÝñà€A ‘Áñmö§¥G,„“œr±¢ó¨µØêwîÓ¥2qÆ(åiA–wEŒ—·ºÔ+YøÆ“rôY!šPrÇ`«æ'K® ü'÷Õ¢ÿÎa>)ðÈè;Õã×g°ôôPÜi–å u> =æìCx¿R' ÍÚ,lÿ ôU¡üÆâæVµ—Ìåd&`|w‰Êñuëê¢9V|é•aSý4±nÂ}4n|Bi×4 ;'–Ãòº›0õØûªH¢–†·™vT?˜m6X=*nÑd»•öï¶í[Òœ¿ ŒhBeb㓽ÚV¿VT==@Š»Ò5°éTøîäRPáÀÊ l¹Å{›Õºo±ßLüJþ;N’º·¾\XbÌT•ãHÅü9ˆî¸öÓNP“(fS(FE:©ø ¯uûh̨Öd1¶ªÃ^ð{=õQ"ÌÆX¥ I†WkYÆ¢âàží)/W÷'æ-ê«m5 †a‰ÈC“‚ï&¡Æ–Û´ÞÇ¥NùŽ`—4e Üò;™œïÜ<]A ŠË|€©Œ w+0Ü€x×­EåÆ*ÌZñøJCPA'¥Nõm-Þy]I§[Yâò,–cYG•º#"!xÁÝæ—`NÞ‚™ŽW¥…ùr;Õ›R»Â5oßUÉ 2FEÛù•³NÃ{µÉx?Ëã34Ìðù…vù»»7-½U¶ÙÖˆKºRCù†ñhùAò•Äw‘“c¯éº…[.Ûƒíôã‡.I—W%H},·q ­”¢%ûu¯cÌÆ1Šm €I'sY¯ÞO}GÊcL Àp ¾O ‚O£q©+rW“m°›Â²éæM«5r?)3_''`±®:«„, M¥ê­Ø{)€Í²@ÿPC\êÁÙ‰×KŠv|¹rrdd ‹z$lȬUÑ|VÝm½{¢ŒÆÁ‹ó&+åÅ·ý¸ ëpÆ1ÕE޾ž•Õ¶“OÕñ«ŒåäÈ2d`HǦõ>>@–̽tê ;' ¤}Å•]ˆ9cakµêxðò1!™Œ „î` …õ©CíiD >k!@í­‹E¨L̇ÌåðÝ€B¸ŽÄù¤ û*Hxã”P©5ß¼t·~”6L~W/€Gü© »¨¼„¨÷-i´9Ò XvP£!9 bèvb²ª6ˆTØön¿ª¤v×Z<íTkôݧ¨ný–öÒ#¡áÉ´{>꨻͕޲¨$v¤­óFÐÈ7n!=çMMW”+*ž†;ÞÝ—$SÔ[½—éY|ÌŽCø )ýÅeµ]È]ßËúú £ú^ë'"Ý­äºë!oó^­!Ìi1ÙÜ"µ‘».EÕZª­´¼w†ÉÎ+îv§¶¹?¬óˆÉù@vÅí×t“5õ(«LþHà(y¦f™Wzž&$žÜ=uÎýD¹yÊüŽJÇe@!BX±ÊžÛ7e R•˜[w I™‰„Æäs > ÇxYf™”²××^ iÝT°ÊAÑY†ò;Gb_ÝR,ÑM;NÍå…["_Ät¶•äq®TŠª6cÄ µþß} eGVtG©l{›ÝZø­'è¡-RfËñ·†}=Õ6\°W»BÝAî=H©reI‰p?åàAÐ3tŸ óÈä(øox­Ñ¶¼üêwÕRñ·í·KYë>þ œùòÈVìhÚÒG@×J '-ärzó÷ëj iSÍ»¶·ÿNö>âkÕ ÊÀØ›m½þÍ¿}h̲[’QUÙä»™tÑN€ŽßEú{/L‘<ˆ„ 7}¶-èѨ†¼oi:²õ6ê{ÿÛ@dNöÞº©>&ô÷0¦Y´¦u#b¤2°¸@[©ïÙÐÖÉÅÛùfºy[û‚±¦&CyP’}¿¼VÉÄÒ°¿ö"ÿ"Ó"7 ¥J•EY¾^Yy<š5ƒ¹lÝ ‚mZEfœƒd?#Èy$$§%Ñ\<;š÷·uºÓS^ârNÜ8*äA‘9‹/¨Æ@NÖ,u³vvU’O& ûk³•>X[[qsÕ»ª·+Hr¡„ÏæÉ0/7„Ý5:kÔ‘DN@#qÑJ 2“t“Blö¾½F¾šíZÚŽ¥xëkWuVî˜Äž\¨&È”ù€hê|DØés¨émMJóL2›ãò¥‰nBxȰ.ƒ¦£[U|ع+öñëXµž|;x„G rÚ"ᣙA:ëaþœžÎ‡¾†ÊÓ fS#µË;ÙåßCµÇ]EG"NŽaž?p ÙAñt·]=:vÕÂÌ$…‘|À°öÜnÒ­‹4Ú†‡mYZ›¶ýQe²xü06Û¿NƒÐ ÖfÐŽ–6 ŽCÁ…`ÚN±­›[4[jnE •˜ZòWi#mfPv§Bi›—#ñÚœTUNv‡Êñ¨pÏi+èš2l)ñä†Q¬jY$Ävxªƒ—+ËædoÙEŽ×OuA4¡›«J mloèëZD·:s‰é’éäËŽó ufpDW'`UéÓ×ý{¾wˆ;Ãvvc*¹;•—ÁmtÐ Š‹•Éšh#p­š_3S©¶Á¯Ø*NG#7Ž™`ʇfeÞaV76ñ`o¥Fï•­­§û’àö¸ï¹Ô#6lÔÆ‘(ò@IB Ì7FÍð5º×QÈaÃcˆ>ZÓy³¡føŒ¯_â®A1çlUä¥ã„‘2:ÌÊÊÓÂI5*sn³y- ð¹ø¼`‘~Ò¾Q4ì”*î7&ÇvÖÓKÈpœ<´èm ¬à†P‘0`I#²¸Îz|³“ädLò„Àp«âaÜšt«áó(gc©!šE;øH! j£‘l,¹"šYïf‹pk›FµÚº×¹ŠmÒÓµ¯‰(è˜$ÞtKfVx‘·ì¾½oÖ®ð9ÜÜÜÓgÉ‚R‘FÖC9E=›Ï¶¹¬¸±£‡àrÍ bàÚâÍ´^Ý+¡ú\ãˈð2†‘$óÙ¦ÃöQ³[ey Iokx×À°’+œ¨ò[ͬ²YžÛv…ÜuðÛJ€ÏšW l‰ešäÄíp|°âýa´T„™.{êŸ2go¨ñÒöXvÛÕ´»TêÛœô’¶­RXê|‚hråÉÆo)ì Û|wñ  ~ÝiðæM—Ÿ<™+i¢Ž8‰Ó[o;´épj¶V’y`Ib €ü7¢8¥e—9ŸRe O]Bë÷ÖmíjL’ÜšAyY ™…îzîÓAfd‰ãÆ¿€J²=EEIÈ«2!U-bA¶º ([&¯æ›™… ­´åy @KÈà€¬-ì%›þ …'$C0;—³P/S:Ü ³kýÆ pFZ\^0×cè¾£ÝN„gKÀÊ#Í æÑåãEŒLOù^¬?Œƒgˆù¬ŠtÞò€cQë ×'›,ƒ ÍC±ñ¦²Ø®6ïªÈ³ç‹h uVcÞ¥Oüšº¿Ü\O…™ïu‹%È»4—ð0;Žƒâ×¶ Ê~G+d (’RÂEE@ªÜnÜ}uq4¢lo˜¤‰½ ôŠ®â_ÿÈÎŽK^%òØ›øAÖÆç¶’pÛèV!¨mIY’²aÌ[/w°ÑÔøX‘¯¢†3ù¨QŠÁ îÊ£Sû뱞5‘ ²†Ä÷ÐkŒ ¨†;7Q´PV]Š7x‰QœDL÷hç€/9ÇhÌacal,À‘Õí"¢ñ¶,s§ú˜ÇkzºŠ¼‹e; ÔÞ!ð$ªçݹ¼fÝ="pœjßÂåOUg6>ëQÜ‘“mewζü ¬ãàùÌñ2(0Ù^}ÃEaøG¤ÑrâË:2¦Üxïæ8}÷f¶ÍÈ~ {jÖ5Š$Ä#tGŸ™®^8 ʛդÓõ#náÞTudÛÇHo®ç÷;OõÍv“Ì(À•7eíªó)*ÍÑÆ—¶Œ?+TòIwµü…Ž¡è=F¡Y JàÞÀ‹Ù‡eQ,n\žE*Ù.Fókv{«fâE¸¬!ÝCü±8ˆó`YGÆåü_em¼U‡†O";_­¶ n¢7!t©R¬VaÊË‘/’ðî.e•6î™HöVŸY¿)T™¹™räMdÈ‘4w!Ig»ƒákYÙW/È N·FlÜ¥l°Y ÇЪ’†ú[ý”D0q¹1¼™ìw³».¾¹V߈_mϰPÊÖxâÆ |Ã4zÄ5»)ŇŠ÷µGK“Jš2íÓø¶„âF®NFì÷7·ÅjtU:Ñ(ô½>ællR5Œ›ì.Ì4¶¦æ†l‰Þ7v1ÊIÔdkÿš¡–9¤ Èë"DI“]¤‹]^ǶƜòÅ o½®–#Ú7nÓQÝaJª¿îP™YyP7™ ²Ã—½îlÌÖ½­r¶µcæä¾>,wÅɼ[IDfð(66Úm¡ÿ}Ž®ˆ¼ìfgŒõWY;·/³J´ÄƒäÅ•%ÇwŠxœµœ20ýN½¦ÄSݤ–3???)ÎD€"£ò#S§‡~Ð@סûªX¥…2à•e/`Á¬ZC±‡¢ý¶«\<Ü|Øš6 I’Æ{ öG¦«3¡Á¤‡#níXʲ[ø¼*ãÛM5.ÎijCðBóÕ5efÑⳄyRÈÝ4bOouSä.qe;„¬ä"Á%‰vôE^‘…Ë•ÇÜr¶îæï¯q¦ÇÌy&d$l«Œ_vÁaú„„*|WµÇJjr_’ÉD¥9+zqqÑÛvlûƒ—A,˜®ÐÆò“v[8ýEY¿®•>,‘clãÈIT£H®Ú×7ÜÄ6¤ô«øÏ§2qÂ͉1`UeF%CZú5Á¾ŸˆSÁÁ¼xGÇkÈÅ!¿çV¾¾Ú¦Ç:㪂¼JœÖË”¤âî³41À¤X„Re=§©ôñ/êã˜/rÆÖ½®u6e“7 ëæãâ-㙑»HEk{ª—?“‡Y"’Oò’˸†V`\ô&’ü7Kv¢Þõ»ItAYP³ºãL\ <«ëe:qÔß±}ô[cb2ù‘¨?ÔÚ-¦–ÜâF¢ü$1zÀèÄuReö+-ËO$±Ì£j‘iSOPV¦­RЪP¡|Ƙ²47‹eÔ²ø®4>%[6Û€zTæO2ì*ì.·bY Üvûu5¼&B·ü¼±äz‚·÷$Úja™‰ÙóH=ÄKý…-»Û§¢˜ÂN&åh§b\ŠH¸7ÓǺ¼\ ŠGF.ÊÊèHmv#ì©1ež•$Šá· Aë ë©&¦$¬3Ev*wK0ÜOáí5±î¥ÜÖ­!•[Ñ6&››%ã{ž²;®¾>ŸÕ¯NcËê‘GÄÅEØvY¿ -ö^‚š&tޤY—WŒªMÊúƒï¢¤‚S—޾^ÌU•Gdó$$Ø»,gîéMÒEаÀ€¤d ‰çñ.÷ІRzµïsÝÖ«~¡%š7*LH„±:“®ã]Yärã‹d¾\ñ 6Èšÿyûª ŽfFÖ‚G^Í¥Ã3øVû}EZ®Û§i‘&m2r\(qbTuu[@ÖíÝ×¢8ر&lò–˵Ì[›|{T†R§o_U»ª«@¹PHíåcÀK­Çˆ¿¦?iôš·’|IÞ#’Þ\ÆÞ\ŠvJ›ºj>ã\÷N¸SN`éNrú<§\©—7êe«M${l«µw¤UT9aL©¢W$b UëeTÜæ×=€Rø°ÂÑI?ÍG”€N<©zn?é±þïôQ3cHÍÇ`­øãoÿqôÑ!¡Û´Xø4ÓÙ^ÜeÔÔç8PULeÉNJbäùÓB`„’Šà‡»Èw39½À¿JŸ';EŽYæ:€o´{šŸä!¾dI#%aª ½ú÷éT Fƒ{\‹€GCcÛîª*ÎY7xÂ/Ÿš¢¡[èÏ"–³.ÓaûêŠYY@Q¡cýoÌ=b’]w ²‚AÞºÔLŶ‚‡ÙÙOZ¥¡;Ù½uĽ” Úý;¯{RwÜvž²»²Ò _ÓO‘Bî s ¸èzkjˆþaêÓ¥4˜Ñò¼ä:£mAü&¶¾+þ™‡ÿ±ùc eœ›£°Þ¶†ÿum`+Æâ)µÄ1ƒnŸ¬fJ•*Àphæä­>Ìir&iP(nŒTßuû»«¿¬Û’òÿšæ¢JŒÒW §V7ñ!ÚG®Š­_Ü“^"^c`àȘà9½É"9]UOAµ¾ê5ìQƹÿ/TWÁå–Œ³5Ë"ä+¨ï¯qò®2:ä3k†ømÔ;Âo²„‡ŠÈÌ‘ç2¹Éݾñµ ;”—v]OP:Ú§ÉÆœ­,η~'ÇM‹=gÀ÷'#"iþ«nwlXüFÃ]5÷TO´… ûtl}ÇúZùÈcs |ÍCJRñMm ‘VûM{ +“dṵ̂ŒâºÉqèî÷Ô++Ó·l~°#dd˜K.C°¸ŠÞÞ vz¨ÙøeV„Á5³ ÌBFùK5·.‘ì[u¡$_2Qç«ÊÊûj©øníâ÷/ªõ3feÅåqj£Wo¸ê¦µdž½Y6û¸¸&8ÃfËæ1ðÇëÍí§Mq|rBù–ÓÇu]Šzƒcè¨1šs£”l™ˆ)èß…‡ Õ$³ò8ñË„Îâ>\‹Õl¦ö¿uõ¥Û/¤§“k¨.Ë&4.¾v"·œ†ÿ&ß§~ÍºŠ“4‚Sõqµ´zoEËoOi¿Zrc7'Åle"UñÄäX?ƒÚ­×Ñz®HNù‹ßi`ÊÃâSnÑT㺧Ϡ-G|cËÙ¤ÇuĪìØèµŽ·Ö„ÌH2ü¤… ÷b?p©ñ•ÍçüÚ) ¦âÎÁn×]¤Ø°ön <錘òígG—XÖVVÚ‡eØ£SꪾYZkß— O]; ’§`ýHbkVÚl<@éñ\zÐÙØpNöÚQ€Q¸ÞÊÒ°P-è½è~;”ÉÜÒd3:A,†Ý„U쵇OU™¹Òy#dG*ÖÜHhÛz‘á×-—+»‡Ž™;ø×µ «rYÇ_0œ~“Nˆñz÷šdq,mkÝÛV'·úvSÝ‚è54žE—é 0BHf]åz%ˆ÷Ô~T‘²¶<†Êë'”ä²^ž‘L“)¶±%¿(>áPýÌB!x¸N•‰ÛÛxiòr FRhÂJt[ôO§u®=DT‰ ÒČΪ„‡;×PAöÕJe–ð8o¿J–$Ûs!„ Z(mK¤ N.4æ7¯©f¸¸©†ßÚXŸ¯};½•‡I|БF±Ñ‰É2; ø{‡æ6 åe)l !½÷£[³ò·ï§|Æ<²ÆócÈÌš3*DzìúΔ"fs‡y6lp¶¼t'|ù!㦑åWó%×á»xl=]õ]ˆÂ5:Ýã´‹ßü´>\™™î «äFž„+=: ö5ba'˜w)Ü6‹j=tü[xå½m¬·â¿&‹NáØØ²4i( —šú|Fç­TåFòfHo eÜ:xÝ(÷DcúŒÎzÌOÙÒ "éèª[™5…ó7âÙ9µ—À‚ÈÄ¿¥3F½ÄÜ_Ð è†Éͼ4Âx#o1”(ê§mú\^–Ö¾¦ÞŠòWDˆž§°úžæÞˆ¿·Tµ0ìl•’(Q¥r,³½ÀÜò_Ãf¾ênO'9XA˜)Ý5E¿Ú}•W3C"x”\ZÄÁ¯í¤ù)o±;}ÙZ<>(?+“…ÝVXâ¹Savªvƒ ɧ£"`DŠ|CV…».×­ºUÌX4—ñ¡µ¿u?*T2± Ibƒ°ÛâNãý ¸Û=Ãe—X’%ðn¦Ö,½{­÷š—31L³86±¬oÖÁ¶¸'×­-Œª±ÄìØÌšé¢ô=Æ„&InÌ|JÛÐ,«L«œŠíŒÜcÆÁ±‡.­­ÆàSÝzñf´ŒÞ ‹K|/n×îoOm ÈŠím;(aèéR;JÍå;m¥¬XmÝøŽ½”p,¿“N E¬@ïî>ê—:\4cŽBzT¸_=㥠+)œU‡Oë-G­žÝ¨¯ûßZ—ÿ%Òsð*¸äÙ¸[ßj+šÍÈBÄ(|;#¸'ÖÝj¹˜voÛjaݳp:_Üh*®Àw³ÄŽd$²%‰áü^ÎúQÇ,ߦˆ]‚Úû¨÷k¥(ñ¥wEgqñF4}:¾Ê6%bÍ,ð¹É„+•,cfUübË­»h¶#°&4$¶çº!%éã#E'²ôø0e”:’ÑÂÚjA6û(÷ $Øû®Ãõ c¹ˆ_ħñZ–!…q²U-øÏ˜·ÞnlO¦Æƒ³‰ên8µ¡ø&rȇrÝz €Ðâ,\4²LcYAeÐÙ…ëÔ›wQ"‰sb±)rÀª§P·ÛmÃJ,ÎÐHæ1æLXÇv}×*7kÛø»)]íк¥zœÖD- xÉÜ#m¥‡m®+hãWgˆŸ–ƾ…r{×6xšÚËsnñþúÙ±ÆÜx€ìEeVf<ˆ[VKJ•*ÀpyŸOåKÈçæ¾4ÎïÞJ !Juó ·õ\WyJ±ŒËùG:^G;”DãàˉŒovþÖÚ¸î]ÿrè‚'ýŸÓ×Zu*Om ï[HF^xÞcP˜yŽŒâ&éü7¶§ƒƒå1aœ9L“ôURŬ:Mi4«lZrÚgd¼g+ݱ'v$ŸôßOðûªŽ?°X°ç%€Üâ'ÒýƒJÔéQTF|¶ˆÐÈ‚çU·.@n·òÛ÷TéÃòÒ2‰°'SÚæ#OP½k¨´˜ªÍ„|,v˜ñf ¶ÒÛHôô´D\.8Æu%JØÛ§JÑéPtC.Vº#>‡æOŇ ·i·ï©Ê*†li5ì síÛ]í*WÅ_—ä_²8Æf ~ZþÓŸÙKùnVÓl\݇ÊÝ]ý*Í{°ÿô[²8Ãr npæ¹èvßI¸>VûWKÖÀèë]ý*on¾ ÷ïàgoÂòë Ã”ŽÂªí¨„æ‰xùŽ·$€:ûkK¥GbòÙ™{}9Ï›†Â‹kÐößLo§9²ÊÃP€\ÝuÝb{=v­N•BïfTÿMsn–mׂ»7k~´ßû[êÚMË{_mjô«A·3'ÿ¶¾ Üp$@;»i'ӼΟòS,ºXÚÚÈ÷Ö±J´XÊ$úkKD0¥m„Ý‚èIî>Êsp<Ù‘àÌX'ˆì nk“©Ó¶µZTA&FÿNó‚á°g$€ÈOoî¢éntÄÌ12ÆP©'·NúÕ)VfN ™~šæehbù)”•ÕÙUÕ¾*..–ÇPƒŽiˆ:Èѳ\z˜ Ó©Pƒ7&qüŸ™÷8ž/âÆpuô"Ÿ²¥N'—ž\f‹XãÔ°&Ãb·}lÃ¥ÿÝ¡R¥Ø…ƒ:ÿ·ù\,&–A?ªµ †¢{ŒœX? ­¸µ}„ÖÎÖÞçÞÞÞçèéèÆ½½çÞÖÞççµ­ÿÎÿÿÿÿ!ÿ ADOBE:IR1.0Þí!ù?,îÏÿÀŸpH,…Oˆ—ƒ8ШtJ­Z¯Ø¬vËírqÐhd$‚g‡kB¼ð¸|N¯Û³¡ …RVùac2)j!wŠ‹ŒŽ^9./*=0* cœ j£¤¥¦t!((bd *d j§¸¹º»B(/~ce› ¶¢¼ËÌÍs:(/šc€²žµiÎÜÝÞS ~®ƒœŸ¶ßïðÝ() ³c±œ´¶ñ ̵ÁÅ ­XÐ[PÛJœÈhƒ>0,pÔ;)ŠgÃAN'›ƒ‡$, I³&– )öPXâDCÿN‚46hçΦѣFLÊX±b —Á”©U£eP1à@8}]GÀ WÓÖÜà`O U(è¡@1`§‡ ‹ªÝ;qƒV¦q_üzAØîKPªò]mX ~Ao™¶Ð2ÞüŽíŠ>Ë`¢@N>˜¥Á¹u7“;-‡=Í)'ª9°vÍ»ÈS8Û>Û6ªÆÛ>pPÜ»”“'Ï4ÉAB&Գ縱ýFˆ;v„áÁDP$蘣Î=LØÕZ›nËIþ~þ#ºëÏMg]vØåÀà Þ÷Ýw;ˆààx°€y ,`¡„"X¸éé`ÿ†LX"é‘hbj$¢‘^ `C mP,.ÀB"¤à´!<0€ôå4Kˆ ÃnZü'€Óñ0 7è‚S2xCx8ˆž:¨¥—lX¡ˆ Hp†™fÐ ÈT¢ RƦ±azsšAC|êÐá†ÌØ€EʤÇ"*ŸT‘xhlÒ@ 2–‘^zÀàs8a§m– /!ãa—ãyð%˜$—DiHâ…ç¥Á€…®Šàê®Eöê z†¦†µ”b‰±p(iPå! p  $Fèœ2°(‹HÚf›|Î¥*èC)Œ)PZÿÈ€üÔPÂíX'œ`Ú ¦™FA»tpŽÀvO¨Dj')Üú*œ§Mrv¦‘´È" ’n 0৉¶¸Šk™ÂJ(!(Â)²˜äe«Lj–yV~ªð¥ú´PÈ0 ’‚ ¤îA¯½.ØKA¾¦ `ôÑ0@ÒÍtÒ <ÍtT'À V[õÖL ‹°|(råñpÇ{@kš ‘ @j àC²´†(­Ë'R«­É Tûb&3f6à°¨€ íªž :0¬­ ìQ2g Bµ”ËPÎÐ `P@AN7´§K­úÔPÝzÕ°Çîúë±c]ÿuë[g­; ­Km¶ßZ‹™—~ LïQ‚]'ËÛÉÁ gØ,¹wßÐV;g,å4éCOž/F_PÀÑ觤Ӣ' €Áé´8n0\C0œƒ×èZ0µ¨E­i‹i–†ºÛð€O[Úú(€ù ,ž[³ö:Ûa-¹{à-À§ ômaë@›pé’É8A'd¤~ 10H JP|Ù+2,ˆ Zg¯èÄèo§A žÎ‰JË×>×?• or5ÀWbP€®¡®h8ŸêV"$Z¦!¨{ÚÓ¨:ˆ¡oÁ%` ¨Ùÿ.k„ZÕ®f;Üõ„ H€$ éI0!ˆ~Cpâ3+Kq& =5à”¡ ûp‚¸ H€NÊÄ€” &P €j¤–6¬VL¨‘‘ìRä£Ñºh´Èt­u¨k WÂØ`Hcú0Ð?¦õñu§£P€Ðí¡iÝ4¦ê`'µÙ…qwÜš@hYJ{0œôˆ‘]`‹pþÒ³ ¨ X‚Æ$@—”¨ä“VCh,) Š²% €Õ@hº¤]‰Ÿã,·\ Ñd޶ÁÖA±œÌg¾’–‰( –ÿëC@ j½‹ê@ƒù%€ sYÚRTLŽ´›ÕlJkàÔ©¸jÁi·ÊÄ Mt! ´ŽùLTŠóN`$ÚhÐ#<vA OF®HæspYe*ÐÅ=¸`h]i,PË à°¸ÀpyL Q•2@%æò ­tRk N§F>ts¦@€[_ Jø²F(ˆBé:×)`}@hEZ‚ªdªÒP‚5³5*€ ì·‡ÿí’û{f!OˆÕ3É„Xòjhðå…%m;$ä¬ ZªxÀ0· àüÿin% •¬¬@dÀEŸ¼R ˜Áö‹XÅú÷$ PT„Ñ hûö÷Àq.ói“3ñŠ@ $ë¼@´¢] #hF´3@@ŠU¬â(lh,0†ì ¹ý_[ð¹œÀ‚ (@F‚—ž¾=WV‘.X[²­ÈÐb\­¼€ÃÑEˆÀm$ °@ØkBɇþ4/˜Á\ô»ß $ö°Š½3 äLàƒ@ëâ1 øJ=ê±jÔ€ÑÜjá¼ .„µ0£/¬a=†V¼'¦tŠgÀi¬ xÁÿfp)ç0¸¾ì¡½4'\áz0€åä.@ÙM1ш$€O.8ò3BÚŠ:ˆ³] e€j€£ë±¡y\"È@€oeå3' ¾†éºpš¿wƳV`9 ä‹a¢šu')]]µˆúÂWž‹[í¢‘®¼õ­°S€híV@ÒFAbR€_$ÇÀ±=1ðah*XO%é0´µ°ôˆ(Lg‘ƒ##Ö]ÒGÛEØÚ+ÞœÀ'@Á à ¼„Î ‚4`Û—æö >½‚oßÙ¿‰Íí ^àáE›Ò“00 .ÚÿHÖ†éüZ‡_ (úÞ¦ eVq›Ñ0~€@ צm›à*VÁ·.è „y8=@Z¹ÆE®P¥C)“xÁØè€Ñp!U@ËÆã@¯·@]·,¥>²h³6ä8»®øðJaýÊËi+оö^penOâ ØÊmgq‡ûTÿÅ{Äm ÔCi 0à _†`À¢· @Õ~á o%|Xàh#@¶ý¤Å;G_à;h˜_P€4¤ Ã& ã@àßìª¯ÂØ $&r¢*^Ò*æ¡r9ÑdÂÁ ÎCcµð=àCC5=ábÿ+!¤Š„fW#½³m vzv+€rM×{†•g†5TGà‡v’6r&`Ö¦Ñ9ôSf—aÜFiÐÚcBÕ-0cs IGppw§7ÐN+0¤ÕC†Ç/Kèu‘Z„Qb%¦…æa3d$Ð&€^bäÑBZÀñÒm/c"@åf`’%'€a”ÚÂÀ@wTÓš3´‡t“¦a*0ú…X–xX„¡À‡aw'i(p+"@pQtg`G93E5ÛÆ…A˜vŒ“1x’1‡Â4W pµ'it„-(jÿvb‚ñWô;NÁ…Àxp{ÏdŠþw ^BZ¦+ ^9;Ѳ!Ä¡ÒòP~¢5ÆS9’ðŒéa;[{„Z$†wçEx–g–[ubô7~V,š°K¯`ÇáJíæhã'i°Ð’Ãâ!°Èet1f—‹­Hp„aa×§'p ð…KHô' °‰ó(^ë#Y0  Ó¶†Ñ( ¡rÀf3¤ "w{öVàu ð¥.Ð#¥ ðI -À}C§ZëDSÂg`ÐNQ¶`ú¦v¡õS¸xaŸ&Â`ŠEÿ*4Š™°~­8•“1Z¦!ZÕ`Z#a„'ŽžFâðð*^ØfA55`%y{à×Vo%K¦È"E"€ÔHÖØ ð1Vê0€`Á@‰3` —TÇ…?e: ¯$€Ñ÷}ß§Z   4æ 37*°(DX{¦‰¦a‚QŒ´EB–H—'Ü28™àžƒDc§){P~Æi*†¸±PˆÉ ª…¢†¢ñ‰ŒàG[ж-¥8(€—'“Ú ™–ÁØ@¦@X¸4o³Q#C#4>³4vHäÆ×4ŒJI'' gåÐ0ið»ÄC„|)&W¦ @`+uÁ…¼e–$0Ðâ ¹X°Q ÀcÚcƒÃ>- ¦G{U%0%°¤øF~5=¡vº¢•ÿ!Y”<áK¥!ä 46E{Œ&}ȇ•pB”yyÎÊ8P‘D-0 Z 0a¤q¼ã9e#`&0$`<¤ æh¢–‰€T„¤—†®×–M›¸„Uw—¦AgvÖ{ÀXœÀŒ“š†$@5ùaÒ&ˆQ¡=fbšp¡ID‰²«{õ„,&ŽKW$ []+ðJQóZT#ˆÇ…¦ñIÆ—a³¼Õ €þ©eø­^ôQQÑC£Â Þh ÛJ©MöJ{6ðª5®Q„´zç®RËi@ _’–êJ 0}øEP`ÿð#°u†X#” `š‹µX& B¹§':9”6|R-`C²ù3 3EPo[0±„_?åPãàpª¤[R£ 6%~ˆˆfgvM5©»t>D8™7ë Ð*kÚ.¦§kB‹Ûz†Œòùm%Q@®#ðRTÇißV~øGáÙ: —ЂD’U0‡Iæã}!Šun†U½1`>CDkqÁYw ®€}FÆ"— —C>C© Zf'J@Q©fTsÓ©Jš*¡`™F¶¾À>˜ Xylh<ª’yfÓºLI2/ÿpê¥) ´…™;§‰D–úeÑàP¹ ª3pš ®`#e®4Kë™,b®$Ѐ3` 0£jÛ{Q%@PFUK5ÌCô`·¢Õ~-B'{Ã2Zô(@Ƕ¾{7u>ÛÔ:H3_!¹„ª¤4u•ÝDCc Âfv†ÇS—3™!!@8@ºØÕ¦!\‹"S…æZCt„D\´MÛ¤5[gа¼`‡@_A?8U¶˜|°«²zXüeX7,n`>XiŠ”Ú7ðZøoßw„t9v¢!B4 C$(°ëÔ#è»—Jž†K ÿ_Ê0eÐ7ÏéNÐb9€0fZs’*ĺ’Žà 6 *4ðUÒz­¡‘¡hð.° šJ`ò¡XÂÊijF® À»!|Q†…pkg1@Bå ¢*Qr¥´: «&P‹X7ŒÃÒ«4®0·sQâ•Ð8Zu+Í3‡pKƒ_ou¶þ¨v Ùe£-Øs9`—€Ð. q U©éàdøuÓFŠ1q gj<ÑLºZ¡t¬€œ©ÍâìŠi/ ¤–qúµWå:#À¤Òwšð¼—`—?Z4S´_§Ytú¼Ï–¬¶ˆ¥f=MKSÅqKå9=@ÿ‚c&Üb' ¬µŽ|2+7„öÓ.€ Ýd¼ ±’Ò öE‰aök4—“"àÑ:pÀªÂÒ¼³Æ1w§A‚c%Š#!U‚ ©a]t>3P¸f´weR˜Ôàv‰á R§™/´FÔ®4gJMÉðÚ‡%½sG9¡£Jø ¼hZ-mÚ221áTfaq¡‰1 |ÜÖeÍc ³p a‘5 Ñb`3<I¦ÑV)g µ é(ÒÓ›·Ó: §ä³>ØŒ(' V}=C0vn­L`óÍ»k{‰åVKpMi 0RH¤²£&ÛXÝ{Ãÿ`Š*vLao«hÐf|ìYdk&*x.eÀÖšHuø•‰qMc’Ua û"Ý-Á-`363;ᇚ³P*ÀÚÄ­0" pÀϬؤÛ*l›× vÒB•=po•ataÀv^W.®$Zh×ß8|X)ö §­áªX°MÕ\þ¼[þSèFXdebÚK}ç†Ãb ’„>o«çoUÐà<âÜ6…û „p B&Ýœ0ãëQ•{à‡L¡ªõà° †µÐ]ÐSÐÆs`4”Ù`\ƒ¨ `eóýV¡un¢vÜ+Ú®‚¬bß¶¶kÀhÿ§y†5,à w¶¡¥æ½·¤jÎåz0döBa—vÜW|[(v.2:X&3^« õ Fp5Ì×6Ö$k8„ 2„Ðd<³+ )&4!p J€p¨é­9§EK9IŸ°!˜‹Xîä‚NÞ{½Ëg°s†e‹¤ÔÐÑ4okë•|5nŸ¡IPJÓ:P9ŽŸofü#máR• (  5Ð¥i©=ä@T÷ˆ#CJf§£âGK(•-è^z¹4’ß®Õ'GøpÝý9Þƒ.ÂëÑ aàCP$@H½´¬"˜`!Ôhs ‡2)Q]¹$ó‡ôÙÖ'ÆNBg{m-‚V÷V–Z PAè¤$ÀpdÀ-<Cçƒ:K#m®Ô;ûGPÅIp~Oq‚/0{³ue«UŒçaãð(`Sn¥‰=a[ƃãPY.‚‘Õ’|°ƒr 5S ‘ 0Z…_ 0ïâmﬗ¹NGêôB ”ÅÑt4 Bñð:ÿ׃âqQ±ø`l^™€#PqT"°‘Ȱ4N T Ž, P4²ÂÞŒäP"X"./R_VW b< P^\%àÆ4f :TT(e†ž{Z3Z4693ˆX£Á hT1¼߈Aá@S&È$0hÏ8«fÌÈù à6$ˆùpUF8u>š¹gçŠb{ èP€:°$òRz+€'˜ —p .´`2#M’MdìsÐCEXm3ÀàÀ€æ±øü%‰Â8·×à(0W|_?,à·PÞÏwjÁ 2 0(>H§±Û Á® ²À œ!KsRè…ƒŒ:À–_Hëâ)Pá‚( £„ÿ^àÈ£,„Ì0 ¬»Én, &I. À‚¨h N8’n Ä…Pl,`¿ –qd hJv|‰²æjª§—7äH·ö’Kqد<ˆ1ËX“5 `«b´àÎ+ŒÁ ÂC倅yxyà:bBÆ‘`rZe´Î4¡PŒN3ç¹ 8+IZ“1*©ÝŠ™+9ib‚‚0  ‡Ê ,¸òV!­[Á8TØ)HhÙ ¨l)FY€•Žèa€`gPÁ¥(f†?î,·\Jø`M ¸“] ®ù«@ @ƒm÷ÿ l%²aP =ÂÔÀlz~Œ€¦CTÌŠÜ 0Ô€â”ÕÀJÞ«A`€ ÿ€Öµ–õ&0ƒ7¬–Ìú˜ßŠÁŠ–×ì…Ð¥]µh_׫…Ç )”fª P@#"†<2SžéœÖÔÙvÍë]ãƒ×5Š™© mô/ Aß$˜ûq˜ÑsT˜àŒžóÒœ®åGìðÏÌáÄ"¢™A)] Yh³¡™µ­ok‰CœÖ™x@Rt„‡’ÓBµ˜è•+0dT9-¹’¯T µ©‘‹Ì±D6E`š%/Àv.À¶ëJ€·~ã–Àg%À k\ã]E/P¼¾x;7Ý*°”‚oH„Abt„d bI=CßÈÈ€ðºQ°³ä" ¤¤,eÿ) 5#,®7|¦3 ¸­^ë ÐÚ eºÀ‰0Àëýð&Bª¨ÌL'³Nà(Rj- ä›,z`R-£ÇöêB¤Uós˜>2Ì™Ár¡•çULôµÀ[2`‘Uæ 8ÍÕ‹¿®‘.,€Ú¼z„°£¸®g(rà-þÝ‚½R . ÀtžX‹$ i\(‘zÄ0€l`´õÞkwˆëz: D“<îÈ'ÏA#øÞ<©­*Ü• ž³« ‚+8Â’aºZ`¼’€\`‘"`N VÁ‰`|vJ%à‹N{Œ£&B*æ&d+^€ HÿÀê2`Ñ~op Ä0Ä©~kb`€FŽÅ èÑ|JöTA4,#zb—P0`.Àîî×îΔìâNã ^f'Xà§= +?Ôâ«ËÿøÁ,’A-¬á/ºÃ;Æ£<‚æ*¼«æöáÒŠJK-{æà¤¸ ‚—€âDÍ8¸ë ÌÈ-¤@qPF"#?Œ- ,À²ÎëÀîª6X¥–&‘`vjAÉDTÀ¥0Âl "¤ /â$nÖ.yFDT5N€x"ŠCbñVb:ŒÄ*%¡‚Æ*–O>„„%¯Z@Õ`*kPÿ`Òlœ–Œ BA\(ð)ä ")P €;öh<Ñ¢ ¶ÜH1RPEìÐV §Dë}þé#`Ì\¹øavXB@ç’+°/ûN$EêNÖr`*hê!@!/J!R €/£C Ëqe6 ¢…ÿf‡iVhNjˆ‘²$² —Â"@²T.ðV@šRX+Kb \XÁb&w©L °­à8 ؈êG ŸêpRßà(Z@'egöÁ*` LÁ{b!šfPA¸âüDê!$ZáCØÿ !è•"iÐH`¯šæGb háv¬BZâ[¼¼0Îk@â` |.!&<Á¹@ï 4ÍÚc*ø28òƒYZ­ëC.ç ¶!#6É"³­þ€> "å2À0¢ò÷>lq\`¨­=´²„ä#…@Š:N!ÅBbç0fÉ R )&±j  -x”aæü©–âpW ^b`@¨‡ªðBÝe4"(]ꉘ(Û¬U(Š¡1kÁznÂ.€8ŠL~@o⇫LPÀ¦l:`_èÄ_œN÷À2 7¯n7éÿ/øîd6 6 ‚``hÀÓ²JüÍNêBM(eàÅlÖ*;a'í/$`¦'z#'4À΋ª/€³¢Í5≺'æ´€{ÎcŽÀ8r4Ó>ó`26 ­ Ghåx ”@ê4á?Anç ^"¾Ü"08à bKÂpÖ1qF¬ÄÆCqLT"bp­üˆÅ~®jЄ²,³ÓßNJÓØ©® &q|ôu\ VÖÉH À,0`ÝÜP9šÈ:æ'®ô (ç!ÁF3+çŒV3¦Lê’JÙ€ñêH¼%¦FMMÚT¡[¡ö‰6ÿèHúF0 œ2 üçOÍi¾HÀˆ,¬…N `F0€^¬* 4*xdE-q$Q]ÍuEa‡±˜`<’º#lé ´@‰Vª¤bd-âVÀb)(k`]* ök‰&«"/2à­À$@*’‡Ö8ª¡¢‡²–Dâvàgö£®‡NŠ E¿NB"x`àò’Cà…/*+¥ ¼"<‘Ó馇_‡$*;`Gd #•G‹U*#N 7Ç$)8jú‚ƒ0BD²*é¡o'¢J`X{Âe¿º´5ôpÓB+oHàÑÖÒ¡®ÿGå: Œ/ÂF&?’!Y²<Z©`Z;, àp¾Ž÷N„®anÄ" €lxtiÆa'9ÅŽ\`·Æ¦ði) d §#vK€ˆ$`·ŸnâG ¯”º&\•*ݱ@1ÍðJÁf3“¨ €rºÖa5…J# xÄ« šf¼PÀF@@ x†îB—°¢*ƒGè”ÃeÌ€[=à-fkPŠj©: ZàÑ6€<é6êG]æÂ}´2scDS=Í7u¦gÓh°#UWJ€c¼XÂ?eá@ ,¡ *âv<áR& *À æÐVDÌÿ ‰"“*©r D8y¶V#«Aüg NÀƒÏþ`jR®“¨ƒ†“Í*a@@®b¨$*,£áYv›âˆ6 ‰i ‰aà%Œ`vd§Ð¤7Ô‚^ s7¢GÁuF! Lȸ¢á  `5@HÍ €I=å!üîZÔ“2ètr—a=O‰zð˜€yÁXp‘J8{>85cÎ.½W‡®Ç2, ‰ßª¢L€F`JàjGÆA>b`?ÄÌé‘V«;Ðg'Ìà_ˆ€ MÐeÿ'j  ÷¤$_ÎdÁjVHO¤EHT:•±"õr,uÔîr&ÿ$~HƒUCX„çÀ:`ŽÛ©0ªxmÍb•ͼt-ù3DDD¤<ƒ^à21¤ñbqˆÆ”sHô0Š,ÿnä9š È †­’ dÃkn§¬Vä|¢Z6 Ûè|¸O2š€?ö£t€/à ¤`QìЄî =íušó¤æ€ÞØ)ko³$g’ç9èávn‡Ÿ ùL!Šfƒ1X2 k›?C,FD ér‚\gX…b%’Z€RÄÃ,4¹YLê H­J uëoR´¤c»«’éIHðX÷Ï"¨PV“©ÊápºÕl"Yÿ…/zÑæ"r à|!Ãb]’q_bÀ4ç#n‚¦ðè¤@ ƒ“Ç7au,ç–p¡J®ŒèÓîØš½4œ=»ÌðÁ(!N*úâàÅ '/c<0<‰©ó¯zu„¤š+™ŸqíÊ0*ˆƒ&YrtU ¡ ]޹CÃfHo %ä$t 8)À¬©zà<£Œö' œr/êG3Êåt O, {ÙÿVÀîáÁ&ÀJºD|0dÒ !³ƒ£FÀq.{b XÂòpFµ¯å;:©ÛŒ—!¼h%Àa&%YôyÂí „öÅž*ü&L‡žìÿ©zq,{“<²neÀ$Ùi ,‚¤Ä”Û%04ÃPÄêȪBo„Æá;)àNò:HïRÒÂ-bÏð¤’Ù…g¢%B8g\` åûˆ|¢„·Je.'Vw¤Ä÷ŽQé6(hš‡h ŠÁ f²òÀÂæ dAT`·Và&Úps¬Z¿åÙx£ÀèŽ*O8‹’GÀ†’¡ì\¨ÆžêV¶ €†Àà 6 0ŠŠe#}` »1Bà ´/8ê|†œe›à4Âa. ä_Œ¡šŠŽ]€x_^gÉS &ÌÙS2âJË–²p¡k1FUâÓPB8BbtÐgkn¥Ìqÿâ»>[œÈ rá¤GÓóV8ï&ôù«íi'vË…;b÷ö#Y”!­ ’$€ Zàu6vàFg ÄÝ"}³ÉC|¥ö%]ÔÅ@Xrİ<` ¼õzM rm@‚öÖFó.HºÈ„_»ÖͶ€†×ÜTwpTŸp \ˆÌU*Lb®k“w¥®Àãp†+U8Ö =Ëh4+\ p!… šC¢e)¤Ägþ‚8a!(Åߟƒ8Û#B)#@ç–)å˜, $½¨$‚Þ±¿Àz=Ñ, <&D©;: ÝÐ)^¢@´ÈGVGðcvð((~efÿpm2­à ÜÒ-MªzRáÂÀƲ`kúI7@I,f S#ÀBw³`²º‡ÔÌùZ}·vƒð&¸2 ÖfÍ"žcsó`/óÒ§Tr®Þ.€]qb$ÊFÁ3BÊŒÀ¬]Ôº•©þ|ý€X`ÿhH/àÅ,Üå£è„ÎöZ"ÊMædŠû(‘­Ø›.Õ J~¶á+çŽRÀ d   Éñ‚ZÊ9,,uËs.P…Ü‘*›¦79?ÆB1$Xp( “7PƒAbX¡‹ á8”D¡@Àõx ^áðá t€HE%p\ÿY€%³XÁƒ'päü Bl8t¨¨P¤1HH004T2d40ªH,èTJ4äLJÀ$$HèÀ°$°¼Â°D h ÌÌT”䢬¼D8Ø <P¼¨P¸¼È¼P0P ÈP8l¨ÀÀ|«4´È¸¸lË ÈÊF¼¾ lǹŻ]PPbE T$ƒ‚ Š^@L€`€Á%$Tœ¨õå ‚ ¢8°n€‰¬”Ò:Há! á$D€b† @¨À4N£0,th€ÇS%¥/%-`àBJI Z¤Š5‚¹^hx0cB¯[ÿˆ6@ÃHÂ@Æ Xhq@À `‚à( W(H\ÀÉ€Ç"À ñžÅ‚«V¨ð13&áÜ!*œ( AÃ\o•1óÀ€ HôÀ$À!ŽfR-\Aú‚ú„`QÀ‚ — 8ÄôðR9sBHAÄBM‚|à`‰"¥25)ƒÝH¢POJ*ÍDÀµ«D\2èõÍ'l° Q0À +(8€  €„H­sAb `#ÀµÉP€cÄ@K<»¡ÏS±à‚²À€B(0@/  À <. `ãltÿãB@††”imô€!™q #(p‡à1Çsx Ð`Ýu8ypÀF¨À…W¬sI1°€5%ÁQõ0×+ZვVZÑ·â}¼ Y%Ì€ .X8JAp¼u Éã*6+€ºÄ6îD†ÊdñÔ2Yò¼ €€¬¶€F>BdcZiA”¢‰ù§e P@jÆœ ‚  Ú¤e°` $ü¤†±.¢‚70xèR{ðÁ7‰S‰¾ç6W¬/\¨žé-A Á2_*,œƒ9.œ#»UÖ TÐÿ_GÐ.¦(ÀŽ!9zÀ(›À¶ "¦˜1`­T©ºêf+N  ,‡A÷}U†0/ؘ ¢hÀ+DÄ~!$ !ñÛO+Ðp DM>YCwØ”‹ Xˆ”‰5‰Œ"vbfº†Ð€B +¤@Á -8à‚+ÈP ðÙ9NStëÚ 'È C )´€áp±À`(ì2A 8É©±!‹,Ç!i°±‹1yCT(à+Σ‹¡q,¨¼f*sz%<žËW ÆÏ äx3D@[¯¦³2 Àï¹zþ Ô–ÀɹTÌ­u8·ÇùÖTÿÝØØåÞ{ÂlŸPYV5`×7ð} ƒÈ ¿6ÆÀ-@¦âNÛ¬½“Àn_„d QÐvU1œ¤Û˜GHg»Ÿu.‚S«¬€w:WÑ'?hX L@31

 ?™›Ú@€ \m¤Ú  † ¡ú}äd R€  9ªF1BAÏ Þ=0„%Ë&>æf³zEµÁ^”ÀENú.nF†œ kXA»¦ä†hay¸’(TF²„Aßp‚É€cÆ[ÆBÞ±¥IÜÁ\1ñÒ(°J$n HG—¨Ñl`– xÊZ¤¦²˜ ùéͨ&K9Àpîxé *e£ À‘b`ƒ.ÒÜ+cì\6o±»;&àîEê䡺 ÿƒäÈÁrÀ]A„coIÖBJv‚x Á•DÆxP¡ÇÀÀ-(@œYðm•Áð<³¸d%¦6¸ÀJð€™ðaT¼èö’¸Dj4±Ýè|¾V-[Ø‚éäQ“E?7HS¨X|.Š!WÚ$šG0ÁŽèj›&tA XôªUÁ#e–Œ§J†œ.î8UÌ © æ)èŠøFW6`~KX‚ à,ÿav'Ýàƒ2„Žjᨄ àNk¢"“<@رµò}K\âF#Ëqà °&Žx&Y‡¸f#À)Hx¦FlÊÎv! Ñ \9ÊýLÿÀ?[¤Qp Ø#ýp**— |¢3@BffÍ™ºqMÜ7Æ1¶ùo-8 x$™”¢Cp®A€‚ƒ* %(ØY.(bM,`]ú`£cƒ™˜÷¼gj%ÚÞ§DvÃR4W†@;1Ì öãâFb42ì(§A‰%,|DrµSpÁRÂ1@dŽZÉLêÈÄó@/;°FFR– ŒÂ :È`ºˆ‘ÃÓ€âJÀ.Ø›.p庠@ X–‰:° Q‚Ó 0ç#rLd&Á&YÉØŸ]É^Y†T–4ˆ/.qdI H6§ÇÿûÂT>j­IhD$ǽD½e€D]Žà 爴* mjŽ2@ ¾Êà, P <عS]‡]@ \&-€kàd 0m0@º€°`óF¾Ú:2‘‹4 1íxEPdëq²¾NzËæ…±£ |4µ^˜äxaØ×ÏÛ8h·V H·Ù¤p0L!©¬ŒQ~´-G\~­hC0ñY+q£-ÇGÁ3Å*N` ˜ÿ–~r `»F/hÔŽÃФM=àÆøKº´+oi@H‚8ˆdÆ´Ðt;Ô¯W`7Þ Ìh0XMfpèöг`àî¹ü®à h< °é†»IÄ6NÐâå‘€ Èà;‚,UÖfïüé²V'Øá|àšMÜþvCT|£Úÿh"‚’Ž`l0E’_ŽpŒFB|ÐV Ý: #*¨‘ZQI_(èÁ,“9l$Æ XÈ›$ÆamêH GôH/ÀX¼@,„ä‹©Xj$×Òõc@4®À|• ©9ȳ€ ´•4àˆf ôwÙ'ô p΄êfÿ bž"d7t øD ™@ FW¼ÀÒd¤$°@ðÁŽ .°Ô¤Â åÀ£t]A Æ9ϦÁÈ,G1ñ“APˆ”\…€JœlÖÜŒ €ßõ£ô˜ Hȹ¶ Ô!Æ«i’@8[@ÈüáaÈÊD0OÛP.—Â&—à ld+ `«AZ\ÝÔœ‚M[‘Rt€OSܹß dã€À°Ñ+ò„dâ$,1NHàT©¬>1—…XEàÓ:ʨÒAwI€,À”Z€X†ÿ,Éà9-GM}å‡0dÞEð1‡4ȼ€ ‚à ÿ‰¨À̉ýY JÌË&ÈW$8 TBMX”B ¨+m*rÃlÀ·6b´pÙÓf&¨Å†‹š†DH¶CñAOS¾EýÄÁC¼J[ E@[pB@ßY(‘çöAÎÒÚöÑ]9àÚ@×ÅCL9E°À «Ü΀Xˆ‡@ 4­“$Žp¦ø4Š_Àn¥ñSSÈ>-lÀÆx´… 2G&0À |Úò•ÀŽÄp‡; H”ÿ-²”-¶Ù´\/¨Ñp†°J-Xíˆé«^ÍÎÌ@2,‡?-Ý0Qš¡Àl@˜È/ÛlŸFù¦ç ÄþªÃØ \.ðÝH|(Ó~ßÍ@µ!‹Œ\@%‰T°r„m…È@l6ì­Œðü‡saâײƾrlȆ¨0 W!ö’ÐtÄCäÐ ⨨e¤G´à¤´Õôˆ\ ÖM‘çâÔFÅôY_n^.H‘¢,}Ñ,yklÀ„áâäÇGH*h ¶´Aa:²Œ ‡¥YÚOX”TÚ¨\Àpî™Å”â!sX€pürXänùlÃ,]B2€ƒ6³ÿd$uº“îŽÜ± õË-ûÆoÐ#´äG„x,ÂÕ¨€zˆ@°@ È)"r@(_èü~T äšH1×äo0®–j kÀ‰Úðè5ñN,¿ÀOXSm”I @ËñÚ¢mô€À.ì ]sîÂ’çV‚è€DíÉKðÁ`%Ç]36ƒóâQ¨MÁT†çö€˜%žV¨ÎfXïð]zÄÄ¿ÝB²(#¼ Ž‚ Ѐ0JW%Ø(Šò(~ÔŲ7pÍ`ÅCòÍbÔ‚†”ÏâWuØ«È6ž pÉôÀ7‚ÀR„‘(á1"GâD³Ý\‚(àÿ˜ö(ø•$¸tÜAJs<6óéÛ¯°0`e|Íæ&A¡ ¶Œ}ÄB,×k 9êâƒìM[‹‘l'p@ ¼#cH‡ø åFõFõs7ø3® ô*£B6`Q(Ä€J*,É~©”(—9íTóá¦à¾p‡ S„ì%)~.耞üŒEöÚK|åKÚ*E'´Ï©˜qŒ5èÀùœôxí‰w/€R:ÒÉ|[fÄBh kŒˆDv6åG qb„ˆ .€Lp@rÝŸ'nŠ2æjµoâÚIk×ɈAéåÌL¡33SÔÈ8<ÊüÙ‚ +Ûˆt3oˆ T·(T”Qöÿ 8‹›ËTÔ‹6ag@+¬€­Ú…|8§4É(À|ÀKyDùÕžÀãD€Ä · âŠìÐã €jÃU}‚ÚaX€y8ܘ¼Úm6ñ­qHT@Œ]…ž+(@ Ôm[)\ãE3$A¡R/Y ;sÌ8w¶D!øt‚‹Ÿ‹Š÷×`‚Š X3 +[ŨÀžÜAD½DEá|ãÔ^—M>`,ÔÀÌ4% 8H À@ÒÐ ˆ,ßpÆ/hȪc]—`w˜VdWveT϶?a7Œ诈GLM…ÿp¦8 @ÐîÄm±gHø«ýAúsÀ¸ws öÿ˜DÛB{ëà—ˆ‚€¢_3ÁaWL´£øÚû¥C%H-l ô G™Ãðèvb¤Â T v½ פÔ @x˜¸‚_( lÚÐÔ&àe쯢£Ã:ÃQ!#{hõH£¼FäX޵üÆ}Rú´Ã‹Û¦ü—ð9…9a e{Àì8Çdä®ä ¹ÓüŒ#`ÒKÁÏÀôMÁÕ;äRLK€áÂCðŒ”Ú`„KY xØ4±lkh7´@3<DA푊½OÍ ;›€ÙâXLo@IyƒÄH­Ý¹g;aO‡µÏÛ‘üõö„ÒD`·|L;¶í$¹›üŒçÁÿKxÀN„„GjÄ@`jLB¨¥kØÙ¤$6d9ØDB¤¯…|·¢bH‘y"FáyxNI€ÐpÄXÌ@eI°]Ø£§=µvEÉøÁíG;¥‹t\;·/Àà¹æ¼F~k¢ÛŸûL>ѨÕ´ð-ªªìT<}¸µ)ŸâxPp ­_ßåæÅl9çO”D¼DƒS ‹Zlhá`Á!¬Pà„$ðuËÎÊ\ofT˜‘ò…0šÃPC‘¡\´"=ÏY;Ùl}¢K…€Œn4¿m‰0ÀÉç’h÷â„‚™4Ä©×ò©=8"@{N„6 L´ÀAgDl@ÄKÿ BƒE'†Õ ›Œœp±‚… 'P|CCÇ^73fð’)ÌæÍn L%2­Єçìj€T¡ÛÁÂ$à$à‚\1“bú0Öñ3c, ßûaMª ]I Cœtœà”>€ ¥ * @µçœ€+`6»e(ÀR’œ L®Ù›Zp—… 6,‹,)c¢lÈÜÄh#¯0 ˆïÊ Œ m ©&Û2¡0¿ ‰UO*‚ §´»Z21$ÅOJP° ý¸!Ú€çÎõÆÍ Ìžª!Un¦¨ ¾ÿ@Iv7G„ =p¡›Ðy˜*àŸá̦V‡d¶à"€ ›s=¸1Efn‚‡-HhäwBP, 4Œ¸<°Òkà4§É£FüÒS†µàaÚ→=Œè­Dî˜Å/S „ Ô (xRP×Ù ‰S*8Fn…ÐO°}¤vSÜ]ê`.)‰¯tÅ>hÁ¹o Ì 0˜Ô'iÄÙY"ƒ<¸ ˜.¨UaÍÙ pà æ•BªŠx–!¸A,{ È÷²l»šþ"ž? ͸×IjÔ‚$ m1¦}~9(¤ ùp@œû T¨QÿJù+àu+h`@ªU=½¼B”†"w¸ 38g\+Pú¸ f®^m16ÊãüuÒJÕ="èàÓ KÁÂÌ÷¾%,gæ†Ao. + `’wq*ÃÑ€† ‡rÁ7¿Â6cÛ*À¤nÅV05œ€bÆÓüËGÊdLXÀ:ü€ Õµ±0jñ(=¼uy°É Ô;-–É'“æ=_ù:ùÉ9ÅÄBýBe$õ%Œh@ p@ã'9@€ÚuBRð\YÖ<ä ¹(@Àм¡ ešâŠê”625ã†eDÀ Vÿ¤€~ï³ÈO£WòF”ilæžbmF³‰`!È5?½îI(o³T&uEjf^oàç<Ž«ã°g4Ô-:‚ÄB2P€ZWzSÁ`V´ÇkV¨Éø<8;ÌÄÙ&­+²bÃÐu@l(Sñhôdá ®Á>Òa~ ð€Ð š³Ûæ–ˆ2¼7¶óR¢a<Š•óÚ>p¥2˜Ñ<2½ )Ñ$À;÷c6ébv=w“æ¨7 7zm–% xK/)@1ð2©¼ÚDh ^Nb5 :`éÌ}xúæ׋Dt>‘ºåM¤ {cÿP[3(.3 ‚‚è`9vƒ0uaRœC幨„ezÓÛÛâ§o¢lJ(kLIC ܤ€é'œ»4¡L+÷äªc.È1dÞw¿Þø•¼e‚p.ßÏùïh°Î±Àe €( ,#]0·yú@*¨8+®ø£Ð¢u*ÆÆ¤=c7 hÀ’T)ìÖ+ ˜)ƒQ i*à¦"3¯r:G`€˜˜K7û:¾ôŽäۄ棲p¢2Ä3Ñ+¹µj(Èó¥Ë0±a€ „€±§©CG¢ƒÒc‹8Hz02ðR€N9Œ 0&ëÁAëA¨Ä €‹ÿ £È³¿¤¼Ã#€ù"øR7tÂSI>Æ 2øæ‹°É9 2ñ÷@šÇ¸ €‹€  € °€˜ˆ±ƒJ2 ãͨZ0Ïà€°€  ¬^I Û;¬ È»á±ÈRhôŒàN{ÂH„2ÁÛ˜öø° ؘš‘¾w‹ü`€;tŒ 0Ëè>šQ 04 €9ìª$n˨“‰ iŽÏÐè0³C eR©R`ÿŠœi™žÐ4·t+>I”Æ!Ù‹Y¾“L¼D€žh€Í™CŽ¢¥!X™ ¸ ÿè<ò'°P°€9Ç¤Šƒ³è(›˜"*p‰è™Pù0Ä#˜«‹"p:ù•…”€MƒÄi”ÈË ÀHŒ^ÛÆÿBÄxáÚ8c¸Ã «H: @Pè<<À èžó«´x—+h ]q•rŒé(ÄcÈp› š%P B`À‰tÊ'«ÈéÈFŒ<Tž¸à€sò5ä8 `€„s€ à•˜ë• ¸Ù‘ ´+X¼¨8ŒÛSÉiø‚  Qˆ¡|—„;âxÄš{ÊÃd7ðÚù°fj&ãH؆ÿ¨Ê³³ÉÝ¡€.p¨€B³€JB‚˜)¸€Øé%w@™Øy,ÄK¡lHÙ‰C°Q„ÁD·¦DÌÜ|ˆÊ&D•Ì¢lÌ›áÌ›àD˜‚p3àaqˆ ó‚Œ,ˆ„[«Æ1à¢%ú¹'ùNèQFß‹œ×$¥ÄMÝTO¼ÈïÙß|Ť÷è5âÜãT‚0PÎ0ANQN¢â•Âpòë§Ú‘ ³€˜rP"jé' H†¤M§ƒÈô\Ï Í z øD>'Œ¬Tâ«dX²4 Ã`³ë¥¹l(*@%Ûƒçû?‚˜ÐÈÿ9‚t( ø¼ë'€ã&ÂÔPURïp­<@QIPLT²R±ZHÑÈ”€(€@3 ߨ©ä?'!‚¢;bKGPƀ鉅觱Y)qÆh\Ò;íŽؽ¬°ƒœˆFhœ¢ÐÒƒ+¬4JÑö€.e:%+€iH€cG–˜ ˜”;BÌ€F<Õûòñ0’z ¿ƒ€z™¢BmÕµb“ ÐIš"ˆ–;/ -$d©™Ði:Aøô´ÓP%Öµ #)U× , ‡ù¢¹š]鰆РMhºÆÁ†•³»"`ÇH¥TÛl²a-ÖqEžc]ŒÿÀˆW,qØAM³y†@ÔQ9!š!DÑÈi9›Ë(¯XÊ$%×}²c½ˆd5’ p¡´  çú%]®•DlHH]9óJo!â#ØŽ=>s$Œ@ÖQP¦3ÀƒÐ{®|³ ½<£Ô€¡Ô*L¥Y'4XeR¦‹@W•’ÌÜÁ‚Ș8`êÜX14ÚòVq­Ù¥]·cUX Ùe!x´‘˃Xˇ†’Ø$ Û ¥eÚ°µ9=MŒü Œô°¼š |° °Ð[±µÛO#Û»*ÛO0Á Ú;,•¨¸•†°Š)½ÛĽ9sudª*CWèÿq晽y’3hpã«ãšÓ»Ñ@¨œ>îcL@)hðèÉp0‹Ó³³›:adF3J¯…„8£c;–]‹Œ5îdF榀Ø@¥ÿœ"L´µiá€.nåýåçK^ª,NÓänT]ÞåôÍ£€ŠHŒçC¥q’i©ÞcneJnè©Ü¹V  æ]æhöd°‘4Þã$øæV®ÒCX;s¶‘aîæi^ç>ÎP¡^Ó¹m‚˜©ç]Π—‚Œ€7nþ‚¦fàÃt…6zVè]fh(è !©èæfi²Žé‘&é’VÜ ;ntpsec-1.1.0+dfsg1/docs/pic/gps167.jpg0000644000175000017500000003634513252364117017074 0ustar rlaagerrlaagerÿØÿàJFIFddÿìDucky<ÿîAdobedÀÿÛ„       ÿÀ,ÿÄ­ !1AQaq±"2r‘¡ÁBR‚²#Ñáb3s³4’¢ÂCc$%&6ðñÒSƒDt5“£ÃdâT1!AQ2aq"BÁ±ÿÚ ?ôÍy@P@P@P@P@P@P@P@P@P@P@P@PkÐkê:–›¦AÛêy`Aþ÷&T‰vx\о5›¼Š’wËÝÔÚœ~.¤ÙM;ˆÎ\19ʼn°í&`ªúEÅkã¬|Ñ»?zßŨǧ®·FT’­Å2#1µä•G]Ú§…_–7?ó®L:Ä:2k8³jy ÙÅ ö§ŒîVd ª}£O |ºç ¯ü——¯Œº¦+jÓqvxI*´¤ %U&ÆÃ¦§_’g …Õ´—ÔF˜™Øï©f÷%• Ü+ëx¶SƯœ=ï8¾õî¼~öT¸Æã^ׄo<â¶Þª˜«åñÇÚ˜x×¶U Ñq0§`%wÚ˜<£"<'Öµøzm×jar °Þ-C «r cÕ@XÐ@P@P@P@P…fÜ/C(`çÞJåÐ}kX¸Ž?˜ãí%ÿÓ‰þ*³[XÛ²G>Õÿ̆Ї.h¹z­¯ýk(Œ™•&>u˜4Ús<¹6af–åUn7ØÓÇщ:g2kZn£&N›¨K‰©Ì²ã;f溶ðò»]×ðT²pÝž¾œtæ}'R›3UŸSÉŒÁ4òHsódF±·gé"í:ªxD÷là÷‡Î:6NTðêóãeê #ÈÈÎzÊp¦ëÁãà;vRëæ6tÞô9ãDŸ#15I;lÔÉ“«·jNÛ†‹ìC‹Òé ÙêÛÓ»âçý,æe@ä6x[åêà,q•;û­oK×ÊÏvΓßg>ië›”ù#Q÷Ð-—©¨ƒ_BÌEŽà-RõÅ–ÆÎß¿;âãçÊí°ùELY™Š1±1™nf u î¥ë„ÚÑ;üç(!Îi-viÊ´9/»bb‘pÜ,vŠzª|pò±±¡ÿ˜>hŠ,ÕÉÆ‡_ˆÇhG»ãc[c+I¯–þ^£ÎÏÔöþbuÈÆlYøQkz„Œ§ º‡Ë-›ˆnéòÒõBvSúGùŠÔãl¸u]:-KP”©ÓðtÛŽÈí ³?§³vÚŸÎÊ™ÐûîÕ<§æ-.!!àãá7¦¦ûRffeÝ#o‚¥êY½÷Hr¯{™úÇ3béyzt0bæ³GBÎÒ#ð–^"Ö 6mÙSn¬Eó¹^uMO'!#ˆGÂÉÄK©;on†Zši,o}ñPqsîD“Íkå¬ídÇW*ŠÞ©s´)n¾§‡¹6¬›¼L4h;L7 ‘2À9!ïÃÇuÙ´Z§‰ä–Ä» Äku¬Š|ü5¿Š§Èps'L3"3Tøªü‘×ôók‰—Ç>kÔøéòC‹­io¾+¤Ž<â§Ç²ùÆCVÒÏüÔcÚ%|àTð«åKM+ÅïppõöŠ7yiãO(ªs{ü‹ ]gÈÈ͘_î°qå›wåÙcþ5juÚÍÝÎ5ßó'¬:Üz zt&öÎÖ$áòˆ‡œ×IÒç¶û{9æµÞ/:óÉûǘ2òq_cãéöÃĨÈÜ G×I×îUÌqˆ²•ÃXL×¹÷T9³ùf’ѯ’¶Ì“ƒ“ɘ,åsê®K¶vGÕ‚+Fµ÷,B|º¦å\ùh}ŒH6ù Rá—Þû¿¤$\S´+§bÛÀƒï^¦OÐq˜ÉÅ`>±ÂA‰ÖÉ—Óo% Ãka…c üàÓ£2ÉõòæØ)RLc$*ÆYèˆ6£™ü3÷i䪾…Ÿ% ’Q'cè{ëœÜ¢#?Ai•Ár ²fŽ=Y5Y8Gü<8¾Z%+‡’i–I1ÆÕ|¶8@~D é½ KË÷}¤øƒx„ í9ôÞ¥­N Žühñb–!²Xtµì ¥Ë“i«–oÀ‘¡Ä`%þsI^Öb?¥Ë“uE$lävQ2cå>ø°ÁÎÏký)¢žJ¬Á#®,Á¤1ád>ã)÷íEG¢†¢ð'"÷œ•LIv^¨Þñ”|1ã®ÅòÔ¤…”3(ËÈCÃoGPÖ[…m×*ïð\¤€–ËAÈùñ.ìÝ@û¶ÛýÜ#ÖøèVP$Ú§Ý㤺Ћe{¦™£ÅðŠ‹b^-™zž_ÕÓ4áîøÃÚ’ÜoäÇW%‰\u‡Ã$Ä€ïŠáÚ>³yMEϧ¡A·€Unì—´çíaáHÜ]!sXìâ˜õvŽd^,„[ÛŠ·ñ’+ŸWÛ[ìå˲ˆ†eÓ5¥\c˜sqæHøãwh–aœ†GR/·ßÁ\÷×8Ìá½ëyò䫬0ŒYs'Ž,8nÌä$„Œh<"çÀ+zg,ÕÛ‰‹Ë²»G:z€ ZoE`w‹Û®ƒ ì7[à I<—2C„ï,Š|â‹–¾F£dGÙϧãKïÀñ)[õÚÖ¦L´æäîRž.Æ]¢ÿv#~j¹2ÓÛr„Å‹1·¬°—Šþ2¤dËR>é;¾„¹ÇÓ;¿­$r¿Þ§$°øi“-Cܯ"v­2E’';¦yšf.׎ÔÉéôkOÜg(Í)–L¼Ù›æ ‰ª¾ÊÃñS&#^^ât‰§Ö³g„z˜Î#HGÔˆGzJz5¤î"2œzü±â ØÂ!öŒcˆü4Êb5Oqœ\I‡®câc·¬˜øå%ojfgj¹\F§ÿ¹’ C`ê|õ²8$—$ÒKpˆS0Äjäw%ÍÑ9š)0%uÚÙÙrI“8ð¤eV1LÄñie÷MÌí’Ë©eì!ó²Éà |7®$Ë_»¬ç9¥|y!‹Už{³d,0(oTŒx¬ÄxÚ·6–¥ëôå­òg6ixËû×Y1î8q1ž,<5¾ÏH†íu”K¯¢ fØpa”’v¶•¢'ërH$øOÇWÙ´žäL=¬ZQ“g¹éÃÞó¤ð<Ä· òù(ƒþÞ;fè…ýç(ûÖ¢÷êO™«Q[:NvSŒ¬\#cq¬kD³ŸÊ‡Û|Sãªa1/i¯¼j2K­eÒeþD mohŸL­‰7•œ*è&Ä@TQù*,£ÉEËÀöª„/³fÁôŽÊ…#:%žb}%¶âUsî›Qƒ3Ÿ´¸ B#2‰¼8°ZÏoÚší뇘¿j‡ôÊ5Ï«‡NÎQD[0uWW3~é~.Éxº …éƒ'TXXlT (@´@¢(€ ( € ( ¢¢ù‘óJ”aþÐÈâ? „%ð…c~NQnN“•«Œ©¦I‰ vJrªŽÑÞRU»vv>¿Ã\v¹¿£· päe×pHR'8Ó¤®@bWSºCé¬cj‚ï½}s|î7Sîêæ1±‰âw¸² mÛZ¿tf\k\ZL…8Sdܽ‚hºm'ªI@<_Žº¹á%ƒ êê…Oc˘ͱ†õéHIàúÌœæ\jºÌXø™pa †!!X#xãP 7.ÚÀuí®½}YåÇ~ÌL¬ù¼Û̺\hùZÆ,Ï ,˜±#~ȤM‚¯fšë9_ǽ·Ì“9Å0v<:^£C TLˆ]”oµÝÅüø}3+;vÝvñÚbÆž‰ÞÖ·ªÎaƒ@‚BƒŠI)ãU,¾³¯V׆·íš¦Ï?åÅ*C‘¡ÚWõ#‹?³{+"ÆM[ÓcìêÃ3¼ý;#jZ6¥ˆ’ yTž«¤µºöŽšöʰhÉ¢ëØ­“¥dvËx˜–2wC´_¯ufÇIr’¨€ (@PàÄ‚u\oòQT¤Ôµ?QÁ÷̲’áâ{‹B«éÙH?t›Dë1]¾‰"¸íœºI0µé‹”4Üa–¼=˜2§Ñ¹$/V»G5½¢NF€[Ñ[õë?åü5þ.]©kºfž8rf^Ô ˜°YŸëÃË[c1QÕ9ßPÈ'}Ö6Ø©¥+_­­‚‰k3“uœöí³[Ü m¤¸¼Íâ^-0¼®G/i:Zßfµ›&K4‡ËÑä«i„™aí’jepN#}µ˜¢¨ÈmµT¬…Et~âÒüͨ¾ÏG‹túR§à®}¼ýÎÌžÆö[Î*u{µØˆ®®b€7«[½ð§íAú婟Yû¦Ü8÷&ØsF=%ÿVÕëêååîáÖtœmU38Š|v%YlÄqoV[ö«ÛÕ7oð6þ>Öã9lcaG…¦{¬lYc‰Ço$‚IøMnkpãù_‘{»6ÞúeRîÈß÷³ò«=\3ßì‘Öùc?3Y÷Ìyc‘RîÇÒ“fÁÒ:vTÛKœ¾Oåþ6ý›Íµ¸¾wÙË9=$4Dׯë]œ>§UÆÈêór1ùã8Ø„ËYqò¡ÆÎ/âe¼ûOJ÷k˻޸º ½@PDjZ®zêði:rc¬íŒùÙYyœ}ŒP#öbÉVvfð€ÚœþxŸ '1é²¼ðeÏ‘"gl>à ¡ï"½ÕhÝWCMæŸÞZÃ`cሢHašFÉbÉáž!(hñŠÝã[ð³õº* ýGVO›9a’EÔ29càáŽW§$5ŽàhýåŠuoÝHZLÅ€äÌ]bŽö^Õ¯è—ùƒ¦Ô¸\Í•¨¦š"ÌÇÏhšt‡'XOd¦Åø˜pðßfý´fƒ‰ÿ˜Ìü¼eÓcÅwY'uB±ß‰‡›lÛXÿ/á»ö.I¦r^«›is›Ü`m¼'Ò”ý^-tà v— éz`þ©íO­þ”‡ëÞJep‘ƒéŠ.iB_mƒÑP¥«îÙV/³!Q—Nî/¬ë}X×ÃéIÿã\ûx]>çHæOÎcùÅNŸv»=‘®ØsTCs–‘¬ò®¥¦ãmÉ–0ð'Òx˜H«õ¸mR¥yûNÏŸLÕ"ÊTþ³‡!A%ÔÜ]]ox¯N›cÕçÚfad—šô †24Y˜Ò´ã(²å¢‹.ÂÃbø&ºùÇžõ_£gœôŒ=?"^\†u>ïBcn&^ÆåÙmâ©·dÂN«Æœ£¬Á¦¶LSH"íû"’±`—‰øŠ±PÄREíS¯i‡^Ýo¢Ëû÷æwM^?rÂ3\±^Õ,Š—–Þ•«¦f{­ió²Ë¹8¹Y°åeÏ2ÑÀ_‹oë[i;ú+Ÿm˜tëŸÙ¥Ýˆ'Ÿtpé%>A í®[q^Í€®¢€ mòqcWi&|\eGâkÜô ¦%ÌÅŠH£’@aÅÚx€ð‹Ðkj4YÓcfG<øyØêˊʳ’ÜHÁÖD‘ÀÙ”íÛA©ƒÊNVDaå“eÛðHf—1ÕåšBW׺ pØ ¹fè9YºÖvN.G½ãcL’pìÆH!»{”á¹=5 ÙÕ´É3äÓ$XÆŸk‚ ãXÕ×€[q<{êʨAÊ9k¯ûò QÅ© EõoLgö\68Váá1ŸVüVáù·¤©„¶‹¦dãË›¨j’꺌—£$¤PFHƒ2lxv›X“QjRˆåýìØê¸†Ã‰PÇxØwVgÝfìþª1ð›Ö«$â¢Ú©a ÊÒûm@ ÔZ[Ð(¿EX22:·p‰|­vSÑ:|,çä®]¼.œº2zøÞ'ó­:}×± ]\ËTAOï'DѲtµÍŸÍíãC•ÀR¥[au±;ºkZÛ–7ŽnÚ’NÈ8}–qòÖòçƒ-ËZa‰•‰îm¹P|ܯæ4ò\ ~NË›&(#Ê„4α«2°»/kî½\¬Ž¯È½ÛãòÆD™ÙYC;Ueh’DB‘D‡ÖìÃ]‹6âÇ¢¹í¾}#®ºás¬( (#²¹wHÊšI¦…Œ“e!ÚÍp«»æÔÈj>VÒ¢[CÚÄ¡™€WÝŰÕr2ÅåÜlLYñ±§•#Ÿ³°,l¢"¯¢ÀXÚÛ*d5&œÂP5|€’qÙ.À=ÿ8í¶Î¡ÐehºÌª!—iŠTv±ÍrIXZë‡mú<4QŸÌc°Š|ȤƎhÙÖ5{8 ­Ê“n;/{ôÛe_Dg?þJ¹Fxc†[v‘ˆËœø£`8—o˜ílÊŠwMuµ9°*bIÜ£egº*Ü›#Óé¶áDIPrîõÛûbÕù¯òÖgÜé~Õõ¦EHTÐ5Pµ·­E(¤£>šŠëÂ'Ýëò[çc%ü’åÝìºr¾s&ü_ŸÉ§Oºö!««‘h ”EZ+œÿþ ˜üÍMyg~릺aÌ^(…éª{¶ô¿û®ýD_¬iØúmã5ÅÔœTèô z€¢‹Ñl¢ ÑK@Q«½sý¹þ~ÂÖ'Ý]/Û‹ÖØ4?Ž‹Ï-P_ÁD÷-ê)Òô+!Aë¢aØ{„OìýrO¥< eüµÇµ­>ê¼ó/ü¯×þM:½ÚìB×g"ÐzÖ©‘£æj“¯xq4½˜ØY†Å[þS*Z8&¹ÏÜÙ«³ûÖ{Ç0uÄ*B„n°µÍºÉ®ºéŽ\¶Ùºö°»²K{J§äªÎ/3êÃah˜u”üUÁžN¯6gZ([ÅÄ>Za2uy¾[ޱÒ¾žLrKÅŸÏLÿfC\¼k·”mÃÞÏ!K¿>Xoþ÷ao*†¦)åñw…Ȳîâ‚z¼mEEÌnÁÍ<­5»kú€ÉˆŒŠ’ªB œYÅàȆo r£ý’j¡ñÇr<ôRoR2o[±wÏΫëûŒÞÇ+öi:Ú»7ñûð×Õ€ÈÒð¥[ú\h’ìàTðO7Få.oÓ9ŸO|œ5h2 !r𤠼dú¦ãc+t5bÆÓtW$ïTߘÀ=>ÂV'Ý[¿lSklÿP@·6ªB²йVW¨lñôÐvîâRܳ¨¿CfØ}X—ð×ÞZëæ­ÜÉ»ÆÿɧWºö!vr-ÐW;Çÿë_¡_Ö¥J›póÉ"÷øzrÅÇ¢Ýu‹ÂÅוGõ4ñWÆü¼y1Ú°Õ絉ßRêžE°µKªùz SÂryÜ^âÆ“HyÒñ7I¸«ì×ÉX<¿¯?_)󊹿RoZï¥ioëá@ßð×ðV§nÿZ³°Ãòæ‚ûð"Ïùˆ­ÿ³Ù=ɹ¶å-‰¶1OfG-_öûg»¤ík¿$è§Ô|„ñ8?iMnþgèÖ½°Ëò.ƒ2e꺡óZÿôö÷Žšî¯ëz4ÚNJDÒ c•xâ \bëïü_ÊÚÛôt¼z4xöשž] ¹I¤Ù—±¾ ™ •‘8~ Ö7t×—h®nŽEÞ™þòŸd}„¬Îk{}±NªçNÚ( ÑTj ¢”UÀ[ì¨2Ó¸Õ·&d7ÒÏ—gŠ4®¼·×î´ó/«ã0«Õîv!+³‘h ïx¿àMoô úÄ©Snw6¯F\nÃÑ#Á¾³· …Û•¿c[WÄü¿MØíá> q¢•œQRÔ‘œúZÿ% ^š4S²™ oôðTöªU«Jû+;:k²¡Ï„ûÆþÏñ…}?ùSÓkû=:ÜÅ_§Á_SÙ§CîD_šóÑÀŽT©¿šGi®NŽ?ÞƒyߦÂßÅZμֶâ*7òÖ˜‚û(´lÝS /¾ªõP ŠªR-(4#¾w$ä@Nã™9Å÷–ºýÖ>dõ1½§ó u{¯b»9–ˆZ ÷xƒû‰®ÓÖ-J›pó¹øëÐåX·©knŸauåSýQG‚¾'åýìöp°:kŒ¥¸;)8KŒ‹ì먙-X´ žÁzj{6RM2 ´L ™\ U©@SÙ¨¨s×íxKýã×Ñÿ—ÆÏW_ Ç‚¾¥k—Fî<_™µêÀóÌ•ÝuåÙ«›n7Þsßš%èá¿Äg^k{q;ֲȽõP(; P_}¨Œ†óSÙK~ª¡E¨2îdÝþ!ëÈÉ#ÿPן³—N¿t÷2~oÚ0«Õîv!+³ Z ÿx_àMsþ›ÿ‘jTÛ‡ˆÜ~:ôW+ìF„Ölô"çÊ¿²/M|_ÌŸÝϵ`Ù\cÁµõpÁoL.KÑQrZ‹’Ô­Í…BRûêáe.Úζ˜%À]ôönr¦óåý÷Á }ºú_ò¾Ý¿w«Yè¬ô×Óâ5'¸À¿«7V†e¬ïÃzáØë›£‹w÷朑U˜|uy­ïÄUÉÙZ¬ûi :þ*–€uÕ!nÂjx-o½|5Êë´öYÒ}r±[tŠ|µ›Ÿ¢üUâ;˜¹©ð³ãRßf¾%'Ÿlu<@:1üîkëÿÌŸ×oÝ»0¬Úíp,+è$ç.ÜRÿiëg¤c@>Mgw]yuÑ¿Ë\ÛpÞð <Õ›í¿ë³­å¾Î'쮃[¬šˆ(¥«AãÝD*勤·ª2gŽƒ wÔW¤û®w{¡Ûq…Ï”Êæ¼ýœºuð‘æOÌã{mök]\ÔìA×g!@P-QPï—´,½&MB|_PDžö¥I7 ÊG–šÜV6ÕÍ–´†ÜŽžËŸ–õÕŒnTÁù³Ì¿Áo’™Lzš~R]¼goÒAò¹ZmùS,z¹5º ²þ™ôJa¹gU_TDþöóW&)¦Ðu…³ßÙeo–™k>†NÔ£õ±eêR|שìÎM4s'¯§€«8«Ù‡ßm¨Kê=â×ë©bLå—X|§Œ®NBú²°:ÎÝZý™:ºŽjFfðV/FŸC7>‡“]ÕStÇËYÿSBíZy¹¹9“ rÉ TÀ€+·WTÒcSc"·ŸFd¹u.â‡õÝtôvâÿ]ëºiË­.ñ\Ý'ŸMù«7ÛÖ5gOv÷öý•ðMmÎ µ·4ݵBÐ( ÔRÞ‰ z¹0Ívíè·šƒÓ=ÛÅÙr‚ŸýUoá1o–¼ÝœºéÃw˜ÿ1úCök]\§b »9@´E=ÿ†äý<^sVr›pçÕÏ‚P*D/Mþ:⩑娥¹AÄÝdŠ.4Q?¯?´ ùÅ }7N_#â@<ÕS@ÒþX/…Y—Ìi•Á–å(îíSÄ÷óƒRÔ’nTÄ>®D«ã ß «“_”þï,x8ÓðeL7*êÕšþŸ5\§©ý+¹—UÌ8¸QÀÒ*—g’`Š/´_§ª³i3—cä.J•t¹b’a“¨å²¾dèAÀHã¾Þ¹Úwšçn]u× BúÃÇQ\ž<Ï›í¿ë±§þºoíû «¥`µ }•}ÂÞ¤‡mW?†¨Ã#'²2dB›ÙºOPëñRµýGXÈ0é±àO^YÀIüef½ÈhS‘¹yY¸˜iøåšÖ¹( Ù^Mùz4àÿ1þÏéÙ­urÏb »¹  ÔÕõ8t­'3SxâÃ…¦1ßÖ#Õ[þSXRÑÁ5ž~æÝ]ŸÞõLw`ã© Øú .mÖMuðÕڣS_ÖþÑiTü•¦sêy9ŸUÒìœxRÞb(°òófPõ±£cù,ÃðÕ‘'6§ó˜Œ=—Îg ^O§5iÇÖŽeðÙO˜ÕÂSÉÌz;o•ÓÚFù/Ië¬éM»21~‚JùÀ¨™?V,Ÿ›ÈüN§å©†½ÄlÚ<BÀî5Ü´ªMŸŽ€¿ú©S%µUžïöÖIêÅo¶µ¸kW@¬:=uñŠÎfüË›×Æßm«:ë}žß²Ó÷¥Oã¢Á¾ôA{ P«´\î¨4µoIƒµÉkÈßšÇ_]ÏÈ<5xŒL-_š2†^{˜4ä?v‹±@êAÒzÚ˜L®˜Øø˜X}†:â@l£¤Ûy=t²ådìùWDKX®ŸŠðö+^M¹wÓ†Çû,?¤þI®\³Ø‚®ÎB€è ¯w‡þÖÿ@?X´©xy嫾lY´^CÍÔôèóŽdxË0&(Ù ’ ÛˆV×ꮓ«1ÃnÛ.wi¬­ÌYxÒøhŸ#RõŸ?èÒ—9¥/àmû¹–çø\4øêÎùô@I‘JñJ¦9c%]Y”à×:í,§ 9ã&,ï z²ˆ”ø˜ V¦µ·×êiÁŽâE(zœóÔ‘fñ€hÉôXà4œBð¯P©xi’ÝMÕ˜x‰j¤§—79¡“*ÔíøjE¾§“ZÕÒÖËûVo8¦SëÌÚºüøÜ~Tcäµòs^xõà‰úíÄ¿)¦& ØNn?oO Ÿ„Säæ¼n8eO yˆ§ˆµò9rÎ6¯7¾f{˜šÎ'™P±plXvó²±´¸]/«¬ He`]H*AÜA5ÍÕ”cïÆ(<ÿÎûÉ›íþѬiÃ}“×øC_ðVØbOÁVP^ BÕLvÖßдD0sN>˜ Z|ó¹>l}Eíæ©W(Ý—2³§ýé­³;HC,-ë0èâ5|dek2¤h-dD°U¢®GSîǺ<`C®s$mŽm&&žn²dêòt¤G«{x·òß³7®™w€ªª€@°l ó»ÈŠæ/Øâý(û&ºurÇgîâJôîñO÷Zýþ±(›póÉÚ<›+ÑkŽ[–³pñ¹wJIæX™ñÙÐ6ˬWg=^ˆÚkÓ8y;=v©hõ6G(™P´€(x¬Ö ÚýY0 )‘’¤—·f®b㧇ÕéÛWÆaœ¹Lx"Mdiêä+dû°“y´à¿ŽÕÂLÜ=žX×.ŽÝÞò¿A Ê@±LüFÝ&÷_ŠºøGžvmõjËݦŠß›Ëʪæ7Ôð‹óTn¡Ý¤±c¼¸9æi#„ưlNþ­•›ÖÔîªn4dÍñ™&˜„Ž5ÞÌO†¹ã5è»b,wÍ­Öd=!&[ápÖ¯]qù§»RNPæˆïÅ¥Ê}‚öXÓÂå¹Ý­Gåéšž"qeáÏŽ—·‘²®ßÊ"Õœ†ÝÓ š—Æ‚t‹ÄPÅeŽ5Ufý“Òæè<:ÿ1®}ÏnÓú:fF¿…lø’¤‚HJíÁÈxvüÐ6øÅvËÉ 0iï 37iäx”7lôGfdG‹výýULÆÔ°òÎBc³ âY•£táknôÕvøªQ˹,_™ôßÒ7Øjå§Üõv_êë–¾Ëm®¯!H4ùѤ¸0Ê¡âx\:6ÐG ¥]kˆ%ì§Á^l½®¿ÜZÛO×O¼câ=sìövÒ:|_œ_®m¼ñÍ$`Ì·Ò58o³”IðV« G¤M­¼õQÍ‘4,®"Š=²JÛ¨ªf¥¬ê:þIÓ´´eÅ>¹õYÇÒsóWÁUš°hº&•ã´™L-$öÝàKîp%1àÈÊʇÈËÈq>f•˜øZœM‰•ádZÿ”¤ìe=W_,¸Z¼ò§?è˜úF>sÈ={+×®òÇ—³Lܬ°ó+K»S…I蓉ñ€­1á[Ðë:,ÂñjÎ7›LŸ)¢x×.æŒÌl¾`ÎÉÇq, áuÜÁ)#Á²¸{½zLkêêyPOŸ ¢=6d<±ÚJ¤lzª~soèß]ÞM¢*^_Ô–l׉ ÷|µDqñÅh uìãâÚ6ÆM€µújOEÉÔÀÕcÍÄ’ž :2½¶Ÿüm¶V"Ïq± Pl#ѾʹLú(:fÞoÇÙ¿Pßÿן¯—·oM]K+JÓò\¼Ð†“ˆ¿$#Š÷èW|‘ß41º{”S\XKc3 Ù×oÕ«óú1z0§?+sž•Cè¹ÑÍ ‰AÊ8”Þä `vÖu윺m=1…”w‹$ ?yhÙ8î¦Ã‰ý6*Ûá®ÿ&¿Wˆì=ærÔ–ã\ˆ‰èáGû-ZóŸV~*'ŸùoÝ&he–IŠ0ŽÉÔ–"ÂäìËKfu\¨œ±™Ÿ®àee7 1°ÛÂJßŶ¹ié^ŽÉ˜ë‘dẫ")AÜc‘[Ìk»Éu°éúTۮƉZú §å³l ¤“Ñè—…“ÕÄ£*âÕå¯tåØûÐõsÿÜŒ|V7uÖ:Rzëã®m¼çÌ"úÖY;ƒ ª+:pßg*Æ»ÌXºZðK˜ãîàwQ{nºÂµ£ê:öWï JF\s¸î,ÍŒt/†˜J¸C0DÀ‚(cH×p¢&9W”õîkÕF›£ÂÔÉË’â t'Ö‘€þ ¦–Ⱦµé¾HäM“´Ã‡§)—*[íF@;iØuÛÕAóPl=µåÛ{]õ× e¡@P>½c>8 A¶Hë®Ý^î]ˆy0pdüæ4/ãtÞÒröŠæþè©~˜ÙÓÌi€Äœ­¦ͼñxœ0øVù¯IM6vŽc"É! M€'zÔÏ©…}sdŽEìKq´›ôî±­d[4ܳ"¯þšµS1>ʃeâÁj ª¥ (Œƒ°ÜÄxEdf‘‡ 1`zhøè<+GÉ¿¼éØ“ßy“&? Z'ŒEäò#å_µÐ±=1!ˆü1•¦Ré’÷EÈ2\®ðþë*[#—|ª|q“Üg,9âÆÔ3qG†Qñª޵;6ú¥êËQû–Ô!ÛÌòÇmÁâ‘>8åù+S¿foKS7º¾ðäÇlßÐfc¶ÆŠI§@èñ#yêÞê“«3Ý>‰Ö?tÆ*M»a“f£¬îk>q|oÑÖy+•#å tþÔddÉ!Ÿ2u+HÀ ;xT Ö-ÍuÖa>ž°¨¯(÷…Í>ã­fáâ,ÛŽ9ôcºÃç5g^;~ä‰Êí3ŒýZîÏé¬ ë9?:CòVðåÊÐÄ6°`tTµeäïu¾tÏdÄ>ë¥@Ü9Ú£­Ñ7CçËc»péðãk‰êÔ™¯Lò×,蜵¤Ç¥høý†2mw;e•úd•÷³ŸÄ6WŸm­w×\%+*( € (!µïÚ öÎ+·W˱Ñ]X Z xî0Çô„àšÏ¸¥.Ù~P«¿L…•E÷ÕQn ÚŒÑ £*ÔÌK1²¨%PM)¸C{¼ÀqeRlzÀkÐd2áéžÜn>Js°‰°ž;õüv¨R­ê°oH="€¢’ˆ E*ïÿNª#˺ކ9·RÔå¶D²ŽÉO«‘EíÒÛ*iço®ÌXï¿”šÛŸ Çv½Õjã*çæ³árÔmfÉ]’dlSÿ7ešNŽ‹ØÛyk—¤tÝ3NÒôø4í710q—‚ xÅ•GÊNòNÒwךܻɆÍE@P.¼WÞq׈(Ä)"çÒÛ«Šåº<« àüÕ‚m Z ?]—]™ÈQá ŠÏ¹Z:,8ÌýäÃ{ž¿­`LÁ‰@úD> X2½Ðƒ ¯Ùfýý“A§¨fäGŸ¥àã2«å4’å9ŠbcÇw`K;"ƒA›ÌšŽ$ÐÆ±Å‘Úc¬ò¼f>ÚuŠŸH”HÊ¢ö]•FÆ_4@™X˜Â5ËX^6¸úĦ8¶8'j¤Žz‚ÒC,£Ö4¬J}4áß/åYHv`B±³¿hJl`¾Õúª`9¥j&¢¼ZsLÊ'vQ2* ׎>3~tô¼T¡Øµ .X£ž-RÐËš'i+Æ-wS*í_HmWe!gD·Ùoký%Ü|”æLaŒÊ¥œ'ú m,Çhܪj+GL×°3’7ÇÈYVG1”ôx—Ó(Ž2U‘ÊîÞ+3l­ÖÄ éðæ­2óN½"®­–Ìlí'Ä)¤þ­ö}ΉÝwsSk}޹ÌÑ<)³áéÍt—(o'ÎH@ÞÞ¿;öatËÐÇQ¤Q"Å j(EU ª6y­z$Áh € ( € æÍQÇ‚FÃ991¿È—ãXÈ$‹Ž‹×N»êçÙ÷\Y´l´ä› 'B¸½¬öÀ‚¡Øì ËÅo­ù3!Ÿü›6™¿x¹LHLÙS²B­{*+°ÚXmðUò¬øÓg‰v:²Ûé)zÖ@â–ˆ[Ð-ñ¾¶†>”HǬ¨üœ\n„á?Ì¿dŠ[Õ–Qõø¾Ðj³È¿£‘qÔè§ìðÐ/`&ð%ùZƒ \¨chÐqX3‡â\³„ËÔ0q œ¬ˆàí£Ú^¥«‚èiÓ0Xsqäf6UY£$“ÐïC¤ôc“ÀöM‡wƒû‘£þ…Ã#W’½õa ( € ( € ( € ( € ( æ ^&ë jL|y?9 oí*Ÿ8«š`Ãi:c^øÈ/ôn¾b+^wêÏŒ2Úšw+§‰ÉûW«òìŸ4ü¹Œ}Iä_h+³Zùj|f[–åù¹*}¤#ÌM_›ôOŒËòþ ¾©üLGœV§lO i´}MvööYOËVvjžËag'­ ðð“æ«å>©Še®»=Dç­#ëÐh#gÎXµI¢xZflIß5$qK <6$/hX\ö®šÏF”郑2dç‹5š8âqÆã‚>3*» ‚êGX5ÎV¬Hi™SͤÈ­¬Á‚uˆs,®±E^º<¹óçÐI~œ=8nÝØ¯§&(Ht¨;§)ÿï*hÒÐy#¼Ö<:ÊöîsÂ*»¦uí¹_wº'Ô )ƒÄG(¯Œg`wY¸¶Ü^ñ5èàƒ¯Õ~×a§›cÝ!`w„²¡)ŽÇÊóIuZ‰ª”b[ê%ÆVJ¬à¦uùÙ¶M{l(à)‰”QÆk4òŠGIÆŠÕ‡ä’Löˆ (ª˜Ó5ÞF¡XîdÊ*|ð±É#„泈æƒYµÈ$eM²ˆ’ ƒdÚ ­H(a~êg2 dˆ²˜)æGŒä­"'zt5ÕfA&Æ&Fƒ„fi‚5½ø^ž»í‰%–º ˆxŠÄRè¡_ì…œ/LñBƒ0ÿ鬴"5iE YZXDI¦$™”[n=p[f· ´G,мbH Ì’ÂgžñÚkri¥Yë¶EmWMµ…¤ë¸ž â+°#Ù†Ÿ•0´Â„VPˆ5W°ÐÓ5ƒ²+ÐôÛY_Å+ê@¾rë^”&ÐÐd¢D.¹/ÜZº"Åx2ò‰ï~z1CUXñ/E›ÈelðM;Y÷¯š=ܵ3—¬ÐMR]œ§ÇÙ ‹]O76—2H] ­ZdÚ¦×TVQ•X鸯ÆùtuŠ4™Í"}ß°ë"û„ +èaÒÏÇtfà¬òÚÝäÐÀ?á„uB§ëg­ÈÙÙ Y{ÿ47dYrú³G6>‘ SÔ2ÝX¬¸QÜ{ʱ½U-Â_ÝYdÖÊ qÒ\­q|‡´ðßÕ–uwɸB“½;©z„¡žµãÝ%*íF·mÐXj[n²ãt·wEg1†üí“•ÒìS²EŽz{ßf9™m|ÆÈž±x%Ä ÜpÇ|aÄ‹ÊQCê‰Ï¸|X’Ïdï6•ßög™!›ç*‚Pû‚@+à?Ÿ„¹šäd²=cAdBÛÉvfnD,NŸDf—_ÉFM)žð@b»¾@…`AIµüܰàBâÌ üÒ¿ÿùï$0!œZ&2ö8&ÊWÆ/àY&mБaAÿF&“pA„!W{IL$A÷±|RÂFâÄu,ydU¸˜X £A€& 8t¡±Òâ‘*ñ] »á½šfšñ-Dqö¹™@bâ7à \a± å:ø8‚¤f%MŠr8Â”Ü \•£ˆæPÂ,\È/có tˆJ B.¼ß23ºä€7*ã ¦S’õe7Š‘ŸE ‰>2‘¿£É\€¸Á9öñ.²œ#"¿·8[™ ‹º  Á° m& ­pa¼ÊH¹µ@D'Ý ¥mbDÄŸømD K -Â7ÂÄ•‹›®ö»ªÄÅF Ü |S.Læ™V)£\Šr#ǰTZàa¢ÿOUöqiŽ[Î6Ý2P–øÊ›A±QO²tí‚„ÿ– Ïx*éƒB™ì†%”±ä][)(À„„‘›X‰–²Ç1+´â3/hE +ZF¦]ôWýô•cvÏÌì)eH¹EÒZRçf(Â¥=+´d&jO £©<×’¶ l§ á(HZ…%E¤ësW´w™´$„ó˜¡Æëâ¥é\šé=9v5­¦bFµeƒ$˜àá¦6ß{ß^hOfÆHŒø”êTK2)6!f‡Ž¡>{оV¤}0:)ÜÄZÒBh'WãÍcÊUe:¶…ÿ»‹d‹¿ÿôó³ËÜÓ(÷„,«¢œ¯ÔÈ‹¦s6&z‹…¬lýÛ¨²6³“¥pû¢É=Ý„·ÓŒâ‰¸ÚÊU“¸ÏAŒ B±‰MDÃ*¬8ïw•(Jåʵ…£¬©5EKj÷qªßõlh˜Û"µ%žÈtKš"â¼®(È[ÞS(鬊õHk]9xb§eŒeGJV´¡¡ÆJj´ôTòK†%+· ò†"™a0‚7±Œ›&Ãõßrkú©ÂST)–‰ÅÒY%úŽ® r)3`ç4`9E%Šq¢Áј±–ÕK@ç¸ÂÞêR¤7GÏ:Р7ìÔ°¬´ž[a³ÿ(‰ Öʸµ‡M„"ŒV(†µ¬åPòÆòœ0\%Ëž‘-SÀQ"LOÒa-:Äÿ%qQllµm±²M4ÑyÀ:ï9 „žgìgºŠÄD†Ò\š™IŠI-kÛ`§„2bºê‚”?§DÉGë%’¢bSlÊ$o1LQó¹Æ<¬`-`ÈÆ÷Õ›”Ë•HÖUTÄ=†H(W÷ÕÊùÄ0ë-‹,MK›'uæ´±È+Qž‚ϾÎÈ ÌÕŠk´"ÕSiudá …”À û’m]îÈ2ݤÑuLÝÍDm$æËøÐ>ìÆ8¤"ø¨¬„¢1p¡– ˆ±øÿöìþDõïÈJÞÜc¦möP%¬ ež^¢OÛüT¨X:&–¤Œ&ƒøŠ,I磌SAû.i§# dªÜelXW€“OÑŠ•tç8T#˜£Qpƒ\séF {ú—•î4¶û)­`Qr€HŒe‰ÌZ4N!ã›Z´"Ë»ÓÕ"ì¶$Pc`î?.dyÆç¥ˆ¥R‚™Áj4Øè r”(L&“eEçW`öŒ«m·¶ŽÓ©Ó±îˆh[‰!c-;k[LI}eì=œ\àÆz<‚#/‘ã¥Ú :Ð@·*Ï´\z°‚ÁP(<Ö…71E né—Àõ$¾ÿcØ[t~Ì9Í)ì“Ò>#¸/.1!ôw‘{ÂVèBŽýwoÈߊY 0€?à^$•FfW4wvbô. hpP°¦~qC¡ç7²×&ˆp!TÆæ~¤F_¬a2qoÍ¥z9fÂW^­p+vó?0€6¨©fIO°€hp{Ð\Žav{Pph î–P ÃL(T##ÑÖ` ±P Õae#n¤~ugwRA‚| ³s_*³ Å0a÷´ÐçhS€?Pj@dj‘ÞÛ4õäƒÝ·€{€‚ÝÄë2E± Õ0®pˆˆ Qh ¾¢ÿ€…Ut`…Bñ‚\X^Ëà‡Ðu=Çxÿ³–XqySðYÐ/Py¡ñR@zvX}µ¡(}Pp·a Mr5‡•o± ¯€ˆ‡ø ¯…°Pˆ‡X àY‰ÑuH—è~º8xPPgp%h./ègá×`72ãBX}W}‹?X}{ }ØD[£{B!=i'Àè Ä(ŒÂø °@ °pˆ±NVPw2 k]`2€8’ˆ82|v à¥4–IÀç?]ð‚0FÒv ¹5»R‡g7zÛWp=±€V°‡ÕhÒ)%D þH ÃØÁ¸Ãÿh 0R”Yø“uwt(Q{Y^šè›` Ú8Q «`u VWðEyÃ*¯"'È”€zÀƒ×yuXv}˜*µ5GèAY‡k‰ˆÄÓpŒ× l )YÈ ’”Öatòa‰E u‰¡™d† ƒ†VSz#:^ôW` `™’7’ŽaùŽ h À–Lå’Œq“ÁX ÄxŒ¥éÇhkðGЮ@ )Q@YtVðÙE×d‚°`Ég+9˜Q ‰‡˜.t þ'•åRŽ æbv—ŽØŽ˜’Xò}R‘kxšþø–Ãñ Ô°ßé Õÿ$±à pþ˜³±G2‡cp{ù“]àDÙ›åU  œQ˜Äù‰Âw :‰åã+ð¥@}îÈŽ‹xp_åmg ªàâ ÞYšŒÇø# 4ù kð±x»ÑJYˆ›Ì”YXq#!eå Ëp ËpŸ—¸ ü9Ë0œãg†€€†Ñ°_÷*2Ó Ð ’fws ¶{€÷Q¡1¦Ù: ÄHŒÖðà–% qõ){F'x ” ©…)uåÕwPY”ûi9°`-Öb1¨¡À@ ‰C+%v,&!15­˜•„ú¬°¤ÿ–·™¥O!bŒ9Öð Õðÿ؇8 ÔP Ñâ–G…'ªeŠ ù“ ¹ èpª$a^ӨŸ‘Ö&¦€£v6¤a£4¦Ð°=  ÃׄXÐ+½²C c}“чv‘Y2"”öA.r Px©Ç7 Žð²ªkžÕб`ñ©]07³Ù?²y!F—u‰4F”/Ö›¿9\À p”ö‘ñ72u‘å2æloZPHTQ-äyÑuPRP¬Ð ¥ÀI×P Ö ž…È`²„" X Áة糈ðè @ÙB¸ ‚™r" ¶e€I‚§ð‡ÿ ¯‡Ð$´:™¨3¶ ¬;T@q —SQÄLH«.òÁw¡p d’Öz ¯……øÔà#ÀÓ` ³ÐBl)¢r#1 ˆp, ‘¦¸ñBÐØ?œ“u•¯qo¡§S««¾) üj|;«é¥e5v Ñ@^ö?¹Š‰³°ß–„ {. ”ö§ÀwÑ¢†ÂqŒR¸©Çxj ÖÐBPžÃÈ8„¶ØÐ³à°Ú+ªL ȼÂVp<›'\oÚ7˜¡ 7¸„‰ŸX‘G+ /$£f^bf;Qa¦‡C‚ЙÖb§ð[(#0 GÿÁ Z tI¾¤9ži ÖpBlw ˆH a, Å?2³`V@+ÐP°x¼â)Æò*"ñx‰‹¼M«1pK2¸\Èw9Z "§ F¥ !_FÁ¶•Sír¹!ÁwT{^ꥫ ¾çö„Ö0…°ÀšÖ #&Ç 4yˆÖ@%*°'ad árÒ»¬Pw1?¯S¯¢ì¢Y`·Q£È‹‰ˆq€;1ì»K\^ÏK‚G‚¼Êk§AJdöUíB7þóLu Û;(#\FÖ@lÖ:Ã—ŠŒÔ &U£T¶ôÂ''äG8Œ_„Ÿ!-Š €qOÂ2í¢[˜ÇA{fÅ]ÿ˜·’s\àž|9S<ÉZÆWð ÎÉ€Ð6ÕÅ¥I Qoñò7¾±B@v œ&G€¸q©åZÂa­¦‰ˆÖÀ'ý£´°€g_<]Çá4a|À´û¶˜Ô\Y³»+ƒ¼¤F^5›¬ À:aº”ÜÍvö7«` Ï9Ïp Sžl¸,q.ð?¾˜SSŸ!¦p ³@(E-­l±×?¬W+¢[šµ­;hZ ºÕ?Q‘’áƒP­èHaÌ ¥pA{4 ûV1—ø‚Òl^D“™ájn'ˆ¶t/aœÞLÉY=ΰ 1ÃÏÿ h0‰V‘<‡¥RQƒö ±ot’˰E±ˆ+ Ô ­€}`Ikñ„… qY € [ó²¥§ë”cD¿ÌÐ7 á{1w€ÐÃm«¬À…5úÖ|°!͜ԺYõ‰AS=<&­ q ÜÒ\øÒ¯a K0ñ ’ÏIq#nçYÐs9ýu3s^×°±@ÆÐPˆ^t ^ôo˜B0ØJŒ…H% [þsÕ°–ÑÀ=~åG³ÛJ|,Çìƒ0Yøo¦£î7µ\ÈÖ\ðÈCª§3Ÿh,-DÈÆ’) Øî·‘$“oá ›\¢¨è“%í³õ[èRŠ©ÿv ÔеѾÑp^/µ7 óRŸ=ÓÙz©«ÀvìÆÂñšú·S ý:Y#$âB1°,— ‰#Ø›U#‡ RT5ÜØaÜä·R7_ö1³Ì=‚M0ˆÀ±kPÙ—í¬€Éi9•Š,"+­6JNQ›£è‘¢ Ä&†¾û9»’Љsš$âY¾d­Åy±® % 0d © ‚°'D¬(1…2`›‹—.;{‰7ÆÍáxhÜ/TÜõ4Fl7%<áÓŽÜ{(×p*ОØ` ÇÐ r¡Ø•‘%û'ÆavûR  «0 ..&«| _ÐvãT§0âÿ{ÂŒW™d­å Ô. ýäù½ý r2|‰Ö ØA~]”w,ÙÃ[ ߀ßðe,Œ*Cñéb‘õwá¨|Ùe”Ï#ÐL× Î$ÓGôvxO"*X™ˆÛµ‚¢Êˆ›‰03h< áÏe˜=.$ÂAšµÜ[Á×Ñéú3€² ‚²Ð¢+Àæ¸ËPÀ4û"&·wþB-E~Bdí2b2! ׿Þì«XÜ]pÙ³ð-¿ž¡ÆÅJ46@×è1a^€ SódìÜûçcÒþv S€L¬üÊ 3ÜZYÕ¾1\ ‡ 0fuI4óYwr´ýÿ›,ŠËhó®ÎgË(¿‡¥(²ÞiRu²ÒÔiZ D£ºÔ°¢.l„HNA˜¼h%íëQáD²¸‰CwYPc2ÎÐË Ð˜…šFБ“é#4Í(*ayÈ‚U”ßì”´ ß]$TâˆGH7ˆ‚PÑ+úÕ¢ü`e…ƒŠ â˜ZÏxÖ;ÑÉøT–+\d‰¹y™BŠñ…‰ ®Š¡zí\ã¥f3wYÃް t[²ÂHéa2Öê€Ãª`)× UNäÛ„)RÅDY¨#G:³¤ÿìQ›"´ÉUVa2 ‚„ì×nr¿‹l«†Â^ÕÐBì$ìš“N d ƒ QÐäÑÚµLíaÊtŒ)x£/P¦‰™EH~fÅ—Ì«3f; H0˜àzýRé^ ;¥M…˜$‘MA–“̉¼bÑ@D+¼€Ì`X»)TIlz ‘•d ÚÜ£ÃrÜdœ„lXÛV¢ËuÊ,Í ž0¨ùKˆˆÈ’=É·Oæ#7j"¨’C,É"ÏD’{L§Ò%EcZm„#\rš«d‘?ž™gf:(L2ÆÉ]ùQ¨­AX¬í»Ž®á,ÀT]ª)SÖáÿ<…aÛZA¶ b¨ƒœ|4¢‡¶¬–öü’Ró„ýôK:HÄ*]¬#;Ф\ÀŠJXQÒ¦¬ž£Ò¥€C,c†+±×OW=å¤Q«Y +‚Ù.M¬Z×XF%ÊL‘.47(uÝ5h ®‰³ ðÂp5r9“Xs T¥ìï´2MÏjDL›WR(Ó•D-#¥X'Š>¶ñË Ú ÉúöBÇž¬m­ÜE’P#ªÜF€´‰bíŽ"ìûÑ’ZIöU ‰ÙYâӬ䮢rÏ^ðá‹©XÓÄ 1ÒŒ]P»UL…/†¼"z%w#¡½{ÿ¼BZ‚5úvÇ ‚`Å·fp¿ùQ‘ha·ît³(ª?]Ãg–òÖàÌBˆ°õÈ혣’öb¢L]o*†VÕU1­¦ bŠVs€;i¹Äh j_<%èõ.u"Éë’…VKõ†Y? âMFzÄš÷"Ùq¼@­a§Ka“Y2ß(wG³+®l–¼Uz6y‘;ˆYäDƒÑtKÔZ½Øí±ãT¾‚`á¶–ªÌ¦Û\…}̪0÷¾¦³ňFœ8•,ì¸ÜÕ6U¬ÌB+XTa¡Ì»Jç,¬d èf6¦Ð¤S\\A4:1aê…— |ÛFGáãKr„¥ÿ®é¢”iÔ»ãÃë¬v—€ÜOŒD(¿ÔÁD«giuO¯Ê0sÑŠHôªSN1‰ÆÚ—ÿJ—›Nîó÷JôÔ6ÿÀh5MD0“ð×Ì."[hl:DÕÈjEÑÇÛ+Ðà O  Z€C–"èÛ_ë3¿%è[«ð|PðõóWަ‘U‡«ihÒï!4la­¿É—ŠóºLÂãp—†1–#ÑŠÞ+†eèƒvi ŽÐ®b`¬-IlS˜4 Òˆk—išÁë‘ ¾Mhùñƒx©Dðˆ—"/Ä2:˜Ú–˜/³‚V¨“@È·À‚lz?lÙàû "qŒPh£›ÁB„)c…Äò¾»/ØÁª„—jDäš~£Ä,Ù¼øb/€2úê‚Љ¬P0P žW!ò¨œZû“ú÷:À‰“ ýšŽY$œ 4´2Âè( ²§ÿ"‰1‘6“° I+L«Æ›˜¶ÿ‘ÝZV¨F2îx<Œ(ž¦ªš+/DH€ÄRIX4G˜hºÒèr*¸€© ŽPÇT4>E°Ó˜ô€ÂžPýh=rÅ¢-‰Àµ‹™9H·› )AH×ð#› X²‰R¸º+²†Ôö‰q*DDhPC„ë Ÿ–úH¶4:‹èP8é „ /hjì5—ÌèëÊ@ð‚Û,/p¸Y@1‘7Û¨žòŒ­ÛµÁŒ¬Ð$ ²B§»œ‹˜Ô¥d;2œ¤QÚEQÂy䢧Éï*:¤ïÉYà0ð¨"jlK¯u¬‚k³(:AÀË•²ÿ½1 È.P¼@x€Të‚èŒY¸/Ê%þ³žžˆ¾À'Òº“®™…Ú ÊVHðŒÎóêÂÔÀzZ†\†—Ó«r9™A&PÑKŒà‚-«J”[ °ÀŸ†´—•`«Ž´K}»2zCD,¸?\ˆßp|³Mœ0¤ÅâIP+PÉõ³ÁÎB)NËx 䉲𠍋$¨I*‡«²šT¸Â(Ì•xk2ŸÎœ’œ´°Ë¤‹–„‹C@ÏY\†=£,8ÇÈ@»"à‚븲(üD¹m„Ñ°Ž ê£ŒP·M ęˑ…ú°Ù‰†ÚtPt,j§ò{¼L€”,DÿŽ‹øÂ‹ð è¾(S<`UO/i8š”…L¤êx8ãD„D°¦/8 A„ðÊ—dJLý™i+ÅÛ.ì ¢· Ú"€mÒš˜mò…‰†Èo~=ê:†æ£¶UÁÉoÿ’ˆã…0Cl8'ŸæìB/µM!6:P¶,a®Y\R³p©iÇEÍ O!­Ø%1 h"‹'°ÛËë(jjî ¤¦è¦ñÃäšë/A &º¸'FáA ¨Î)Lo1r1¹N//=ï4êšÅ¶;£O I;,Å‹(Š›ˆá(‡@Æ">Õ›€ó.o‰Ûè®k8RúðC0gHm/]ˆ‡`Ÿ’ë»ä°ï^•©B¼QV°hõòý Ë‚L¤¯©&ŽH¡0¥­,™a±Å˜@à‚ƒôψ–/XTQI„#èƒ+›/ûâšÿåC‰^ÃÔs€¼;àÿçš®±ÏÎòÁ8<ƒ”í^·²Vc…H Ô80ÕC‹YÊ—5 âµÄ(Y˜áGŽ1¸gg€-ˆhN‹5 …Ú!EuìÞ°´Añt ÷ŽÅ"µM wŒ¨?ï€ÑYðð0aÅDX½ L…ë>ðáü@ Ò)TzD0èžq&uŠ÷–‘yV¬²|Ç'…‘A…;P–ȸtõPé;(–õ§Ý4ôNvUNVƒæ#ŸÞÏh›·Ñãã:'â`Ë´Õ—*“7Ah£gh&íÐf€ÿFçÂÇ0O%b¿Mîes˜øA­áNR{O…úîH„*cµÁMqêÆÿ¬_n¹·F1šâ ð$ ˜…4Fp†SˆƒPðð­a{Ã<ø*“½A¡ ]²îO:]^¹ôÿ~ð¿R¥¼Æ$¬Aàôa¡lzk…ƒ)ÏøŠ°ézá4¶ôšQ ˆMn:µåƒ„ p‰±P¡©S¡R uMP l7rÜh–"G’‰EPÉ-ul‰-—2gÒœÑåšËœ:wb³†“§FV5‡-:ÑŠˆfÉš…ˆÌ,/9Àòâj¬Z·rÍú"KY¿d»uЬ5#F¬a5­ÙªV‰dzAd QŸP§®]Ctàï@2,#P—@cÿ„Šãø1dÇ­¢«¼¬UÖVË(rî­ñ8w6µb%æÔS²¸pák€Ñø^;dê+V´/¦ÙêZÌ+tY-›È9Ð ‡ÉÆhø°¹r1›7'x­‹Ì.]®½6£w,!½§ôbjçìFi–ê^'ß÷B×Óê%lV–ΚeåclYt5 Z½ðÅ\-0H+q¨ÖÊ&q ²Š*ÔÄ‚zűI(vð1 +„Ép…™¸Âa‡ Cc‘¹hÑ2•sJYDÓŽYÔ`d›°b{˜ÂC§DcÅ +Ä@+H® Ûnà!rÝÃ݈c(‡ÉC !´Ð ‡$tÿk¬ðÁ+¡T¢£ D ‚‘KðV m&pÅ,½Á§zõÉ$ÈOï ºÑoŸ‰vÑUä·ˆNÍrˆ f X`Y$RCW/´Z¦¬ªc©93K‹wð!C•Xbˆ f…v¯šÈJD.êÇd2 xMŽ…²Ì)ÄFÍ)¾È aÞf%w°H ¬LFb’V@ÉJ)aqÍ)âuáEuÁF# ™[zÙ%08Ô\rË1Dl(¡ ‰¨Q‚L¹p$Íðf &u[+-YÑf¢58(Ã|ÌÓ] ×—H_0ål+|Ô_œbZ` ˜Å µ‹‹¦LCMÉqðÑÿª‰… &C’]øU˜<îêâ&×ĸLYì^>_Fª®§ˆà |9nˈvM ÿñK^I3[ê2WKÝ5Ñ,sBê>¦B10Û4r”Ò…pMûîFÃH^ ,"wÔÊFKìf  »wMn@±÷ßCÍ2HV‰Ì‹,ˆ<ºTS‹/ðñV XV§ƒôY)²DÖÇ[‚´lGÌ…­ða„³‚X @¸ó®Ãƒ¬×…r,¸Ñî˜Òd‚ûâ2„[‘l›ÜM+½µÊzõþ5gz™òåÙc²ËÙar_8‚Å 'áK4Æ<Ã"ˆŒôæûˆ c½±Ò… Â=0ÿîLÛŤhàÇ“Ø îFáßzlÆ”« ÂqLAJ~äÂ1DpSW ²Ð¤9Æ¥€Œ´˜!ˆ "4¡Ì`R÷—Ä”ˆ ¨)²Zô;âÝnX9ÚËgR¯Æ¡kÃLø`7/èF†wÀI±Œ;LæXÔ»!»ÎÖœ´--7²¸Æ,¶À€ŒDkêI"œqŒõ¡$`"ñ®ˆg8ÃêÓ[´v…*ýI8ˆð¼ áÔ‡pücN¼S/Ì¢O5qàœ!‹D¼Àq°F"a¨@@hA+œ’ ‘µâRòÔ)vE P<泘†*²Ò²>0I1* LØx8ÃåUæÿ3C#È^¦C‘%Ê’gv" šSä‰N׸e½žX=@<{LûRÓ°¤Ý°‚7¸› Zñ'V<ÃǸF+áºý«n<ç3êW@P}ºÂÿgÀõL öäˆ ë³”DܱŸKÉÛÁ”HèŽè¤ä¬ –d¡ý ¼p)¬ÔÈ,°húP†5áºê5>'š“M ¨Á,á¢N01cE({I»MXXÕ;–š:S™:fêìÝ,GŸ­©"Ë8j¹¨ÇŠïÁ€9Ê![—rƒ+îF‹1èBI€“͆6ÛÍp‘qŒcœ³¬ý*‰®ÁG›Ð¤.úÿ²⌒Ö{ÞSN ãÇø9ƒdh¸"§¨Y|…VbË: AX ºOY\à‹¾ Y(Ã#Ö2Y(z¥ E+ñ)&Ê‚#,@bu•šÙŽf°B OwEËb3ų©gd(Ó;À1%€J r'²‚Ÿ¬@ÖõÒ@„@5^ á¶E¦1àt{_ô²–µ»Ý]+|ë”ଇˆó4 Õè HÝü-oCÁ 1™2ˆ}á±°Ê,ôÀ¸‹Š-ýaŸPD"IUHÐ Ìjv×€5¶†Y°àýqÆ4ö[€Vˆtg»sØÒ[]ée6µîÿl„;Jì%4œá)À³Îæ¡õ–¬u0ÃöD±LáY¬aÍ!¢ÊÚx¡^m)E°Vˆå æì.3Œå²úä)ñÎk¢ž;âoJç%ÊáÔûÇhœ/q4Ä)ú×Ypêh‹ãS– ]°?²X"z…ÕuáS’Œ_ ½Sı`ì m1ƒe^yÄNg9š¿èjaÓK1J|i,¥wº¥](z{城ꙿӛ÷–i…\¸÷Ìf=·!»ù)ÅÀ€@ЉÉÍÝØŒg¬‚A›ÂMA›’´ yßJ°°ADÃÁ|1$Qa 1 Pq5ó ÿÙ‡øÎâRõ]d"|¥,žR Shµ”HßÁšÙÌcÁŠj$ú5`AVaÒ‹&öÅ”–éî$â½ÄÔH¼KVŽŽñJXÄà.:Åd<’`ªº‹È*2®ñüÆÕ:ˆ¥µ¤$ïÆŒ"y5±NO~eƒ 3hãò8ª“äí2!åÆ<æÑÇ Ñà¶à|n-ÃÚ¯k F"²‚RH:6] ˜XjÎ1¦llŽHM±+,ÀIè ¡•Dèìà”Å×e«KÝ©˜X³ ÉÈï„ÄÔÉ xÏûH¦T=ã¤\LÒ\ ¿JÒ… G÷ï Ôå ´†bœ+m®+ÿ#‹DB!¢ ¬½†b>èÍIFî’h`{é^ÞbÁ ¯¬ÁåÆ6—záu?ýi;¯bÑ2 ¢ìV"®Âv‹»½ù‘‘HŽv×»ÿ.Ô€§ŸwÝè°Iù–~œx’\áM½ŽÏ6¹üFô|IËÀùÚ8Œft¨t/ýâS´þÌãHÕ¤žêqÄý,”ʛYà $˜AÍ‚Å@Æ´‚,¬ +0ˆØ &ͨQÈB5ÄÝÂù›“¼†|Á Dšó¥ ‰EßÜáÒ5À˜¨mÜ ä‘IhŸÆÍÀd‹°ðK+è›á5‹tù\B$Ä5¸\èÑǜ ÿ”ƒ=Â#ð…CN_ÐÉHl ²H„Å‘FÐê„é Lh cøUS€EŒP"@~B^¸EcôAJƒ0ÑkÈS¤ 5TÃÄ+ä^¢õó©` &–äÈÑÎ)Ä` àçÙ Æ9‡K4xÎ|eEð…4UñF6 ‚x,U—xÛHìF+¤€V m5á •  bd¤Úé}¡áˆ!M hØY¹eÁ,8B Ô~䋈†5TCc´BûÌBžÀ† ´ÂÁ€Ý)°‚ìaÁ D£ÙÑ ¸!†ãÅyŸ2Y‡²("©ÑIÇñç}G œÄ ìœ ¶Àq „D,ÿ‚Èo0ÉntË¿8ŒºÎ)î£üÕH+ TåõÇ\ØÄœW"ÎR+ôŸLüŸGà—-ôà)œÂÈÂ"YŒ,ÄE4NŽ‹=+8H Æ B ˜ò)šÊ‚*lÒ tA~ÀÆ ß§‰#!vd9Úã ÎP(¬#IC‚SÞ¤£$Æ‘ÏÔËD\âV$˜¥ÑFöuÙ(„,ü8ÝÜ#Ä+^Û)ÖÖéÀLðE‘õÆ5xÛó@óAM¾\$.¶LèâL„Þœ`^¥À#¨A5ÌB¢åä ˆ”)XÃ#ôH™R¢­NlÌBc.àV$B ädl€×ù$f õ@Ü@ -âªÕÿßH`ÁU6¥CÆÈ‡]Ê„eUZ¥ª €B—ÀÀU~‡ÐH¡EýFnŽLÀÓ¶0åEÈD¼ÉqSOîÅzPä\vÄ,¬•.B…Dô@É‚€@a5He"Ÿc”ÁZ²ðlTÍÙá¤ÌBW ÂvÂb¦8QMŸÉE-öSn_öa݈¦öÇ&@ÍÀA¶¦f\}O2€À‘U$Bv¥§µÍBÜX¥ Ð löMÐѸBOÂeQðMsÒå· !1¡Cy`‚øU+ðR¦,°BpÔA˜‚SäÇ5$‰v^ÝT†Œ lç |Š{údÑTK±p†ÂÉÿ#‚‡ÖøÜÕ°j\"‹„²F©I‘Ê!S"B ì…†M%1ñ˜¡¢¾ gQ<"œãÂGQ0gˆbƒD 36F"ø¥€¨hø[N†Q5ÄBTƒ…\À븨'5Tû¦ÔúdÏì†Ø˜ÊÕçc”&ßåÝ·ÐdìNxÙ ”ð‘ÜðË|¤U_S¨&@¨ Dƒ·`†ƒ&ÂÍéEýWQä‘ |âˆÖðQ¶•\6g Ø¥â€J6#Ílõ!e^Á xAèˆX]làdNžge¡EÀdn'8F*f.ÏÇDÜ/õÒ)Z<ª^Òœ‚æZ†ÿÛŸô£Ñ„n´B -Uø5…8¹+0Zðàxõ*¸uA4D†Ed$«ÅÉ«±* ¨$Â#°ŒÅ æ \ÞVÈÀzÂ^õG`®çyŠëBôA¥.-éïŒT®jxØ`§Þˆh¤FSþÔ„ñQ6i)ùæ*0M&&aW½Ÿ_½ãl¢o^ì ðbdü†½Nèœ"F¾Rí 0ëµÂZ$Ù bÞÁLR~LWdAÊR¦\ ­çR°ì¸nƹzßÐìÐÎÜßö8Åülœ$¢êd$‹Íæ]6¥U™ÑDXKÜÜüa( \A_€®™HÓ¥lbDÿ¡è‘L½zm›~êuxÁ—ñŰrÛøt6xíBÏ$Â4CØ¢tÁ^ ƆCÍ×8NÉÆ­¥ÒvÊLÙ-fâ-‹mÅMõ‘X8©„Ƶ€Qæ]0ÑÆºJ"û…n8£'©¿ôÇû ½À›\ƒÈúÕÓ¶â5€®Äš™!Ÿr~›¿Ê©zBqjìŠ[ò`g!U-]hÇžå¤Ð B’¬'¤Œo+öGû Ƽ)/fîŽ-‰ k¡`)ÕÅõÕ”Þà¶Èã ©–a’J!dz²‰®8±&u’A°VHmû9û:X&ê΄ðÑÄ1íþÚS cÃ.ÿ]žud+üÕ"ýf¸‘ª—,HB¶Š¹9£S° ¦k“ÌFo•¿Ä£W¨innÞ® Б®ÂšÍ1m ä5¤°8¨µ1ÙWÀZäy+F ‘oåõEUmÏ`}@ç èÖ&´ €Õ‡nÈ^¬®à‚;mì~­6á_ÎS›Q¸åÚRqNBš>†^`ª+¯{ÑM…ê"ÂÎáÑ¿Ü}ÊØ›àÏn`TX[ÒFÔµ-môqørå‰Å>’¯S°QÌÓ¸ä«DÚÍ.ºi4ÄDã~í9vdzýQˆ!ÂFtíÅ.Š3*VSÔ©î2QûhÇÛ æÊ¾²<7ÿß&ð“õ¦£<šñ­k\+\Á#úÝ”ô FyŠ’¯¯mÅ&.`qE3ðA“¯ÏaHŸÐZÙ þì0]üzœ—رòç5nŸ¹ÈhôÁyD' <E]üÞÖEÅzH5*–¿¥l 8#©ø­+ϳOÇ_ÜìÈE¢i®öý ßYîAß1Fѱ3;#0ƒS°¢ƒEC‚‰¯ kS8!Jz´^KçvÀu´ps¸éV(`‡5·UO²ò)œôàÎZ+ê ØuLLjñ&ɬ^>F|Á)öO6íüê^1™¦65¥:µ $íûZ¥C?ð›]CSKuWèÿ±ƒ-à5hþÆ?#Â!Û«6‹“¾ŒhˆÕ„ˆeêÛhY[C†{ÎF`Ò6÷/^I,ðg 3nÐôAø-iÌ6b¶E4¤Œ]ÃËX]@¶ÞdoL'´&¾¯r¶ÂÞ1W°Òû…¶ÍiÅ>ZU:˜n`’nèQ+è/=×óˆFš‘*¶]Gò-mPG`òb zäðÛ´Oœ\šý^G~E^|Á§ötrSð‡ù ¸\ùÌ |KâÞôJ"RfM„î1Fywèn…µ™E‚Ù°ƒ H®],ÅÁEKe)· .#‡ÛyÑw}ÃikÙ¸ðóÔË0AÒáö&›YJòм@ô$ÿr/¸’OMU&KTi7¥”šf?'À÷²/w+ôwk7æñÅ),Èn„ø ð…VìE‹ð8‡X„D@å€VenœWëM‰ô¨©‡*Ï5^ÃÈ è‰aŒƒ[oïJ$Â[_)¸é’'ú‰—ó8d°Ô/å]ccø}fïkvæl9zŠö sŒnä5\Ê,èIôAüéß*\A@¥ìÖ‹7ãO­ÿãûí|ƒÖª‡6ìzĶÈÂ>¼Ä»jñAÃ[ {hð‚Kü’GA ‚<Ö‰Jd<÷Vy0}.¹oÅ|üggB‚aÇ£âo€ ªû­‘„Òjiaò º^@|)1\ÑYQÈŸD »ÇÁ¿3InÄŽœèŸ=´!ýÅBÓ¿ä ìhàÊh wÔK=bÃ_Ì+É}ÿG“ÊØ…÷^!äY¼oˆ_mØêuo9_¤ÊD,:kš©—ºgmˆß¾;%/bn|¨™ú "$ ¨ü¼‹WeÁ#þš¦U ?Æ)XA4ý,šc€ÓœÍŽåO?ÏÆŒMv.kÍ”ÒÇó…EÍFv+l&3 $‰?líˆ+«ÿgÑ™+ Ý×K> EzßU~ðo›¬4â„ Dע݉saÂhVÜ’Ë ¹ e©¢4ª¿À2ÊŠ)§˜ ʊŘ|ÎÇdäà f°)³štÌñ3ÒŽš(œZÙ#4srÊ©5 låÀ8RÐ2á ³¬ÝÒ°ËL¹ä¯•S¢ië¶Æ ºƒ¶h¸Ë蜼fGŠ ÄŠS¤r”Ö/Ú«;*!•¶S©1>A¢Ù3GÖtµ” Øâ±À šµV„î8¥•*÷šA £ìê=¶´Ô”\eíÏÜ8>f£SP ´­VXYÁ VX¬_ÊJ­Ë ¹¼ðÂ9çîÌq+bã»bÿÀ„_x»‚Úe+á œÍW£MZ!k[píêꊶ0-dPÁ4÷‹E–*”ž–Åé‹)ði¸`¥QŠmÉW€Àà˜écàžeB$ÐA²XŠQ'›t‹QjÓ»שNɸ¬+þ$‡³º7ä®iÛPTöàŠí¢V øbáAú8Š5Óá®M§ožûL¼œó‹®zžVØ= `…N ‹§vên$Ú”v‘ÔOsb¥±š9"쮽ºk/êþ}.Ë®=÷²1O½4Õ]ԲȂmŸê‚Y0SaeªMä¦ÁSh¢¼ö¤!àâØrLÆY~%vã½í^Ú'šP`­;=ED§ÅnEÿÒ®,Ï8¹.¸þüóÈqãòhÿLC u˜ 2 ŠÙšXTå™Ì‹+ß àâd9>×¾NŽÆ½qçZäêÃé^0¾¡ …Hq0E ¬P3èňlf2 Œ®æؤX°TQ´c›üQEtoÑN¨œ¤>÷e¤Úó2ëel{ÌT©°2´µm5DéÉÑhÃ@ÌJ…}©®A6RMl…è1™ðVFçø®(­ða_t‡OQH/9"óò€ú‰J,—“вVTätIAh² @X¡ VÙn£Â8`DFdÿº £Ø¨žÆÇÍZ\%µ íÌQ,hÔó2%æøOˆYB£=˜>~nFcÍê”Å +(2~ÃlâДõLXÁ 'mÕ+ÅEŽ˜ÕZÐ(­×,g"•©¤šƒŠâ?D\ÈBoÁcYÉ¡‹8N*+’ÀJ"$[ŒCc &­1™ñŒ§¼ŠX—F…bŽÿ"b·Úr=¼ltœZVÙMã`œ¬T˜ZyBŽÈœìZgÉäYQ‹ÆAFÒìB§’‡ÑVT(.r¬Ô5Ý‚“ g.œ»0Cu µ”Cfz%L¹$+`*í"ô£<Ř^¨l¼+˜’Ý”TãÊÿaM†ÂeɉËX 3Fèj¢‰)´WPšŠéOýê'r¬(è\8 êZÃHÔ®t ÛáÏþ Ø…Yx¡eéW5›×€G,Ê¡NZ °´Pp¬oùfb˵KþÌJ˜‚!UGáÉV˶HF\¹fSª;ØæzðÂÅfðRrn•#l+dq½.\!Óc‹"ª„vÑP´ý¬<‚«œ:)—.œH…„}tˆ]mƒ­.˜öL½F”ä iN0r„’ äe7kZˆ ‰mÅŪÆíèvKríbäzXÙ9 ˜U‘ï|é[_ûÞ¿ùÕï~ùÛ_ÿþÀúíƒ\Ò.b!´ÂIÃÖ)ú`’×¾6Û±ËF·#¼+<øÂÆp”ºœߥ×l*yä;ntpsec-1.1.0+dfsg1/docs/pic/tribeb.gif0000644000175000017500000007311713252364117017277 0ustar rlaagerrlaagerGIF89aø´÷ 2lˆcºÖ¯±É¨€r+1(^sS  ¸Î¤ÂØ®µÈ¥ÄÞ«¯ÀŸ  VÓ㽿ͫ}ŽcQ[B'7 »Ê¡Ðà´,8)5»ÃªÂТ'/ž°qenK¤°€1;»Ë‚ÑݨÓÞ®&.%, 06´Ê^…\^dH¹Á›q~6 »Í\Øâ¥½Ëd¤ª|¸¾•áâÛ¦³8ÃÐRÆÑhÞàÍ.1ÁÌL¿Ã–6:8;ÂÌ;ÅÍAÆÎK ½À˜âäÁÌÒdOÑÓ¢89ÜÞ\½¾› ÀÀÀÎÏ"øøN ‰ˆAff8EE&ªª{ððÐøøØëëÎøøàííÜèèØøøèøøðððèççßøðãßøðÚÔøð §¤>EDba;ÜÛ¡ÚÙ¸ñäôê ÖÎøð075 øð@gd3øñ€Ã¿fëæ—œ™j÷çÝÑ#òâ+96 NJ‹„-øë[ÚÔ‚³¯xÏÊÑ϶F@;6ãÎÁ¹qøØõ×̵¶©G¸¬X·¬`-&ðÓÍ·*òÛI²¤Jyq>È¿‰øÐñÌ K?)#?8?9{q?»®pÒ®ƪ75/óáš°«–øÀé»Ϊ'¾¡7hY#õ婽´“ìá¹éãÎß²Ú® 7-¾¯}öîÓM;) ( Àœ;dW4½±‘ô­ᤠâ¦@6בìž Ð›0qV~b*ðèØøðàé˜mKÃ’5¼Ž>ó•ÙŒ ËŒ"G6±¤æŠ0ÍzøãÄøèÐðãÐwNèÞÒ)íæÞ½d¹©˜øèØèàØøðè=Ⱥ­¸tøèà÷àÕ!ùr,ø´ÿåH° Áƒ*<jž¬-œH±¢Å‹3jÜȱ£GŒïÕ²BB‚.4qóq ¿–0cÊœI³¦BQ' 0HÀÀ@‚žŒ@¡ YntbÄɦӧP£Öl¥‡BO>  @¬: @@¢!Ù¼Š*Û·(RãÊK·à€ž4@ ¡Ê Àƒ8ì 5q0^ ˆ™ãU­®å˘; SÑäj†(}>˜Àk&x€á‰bì0áÅB]Yd™ÛmÇãÌÀƒ ȆH:$d`Ô€€@€ý9 /vÈÉ2AÀ‚ 'ÿd#ä·ÝæìH³1¼½{ºëºÞ@ÄÉ #Qž0‚g"¦‚4€ÇoIâ2è¡gG.(¼çàƒ6áÑD'TÜ.FÈÑH˜`²NFPÇQ%£Ë:Ì$È= BXW#.Ò…LÌÈ"Èh¢ânŸ BŒR‘ÂÆ"˜ÐBdŒÕôg€V@†rTS ,×ì&N‚ŸH£Ý’OË€¨惨ÈcÀ°7&*ÈÑUj4‚ ,?¢÷—gʤH5lTÉ%€JeŸÃEAÏ+èPÃÏÂt€Á7çåÉ;¹ü‡¨Gv|M¡~àAÌ$£„.ž~ œ0U˜ÿM6µ(R 8³"èeª¢;¯|éª@fÖ £BÊà’N$—„H8:ø’É$ÈÒÈ+’89,f`г;ÙqŠ^¥æ±@#¾6Ì@«Z]‘ ¥g`6ÂYP rýƒÛnµÑ^ÝAæ8у`¢m›>Û€Á -ì@Û½¨Í†Og¤©ˆ2>!­ãµ¡I&í"ÜÀˆQ ¡@†¥ ƒü‡9¶¨ ;‡ ¾€‚ÚnµÖí l 9‚× :`C´Ñ¦@pâÒaÂ6X¼"Š@ÁÃ$´Dž bÂá ÊZ‹ÿÀC(VÞA{«¾Ä.î=%ø Tðhw5 b-TÄ[<`bág‹„ŽÑ‰ŒDaöhÄ;ø! h Aƒ2”»E°U#ÊÅ8l¬³08Ã⮆"¼0Š6(9P‚jk‚¹âbJ„0ð áÔ &0\îï-¸ Î>:ÒaŠ(l#ã@G4øñ o4âË@;BŒd8kŽ~ÀPG”‰Oh6§è'ÂÍc7àa mÂæþï[ÿ@ Cö`KZa~² FÔö]A-ÿ0ÏdÁ‹7Ì@Îz…ˆ}7jІ;ÜÁoÜëIFÿˆHãµ Ç–-òŽ“Ùe`\N>0¸Á mÈ_.d@üÁ J#€P.oVa?€Öh4`|zxgpà‹w6“@ é–®°æÀj2æ‚ãP¼p`A Ð å ž÷y…£€ ¾à…ð ¿ d@õ×ø7£ h´WÈga€P DÐpða€`<Š€·¦0ev¹5hsÄuwfHˆà Õ¶É` æ "ˆ†æàèpóGZP } “P~"æ È*€@s1 Ç`cŸP¿Ð ‹P}<¶fø—s׿O·ÿær…‚ `à‹Q(OÐÍTÃöFp6#&Ps2×tï€tp6–JÑž°¨KãÀì`½0†=–ÈàY@‡•ð — s„_WvP “fÀ µ`r:M€ù{Càv[e?d9 á€ZO©E*bp3x?@Ð7@UpŦ•Y À‹A Fð…/ó±Vy Ü æ òà¸È ºpñ{0 ”Þ •€ þP(s–¥ð ±à ±À ÏÐ!:8B Ù˜£Àó¦[´×hj°÷à¤ð€ÿ°0&°æOFŠ…"ည á }‹ tÀ 'ð/ C6îKÚg•è€F¡Q@ { é`ˆðß ‘ v ï þ‹ ñ Åð ±ð ±°‹(Jj`\¤S˜ÐBmànmçgJp6/éáÀdd_€Z&h90SPª%Qq a)z ‹‡t 1Ã÷GX(¬Cûð-Ñp1ÀàYÀ²ð !æ ‘€ßÀ uh , ø QPo:°~4Ÿ` 6ø  —¤u$˜€Šp`i M0¡Pk¡ÐqGxŠÉU4(ÿ°€ ! ·&P¹µ^pPÉeZ™Šà}à… —–¤©/€ 0hð[°o0w´·˜ ݰ â p ¿€  À ¹ù à9ž Ÿp~ú@:Ï’A©cƒ¬€ RàGžÕp| † ÐÙ¹râ£-ÇÇpK™€ P–˜°a ¸%Pj@&Í„W ! `€á°x“P ¦ &0 €` ò0ôà-ò°%¼!l€ ð ¬  º ±à µp Š øð; ppQ ¸\ ¦¬0 ÍFR}ð†¢4üÿÐZ I°rÙ)£oG{—P6ð|0eP6‰P‰o0wvPN@†hÔÆ¡Y Fu°¡*0$À Ч wô¥æ€1‘ò•p—­P « u ²à ³€ Õà o€ ïà6`FÀ¤Ñ”à ŸÀ Üj ¤Ðk`D€ Ýè[Â0Ÿyp˜>˜I 5z˜ºÕw°D¯û÷¡Ø$˜€£à^ [D† ¹`‚  M øf‘P v°ö G˜•àòК¸d5ž F°•`ƒÝÀ ²  `Ó2b;à€€ ÇÀ‚_°‚ úh’©ó ‚emÿ° J8Q€ ãÚK ˰—ÊÀxÐgî ðº’ù§[õzkÆ&d˜*N®0 k°Å–Ç`0$±³ gj¹ÂÀë€ °…Ûð ò@¸4넌“ð ÅP ¬pã0 Åó9¤`PF+ ˰dòªÑ‘ƒ%X½  Â@tG• §[µ[ë@ `pU@‘ꮲ'>µo¥[&póêUpD 8pµ°] P[ Vú`󹘘P JfˆÐ°ÁÑ` ¦@ã°â Î)‹à›Ð Êù ³à ˆ° ‘°°0[q— ~ÿ ·—A ß:ÆÀ ½ YP¨¢ : A¶Càáx  âM ýŠI+>HûžaWaņ$pZxp² K!\ë`”A`°tm† ¼1ß°(©ƒ>â@ cá  ÆÐ ¿` ˜à `¬›p àP(É ÁS Úº0³Æ¡À‹0²fM lG0>Ð[æ,ú€õA)à¯m—ã³r8Úw\¯ôj%@N°x kpwP¤’ A¹ Ѽ‘ý€)à V¤>ù%B@ž±P ÆÀ­l ˆpN›P ) ÿP(Ð0 • ÃK°r°<3›vÀà ;¦_É lp.àY0˜@&~pJ€Äj0Gè¯Ø™‘ªA¶J[W˜º–[I`"pµ¸WK~àÁžÀ’°I—ñ°*€khÓ ›¼Â¬À ž§±pÈRÐ ~ÐvÐ €  0_à\±ŒŸ ¢è»³l°É²æ6pG€ùL|K`J`x0Kà<@ ¢Ð^°r®à ”`CØhöx¤V¢pµ?`ŒEv$! ‘p4Š—6‘`œÁà˜ð ˜ò _‚ R ÈÆÀºÿšð °p‘à)pU–ðUÕ°Õö ‰M3Ë $­_F #àÔY Ê„W*àK0¤àx Vë¼òà * ‚&ïŠ: dÆ6îšÐ?@U…¸q» ã0Šmh3 K=&P0<á¶ÙPÕÀ/YðÒ1­œ˜ Ÿ@¿ð•à üÀxÐ ²P(îôT¢mç*©“lpf'§#ZðT6pk´Ü@5Yq–v°2 Íl€#Àü‡{¶<ØÙr?€~…®qÕp „à qšis p°äÿ`½1p€ðÇé` Æ` Çð ¦0™P Á€½Ð ±0 ÎR ˜-uŒp ¦ð –L ƒ•´/Q’Ѥà Ìà¯Ô݈ƒ U&`ê ( `<ŠÞ¤3f¬/SyD±GVÙ¤’I"‰¥˜M"Y#˜xù#Lìˆ" OŽ í50Q¤ŽàØnDUk#Š'\ ;.âÍX.a#_ròå<´ Åâ”âJ)ã$„4Hƒ EŽ˜A)­´òà… "øƒÔ¶Ãñt…-B@<žØ…/Ø#‰‡jØØA EPècLR¢#BáÏ?^Ð8Å„2ÑdP‰äMd™”KÞáÿƒ(zÁD”^P‘CO⨃ˆø)qSNMR†’'xPb‰'ð€&B>AÕ6˜À# œYÌN0IÁǨSjIB˜Èˆ*n°aŠËa†`àˆL#idµVý¸<ð©¥\œÈ”;JÚÂAÊÝB˜c‰%6žÐA”b~1“;œ)%„ùÁM|‰…U‘%”cØèE‘Nri"iš¸áZø€':µxÓ(váa%†Pâˆ]r_ é2\H!”-ª™ETnépÝÕ‰ÜÈ庪΄ 0`è HA Ô %5#ªñ£ ?Á¼KiÿŒ]p.iF. ¥’K ÙCŒEDI „¬…8ÃrìYÄ™OLiÐ}ûÕ÷”S|9…˜WØxʼn眃‰88h¡…ˆ[ÈåbÆ[ãç…–¤næ'Ƶ£’Jºh‚J¨¨ã†œ¸Áa–üa~®æL8ª>?öùa(`Ö€àØÂ»Z’"LÔŒÃ>¦Îédª©>úXw$ \ åúë_†&Hò¥‘~ÄPÃÔeFé¥kâ›q 1d‚C$ÁMdYE•WzÙ 0^éÖ ‚®uȃw@¦¥@?àu8Æh@ ÊP†A B2PìUëº"‚Ô™@WÆñÑ $’"Œ@+.hÀ»¼² hVÚ£#|HPÄ"0ÑŠ Âó3"¬‘8вPD•ä´Nì⽨ÅõÖÀˆÿ¬‹î°ƒ<ÒáqD£Ǩ-”ñ OHBž°&\A‹o|ãžP&òàŠYüb`¡àÄ+âð?†áXD! š0ÌaeÀ‚†Pé,AÂàA\T€‚üôtD`È ¥,ÁxðRl°ŒH9`©DüÀ˜@ {)ZPÓ0@§[T‚6ŒzÐFuá ¤FÁ-¢„5Ô€ŒñD" `ÞHF`^ ¢¢M(, 1dîÈ/ÜáŽ}È;œ ÂÁ‡e,ã _ˆ+8$R ^°Áåè…=xQ ;8£Þ8Æ+vá LÁe¬I܉C”¡ÿ \hC †Ð†)ˆ! =,B`¨p &< vÀA¨€…Ñà*XBxÀƒa¥-„>vÀaE3À ´P`°¦w¿ÑÂ Ø ¾Ðˆ#˜‘´`Ctðd|‡zÓ}H2 xŒE"« ïÊî Ä7zñ y¤ÕxèC[í{_^|" ¼HG9xÁ‹Xx£Ѱ‡=^¡ˆQð"jlƒ‰Y.D ‰øD"äùY2àÁ‰hE"1Z*ì€ CPÝ’Žà„&˜ApÁlPŸeAÐM{+€ ŒÀ¨ŒÁ98 „ô¢d¯PEN áÆ†LÂ}ÊÿÄS)‡0  ¡`D"ÛÈ7€B,: Å(¾!XLÈ#¾òè Úº=,Yð@s¡÷Žh¸û@G4¾aÚÈã®`L(„0<¹ÁÝÅ,aDs!J àŠ" åA È@oí xNàA€t .`i©_,‘ptB x@rG€À€¦ÔGšã#àaZ*‰Âj¤<`ã̺  ÀÐŒwW~ƒŸ“Á1ƹÆ8>‘V<Ø@"z8¢2VÀx€ÍÙƒ2âÌ‹vp#éøF:Æ1ydCž{Ƚ’uÐ#ªF ¸ P.ÌÑžÿ\ñh(ŒÖxàÁ"î`Ð5At7Àˆ0 d€¤6áþ }¡(ÚM°­¤0 1@ª1‚•|Á¥X°§æ 1¸Õ š’ 'ÌQW./Ž‘eâFèÄ8öñfê¡ð8âL°‚d½šAFœ¿±l¬Ûîó8Ä!ÏpÄe,¤ÈÏ\QÕ}«&H°Ã&Ë…ÉB¡O4…q ;PÁFÄZÀ&(bµ¸é@ c}¡>OhÀî„ö‚G"(Öʃ•€ƒ…ðI[†lO z’°ƒ~°F²ÿ€ ]D£N“v1ƒ¬ -üÁ û}¸cí¨³ôÉ>Žh˜ÃŒ ?xшxü#àÀÄwÕ¸u`Â1 ÙDs¡®Ptfé¥î •A,p)x-šÊ€ (Ž•ò•kµÞy¤˜–z±pø‚ˆAƒ\`»•èƒG°9`hxS „€€x¥ xF¨&Txƒ^0…J€…9#R06‡r0ÒØdЕ6ð˜Ⱥ†Xˆ¾øB‡è²“‡ì“³ÔÀ„ð}h«p`ƒu@¿Õ0‚«‘¸&`‚RÈJ06`ƒ#†,Ѐx‚* L©@‚ÿ¸¨€ è­ ØÈ­xÀJ‚!›@Rˆ@¨9¿“@†(híÒ@l™§D@…gÀ€HL¨…Q@…PÐÄc0…TpŸTÈYx„MP=@ˆ†j+d  5 Ž+Xpë8†oȆtø„lpísvð†q0t臱À(Æû„(P*L e&P'¨'˜†«œ&"ƒ'° "¨‚'¨ƒ©¨0À"Èš (,µ2‚À 0¡X‚¾€˜€K )8Ä`˜„O „M`DlS0†3øWÈ€€e±‡ZÀÄP¨&S‡LÈ„ÿPL…Q4d¨/gx†,8³£S† ÊPë€ÀqƳrt@‡t']d‡ãZ‰j(F8Æe¸ŸZÆ–àF…U8‰ã7Ø0H¼¹ÀƒEО;‰] ĈCW˱(¡¤ø}¨7#À\Y‚Öú¸,p‚s8‡‡((‰`0oàL@…8x…O8…aRžGð*ÀNxƒ5HVp…H€Ìc¨Iy†K†hð…L0ÅHP0Y0Ez ɶ®n;"åCIEàsøÌÏäqÈl0qàvF—Ø…bćc”EПL ¨¨,øð†Gð„¤'j # ÿÂP% 1–AÐ¥,‡?X©\A¡(ä€*ð#û‰`h…X8Zˆ‚Q`Ohç`;@È76(J„cH„2Xƒ5xƒ7p€ ˜Vó„‰\/@~øGØ„PJ°6S\„(°Ì¶Š@؃¢X‚@Iè‡~(ŒlðLnè†_°‡^(‡r¨I•p|0Æ×Ÿ ›ÍF`ƒ;8‚)H:¹p.ðuy•íxš€â,!Èc©/ðƒ/0‚?ø‚âP‚t@ ©p@°‹wˆ\ R€†>¸„ û„&8*,Œ&À/8ý …7°„# €ÀlJÀD/ÿeÈ‚UH$J`ŸŽ„jHÅ׬¯r µØ€h E;ørT }†~(0^‹'ÀÉ×”< "EØ)((•`?h„QXƒ3`­4~Ї/(…OXpÐLX5` €Þ²Q•šSûh)8 Ÿ1””l4$‚>€ƒw€Ä&ƒž`ø†j:ƒ3è‚.(V°„0Xƒ.8/èKXMl6i}8ˆH€¸€gØ*]<ˆføU€ŒÜ*Å5LÐg°Á‰P ûÂÁxP ¤2´O –] Põ| d¬†]ÀœY†´< Àƒh„ÿ;E 8à…p…55ƒkŽ®|ÂF¸ƒº{ƒp¨†ßTVµ©ˆ–ò$Ö¹•’¥P>I’$¨‚H"„uàOÀT‰¨„Dx6x3(//xƒ.xO/ðÃDàãMäØZsd5è…jàyØ”ì€"ˆIr€Ÿ]=ð8(ZoHL(‚2h¶P°ž¦õÞÕ]ƒ& g¸Úò’ ¨D˜Eè…'ȾX§O`·rHcð&@¬\ø}È4x‚FEðQ-àû:º^aèLRØ<]À„F‡upTûz8áPØzœÞl‚nÁ—†cø„.…%“,øÜjà>ðÒ5˜Õý‚?Ó}^‡.XZAX†žL ˜€ÜPã¼Ã;4@ä78I‚$øÙ,€†O ‰0„OF`mFØ…Ú[ƒñuÚÿ8ˆS`™M…¸€ `€h€W¨7è 6po°°‹ø„;³p†pè4p` …/бp8>¢`¾•\a@*@#èžÕ@h#8ÊE˜‡b5ûj<ø8!‚]X¡&X¡\è„Nˆ8a]ö<ƒ6XdØP†6À„p¸e5ƒ…r<Ðá'|Âpø†2èùU„Ïm a ¡Üá€ó€äý‘®8¡\^I‚&"h °€]È]p˜…òZ†¦YÚkÚI¸VfÅ‚.XÛ\x…7¨ˆ`…{°€ 0½¸uÀgȃPPxȃÆäoø„rðg†ÿ ¶48"@0€;”‚G>¢%``É…ƒc *¸V;Ee?p9]Ø..Ö‰aÀƒ{„ˆ#PÝ7`„hŒ˜8]¸:˜Qˆe˜ € È…Q…hà6àò †ôLZ<uÀ#p‚AŒ!Їw8‚xœÑ hx‡w†£‘© ¢à€:H‰(˜‡6¿F$õ¸¯ FPó?¨EÐÿ‰{…'脤œ:ÆÐ\Ó=‰EØ…W]7`‚a¦2ºEïP(ƒ9 fb.ƒ2bØyàah0@÷RAеs‰u¨¼Ý4ÈÙ]åUH^X|P”<‚1%LX"Ð XEˆˆ>@†Y~Ø?x8'ð£\˜=ø~˜ÏÝÑ p†I°†qE…KH&ÀˆöRz84ˆ&èa„×І냷ZÅ£;"%î[`*P„ ¨‚”'XLx7ȇj+?àTži×dƒ]xþ)Jhƒ¢Š\¸·“ˆ70à„8H‚Õe„%0xvÿFhV…Z(…òQ$PƒPÀ;@W(žZ†Eø áÑÜ1ÒÊYWùœõô$@À ã0åC†o §pX6øø )H†:¨‚:8†ài˜ˆ=ð`¸`€˜€@^ˆñgàEx†gK°„&`]8 JCžöp¨²^Ð-06PXµèöæia0¸ ²ë’à‡ÄˆjƒjàáclŠæþû `†k|`¨Óˆ'€å&hå'` 5Hgè\gƒ6¨¬( [8‡XV…‚3°;. ƒ,Tt*øáÿÑ (—Ø›ªcA¬ƒèWÿPk•—Iry" 8$°€J4åeK8Rî5XÜc€Ï5@Øp‚DpB.:B«hÀà`6´~$£›ÞpF82[ `c˜P#F1ƒ8uè„Já S„Ω…Èè´OØA˜ðÄH!ŒÔUxÑ)¦hˆI\bn)@àÅå ÎÍ麈¹|á _Ûxð_”k¨Å1Ìô‰tÌâ±…Z Œ{@ `*0›Qh@Cž@‚BÂ…x TP%0ì » Æ¡ÛîÂmñ—æ^¤ÐG€ÂD`ùÚBÐåf´Ð2o¾rõ  …2fø†&1á˜VÔ"—Hz0ò‚{cï…,pÚ´A µ0BI$‘6èúv`¤P'H ³hÅ=„Ä2,BPÕ ÀÀTÛ…]äÜÑÜÁ1èÏp@àýR(Ô ^¡œØÌpR'Ô"Äà@tÀ¤lÞ€d8hƒ„V(Cfí0TŽFàA¼‚3(Â"`‚í2(Ì(Pˆœ`ÿÁ'xB"¨RE@”À ŒpB% Â¸ÅL€s@F%@¡€¡E”@h€>dì@(XÂ7DA-ð°AhA4B(B#Ã%„B5H@À!1À´/8C+0*(¡ìÁ1ü‚4Á.DèB/Tƒ»©ÍˆQÝW°@€A‡¡€þ¸À1Ü]†Q%ÐÝÝQA Xì€ \ÀpÛܨ„‚`GüC<äƒ08ää‚:$ €À@l   &pƒ3ìàhE(,Á¨žnéÜAÀ¨¼Ã%\‚ðÔÓD…ÜÓ1¬Ù4ÿoõË[ÈÅ+laÁqqóÚÌe”F¹Et øà¬Á'Cm`ÂDÁ0)à´A(øÜ8 Á8À8—"’À >ÀA4؃%ìBA4Æ/`‚T'Z 8C5dÁ(ˆA ‚+Cfy€ÙøÛä&,ƒµ%à@5ÜÝZáÀ€ú€ˆ„[0¢DG #>$Ã9ƒ4Ü€h?€Œ@L#Xã5fÄ €xC=~XœA¬@Á¤1B;V‚Â(Œ„Á ¦T2máÁ ´Á'üÂ3ˆ‚dÁàA8ôøÀŒ*XB+Â~œ€ ¸@äÐ+ÔA-ÚCZá@,Ñ"Hå¤@;(B5èBSVbVš„>èCfu>leGÄÀÄi¡–8? ÀY*€T ì@[Ê?Ô•3àÂa~TÌA= ÝÐ &^ÖÓHSà“t£S”hÀð@8L”€<.X¦!p¡+tBAšgž!Aei†&:@Ç}ÁØ ÿB/DAa#ÐB0Ã7lƒ9<&8‘ìÈÎ €© §<|Â2‚3¤À ˆÁrÖB/ü) @è€(G-ˆlY*ˆ0lA´Éƒ)¼B4Œ˜þHRFA†å´Íå….ÀA(¨.$ȱÍC¨„(>dª€úC‚iÕÔ=´C< ^(H¢6Á „xEXAe(‹ Õ²Øe<¦hŠž<žh=YU”$A.øAÍN@ €Á8LÑ#DA¸…Œz‘:WDZÌ$€Áqƒ% ‚’-|A~ù‚;\7\C3ØA @€a@@0ÿ©eÀ´ƒ<˜Ã5ƒÕ4BÏdÁÿy'B8¦Aßô,ÁhÁ(\H/ÐÂļ‚:àAK%9-¦Ez ì@µ-%¼@–‚5ð4B#|G°)(ƒ™@“€¦X,‚"ÀD2M™»A;ÌÃîY@HLL¨€C(HHäÐe<‚Å´H…U„®Ú¥`" ˆâ%€)xÂ1x`×<°EºH¼Â6à(!Ð2W¿8‘.ß&@¼\‘4áw‘”+$ƒ|¾²k7UÃa8€)W—âë D¿š74ÿéÁXg2Ô‚(tÍ ÀÀÐB´ÿ'HÃ+à%MÂÀJ%öÃ,ÄRÁYeXþ<Ê+ª¸´)|<„Š>ÀÃÓÄ<,‚‹ËƒÛ $S*&S.8AÐC…ÖH˜F ˜­Àà›6lÁœVl¨LšÈÔj¬ZA`A=…=Ý¥Â$‚Ÿzƒ#dÂ*B`ÎÀ)I[Ë|.$lÂ#Â$Ìd €::&¢á¡½@8ƒ_Z&¸B-\%”ƒ8\Ã5t7dC ÅR‡¹€ p/4‚ð>de8XÃ:Ä)Tƒ$H&”D8øVÖaTaåA=T¨À<ü¼€6 Hdé hÀ@…W¤/lµAœÁWà¥XAz$'AˆL(DŒA)8Â$$ç¢IÕ¼‚/¤C:C:|BTh&†rZóÚÝšC èA%\¬A€…B#„‚<´k7ñ«(ÔësEfÚX@ p7³&|Á À¸Á%iAàAHƒ'ð‚=<ƒ5'¾ÀD' °lÁ-B)| pÿ‚$'ÐtfÛËÉZÃ<ÄÃ<žÎC#`‚"„€8h%¸¼Í ˆê?üÁ XÀ8ÀyŒŽ „¯‘%Ã6f´Ã+° C.”)¸€ ænÀ&FrÀLœ€'D%ìn€^„g¸À.80ÿ 8|v4Âøƒ$ÓP"AxD5ÜÄ}üGL3àC5l@»t/^À@ üñæíÐØeˆNr(hC•)‡% Bx€ÁŒ.1à4NÏŒÁÄHæE‘A) Ÿ·BÉ¿L@h†fóYkT¿HÀ ¼ƒ¦ O÷1‚"¬ä³ KCP@€2‹$ÔA;ÄÂX+ðÂ7ü©)p,5äÁ‹åÁ'L"¬‚"ðÂ'ð‚7D]ÕÂÀA8ì>†E¶ Ü•€ ì&$Ã<c5¤À˜@<´Ç—Ä0)4B<ø?D>`B«ÌÿöØtGÐ'Á „j$t¨¦vY ð€úð šJ8„‚üÞSŒ‚Œ‚äJw¸H0h²/dB&|ÂMc÷YÂ,PA,ƒÝ6¿˜Ââ4"XB.°Hȹ…Óòg¤<3 ¡"{ì‚; . Óp7ÕM9ð€‘×A H”‚ sl9P5$Â7˜#/¸K•­ƒ'dN_Â3ðƒR´A7äÁ¿Ã”¼C$xkZãÀA=”D¦fê‹Í:jm¢¡[× ?Z¾9ÊDÐÿ½jY•£†©Ÿ=oãö)ª&Á ,Õ$ˆ,Ùpøøþû—¥ÚR_¾üù³lŠFóâ5æ'e‘]ÓœåªÓD8¤ü#Bĉh:uŠqM 'OHÉqývlÙ³8cSäR0WÀ¢Ì–]Ê[A_ 4æ ÆŽ*H€R5\Á"ôÆŽ|D™àdÉç,àAvÉe­Qi°¼¡Ô®ªgÏxUÛ÷­9¡C‹F{¥'¾ù%–OäqGs²é%ŠvÜÑ‚,?–(Å_ )ä;<ɪOfÑÀcbK‘à .¸`ðÊ++ðñëRh©f‘«ñ¤ÿ·acdÙA0€ÆNœpFRðQÆ"Ðø¬´Nê@£…,«@Ã!|ûL9îxÇv¢¹#ÌØp'“mN'UŠ#èä`Àƒ„x€;LÄ ë@á%†D*@P*逑 ( 0½5B‰IŒZ<ÑeŸq¾!' Š1j\¾…Ÿ<¢hã’fÐÇs̹†Z³éfŸ4¢ØGFÐë‡S Bå•vœå’qØ«6Rp ܱgPÀD:`Ø …’á#œrññGFtÕ•ñ\þ™‡@vqæ 0ré 0û‡Rà° %s!Œ:¬-ÿ<Ò\øµjà‘UZvÍ\2ÉÄGˆ¡®8G¾±€6ªðîƒ&€hØ£@¶p@Ào °@'0Á—5â8jD8`”#¼F“B‡%4°ABèW2¢@ 0ÄVBƒèJÎ\,1Lêpb™¢±:$(â… ]¿qŠÜb"Øà'Ü DBÜ@ˆt|B Ÿ(ÆÀв(Jo@ H@‚:P”Ux%FŠZAZpÉ"àP 5<#¤€Æ;da OÜ#V²º‰9˜‘ ™Î kò@…ÿ¾±ÉY°B IØdD1 —TBþûEÿ+D 1$ÁZÃŽ`l 9@)0ào ˜À5Û…®ñCÎØ3õ‚‡#}F~ø éª@ !)AªP)U!t¤£¨EI€Î/ £i2u&¶‹"tá €š“EÎ 8xÀC ¤0 t‚ ‡( JH£;˜h &  d`# 6P…edÁ1‰‚ŠpF¸àå*‚Ð5ª¡bõ›éýpе›d£í Ç4Xa V\W®Ø¤0ðàÇZ\B‰Ðä&- -ôB¢HF`€(ˆK"  ÜDˆÁ8`Ös9ƒ~a?Øÿ:/"ä hhÂ:"º:˜`#À€<`L€ )bhâà„*äâJ4ìl¢` ›Êнa˜" †.ÌvÖ™"ÌÀ2R"xØ 8°ƒQÇ:†ò'Ú!¼œ#XÑ8d€uü‚Àð TŒ  ;AvŽ>ã ºÚ5ºÑ‹oÈ´V’œ•Õê'ÉbØãêHÁ,ÑŠ_°B»®xÆ:ð`„#£d +fá?í&£®ÀD-ªQ‡¾í2¾ o,P PÀhÀR'œ±8u9‡ïÂC2´² 0€á –ƒ²ê&°ø;¨Æ@0`YJ>uhêâÿsÊÁK#v t›ŠC£DÎÐ…2˜! k˜Ã!1OB ‚0€.x àU°ˆà‰‚™ fˆF ¢pƒ4À+â1 8Р *8a-¸ }fC1¾Á«Ze´²G?¾5n4£VSs$^‹wô‚Òo+fÁIL¬˜˜å+S † š€ ¾gölz¤@X¬å½àD(𸠸à°À`:Æ/ÆÏ!ødäf$0¡rÁ2ꉎ Èà¡Ê ¸ ÿLšÎé‚ t*G’áìä–ÁuÖÎLa¬  Ì  æà «ÙºÀ ’À 6á (AD(Á<áP ÀáˆÀÁ6!L©@`aòª—ìÿ `Z`lP` (êáf¡Í®!ÒÁ²¤¾Á&žPö"i¹¡‚A¢á@O‚  ð@Á¬aàjA ²`JxêP ÄÀˆ  àÑ`.,pÀp¨À¨ z./Ññ ~.]ÖÁ ´a`š ˜TT Š zï´ÀÖ<  ‚è2ˆ  ÐÀ vA€òì’áÒQåÒ„ Ï“^Ú°À °À ¦RC€€vÁ d` ¥@Ba.a0 ª-A0€@E¬‘æˆa@Œ‰² 4AÍäÁÄ¡ôÿÈ „ÒŒŸð0¹¸.áâ@š `ð PA|¼ ¦@ ^i²à`@>£ž@Jæ¶êZ€šÀŠàA2òR€š ratÁú‹$IÁˆàn€ v 6` bR  Àf n­& t¤Ä ŠhÕvëìª!šaí”Ë¢Á‚¾¤šÒ ¼Òóîº ’  `³ÖÂÁ¾À<‹Ç4€¢à!! ¾`4`0!.@TÀ+@ ¤À^2Çœ@a *Iöa¢VÖ õ¸áÌ j*ÉÄÿhåD¡ Æ€ŽAð` j¡”A Ú Ú€¾@A‡@ Ä \2 ¦€& 5µ [>‚8v` x@p úáâa8ƒ$ôîÀvÀ\Ü€ È&\ XÀÂF ^À鬓²@†2 æ;+\A(B¡ì0Ê. Ì  ^" ¢ÀæÚ` ’@¬`Cq á,,2u @žÎ€à þ€CB/!Ô l B/€@ ˆž‰ î H@7× Í>ÁèA$)öaEIA¾ÁhÅ>Ø‘Ž®!Z¡ŽáváÔT ÿÚ@ C|.A¶à zt†`Ä€F¡ P  ´ æ<[ J[ò%UÀR p“ ú¡¬üë\ÂÜÀ xPp ì@³Ø`,P@4À6€4wRJž@a‰”á{JJðÁ(UÑ$<­ä!ø¡sx¡sè=£À €Á^š jÀ °@ž \ÔàfÀf`f X jàŽ0ç –!+ôÁr@d€†@ þ`î@ RD<“ÀJñ r:Ì 0!kˆp>A’PÁ†aAŠÁU:´lß@.Áìà( ´Ò²`ÿ ƒ–@ 0aAüàG[•!.@´êfà ä¢%ë@Œ€„Ø Fž ˜üË/ø¢\¢ 0r,p L1ò¾\Ë ÌŽ0‡N &`auF!Ìah-¬ááÖ! 0¼áΑ< ’ \üÀd@á r€\À ˜@žàÀ¡|vvVgyvVÀÞž`–I JàÎT]Š@ª¼`v@ B‚! dÁíàæá N’öõ˜ÁRôY2×®IÚ!õ$I‚ FA:áê €D |@ ÿ¤V Ú  hÀ|À ”!þ [Û`f€GiØ%ª –à½`]`uµ .à pÀ€ þ]Êê\àA-y€ P@NàùŒ@T $`ë`À¯žàU3ì:ýʯ–O„º¡n¥B¥ÉRo›¡Ä"¼Ê»  Ö@ ê Ôcp‘€ÃaR ü„F@xvg/µ"ÙVà†²Ô`ëÜ´"ìŠ@“] †€ (Ad¡ÎÖ ‚¡d!F óøÒá˜ÁöAä!ä ˆ# Œ ø’ˆAøA3‰ F@¦€x“!€átÀÿàÀFh@Ô`…ÇÕ†À›—`v^ „2ˆSS RDàð …š)aŒ©¬Gð€ÙÀ`€vàAS" v!ˆ$ã‹ýª êÔÐ tì€Ô¸š!~¡t¨a¨Ž‚&°Í Ù,E2] Öà¤CH!ˆþ@ FÀXF`}KàRW@’Wà顦 0àL=Y¨5¹|@ „Á6¡¥Œa r#,A Ha–•šAÆ!®W¯Áö¡ÖP  L€³ âÀ8 ¢@îà$iÆaä˜Á<PVÜÁ@àZ<Ú ÁHaBàî’ A àÀt  |@–Y 8ӛŵ3Åu þàÒ@ ˆ· QH`hðn“,tÀ ÿ[¶äY]gÇDÀžà%-€uO@ÞèÓ®æá›Àø ’$Õ”$Ö£6+6Ò6¢À¼  ô¸ûá=!’Û ÂÀß­@H p€ P@làLW f`jÀº#œÀJBÀ%ËÐ1’¼€rÁµëe”“(i ‚›<ÔôÞQ’Z/ra`—0À0îÂv  î`´ ×Ó`ª¼Hm@ ¢x€AÛT0á |` . Ê$ŠeHઠ²óBÁpÓ]~Ø  ²x ”ˆYäAm@0AŠÖ4ë``õx’Ø=6ÿÜ6´60ABÏàþÊàŽö½  ßó8à[Àì Âd žáÝ÷ºU hZíU€>{‚Ð{ëN†´L&éÕˆá†!,eÁló»’†áåA¶áÔAàœŒ`ü ´ð ›u`dÉúhU¤Jx@²I,ì ¦@ AûG€¬%s*ʈZ€´FT›†ê@HR]”¡˜`ÙE€X·ˆY$HÊáÔ¢ ‘:UÀ8©Ò 9 :|1bÄF’9lô§Q3]¸”9‹ ˜^o¼t9Y&%*'uìØábÄ+ÿfÔ\Q"ç MXt!ň .T¸8 ÓŽDEŠÒBCdG'L˜ªÝ¹…-•­Té¶¡»†í̱hò® ³uvصµÃ>™²e+ˆ ÀØDÅL´Œøø ØÇY|¤Ù‡#e!…¡¦M¨!Z’Ù¸€1Äà€M™6-0×4Eàðù{ Û>FOˆX¸Àƺ/ŒÀ#l‡°u/ê9îŒ3`DnÜ=:tR¤,:üòÅÌF.‰>}óÄÄÕ›5kÂXIY K6vPìaCÆM›%LԇфF‰`¨HáÂGŤ8(uB5ÇØ¡‹3½ÄŠ,îÀµ•ÿ;òDãN3¡èÍ6äõ!7טsM:Ñlu )MÀð )ìpÇFì°> áC|4L‘…Gø0Å;x1Ê(K4BŠCŒJŽ> ðB,@g@‚N4ñÅi¦)‚‡:ÕÜ[™øŒRÇ\°Ãýñp›)°ÁÏŠ<Ä@OTAÄM4ÑIÒ J()pÀñËxF"\pK,\4Á 2axQFa” +¹tB‰. Ÿ 3Ø´ÂM9©€„+t€G›0ì€BYkM ‚ Š´£ )Ÿl£…¦ì³Ï'ÍÔòÍ„®e–Zkm#ÏY©ðã &@ÖbM-µˆ±—ÿ 6LáÃŽÙ Å_b¸‚ *l`"J2iAªPÀ €@A1 ¤% MT㥗ú”>ÿÀƒÉ ܶ›¼9ŒB ÂHÆquü9ÇD¸ ¡"C§Å:¤8”C`„ó‰GÜ}ÃE.M„ŠV˜±i«ÓI´àŠ ¤ŠS +ØW’$!CEÁpT­J…kâ°ZC‡· •Ö&°l"ËêDó,´la3Œ;ÑžuÉ1ÎT£‚(µˆÂƵwµVˆÏ&0ŸO£¾ è@R“ ª ¨oYÐ…³Ð‚YÈ¡¨Eœ—aÀBªÈD*´ÿ"­ª"øÀÄ¢Àà¡-¨Bv°„ È$h€°A ˜Àƒ´0…jh! O(ÜÄ`ƒÓˆC'âà„žMƒ%Àƒ—–±Ž](‚@D8ü0‚EÀtHÄ7Q@Ý1A¯È…)$‘ Õ€AÄK§:“Ç]H‚ dhB.„:Žc$âsàÂ+vŽ`hjS¿ˆÔ+äx5è Uô¡I <ЄY¸ÂVS¨iå7 ØàÆá¡´ØB­„.& MŒíÁx,Òµ€Í›PF8Ú0a´€ŠÿŠŠFÀ€D x@jE„EÁxà x ‚ÿ]Ô>¬A„HcqF;Q4t"?+ðTÀ ˜ H 5Q5l R,t ² FЄ(7Àûò '• É ×€ € jð@x NÀŽ ¾ ߀„  sÐÜ“ló¸Tp ¸P Hè š€ ¸$ @àN0òÐ,‚,…Vh¢– ô0‡Îphô0_5$NQsUÒÐÎð ˜Pˆ† Û`!—è°ˆ‹øˆŽxód<ܤ.¢ L¡F‹À&PI`3©H©è X*€ 4‚7.À6p(°20½#µ",PR‚G! ¥ÿ>° ÄBdp "U Ê€À–Ày € ZqRžð5" ë0Ï`¼Ð y ›àƒ™ ƒi˜ƒ †ðƒ© ° ª †© ð L0:/ÀL  è |÷ó™í ‘¯ _ä„,í0¯0ÌÁ'À<÷š¦ð û µ  šôÇ ,·›$òˆ½iõ× Ý@ v &Æ#à m€€aà ”+¥¤+)`Ià^ Àp31% <`x  "¿a:ðËàáðx°B°lÀp 2Õ ®` TC5ç90pùní¶?•P üð„ù6ÿqiî íÆ÷!þc(ýH€ ®¡0 ™`™P˜‹¹˜„P˜Zx˜'z gà O0(OÀ@R`Ùfßð ¼ í@sÊ‘ í°ò0ãÀÇqΠ‘´y?ž©|ó‡éÐ ÀÙ›ÀiÙÐ ­PÞà ê€B¨E­/9“fð l ŠÒ ˜ð/ ( KðI ¼$ ( MU+D`Þö«óÇ]OR#÷>í£>¤òé™ú5z @h 6Ж€Ä@—¶€ ã ?ª | ( ¼Ð ¨ ˜P ˆðÿ!º˜'ª……  §ð[ ˜°`¢P¨I€|³+~ #gBë@#YÀ23 ¹ K pòÐY%Nu‘Ò0¤×ù¤èð Ü Ù0"Ù`¥Üà™ê Õü€“ÆSTø€9`8Ö^p# A ó p`Õ° ¹  F°‹`“n jð: Pi46ñx`?@Dá ~0 Ð! &@4> PIQŒs²ª@!Zñpýƒ ¼` À–€Õð˜i Ïð $RªÐ}°`à Gë Yõ.¨ðl)q§ µ{Üa%èΚ€Z GpÿZ(@1€o¤ lPX´tÑn/ ­Ò‘ý0 vð mær&ÉßÀðW¼iì`!ã°ã€è@ ¡`Z¤Š „#Lp a3¸ÖjP<»€ —¨zWO xX¹-+ð x€R JPž•8‘*2à>‚ ¤"'Œ€õ˜º ©ÐxlùB /‹€ îvR£à Õ2 'õ€ ’p´–` Â@ðÑUÀ1M0uÀ7p%W²9œ# pv# y Z (à @(Oð‹ vHÂÀàM8¤2а qè Î`ÿ Ç’@ßðÇrâàEZ›¢|¼™ ž°EF° :ðžÇw\ Ààðȸ ‹T ~ÐT+PGÃu0%'0+ @+À, ž1uº3ðY&ÐNÓc.@ ­°°?’©cƒ¦€r0q‰ײª0 Ä`ªÑ› ÁÐŒpÏ€ ­à x³ƒ@ # à`à©0£I*¤€Py·" Ö Â 0@ƒ² » q‘Çá  ¢ ŒÁÉrž‰ éèp®y p¼¹Ñ EŠjÐvà ­€ÿapFœ 6ðQm´K°>€ “(;° ͳ/P6€¨"x:|4Op+K3Ñ”Fc,pÃJlZMð³  ´5\_³?²P <Ж/tpYq—Ä• ÿö!'jÏŸ³ .[@£u¼Àx¬Çž:£ A³íŒ Y𠸿‰|z6/0(µAÌþê'¼#V Jº \¥ŒœfÙÊX(ù ã°¸"ƒ¦¬‹Î`®p#š’`ð‰ =aÐ;`ÀÇ@ÔA vxN”D Í4@ÄQ‰0v@©+)ðÃ@¼ÿ¸¨>á4¢xD  Ÿ0[,{,˲› Àùf5[¦ð» ˜àrõœ o Êpy ( ëPà``ƒPçÅÇÉÇ|à¦Öp˜Ï40UÂÁh{n0É ¡ V›žlîœôw¸èР̀¥Ý £Ì ¿mºÐ[T‹  º° )¬Äl>½{-côTv)Àâø `¹ §âxox`@P`*Pí2P)àGr» Em`Ö·™ϪP îÍ–(—ož€ !µ©0ÿ ” ĠדÀz@Ý•Ÿ‰\*¾`ÀÇ0£š9£ Ð °9 P ½ ¢Ð ŸÈ60p Àw*@0ݺ$Eld òœ»)ê:çz®ÝPö`Sš \®Á‰ žÀ6ÅÚŠ`]ÐÚr D0¹æa"ì Ÿ@ R“œ¶ÕàÞ1 Í2ü°ä3SRПÀ ‰@. ž5Û»>·ËŸGÅÚä  Ñ 6û] 0P¼É–ð ™z—T ‰Ö'% ¼ x@(½Z:ðg)"PÐ3zÐ Pÿ@â<°8±ZÈY@J.ne•ÄÀ Ñuð¤°;Jç½ Áö X¥Ÿ`k\ ŒÍ€!ÜÐ OW ~µ=tº€ ]€l˜˜€XžÀVÀOr0 '¼ ½ShðšÎàùî (Àu°xn ®P¬p @ Ÿ”€ÜômS\@©p  ð˰@%¿° —ª   Ë@©!…élw Cx0H•/`àú"Ùî©!Îó ŒyP˜À#6€0Ðë¾Þ÷ò  ô N ± `ÿ <oàÈrÑ ÌÐ ¥@u—Ð ˜° x€ Ö MN öð =ôuų¬« ßPb€ž°§ XÐ eàv8°)0;Ñ>. Ü5ÜÔyCW•á ;Ç8X42q…ãâ3Òõeý “~²ðïpGp ëð  •m€ •ƒñ` é0àÅrR•@ áà ºÐ7àUvì L!p Ùp@]Á$@Yðbé‹Lßж m9ðáUŽõA0ÜÐÁ è€ ¿Í òÀ Ôð ¦°XºP BÎ@9lœ9S§ÿN&‚ >|hOgº’x s,L3a XáÂ¥U˜2\T@°`¡D–)ËED˜µ²cM2e¼>¥»•ÊÔT©@Q §¨˜8N`¤€QE„ Ñ¡¶@àAá 0e)£ )0L¡ñV<p ÌHÃu Ý,jÔRλOظ½6—.µh¦8)bó°Q6lxÑRœ &uøÍûäÑS3̹H„Ä ¥0E\@ €'·pÕxv9Ë%j Dˆ`²aCX’ÿ$@`b“âÉ|mD^€Ö+Õ­µÃ@½[Ç›tÞÈBðl€P6yC 6²ø†™tÜi«?[Ryä(ø@‚ O<1¯(@aøË‚0௿  € 0Ì\aˆáœ"À € ƒ`h‰”]„«—׸1Gs²±c—•¸ã28ƒêg+ÂHÄ“O¾ùÄŽOX±cƒ 80‚JkÈ0¦K‚²Ø%—iðàˆ ^°ï…ê°ï `ÀxBE²C9ªÁÄ[TIE•`ø¡…œqŠ0 DaC3?¡EDÉD ÛšŽ7€ðÿjx@£xX ƒ­þš0Ì00YÂFà ‰88©¢Ž:sÁ2`!¥¥ n“hÌa—w¸ùåg„г^ˆÖ±O ` ‚ RR ,` 'êx käh$™rå€ †ì¨‘Œ¨† 0vÙE<ÀÀc†h"©™wªùdœa¢ æÏ `V¼QD–MT%‹qÚÊôþ0¬$‹>!ãOÈP¤4d8†& ðÀ†Á\d@€þ" äO>™CûH@c²€ƒc¬‰¢%5„SÆ›o~¡¦›rƱäEž¤¸ðÿžx RJiƒ'ª1¢‚ #Ùa  ç“jðk»à1"ª0AEžm¢²f`MfQ'X`‘“o€†å–¨`Ùd”/ ù„ŒjTæ`FÚ/°À@ç [  K OУŽ*Zh †Îÿõ¶„äÂ'²æ{x9ÆŽcç¼^ LðÀˆh‚T`àÁ)ÁNˆ¢IåÏ-ÁÄ£„‘Œ3ŒƒÙˆŠ*H‘|æî€Å7ŽÁ3PXCªØÄ&R L©¢ŠØ2˜ )ìà¯è„.ò †à1@1C! ‹)ÌÕ´ÿà‰X¸BN`‚ªo!(F Ò ´ÏpüpT5á(2Lì\Z‡øÁ™tà.€âÀÀ px` PÁ±9ÎUŸpÇ'zñŽ3D FðÆ*RAŒk\h#pÀp€ÃeÍ Å‚Á_íÀÛ,A eœ ýðÄT· P0bj³ØP€ !‰ xÀ]ô€ƒãHÄ7¢¡ N¼¢ ‚‚@¾e4@È 9M.±! : ÅPFðI©Á €@( 9" ¼é ç‚!qDCµ±C-‚ dL¢p#ÀÂlAŒ¨dÊ À@ÿOÈcš€Ã Õ‹Ÿe*…)„…!ŠOP‚ãHG& :\BpZTl!YHªR¬ÂŸ Ä# ñ†Pôž`B2@ Ì DÜ–¨9Tîx`ùøɌРÊá!OÁá—”Q‹Sh”Žp»ÌáÊco¡hô”RdŽ ~ÈÂ/PȳJ£¸Àzá[„ÑøÆ7b!«aÜb²E8ôq 4 ´È.`JBÐ!—¨ƒ“¥,l ©€3RØÐ`,cQŠcT"Á QUk/8ìàw#È:°¢ LLìÖ‚4£)î|ÿc¡el&²Ê®3`‚ß™û‚£?D^HÁ T #ÊÈBÆðˆq¬xÄøF:šJU€Á,>‚á&¤Éã°êFA¡ J¼  Qaþ²#]ÀYÉ:Ž¡ DDÀ….f Ñ­ÖÁù«Aò †uc;`H2x¡FYXG2 òLd¸Œ•‡Vß0ŠÔŒuFˆ°C0A R`bÕÈ#Á†c<ô¯²2+ø#øP¨`;̯€„!!åJT¢Ÿx͘UD«}aHh‹ÃPÁ›0ðCŠo(BĆóy‘‡‰AÄÿF "fÒ-‚Nr‹+îà‹£X«£fp…(ԃυŒÐˆ?ð# QP„5hŠ ÿ•••x‡‘üŽ5”©˜°Ç:FáC06 ØÃÎ èò0.úF`]‚í7-E%"1ìH„Éjœ•m¸,à!ªrà&ù6°UŸá‚~Â˰ƒ& â^ؘÂxªÝ¢° s&š%˜Àƒ.² ˆEDá YhÄ2àAäx9Ö}x2vA„¨F‚¢ÜPåJÔºP.V oP‚ 0aPÀafÌPA"ÒQ‹jx¢¡ð#@Œ(ÜaÙ/7ÿ\5ôHV)àaQ`Ã"ò À’‚ »pƒ3AŠEЂ£@zá†tl€Ãòñ‡e¬w€:çà°+¬gA ¢‰ÂåR ²› AÏvÈà8¶¥;ƒ±¾HG,Nq&*Ü! Þ‡# !qHÄBéP…–±8(‚ n8 ÌO15x‡c¹ƒ£¢ÀF6ì@Œó°àÐâÅ«vß8q!àžŽtøÂ¡%†/n‘ D\¢¼x:?ûz‘¢°­W2ÜM{“B…(´h qzBd"x„!þ“åòÞùχþIá3»ø©0~ò¥¦Pã8­µ*›©C°ZÝ Ek¯\ÃòËV¬Ù§ZÊž] Dm·lãꀻƒ®Ü»4ìæÐ‹·¯ ¾7ûŒBp ÄLª¸ñ½(°à ,XϱåˆO8 à€²òèe¾ ‰½ÇOx“NÜbÒq , ØÖ€Ò§ ìßy'!z2@&»Eôž¼9ŒXÃäµ@Ô³ÑÎÅb' úK €ªœ½<‹í$tû¶(!zóQß·)ý÷ë ÍŸ‚¿~žþà2 X‚®„àÿ &Rƒä9„Jˆ…þ‡a†õmÈ¡y~˜]ˆ"6Gb‰¿ˆ"i*®hY‹.6cŒ‰ÍHã`6ÞØWŽ:ÞÅcqýäZô0ÀÊ$pÀÞD8¤_ò0”RåkOÖØÕ ä†ÜuY*&Þ0àÀ—ŒSˆ!aÞe›OÐÅ7N…m"õ­TBÀt‡ežxÑ'À (·€ðàIhB>c’úHi¥A^Š)‘šnjV¤žî×i¨\J*€£žZ•©ª˜j«L± «‚¯Îj”¬¶>Xk®=áÊkF¾þҮºjQ±ö‹,­Ç.+j³Îöªl´Ý'›ÿ 4àÙ ÔûÄŸV@€”ÜvË} 0P %`š{“|h²nr,稻 E ðì¦Y.¾º¦4‰gˆK®“k”ß8‡Pͽ WlÅ}\Œñoœ‡]±P&Åw,`È‘MÆ@²U´Yå–щ@-¿ðrUˆr*D#Ôìò¦ÄЯ!m¿}¡ ìe÷°A( 2ÝeïÝ ]œ±¸à›Ðõ€ÞˆsB— Pw㉠±&à3¸M¹n1 äü×ÿæ'A€00É¡dWMú tp(¢F« ùë>tŒ{ò™6@“¢$àÞî‚È'À™Hþ©ÍÜNü\B´Ž¦ r°€$vüüÇ@HÙå‘¿«0€¸hòæÛöCé{2 ~—¯=úvľ@Þ⢀7~oÎðg,’¥€“ ð€ñ­b¸Äûú7ºD­^CÁ@R€Ï9™Â"$&±Ã5² ʪsó˜†CviÄ"JˆAî!ÌK[˜¨AàS›acjç ºÌ£â@ÛÈ~3¹0MÑ„8DØð0AnùÝ”lpÁ90‚FnñøÀ´ÑYFŸ[‘[p%ÿT± h€D#·,êo’œÙ¢€‚é=r‹iàh¸ Þ…8èò˜¥œñ êHãCˆ´Œ/ƒlZ\ê8§(@:" ^ñ6JÒ,â8Ý€¼µ)Ša,Ä­Ò ±IC"bÓQÁ&Š%<ád ÐÅÍl<áè ÅR€^ž2 (ààÈB®“T±›Dmi•еnSºCE yªlž‚ ˆ¤ÙF²*1‰ ĉØTÉOVJ> (NÏ.‘‰t²$!¼|f«äÓÎP§ü3Š"•Ÿû¹¯^çà :™+èH!š8è!»"‰Pf¢Gs‚õÐf1ÿÌ$iÄ!™”1PXÉGI‡€À{–ä6• (.‡ˆ›µ £JXáÆU,oêÁ_ `"²|z‡X¨Nlïp笈j8m6Ť°˜*‡›¹§i›Ö €I9ÀŒUÝ ¯‚†l¡3×XÙ`$ü)Xµ´u)Ãs#@’ZµÚ8:%€7°H“ò• (pø ©îu®ƒ¸™4 ð€µbkÃ'0 ˆÒ#hˬÐRÊ)^¯0&µDë…¢%"–el½’¨Ë`â±i3@³M2’`[% “pK@È&+e†&dS;ª‰õ°oà*lɶ-ëvµÿÐeÃq ­ '– P*f³«Ê6€³(DAx‰8ÖZaQ+¥_•AX\·¡ä=Ãü`«ËO0â,("ÁÜ»ŒÊÂ6œ jçïk«û$qdкªØ\ö†@¤}þT8ÀÂ.MCYd&Sî¯Q›“‹“ˆR¸æ£kÜm<,§VÐ㊯ëä(Q9$öœ=€zÊç݇Çô²—„Ëßt•ÁNs(7¤€ìÁØ ûålWù4Eô¹8OˆÁN s&aª@AHée)Ä2í³Ê€T–Œçõ×ؼ=#Äçuäº46ÒZVÎoósüzÞt²«rrÿ€’¨èÐyºJâìÍ,Š‚Ûî®Ò<@€ d5ýjÆ `A¯‘p“0½ÒÑ5!š|`ïÊÆµ·–0d»@€úÀv^TzlW×ÀÀç%õ(¥Ï` ;·_`ùkgþ"ÐÓÖ&³a ¥X/¥—…UÛÂq»@¼6kPg€KŽï³ x3 ݽ‚Í69e@xYúÊÀâxìnu~Æ&ãÆŽ¢œ¦¹?(7Žß(¸´·‘ëÒrµ¦ (€êÂ} …ÿ Öñö*l0èó*C#'ùµ‹[x§¶+E,×LšÜžy´½*™*Y¼ÍÃnÂ6ÿy=Jøî|È+…ÆÍdÞŸŸïâ7¿4AQRªÆê8ÇÍ oO*ƒ(îõ+dOÛæõ a[r\@c»×]½¦ßypä/D§Óöó€};hÙB~>VPÒpÝt Çû¼#)ÞÂûçJeDŒ œòSwµQÐã«N…€ÔÙº †<@†IèöJ8Ä<¸fFpCE‡¨!‡h¡tICeV–z¸u8‡t˜oÈHvÈà Sˆ¼pˆˆ˜ Џˆ¸ÐˆŽh ‰´0‰”—x âTG2ƒèQQøÒ$r¤?/Š G€@ „Šÿl‰°FŸÃjAð9¶Ç ÷‡³H·Ø6À¶F?F@AX¶¨úV»¸SÅè‹Ïø ³X´af†C 7EtIJ(c+õIŽ`Ñq"9– @Z¹(ÂD%l¦mCÖid3pÄP €×”ßX/r(Â¥$›(‚R;–2£@YþrŒäG¤Tçݸ 7V/©GpûXãP ^> ã@B 0 ¥j92ô€I%¹:•µ¦ £$ ˆ+pI‹³cˆ 7$&–,Õ‘Ò 9ìb=;AQcJx´˳%éO0xô‘6©˜Ä&ÿ•“™ÀEz´0©‡ªØe7 % ŽwÂl5 BÕ°24IŠZ1–¤€n^²uéQR"¦Q ©W†£1tBeh‰Ó÷ x–nyד&tâO¤ð ££À˜@@–9‡IET>mÙØ¢ŠÈqa7Àj²ó&ÊYNI˜J«p39ÚѡäíXÑÁ'Àó’{Gá`ð )4e–pÂ'U©q¤ ñ6OêŠhš¦jÚ-qD gI-°k p…õSi§ãÝ9§_˜2„2zU Ì2§Ó  ™.\$‰O,ÆU~êI'R2.¤ ×:+¤Èy—ÂåVx”Td“““º•Š(F2“¡.f&pt%¦êQ[)0Jâ‘§§:€êTDYŒ¤ 0[3Ÿ)£®°F йŠuz(˜zG¢û× ÙF¦4«¤Ù›ôŸÉZoÒÔAB:GcÞМ­i"0;™T¶‘Ûjc#TFÙ Ø®jp€'à­* §ôº¯üÚ¯þú¯°;°[°{°›° »° Û°û°±;±»!;ntpsec-1.1.0+dfsg1/docs/pic/flt3.gif0000644000175000017500000000346713252364117016701 0ustar rlaagerrlaagerGIF89avÄDEFÁÂÂðððáááÑÑÑ$&'¢££²²²cde466sttSUU’““‚ƒ„ÿÿÿ!ù,vÿ $Ždižhª®lë¾°Xqmßø:ç|ïÿÀ p8|ˆÈ¤Ê¨l:ŸÐ¨´ÄœZsÕ«vËí>³Þ° ,.›Ïh2º«^»ßpd;.Óïøüò¨ŸÚû€o‚A„…ˆ‰W‡ŠX|‘’“”•–—˜™š‚;›7ž¡q¥¦§¨©ªF«®¯°±²³´³¢¹h­µ°½¾ÂÃÄÅ®·ºÉg¸=ŒÊÏÐÓĬÍÔcØÙÜMÖ@Ò6ä€áÝê8ß?é1òò }ïëø/í>÷ú# ÐÓ/ŸÁûœm€œO⚆"ÒÈSŠ¢GvwÍ“×ÀÄ(aÿ$äŠH¼r0 83¥M+)Á("Á?Ž!oÚ̉£` žbFÊôÑFq0Å`ÀIƒÀKÈmO ˜Wö¢?``)³Š†â€ÜdÒN6‚<`e)3€0•‘e„$$€¨ŒaÝC †’¨ÔРRP?̃ÉcRN@€ÈçÐáp²¹-àý ÀÜæ@#`^›GÏ´µ$‡H‡ 2Œà6ÿ þˆP`Äð–$9›› Äž €¢Ÿ@Ÿ™L:h@0P)FdDÁѨÈÜ̃=ùOjˆ±°¼õ룶þh‚ô‹1d€¾ú¶HFzT*AÆtÆLò$Ÿ;En˜qù¬üºá8HŸ žÇ~w<¥)E#z) Fñ¢ÏجÆKh¡¬†Gh’úÚ uªÔdæ0ÕQ•DqLñ`€Ø‘WFÐk x"3ÃFÀ1† # K@˹!ç…çˆÇ4d€ŽÒƒÐw¢ ­¨¥ ¯bA qè !RÄT´0fðS@÷ @K÷1tó“F*í¸p¡ Æñ¿QXÏ|æÀ¦ýÎøÒ"ð¡ `i.ä…žR¾Ôý_Ms¡}‘‚§=5ÄħRKZõªXͪV·ÊÕ®zµ«T…(T£ê&ΣuÃy¨0V²*D€ïsS&âäõ Y`Fu1/ú´Õ­ŽÂ`KØÂ6K—séa)Õ´ÅÒá®ú«c$YÚUv²ïÉfÝY9\v!;ntpsec-1.1.0+dfsg1/docs/pic/stats.gif0000644000175000017500000002761013252364117017163 0ustar rlaagerrlaagerGIF89añ ÷ÿîîîæææ144ÜÜÜ™ššyzz›œ´´´ŒŽ®®¯„…†bbcÚÚÚpqrÖÖÖÞÞÞâââêêêèééÐÐÐ}~~ÂÂÂÀÁÁàààØØØÒÒÒ’’²²²ÆÆÆ°°±º»»ÊÊË!#$ijj¼½½¸¸¸ÌÌÍTUVÄÄÄ@ABnnnXZZÈÈȶ¶¶def¬¬­\^^ªª«Z\\*,-DEF¦¦§¨¨©_`aPQRœœ%'(=>?žŸŸwxxLNNlnn ‡ˆ‰RTUVXYƒ„…––—‚‚¤¤¥ˆ‰‰ ¡¡uvvopqstt‰Š‹‘;<=8::FHIHIJ¢¢¢{|| €€µ¶¶.01””•€€')*KLMcdeOPP!"kll—˜˜žžCDEefg‹ŒZ[\ghi688DFG·¸¸IJKklmEFGQST¹ºº?@AvvwbcdABCœ€lmm9;<¸¹¹>?@nop´µµUWWš››-/0’““vww¶··tuv¤¥¥~]__ 467‚ƒ„ÁÂÂ÷÷÷ùùùÎÏÏäå徿¿,./ÔÕÕýýýüüüúúúöööõõõíííÕÕÕûûûøøøôôôóó󮯯ðððñññ¿ÀÀòòò‚ƒƒåååtuuäääÏÏÐìììÃÄĽ¾¾çèèˆ‰Š¾¾¾rstÉÊÊ»¼¼ÇÈÈìíí•––£¤¤¥¦¦“””ëì쟠 †‡ˆ§§¨­­®ÅÆÆéééÔÔÔÍÍÎííî)+,Š‹ŒïïïÏÏÏËËÌÎÎϯ°°±±²ÎÎΡ¢¢NOP³´´©©ª(*+jkl–—— «¬¬ÔÔÕ¿¿¿Š‹‹ŽÏÐЪ««§¨¨­®®±²²ÑÑÑÛÛÛ³³³ÙÙÙÝÞÞáââÕÕÖååæêëëÓÓÓËÌÌììíÂÃÃÍÎÎÝÝÝáááÉÉÉ©ªªßßß”••ÆÇÇ¢££ÕÖÖz{{¿¿ÀãääÓÔÔÄÅÅãããääå×××ÿÿÿ!ùÿ,ñ ÿ™ÈH° Áƒ*\ȰáÁ5þIœH±¢Å‹-‚à°£Ç ¢Ñ•±¤É“…\ɲ¥Ëƒ¬E:I³¦Í›8sêœ(ÇËŸ@ŽÙ‚c§Ñª`‰´iÓ(yª°9ºsG(LjÝÚ1JŽ? QK¶¬Y‰2]K6’DlORª"*®]–*-¸›I|žLjXÁˆÛL«¸ñD·pÏ­ë¸qÞ½Žý®Ì9'aÃC'f,:0äÊ“K¾\Y³ê×?†M{,éÚgOK¦‹{-ëÌ{Ö-¼øÍÛÆênœ:9Õß];ïL|ºõŠÈ¯ß\®¸¹vœÐKÿÿž¸:ùéÙk^²îß(Äž èäžØ{mS¯È†O<þüà³ùg\z'¡3Æ ¢ØàB`Ÿðƒó½…oFAòM# ̃B:Pu /[¢Ÿ^­'à 8 Z7eRC¡C†`¦ÈRB„‘1G¡Qšì‡D¢$BU›œÁň˜Eg¢Šv™Ç$n–‰Gþƒ‰84ڈㄔñè£DòÔ!‘F"YâfO²ådš°E™Ñ%/üÁCe¢È2”@ň’!o¸Î?4 &P`C¤8…XÌHQ7æDböéÔãÿ¤ÓHDÀÌüKpAÀ(MÒ@% RÿTd•Ï‘šlšµf®¥¹™¢†$bŒD‘QË?ÂüñA!U$£Š-ó¸qÉ? òFüà #óóÇ-šðS¥[îÖåNš€†¶ |á< ó6Ò00.ÿt€CÃà À?T4«™·òzÖ®wækIè¸ð‡ÿlB/ŽÜE4jø0Ñ1I”JB!À@†VqÊ(U´g¹–Jh.UšP ÷ˆÊ?+Ä M/!Ô@ (ÀÒ€ $³Ç <Ö~ˆõ—ðQ?íØÂ½g%U´IžLt¥ÆABÿü;J®P@ʱŒÓ¥‚ešÓ¦!EØàE´ÿt™KÛª$®Rïuᣱh&ƒL¤‰eD’„yÒA!`KAò)kDôÏ'~°ÂöÊZ¶œcw;¢û%E¢¸QAŒeÈ‘D4c‚ß)]k’â-‰¸N‡ÕE2ÆÞÿH€C™„À‡1¥€™G ˜ü“ /4Њ¿prú¸©¿í²ŽçjúúDÔQ ¸Á>8A ÍpÀ»À€ûÝ™Œg8°2È£ˆ'–À!z ÖþÀˆ£ 2°Eü±ƒÇ ÂÕøW=NpŠLTÄû & ºÀ<@‘ÛM4!Ž ¦ È8êð‡= ã¯Ãÿ¾ÐÔ#§èE"A2ÔàÓ: ÓÖâ‡ö9í€4)Ù’@Áí,HÁ|1æjŠgùò°‰\q‹%Ñ"ÍÒEÿ|ñ,àÃ*Aƒ”¢u CãY àƒ?´¡KÞãhÀEOqO‹ø¸À‚´a‰øƒ&‡$´Ï`–ÈA(YID IÐä\°Α±i$,íRÇóHò¤¬d†¢Ê/ŸäU(GYJ´ápPå áÊY22EÎä"$vG³88BHP î.ˆÐc1¸ñ(2š‘#:sRKòT“,è`A`± ‰˜‘M‚,K' ÀÿntïoD§:×yœiìc BA‘{¦)Ÿdù…LèÆs®s ­I;ur‰wlÇiHéQOõ­®>Ý4‰'ARdäÝØ/JˆÆ% ÑÄhFO²Ñœð 8€ ê v‚ ?4)—n dã ÂE2QŠâœªé7ùcQÊr§Téév„àyÜ$LxÃDv!Œ¾Ì&zˆ‚D 1†‹xâø‡ð%›²§ÎÔ)X3ÒÅK”¢s ÁpÖ´Jä¤(ûl’‰AxA"9W6ˆPô"È€ÄuU¤渀·Ú´®æô«ƒÝI‡ñ"ÿ€à ù©I$œ\HË&hU+µr!YÖ#ªÁ$ò30#à‚’Z  X€ÒÉcРРXwP;`,p€µkÛª®K7ÿÀ ÀXذ #7 ÷à5ž2R! ÁËUÃ{$ ðNÀknðpp3PxU0›¤fñ±6ñS`¤5_ÐiÚûR x ð^ÕЗ %ÀP(™P@'•ÀŸ Up,;¿òû<à—ªð-1¶`%Pqžö/và¶Ä‡@ Upsq ê@|oÿKd*QÐ +¸Åÿð Q` @b\;õÄT`B> úznLÀò‘° Ià—Z W9¤ðyÕþ \02Z—æóŠ.Œ°ôwéÒÇà™ÛŒ  Ž`b€\°½@?0þƒ4Ñ8æW?P ¥ð 1pg~°ðÐgÀ±Å»Ë‰{LL£Eü6ðRl ½p3îðÿ@Z\De€ŸIÍÿ`ÍØ¬ÛlÝ\%ððbð ° ^#™“ ºÀ `6'p.kCÏA §0øl‚V=òuS |³ÉÀ7ÿC¨ ³cÊdš™ ?  rðÊý ?P{3ÒS«ÄeqÒ'A §øBð[›§À 3ñjÁ̼¤…%<íÓÿÍA]CÏ%Ÿp _0rÿ° Î1A Œpä €ànC ðXíi¢°–µÈ ë%bMjÀ}ô ðuuÂp³†<£@ f÷ ;fRwNk@#q ® Ñÿ½Ãc@p~¸€M‚mÚ`ÿXƒ@sà[K UÃR Ìp ¶ðOÐ~ЗpÙלÙ@ÝÆÊŠÁu h³€Ÿ€ a€K§Àšp>0‚:Æc†å‚ f° »ð¢ë8ƒYw§ÀÛùl¤À@EèÐ5Öâ ;°İh0Üñ ·ÀMÍ jîÊ·‰Á X€ÿ0•P¦p×mòPÚÿð_À $}Ñ0¸ÕêË9Á J BÀ;๠àÿX.p (@'²ð1çðPð ¯p6 þ(ðjÐÇð{à5 ¼à §ÿp ;€˜ '@ vàn{¿Rk%ÀÀ<àR4@vp°¡ T v§ Àâl1c°¨eÄ®4  ½PCË ½ÐT ©€O@í¸©à ˆD@Ž Ð* ÑkA f@©UÃv‰°º  ¿« HÀX _€„Ð.PÔÇ{Þ8Á 8 ž›Þ;1°Ù•ÑÙO’Á¨P¢ëºÎÁ´å—PøxÝO—þ>Î ÅîÛ ”@ž[òÎ$ônüÀà ®þ$üN€_š@©Œ^80V `ª„½@©Tñð`eÿÁ ‹@ø÷¯"‘€Úójnà_­ñ<äóFŸ>Юb1cà¤\ÑOP ª” 0 2­„NM:PòJ¨;àñ`KšCÏ$–€Xà`¯„°Œ I‰ ‹Kÿo掄`€ä^~®[P¬k±ðÕ@ç¨ïɱñ'AÙ¤Ü(ðL”Í1À  Žð*’ó'ñyàlêøÆù%A [ P#¯b{oLâù'úT1´!úq ³C¯öÕM 0(aù5Ñ 0‚~¯ _ÈK`D@7OÿYªÐ "õ  ð.Àà”@ºo °|'q `€ýÉ"ÒÕ‚ Ã?êx4@÷ÏàAƒ hÄBˆ%N¤HÑR¥5n¤ˆ‡$Žã°`KV›!º“ÂD/$éÿJ#ª@%Žþ  ª¸®¢Wꤚlœˆ¶‡pi"z€˜)Š;þ¤éÁ\äŸL\HDŽ>ÑàPrh'Tþ@A‘®D!¢EŽÞIpè¡! %$ÚÄ{¸”ˆ:œ´Éa“‰f  ¨ÈàY€2²X„”ê˜\À6"<¨ùG„F˜Áä ø¡d " c `þé‡þq‹pfá‘4¨Ä˜lpá˜c¤!D“GÁã äÈàŸ>îȤAš÷'DÙ­¯¢ªFŒ$þøã— |èfÔ‰z‰t .L©ÈNùá¡+:CƒVШ7‰€ƒP#OløÿÁ þ0€#H€UFTÝèC, –9vÿ96ÅŠ”eÖYh#Š$ þ‘' "òC†'5²bŒHåŸbà §uÓþÇÝŸà•7¢zWYÀRÑà5äQ‹öþ¡„_2ÁA‡È±{yò„.±þå‰VþÉc‡KÐqóZ.Ö 6ˆÑÈŽ?H@„VÄ)e "Htœ*è$0Tž#b禢à# ĆHjŽ2©ƒ"9³RaG‰"AÂ=APËJiŠ "9ž€€A>!ù½d#$RÜÚ8€,!!5°BDVQ‚_D ˆ@Dr!ÿ…S˜g"› ­ÈA?éF·¸ÈÅmPED~à…tÄ6 .—¹Aü£{¸™B·‹-ä§KÝêþѺ×Ånvµ»]îdqt0€ ìOäƒ$âSÕ8’ j0Â?¡þ$‡T|! 2PE Q…qD XEXXµõ±ï‘à ±ƒЂ„ Bìá›X-2aZLD€‡ƒÄ>0‹;ücŠX ±ˆ¬`ÂÝ â‘V@ÄC>цÔ mèÖèÁ[8(8€”C#”p 2€ë@Æ/ázü!oƒDÀ)ñ\GX¡b€ÄèA„âG(ÂA.Ô! ˜ð”`ˆ€Á;vúBŸ. €x>0€/<"h/n1Tøuy\@‹(”à–w¨ž" &Ì£øA>’8cÆ(èX@"º€€$ìõnq‡¼¢9˜€à@th ÙøG,pDôá¦WàCÐaŒDÌAˆƒ(C|ÿhÀ.p„!üㆰÙ"O/¦­ža!N±„D!à2kZ(ºÏ½C Pƒ'œYR¾PósOa‡™¸à _D±€xR}€\D^û.DêŸpdE<‡&@B íc)G^ÁƒHÁŒï=%Û4r‰ Ì¢5˜ˆ¬zR´"‡Ÿ˜‡r yè‹vB´"¢‹ÈùÂ`Íð’ÌrŽGl£Ÿ¢ :O(c·T ”qLË¡qYeà 0[#è@5Òý˜eÐ-´wEàµ_Ø+:ˆ,E4q‡@BOêĺ7‘Høÿè†_9¶øþDç¥Hÿ8ÊÐŒøÒŽHl]$^³K¬/䎼p¡\@ÎÉdK´e³0@Þ¦HA+ŽÌƒÇVɸ „Kð µø‡&‰Q4U#ïÆ?0Ñ 3Ô‘#è‚´Q¡ß÷•?AÇM)²‰D# À.&âð»ó…&Åß3/¢È-PP‘åjÊëïq9NäaÜ]b9Lå0ážLX@Ñ© %.‰aø#éÅ øÚߣõ°â ë†g¼&Ñ>îk@|@Œ@³€½œ(…ð7ƒð€˜%  €ÜRa”@ðVÄH¢,J£œ@T‰ †püqÀ‚Ú9 €ÿ3 ¨ƒ‚ù‡ X€ütɬ¿Ê9Hœ¬ÆkÌF(‡³â¶(Á£¬K»ÄÈtœ 0›ƒà‡FàPÐ,èH†Fˆ(¸À„( 0pH@ƒ¸À&GØ$¨„êƒ4x€s`1øDˆ (‡W˜,o„ÃhÅ»¬MÛ4Çðè@ð€³ò„kp…GÎÿR(DÀ`‡U¨½Q(€8¸RøW ‡¦Jð€`ø+p€Upz e¨ p…pïOWÀ€ ‰Ä²Ù¼Mú¬OrÌÍï¸<à†ƒX†v@º´OPYLJÑhèXجŒÀ%PÐÏËË»è @©|P EpNýP QQ-Q=Qµ€x¡Qð}QQ%Q8µÎÛ?ÛÔÐ 5á+?ýQê{>ʼn> -R#5¾ìÛ:½KÝÑÝzÅËc—çˆEïPü¬Ï&ÝÐ'uÅ(-–)…Å*M›+¥Ï,•Ð-µÃ.M”Y=ÿ:t½D¡PŒ,Ó=Ó:LS$)¼:tS$Ó‹”S¥S:´ÓÁÓ6¸×[R»ôSÔ9Ôù Ô9D¼í³OEPF•CG}H•CI½Q­T½TЏ„ À…(‘O@‚ ƒÊÀ‰LýŽMåPð€^ "€…BÐ9»Ð$È2è›»ÐÓáS‹UûU‰ ÕŒÂXƒù€_è(ø‰XåŽYåR˜ýR…h„p° Là p9ȨÖ3Ô7EÔºDÖúTV¹9{0OÐ…ùð„r0ˆk`lM=¥cSœÐ„ȃH‡F(¦Ÿ0…ìü‡(]û*…=T­Mÿx¥Oy…ˆXI„Y0÷2ˆp¸ƒ0—]Àb¸?`vp [(÷€2hZ„ p'È$ˆ¸p ‚å)ÂË<  Xƒµ›:h¢„`†ƒEÐp…Z‘ˆ€†PZ׉m׊eÒ8ÜQÈXˆ@‡J¨*X™KH†Eê…DP… ð0 .ƒbHè3Hø€Søƒ>Ð…¶:#.ùXXØŸX• X¤ó€$Ø_0€PxòGR,`+ð˜^ðšWË4³èÔC]¼Û<;ŠôÚ¯œ°*ø8JHxƒB`5ø;Ћ ¨¨€[¸cèÿz¸A Søƒ*„L\h„-ˆØÁK”mÝM 7¨‡À÷£xePFÀ…&¸… аp@ƒÚÉYU@‚hÁý\vÝSÅPÒ¥MÓ°•¨„j@‡MøƒYÎVx?ðƒ]ùgxSƒ@؇ †* ;‰F°—ÁõYèÚŸ@\ˆˆp€G(aø.àz°€}à„Rت•ˆZxÍíŽø-Vw=Ê‹½Íü5ˆKx&ƒx…$@€OøƒS#†0`ƒÀ„ ðèÙ^8Sx…? àˆ …{ Ì †/Î F_ÀmÛA1£„xáÿˆ8†^pâa­áù0ÖŠÌaÛÜaXaƒø„:xLø„K†zP$6S(¸©M ƒ/0£øc¨bŠ€T(;•ÈÖæˆ^ûؤ;ˆRhÄ…RÐ…1Œ`‡ø€4¦ˆ †³ ÐÕÚOíÚû•;Ž90ƒ¸0ØK¨…j8‚68Dðž¤[0!ƒ€¨‚6‡? )ƒYØ$ÀÙ-ŽÖI0–.Ɖð¶ú8P:€jàƒ'h`„ BˆY¨¨Rà…¯Ì:8î V09>G:®M;þ‡ ^p„vÃ)‘bˆŸ….:˜m‚ÀÿàoK^ <¹ Üæ@ØSè'M¦S¨…R(…YèO@Z¥5¦e P Wø€MãáÐR bõ ;@ƒÖ:ˆ~6Ǿˀ6ALN fø>Pµùf9ÌiÏ(€?nxˆŸ.Ç ¶Ë¡–ÀH°ƒ$È€Ñ}8€7°™#Ð…+`j´XƒNøjý€@ˆ›)†VÀiØÚD¥åZ†ˆ¬Ž@û9Òé37pÃMë:ìæK€¼~W½Þëƒèk,ê»x3øƒf€´îÁXnŽ ÀÌ®M „6Ê«®KÉ~@Ê6 Lƒ?¨ƒã iülÑ …øƒèɪ&ÇÿÔ>ÊÕvÀÖŠN°|@ÚNA§v j0a…¡ÆÆádžìîyßT€øñPvÐèmV=œ`và8‰°5X…{óºåæŽÞŽÅß6Jë–ÞJ•À=øg D¨ž}¡5 ‰O@„è2xèˆ+x”3¼õnŽö†Å˜OꞈøÖ~`:@΀¿dŽF’/Ýv0ƒÖ(uz ‚è+@†Æ SvÓÛœpû¥îü]vHœøm €j˜iIèÜh q½‰@¤ø€gÈ?ˆ jh4É“ñb¡ñnÈÎX[xƒS¨ƒDÀ¸ÃU€OÀ'À ÿ²ˆéïNãˆP‚ùw¨R’OȆJ׈؅^pYÈŠ¡ˆÀƒUh‚/¨¹ ‰ŽÅ÷.Jyõ„(¢Là8À‰Lz¸„AðW• è&8#çâÂ>ˆw‚FÐh€FàKˆ@…^h"ˆÑuØø‹ hÚ:?‚(„ÃBeΉEFwÅG'Jy„? ¹˜óÊL‚ÔˆM ejþî€MØ»tb°‚tA`ry>0ÀY ¬Ë,€vœ°í8>í¢\vŒlög7HøƒÕ]xD 9ïJ†T¤3ð*OÐ!`‡\X#ÿ„Èö"Ÿ$ ˆ†p÷/‘ÿÞˆ]hþþ ‚¿=ˆYè 24Xƒzº:¡ÔQ!ÊçïHv;Ô÷‹äwh>ø‡Kp…÷ó¨‰è„XùRP)˜4‰ÈöÈ+ˆÇp¨›ŠnÇq¹ý`°Žˆ¨€XƒðAˆQ€‚qå3‰@ýê€Á‰†58f•°òDÁr‹Õò½æwöÉ„ÈL8·ø2x‡¸„ˆ˜á0…' 8x« Xb„Cø0-bzGhHJ@ÐzÅàúŠP‡ nðª8¸ç†3Ð)Vh‚`]‹†\ÿÿ^€ƒº$ð¶d lˆƒEr‚87öJh +Åw¢äy‹äw%`(¢EƒV¸ƒ0n¨ xµ°Ü‚@È„>È;p)ï‡@ èR€½x…6؇{ ‚é‘›Žˆ,hð ÁXUD!<ÈŽ‹¬2£üአƒfdÍsRMÁOx0‘Cí&#ÕN”d$•™3Òüg©Ò‚š:wDCÏ B‡-Zp#Œ2mêôéPŽtFúS«`@ÿdùAÏcRþ½ ðïR†;¸ÈÄäU‰ ¥´é ¯h(°Ó›¢ƒ¿”4½™“nPŸ@ÿ3n\©RÇ’'Sþ'•ªU‚˜Üú—ŒÐ?ó6ñääŸØn}Ò ©›(Ä[ù ]†EGÕãE³‚½5÷9 &8Uaœ·*^î\2ä¥Ï§Sox¹æ!Ç 0«IÀ,¡`ôÉÊ…Ž øú Q§š‘äVÿ—Û¨yšù©^DÈ.LS(W]sõHTt*øÜu V6ŸmÔÝç`eF]jxTRÒmøáS ‚ˆ„õM8"b†Õ—!Š&Ø"ŒAÉÐ;Øx#Ž9ê¸#=úø#6²#‚ŒÔWJ $$“M:ùd޳8Ã+þ£‹b¹%M2üñ%˜aŠ9e&™ešy&ši’YL}ìT¡&œqÊ9§™U,QßU@… }úù'  :(¡…z(¢†F‘ÃxÈ%¤%¥•Zª£Ô7Š—zúi“" PŸ$z*ª©ªºª XI¤± ;ntpsec-1.1.0+dfsg1/docs/pic/peer.gif0000644000175000017500000001151013252364117016750 0ustar rlaagerrlaagerGIF89a)/w1!þSoftware: Microsoft Office!ù, %ÿÿÿ„©Ëí£œT†‹³Þ¼û†¢VM㉦*Z¶î ÇòL×öíÂÎ÷þ ‡Ä¢±ÀtǦó =&•ÔªõŠÍjg̨÷ &Noݰùìo×ì¶û O”ÑôúP]›Û÷||ü(8¨ ÇwˆæÇ…ÈX§H)9¹ØhöcxÉi”I *ºÙiôùRzÊÊ“: +˶ÚÊúÚRkkŠ;ëû ¬¹;Œ¤¤KlÙ¼ÌÜ\ˆL¬Lq (íŒ-Kí™´ÃÍkL§ñ-Þy­½ÎN•±4èÓ…þSï¤q4Gɘ؆vó ¸ÏŒ!&û\§¸Ö .PТ ÿð"”3…ã’"Gú!ù±ÁÉ„`j1œwáp3ÏÕT±¥˜‰Hb:¬éͦOœ¹8â)yeH¥IS65€”ÁQî<LZÈ)Ô«[»zì•jÄ;<ËÒówΡZL9¿¸,›Ö,Z´pÝæ²ŠÕkÓ•y;úÝkBUc*s½èuêa}Â( yY2OºäÚ¦Ù7óds”Ý•j¸o¡Ä¡õ2^<8Ï»½«Eá4„°KÆ>6;®ä†-Gy›¹ŸnÜv’`5SHäZ¯)N]öå¤H£Í»®=¹å4KšÝ•o(ÀÏv/ó=(QM®Gƒö›Ê¹lèìcœWôs øUÚÿFEÜvjmÞ9ã=QÞZ¹ àzÓ”vj AØ_aíÑÃ@ 5×Z~¬m¨•uêsÆB›E6œfæ°…C&ÆÕYzÚÍH^Q#štÔ#eÅaTR‡a6¨â\+R¶ÛŠ-HÞ‚0©`’ Ú%d•VVR¢zOö„g>i¹d‹ähùPJ2ôåM,^Éf›&T³ mRÁi‹œnÞ©‘t"Èäžá h µù)Ž˜„¦#h¢nêyhv>Óè%*Jé/ŒFÚ›¡˜>Ti§ô]º©=}†ê §¦*©âiªjq§¾Šj«}Œ*ëo°ÞºNª­NzP­®â ¬3+ Kl±#¸cl²Êÿl³Î> m´ÒNKmµÖ Ëë!^Ëm·e»(¸ÞŽK©ŽÍšKnºÒ¢ ,»ê¾Û.Ïjo½ÎÒ;¯¼öîûjÐúËoÀ°n›¯À,.›Ì0  Ÿ›pÃ7óp°OŒ1†ãºqÆç±•L2;#÷rÉ*Grò©-¯ ³¥) ùrÌ6ÃRs§9ßÌó$;—;sÏBcñ³¢E4G ºtÒNoÑ´ÃA?Mµ&SzuÕZW5ž]o ¶ _‡vÙ€ŒÝ&Úf¯­OÖ©©ÍvÜ?Z ·Üv?UmÝwÛ­7Ínï vßA x·ËŽø È&Îxãî^©®ªŠ+9©ÿC.¢úZà«#´vÞÄåo¾yt–jÞè¥ÒPy¨ÆžÅå¢×GtꪇIÆí«óçËÔnǶ!I€"S;"5|ñÈ'–íºÛÊêó5r/TÊ#DüIK-eÒU>vˆ}Dz[øF¸Hü„‰¡¯—ù ŸOë›R.=•%P×¾‡äk¯}_)EØ>ö‰ÈG¦CÌaÔ±Ÿ¿ f9ï+Ÿ„fs¿ú¥ásÕgÜã¿ûɿРÈ5ÔP?S`]ÃÀ‚ïÓ‰`ù½:*ï!!?hÂvŃøò ïj7BÓ°pýéà)Ô@ò½I†¡£  tÁ *Q‡9 bG¸ÿžˆùëb3XDöaÑCZÄ!³4| ƒ~ QSèÖHCýÈqˆG #«c=û@Ždø^nø-þ5§{ÖÛÞ‡ŒHääâ?õßœx›)yŽ9ðcŽJXHø ¦$"á¤Q IÈN³cc$‰™:AR"N’%s•±Rbé7 ÒALl™F\öCMºÜØtÂJ\¦…—fâR—Ü=XN,‘¤¦€†MWÐ¥K4A%>VÙJ/Is-ÑL‘M¬ÙÄ\òn¼!7O„Ωhz¹sK-•´¥tžìÄÜÀœ¹ j2H’7Ñçc¨Æ`ÂSF0AÒšìÙ/|rf ÞJ”š…îÿEÞ‘¦”hN„&ÔA…sÎdI¢é)5AZQÜiÔTÙуA:Qu¶›Ùœ§GS„RŠt¥:c䙦 Ój†´ž=¥?»™Rô¼Ô~<íiÓXQbrs&Kýg;1áÆcæòHT}¨J›êfž-„b£¤Êª±q²T–³!«ŸÌš ´¦µï¸dV2IHOö/”Ö"!‰ÑWŽ”€…áýŒ’>¼tÒx|ÍccßÃV2±=ß«£Û×Ëþõ‰R ©ã7Y!VöŽü{f—?ZH€U4mdýÃÙ4¶óˆ¢‡˜Â"ЯÌð˜Ûê9§t‚(ì¡àJ[~TryúÿÂl„K=L ä‹Î¥n¿ãÊõªÉU®Ø¬ Yð»Û@¢i>1HÕŽ—JCãV´NŒv¢¦|ê{d΂‚‰©ëõbk¿e^ÞRñ´2»ÑˆJw½K^Œ¹’ONSYSŠ~µ¾ í |kêÒÙÈ’ VD‚Aô ƒu:§Œ)+/ŒÊ™ÎöÏ„0=_4᪜;ég”n‰âʬx’d±±npáø^SÆå"iO&:Í ÏjÇŽƒ‘U!«˜È‰¢1Y,apÒw–Mº2:ˤä–Ê36²WŸÌÏC A–J­9e2;ÌÌ85q˜ÅÝ.GòËY¦Qœå¬-:'ÕÎbÖ0ÿ“]|a7c¸AütÚJ\Râ¼9Å[þ%I?Jè;ØÑs.ñK*ToúÐM†–å’cqÚkî=ªLÌýî”Â-!SP•jK{lÀ3«Žq‹ÛÝLå9Ø«LõVF`Û‚Ã^ökE(` ö+wÍ+cýˆ×ÌFûÚ}«gëgœiF}‹åc«Ägs!´—-KûCuÙµ%5gA‹7<Ž[úÎö·=‹Ôžwµê%ïWÔÊ5z×»ÙÄÆ·O˜ÖB\”êfø ÞBøºo¯Â‹=ØCñ°ÌÝxÆq”ŽË›bâukqøï•gWäàø¶ôìôò å¶…¦íx,²ÿHD•½†D·÷´fgÛ–‡Ú¿ >GlûÀ:úðÁÏ÷ë@]R`vÈ9ßÏnØêЬöÁAÜ6éŽØqloÜâÚ÷dõúV·šÚß^÷ U|îØÊ»*în Às[¬|G˜ß—+x.›„/üZ¯´çfÍ“Ϻã]fù'Ö×Åâ/²ÏW>ag¼èAy“u­ô?ß;êeFÖÿÈõŠý¿hoñpã¾ö¶¿×îs?Ãkî÷½güÛ,|¨¿ø”Hþn(ûÞ.Ÿù žþœ}ŠYŸúÝþõ!/}îsÌûß'¿jÄOwó‹ ÝÐq>ú[ŸVx·_ýï+ýI>Àÿù×?õTi|ö{wû§| ƒ@îçkè)Ø"?ó£ €X}tÕ4 èyWð€ë.h4¸o‚ŽåL‚¸'8‚%ØGÒ÷7(8x,h‚ÁƒG(ƒ1€¯Uƒ´°‚9È{­·w=8|@Øq‡„Å‚wIÈ„Çb„½ÃDÔtŒ&vèG:àG"47…ÁöƒÓá;Xh~¾547r_§p¯Ó;Ðö]i;c¨…6÷—¡ˆr˶‹™÷_§ŒªÈŒÎØŒE¤s1wê5F´¸[Ñ3ÅhFSdŒÀø–å_åG^äC”(Zs£o˜8ŒÚÆ.„UQÆcª¤qØá^´'óB Ùq/ÇAbØÇ^g–bÊÀaS¡WÙVW $VÖŠF¥g­ð‰ªàSlvˆÇ#™‚›ˆJ‰W‰…¤Ð~†º(ˆÉQô„h…¢LäÒ…Öad/cÄ”UF cVT—!PEÿ¹UGù”I9d?é-DèyCjH¥•\¥ež`sùMIÆM\X^)NAi0VÖb75h¦V–²Åp®ÔQuRøÅ“Sù„?‡•)S.…TB–Qfh’kI—/ö—–—Í´“ùÄP|éPC¥”´’AU$ õ%^Õh‰y•‹I™&E™6õ–bð•ïÄ–ué–€ÙYš‰36‰™‘V™‰—KÉ”§é™™Æ—V¥š¤P’?6–}YU³ÆeyN[é›­ œ¹É2y&Åd™°¦SD%™iU2q_ÁÑU°˜dvzyâVH‡å4ˆƒÚlý×t²…{ ‹Â&m¨Â‡›‚Џ`DGWçé‰ßÿÉbáI=èa–øˆï{˜Xp¦ˆŒ¾h2é(˜JWQ÷Êsh9p­¥[Ñ5ž±ƒ ²y†yE¡1¡×¡Uô[¿ÕåÉoh‹Q¨¡ÛÓ"j  ¡ÙØt-ªŸr¡Ñ© ê¸Cì£ô¢šŠDJb¿!k¼c–FEÚ]ù£´Ø¢|K‡"¬è…3IƒÂšj†š¢y¢li¤Aæ¥D¥gwu³}ª ÓH'v ¹†›wi nšš8Z•l:™±¦U9•ÒéI…¨•~ªUšÖ¤[T•jZ“{ê–—ykÛÄhû ¨ƒj—BU›Öi§]Š 6‚œš#wÿO6•©'F©ž…L¥ŠfòdQJ:¦Ÿê3ÿXš¦Vªðuª_ªk1ÒP…¹©÷ ¤âG$hr¹aAQ pœƒÈ”ÈZ™Åú˜bÖH°º™c‘_?g{¶¥ ·¦r RF¬×º©œêuÒjtŽ f(¢«€9iwjƒ½2˜Ÿy®éZ­tÊžäºY“©Oö`~®¯Šx!¡¥Ž™fj©¾j¯ ªQ&U†ùœ3ÒHŽ“°~ùœ{—Ìš¨;sZ±&бƒ°žÈ 'Çñ±Ñ 2uŸNWnéŸÌ”²Wšˆ)ùŸ1)>5ŠIA³i7<'‹H×¶²Ó²ù³ÖLá>wŸ É#C¢PÿªCöèŒ#+X:(E)ZÅcYšnñæ´ˆ!Ž—ŒåxOk¶Qz³ÊÚpÔ8¶¡Ý˜B§a‘gk>%—r'Ç¢ñS€û| DZ º¶cwuée@¸x·|Û·‰»Ún»rdëZã5·hû·rz£­ø¸ŽÛ¸¹¼3¹¥è5tÍX9@Aª>L [T‹«äpñØt„¶ü˜#¨›o¡+¥WdˆÉQu‚d¥@ëˆéCy›¦Ä i«“§·vøGýÙ‡gЦæætÔö’ð¼ãf•ÑÒ„Û«8"È½ß 0«-¿jâË0Xê5Ùk¾éǨ£G¾ëÛ|™€ò ¿Ôÿ²¡§¾õ9,/÷«¿tó¾>ø¿üÒ¾8ÀûRÀ•wÀö’ÀÛÀ ì±<€ü. ¬.Œ3ŒÁÙÅÁ±óÁ\¾yÂ",À$lÂã¢ÁXÂ)\„(ìÂܲ¤ÔÂ1Œ5Œ,6|-3l|: Ãö‹Ã>¼€,Ä@LÄE¼.A<ÄH¬½JÜ‚L|{G ž'ÅSl1N|}VLÅ?¬Å]ìÅ_ Æa,ÆcLÆelÆgŒÆi¬ÆgÌ.X<‘ãÆ_¼2ª0䏯í7Šô¹(yÜw ;£±ï‚É38 Èň<œÆtºïv»ŸrrÓë$èÇ?É_  4sÉ\£•쀛ÿ,¶œ,2 œ£¢ìÉåU¸ü<-ÌÈ ©¢tʨìrª<`xœÊsÔʱŒ3|ÜE ÉJ˦¬Ë»ü¤¤ÕÇäiÇÆÜÃ2,=qKLQȵC­'ÞÉJ—™¤ÀªU#1VkW7±‘:/9\:Yœ%:Z[bB:1ÔÈIÙ¸z^s­Jd¥¾ª5¼©Dql>ʺ9«”W\M#JKJ¯œ:›{"ä×R+1(œ•xŠŽ~L@)ÞÊC×Ãgjj€’v3ÓÑÖ¡:çÓy’‘„³’'rcMUj¥ÕÇ®q`#²©z9:1ѺJpl_?BB­…%…x_л#w`5+B“La›µ¡$’ˆ=5ñ9²©p#;lŸ¡¯ff3„zm;S‘))!”|o,­‘eEA:ŪhRDÜÏ1`Næ×KÌ¢,K“/06νA£˜W¡.Ì™3 Þ ~s?Á·š~l+÷îàLL1éÞW³¨EѾ1­EÒ>]ZCPïÜ5RG"‰Š-‹–½2KŒBB1®™u‘t@±©_‹†‰Â"ƒv,ÝÒdÕ¬ij^$Á¶W´gPK)“½…}€‰aÔÉ%NOBË­4™™fÓÈAD) JJBfffÔ«U²¦:Ûàæ®ª©§{#Š‚MªŸ1ÜÅ:ûöï8—£¦ÐaY1ãÕZ!ùÿ,–Ãÿ <&¥ ÁÃÈIA Ã%#.LAQ—E'9,–DZ€‰I’d³“(KŠ<Áò4-šYhÓ¦0lr² ¬R%X@V²`Á†Qr`ä(RK9o½,ðâ5¥ê” òiͧ(¯Óˆ0mÁ‚Òz(ˆð˜9ºÊiQÅŠº”"Ò8¤ßX/UÛH˜)É-Y¾„ÙNMša„±3ÈO €`=̨·ö9©E XQ©Z¼Uk0.\–%;Í íƒC-qˆcÄ»)êꢴWãF"t(_ަ¸sçË—‹˜NýïÇ—1z»£ûŽ A€šÿ«’ž6vÈhœ´é Q¥ª¡H‘V)^¼p;{Z>Ûj­•[(íôæ„\ ×%”¼ã`GQÇÄ„»ñfà…ND¤á„NXH¥}”IcŽ=ÆÝÇáxæU z (°„í5SZ9ðIeÕõýWÄÖXùÐc¤‘j±%n3¶Ò F$&è“­¼C˜GÓq¨á\ॆ…"¦˜·4¡ót¢æš 1b‰4½%GdR¬”Pç‚z3:±‘{8ö"ÊŽFmõcË 9€˜G©ä1cH#"FiQ;Tê£)<¼”%˜\Êå%aŽ)f[¨¦èA&i²©æ<ódÿ§œrÔ‰Yfåå‰URXy’87z £U6ôh~#È&–˜11ɳ·Eº•Ö•©>œvŠF–L€Y¡¥u½FªäÓª¬ºÚ ¬oš(§˜Løà €´h^g6(ŠRïŒ$°> 5hUFÕ‡–°û Él‡L¬Å³í耵QbJeYd Êy»D‚ G&ò:åäÙÉ'³‘b&é²Éî"‹¸ûV³óÒ›Ù‹xÂ=u3Ã}Þ0>Â÷§üÔB-àGŽJÌCž„©ÇD 4qš-0J#W…(ƒªu)‡ì…%âøÂ¸Ò‘®ã³Ÿe"¬ˆVX츰<ùÕLÓŒ¦²©ý¸(ŽrÿL"ûŹ:¡IeR˜‡;ÉR¡M¬#QrÈN^ˆAú« $@] Ô%kØ…™CtÇÌn6“ÿð,h?뤬©”i=-.Þ™ØÝX7;—˜b‰ÇŠî4·>åÐoÁ!ê(-f3§–-åN틤†$]é2ˆA±‰„'Œ]–ÁQ³þÀ?xÖñ BSæ=g)&Š–¶v¿Âá-õŒØjhF,&q»Üö5xñŽ~ŠÐäUO„ 6QÂ0rÐÍ!–ÌdBD1ê³ 8¦Le gŽœÀ°fÃÛaÐ ›Z(m%JÌÖ¤¼ƒ9Uq%Â4ÓÒÿ†ÀÁ˜ñëGÞÅH_¡@"t¸‘™SÍÈÖ;[)LaŒ8M4º¡2Ýè`¶üœñ1èl<#½+Ïúï^ˆ9à@CБÞtn16;Iª¼´^•¾MHzÊ›0!–1ü -¸CÏ€Ã(ªáé“\º”™!³;KŒI¢Vàƒ3àÀj•µ¹£p..s+3ÁTKœ®y lÆõ›SX…h+DCÃÛØÃÆR7¨…K+[ /›ð„™0|€¾ðE Z°8ðƒôX‡tíÝR–GÜøÿ<³$ê  iðc,Ží«a,ãIðd~¥˜w¡—ôº‹4s£&e(CÊH„>”x ÃzpÆ(®Žæe«ƒZÖ‚K"ñMXä[PFØù‘ŠW€+ 8Áºä` €˜3jß K•KBüÈÃ\ û¢LÀíþ¼PïiTHÔAF!ôÙù{¹ å”7”ñq d¡Ju‚ €x"yñ% ŠÍÙ}Ãât+àTO¬@yðÁÌ €T°Â(€R°öa ú¯ÐlZÇlâ:¢Y p€ƒ7t 9 oæ¦1l¾¿`¨ˆCÇc[®Ÿ#tÿÜ׌ÿ° =ìåÁ Z>б7XäÁ ötö©åÔó ÖE? €)4œ€_€wêÐá” „0 ¯`šÅ á& sç9¯6 î Êðh°wK’gh  —[×7!ôÀ,}¢Â%{á ‹óTËõoÙBF—|€~^â ƒn rñ']Cvz¨7 :  üÐ ÔU^° ' Ú: B A Õà ¸gÁgK´Ð¨5n¡ Y°p’ S讆4'Ô§[Að,Ï‚‚¡±ÔANu8UQ…„PÚà §ë€~”@Úà Õ%ãZ‹ÿ(ØÂ)Þõ¯Ðç8 ¹  ¯ðà _ Kð¼÷ ÒÉÇ ¬À ±`Z^ˆ ã1Ú  Í€K€l ‚ÐtL0kƒç"&^baH‡ƒ|ø8åHâ§ Õ@y è'ˆ0 QS Õ€$5Vî§ Tr ÷H  àÕ°ˆ‚0ð Ê ßðZ0…ðTpœàp`Á'yu}Ðît í ü 6$ Yà¡Ð2!lØQÅ|ø Kq\#qhMF|¨h³”-à êP £°ï ÃðÊppÆæ@Ê0 ˆÿÐ…` °FW “GZ0[ð '€Õ%îGp° ¾ð¬¸…0 ·Àý` ' ¯€IÍ =© + l   $`l,§ ž 9Á‹KðùG@{e}É0ñ³´\L†$™h!I±hP ‰ ÒPðOòh¯|`t-ð  ¹piðra° iP ´06'pvÚ€Py”à$ïX ¨Ö&2P•¶  ýÀ  …[é z€}°ÅHh’p«àqÚp jˆ<¾hCTŒÂ( )’oòâLà^‰æ‡™!9 ê ÕpHÈÿ |œ  Zð ¯ zÀ‰ÁWžÚðžX3a=m°àqõˆN±¢ n€šÕ –|šu ÐI€ýàÎà Á§z íÀEP ÅÐ ´° ÚÀV â€pP k©3o ‚\t‘R@SÖ—oÝSÿõ„ppŒ•¹(Y K†{΀žèi1) 4¸ Àp\ÿà¡Ê` )÷ç2ÀPòÀÚpVèÔ Vº Y€w¹MÊ    qP öà œ üP¦h\° ÀOZ’ðx‡) 3aX‘šÔ hŒ$ø|ƒeGzil2Z¨M6 h±°˜¯ÿpP*:ê Wp$ ê Nbü  ZŠIV ž žPHè i¢RãeàP©˜dc`• ýЪPvW Zù Ͱl€Z¸À›mà µ( º ½"\(%ê8'ÊA #Ñ7£…:£LÆÊ°˜À׫° uð >ª±P å°“å$Ê€¡Ò  ]e–*jú bú¹àPÏð‰Æ†Iaj«ÚP ý0[àºMÿàap¸Ðp  ( ,щH ¹H'Á õu‹¡`.ÖÇ/&U„A2ÉdÏdW\tjT°­Ê€ÃoxG 7y ÿ§ð @ Ô…³FÇî ßÅ ¯À–ªp ú`^¨š SÆy곯.P«Âà ` ¶ ·0ÂPv˜$ Gà«l Å  peÆêqÞ 0'žãÀð/&—{GSƈÀàH%+“U]˜K6 ƒY,K¹°© — $P ˆÐ”“ðYR0`*†'€a¬ –Z ‰K‹¯H › M úÐ ‘™ ýP p·*Ð Ž€·`Û$ <@8±g[ ™`^êÈÜèÊJ'±ÏôE̲'˜O1xðæ£ûL\$¸LÆz˜îÀŠ: zÿpª ¥€  Ð nЯ `h ˜Fy/°üà Ô…-是Kº é ÒPµÂ zð à‹ðÞ«Š™D”l  Å ›P ¤|Ü „ÐIÇ‹¼"0·ÿ¼Ñ ßcSs{½4t½ÇåH͸PL¦ 4Û 8E¸ç" úP ž«£0 ŸG¾û ÛaBWœ€@y(N Öp Æ™ (m±ð w`•.€µ À| m°©˜ö!õMë(€ °y ›ÄIž$\LÐ]ä8¡pså@ô€<ÿ Ìé3ƒ1xL±° Þ@Ž´d”7 ê ÜÿÆyÏ Vn 6rÀ›ÝPF™Ð é€H€w(0 Ò/Zð`  óÀ›2° APŸÐZ{ŵz[™© JÌ ©'žà»=Y Ïš,`‡ÈÆ(r—AS¨ûƒ ¿šRÇõ/Ž'‰öÇ8Y°HÃ@ ÚàÇà šÚÔ5øNwÁ¯½ p™€w®ð”·ÐV8"1€É' à + ›µªµ¶Ð¹ð ™à¹Î &—&TPtzP¡ÝPœ /à å`do;.AŒ""!Çæ" B©± JüÁ˵#QQ.xt„`ÒGðtˆ© jçôÎcÿåÎà «p …pº z@š[°©Ë›ÐlXÒ€n/¢pÓÑP«P] q 1@–›:J¥AΠ†  ÆŒ„i; @ ýa}à !Ç]! ¬l]J,ƒ1‹ô†¨"° î,­N€„°jÇÈŽì¡ ¡Ð¯^NjGxç ç Pjs׿  iE pP]« ðº0ÑPh°qâ@ аlé»+P OyÀ1^’ 0•@Œ>ð!€!ôÐ\à>J¬GpÑ¥!¶Î1A¢'êP " w~¸Gˆj·’zÐ~nÿÐ pν 4 Ne9 <Ðp™ÌÏÀN»˜ ¤`™‘Éòmê Úýð *P"е- Î0¥Ap ÎÀm ¬  ê ^"1r ùá /Ý3ô¯a$"Àp/ÌË=S…!Ð 9 7  1½á%ë°ÅôNn áºòЮø°Ó)°Ú ;¹G Ñp&®ÝàNA°’ü`âÐrhÇ¢°~ÀÏ PªÀ ¾±žû®[ Êw× r`.+À(ð[p:Gá  ùÑ iýÛÝÃí\p“@"€ÌÉl»Ó9ž  ÚÿPƯ§&5*ßí îø  áº:M+@ޝGº€"œ`Ê€¾=¹Î@ €Ð    a `Ê öP¾€Â[@é«à u ‰°@ωþ>Ðç¶#]atžá—ƒç € ¼·DŒå€Ì+ºíÔá1¿‘[b€ÀÒSè ^p \F\@`ï„<ÓAâ"Ð @žð>`ôr!A€· ¤°ï°ð,q&Ü`fÐÖΣ"&ùA ð@±ZñqNS•pOx®ÔŽÌÉŽáÛ¾í:çíK`*L²ò"TFž“@Eÿ4ß¾ÁœÃN-Å_LíÚž‹9AÛãaÞÐçd Pfàã±ðÂouõuÀç,/½@ñO`ñgq®DÑñ?}®5À Û.‡ÓéÄÓ-Áòn?&rRFyÖ=€Nò>p‚àSF`ÓR÷p î€.KîAoL ñ~Ðø›|ó,Þpøæp<Œâ½`œŸõæ‘\_Dñïñ&Hö50+*‡¬ßú“`*§sÎdóq8°q§qÔ)Bâ"27¿‹võPab•pøk–ùðï›!E"ù” —ùF²ùÿ½àŠÿúrî½@ú¥?öØ€ ?$ö¬¯ÏÒ(y?2$´¿U2Áœ¥ï uÐ¹ï €poq¿ïñÄœ¹:u.ÐCHσÀO*ËGoÒÄ'~,°옔‰“¼ äÆÍÏ)ùL&¬d@¥Ê^^)Róåœ9½ž\Àya=!jè”Øq¢)^¼xð@Tʱc;œ>}jCêÔ{Uï±aDëV­°ÌøräÈ&¡Ì†ÊÆê½c•*=Xð ‰°š{â…iPœi‰µw¢· Ÿá•Z* YÁÌšOëÜ™O¶Ë~ö`´W/¦Lw`EÍÆÆÖ­[㪠ñÿ16€EU‚UÇ×î±ôL–.̶—·pïæ8ÉGa¼M¼@ØœE#: çG‘~*¹<nã`Á"‡˜¬ˆæœ›8'úŽLØÛ…¤&‡6ê¥|0¬S§² @¼Ç6m³7ÝvkÁ‡P€ N¸+²è.·<À©o ¢Î‚µÓð£û¸. £“΢^‚yI1Šàz# ¦¨A²—¼°@¥ö€Šé§œ.hÉ(¥ö#Ò¿Õ0Éû`²I&msË›¯vófKó0G ý ®ÑP,¨ŽDQ>?h¡…É1”OD•Ê;†.í.#.x©=òÓ1§Ì|RJ FëÅC"ÿ¹àÂ@«ZSrGGP2'‡³¬ŽZ𥅠•Â-OU²(¤.a$ÌÒÄD“Ë6 ó 4ûð°’íä›®ºò"ê¨/êìä…—óò1ÊázO3EÊ/?Wg]‹4Ú$'Ì-‚,Í´J¤¶E .‹¸µ» ë0£\Ti1 ®0눱wÓ]5ÌZiqÎèä{¡ºnîÄSO£æÆúŒ%t´,-@”ß^•öQi%uòÉë¬-èÒSç£FoE5¡cqE-7ä]_ðƒÜë0§:4 °h]”ó¥×({çÌX_~õÜÓ¨aŸ@ʤÍÎ2a.I‰&ZɈ—†uBuCnó.ÿ¸‰¡ê<Éj­càæ…²ÖÚ 1W7d±ÍÙÃ^NÌiùã‡g~x{žg|ëªÍà|%¸Úô¦Ý})ýôÔ ²uE&j ^ŽÔ5FkaiSE7TQ¥rÈ›oàølTá_þ`Àþ©"ËkÀó w9?äË^3ö´÷îñBöA]/Vòÿñ)'AzÃÒ§>£Î}©Z‰×ÂEµúÙ¯ÃÓßþø@ÔІ7LBu¨ÃÖP€ü[Þó¸Öµ¯qÃeµê+xA“ %4 6¤™(šoaIRß‘¥4÷ ]*ñZYh?¢C†3üŸ w¸Æ$ŒÁo„ãÙ˜„ÚPìÁ:fßM2  /°!¤m=‘%U¬A"¹Že4Òaš‚‘Œ­% ΋_ c÷¼ü™Q†Tcã8%”Ò”§D%*G9†9Þð%˜ÀÂfêü‘‚ˆ:J! iD&²îzD#—A4ÖLå<ÇŠ“Žtõ4©0‘Ö–öS|^M<aŽ˜Þ¡ þSÁ5qªÓmþÔ¬gEkDO9JLài/‘­ŠÔ\€ª=óIÕªž§ƒYÕ*Õ¼hô¦cUB7{šÆ6Ö±ÿ…ìcÑêSo®õ.¸Re0ÒxÒµ©ûê•I­HL¾ö5âìVÍàÕ†õ°¤,%7yYÚÖÖ¶ì*[J9~C¿5@g™Ï‘‚OöÄÆÎ÷(טö¯r§9LÐ4†U”°UlCo»]îÚ6· íæ)]€ß¾ múì"Ãä.Œ´Ædés¡+ÝØT”:}hwõ»ßÈ~·²cø2Fâz¢ëeo{—û¨^Áת¨…n"bêÚkæ4±eÕ.5¼aÚš•›ã$†×Ü1bwhÕÀ^¯Ikà^óÂ?­óës;±â4Ùe(‡u¼cÜö”›Ÿ°Â H\â'Á¾”ÿâh‰Æ‹ë”§ƒOVVg|رÊ6Ã5D–µ¼å-ëx¶ŒÍ²—ú nØÄÀ|DŠkä×ñÍQˆ"”['šÀš@µò•#Ëå0kx±ZÎÀâ0‹ÚB Q„!Ÿø ohdšÙ¼»j\ã{p•î¦o·äú=«V Ð<¼dýæwsìX=þ ¨‰Oüˆ24¾ j—¢Øn€ßªÃíq‡{ÿb·|á&÷îOwkÊQŽ^œ]×ml»æ»ñŸøA,ÿz :þñw÷Üaî“ÂûÞ¯ü÷+Æ!oß ŸÃ ¯’¾¿>þ¦eW9Ø’>œ¬lã…™?èç?_? ©9X¯Ø“½õÊ>šà>îãƒäƒïƒ@ïˤòã> x¾…j?gë0žú;ùs£9b£â#¾$øÿé;­æë¿ç[Œ§ ‡ê›†œ†Ç ‡Š[Œ³ä0ðAŒ@2ï˃<(‚x¾A˦²Ú°‡ú°‰ú@¬#*Û!¸£h°…,T…†õ+¼8è¿‚1„ ð„FX¯Æ£Áÿ´Ápp·ŒóÁ8t…9¤CÄ€;,Âgx1ˆMÐDvX$|¾ª0Rʳ‘Ã-pòÀršÂ),A,ì‡Iì[ØBšÓÓ9=P†NT!ø"ƒ3DÃØ[ÃL37|CO˜:ü…4Hy˜…Yˆ€ˆ‡xøƒmðE@Ô„KƒgÈ>0\°;ø;¥›B(T2=®ë@Šâ!W" þɆ,ìàFnì‡Â:ÁLD…q$GTÄP†E!PÇ ¸|h„x4Ài6lÃìÃ|yˆ€(øƒÜÅZˆ€[œZ|†6hï[À…ä¾HÆXF+¬©PAÿ*CdÆ;¢¦,´…IìÆd€J”¹6ŠƒLÔ†rDPÉ•\ÉpI•<"xÇxä5T€z¬Á2@0|ć?ˆ‡‚$ðAWˆ¥¢Œ%0`È…t@ø–;+¤¡sD¨ÄFÂÂBޤÄmÉœD[ØŸ™ƒ.4I”\Éd4ËdÃS8…PE"81š¬¾2 Ç›´Ç\|p0 Ê¢¤Ã¿üKL4À;8L;@Â)Ä)šÛ¿ý£F:ò¡²¯7b¾õË9”,G–LL;Ã1üµ\Ë´$"ˆ‡?’ǹ,ƒ›´KUDÀ¼„ÇFÌÙÌ8üÁÁt@0x!@L§Ãÿ|Ì’ôB“ÔÄâ4ΓÔÌääÌsÄ•E— MÑ„ÎÐ$‚ €¸L†³SMº€_°ËÆK3|¤IÚ$O:´M0Ì;¼CWø†ZøÞdItôÄN,ºÌSLPÕëãSy Ò+¸B•ÕBÌYEÔ)ÕMy †H ÓéœTNÖ`V"M¨TaÝd€€Pý£dpVR}ƒSÅSÖTÕU}¼ ½ÓƃÕYåÖnÔ[½U$"ØUM¥ÔsÝÔaU×u=ˆ‡„Ug Òh•V¼ÉjÅðäÓ,rXr¸Co%XBWD}€„Ô(€Z(WN…Ôu•Øÿ‰ÍQ †Z @È6 1xVz ÒG³>}%YVE°|€­Ö-XoMØ—=Ø—•Ù™}€Q@"ȇ¥X`õÔžõYOe؆`Lƒ"DÈxüؤ Ùs‹=’ÕW~ÙÆÃ„A Z…mÙn¥Ù@ج%®õÚ%4ÀEvø€€C8„ýYO…†¶…d€†*8ƒm`¼„&h‚$ÍÐ7HÚ¾µS¦•Z’ ø[Õ$‡©¥ÚAÙ«åÖ¬uÜ™%"”\ÉÝØÊ­\=|†4x†?äÃ\ô\P¼õ,È[½5ÝFh‚¾UÝd0Ý\’]ZÃEÜÄ}YÆmÜÇuÜÈÜÝÕCÿ ðÝß^¥€:Ðß=Ýx<‡s@]¼eÞæe^1€ÞèÞÕÕÛ&pÝÁÅ^ìýÛØÄõ^bßð_bÀÝò}\ÝÝÝ<À\Ì ÞöõÝmX^çm^kÀùm^éÅßüÞÒ­ÞgÍÞÿØ`î%ïýÞñ _óU`™õÚÉ]ßîÝöm^|؆K çÞ&€(xÐõ`ÐýXýÍßöÍÐßíÛÿ‚€Uá¦ZÖ¤ÝNØ®UàT áTðÚ®u`^_÷½`¼mð]û],ð‡þàÕ]Ýà¥Éö^ÿÅÞîÞvaªE`ð­á+Æâ,Öâ¾aÎCŽ`n|HÞ n^ÿn, ÝFáÿí[æýÝ^üuâ'^aïeÍ<bØâ>öc?îb¯0æá Æâ&]p#…6Î^#Ndûmâ:Ná;NÜî¤Ð_„N„?Îâå@¾â@&=¤ÅTVåßÅ[àm<@dùEc5>cö‡[Æå"väÐdæMZHVaÈdMÞdOþdP6eÀâdffY\ßTv_ùÅ"VdÐe^[v]Þe(àöf!ö`HFá€æLÞd 5æcþãdÆâ\pç\…x…w¦gyžEUNåŒÍ`çýá&ÀƒÆGVãvd€æfoFè6bqg€-ç¦Pÿx€t6æ?¦ç‹Î…Æh{æèŽÆç4a1ø`|à€mðg€6âÑ]€Fé†â„†é\–陯er~èî”艦hOöcŒöiŸîè Žça#ö¸P`é€öàsð‡W^ê¨Æn†b°jØà 怭æê®Æå«¾j<þ…œ–hN®h-þé´¾h¡öh¢Ö_¾Ô=ƒjféV^¥–ê¨ë½ë¬öë¿þk¾¶jïíä±&ëVgµVl fk{Æçþ`O8Mé]…Hh‚9ð‡¼Æƒ™lÂöämѶÀ†ÁökOÞäœ.fž^ì×^ky.€Ù¦íÚ–‡Û¾mȶfÿO0‚Kðí¤väsè…F éâ¾å«Nít„ÑîcökÁ.gå>çÖödØ®çxVëçZ¼mÛÆíï–‡ÎõÜß¾„3ŒòöíŽPèP0nãlÕžnÄ„¬æë¬mæžnå¶në®íïoÏ%ðßn¸eôFïH8Cß.ð­Îjæ¦pÑVnªEí îä §pþîoÅþïÙðçGÏÅßö„'8WðxDï‡qÇítN\Ô¶†·gnÿðrï&qÜ6ñ¿oh‚ófñßÖPx-0EöHÞ*?‡‡ð¯påöä¾ÎêçñOçŸÐÿ27ó Gó!Çm‚,ò\ Æà€%_pðq`-@€…Jè…nh„7‚­Vc5†q7tOÖqû¶j¿ó0óêÆh4—ô WóÛfó"U$°[9ÿíHÈæFàåFPªJð=¯„ÑI†@ðä5ôW/mÓ†wtXoô(óI×õG^ïõ!¿tg‡6`†:p%÷ô÷E`öfw,àÂ}ƒnP•hV·rY×vC¯u[×rÈpç]Ÿt_7wö[öˆfà#¨`9÷Ãy÷ÃKˆ„mpö|ÏwhO: ôäÕvFçö0t,ðvkY`(@ÿr¯ís—øs/‚Š·x‚dsvˆ€< 2¨1 ÷yzw0ù“Gy“§؃=pv0€Nè@x‚3ô+ßë*uGGx?xS@†…G5(‚‡§í‰Wú¥ïu‹¿xŒŸ…ØxG`fP‡ºù¬Ÿ÷•Ÿlߎ„Hôf_ù”_y8‡nðq‡N¸Oh*·r+/xƒt+÷y,@ZPƒ*Xxhhø£Ÿm¦ü›uú§'ÈZ˜… i¤U@SéÅûÇfÙmÚ §Í~*´äˆÛ¤jתÙÑC×\ƒB¬áŠ;+е^ /žqF£¾æì•–{l³‘Ðà@9ë,´µ{—B-˜£I¦›…µÍ]Úf[ 1%³K.âJ\(S®aŠ»ÔcOî† l<:šy¬±ÛüHƒÿ¾ûªì¬Â-[[ž-´ FÁšBÄrË«`J.×4L 9L ¦¯R^(Ëõ¸’“£ðBÁ,H¯³ `´2ÖÛä¼5žÜøBA³ÌzºõªÐÈX1i_Ó§Ð{\£)²@B=`Ðø«Ó7ª4RÇCõ¾Ùe3Ùdk7H´Q3gûžç¬Ai7qÛD¿í¨ÐÔÒ…+Èäít`UPªÉ*?8øØn¬×`[ç¸ë¹!s´¹¨}M›77ºdñÜQML«‘·É'…eÊ—¼/U{°Þ¬ë-Wí‹Þºëùåp¢äÔ’ŠÏĤ²ÜPÀëXHneÊË?¿ÿòcB@¶ 9=õÕgÛìžáˆ6œkþËSsd>$ÞJ[ 0‹ J†(EˆßèLÁ{ØÃl2:ýB˜ w¸Ã.¾‘‹wÉÏ(ÊFÁHŽ÷Û"˜' Lf2Ñ[%6?™5RÆÀ mXà U–MM ñ¸F!G1 yQ2°G ®áŠ LàéÅå±DaÖâ™fëO †= cô£¦âA&lê¯*ã\%ëÚìèŒ#°Ô0Šo< ¹‡|ª`ŠxÌ»ØÅä‘ÏQ¤‚÷LÇI¿Q€(€Ñ&ÆØ\1 ”kPÔü)Øq±jjò¹(DÅRø‚¾Òp(N¤ž0x R^ Ä$öÐøÃ(îð€o¨3Þ:+ÿ:ÓA‚untj84h€}Ì 鈂¡Tj²ƒ{ j(pª¢qªb0fÀÑX„Õ¤£ÀÒQ$1‰ýœÅ5ЀÝyë‡Té,ò#™ žŒ¨;ȉº’ræ^ƒ*,À¦ñà|ì*#`…Ä&e%nÓ¸EyTÖ£¹èA Ôióž²T©LF(\!8gœ4䪮ÏxåbË ðæ=¯y¯\1F±Ì€D Ö%†Ku½ëšd-”¨K$Úã¹E Ѓ@W2RáL`ccñ0&¡â×€!±dÀ;ª°WdE ½N/TíÿËŽ<8‚̈)¨"•?”“öEá$Á¨O|Fa=ðcY“†<˜Àd0±‰K°Ãè;V¸”4L@1dàCâÇ×Û~½,ŽRÏ»¢Äd0eÈCÊTùÅ'!5á|.§òÀŒQâÕÎÀÄd Ö¸ÁŒD€æ5 M!™* hìÀÀQf@c`@ñ°J€ªìaËœw•ZfGø28ÂmМ²“†Cx¯å•t6ÑLM@t³H žQ;3)wæ4£óÄF@¤4žBÐ5Ü‹ÏàƒœÑi<*Fø5´MpJúFÀŒHaÿ_(ÅbÁšÅ=DÕ¼bü©™£¸d€Õ2š`£ò`â ð¡ Æ¨³ ÖÌbd˜Xí@;–<Ú¹g 1ˆ ¬@Š68å)ìØLŒð‡Jí›áVà¶²;m…ghâ ºÐÁOXíÇ–¶¡Œ¢{ÓÌ R0 w¦w—™4ÈR`¨ÎÀ<ÐñØ÷,†¢UaQiÔIÑp©“"V—z¨®`GÌÌ Eþ` ¡üàž<õÊÓ^í–¯E’ËbkñÜZ,¼Äƈ€1ò0 Yî]–ÏHÃ6ª0ôÁÐA=íÀ÷v Ñ€Øò¢·±.Ì@ÿ…Å“CÆ™¹øÇ›íjÿ<Ë«[ðºýí²ˆ;'¸`cT ȃÜ'ø”ƒ¿ýí‹$ }÷e_<ðƒ¿xY0åGøÙÑmÐ3_ô{=€ËõûÈ2 Ƹµ1 \‹G+^rÐÀ=ø‹NèVƒñb÷ëî/ýõS‰>^l7ÄÐùl>ÐÍ}ËM¡þr-Q8¬ €A³mÅïß÷ßÐÕŸÙ¬ÑýCA›éD \yQÚºzÕŸn ‘}°å_ô•Þâ‘›Á ßâ8!`îE$!ƒá ÕÆCìÚq  ÝýžÒ™ 6Ò€í© îA›ÜÈB õÿ•¯žlÅ 6¡þPÀÀUÛ¯í`žà¤€‹ K !áU¡äTA»ÕÂi  ‡>¡®áž—JßÂí,DÕîzá…ô È oh`¡ ú(F¹–"0š©âÎÇ÷ÝOLS¥˜‰üÔÂÌÝ¢4j ‚¢WiX&)]€”b CfâbRЀ^)Ï,ø†ªrüü`Ò4v"1Þÿž5B ý"0ú+F $bW1%žžW-ø…ÆÃ4¡Y¾£ÆãàÍ#íUÅ VÁ=~£bP±€7zãà‰ŽðFª`<ôMË™È1"¤*dFz•š‚+ˉȂ+ã^šEašN4U‰E a< ƒ¯ÛE#In Ižd}üÍXœ_ž*R$PÞÞEÝEbd-œ¢éDeMöŘ ^þeá åIRÊÔ˜¤;j¥Sz#(FeT’¡Z¶¥ VÌÐécB[LFÛ!Üe´ÝÞ]î%_â%5‚%Tº LÆ#„ fF^ä‚6'Öz°¬n;-c赑{ 渒ïmÖ¼¶Ê67ô›Ù¿þ-¥¯¦Ö7Méž­' ½A¶VÙyk(.w {šçåm«wòUH»)ìõz–ÐÒMª kH†9ÖŒw¸ÿ]e·ëdU}eí ´6×2y¶ºl;^æ©[×:vM¯É{é¬Ý[˜XݺXölõ_^ç6Ÿw¾Ý®ý G]Åým£k#-ûœü¬­h|Ù]`†YíkœÀ_³õ“6œzÆPªd],Ä¢Èe{Yô¯¿ÓönþmŸù?ÑRwUÀt´_[wWS%£Z¶‹Lþc_³ôoÿ ©[•Ôú…,£¥9ÊǵÕËžÆKé1Õ¾ïÐý/fç~b@VÀ¥(«µv^5§}Ý?Ú[ëa4ÓkC„;óUÿñW3Þ³~Êq²w?#í kÞHo¦"Æ;Ô—:ªvÛJ8¹œFW–ößÒæ¹Õæ˜?¾ßc¶þ{ÙìCnev>·²ÐàͰêËK¢¸o¨Æ;Ùô¿šõ?Gô­¡»§vs6äŠîŇz~–U¼¤ìOP{Ùþ÷7úOôjWeÐì^£dâ¼M´¹¹Ž°=ßc®ÆoÉÿ„Ýéªç6̶edd¼[cÿWìbC6ë”Ýìý;vþ‡ìß ú·ó—!çuF?¨ã›¯õrž×YV1 k¤dc—zvþçÙômˆŸèv:Sz#íûtWUÔTÚ™H²Û kí³Õ¶±KVæÜælûMÊåŸ_:+r«ÅmYoº×²¶H0a fïQÿGܲsóqòëcØ-®·µ€ƒ«Cêež›Ûô˜öÙ[ýŽT1+®Î§ˆçq¾­~j6-Z?ÿÑõmv}µÄv.ªõN±…׳݄ÖÒ×rÝkëªÐ[U—ú˜”²üž¡}[ý_•ý‘yÞcúÞx`Ì¡™[³Ö±îäðØ·sw­>Žÿ­S¨GO̳ìJÝ[˜n5Šê/oê´—¹•2Æ1¿õ´ØÙñ>¯— E“Qï/Jýo;/§[ê¥Ù$h}jÝ\XÖímÌÇ­Öío¦ïN·ßúÂÁ³­Ôë]˜Õ¾ë6RwûOÑöÛ]ù뾫ëùúÙE…Ïs²Ú×N•6¯sÞý»™ïÙ³ü"&Ö†dÛ—ˆ.nUïmy–ú¬kô¿H÷±Û¶ÿ!8B_»/±o½‹üî?ñ¢ð÷uì{ÙcŽÑai;77hhN¯Í®»>Íoç©»ë&;ƒÿÉØþö†´‚é¯h-õ(÷~Žß¢ÿSþ ¥Øæa}gÉÈÁ£ªÐó{žë±Ãî¦ÏNÖíÙoèëc7»þ[·ë­Ö[[ñŸ}vÙ¹Ïć45¿§³Ô£ô¿¹ý„¸eû¥^î?óÿ/ÿ917î6€6íØ×ÝݯçúCÒ ³¬µõ+­õ¸÷kÁ™çvíÛ÷)w9/ë%ø×]O¾æúvVY‰½Í,o¿Ðg±Û½‹΋ˆl;ús†ë»Ôdå߬V=¾öÿ4ˆ„ÎÑ‘ú(eÇÓ$>’‹“õŠüŠ]E´Vá´¶½Œ‘àïIŒÜ£W^Å©»jÆ{,2 ¡À¸ƒßpw±Žöµk» a—87§YhhiÜËÐtqµß¤ÿEµ¿T=g4WÓ.ci©¯qÜÆÛì÷5Žr>ÜÇèKìAÍ®H}e3/ëK±ê6ä>æ°Ã+-h,ÓéNÅgO©c3(ZYŠÓé¶@/ aÜvÖÆ}s¿9¿Rrl¬XÌ|p× äuñý ßUóº}”n­­78š›MÆÂ\͆6mk=ÛÛô”QÉ È@IBqR©%ÚêÝE·WÔpq¬ª·eWh¤í/q-µ¶ú±·ôõ7³ýÕ.ÖÛo\éÕ¬h³&¶Ë™»úé°ïêã¬ÞrÙu7äãÙ°¾¶hækvÒÆ¶¿s™ìÝWç¬:—YÃëC#$æÕuVº¢Ö±»Øṕþ¾£?œsT’„€Ô§PÂ' R¾ÆöÿÒÖúµõi½4×——]@ZÀa•ŸÜ¥Îúvÿÿþ·é®_ ¹úÉÕ‹AÙ»$´îHÒ]ïþÒï0:“mèõujñì~ê…¢€Ó¼Ÿ‡î/ýâzUYÌ˯–[s®°czž™{^çdµÔ?eÕ6·¹¿g¯í—élTùiÊFf{íÿ ¶óD áè–λy´U[lqÍÊmxͪÆm{›±­ s¬m³ô³ú7ú/Ü[c¨Œ~™öœvϪf¢Ðunö¶w[éû½'})³ÕÿB¹6[››•n?üÚË;·z•5Å®ù·0¹Õ žßÑ­¯›ž0î³#êÆ}!זּÇd<¶­›ZÊ马Û]nÙïÙûÿMMÇãí 8 õû ¥Ñú•yÝW!¯¨‹(5±®²Ákší¯ÞÆ:·z{=&{ŸüøU_«õ×Õ—•K}OM˜åÅÕ¹¬üâ×»ô§Ó©¿¢¯ý«é~Uè™ùõÔr±~¬æ?|µ¶0ˆ Zæ³ucý©Õ:®CúŸ¥wÕüº®·iôÒ/yoéú=»¬ôظ;´+ƒZ×ìzlž£s:7Úë¶àÖ ¯±¥Á¡ÖUì6>ç[¯éÛK…ÇmŒ·ú¬sÙ’ç‚ÊØÙüϤýî¯g³ôþÿô5®«#¨u±?ýWê0€È÷NŒýïrçqz†Eî}”tLǸê:¦™$êÿÑûw%î 4GÚ4íµýRŠœl®§ïc]Yl¿p ôëeÄPÇ»wóÖè¥Ý}\³//3 6¦WkkµáÂÒßÒßÑ×½¾ÿM›*«ùµÇeç^ʇھ¯çm°‘60#lz>ïcœ·iêýn¼j±1¾­õ ñ^Ãg¦Hs¯ªçzŸôê{Ñ9 GÚ£ŒYßìj¯n¦¦3öEk³ö‚KÈö7s}/nôl_¬c®e¶‡àÓ‡özݵÕÝêïu–â°³ak?5¾¢Ñ§ë?Öš(ªŠ~¬åµ• ¼V% q®1Ûýoz¥Õþ³fåÕ•gÕÜh--fæíw¨íYS?œrdc‚$Hp‚?¬£îF¤9­ÈÎËúÏÔñîš2 ØL“[ë(Ùû»^¹OIýG¯¶°ÏSÖÈ’ÃÝ >gù Zø–õ*zõø¹8ÖbåeUuT×aÚâ뇭E§·ÛúþÍìÿH¯t N•…ž:¶tý±ö[öŸnßÑ–1Í·/ªíwè1Ùcm«þ¹_ñ_¤SÊ`ÄX„uÿÓîqý(vÄ ±Ä~nØOú¶çϧ·OSnßfýŸÊú ç–8ØyºÝúgô›fߣ>ÑlîëWúOûu©›é“¦æ?¶ÙwÝ»Ózù%9Üß ­þsÿýcN·ý]¿ï¥ÏÓ3¯‰Ô»UcÛ¿o”Çà¾jIW–Ý7軳ô©ÝoòúHnõgQgÌ»û—ÍÉ&Ï¢ø¿Gdû¼õw÷'=üù‰_8$”w\_¤Lw™üP,ô£¼ë?ëôWΩ)J#õ}×­êÜvafõ"ËMw]é»)´Çë é8ÖìªÛû÷eS³ý×<¦ñK²ÃÆ9`µ¥=ØMy~;=Þ§±Ž²ÿåú^šò´•Ÿòpíý_–ÿéñœYž]üwÿ ô¸ÿÿÙ8BIMÿâ XICC_PROFILE HLinomntrRGB XYZ Î 1acspMSFTIEC sRGBöÖÓ-HP cprtP3desc„lwtptðbkptrXYZgXYZ,bXYZ@dmndTpdmddĈvuedL†viewÔ$lumiømeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ óQÌXYZ XYZ o¢8õXYZ b™·…ÚXYZ $ „¶ÏdescIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view¤þ_.ÏíÌ \žXYZ L VPWçmeassig CRT curv #(-27;@EJOTY^chmrw|†‹•šŸ¤©®²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿÿþ&File written by Adobe Photoshop¨ 5.0ÿîAdobedÿÛ„         ÿÀl°ÿÝÿÄ¢  s!1AQa"q2‘¡±B#ÁRÑá3bð$r‚ñ%C4S’¢²csÂ5D'“£³6TdtÃÒâ&ƒ „”EF¤´VÓU(òãóÄÔäôeu…•¥µÅÕåõfv†–¦¶ÆÖæö7GWgw‡—§·Ç×ç÷8HXhxˆ˜¨¸ÈØèø)9IYiy‰™©¹ÉÙéù*:JZjzŠšªºÊÚêúm!1AQa"q‘2¡±ðÁÑá#BRbrñ3$4C‚’S%¢c²ÂsÒ5âDƒT“ &6E'dtU7ò£³Ã()Óã󄔤´ÄÔäôeu…•¥µÅÕåõFVfv†–¦¶ÆÖæöGWgw‡—§·Ç×ç÷8HXhxˆ˜¨¸ÈØèø9IYiy‰™©¹ÉÙéù*:JZjzŠšªºÊÚêúÿÚ ?õF*ÓtÈ”¹>ÈÀ„1ÔôðH7 Phwî0ØM4um0 ›”Ûß E¯Òú]+õ¨öë¾6ŠïÒšu+õ˜éãÈckMOO'k˜ÿà†6´ùÛó3ÊdÕ<ѨÝiút·PI34rGĆ × CÒÿ.¼õòméEv¯¤E:Pz¡–xÉnüIŸN>GÒ.üÝcçMRëÌ0Bð#XÁk¬ªy¥$‘ç©Å/)‘66H6óaî<ç ‹T¤I¥Ý ý1„hösš˜ƒI$l9ÅÂ4ØL°Ž Äì^ïm}m}W–®$¶¸E–‚ ¸¨Üm˜öÍ]N]]°¡ÿÐõF*ÓtÈ”¹: yJùëʾ_•†µõ—¸•œ †Yrý­¶jà‰m!,×¼í£yŠÖæ_/‰âŠÚÙÖid‡Ða#ƒ€Ú´ŽO.Ôo5]"ËG’MWR»›TŠI .8q1ÉÀ³V¹9FÎÔˆ‘Ö×Ûë×2H#–çVŒŽ§ë€|?»9T„¿£òÿ6ÒI•Ÿ™- *O6¸ïê-Ül?äÞ5>ø}¿­—‡µðÉÛþ_ê³O¯™¯Õ$aü¤Ðöë’œÀÓDy§-ùc©D‡˜ï˜ø·˜'U.àäˆ@¨#ëðîšõËÐH€×ñÁù²?…Ÿõ¯¡yÉA5e þÓ[‚GÈòÇóµÑ?–zùÏ2³2ù¢hùt_HP¿çÿ£ö±:h÷¯OËÏ<«y®Sãû‘×è8ÿ(æý¬.;ÐÚŸåÿžìôù=OAypx¨ð®]‡V' 8Z§Œ©v‡,÷ú~ž«·éK…¤>”9P±<Ãqÿ…ÌÃA©2“˺¸`¦õºî¯Óî88‚`±¾–R΄±Bcy×âVâFÑøä7ïûøñ7ý’_¬ØÜFÈ×÷Ã-¥-ËM!!w¢ÖõÉÂûØd”ôŠø³_ÊmI®|µ%¬’s}>êH%ÉôÜ ¢ÚEGUá'À~ÇŘݬ3pqJúìrHÿÑõF*¤eV,ƒí)¡õ䨟dax½yäë=CÖ×ãšg,á!Š6‘ «ñqxÊä"OFúAgËwv—ã˶’Zج$ÊÒ+GYˆ;ï#†›× ¾¨.Э|·¨ùG“Q¼µŽ{häN2•õ²øK}ŽÝ2R;µƒIàÒü•?ïžûL7÷†F,Y»¶ÍMò$6O-›P“EòŒ‘¿§ye‘O‚AÃa@x–?¯ Dª¯fUaæ=&9âs,dTz©½>œŒÁ"š¢wdÎz)Oüúæ'å{h˜@Ïæ}2Fj\DNüzöß|•—{`̵»j%ˆí°)Ö¾=0,»Óù€‹‡\±z¹– ¤Ñ¿xœ¶ðäŽ^KãÅÞ`ÓxHµÿ])¿û,ÑOÉ‹»Ì¾b°mú’²¼D ¨#á$N]ƒM8ÌÉ®s³ÂRÒ—M¶”sýOŠh‘È,á¡O‹®l·è×JSj?–0ͨÇ&Õ “¨éñPŠþ×L}]ÈAÛÞy¤¥Î©l I1úìAý‘ÇáýxïÜ‚§© ¬Îm/n.‡)/«%v4`x×$=Èzïäæ¡[Í^Õ¤fÇm{y.š†‰Ú·*®Ûªsuý×/‚?±•dæ l¥£&Ç$ÿÿÒõF*¡s:2ž2¯Øoà}²!R¬°W`Ák¶â´Ã{!à3èvz·Õîu üe¨iÁu«¨'~™i½ &›¥YEy§i…¥ø…¡fõÃ^%¾#ü¸Ý­!4ß.yÆËABú=½ÚÛÎþ••Õ¼ÒN=Jr  éíüØ2a•Ü¢¢Æ3 U%¶¿—~iÔg¹¹–ÑtõåÈDñÈŠy’i ÿ åñ ö² Dè>Q×RV‚M)|^­úLª¡GÙ)ö²#|‰õY@×E{­3S¶¹1…m›#Ô'(ÃÅInžùW†&ÛÁ~£k{`Ò#yzÒO²ÜLÑŠ¨Ø1aÿ]`ðó¤¦`tI.àÉ!+ÉU@_X¡©añþ×ÙÉŒgùÇìk3òTM7RÖî౱Ѣ±r]ÙÑP ¥ZFrÜR?æÿ+ù²PÏ ¯£/É—Ú8X¼ÁªKq|@õ!´^[mÈde"FÍsŒ«Óõ0MRç\Ž+:{qÿºQ(<‰ãêo³—D1Þ·Oü¹%¤i»Ô"ÓaôÀ2OÊŽÛ| @Û÷Èžm‰¥¦­l!½ŠMzÒßê¡Ï[¥%_Ý’Q‡Â¿gâÀ¥6¦eÑËbÈßJ㞟û¿YTšq+éìÃퟋìa´S^qÓ4­>ÒÙ¬5KMBI‹,Éi,r²£r"E!I®U‹?ìC)B‘¿•Ú•§›tË8î­¤’ãNžÞáb»ßÕÕÑd†ìúÞ¥¨¨¨‘¯,99[ö¯[m²¦T´ÈHÉ?ÿÓõFBÝÎ"’ ôwáô‘¶@Ùˆ+XÊ÷"Ÿ†,_>júž£a§Ùêw&Ú f™'xÚ0ÜÖFÙFD!*º^öZUÌ“GYEÃrzñDÌÚ†í ¿Ëú«¬Äí¡­±BÅžBd^4øBó}ذ ÐIu95+[é`M[XˆU’þà=ÛÑøõðÀ”çËšV¯«Û¿§«y…¦—3ëút¡+¼Ž '2@y ¤÷ך•ž©è^†Ú3I‹]3\mPÁCZrû8‘Os#\ÝÙÞk:åÄV±4“ÛÝI¡Z3É‹¯5Z ãÆ‡—§$ªc¿W›Õ±P­=1ëÿt(y`'cºŽiŸ­ç[" ü¾º©Æ4–9e—šÕG¥ûÔŽž•g—ÚÌl$ïrãg*®HÍJÕt ˆï4‹k+K—2G%ìv™½Uûur9~ñY_ý–^æÒeI«æ¿<5´¯½:²©*±Çoá°Äâ 2oÉŽyoWó­ÿšô˜ïu{Éa{È}Di[‰^`@4¦W!³;ÿÔõFI<Õ3C¦‹…ë ±¿ü52©³‡4ÃJ¿¶‚R¾œ¨Íñ©"2œ˜`ù×Ívþ\¹¹¾]zýì,๘‰cEóõXP‚W!ä%æÛËv:G£¢_5í±G•ÕSâjÙgáÉj§qæÕÓ5¥S¨Of"EhÞÕ=F,`/(Ôrþw9 = êšêÜš•ÛÞÌ–Z4ƒá'‘ë¸ Ëýjd€bBçÎqé×ÛM¨M¦ˆSÔ·x#i vcËáF_á@ÏH=êˆÕõË^g¾ <ŠB@ Zn[ŽÇ#kÙ«­oO—QÔ®ÒjÛjQÏ“ño¦ ¥*µ ~ÖC€ð€›Ý×½¤vÊá®`–I%~AeTÙplˆ”I5¾±+>«¥ƒÊ]¥·FÜ#FdZxq‘ÿàpÏeØe®ƒù‚grÚÝGñ7²‡3)—™ýßúª¹g jÚŒ‹Ñý q™“pRX ƒQ•²¤¾Kº…€·6¸”ÙÆ>•ßêñÂÞŸ6„ :Ê^¼4>ŠØË‘­KÔ¯ÃO³þVH%o„ö— Ñê²\±"ÎH#g‰To™•©Í¿ÝÛ7Ã’ O7M&‡•¤Eµ+{ä• ÜòE*Ä¢¿¼ôêü¦7ˆ*+`•ÒŽh¯0êþZ¸Õ4¹´­JþöÞ)Së_/§IÕ=wcñŽ\ÿgìæ>( ÄAœ›&×çY×U C"41kÕŒlGÌ:åðiaÓË_‡¨=rÖ´_”m­ÛÍZY*.ƒ¿mò…1‘·ÿÕõEr Iüǯ£ÝGP¤¥C‚„šøddÊ<ØŽƒç{›.®uOKPÑ”/š,ãk{y_ìŽpÈÍÊŠ>+›bð6JØÓÁüï–ïµ M¥Ž†7Ó= G 21d  ¨? ®F;7Õ†= i^W±Óe†K˜–I ÐH_ÓZ|+SNMÞ´Âd‘?³5ÄåîbO(~×}êßG MfÑm!±K•Õàžî@ŒÖkêS'T PÜ¢êßx“ÂPÚ¯—Âjvö¶º¼71LËyñ*U©Èo_±’á)òõ«_u֢Юn™ÝŸ—.>€@ÊœÛÿWð–¡òê½Äð~œ…#·e r­#½Eåö9qOKì7ö±â S¶Ñd–§mEa6Êߺ3II½-Éä[’ú¿²«‡‰xJQÎòÖøÜ#~ðñpÙø˜/6%¹'q ±$2cçS{f–z¡½[e<šÞÝÀ…Ïùq+î«"Çÿåb$3‘É ªùÎÝ-äýe4óÓá{’¡AÿŒi^_ìŸù †‹‚K¥G­_JÏõ†…ç ,²¼¼X¡j®Ü™¹9+aE;ý1ŒvësÏ@•îÄR¬ {Žø7ZDÜùòÂ==u,YÁ¸,†¢Ÿe|]7ÃEyÚ)"š;Ö½¸¨å —É_r+½>“È|m6M<Ø4kÔ¾“§Ò(¢Ÿ½‘«;ד±ÈVñßÈ?ÎiË·/r`RD'ˆß쇯L¸H5ÒU§ycΚã 3KÒ®oníb¤0¥]UHROû,YZ´ß•_šv“Eþ\ÔD— ±³ÕºÓà,ý–BVž]ó[êï£G§Ý¾¬ŒÈö(ŽÓQV§aŠm{yCÏXšÜhÚ‘žÚž¼BÞrÉÈTrv® ÿDù¤‰8Øß ”ˆ¦<~ÒµÂËÜVФë ™¾°!=%"@žk¦+k>»~¦ž¼€û±À¶ã©ßþô?üÀ¶V6­¨ËCÿÁ -•#¬j®dì²Tñ{=oQr–åœx9¨ür$(’;Uó ²ØF•ŠupZXöb´;`ˆ)%OÊ÷my¯ZA©]2Ø–&à³ðª€M9{°Ë`Ä­ii¥Ù^E6%ºß)-nÇŒò+EPò‡+ÜóJe¦Ë{5åäz•Û6©{ ñ[³8zþå¶UÝ[ƒq''º `wúG›ô¥’V›…¼ŒË"¨Rì(~&Oùx×r-8ü©º×óG[§RŒdj„Ž„zMÑÔ„Á¤Ç›ÿ×âóy³Q!Ò[”ÔñVõ ~‚§õåÊñOrÝ3ÌsÇõ”].;)®ÿ§GÍe@†¥Uˆ?íd„wæÄËnI®”|Ûu©Í>•-ëz‹¹ki$€(¼ÊŸ+’<˜Ä²o¯ù‚ÚÞæiîµJÚT\D·3‹p«ê €9WáÈÑl°‹®¼-a–iu›D¸þín¦ž2àö'— ØH òS¯4x¦¼³’öLä·•£3ËÄàOÁqç¿0Ú‹·“VÖ—ê̪xÌæ¥¶l‹]Ê‘ùë_+nbÕµ¡ãšGB;ÊÝjk滊§˜õh´Ÿ©ßjóiîÂ3f”\*ª‡c¾SîcóZyRëUº·¸Y弈–™Ú:‡*99çö4‹ ,t/,_ÛGs³œ°`è¡… 6ך6î@5‘–šhæEY½ü`Wµv¦?îT“ËÞNúÜv‘Å!šH½u¬bœJÔoµ:÷»”?Âd¶ÒOobgX¸óXÖ­ñ¸h½M]•vÁŵÞ̧ŒƒÃ[©¿î¨iåÛáN¿èÏ•~góâ§O?桟ȷ„Ñ|¿}òú´ŸÓ%ùœÏ‹'óJÝÉô}A5]>âÚGŒHbÖEQÉx?³÷ËãRäl4Èw‹o3»y¿O¾º½2È.FõU#'¹<~±·\$$£3<Çæk «%oCZ\À¶Æ(ÈXÚ«NDW~5Â#ÕIa^TóUþ¬iz„ Ìle4T¯%òOöJHÊæ6LNïÿЂi–ójQÚYÀfžcE‰IÌIžNtbI Ë¼ûùi‰ùms«\+I©$Ð)¼P«¸R+Ý·§,ÇÁœÏ ¡émÍŒFþ¤7ä~­sfú¤QpâÂy('`ß×7ÔÊ¡NUÙøóeÅ·óeÂôëÏ8­ƒ«a2^‰ 4q,Í,–ÅC,‘¡çDVûoðþΪ‘çM1ìŒ@‚ öþ’*5Á¬j—ÓÕXƒH³Iª»•§îµR¸#«”yS,•ŽDÈ™ú¿¤¯az~Ÿ4)h—JI•¡‡,Prû'ùi\2×J]#òAìœwõdÿN£qçØF‘5Ž¿®²H)p2FÑzu.d` àyäø~ÖGó&îƒ/äÈðpqN¯‹êõ¦By?L»Òn.n. Hä¹UtR¢¼ãcÞ¹dõÒ;T~H—eÀ׫&ßÓLîüɧX_Çöp«Ü¼r¸ú»9™9i@>§¦@ Ë u$ŽAìÈÄ‚%“oé)Xy–ßX¿¹a¦!m=‹¬ÒDµE‘Š+v+Ο³æhrŠ%Ù`›ãÉ¿ô•u{ûjêÞÏâ` õÂ" (JžNB­:×%YþlXžÊÏËþó¾¡ç»u¸’ßÕ³qO%"‰Ã;ó ±¬”ýç%ø“öW/üà»áƒäöŽ{y§v·±Íoê,h W’Ð|$užÏB&þžOôÉ,žv¶°ÕD3ÚÇúÓ@e’¬#ªìOÙ|™Õ‚>ˆ5þLƒ|sù½3ÈO¢êZD:³·»y!Hæ¸XØÆŽyH»üG)dg®̃.Ì‘ÿ+—ý3 ™%•ÚYXŠ̲YÆŒ‚†­yÚµðÌÐÕqa—¦1Øý.wfèΠœ™2öšÏåÛËhÛÖ%wØ,hv¯|ãqÉêe0y+XùgPwË]Å °•9bï8Ò¼¯gúqžö˜\j:ŒsE(­pZþªœì{7P!éŒù}O1ÚÚie Ïü-…kÒyO1ZÙ< ac VoN(È"ž®¿i…79´üä+û¸:Èh2D{’_ÖYù•¨Ùâjö¢Æ30x!¼Ô ¥ÉÝs9â Gßø›§¤™Ì'âO†1þïø%ý'ÙAssªC ˜_]ÈXùìµ§|™çÁÿÑŸù3É7–mV©ê]0X½VG>ŸeÉÉåÔË!ܽ$qF‚Wùå-ì–º”\Ö[)¦µ) ‹F…ÖQV‰‡óþÒ¿ûØh5Ä' UˆWW–~P¹š•GTƒð囉]HýOWÔ^ý#ì“ZEé;Kiq G)Æ$[‡1üfF^MŒ›8 _—ºõüúž¥xò~áaú¨†VÙä’æN(±#ZaÍãìÿ>-”@½Η^†‹ÁýYT7S´¨°ŸHÑ×Ñݾ«|Å‘Dmä·¾e×mthQ®âw•€:w£Ö>8‰PݹôÙg'a<ëþL¹2éQV;ƒ 0D÷QI$ÂGXÃ5L”£GÈGÅ?v¼2$îƒy£[¾ÿZµ«Ám‘ªHÞá e‘ÀIÚaÅ=fýï¥ýÎHIx +ɺÅÝǘoÚêæÞåÈKxÑ¡Y™å_Š€äú½ø·îW’ðAù -äÑÂH“I(ŽW¶XÊ,\ÔÆbä| /³ñ`‰b#o!EKß,ÜC6!]…#1!½yÙ±ýè_åáðe†kÀ™è–nº9‰xq’0#ôÄ¿` ñ;ò'í7í}¬¤Ow#ƒf3%½Å—˜£¹¹Ê…å-qoÐR7i6f‰aòþ+1ƒÔÿ+4û»ï4Oo%äú¬Z4·,dôêÒ¥´!mÊ?ëû?±ö[!)/ÖÌ?1’çGò¶«i Ì–Ú”kõiW”RIó#!b8ªýªâ(¼^}[ÏB²6½¨NWöÕÕ[é4Â!àÈ™!†»çÑ ³yƒPŠFuW[{wÃÁÐ1&OGò 쯤é_Yºk»Ø£5äÒ3»—>£ŽLà3 ¹dTÞM­ÚE?Ây^1w<>QöŒŒ¡W‘§Áו2s;0 ƒóÇ÷Z…ä1ݱ†CZ ʆ5§úÃDžlòóI“Sólq! èD÷ O„t¨ürŒÓ2Û†RÿÒëº Ýž¥§A{nUâ™C+;ýƘh½6BÁÿ>µ>/$Üé 9ÞLð9¥(вSîi›-=…œž{Ê,ï­4û»™/ ‚uVrŽÑò^LAeŸùº<}n×üëùtÚ—¯mª#:0q$jô ¹) e&¾3º·–¼Çù[©e¨]kkY3´P€ÌßäkËo‰¾ÖH*<Šksæ¯Ê™4MJ+¿6ÝÞß\‰¼ŽìȈƨ« B+çûXw®L@ߘ¦/§yƒÈŠ&Öhë*9šRõª/+1êá~1=ͼqïz&•ùùk§Ù¥­¾¹l±§rƤÉ;wÉoÜÓ!}Có'›¿/gÔÕíõhÚtšF‘ "5A ñûX8KddÜ£ü§æoËË"×’ëVË771$Js5g¡ý§'s™¾I—™<ïùs©i’C6µi#(-†äKS¥)½r[–^~þ`òL²mJ&•ÙYýJ€¦5â”cཾÎ<%·Š=è¥ó?•SoÒÖ£À <'¹x‡z˽ÈÄÉ­)e EX€d^/¸4©]¹d€,xƒ)ò—ž¿-¼½iõ«-r1z€WR@TRIãðïÈþÖFP=9¨–âë…wù¥ä‹±×ZÕ¼ÎO&ŠRZ”j€ÌkÌž¤ãG4ê2 ’°#ê²¹¿ç"üJaÕlJ€ÑýTª…é×Ô=]Åäáø~h=SþrÉRé×6ðk6²M"4q¹´àhàC ‰êßg%káù¼òßóÉ\O3j‘žp\ â &I¢díÝŸs„I3µó™×m¦æ“.RU~µTpÀŠîÙ+Ú˜ÙdŸ˜ä^]k -×+«{—f¼âv€‹©n1­rRåhè?•_•°y.Í¼×æøjWvÌ4/r¬ˆx“5Æÿu ðÿÁü»Ì\Æ4 ¹öMúxHÊ¢ÿÿÓé—¾U—ËZ:{»Ï)øåjž!e€ÎO.OVôr *ù)y§òÆÏ\¸k…Uj}jËzt`Fês+nG“2 ·‘|•qå©RAÆD´i>£§ÄXBaIY‹îÒ8Ù+ðÇþË.‡hT…Oñ5O Hܱí_þq‹òïW¿šïJ/cëm¦Ò=xn•Ì™qË|RâÍþ&œ"*qÿ>?J6ÏþqÃòÂÒÖ8.tY'’5£ÜJò†sÝUÿÌ)æÎªãðgÅŒòSÔ¿ç¿+î,¥†ÏHkIœQ.‘ævC✮Wù¼·±¶B0ê"‘éßó‹¾Nµ¾ŽkÉ¥¾·Z–µ*Ñ+6äÊܶöÉO]—’D1w}¬„Î<þSðѾ´ÿó^Djòÿ9 {ƒÖÿç<•uv&Ó¦›L„(V¶¦¿›“·-ü0ŽÑÉ{²ð z&š7üã§å…µ’Ci5ýÈ$½ËI,DÔì8#qr']žt‰cV¾ÿœxü¤’ÞHàÓe‚FRa4Ä¡#fš†žønQü_rÇê·ÿœ\òŒwJ×:¥Íźš´B10ðåÈÓîÉÔlÌiãÝö¦'þq£òʵ zG‡®?¦CùK7xeùxw õùÆ?"ÎÈlno,€tn3>5b´ÉG´òŽh:hw&Úwüã¯å•½‡¡yg5åÇÅ[Ó#Æô=(ªÜ~–'_”›µð#ÜÎ=~S,KiNäïiKsFðÇó™Oñ#€èüãïåJš •{™eÿšòšÍüåðáÜòþAþW)øtT§y漉Õfþs!‹óB‹þDþYš2òÿÍx¯/ó‹!ƒóCó§üã›IwÏ”-Hæ`’Ø("§Y9±øbþnG3tš©ÏÒw..§8î6Eù‹Éú‘¨i³j÷62jÚP·:…ãz“ÙÈR%kxå‹«Vž›rû|ÊÌü“1…Æ\\p•Àæ‹Î†¯¨ù²êf»žê×®ÖKtáZ…†Ýþ/IaxF¿µ˜™uÉ@ÿ±rq`œ ‡ÿÔî6ü¶§ã ô3F·/L×¾_.MšŒ4åûÊcÍœ¹uDIè|>¿§_دÚÿcû_vdCŠöæÓîµx¿KSý×ãÿÓÓÿ’¿¼§ú¹´Åù‡ûgã‰Ç—‡üUþoã…¡éÿ¹C×ïèrÉåð?‡‹ú ^«ôqWôw_Tâ~­ê}4ãøæ³7…ünF>;õR¿«Ç÷œ+í”É×F›ÓäkJþL©"Ú^?GÑ’‚•²Ö›VžÜkŒÓ<ž¥6ëÛ¦PÚ)Lýf›pöé\C?JŒŸ]çíþOã‡vC…V.\O©Ê”ÿ'-&2ò_ r§½?Ž[L%j­Ç—Ãôå›1澟÷•øiָǞÜÔòß’n5jñå]ë×ß"k«`¾‰oæ/øüuþô}oúZò¯Ö½*|^Ÿì×þ5û»Óx~îÿÎþs¯—‰ûÎÃü×é_௨§Ô>µús€ý'õÏWCŸ«Ëàû_Üú_><2Œ>/â¯ø¿›Âäfà­¯ø¬É<ÝËô=ÇÕùrÚµëOz÷̪ÇûëFœz1í-s_€¯±ó+÷›VGÃr~W†­°pü± >/¤~uÑ:šÈ½Óv„¯d„~´).ós²¬›ý‚-Ê ßÏa°2ÚI}Õ·h‘¥u+´(¶÷Ž=&×Ä{SŒþUœUõê‹G »ÁIñ}?­pV¢´ÏŽê Ü#Ipè¶æNäή"Çhl‚‹\$žñ}Õ‹ªƒ1Sf†ÑÛÜÚÙ NTy{'¥ðR7§;$«¨“Sî'¥E³«,MÇHrë NR¤©À àíí®íjk,€¢ÍÖÒÔ{Q^_û;p¸\Ø‚ÜPä–]unvË a–”µñ€22@'¸Q̓@Y¬ü¾Â&͈¼ê=Ÿ²žI}jçê6‘¨ìª%"íˆsä‚=Æ›ö¢Ãœ|±ÿ=?­w•i¶Ï$Ë·Å£¶\e*'ÞE0±Z;>ÌÚáp÷yº?JœiìJ8hDø>ŸÖ¢æª°´2»Ä!ÿ®OëR^—°­<+³@#ž<Ý?¥&ô½•…7f€’9Á4à ö¿Òì Ý…ŸõyWà)£ëý=%Å¥^Bx”ê£, n+u0!6x‘:Oxe þÕÆu²ÚО0F:S‚‘½ÛCiwÏ£Ö¤¨¼ïÕ5êÛMÚÜðòOçAº£Hαv·=<áL0{IB°@æPåÓþTÍ­ÉÖf§±)× Ò¹P¡% =FIe\€³öãMäÿÒñ²>ÕM­g§t6‹´b³Œ>uçÓe»9‘">mŒ~Llܸ0}Õ×NéIz­-͹6ˆ6 ®&˜e„6·¼IHxü;èÖ6ySÕL}•¬—Ç=ÆÕÙZžÞÞCˆ˜‚ŸX.#ƒ‡Û¶Õz²®*cAŠÛ ¸[N3íï÷Õ°qÔíãSƒê‹Pß·V<[Pü©~Ô[Jx¸Ýáï,,gî­¥}cñ§%[úJøÓƒ ZºÌžr•Ÿ«Ù+? f›öÂÊ6\°ßûT©ˆ­òHê~5’7ßÛN Fõu…jÀºÄ¼èOã]Sª,jW»A*îó¦ÿZÑ,´êJ\i Or’ qUªÜ°x­ñyå„~”à¨æ©±°B\»AIñ’Ö u}€xžï9GëV‘`³7ž Lçž#£ô¨7cA%6x Ï?èèý)Áœî½Ó,•Þ"gì9ÅøW5yDÒ ÛöîÏé[ Xí,ZµÂA<Êc§ô®ÂÛ>¬Ãÿ?¥8“å+Nè;)Ïâ­Cð§P­î ©›]éÔô)‚¬,J„ð¡)JzŒ –OyøÐ §XN_ F‘¾(«tå9;T¤¹™¼î“º¡m{q6TFSÉ9Þ‹=½kƒƒYÿf¿Å5bV ]ycÿ›ºúáÉÎ 3ZP>ý¨‰.ñcÑ ráT.–¸H囌&_lŒq)9)÷ó7`L½7¨vBä[Ýh½Åœ©I³ì¦¹S¤á@ÔSºð©UeVjxá¾Øæ¦”ÝàÖ&ù)mmI!A”ŽFˆÖ23ÝU™l6êñ}?zgd±•¾úøBx–µ@êhUS®º´©6ǶYù…¶?Õ¢*?•lñŽå ©R©¡¸Çr¾^eʽŽÞl†ã³ÄÆê¸FO!“Vký§ajHfô¬²—C€¡e*ÛâjÁ¦&Æ.´ØyÇSÆÚx·ZG2Q¸ßƓӣÇq”<ê[[Ëài*Ø­XÎyÀ «ümj]5Îóq^j#í¡n7Ú6G.µ ¦ã ãg¹Ý/ŽÊj<À·KÁ 0ÊxNøï'ßW ü±-ä*SiS%§BŽ8­Ò“ž§;U…>„¤©AA)$Œ`úó6®¶+ÃQÛäZ¸Ko±óFTîp„Ž0@À‡? É~×r¸\ÿfn®2.I|9ÚžÃÍqžÐŒ“êïé‘5éêÕ4GmõÝb!§Æ…-Ð8“ÞÞ¡WØæÄ•)‹ƒf4U¥¼¬¥ *å¹çC—ø Zæ2Cºv× ¥¶äLcµyA#m¶¡¨†­®_& í]š“)¨é/!-EY(ÏÆ6 á)Ú™ë]!7S—%±#‰O€;ò*oÌb3]¬‡RÓ]ʼn5âsáYÑ kŒßrkíŽÎ ¡ K VFÄ`çîåZsóššáèå™ÇXZV™WWÔ[iµ$¥ ä@æjàõûUÍ÷><—ZZXQH÷UâûiRBŽ ¹@Ïë^o¤¸®¦®0î-¢Ü´­pbYlñŒ$cbzѵbÎ4ãÓn0„³ÐJÊ…(€0¡êôÉ𩉢gd·¥¼ñìÛm%KRö ™&¸®ç  \¦R’€à*q#Ñ<•¹åã^>òŸh˜ÔnÚó Û£G›Ã¸Fðöm<•+žÀ×i·XÔ…M˜Ì`­‡lâQŸ‰¡üA ÿj‘d‚Ò$%‰|=‹!!J e ào¾RÓótC6Ô?p•룈[“ÑÄñpúÀ… †yLW F›caØÎ¡öÏÓiaCâ wÏÙWÂ,Ø“¬~T±[ióu7!e¤JYõJQáõ°(ï;xÔ¡¸Ï>|)qôð¥ÅÏ Óæ‚£ìqúH JÇ,r>Ú¼hH× iS¶»ª^Нwª|G½Áöð{dzÉøŠÔ»Ê–~ÇžÙµäëDñeÕÌ©—ÒxS,Œwt#í}jrqÓŠ“ò†ØÜnÚªö¢Óе5¡qd¤ã,½I¥t#óhKF-Ù­%ÕdO°Iâo;ñ ¥IÐ3·†*OG¤'ÙIkáA>é®o'ˆ¡= ³ð©‡t‚¸£]0Œ‘s|íén?U°»”d]m+ÄŲ^K}JÁ9åα´nR›ÚyvÕ=|©¶íú’eÉ÷ kHúm¸8qü\&¯µ'†µ%µØ× -UÚÛ~…vmjŠVK|Ä©<%i øàŠólK4˜Ö>5ºÖ£C^r⿳t§‡¼f¶mqµoz«;PS7’ü•«#…ž` oZÏ”èͽGNÊmÇ &4¤ÃQs`§THO~s\.z¾Ój–¨ºã’’2¦c´§T‘Þ qï TÇœˆòÓ-Aé'TÇ:Ûe)Q9 t­¥®ð¬ ¸A»¼ÔªåºãÎÊ%¸*%*JŽÊÆÙ©‘t[ P[nã=Jxd¬´Ð^R¥,g)Áß#ºÉ»1lŽv³V¤3ÆRž#“Ój ¾Ý½ß¬?³²":”Ét%÷B ©l“ÈnžÌu®…‹ãzÇL&ñ6#þœ…¡¸ì”pÞä’NyÔÈÜâ<ÐÇ,וδ¯NkˆíÅi“ä¥.:*áiî¡<>©;`øŽêõ… ŠÖÖ'ï:}h„?§Gq2#pxÁäLÊ‘B¶ûZõ†§.HŽ×É–Õ”¸B–|áÞâWém×Àx×§¤”¡ À¬­-g6M=ð§ÂxßP9âq[¨ü ÖVB;@%_Gk¾\Û„è‹kWJ‡¤ ð:Vt¿)©…`´Î]­nʹ%km†œÙ +œdôä+ËíÚ’áNÞm!¥Ö¥¸¥H…JìÓÈòØ{MlÜ!\T΃‡n%E+g;p(¯ ïËzÞDX5ÍêV£‰j¼Ø¼ÇÏ¥0¯I'`NH<Çßo­Xb4û¬<û:–Ye„ñ)Åž@t;¤´4«]ÝWËåÉw ¢P“’R€yîw'àZ×Ò‘¢[Å]ƒ7FàJJ‰<€æ|+<jùñ'FEîÈm°ä¨¥·×%+àV ÂÀõv¢­¶©MÚ®Ž[ÞzXù¼uV9ãÇ?ª ^õ}½weÛÝo‡‡#@Xùù##JGÑÎ:Ù[X¬¢Úè—)äpG·²‚\âÆJqèŽüÕÈh†=þ»€ÃèrAa2@NáM«‘­S›«áBbC›‹[‘H8I.+Œôç>mÓwx÷Åy‹©MÞÏnŒCDŽJ¸ŠÚ>á[ÚJ$]K>ïr™ÖÔÕ̸Ûävk-%+ Ž0E2 ÙzêÅe˜Ò¾P’}XðR^Yøl=æ´d_Zƒj‹>àË‘Rò›B›PÊ›RÎV6'sXÎD Ê-™˜¬4ÃbÛ'i#ÖGAZÚ†Ôo6YöýÇœ0 …}UóIøS ê›ä5_WeK™œ†êFø '~ÿ Èý»¶pº‡Ü|>¶ŽÊ;Gà8*JGÑÎFN9Wœ·"êÌk®ÍFZßT¶NÜ=˜loh3Dv&ãh »ˆ¼¶¶Ú“Ìî´…üâ@<>‘'Æ®@WoÕÑ&Ì0¤F•n—À\CS[à+HæRy{k‹úöÀÊ–-OX¨í-ԣĩ ŠÂ»¾ö®(‘#ÍZ­í¼é’ûe yÂÙHJwáÉ=k â{–[uªæ,¯EB ÆÙ+ó@ÛŒz(ÏŽûÓ 8›ªÜóô[í×.RTÊâK !+Ý%J=ã|Vl™:b7D7×Ô¡ÞÑ@¬xÐUÖñ½[pcO…ˆÑáÅCÍÇZRñk…@Ô @8Ï'æ"Ûoƒ3K‘&¡ß1U®cjËî+ÒÉšÆxŠÇAL]“.û*ÔÜ~4Åi+yî/E+W$c¿Ö{­¦N²‚Ú9ÁŽãŽ‘Ó H÷àŸuXµÁcJi§’âuT™Ôë˜Êä=Õ-; öb¹6n<ús»ÀrFØJ‚FÞÜÒ¸‘„e,ïŠC•0?8Ÿm‰V,Ðü›zã°uÎÅN'û4çÒW·lj´j„<ö“A!Xø ,IˆìGa¶`¡¦Ò”°ç]¦Ò’¥6°Ü‘È|k¦1XwK«Ê¼&Í/iÅoÈ|¯„2ÂqÞI»«1¥ø8ÂDQ!¢HâmYî® ¿ZTìføR¤8ãMð‚AZ=`OB(.Áa™LÚï–¥«‰Ž•>•órÀ'ÑPúÝʪvQ-ÄX&&+¢§\ ©'Ñ%* ~Õ¬ˆô9×›]©ôé Glò., ûrj´}Ua~f¦{(޲ •º°ÞxNˆ4§nZ>46½zsÒ’d ­Ô/ªRœÈ[:Ñm‘©’-Í)f\…´d28’Úœ$`+ Ð‰ªtÍÎr˶ãÑ™S²›!(o (…±Ë8ªÒµ]–t~?‘® ôã0c¥6ÐRŒp¡Ì>Ùç–à«IofÚüæ< 0 ˜ËíAǪ0{»©ƒ^Ûy³K·©JiN°–Ñ p­ºO#]zÙôÂŽù-)-¬#u!;OA¾hÛhº§QÛ’ÛâÑ&cs%ð†Òáe Z=ä|+y puªbîîLHK¯¾G g áAßÛ@Q-ˆaÙqQd…<”Ô‚yPòõ¦Ÿ(ZZ‡2Tp}'#ÀSíÆ l_ãG“eš™iyÖŒe%ÄÇÝÅxO12ÂÕ±$k«  §a„©#¢x87=1@[PéÖãÀyµ°Ãsò#e¾29“Ö¬\×djKbã:¤)µ¸€ë)ZÊP2¬lywPb ={b=õµ±ç€:ÖÖšUÙEE— Òœí— @p§Á#'˜åZWã&=™R *Y_¤Kg´ £”§¿8 ÛLå?«!\•r¸]"Æ#·¨]“-pœœàýÕpѺµ”ZUsD¦Þ†Ž%²®°9#•y­ÿN¹w‡+Q¦Þ‹[ $±¶ð쌭#‰Ð9sØc5¡¬-Q,wEj&í1§2ê{ãºß ¤z'§­€GZb ™Õ–GcH”Ë‹r;Œ§›IRTµIê75½Žc[}¯ç^gt]²Ë£bØZ’‡®‰Ä‡˜a%N)|ak< m· zG¶«³·@ó«¶Ê„Ð#³T®©ÁÔð‚J}õš«ØÛd+ãüéŠAÛG¿Òþu<ƒÔ|jY5qQ€{,9±#Öþtpi6Ï*Ðd!<)¹A[kûKOòŽ‘êg¾„uBõ®”ÛÒH$ŽàZÞ ÈTpêF3±®ƒjææËAåγ=Z µÝcXÕª¤KJÄvniR¸Ä@ZQ¾;²sš!›x…åÞð&LÞ2Ê@‘’IéCQ¡¢é3[[GôŽÌ žªgo¼ Å·Ønچīü ¦oQƒI·6¼§„3Ì~¹âû«H8V ¶¢LöœB€·´—Ÿtœ¤œg<ð>ñYpuõžE¶<çc¿§¤+H!…Ž]¡®A?¥.ͨ%ÛÙŒ`6$ݤ*}ö%-¡)#…•’3„ŒxUѧï±oá!ˆ£Ü8#Ï…³À¸#éþr ¾ñz…f€™’¥%ÂÒ”êÏ$¤u&­)˜Ó’ü$8 ¨J¸IÆýh;Fé’Ü•\n ¾ÛL8â-d¹ÅæÍ“¹öžÂã´œe#+'ñ¨¬7µ‚=µ›‡]²ØŽ–ØÊÔà%%(O>†¡S6õÂ3R´ýÒž_fËÏ0ÉèH$§8ëB*Ž˜6X~uðÌ–n’Õè q-”•’’7r®áëÍÁjMŽá©œ”Gõ³Zn; ?k)ÉöL”R’sÀi¸SŽ~í)Ie°ã‰[ )Clœnqí§+XgÛYÚB{08wWN€}PmiÁ‡>ú«wº7j´KžP^ì+ £u,ôwœUýTì(±f^bÔx¼^r†ÔâJzã}꡽YX¶Å¿ÌšÀŒê0Ãêg„­ð9«§* ùït[D‡»F5“"â¼ÿV´·R< HO¸Ôoérà ]‘*CQX¸Fo}Ôå¶·O‚U»kügÔpok{ÌãË 6CÏ0¦ÐæsêçsʵV–TR…·ø’ sƒßí KéRï*Eòçrã%¤-Řƒ=IÆã¸Öúí—gn/L]ì% LXˆhÒHÀS›ådséL5¼ÙQHéÖ¢ˆÑ›qN¢2â·R’Ø >ÓC)´ßc@±Ô »s´ñÈÐ-´ž½“c`wëœâµ˜ì¬e\}§“%|KW2Iü‡°T°RÔz®Í¥–Ò¦6â¤ÉKl¶ ŠHêwt¬‹_”Û%ÊâÔ4›çÕ†ËÍ€¯që^pλ*Ö²uöñ5âžÎ#EÂÂy‡'y&»Þ#ê ÜYºªêÒ˜q–R˜¬¡< 'ÖÇ0$äîIÍYòZ?Ÿå/OC˜¶ÓLǘÊV¨í÷ßÒ'¼tÚ´àkËÂÉ"ì—œm˜ä%Ô8œ,ÈÉéß^K¥ü 7gñjaä¥Âçh•ðN=Ž@ü+ÎÉ.&ŠùRSE—®7¸H %+áô|I8Ø«~S^á&íib|+KœLÂTÓØ¢¢: Ž}õEZÖÅÛÝ[SùnÚØ\‡q”’N8SÞAÀöšò7¬©Zš .¬ÝîËÅAX, åˆý'»–qί'L·>íû!e|FIvå<Œ…ºÉýÔ“€;É=*bëÕï:’Ïh³¢mÅÓæÏ¤$ ¨¸9×c½]·ˆk³Æ,DSK)-²¶ñÀŒdž›W‘hÛ%ÏS_#G½©Fß`ù¢ÒÆÅaYñå¿€½©àÈÈÝ}Õ/ ÍSa4åH ƒú8a²·ê¤ )[5–û40Ú]LÖh•MºËˆdwמÛл}M^‘6+ɈìU>Ü:m “ž$’zŠ'Ów«½ÏQ´8MÆYQzRíÆ7d¯¢”©G*Éæ*àÝÕ‘ß—¥® CmJ¦ÁBH>‘J‚±ïÅbY<£Ù.\æÛ¥ŸEH{Õ*ðW·¿pHÇ1ñ¬;Ö˜³^¤Ì„ÊÔG®¿r†õ%+UACe)*\;o·}t Äì®b¼úÞ¹zö‘)ÉzzBû4:½Õ Ìà}“÷W  ‚¤àƒ¸äiR;ËŠ¥ÈWdúÈ9¹~U¢òËl­cšA xÖt–ãpV…œõë÷ÕH°¾Í–Ôã6„$)JXÌ“AŽyHÓ«º³4gd©×Ðu´Œ“Žgû©ü§È”­"¦"ñ+´y!îÌd”nywd Ž‹T¹6(–+$Ä:ÄÆÜ‘1ôn±‘œ‘Èg~êhUzòl´N~kwnc¬´@ÂØáŸ k‡”Æ-~b$ZA¢¢OkÈHVp7'"µµ…¢<­)qn·´f?t·jhºÄˆÊSÆ´´§‰%'Àë±÷ÐŒ­*4kd{T€ßoÌîŽjmD-Kñ9âú%µZ…—QÊ›J-2ã¡e);4ò=‡ÚN=â¥U›–¢fÈe›l¹ï8’®mqã逬ôkÎåæ÷+:íÌy³’C²A<(Ær”çêÆ§ŠÄˆª×s)dñ)»rø\t¸vßú·éÂýÙRÓŽD„a<Ó‰3´[êV AÜ”½ÙÍ\ˆß:µöÑÚÃÒs׌¡ß›h­=ázºþ§tIj3Ò¦Rûì•¥±*Å‚p¡3iˆ¤)”h7Õ)Iá̉\L¡\³ÆU¸÷f»Ù#]4bJ¢½u)¶Ôâã\mħ‡V N1e0ÖùÔ“"MŠÍòÀ˜lÉp6Ì”>—› #`p2’{ëVù{bÃk\ÙBÂJpÚV•Ä 2çÏ>ê ¿?Ôp¢BTa”¾ÓOð—¤8“èá=ÜïÏAÛËPÄk ß=qhS·[á+N áœs a+Ôy¦â9!a•2„(’1¿]¨hkEÈM¥lÊ‘rŠd4Ò\JC`ŽØß˜¡sÛ]æÊ]ÎÅt¸Jí–aã°†ÂA!'$ád€ '5¿c±N¶È°8ò|ÂÜìwJ¦¥1žcntÁu:¢MºäümA$¦/%Ö^Sƒ|'Ñ«'ls§V«”JV4•áqIt²pz𚆡´Gœ·.r˜3ø!v…KWhÄ•gb:Pĸö¹Ñ\fÛ§oÎZH žû­¡£õŠŠ·Çp©‹¯Me¦Ê˨ŒÚ¿Y\ (ûv¡ØÚšmÂö«d;Œ©—rs¬4TGH’pp+bÊ|ÎÓ‰ uæByϦ 0M Ÿ\ÖSn0lÏ.$†YŽ]–°ÀO•Ä Iç¶ÔÊ J~Âj$6B|*]ªÈZOŽj!h'*RGvMg¡Â8F8E È"”ˆé[ ©JÿhéÀÞ“Z·í@Åž*xç_<b åN¯ò©è+6 ©v˜QÝ’ê_¸ËžÛÓ”µd`}0X q•ÐZy¡@×QIx( ò©=ZÒågUj•p¤üû ''l5Ê‹2³Ì'ãBºKk樰kûÔº²«7{å»O°ÙžàI^CM¡µ-nÐÅ”(! 'EJ<€È¬iZÎ]•Õ5²¦2ËEæ•ðêV”:`ŒŠí©å*ß©l$C—)¦“!µˆ¬—•%8é½bê+eÒýhº^&Ãq‡SL[íéôÜJJV>‘S#åG­¶«+× ,‡—Æi²¡ÄQæq¾ÕWöâ:"ÉjñiZ$·/ÌÌVœö‹áâÀÀÁÛaË´j9·AòTåÃITuve<¹ €ç4ò#n¼ê¥É…žÁæÑçÙÚqRd´– ²ï ùį~=¶Î7Ï…\ÐïL1n•pŸeUš®B•-8è”î;±ãU©.;9?³2mq@Ý„, íÆ[<†ùÜò¬ MʹXåÄ… ÿ-Âãr’õÔ€•–ÔÙ¥$äq ôçZòužž¹BTw¢Èš·°¥[ŒU©Î-½’1±ëœRD:eèÆî­³ ˜’f)Ô§0ã‡J ;))ÂGŽjÄ»ýè6ù}“Ó\}eÈq›l©Õ)$Ž ž˜ßsŒf¥`Ž êÍ@‰ØEpE(!ÏfrMºâ†lI“§µ]gA’bÏÅx¡¢µÇ=º”R7á óv™ñµ qvj-ÊÓ2Õ.I ´ä’‡O0ž4äg~F•®ñe6K•ÖÝ(D5:i)JVTÞNøÛq¸Ï}fÞïQu[ ÙìÍ¿%OImj–¦†£¥* *â dí€}`EÓ—¸¶)÷S|OMó¦¥Àx”—RV°…§<”åÔPz2îQ¢X^¼7-öv´4‘Æ SŸyÇáUÕpšºY!%±wl¸Ó¡C ÈÈñÎ*åš?ab‹V"´ÒÛW1„ kÌ"éË“¨º­œ‹‰bÚ¡ôƒk.`w䚘¯Aª£»{]°ER¥®#N$Ž© …¨žìgw«‰¾0«Î"Ûàö뮩C„¥I*û€ Xð.Þa¥¤D(bã&\©O8óEHd¸’}1ìÀõª——¶äÞ­;>mÕèí Gd…´”/q°GßLqü¢ØÜ´"àê^l,¬CEkO ß8Áß¾¶,÷W/Œ…[†‚rØ|§‰hÀ°9gº¼þæÍâÆÝÛÏYŒ¦¯0Gc*Ru´a9ï%9ëŠô‹PKv¸€¨ Gi8'})â+Ê·¢ål™ä!MÉBÐA霌üj¾ô­3lyô‚ðl6µw”’’ËMs»µg³Lšê-‚–ÑÕnB@ï܊аC6û(«#´m¡ÇûÇs÷šµ'‹Î‚¬¸$wÐìûãvW;YÍ%0\uM©ü_N-¹סºÓ©CI:½¸ˆCújC7+„L º•J}µ¡iâ‘¿~Ø©ŠG¼Óüæ3œþõpóèøÉpmDÜa eRZHï*ÅL£¿ÎràIÿò¥éƒê'¢æ ´5ýeÊ*|K©ýjºõnŸG­y„?ñ…:6>sê§ø©çÕNÞ&°W®4Ê=kÔCûª*üU>Q4ÞáÝxîHj:ÕùQE¢‹ùP±×öÕd³êî>¬\gâi†¶qÂvì¼ïé!)üéÐU—3ê'?½ü©eÌú‰Çï*:ªéŒ£JÏVÖ¤~Un ™&BOOŒ@.)h)O‰ß•:ÏO<]š3Ë9þT²÷ÔF|þ”!~×fÓ|M²¦EÉioþÀîÞw;ãsíÒÑåÏt“æ¥ë|Ìã±–žžàygÀâœN}Tÿò§ w'ÑOñ*¦åÖ#+uö¾‰ZÂOß]“,,e°:a@æ®S]xꔟñÒ—ŸU?ÄJçÛºy0p{óúT{Gºá>ÔšeMvâwê#ø¿•EN8’ryz_Ê«"àÏÛíÐã”ñ¥<ÐË=Üg]oÎF¹7n‡mvd•2_-¡ +©_4ÅÖÏΕ g  þUƒrÔ¼l´Æ §VЯ›gÅÕòHðæj ´ÞnÙk·šÇW8–Ü£#¹NŸHû±[û|LTÅ‚Ãl2xR9žòy“âj ë5P¤9q¸8&ÝžrAÙ(OÔl}ýç­Y¾E‡Å·ôèüÛ­0¡Žb±u™ù%”à—.Lä~îUùSõ[¢œò¦ŠsPÙ`ù”ëÂÛ}.<ôÎÝh;¥HNÜw­Ô¬©!CÐÝÒ$™«µ™ÐÅÖ7\+¢Bˆ-¯Þ2C\-ºÖß.A‰p ´]Q²ãÉØ(ý•r"´È¯Ó'èÓ8ÏjžŒçbEqˆ‰ ïÄ‘±®¢[$`¨Œô)"™Wb ŠŒG>±5× Æü8¦L†‰Ùy÷u>Ыî4éÅH¶ØxüÖháÅq¬4Ž%wœu«€¹ö~úçÛ£±ÏÕ5¨µ„K*S†×2èöÌCl"O"{‡ßNˆk-HåžPáð¹v˜°ˆ­ È$ãˆøÇßUüžÃr ’k*sµXžðS„îµ ~ Ô4¾œ™oßuÃ÷y)ÀF2˜èú£¸û9|iôœÄB¼ê;ŠVš_l«Ÿ€ýÿ+=GÓWÙ“OÚ£c“¿§ Hÿ‘¨3odÙírnT L'‰Il+ž6÷ÐÌÍmu€äGçÙãD‰!Ô6"hóŒ(úÜ`3šÛÕñ;JÞ#FaO>ìr–ЄúE]1A iÇn XzaQ\}°™;ÂƒŽŒÊ¹'»–+Q…m»¢ç"c, ÿD°Z³è¨ðƒ{·¬c­Ù1S0d?2Cï2Ì6OÕÙ¨¤¨ž@mβì¬]tË—t=?*cj’å>–Û(Jr¥ž`òNê0ÙžˆëBå4üx®¤:–ÜwŒ-¥³íé@gm¾]$MDk•…ûxZ CÊ}. ‘ôI‰éßTÕ«&Ë’óv[‹“L8[TŽÕ-6Tža%^¶(ed]_Š#Øî±]nJzáu”T´¢ž#’ygU‰Ö·`Ýe³o²jT²]*A…pJ_ä€NS¹©‹£‹lé¡%çà¹ íø˜}@©88æ6¡kÖ¿jÕ®-ÖAÙœ<2Ý'™_¨3Ó¡>ÑWôÔ;½²µÜÝuå,•±o”Ò@õK˜$ü+ËœÑÓ¯šZåªfvêº>ñÜpƒ•7ŸKls=rG}k^‡¥õÐÔZªënAlGkäV”œ,øäàû+ ¯)Žìô†í–Ë[…”-ÙRÀsõŠ{ºU(Zã`¸iK©¥¹/ˆyøÉáFNN{‡ )öCv«<Ã:b†rê²úËnHíÚ@Qè0Ʀ+Û—wÓ‹º%è϶Ôu:¥´¼¡JH9džA7¡5§Õsæ1 ‹br”ð¶ §öI'aÔûª¤Û°‹d³i©ÚyöMծſ¡° ðByäãr;³GðØ#Q#%-°ÊBFÀU`]ú¨øŸÒŸç3ê£ãü©¤ãœ-8çPGç3É?Äj?9õRGï*ŸÎÇî¬Ûþ×kSnØnàáίEðWõ@ÿS¹\·DrD•6ÛmŽ%-JØ}ßwZsÊ+3ßtí¶UÎA8ãáìÚO‰Qä*õ²Á2d¶îZŽB$ÉB¸™ˆÖ{C¤¯Ë¥?Ô®Pm^åÆºÜZSq˜Qr%í…ýkŸk¸}m(T|kªiÈzT×2úéû?·4‹NµêÈZ@nç z¼òAüè ú øV|û! ¸ì>²Ôëm®ÈÙÔó¥Ù¼F ˆ óΧÆGökøSñœV¿…6Œy^Ó,ñ?n‚µdÆH'áYR<›éÙŸ3 õ*R~ìÑbÞIR’ É'ñ¬/²'”Ƕ4{gÇN©9 ³ËµPñ9áO^|©Ð7#Éfžh•ªt–SÄŽ$Ï!¸ÎM#ä–Ø?ø¤Ð:ìš$µYp\©ak "£‹ˆ§>³«=\WA°­Þ>Ê|v«Ô$¶Õ'é<ðÔ‘ä–Ê=i³œß½#ò£¾×)ݵ Š»kXÙfËNΟÈEŒ”?x“Nš¢Ï’Ý8ÙÊšyÏÞpþU)SFÙÓÇ-¸lõîŸÀª°¯}G)²åÚéNB#!¤+‰õvÿ PS÷m3 îÑ‹|»«üË÷ŠR£ßÂþ&¯AëºBCQíè˜àÿ»ÃãûÕVãjW•ƒFJH#e¸–Úâ+ÏÚò™qŠÙnº×¾æÙ?­q{Êf¥w<hî˜HǼƒC¯RN¥Ô‰P³„òà˜ÒºþÕ^»Úfg³}µŸ†kÅÜבÌñ^¦{œÇà*(Öú‘ âMæY?iyüjñÞqoFÓ˜¹A=KðÕÂ=éÈ®³µ¡‹D‰Ñn,ËSiÂh‚µ¬ì”ðóÜ׎Ãò¨â©=³ÌÊ@æhe^ñƒ[ñõ昽a»í -[vͤ,Œ(}õ2Y³Êu–ÝzcÅR¦<]}iV *ñèÿy5£{TMA 0Üa¹WÔ¥<%Y#*æRBˆ÷s©Û-zâµ.ÛpóÖx@‹áe;ôϤ ñ«šn×Õ©å½1⹋„@îµ…EDä À­eMƒH°{;lx¬H-4–Ô·Ä@ûꓚJÆòÔ¥[c…+™J8ZÉp=¿ˆ©…«ûµ|EsëL¢ì©9–<;eãýêJÑ–UU‘ܧVGûÕ¾V¯¨~"¢V¯î—hýiж¬^Rd[£6†#Í·¡ÆÐ„à ’}øâ­)O«ý%Ãk ’œÿ3ƒô¬ýaó÷JK!KS¬YPÇÿê»Û–eyOºžD8L1‚yXýõ©âQ¨ ï –Þ>ÁDQ_u7hz4çÝúÖ>žiøV $Ýtû}óT³²Ò¿ZÛ(ÿbàø~µ…{pü¹§ýgÎÿÒ5d"œîžx®KW£ºT7ªyÊ9‚«&#ƒ‹”‡G.^™ª÷­7lÔ1ÃW(éw‡ÕXZ}Šò©ÙÕ–eŒn&<-óùÖ—ú¦ª?bõ„ñé­Bµ29D›é'ÙÇÜ+³z“XÁÂnºQr1ý¤%ƒŸvôt åÂ~"‘*ú‡â)Õ ÛõC²æ±E–ëOä!É $#ˆ¬dàzU»µí›@Ž—[”ûÒ2j;³‹Q<#»©é\ jT[nhbð«;LÈ`¼Ëñ“Ù%$(‚UÏŸ=ºÓ¢õÖŸAÇŸºUܘ®þíX«,W9"4[«*}^«K >À 3WãÍbkAøÝ›íKmĨx4®–nÒ,öx±’e˘c‰(NÙÏAãáOâkÒ‰#!Ôûj¬˜±$-*}ÄñcÓáÛãXWK¢åNU–Í.\qóΕÜD}ecš»“ñ®ÃEØÖÊS*Ö%:¦óëâZÏy9æjÎ VáÃI mÁÏl9‘øÕ þ¨…§Ú@yKz[§ ÆaN8}=µÍP­:NÛ"D8Œœq¨!Yâ#nþ}¶«i« ‘]vïrArí(eeDÂz6žìu=M_{Süe¹®/±dÏÒsØ‚=%<B{ÈÅBéåJÞÃý…²<‹’Ç560‘ìØ“ð®ó|ó[­Èì<ìK K(RÛ8\Â9àôo?ÔókV•¶§°·¨6ÛMÆòÎÁ=äžóÈdÔ“þšÃµùRpqƾG¸öíŽ"Û ‡HIäF纮?å !a½z˜œ?Aè-«YÙg11î RÄ€Ú²‚ÒŽç¨c4f—²W¹èif, '|£]”ž8šRâ´÷­$~ 5HêŸ(W L:XGB¶Oâ¢? õ5`z øÖ‘Z‰þ©[÷‘úÖUä³þQµ ¸n3× ‚w t  75Ÿ¨ô,-:ý9%éKš–ßRÏ)Û ¿^y¯k*9þ­_uùBl;7N8[$¦æØÇ~ä*ÏRŒ­öèÖØ‰‹ –Øa6¿¼øÕ:ˆu@g²_ÝúÒBø”}mAÔSYð4³µq(sûßòв~Õj4v‡!ÆvÝŸÒˆ ÊþåÏ»õ¡‹SŽw}@iyq†ÆÙØ©=ô›ŠBëOðŠäë¡•¶…>xÜ$%!#'sìõ>ÙA$ö.{ñúÐÓ~Ý:ð„8|vQ Æ‚ ”c~¤ñwu$¥©Í”»Ôöm ¸|Õ嬾¡ôÚoCn…d'Ç «Û­¿J[ž¸»³ŽÛiO( $¸W[]¯äù¯:Wi¸ì ‚RÚFNwæVI>ê ò£è¹–ûœxî½*IRxr®,äã¼ð äÔ¹×É‹ùv|¶BFÑá9Ù¥¥„íë/ˆœç— î­¸úŽfžx[µ®-¢OšÜž$¼žx+ñ¯87gW橋IJ®']Y=I(@=ØDþú¨•àéfzý.DÙ—¡ÂÀ˜ÆÔ3è’9g–N7ÍjD¢”É“â».é1Ë-•#<9 øñúƒÀeTsבíñ—oÒqL&Iôå(Ñ~=þòsCZ‡RNÔwH”áöL‚x[ÃõëXþ54‘Õù/IyO>êÜqG*ZÉ$ûë‘>ÚlÒ÷ÑJ›4½ô±@³J–)øOq nTü†{én1ïÅG•D8¦Ö…¨n£kº.6-º”*u½Câ2ó'¢‚¹œ|GJ“€2M8;ÓL{Ì=C#O=«œµM³É Cº ‚0y%ÎããGâ’êŠT2"¾sÒú™6ä9jº ɲËô_dólýtwÏÆŽôÕùý'wnÉq|ȵJÂàK*pŸW~ïÀÓÔz¨GúÅüE?f¼_¼Ò¢—³?Ä*\nuþaYê‚5‹¦µÑÉ*R³%ÏvÀ×]Ÿ>¹jKÀ*s<²ƒ¶èlcñªæk‘uM²H r²O#€}¹"ˆt]µËN‘·Æ[<¦ûgO5¯Ò?_À@¯ï÷S‰ÇÏ9÷~”Ünçú­¿|T¸Üþïü§UÚÿ¾sîý+ ò“ûG`ô‰ôŸ<¾À­î%ïè}ô?wY:¢ÈŒp¢Bùý”Α|éúTRs½9ä|*+IJfÞZÉÂ&doõ›A5´‰øÐÍç?hõ<œaÁ¹·Ê‰pÐ?Šª%Þ§ãK€w«ãQË?‹ùSåߨŸâþTºüù½šàUýãÓ¿N,Æ»@Is[Þ8V¯™‹°Aï+V>ú޾e×´5Üp'Ñg´;ú*»Â¸hÇšíÚà•)çYNê?E„xxÕ¼#¼üiøAÏÆ¹‚éæ„ý*YtrB?ˆþ•”ƒÔüj8Á'ãH—O4#ø¿•j ìÈÓ#Ùí,¶íÚRJYÊll\_ä:ÕϺÄf‹¯¼–›Öâø@÷š¸kHÆa±2zsŽÖ;g²â;ñ”IÀÛ<êp´›)Ze]ÖnÓÆåéJÊ~Â1‘¡¹"ãkË`°ËËu[íÄB3·BI«13¬¼V©7]HûÎp$¼¨1œ(e'¢N7Z¹ “Ï•JÕ¦£Íz ¹CmÑYR8“Û,ñ¯ŸÕóZ÷Ľ.U²Ú”¤6ëÞpñ'è5…cÞ¢ñ­f›óv”@É*Qâæyši€£m‹¦< ZÛ¶!LDº¶ëoÇBˆAZGTJœ}!w“x•qŸpn2¤(¤®)*y,ýТ0ŽdO}=ÅÅMò«de",Gdþ° QǤ>ˆøÒ¬R¶Z Y¢y´ g‰@JRIÜŸV\q¶Èâw„ø«¡Gq»\u}5e‘æ‹,ùĹcu6Þp|OéSgÉå 1nJ²^•!JQ>ÁŠ’ …µé9 .%æ"(/`ŽÓèòîçíÅ\”Ày‚ŠÈse'>°ê=”£Dj dǦ!¶€HçÊ»¶®S¹ñéU—$0ÔVF0„¤`°†léV¤¾=|uJ0#)QíÈè£ÉnûÏ¢<*Þ®šò`·n‘.à¿6kÕÖ_¹9? ¼UÁeCmµ†B[i´óWD€;ÉÇÆ¨åÖâõ$›‚Ü<¤²Ð—ÖÿØZ0Ÿ]}:Õ[kOE„”ÈŸQ.:¤«n2r@𽂬ñ©KâàØr¨®½‡¤½¼ip}µüi¸Ö97þaMÆç÷_æ:ºe ç¢óí ½\…9}ÓLq¬æáÚdïŽ(щ[…Õÿ˜P~§ :¿Lz8ć1¿ÍÖþ}f‹›l”t{ÇéSm Q*QÉëPl¸Íÿš¦Ò”®"¡ûêQ"œŸXŠ‘s¨‚{¾ú‘åYDF@¡X¾‡”iXÏÎA9÷81øÑYÙCzlcÊ ¹à¯üDÒxÝõj÷5ÇK–Øä¤»ÙÇR‡0§IÎ=ˆJ¼V«–æ^†Ôc”´Ú›P ÛÔ ìØV fÜ‘uL„¡JÅÝ|dU!ŽO‡/TS+›%%$ÁºÆ¿Mr ˜Ø3$,2Æz(ý#à‘•e'P9›&˜zïza ˆá}“qóè¼øß à{…y Ât‹Œ×¥Êt¸ûÊ*ZS[Z®ð.w2Ô¨’Y`Ÿ¦ôœ>*;ü+ Qg ækV’9Ó¾¢O\ÔÖ­¸B@Á܃ι{h84¶§H$í·‰ XÛ•,êž6æM**>4éÏJ–iu¢WMžôÓäRÍp“ЊE';oR8#Ü$r4 tœA£5V§ÓÒ­)IxFA‘ά¹Õ³öV9xŠ Cüh =•$z¿gþ;¹Q·“ô|Ÿª£É‰pAaYmÞРüpFÇ~£žh=É–¦UâÎmòÔ|ö äU8ëM©éU‚ôÛ‰—®5T…€]i棤õJz{M‘šó‡ä %å:CҲݺô„”º}PèØƒÝ¿â+Q+ÑyßT/jÕžJâ4§_CeIm áR±Ï„ô8ÎEòŠ—a¤4Äê[Ù$-*(V9d- ñOã[WK£6‹¬‹íÝ m¶Sæ q:¥zKRs¶=Q“Ðí¥o²/ÈzTæÚi*p˜¨A*âBq•FûœgÊÜŸi¶ßY`\b"BWh„¯8åÖ¨Þ,²”Vý±ÀÛ¡¶˜i ÒJÁ\¾ÍM\YŸxo)C…N>¿Q–ÒVµû7¡ë†±»DQWìô‘G÷OÝ[¶!©G²JžRR]S-“Ƭ “œ'¼£ìª¦<‰m)A–1º–çøÇù©ü¤ü?ŠƒÊ Ü³ M.,±Í—¶'ðh½·BÎ1…i5ãšÆÜÅ®e¶ãâ·[’„…¥ y °ë^¸ÊŠÜFsÄ‘¿éWêLâK]ÕëŠÕ:£J“æ;¿þ*1VÇŸêÏÿi,s7隟+EœçÛRBxv¨5ô³SAÎjP“Ê¥L9b•ea®(:êó‘5Q}JÓ I÷'³WëF'Ö¥ Ìi ×–þ.";9;±ôQ±ïªÏúÛ€Ùnå1HI1䥹XõsÃÂGÀ$ûëO¥E¦ÐÓhm´„! ¥#êf´‰æúò¦Õu[K¢´˜Lœÿhï¤á!¯PY <†æ¼:Ešç©a·6:´É™!kWr¾g§ >eKi†µ´¢îñ8;4©ë+$í¹öVµíM943 %L4;4vcÑQÈïß;õ¬W”T­ÔU°'•QÊ– þu 7?¨[\F–)mÖ³Oš[R &ž›4³A iÅC4ù –)êTù sŠÙ°5p\¢ý¹ qÖ0¥!'rœ÷uˆ¡¾sDº2¹7èÎ[¥!©M+Œ!\Ôž¸Kniî©A•%Û4ì•%hqM8 0¤ú§ØkØá(®u+™içØ+É|§\u †ÔÑãuiÉ ›‹xô5ëí¤! @õR8Gº¥T¹bž›4ªÓ7:J…ÜÎu¥©8åóþvèŒíC—N¸·ï°‚ïþ¢*ÏR‰G*b2hÖ]öÇ PZÜ9M¯t¨zÈWE$÷Öžv¦* ó–®ô§ôf˜jûmkfÉ8t'§Ç4Ò¯ú£RGv×N½n2[zT’pÚNćOo²½‘$ûzUÔyÔ«òÇfzlÍNÒB”ùºc¡ ìœ-É å×aN½n—%Ë“c‰Û¡îͱ2r‹Mz €7S„’NÍ ê ›º£YÊiO [­ËìšC‰*A_"¢9H8ÎyåNü˜ì©-¥|S¶ChB;i.«ê¥?{—Ô{A$Yy2ÖÅÒK‰Xn?Ì6æ¯G+ÛžIOßQ»_œ™oVœ°ºýæJ”óÉVÈg;…»Ë‹gŸ¾³Q§ÞTG.:ªjm¶òvÝ%Ç{ƒŽsQû)Û¸ Ÿköšòn›m6‹scÔ£/9û£èûIÏkÄËkäæ#·¨/IˆžmV€BŠz$éŸvi±b…&Ë?Íl­Bœ¶T.¨.@%>‰Yܤ“Ó$×–Ú¯¨aÞ(©™N«±ó³dgž];6Ùשèû¢T•B}0ãK^\"’â™@úN¯|¬øŸ m21^Õðn±íP•ÚÇŸknÈC¨)ÂsÚX‘ï«,È“xÔëœÃ!Œ³¹nn[Q8@æx” 3ËÑʘ‹—\q–JœO ŠRTžâzma^ Ým3œy µD.)\¨v¼<(H8{騾Íú $5Q\I Ù8xÿu\•î5®—v Àд«¶•½Å1%ˆãNlPêŠA>Õ¿ßC-²Ç‡tµÈÜ­‡tÇuIt¶;†ya÷RÈM]T çœ¥¤·¦„}å$u`È}¶}iK¼?Úð.JDZN |(A¯]­ÔÇ]ƒ´píI'ßœV‰Òº³TFRînǵrAâugÛé‘LùžŸÙÒ ZËT1=l-˜ ã:¾3!ÁÈ6 uÆÃ•zK,¥¤í¹<Íx¼í­ôûeËuÂCñÐ Ä9 #Áð¢M2ðûpL›ìù“!×!¸ÈìÚg$ej)ÎvÛsŽ™©~µqèËß;ô¡-^xozMÁ€χ'ÅQZÕ×*({N+é&îÏßš±È¨÷ÔÀÀ®iÙXíR…MOÒ¢j,>=?uJlqlPä’£ü(IÖ±ž-`Ç{qWÅH•f5[¢ŸßL)½Er–¨ ÛPÛØkÈìR^sÉt茩ai”¦ÜRFJ (íâ2üP{é÷Í9a±H·êVҮώ¦Oñ ŠÊ—d›%Å6dmaÖÿ‰9Ç¿á!—_KO­M ÝHωEz¶‡Ð-¶Ô¹W¤)!¥%qÞiÜ$§+J†ãêò¨1Œ™2—›h¸ µç‡'aœ^Çz\ôXí"ÅÎSI‚]+ 69ú_WòÛ¨©G="Ïíg”)ú‘hQ‡ öqŠº«)ø'*÷Šõ`1Y¶;<[ ¡‹tA†š«ªÕÕGÄšÒIæ•#K9¨Ò<©µ.F ‰4;<(ë;jvϘ¼T3ÏÓEÐô½µÌ-³›{˜ÿÍEYé[îq„z;Œç»­2¶igìšéÒ àÃk8M Ò§´×ú ÂXFq¶ÉQÝèT5ª•‘…KCc 4sVúCÒ¥Mš‰ßjbp)–´6…8â’”$¥(à9“B­kv%ÝÞ ß"G Iqµ‚Ú¶>ªq¿é·dÔ¢¼Óæ¼Ú^¬Õ Ôé‹l†Ä¸ì«†Jk‰ =SÚ¨Œ¨uäk?®Ü¶Hi«¨,º®º—›{Å)<_VüÓF$db£·:Iq.´‡r…€¤œs 1äEeRâɨ­\)*îÞ V¹ä(å{›«..Ùtû…¶<3.pØú¨ïQïéZ“RלÚ¼\å®ÛjBÓ%Õ-r^QÀc‰G ‘ÈœoÔòfŠÞ“`òim5Á>úâ}2N xQ?dnj– ÕÖí'oUƒJ`> ' Å×é+Çé^\ã«uÅ8âÔ¥(åJQÉ'¼šº4ïZŠå¨&yÍÂJ?Ad w%<…foì¤ÓN>â[i ZÕ°JFI­{}¹¥È üòRˆHe ù´(òãPõÙOÆZ£Üߎúá«Íâú¯ÊZƒhHî+çþϺ4¤´Z¢*&žB\[êEÖpìÙÛê#Ö oÝÞj¬{d^¾ãr"[¤p•°Ñ?A¶‘ë/Ãë“ËV=ŠB'ñ,Jí¤•>ÐrB‡BÛ(lw)tÜðÍn| îÉëíÎ}Ù—TA[ŽˆñS·4 V;±š2µ3,$10æÐ”p wÏÞyÐsq—ku2$)»{®å¹ç“ýÄŒ¥>àk]»²`¬6¦×nÿJQz[þÆÓ’=øº³Wo°Ô–ËO´Û­žhq!@ûOÐÖ© .ÀSö©=‚²ØÏŠ9ÖnâKèmô\pe :GÖ l‘ÿ­ ÐyÂå«ôb»K‚[»[Ò›O Ò>Ö9h#Ɖôî°¶êüqÞéþ±•ì´{GQâ(•m¡ÖÔÛ‰JРB’¡AèkÇu®‘¥&'Qéõ)¸í¯+i;öõñAäGLÕ—}Lÿ`¨§=£µ ZŽÆÔ´a.§Ðy¼çcòê<(‹Ç¥,ÁÉc'ÙBì)Oé¤'ÕUÙ­ý€ÿ:/pããAÚóyZg'o•›Î=†µ£ž2{êi劂1Å·ut(])*‘¨‹ŸÂ³dI!ÿ¢#%¿yVkDd\™Îäž¹Hû«--f›=*+Z[IR”2I8P»Ú¹Ù¯)=oUÄðªRÕÙÇIýîj÷RM]òÿ)¶7ãK©íÙCÌ©!å#šHõWùuFkU9ó®Ü`àïÙ& þ.,ÒnéqqM¾û7›ÌqMG}•e$à……rÈüªæ&¼óP<Ö¬²þÒÃÉŸ8*(ô‹Y>ºG<ŸùŠó¹ð—O›¸’BAu$z„ŒðŸc>9î¯Y¼h+•’àoQõ¥HÏ`¦°IÇyëכּs¢}êÙ¨Á¹1<[îQÆwâIÁÿʵ|eꂜsÞ¢‚1Sͤ©HRçQMMšGjX¨‡§ëXˆ.þâ(‡¾†îêÝŽF ¹ÿÌEjzQ åL­ÒGxÅ>yÓgj€;ÉúT[¾ÈVýµÑÜç8À£<ÐÆ„m)ÓAÁÍé/¸vê\#ò¢o´>v¨-Ä q-A#¼œTˆ Ý\‰W)["=Ùº–;toŒžÐ¿î…}$Ô·Ó.“5V¢ù ((·7Á"HmsB~ÑÁ>] Ïòd! ”çh꺨à=€ …u ˜ã´KIC®%!j@ öUüêݹȒok‚KHài´6‘’0>êżé;MåÕH›+@ô(…¤íùVŒÛ¨Ž·‹ ùÒáãi‚Pã%Dš…®÷ê§™KOG—€ôi áq¼ò=Ä„dVe«6›é¥o1Ñcvk ¸çù Äì‚rR¯iêJ-¤¬p¨p{¼kRÇZ­rÊ!·5²Ò‹±' HॎTÅÒó&ÓGÀ’‡g­¼¿9 QãÓœ€BøN7ÜmÔíoSñ©y¹IÕ—7týÞÊ?ö„ô’?»Aï=Oó ½S­£Á†tî–<mÉ ŸIÞð“ÝÞ®gÙPÕºŠ%²Ýû-§Wí¥HIÝõý!ž£<Ï^\…yêÉ;Ñpвw«Q ;-+pa¶[ÝÇ–p”{úžà75ÚÛmL”9.[…ˆ œ-Üd©]ÕGîæk¬Ë“rB‚Ómœ1;¶ØêN}ež§þB‡B°ÓŒÂCéŒO Ž¥›ƒíIg>ÜÖ…¾ä"ñ¹„²àôYpöcœr$÷†ø­ UÍ«•¸C½Î-‘Ð #°Ø xfäíT8bÛž7VØC¨.ÿC„ùãÈïXÛ`rϲ²¢9kfÑW{‹îGÃ–ÐØ ~Y;mÂ3Ón/ei£WM‰9PÔж5 ‚”!žÞbóô”>±³°è ¹y—ò®fÌìæãÉ4Æè¶œœœ¸Á«v曺Êí¬KWˆYCŸÒ%gšYÙð÷Ô}·šºùûb¤n“ÿY¹Ìf“÷xWXåæTX­*ÞâÎW ó‰îõ®ŸE¯y¬»< ¢U*$7“%ÌðÈ\Wm I[øÎ>Ê2{ÊyVÌwÚm¿1!ÙDÈßfÖG0·ÉÉ=ç‹4V¼~Mù·—ÝÚKHs·”ñïZÎäûöÑ /©ÌˈGBï¢O°søÐ÷uZ–ëÓ+‰O[Ê\q=ÁÇÜôRO°š™«_ðµ!2mZ’>à)E4ÔïºåÆß¿îÙŽ§OÅD~ìWiÒÔÓÿ`ËhüùoL£ŽÄÔÕ FyûUÖS7KŠ¥ávÉym©)EA\;lsã^ ò̦§Æ“÷r3a ­“÷’vñ¯aÕ–l²ãÔ6ØâI¯ )ÜsŸº´IiÈú®2U.U^Ð%‰ 8$‚¦ÜOEà…@~$o(w² 5:Ú„Ä<* %hî ðð¯8Ъ’ ¨í6Gž¸Û º’Û™ô”“Ð¥²³ž™ìò,–‘ifÑšTv¸RÊVq…rNE×¼Õ™‰XwÛü°µ3-<.-¾£ ö œú¸<ö®Êµ[Ü\»œ©’-þp–ÞJ™”¦Ô•¥×¡Ú¨éK…âõ-ø÷¸mªâ,zÍ–Ô¤/©éÏsФö–¼Oò’.EÑ¢¼…¡aä”´Ðæø9‚F|7ÍÛ6«Ó¬¦e›V®TU )‘)´º• }â®Øu™s¾K»Â\cÛ%…!Är%$w`ï\¬|“ª®–¶SˆO¶‰ì rl¨ð¸è2Ç6¨¶N~d+¥¶Iaèl¾B‚B¸‰H ÂA§øQj~¾›f½D¼EJãÈin„!O4…d´TœàÖŸJÅhÔ©éºT{èjá¾½¶ƒÿpwÿQKC²| Û{¾Owßó‰«=(œnÉv ô ¬ ¦]FÜD øHáɈ޷ñë?^ ÒG¹t®•‰sbç9–—i™)Æ{Wš.|=ç5¡ÉLVÛšû/JHËe<)W ',XĺØd½)nFyÖÓ"{/<¶ìÜ ð†ÇÄ]¤[dµ©,²£¥n´ÓO±)å¬p ¾·¤+xžu‰~½*ßÙDŒ[óÇÒ¥¥NúŒ¶œq8®¤ €R@¨ªúÆöåšØ Dö— + Doë8y`ç^a¨nHÒ—,pdv·yƒ´¹MϧéoÂyϸxšÖ¼\ß´Ün7¹Ò\˜mçÌ vørV®²@`tÍyD©.ÌãòSŽº¢µ­\ÔNäšß“õÄå[U£2R^’K0˜?:âFVâ$$uQäNf«ÃŠe=ÂVm#‰ÇÙ´ŽgôN^•srª9aÖr†¸ŽTÈ<ÏûEu=9 R*¬ë‹ïÉJ×`ˆùC áþõwždÔ¡Áq÷“”-×sÀžg­BØJmlö’œ 4¥/$õ=üë²’ìéXY!G )+0yàÔ«¸Fów’²ñx¤p8´à%*ú©#ž<*Ý©ÒÚÔ·œ6$- {gaŠê¶˜ù ùŽ!×=Q-§®:ñ¬ûwk\6ãHmqX†ØR’GáPlI´±6T{%±hv@_Ùê_sÎyp“Z‘¯­®Wȶ«|¡nâR#(vòÔ>“®U$ôî¡&.‹\‡je…7PN(rýޛՏÏN4dÄZÑÂVÜ$¬¤¶êºùsöPɸ5%† K‘ ˜’”ÄŽµ"#|;«`åÒ:žµBEâlÂL‡b²ód¶ZG ‘Œ%-¤a¤òyøÖ ½œ¹£¢eQÛK - p¤pƒÏ'ó5ÅÍ`c3 Ûa7ÖÂÌž#Ú"FqŒç}€ï¦ÒRÓTW®+m¨²¢ÒsÄÒq²I ÈÇ=É'žjƒÚ©)›ÚÆ‹µ/µ$p> Ž:ƒ´==§#Ê[/á\kä €V;†3Ò¦¹QÕkŒÂc!2ZykS zé!8¿u\FŒ‹ÜÉ )0ä¿Ù-*uævdçйãpGvÝÕÁéóuO¥õIm–Ëd)Dú'šp*9¼ ÍíVC€`v‡*Â@ëœ{<=”ÈuÖÐâPµ%.'…`”3œxAÍX¡ˆNÀ[ ½"W":œlq:ïq‘È• ¹æ»¯Ê鋵¹ˆc±%¥öI¡+P8À>‰#–wÍZ­Ò.³[…!õ%´$œd“ŸÊŒšòEªÛ'ÌS• þx|yoî õ]1ɺ2Òó„©}€A'¯ )p¬í~°”ØAu`üMÙíMY¬ðíÍ¥†Â3ŒqdûÎM ùDOj››—F@ïçWå¨;}M# ¨ã'¯3ŠèžUš¤Å#OLjð¸^›} Ü å îÖî(kI;Û?~=×7?YtcIÓ—=-svç§Qç9‘oÎøû=øéÔxÖ£w–.w+ ÙYì–ëÜIXÂ’âZõ:(dÑ^6¬ ý™§Ës#8˜×$º…0áõVàÏPëT3Üi o1ÈšN`eD?'†3XçijÃ5¯k€Ý²× CÐŽÚ[áÿ: ºÝ¹ÉnLتÊL‰XPXqñê¡lqœøgŒ­3:Oñ ¸¤Ž4¥A\ ꓎îUlàºJƒ­6â \HRO0¡k„»¤)â—2;ýc€U!¨áH<0“"aïa•ÿÀûêAÖçl·O¶9tv܈ÄR¡€œc»ôlÓZI«}ªâíµ×Urx!–žt«‡!Gq¶Fß[W{¤Ëô§4äFÌu¨=t¨2ÉÆFßI\†3Ö²®M«Pê(¶«4£5ž:²ûC<+#„!>8϶µ"7nº®Ã¤aö [aäF$p2<06Hñ5âú›VOÔó»Y*áeæ™IôP?3ãYÏAžëòBã¾·[p¡Ï@’•¾|j¸iM¯h!T‚Šö= /Ï<›ö jáIW±À¡÷*µÎEÄn&?ó§Ó'çÈrúòJÓ†Õrƒ";¨²Ué ¤R;ý€ü(úÄŸú8¬mÚ>òý¹qUk1]z}rD«”ÇsÏçÀ}ÕEÍjZ»RôÀà9 ír¸ŒQPÀ¨)[cë*ó Ô'ÛòeyiÁó­H°sê­ þæû[RCj[²W(ÐÑÆáð$ìŸnõí·f;o'wUû.:sÞ¥qÄVTm+ I»Iu³Ž6 $0Ê|¡ÓÚ¬Öâ1´eº{š¥ m¸íǶü×›¡ÒCiZrH8ô•žg©r¯BÕ1å;§Öä6˲"ºÔ”49¯³XQHñ Óá«V±”Ô×»ð Ûý–dl”å ùóÉÆs^„9Ôú¤ûœÖçØcNµ¿³’£­*FÅ_8R¯d{ªÄ˜’-÷³tˆÙu™KSIô½UÄ÷ u¹SÛ“dœóó­ÍÇqÐéCËlcçHô‡=ëWŸ]þáPcÛYvEêEÍÖVÒ A†‚ƤñÓ'çãZ²ÚËधqÈçTo NyVø°‚’…ÉBä:¨Úâ }h¼â[H*úÃf›Óð¡Xµ[n×qä¡rÔ”3é8=5㧦¢1ÜP&‘·Âvý.tÛóh…ÆËãO¾³•œýT‡MÎ(ìTúõajsK•E5 ÜWÃåÒqÅáþtš* Ë±ÿÞ•Œû“¿ïUž”f9Sxà)é+'ÙPQ±'‚Çw2+C­±&íæÖ© S‘R›ƒqˆ“€â2B–®ü€p:š:ÎsÝ@‰ÎÕvµ³uŠp¸ƒÆË‰QJ›_BÜ{«C™çO·:¨jåp°0{ì“$…H [ËW¹(öšÏ¹Ê¶ßÞnd[ìÅÄHRmÒ8T³ë©ÉysåÈáćP¯Tó¡MIÂ…¶óöÖšé!·A-+a’xÓém·/ Ôêxâ‰Wk ¤²åî%Õ•z±å),ÊÇÙ#ehïªn+†¨»¥ deFJöÈG‰yÚbÝ}b+·•<™Q€Ö•ÀàG0£’NÄçc×jÏ׈DSït˜å¶]a.68”8Pê“‚ <Ž jf¥Ð^¿¼Á¹º†âÐét»)—[)-=€‚Ò8Gã_@ ` ó¿$Ö/“tò®.¤‡§+ˆd`†Ç«ñ9? ô *ϤÎh#]¬‰úgl©WVÈÊ5_¬=”¬«´ƒg|ÍR±à«nñﮂ¸ ì‘í® íR‡¦4¹Ò5‡QBº-_Òu3êÜÜ?)&ƒ´zÂu.ªc ˜=ù¬º 3µVœÇo%JBÒâ8NNEYô߸êBÕ…»ˆ‹éT§–Gm*â!$s$ÕÆ¼šYZ·Pô𕫈 H8Ï·£L |SSPt}ŠÜ°ëæ”îsÚ=—ñVkUîv”¸–ïÚ±6Û+JXæÊ„ü‰*RøÖU©çNÙ!C rå°«…¢a§­¿+|¦Ó™dån2µ#´?h…{Åjpq¾ËôƳ¹3h†µÚ&<ò‹h}õ°Yi%Jn-Ï>œêóúÙµ®[ÈŠ˜ûN6Ó>— $)JÁáW-°r{êt“¶ô«u5Àz+–"¸OZhæ>—f”œ•tú¤½jõáè‘-.0ÜçSoC–…¡m’p@ß¡®ö­ëWænï5 ÜëkãZ-ëqAÓãÅ€꧇®¾N4Û–;l™%ö„Å…6ÃÄq!a%@rQæGAFôÀSÖoZ.tÝ)éª4xtÿ¥‹RpD5ƒï*ý(àÐÝ|>Xl žqåûõg¥ŽT”=ì¤9R;$÷TZN«ãéÔ79Ž»Ù¼ò"Eä†QF|NPÞ†HFšFTŒÿ檉 /¡t¨“µ9Ú›#5QÎÙÀ Ýoj»I]˜. Ô®Ã:“ŒãÇaµ¿-…=%ÃPÅÓYKPiUä ¹³Æ”ç*m¤à'=ÙñéU[{Íí²\1‡ÈmÈ‹Iâ9âíßš§-kqçujZøøº?Jâ\®Í9+âãëË—²‚nÉqÖÚBÕ”´žáDš7W¹¥eIsÍQ!—Ò”­%\*Î>óBÀxfŸg‹9ÆØïñª µo”IzŽ!‚Ãi X+IW—ƒœÐxPG.tºÔŠq·3Ö¶¥R (€~' ®€z8Nb¦b¶¼Ò:Ÿª;%á¸p q|y×|u¤1šUŠT‰¥Î94Ù jóë¿ÿœ¶lÿÜÉÿ~½=+ϯ Ç–;ÿ¤ íßÇVz= <©+d“H…%@û*?¢Sçúò¤+âꨈŠÑ$0ÒŽÁo¾  è]Uf”6)mKˆg¥,ó ~¦‘éMO7 XÍ5=#P5Q»ÅLûDÈ„d<ÂÑñIz¢®ñT|ŸÕE’áÂ8Ò—’FAõ;« ޶ӯ•¶´ñ!@î@Ø{°(ŸÊN›rÅ©]’ÓXƒ1EƈäÒO´¸Ð«R‹mð)¤<ƒŸ[e ‚1žï Ú9‡ä¸ß›4TáNPžK `f­ÄB”ĉ¯q CxØqcÜj»/8Ó­&:‚žK‰[g‡®1껡I2à ¸…+9ôŽÛ~t ‰Ø†õÕ¤´Zt¸ê’â@àHFB÷ß';mí®@g&œ ¡ÁÉÚŸ‡}ÎiÂ@šD„òöÔ9NÎÇz%Gào’h%Ëny«P ˹Ënûî%­]7£¯™ð˜lFÎ%ÍŸ_`¯uÒš>”‚[Ž;I.]!C~¸xTÑCChXú^(}î®n§çè€~Š3×ÙFXÅG‹Ž{èš™5~ÔJ,úB‚u Ï”m1Ð>G‰á£Uzýûr «Ù.yOÓíƒý\gœ$ø‚?*ßË4fßÐîÞ»ô® ú¨ïÍX«5N9Su§¦ëPˆ” >k˱·koßÇùQÁ<±¾ô =]Ÿ–nøí!”Ÿ‚ÿJË øTªmRÓŠT¨—}?M¨#J˜Ð6kÌõÉvÁ­­Z™HZá)¿6|¤g€ïøƒŸq¯L#z¯.k„W"Ëa0èÂÛXÈ"µ.%fFƒg¹Í~e¶Ý’ZáD„ži?ŸßZàq'ëÎ/zZv’µ\&X.$@-+µ…)D„ô¯­žþ~4@L"JÐØà Z”p¶äžƒTgj&˜óf—!Êýg°0zx’G¸Ö+6]ùÀ™HbC‰ ©Å$wƒÆH¢H×;]ù—ÚqK¼˜îaIî>þúR.í…¨«ÏÊX=›)n¹ŽdòØwœm2´¡µÆrD=Kv†Ói*R\_œ 2}zVm¾Ûv»O웾²aíÈmöíÈÊÂò3±Àç×4I6á%«ÄsJ"NiA ÎT‡“é+)Î1Üj†Œ!ʹé׸ˆñ7Â1ÆÂÏO»`"ºN{HÇ€ü9}ÕäºëI[´Âv&<÷n’ãHNPœàcÄïð­J€ÇS¼I@áHHáHN@ïï>55¥"-½A)…&T‡Ñ1ÂûÇð­£S«çJes¸í…’”qdãz T•ž•&YvCÉi–Öã«8J’IöνÆ‘»$*DÉÒ1ÍêÖœé¸_˜²ŒÍ1ç2ÓY9Âì*É> ¡8Î|¡å¦à§S€Ûg#ó?ÝÓÒ{(¥ ÏB:ž‰òðÉ­ùö¬jH ¶®5úì¤EZçÀq¥(%HW†GqwM´$¿p½9…;-õ6Ò»˜l”¤B•ãšÕ¸ÄLûl˜‹Ø<ÙF{CñÅQÒÍ;LÛÙ}¥ŸM'¡É©ºf8jÔ0€ðÇ7ÊI>+ ?r Mím~X-ržÆãÇW‰þ‰¢½HBÚ·Å?ÛÏg§Dáÿr²µ@î©Ò$œ©¹.¬Ž¸ ñ¼ ³À\Å8;ž”Éõ@ëŠqYSó§çMOÒºSŽt©P5#±§¦4'jbsITÛ‘D s^töþ[ òˆ3ü½¼åIÇ–ìçœPûug¥z@;WŠá… ç“j?q®Àæ«\Ž-’Èæ^ç÷MEay>_†¶žxJ‡ùÍB¾NAמe ?*ÚÍG~”ê;S ¨$ 9çÒŸ4ÙÛßOŠR¥Ò•±LM?}5Šb6§¥A^Aå2Å{:‘7ˆ1ü7J KAÀœg!Iß¿º½„Óú£åÖž˜§„eBR’½‹1ÓÙ èH#½›Éæ”™a3çOi¨ïL)ŒÑÈi¦{ò~ê7ìÑÇÇÀž/­Â3ñ©wåWuSã~fëNyT#¾˜Œš‘¨æˆXÞŸÙ© ¢=yS™éЉD1ÿ‘ïNM71Š¢zF„õÖÚUãȸû^ò¨°ìMk…†îZ]ìÃtHøŠ¨3‘®™È®Bº$ŠÊÓ¤`T*cU#™å@7Â[ò³§Õõ˜)û×G¦¼ÿPŽ/+m'£YûÕXŽC¥PªY¨©g§QHSf–Eóµ18¦ÎiPHnT³µ*¨T©>w ñõ;ò–™=· ÔÕe$ò<`þaŠ#¼´½3­XÕ J²cb5Àû#°JÈîØ|}tò¤^¿Ãf}¼Ò0òR‘±q<ð³Ä±5GíJU2|šé𒙀‚R©ÀZ¹L62§Úõ+ÉË„4d*Sã8í/ £=:1–å9 ԿѾš Cs8çÛ+¾œFጉLà¾poN™±rçLqC´NW“]9Œd'¸¥õ=•ÁÏ%–¨).MAðtÄS€Ý/6³è¸ƒìP4¨›Z|¡iW†Á]«g>?g«ÉL籺Íl÷“úR¡¤Úoö¹ ½J–ÓO—‡xSèœõ Ié^ˆ Åcêâ޼p<ÑÎ~Ê×NÀU+ÌUNµ?.©µ);($+ps‚ÄwŠ=™=•ŽÞ‚0DfÆ;½Wx¾4t¾¦ædë B·"3®ãFÊ8íuUñÁÔÂsTž„Ò ¾~.tDE'ç.··Úž­þÍZ̬vâáûsœ?NÃì? @’}Sð¡Q ìdà&bvcƒó§ý€±óh<óçŽgñ©ÅoÜ~ã r? V‚¶+”ëÂrg® ¯'v…zó.ËýéÊ4à/RБé(iÅqTÈÉ82Y×úЉòg§¯çªjJKýéœÕÿšjñ*¸B@ʦG¼º‘ù×#w·'e\"þÝ­ £ 4v߸gLHá$—ÏðÔ½ÖŽîÖJúgOç^ŸÙïžþ*nÉXÇnï´+ùTâ¼Äk=zTöx(žhàüé—®uÂNU§@ü«¿­zqdƒ³®ï·¯NÿZïñÕØ0O”]TÖò4òJzüË©ýjgÊùlð¿cZT98ÇâšôÎÄ‘»Ïñ×íq%cÎKØåگƜMy^·º=;T”þë¨?¥f#SDÔ~S¬rb²óiCjiiy ¯HíŽb½ Ý+csuÛ#þÉ?®Li;$IÉni—Û9BÐ0A«1 äß]PVì ·1ûæº!<J>Óšœi”2)‚Å?ªŽý/ß4©W7WNú•*U(cʦ)R nê¥Jé»éR¤ ¡¦©Rª&9cÌÒ¥D%}*Ou*T ­sý)RªŽƒ‘¤yŠT¨ùSt©QHó¦èiR cΜu¥Jˆæ®u!ÌÒ¥QQ4õžúTªÁ1Q{ÔW²•*D¾Ð÷ŠŸu*U«ï¨+¥*T¡'uO:T«*tòëR¥T1ä}ôãÕ¥JˆaëT¾’iRªU†•*9ÒiR C§¯¾•*¨~úCó¥JSõÚT¨"¯[ßMô¶•*ëRï¥J‚ åOô*UPÑöÔµ¥J n¦¤9ŠTªÿÙntpsec-1.1.0+dfsg1/docs/pic/howland.jpg0000644000175000017500000003435213252364117017475 0ustar rlaagerrlaagerÿØÿàJFIFÿÛ„   (  &"1!%)+... 383,7(-.,  ,$$,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ÿÀê×"ÿÄÿÄS !1A"Qa2qBR‘¡#br‚±Á3Cƒ’“¢²ð$TdtÑÒÓ4DSs„£¤³EUc”ÂÃá5ÿÄÿÄ!1AQaqÿÚ ?¸†Ã§–7¤y zŒl0<¾Y—Q¬¨@Yü1ˆFÁØ~Qn€wÇÐÏ,y ko!÷`ð²6íSZÇýªUü¨Ç&áXíaQZ-þ·1ý¬qC·û±°û°®¼$ª«Çþ*SûIÇ¿òHö¯¯σûS úû±šÂâð‚÷«¯'ÏÚ¤ýÛcßù&ƒþ³]ÿÝKþü\Cä1½È}Ø_%jŠÛùû\ÿ°µ¿ {NQÒ¦´âd?Þ¾hCÒ<†“ät«¬΃ûTãFý«jÇëDlG 4!AÀQ’KþUÿ§ÿ“I’Ì?ëõ'õiÿäâà1 y» y lŸéSFùxè(¤ÿ·üV?Ü£ 53@òÍ#È`[euªäÍÅÿ=²oô¹«‡þ 0Ñ-#ÈcZG—áo•T¦J?›‡þ qlž§µ|ß8 ?þ 4h(òÍ#È`ð5WlÂOœ0þà1¯àz¯þa'õ0ÿ» 4{@òÀ£Èa|duvÿý©‡ýØå7Ö°°Í%Sæ ƒýØ`eÒ<†4y 'ž¯ÿçÔÀù‡æ©%CfÒÉjÎÑ´kâÓ¾›’@¹îñq-X³(±ØcxU¨Î¤y$†œ­¢m/<šŒa¶<° nS¨êna†šcè>‡’5â?÷³ºi&CÐ|pëÞçfÿÞ“ãTAÆVf±#-ªK_–€»Û±Ò =M†1Ýê®Ìpmk³ËæýDí¬nwÓaf2)èÒÓ¹°ê|ÉêO©ßÄöú†÷)ô‹õ™ÂŸŽ”Õ÷>Xê±U·Y‘B!û]Ûöbi=ñÖ;ãRþ&œº õ¬˜|œ~ØŽ=®S7zº“ó€~Èp[XvíóòÇPqwñ3ôåRöª©g?¶,zZ:…éTÇþöØåèý¸/|h`¡EëêÓËóxÿ°~8ÓfîŸÆÒΣíFQ÷!×ýŒ8ÐÀ›‹èÐx¦ Ý£*ÂSè±Ìcèã€Ïj¦ÿ£Q8^ÒU°„|DcT§àUpÅapm¸èq¼çðva%ùµ©ò¦n?^f{ÿDc×ù(¬?+S[/Æ¡“ð‡@à ?hßü ü£ïúSLß¶LzAþ‰ø­ÏÞIÁ°/ÜŸñéŒÐ=0-F=ÚpŸ Y?ºÃãÈ£_qêáQ)s»Ã´/ÃÃ1if_v¥¤±ÆÃûãkQP¾ôHãÎ7±?¨ûéâyoŽ6 樿Æ+Åë ²ÿLŸÚÄÕ{‹Ž‡§La>‡îÄËcÕ óe'Õ€üH¿®©ú½á…ߤ 4åÕ‡py/ku½¶üm‚I\Ñ3€A*ƒ “Ð071›ù’Þ+›`WÒ3[-ª?f2Ý>ÉRf,‰]ò­`¢Ž7²‚×ßS“vc~¤µÍüñ˜%€ÅqӷߌųºÌ½$D6,,ÑHM 62ÔI Ø€Õk±ìtêÖØdQ°øaJš‚IhdX˜,ÑÔÔ4wé­*¤e~„€¡8åÅÒ˜s:``‘4CA£²²:©µ…†Á*¥^Ï*„Ž9¤nbêv[1>=ÉÚû› ïŽù]d9;†AŽx[Pdn‹ßçÐÇ\N“&…¡4æ50Òc·‡O•¯µ±®…TQÍ3êWÕ=LæYYRö™£[$q+2±  w¸’rŠHä‰Å\’Ö³¦†ö‚Í#\yJtrí{ÙlølÉøZ–˜7&%رg$}’\“§ózcÖSÔÎϸOȸÅÙ¦ÚÈLÅÜ™]ì5L@Œ“ÞÛ(“a‹&ž¸G:z˜ˆž3LVY£"Ö$¬?5¿¡¸í„>)i¤z‡ÓXá$‘CÉP)ic Ä 2Xs~·ß 敱1’¦~[+xiÙÚMjŒê¤µƒ\)›v¾ihÔ×FÓÑS#M)!'©içÝ‹$bñ Sr{|0ÍsOÉ@*=ªD1B©ž:U<µ»M,`»17b<ˆÃvb´´tÔôò—™WŸ³#Ê®u’©a¨òMÝnÇ¢ï†Ì÷7ZFC4v¦’ë,Ö$#øBstB.5 çˆu“ÆÆ* hå¸:‚D¸,Ì/á¿…UA$«ôµñzD8ši9u0£éŽ7ZŠR¥ f¸±:®I6°a†¥RºþÕ)†D×L$åLŠìfpC€ °Ù‰¿iyŽKI HŸÙ¢j‡,ÚÈ%µ‹¨^àñ[kac=Êêjäj‡¦ËcDÕ¦ZŽc’ˆM‹ÚËkoc{_ 8²ºšZ­#J¯ @ÓÌÐѪ˜ÐìWßcrm×qÓ+ájzXàG¨e$µØ™íÉÒ„Û{ì oùï 2 (£a•d,ŒZ4f N ž„(-å釼¦(yP´qÓJ”R¤H V÷Rˆ±é|$ Ù'Ui•‘¥¡Ö^IMLÄ-É$11ÆÛ€4ß¿¦x3(X`Y É,³¢<#–$Úà ì j; /½ºžŸâçâ„( € ᆹÔÒ¬ŠQ¯n ˆÿGb ˆ#sÅRÍM-¯d­°d‘ÕÀíqpGbkÆWüoþü)q¼š#}Ìr\o¸YiÚÿ"?µ„ô¾$ð]Qz»Åù?N#¡ÿ´¤üñ¬j† ]l_VN]Jþ½ÑÀýxõÒÆ±¼gLkÓ倜0Þ ÀíUQøÈOïÁµ†ð›íUÓþ™?íóÇZãðÉyEM$¾ÍU°g ©%QõfO­èÝF8ÃÅ3Ae¯¥xý´¦€ú’£\<5†xŸçðQDf¨"€êÌ~ʨܟðq¤vËsˆ*ÉÑʀز0 ^ÆÝ »#ñÒÕ=4Ú9È,K)#P7ñDm¥€"Û•¿n›¤ç¼k-p™ôi#½M¤œ’¢1)]ô1ué±À¿\.q•??3¨JxËê«@C( V^ÖØî1Q~åyÍmNyl²ÇuæF븱+¡ìmð>¸íšÓFái¢˜SN6.X]JbB™w*G¯l|é“fu9EjIË’2»mè;àÎi“@ï­ <ÂDÊ‚UC—"ý‡_¿ 50…«cŠz9ùn»«4e••ÀðIXØ'±]ªžx›Úk¢Ë Žž· [³³mâ~ÂÂ×73™ÓWK=BÓË<[ÈP¼¬yIp€ u7ÞþXM®¦&r€VI HTÅKŒ›Cñ•FľvßÏç—ð¤q‘RóÎò”ñË;o§R=­î -aÒç˨&¡ž¥a—0jÛ¹äÀˆ‚YA(4ÈÀ ‹1µðÓ˜Ó$yqGŠè±Æ»®¨ÇRþ_žTÔUH¦¡öxÁ´r‘»Ûë0[ˆQ}ë›±`¢ÝoFUäqTT™¼D“9UXWh”$‰Ï7rA^‹ÓT¬ab+°:‰²Ø\žæýÏ]ýp‡OÂóJUG"¥‹L[üÖhÓÃ*3X‹…± ZávÜÝ$‘âXbšG–i<–a\¯2Û+i#®å™°š†Vä¯×oÿ˜êüXãÖ±æ1šÇ˜ÄÝ\xÕþ,p‰Ä‡Úd¨qü\ 2µŽï$Ñ4öí²¬KñÔ;`æwœ»IìtdƒüdRO×~ÅíîÇÔ›` Ç ò)²þD]D€“v%æŒ3±îÌX±=É8³Òø—4EªÞAõ`XÎÇÞ.^Öô[ÖÌuɤ×¹êÓJOêÈPe3qœ^ƒá…î :–¬ÿ®Ô¹í†è0±Àå]º{}Wþæ8GZ‘ÆüS[OÎp]˜é¶¦±;žÊ¹?uÉÅÙŒµõüÊëÉ¡dÄ/¤,q´‚4¢¶rø¿8Æ#ÌiÌ2í¾¤qÕÄõêAÆ>m¨§¨Ë+‚xêl#³v*Ê~âAÆÇ˜q@žš©yI5E8YT¨åY&uF#ê…‡Pé¦þxº¸'„aËá²ZI_Å,çÞúý›ô=Îøù³9Ídªe–@ªUJB E"°ž¸°ò^0¨É45'Ú –›”6’ײ†;0ÚúM½-mî¢äβz¸Ìu,Š|úU=Túƒ|Cá~44æž9.‹#´%%QÂ6âöbÛ‚;c¿ñ=t|ÊiUÇÖ[Ù—Ñ”î0ZÇüP=êeOzqç÷«?!«eâXüÞl>²Å*éÓøàÕŽ<ÈÚEØ€;’l>géø²÷ZÊsüòʼnˆ)J˜­Oø±-βúçt†JyÝ}åo1qâ¢ã‡é¯ÿEƒú´ÿ‡v‡6…Ø"Hd`Ä´tŸ úŸ€ßÞU[’Ö©&ÃïÅ{&[OM\æ›Í‘x``«!A´A ±­ÁyØ\-Ë1³D¹ ©OóÛ=ìDHΩfg`mã6é°]ïÕ±ïñÇ­8M¬Èa€èŠªµ$;ˆã™æoމC€=ZÃׇ(ÌHðÖ²ysã…ÏÍboç§ü_§ã÷â,Éo‚4‰' yÅfiŽ\<]ØI#°ó%B«[ôCnÞq²ƒÏñÂ$Õ󦸞…–ö¸iÚÄu\G‘±Ä„Ë3w¨¤Qù´ò±ûÚ݇A€ºŽÿŽ0 ß³ª)L´µ5®"Av°HŠ®¿¹°¬™Žc^-– ¤¤=*ª.d“ó‘íoVëæ:bì0ã[›,gJ«Ë'ýœ ±¿“1²'ÅÈÄ6Ëë*Ž”RÄzÇ•‡“L@ ·P‹&Ânem”(ª’°×Ó)øÊéeRlY7=>#Ôw^_T³D’ÆÚ’EWS¾à‹ƒ÷bfS4b8P"u°&䞬ÄîÌ{±¹8ÆgM0>SÓ_õˆ°x©Âï=©”ÿ¬ÒýLXB£ðÌá^®•ýø§yWz)ØÈŽ=53/Åq˜ß𹜬ÌÐO"9SÞ Þôg³'pØî3ϤÓt,*}91Uíÿ_ªôþS h6Uú6þ.³ý¾«ûøá)·/Ç—Ów s©Ådj9´àë·Öˆõ¿žƒâøÅŸly–0À‚XƒÐƒÔj#ã…}ZEü=>ë·øé‹*~&59tóWRSͤ)j z$wéÛ{ ñ½öYã¾9ukÃÒ&»ÂÄà=½J›¯Èøu®Í)¦ÈDóQ¬b;ÓQªÊþ"têck^̺逸ðV_Y,2+Ã#G µ™†ø\zöÅ·–}(WRIÉ­‡ÚB®²Ê9s°ñ:Zèق›oÓ _BùRKZòº‡дȧ»Ü6ôÜülq–ª·4ŠTNk/>­Ó–÷ë¿b¬ÊÀb‹Ï†~諈Xå !é¾'É~«~©8•Åõæ6£]­-R©ˆÈÂÿ¬ª~XIã_£š:jI¤¥¤–j†ðÆ»rËo¨ ÚÊ;ß¶øK‰jªg¢¡©š’ÊìnúR5#‘ïqc×ÏãìÔ¾sÊ8 Íòµ§Ž8œ uˆÀQÉÒT×ÄM°çÄsT­;û"+Ô*š•u ·¢‚ZÝí…/¤|¾9 iF¬šXÄöte±æú¨ª¬tì = 8°@Å©xG†V†-72ÌûË3µÉù-ɲú“Ô’u6c-K´T§DHJÉQ±ñ Œp‚,:3›ª€c}>¸²­ôÇOe’¥ùzÅ®‘…-+ö!öf\¢‰!R4Ñ( GA×°ÄÃ@³|â—*AVi%'LiwšfîMÍØôñ1òC2~3–GsQMìp*×4‚äÌ·•Éò·®ñ>_Z¹“TÁNµ),¦UãX©-u½ýå¹ßµ°›èù…R*ÇTäDbʱFêܰäÞÌw,{é¾K† ~”B”ÑT Wm+; !¿8);õ¹»bÂÅ_C•æ5¾Í tqÁK £0VBÒ´~êØln6Úÿ+Ë]uUS T ¹ÙfUÜÅ èn.ú©ô¸%(s–Håº 'ê•ÕsòÇHj‘܀Àýö?"DÆL²š‘.=¦GƒnЬ’-ä9K Ï`•ELsy¦u>Àzh[a){D£ºý…=·=pÔ3æ,jA¸n„X* Ÿ#ã]±Æ°Û—MƒRîWêF¶"I <®OÕÀ®!Ñ ´j¶UÀÉB¹rTÎQ*)ª";‡ØötØýÄ p'ιu5-<ú”B$w$C ÙˆWa¹pð.ã½°ÄÔlñÓ4+(_—ï•DTtSÕ k®­Îà È ¶O4"Å\¸À@¿{zHbw ÍÐ\ºù;+ÕpE]Y½udÒü”L!Œ~ª†Õó7Âïp”™\jiejT–)'§vײH¬2@µˆ±½vůO]Í{Eâ@|R}R~Ê}­ú·M­¹½³8¤C,N.²#¡ø08½_„ñ¸+hÄ‘ÈꬤwVÜf+ÿ¡lͤË^'¹jy "C¸±,f¦âÈŒl>XRú4þ*¯ý¾§ûømŒì>XQú1‘ªõ¯©þþ8GSŽ3ŒÅB¿p¨¯€i*•Ý¡‘…À$Y•¶>cÛÒX¾xֹᣒD%lT;¨»G`%‘EÙ³ »_{[¤ü%“Ö°ŽÆC;±Ð ÜÒÆÇr¬/ÒýÆ7XÿFÆ9Vé’kÇ˧Sx”¹rŸ¬P±v°µÍñjå´•vj‘D‹–†;€úˆ-tbt[J‹\Þý­„@‘*Uf<‰,‚œ¥T`oÏl’_ɵí{½±rÂGÒ­'±VB¡V’D‰•z{<–—à¾>w¾-!c6`3J@Ö³SÕ¿w¼ïÐå|Î2Ñ<Â/™&°×PµþXƒÆ U¼§åTDâX%û2ÇÍXxHò=ð3!â…© ISª–°)Y"k.®Åáb,Êz‚7Ž*Üψf3ÔTÆË¨i–Aï41ìb‡È¸–.ÜजNÐO1’tŽ®UÒäx£¥‰nVž5ö,|*|ìogžL$xD Ë š¬.ÚZþ­çŽœ'K9‚5°d(_@.n,X¾›–¹½ñŸÔÕ|+ž·)­¹wŠ¡õ!mäæ4ŠÀwgê/¹ñ“†z~­”=\¯¢¶¡91 ?“¤…È,mõœ(¿«=Aº.¦é™´Ò«,JwP\‚d7ܾÇÅê},b,­V¡ê51‘Ñc7>ªIGArI8™ReôÙMò“Lq#;¬ä¬~³€ùü–:P5ÓÒ¢IeñÍû#üqØß1™HÞ†{mQ*›¨_8Wè̶S~ÓÖòb̦?É–oèÀ„a^~{OUÿk!Hÿî¡%ÞŒüÇýaˆüu“½B@ñîð̯§í#^9Wú·còÄî£äÐÒÇö`ŒŽOã|JÎ&+ lîDh|™µ~¨»~©À+À“Iü\:ɾ¢Í¦=bú¼[øYË{¡Œ› `â>uI,õ“™bsÊŠér:±÷ÞçJÚát…l0ù JŠª¢Ê (@ *})Oj>)§‚!ëyT‘÷ˆ(áT@UUµ€ŒŸýøêFqžn)hêg=V2ÕÛ£úDbÏRø¯þƒŸT9©¨R>`þëcxŸô'–˜²¶íΔ¸ý €üÊ“óÆñ®>3V$-Ó <' E$²SºFVZ™æR®KˆÜ–2éÒ«{.írOM°àŠ6ø áÐ¥êØnþÒêÌzÙBé_€S°õõ8óñv£cXãÖ<œi÷ðÄôÕkY–RÀìñ˜¤Žâ;á¹¢ÅA¿FÞû 4ûQ™¥<ÎU<$©p@.ËïøŽÊ‹îß©!·Ýr»‹èž?ɉí,"¢è>³‰4•m;1Rm`v=ç]³AââŒÃ.GUI=ED‡˜†¼ ºFÁÅȱ¾«‚}m¾xJ²I©!’`«#-Ø-ì öë{ímûõ©Ïê)á<ôöªgC¢²¹†Í4+Û}Ù6ô›ôu˜ÂÔÈ“Å#¤H®Á …¸ê:wÖï¬äžÉ@DüçeTIŒ·'mÌ#ø >£ðļÇ1–À™%XØ“âù… óW«Ï&¬þ!Ui¹ˆü£\êl ›,×_Mí‰ÏK ’1PÈŤY§…µF’ä0*t–[}[‹ ‹8–¢æ¹Ê==M5B‘O-ÀSb°'Ű  ’Á€M â9 ˆ­‘ˆ22:‚t‡±W]d)‚º…ÇlwÊ+)Þ¢X‘µÌ®ò16Ø(m夣§3Šê£.–<Ê¢es#³ŽBDhf‘%VVßù4› Šzâr"çÀ® Ë)gTZ¨c˜j²+¨'Qû=ÆÀ“nÀà”…ØsÒæÛ›v¾òÊÃQ˜U ´Rˆá_ÒqÌ”üÇ-Tâ4÷[—Ô°Ñ æž%² ‰¾-ïK¨}Ê>8 ?W®éšU+væ$.¿Ñ°ü0ûle±v}&_µo'×P©\Â4tµ–®b¨{¢Ù€¿R¿+àÎq%EE"”h¤ ¡Ä‘j1H£sŠ/ ºBÞ¢×f¨£VR¤U"à+b½…NIT¶'ø2¥ô•7"šVèAí—íY> oÉχsÈêP…¹#<$‹§Ù#N̆ÇK »@Åsbα7¿ÄŠGÄqÇŽë¥Yà¢¢Ó E^¦’p£RDžñœ{NÄÜ0e<;4ÞÌÀÉM\ÜÈú‹y’I$þvä¡üCÄ«C–¬û\¬q¥úkp$¡EØÙN#ðLR¡åi"æËÈ/ï6#æù V”ØeÛcŒúAàÆ¯£JxS—"²^ö°R¶=Ï…Ìbºá>)¦ŠCk†³–{¦%‰âó½É%­ku=pÎxòA6c•RZÿ–j—ô)Ó‰¿Ý‡ÜV¼þß›V×ÞñCþkí·¼GÝ{ÿõ1––;›bœúZÍM]]6U… "s[ɘ€£×H%­ú8³8Ÿ8”òNAmÊ£«96U¥ˆŠò‹†M$4µjʬ™ºé±ƒä;úúfF}«"’‰ aŒY#z-óýøÌwœìK÷c1¦]ãè>Yà™‹I˜ßµ|ƒÿ.,2§Að¿í>f¾U¤ÿJ4?ãá4v¦XÂqëÓ!¯fžVŽZ¦1¤&ÌÅÛYW6é¨î}-×Ô´‰kj5U°ÊØê¸C©â±-l´,‘“–Á%²‚]¥¶”]ÎÀ‚-¹ÞØÔí/NŸ -£­žœþF•JÈ…‰Ø$mî©={(¹ÚØ’xvHœMUÚc4q¥RI·‰Zö"÷^·ÃeQ GZ%šÝX’Ûõ»’N×$ß`16hƒ¸=q¹Ë+δY›,"!e²Ì„ô+‰>šÆ“é|’*ºŒÂ–¬¡†š9ˆB’F²êPO¼§]Ã-ÁžØÏàŒÚ†WJ2²Ó1,¡ô=¯¿ºÎŒ­æCÇÅ`IÁ FžR§11³( ¶ŠúÎáu7Ò›³u&öI{[rnY*êë y#I5)6Ó®Q}A.5ƒ€IêÍ·º¶>rŠÄŽH3„©@]ùr0_»Zìå bû(£½ËgE²´Y|‘ÔT¢$‰ƒMp¬vPnÅŽÛ<ȲD‚ŽV â8Õ ÅÃÔØù¶ø 9~q啇Oz6]}Jµ½FÞ¸Xú?žõ™ºŸxUƒò)eþî5šk|Ê–8ÓCÄy¼ÂÛ=1]2-½âÜÍ#NãÝl@ÈßÙ¸Š²#°«‚9“Ô ±ß?,EYÞ<ãÌŒ@$ ‘Úö¿§Çt#sú8ꢚšQud±žcÈ©±ù¯ž%MZ¸'M‰Ø=ë¡Ðï§Ð÷鱘÷†÷ìÀnG¨ –šÍÝ,¸–i(ÌäŽzwªÞj ô$elË"ƒÈ®V5Ê’X¤ Ú´³óX·éab=ìp,¯üéjxeˆÅ*Øn¾òõV ¿Î,Ž-#Ìþß,jÉšè§ÙWÑ­=>bÕÁÙ®ÎéJ;ÞæýOV°=/é‡;ŒaÃE^LÈkã‚lÖY\* ¥ï¹å  nÌI(Ü­ŽÖžÆð¶®¤jÔqŸtF”7ÖWô¸î{4üE=¢Ä£DÇLUj4­ÏD™z!=˜xO¦7ü³§00‘Å WMZ*i)O6 …ÐÁÉ,O“.0o‡qŒÂ—¼L}’ZšécŽQ$ªÑêcå±Q‚os¦÷Ü›L5RW¤‰« 2¨düàWVß-𧃨w­’2nÌÍrÆä/KŸÚvÂÆMÁšÇ6ަjVFx…Ï0‰.l¶ÛqÜuƪ,´”@;©±ô6½­ˆ6õí4TÄÚY50Ò4݆£cs°Ô7,v'Èb^Y@´Ñ$1êk\êcvf&ìì{’MÎ'ªâλ=+Ó¨Ë)y’…,ò«T2-•u·PðF4¨ïas¹8í™q‡‘4R´² D§RŸx‹\Ø-Ø›ml0JV‚,AèG–¸{(¥†¶¨E $ª±5ÀßLº_ ,‡§–$+¶_”Tµq«©h‚¬f(bˆ³X1»33*Ü› €ì<°éS-•D8¼ôm¨µ÷ôßóY±` iAƒµŽñ4uq+ÄIY)¿ˆ­¿×Sqcù¿hbOµ±³uè¤tžƒÐ7U'Ýk©êm]gœ-U”Ìõ9r™i\ê–›ìú­·Ûê²ø–ÃÞa…ø¦ÁKDG5E¥…ö${ÃÜ_®‘±ñ¥Ya)‡›ÞæÇÄHøvæÙ—£¯–þ`è¡¶‘³t·c»iì›];{È{ŒxF±êMȵϋWkžœÀ6 ÒEÛ|mMŶ·Õ=ßuóU-ó€-ˆ©Ù<÷[ZÛôû'º|ºÍ#Èà–eñÜ—îE›`.AêGf‚<úuÜ€8XòNs^:¤‚CgžaÖ*diX|tì§ÐœeDìJzº}FÁ¦ÂÜô…Âþµ±DÌå…A’z´$JßadGÅŽö@“Øù<ç/zZHh]9PK`´_âÞÛx”\0A½Æ=pÍPlË4¯©^ aP>WS÷ã‡Ò½!9|’§ñ”ïB"Œ.Gê“lÌg;×&‹Ù³j ¡²Íª’_][ÆO®¯ÙˆßITJµ¹UKtZ¥éX¯âñ.\s zs„ÐT)kÚÊÁŽàuÒOϾ˜)Kå’²ûч|IŽto!8‘ql5ÓÅÈk©§yOUnb.—˜o÷íŒúHÉÚª‚Ui¢´ð‘Ô<~!oR.>x|l`羊‘ÕÖÎg]˜HzƒƒXFx“èú’­Ä«ªž ‰à:þfÛ_\6» $7$â±â*üÁ•¦wˆSÆ ÉO:61t/Í!Z@:’–‡`u¥A˜TA:SÔËM\nÖ“¨oæª+!ãv*.F÷à fÔˆt´¥N›Ä’@:u¤Šß¸â½~dµ G@h‘œ(ÑIiBí{){¹b-|Y¼?ÃÐÒ/€j‘·’V÷Ü÷$öJ,@1nFf‡Tq´@h¥‚¦©€°Bávó’@«o…ð›qdóH35u3\ˆ¢õ:‹\É8Ü.à€uy©Ï&©‘¡¡Ò™ªÜ^4#ªF·Çͯ{ôÇ»‡À«ŽS$“¨§”HÓEù­Q¦ÁUm*ߦøËC|3—ÒÃûÆ!auhìC=_[âN$ætM*Ù&–-7øêÊ~cá„jìò¥ž’†žY˜´ë}–àXcïè/Ô1Í87®Þ®¯ÙbmÌP‹5¼¼'o›6&«²ÉìÜA—/í4zŽFHÉ °Pð¯`:áß5Ë’¦!’ú$BŒ±³ û á> ¦ËÁå+4»ÈæìÄ^Þƒ©è;áŒb¢]@°C)}ª¢ê76P¹ï°Ä>-¦ÑT¡úÐÈ?²p]°«ô™ž-%ÆàI*´Q‹îY…‰ø(%Ã×Q~Œk9¹U7æäŒÀ~c1¯¢ú&‹+€0±bïoFbGÃkcx¨mNƒá…þ > ¯JÚŸïß÷àút_à5ü„çíVUü÷»£tÉŠÓ‰8†:j¹Z²m Gä˜éÖ#–ÉâR:2•>}-|Y q]ñ•%Y’¤¦3ªø&UjyÝn¬­«e“O/~¥Aû'ãë<ªX±<”9«SxIX„ÊcÔÔr¥®ý…°Ñ“SWÃH'öÄuÃ4k‹¨ iu°¾êËÒýð©ÃÜ/-XfÑ—C‘£’JxIv Öq¥u Bä_ÌbÚQm‡Ljß„;*ʹlÒÈD•³Ém‚Ž‘ ú¨<»›“¹Â²WÉI=MG$ÍK<§šbÍ ‘ ^20Ó¸Ôv7Ãæ¸¿(«Þ³.:Ùìj)X]$Ò ¸"@ÂŽâÅ*Ѹ×,k7µÓù΀þ;ŒÊóˆjn`•%QÕ‘•ºôè}1_pï=J +,÷!™9Cº™Ê²ŸFøá¢>"h|užÏKó›S|,(ù1ÆïbT|ß†šž«øF†5çi+QØN„‚Jô (µÁèÝòœÁ*"YRúZ÷ e=Õ”îˆ8•ñÍ Kòã¨MM²†º–>K¨ üðÈq†•ÏÑšjÜÆ‡¢,‹QèɱÐYGË>8r Ú½®.#„ ¶á»6÷ßò»t¶ðåé<âŠid§´*ÖHäÐÇHp’+2_¶ ¤oµúíÑÔW£¬š8ʲþP¤’70 Š4bI%´›Yl[±£‰ªÃù4Qr [ ݘ›»·wvêÍë÷`æ/UP2Úg(ÅCÕLbˆýU=¤~ƒÈâOqZÐCá³TIà‚>¥˜ì ¾È$_ÏaÔã¯pù£€óº™››;Ë;v¿’ÞßžÌʆ8(©Ô"¶ÚWê–ÖG™bQ.{É}÷Æê3arLm¡¡ …‡ËBj÷£iæçÍ<ÊÛ Œù*‡ó†GøB¸ôƒw¥Ž™.­S$Q/§9Õ?£h¿Å~Œrn](ª”jª«ü´®G‹Çº îÛÏXñ aT*ì>XclÞ[œE{Æ‹[îaÇÕ5[nêÌ4¤ˆÃ »(v·Ù½ñ¯èþº³zìɈ=b…,Ÿ¸zœTÑ..úL£¢U¹óäã"Ãôߢþ'ÓÝ&UYœÕÁQZ­3É¡>¨ ßDJw±Ñ»÷¿^–±òî˨Ú%Zq$ŒÞ”êoÔH¾À½€íç‚üDöšƒÖ¨ý=F§$"*¨²¨ØƒÇIúcx£œm°Àn Ù»ÏR~ú‰N D6,+d­=EÄ=¦¤1U¹eËàêmÆöïŽ1º56{¢‘‡Á±XWPÉœÚ5>…ÄJ*VLµPKiŽ B1û»×SXXo`<Éò™=\HhBôŽXE‡Õ­%~;ãœVau‹0ˆR»$ªÚéÜù ,47渄ãã?é–šB"ª*‹*¨äØc¨Æ¯ŒÁ^¯iÀ'ñ/Ñå5\Æq$ôÒ°³µ;„×n…ÅÏ®(~èRªD+%[Æ{껸$&À ÁRv6 Þx³ê%XÕÈ  ³Ð.OÝŠ÷†f2Âõ.Y=ªI''pV+…Õ¶úŒa"Ko»|jvÍ@«Éç¢F‘%}÷é&™m±xIé'U—sµÀ7¸õÒ¹ C[TfÂ3½­°Ü_20éES¤€F’X)Qaf¶Ñ(òŽ=Øô½íÜ`vGaURÓÏ0§‹”9HåÁŒ8v#Ä}ãß8+ªTUÖIgªÄ†ü´@Ä+ïv7Üþì8â¹àܾ™3þ Ö”é Ìä<ŒG+J¹¹°W:ÆÄ\-âxrø ÓöDÔíä?y;ŒßÕ‚Õ•i4’º¢(»3¤â¹Í¾’%¨cUNóÉÓšÈt/¨]¶üç*>8™Fij¼Öof£_p¤[Ì“Ð[ë0Ô{ipËgåÅÊËh샣Ëx£=µn ®mܨ¿Úà àþt›Û3 }¦°ÛM÷XíÒÛ¨oÐ;y჉ó•‚' ëÎ#Liq¬³l,·¹µïòÀÉøbª{ûU|ºOòtÀ@Ÿ CT‡æØæßF9iB­Ný\¼…ï笵ñdÃ]²º!0ÓË ú'7ïÖS2}p±ý£9 AÑZÆŽ4Å÷*¦!Vð}'/©jˆ”)¦>!pÃÀÝ $ý‰ÆþŒªÖ§0©›L¼¨âH-,a–\xÜwbÃV¨ô1¡X²,Ž*záchiIõ/<Ì]™<±†óˆ©– ªùÆÞZQ˜®ÞwvÄ^"Î’ŽîB‹*yÜ쨣»3l0ß3ægi>i¥_ùF13mè†?éœJãGÓ&\×£|r߄Π¹´+)¼ÏK<óÐÉ,¢àz*¢¨ô\4}$M¥h[ʾŸñ,0Cdý1˜ÊwŠ5Ø|°•à PÔàA"D†¦±¥®¦Nö¤é¹ßvXaÕÃá€4™±G$QN‚7’W³ÂXŽk3q"ßv=±Ê7Qò>"ÓQûCLóT"°a µËî×JÛÏ3jâLë:G%2Bï,enl £'IÖÚÄ "Ûâ}5节¡l (€°Çûp.nW¨3´Ï­¯©F¡]tx£ÖTøvéìg²æY˜ÕeRÉ ©¨¢cwYÞ˜I®ÁKx¤BïÞÛ€رrÌÊ*ˆÄH²Fz2Ÿ‘ô8’pÑ¢C4ª ›ždm!ØX ™°}†f?G.ò™éêVŽbnÍOˆô“¦÷ßa…ÂjÄÆa'/áüÞ6³T‘<š•/oïS|¨¢«1ɦ¥D~[r¼)5½ÜÈ$ù\b)Cé 85RÿÂúA«%ú#ÚÑÝÚão.»^Ò*'Æ©ÙB„Tñ¹*<T{úFë}‹‘¬ _½˜’HÖ§iww1^Fs®îÌÎA$9íåå‰ðdªüÅÌ'Vñû‘S}µ7¼‡¾7,fÊëÂù+è/T ;®…Œ1"(ɹBýYœø¾±ëq`#q>w–H„FicYfþ3”BÂŽ¤‚iÞì66ÄÇ᪓ÿÅ*þIL?Óýõºy1ób>Î&áŠâ,3i¬z†§§?Ž‘ð¿ ÔQ´äÕ™DͯxÕt¿v ®E®a‡«ážjDr”¾é"ä|/ÐúõÇm€ÂÜùFbÄéÌUaì±ŸÆø‰'fdï›Xy-$_¼â¤™XìA±·^þX‡›ÑHãTò¦ ‘¿6E¸¸õ0ìz‚Ÿ˜pnd̲G™/1zg ¨}—ÒÅX_ÍM»`¥™¤q…zzy¤]j #o±Òc%MºŒ^ŽÞ)óQ'³VD`«PJèb (ë%<¢Å‡šõÁÂÿðý¤GžC¡€§Ì£Y_¢¥J‹ 7@âÊI"Ë{1|û*Ì«UQ㢃KI’É$l:2YÇçcЃŽÙ¶C™TFazÊe‰ö“—JnTûÊ5ÈÀÃú1Û…8¢F—Ø«EZ‹q§ø¹”)ý«ñõ¼$'T#gÌee€Zœ`ž$³²D‹‹ÛÏÕŸþ'Sý]7üœEÑÜ×2Žž'–W  ³Ãõ¯_2Öά°DY© `E´\š‰GÚ`Œª;~8+™pÔ3fRrØ:°i :6‘’Gkƒl{ ýóõ¯xúl-îZÛðØ…œºME Ö’HÏÉå?¸a‡éq­I}šÊsý£Ž°ðYŒã0­ÚÚµDvÛb d·qY4úÕõR¢È²h~N’PÜ_L`õÁMµ#cþ;ã1æ`tÿ f˜ò‡aŽ«êæÃsÓUÏ™Ç'D¶Ö#k>g|Î*$‘Jq¹ó8ð\ùœûã/ˆZÏ™Æ>g6øÑÄ0çÌãzÏ™ÅÉÆ_µŸ3ŒW;npF7ˆaϙƵŸ3‚' hâ sæq¦sæpT«ã/ˆeϙƃŸ3‰¢n3õŸ3Œ.|Î*&cwÄ-gÌãzÏ™ÁRïŒÄMgÌãŸ3‚%ã1Yó8Þ³æpfèq˜‡3›Î5€ÿÙntpsec-1.1.0+dfsg1/docs/pic/alice13.gif0000644000175000017500000002637413252364117017254 0ustar rlaagerrlaagerGIF89a’´Õÿÿÿ ™šýýˆwhwiXfXM¦”{A92™5.)VOC eiaûžžÖÖÖ¬¬¬þÖÖÆsogMD9ëìëll ÛÚ ”„kž0MM*%) :;”{k¸¸³¥‡Sc”” ÿÿ™œŒsœŒ{ÿÿ”„s㥌sçç÷÷¤•‡…ƒ¹·€ÂÂÂ:ooÿÿ9ÿÿÆkZ{µ{sÿÿkÿÿÿ!ù?,’´ÿ@ëc)ƒ%9 )#§´©DºX† ë2°V›s$}Ë]¯wÔŲ®n·W΢ÛÕõºa/ÇóÕ~r )+X^B"i^00+‡”‰‘+‘_+u“’ž“¤‡¡¤žœ¡_¬¢«­©®¬°{|..—¯º³º‡{“­Ãu »«§©£Ìy³¼Ä§Ë½¼ÔœÄÙ¡."áááX0°ë²j  4ÊÒ‰Ú°¢’y¢y’„⳩V ƒx"á9ƒ,"‡âC† x°±ÑŠsÒ$¢„ð… ) €²÷oN‚cTUŠ” 6·È hB@Lÿ'e˜ŒÙ€ëÑ[+Š&¤”ŒÓLzÞTàÁ X³jÈPÃâ.ˆ(0bCOG@«À‚¹—^^ísE ŒGð®U€¶€¾|÷æ]k¡°#û²-p€È"@DNð!Aä+SþÀ¹se¼R÷[1+V ¦µ:ðNÞ•f, `AŠ…ÈX†Mƒ•ˆ……¢7à “$(›0+ØLÂ; &m·NØ)L ³–psÉp@wnˆá5®j`£êÔl¤¸€ÙCJ* D‘÷ˆÉÌÕ‰ @ƒL2M¢E+41 5á´Á„#@& ”—NO `ÿŽZ`‰u€m:±Ä] p—€zXyå@%°Ú{3š#¢8(4š^`%p€“H‡Ênuøèb‹vX_F°ÇÜtÃßZ‡A@mU©ÀVX—e‰–—€ùUD[€–¥ÀgZb¥€V2ŠB›a}ðÅsó@Ÿ“Iq#]?r “€è@µi6\཈¼h—³ |a‘™8êÑ> Å…vsFé>ç¾ÐùCxš›_1‡ŸûIo:Ѐ«Lã·zý€y"à\pæŒÁkXè0ˆ<¸/ƒ5^{1CÀƒ÷ûÅFØó¢6±5Væ!/YÑ®ŠS!ð†q¾¸lÌÅÔ›/ôB@šÅ#ê°Ž‡©Ë÷ ˆ}ˆÒÃߣ²Ø¡ÿf^¸Ààox…vqa8eAÒ¯`,ŨJQ ¦$…\CJJY„C¡p%'(Â)K$'\äá€# ¸¬€ÅV/CÁ¾4޼ÊU³äâ°1*:ð .€Á’„!Ĩ©Œ©Œð2-´h+77f“Ôr$°Ð/]bŒh™„=p‡TPiÅp ÊÔ Wq•ÿÚ¤«ŠxÍðÊEÎKY^ W@…Ða9’PÌ Pª‡*Õ,´A8<‚I<=Ï<‰BZ¶ñ†ØŽhÓGìä?tœñˆ*€ À58ç]è{ÔS4 ¤%EjKÿRРDREÁ,ˆa ¢‡/@È™|@Bƒ8Y–‰%Båyú5° ^ýŠU½ÒÖ À#Е,?`ˆËÍómè†^X€®rIm)BÛ_…LÆUjBRðF1s{ÂZ„ I8Éþ$L0F€$Õƒ7ð_ÿfE¯ULp«‰" 擸y¸XXt=G±‡H¬cGzê%w’­ì@¥8ÈŠ‚&DIå ã&ÑQž'…SœQ±N€#ªrÙ] kõjS®²§8ì 0“m箸Ê=8pƒC2À© 5ŠyQ«ÀârC(2ª‘”Æ* =QB{íéÿm{, @¯€#â8\çŠ=*¬ Ð}Û7$¤™®óÓà\ä`â]Ãb§ P=8> f6(œT™/Œ¤¬¾þ‹¢;-p äe‡úhßÐ6?ÿÆ­t¼©+ ”;n1±ã¤+;"¸}àœ´³ ‡)ç²àBò\ÓRlvyú/ŠŸæ42ŸxÉʳá€hðfQÀFÇU1#/ê¹ËhÁïWûL݈I¼œD-¹ÿ̆ã,2“71ƒOîr—E†¢´PaÄ ;=Îã0GËè£ÕÅaæÒ¯)Bþ‘9´õ,Øbu``¦e LŠN¢¢à$*Y»¥3ý.h—.a¡Y.6ï½²ÇÊ«pEÀ¦±‚ºö © Ã-|j!Ãç“hÀo~?©þ–î“âÒy|fµÅ0ðUƒ.7Ÿ8âY‡|æÑ*`ÓøÑø>-Ð=@(µÌ…‚Ê@=PÏ:œðÿÉxÝëâLØ1óÈ 2]·8>€¶™Ïõ ˜Ï¡(£Ÿäìir vð<úû•‹ë¨žE@*JH°@~gê§yd@2 (BbP€<„ŒÏXˆóâ²!H€‰$hÀ—7Ôý~ú!œÍ¹+r/,”s'Z€ß"½a ,^Î!¶`ij@úuP“â¿[ýݤY¾ŒéÈP úéÈ @8ú¼®) ¶B@×È^-0Icn§ŸI¦^̶œ'€…(lìð?rü}×’é°}„­‹»§³ßù0Ü„\ÜN Ä=¦BµÛu[DP ôK¯!ÿ"€0`+@|Æ×âÕ_mSÜp¦Ðh³PTeaJ@Äh±5`xF÷52÷6†RfÑ8úQ)Q6†}œ4 Ð +P„F3룄FvA›Á‡¢‚>c0YrX÷tÔG‘C|‡¯wiè°nØu(#’"h‘ƒ…TäCœp®U:ýà 9‘j7qK JqtL²((1ÀÀÿQ@À1%PäàQp¢È%e1ƒ±¤\*T Âö XŠÏX3#i !#àŒq%„%JRÄM„Q%K"Ðwdÿáç‡Hð ðPŒ/À?„á/ð‡xˆ{G çÐD†\ö‰isä±Ò%lW%°;³3-kA. Äâ ñO:—FAÈI%±¸` š°ó³)ÐPŒpŒð…ñhà0‡hˆaZV;ËþR‰0‘à?zV ‡ò~·EÀuutŽáç@ ð “Ž¢$ ÁS¢$”wXÀ „5Œ‰ n 6òŒ€1ÐwaanÐ/à/I0YÙxtic0’в10s&Ków_çuœQuc©Š‘qÿ]Â=P8~bဴ§  (YŒà‚7@ÀP;ÿ†_UE°1P”vé” Ð^Yuµ‘ƒg•"Xba(¼3ˆ(=ÅeyÛEEÿC}çƒI N ðšD) zá“ÒãžÆÉ;íAœ P”á ”°¡¿ÉŸNYƒ+Du€~\““€t!4pQFjYÆ' ®ö8Í À‚P!ØY‘" =`#‡…›ñI”*Ú2»Y‘ê”ФIIŒ/à¥À¦PY –™ žH@rRv:ÕK)'‹a´%Àt’69*[çtaAž-z—¼ùPÊèsÒcÄø›Ä9‘ýø¤ ¢Mù1pˆÅ(£ì—jókty *´ŠS·%Àrs…á0°(GB~}™Ö‘‡0)°¡ •’Ú˜ pkÿ9óÑ-Š i¦p—Æ:´ zf0úq[óðv–)£µ3L-Õ(>2L+àkáÝ À³6³;É!:ù¤„ê¥"Ð`)àb“fZˆñyŒ æz— ¥¯i¦$UiÊr²;²d#(0>çP !2™6üÁdá$`…ép_p©a £Ci·Ö`7P¬àŒÇ™”JÙš™ ¯ÇêšÆéšÈ*©öÅÎê¯XçN‡Ò‰ {@,KG17#ñ@9ûAƒKÄP‘ñYœ-:¯¢TÌhŸ À;ÄÙšæ:¨ÆZ¦ùš*;²$EAcU{[3$ÿiµ3àAZ¢FK’P &´3L4•¦a©:yXx?~’›¼é²þè”)@Œcj¬†èŒÆZ«.»8Xç Óxb+­p÷Pˆ Å(~a"G*l÷–xs3vçO þˆ¬Àà‚¤ÅŸÜcˆ µÉŒîú¤p©†˜¸Pù‰:'¹´3³b;°M•q“P%0qW{^4L’p´êš¨ëA1`®Ðè¡S‹¬‡;¬ý©›+ÛŸ \û5['½ë»_3Luw1­èN¢q>Ô;Íà-R· IŒOGðǸ8«kž»ÉŸ:¸Åx¯õ •Л·´s›@*ÿèÛÀ´Ø%ÊuÌ;¡gE¸H©PKKj @И…@©ô”¹9‘IŒ¤kµ/²*›¸µ:¾f›i^p¾<J{QFxqI‡‚`õ1Û°é)Go$JG[¦´s©ÍèšaJ”‹Š¢Æé¥›¸"»› 3œ´, Ã;ü¬ÁUm!¶Æ‹¢w%a\ÄÀ×UY0yG²ÙšT¬†˜´:¬²ø¹Ÿ»¦ƒxjQ_ ƬŠèzgòèçC·C l×u-Íw@{+J й»8Éš¯ÐkœIy¯EYœ*<”õ*˜´SdˆÈè+;Syá¦â h1“!ÿ)ÅIg'=}«¯aq¯†»¡&K©xìŒô:ÃiŒ¨,Ìd <ÆËuçeY=FV@æÀÖ ÁéùËÒs/ü5¤›Êöº¡©¬“`Z”†Hº¯iÌ^ú¢¾Ö|aLÍ»‚‘hƒ !ÜðKzà!süs*dž3\ÃwIŒP™¢I ©NjœóÊOÚ¡6ȼ_¹Ô,3êFU‡6*H >ê!uh™ìh8𜪲Lj“9ÙÐñù5¿‰ÊO‰ ÍàÐ%Ï’ ÁvBDð E#§>;°Š×FDÎ÷££“j¬‡²ZlŒÂ ¯Çh¬‚4"`’šù¬ÏÿË ®˜[ç'u¥~Q|ër?)ÕçÑVKÃÙÐÇ<¯aÑÌñù›Ö Íag7KÖâP X”PèXŽC~(¢Rý:úÉx}®´©_ÃÂ1:ÊþÈ׋3L'‰œ0•c<­8-63;iA|bPÛPt]}èlÓ› •&ëÄ”˜€p›‚4-x!•2Ôè›lXl'`¡4Ðû•i¯%¬É´ƒÑX½ÙÝ *ښЬz/æBtµ®ð°ˆ#JPR„pû…6‘À€gàIΰ+/ÌšêLÃ(û·ÿhÃò-Jp XRu?“. Üÿ¾‹RN ³mºBpäJǃݿŒÏI¦»©Û4¼º¯+Ê_cÙpt ¢•¼.¹Ñ‰0 @ì’°Áàoc„"p‡:LÌÞmÕþÛ˜âÛ=n×ïôÖu( ¾Þ1c Uw’†hH<ãXpá àãüû—p‡ŸCŽ×–*ÊÅJœ;:½Tj×6{‚(OIñâ’k³,Ë(ÀÊ­˜6cã0†€`îA€l×»x\æó|![‘ÅŽlÌ‹“âb. ÚÔ€7 %- -¦4ÜŒZ¬“8饦+‹3“ÿ0MW 6¡“@ç’ \õ­„P;39òðƒãÓ`ˆq‡™Å®Àm²QÊÉÔNºö,Ú´“ÐÐìä«0¤ÞP~”2+òthal„Ÿ€P´pv"áÞÀÌÈêYë”dÅ¢ŒÂ­ÊMÚ»âí@°ûd¼žéå.°ìƒS‘®¢-Fx3N;ø0ÀÉY\æ©±ËØŒµûïp ¹ D¹¡^x5 I¡éÔì=«8+g5#ð@«0ÓaÈ^ä+øã´Q€m¯Cyô!¥ápœì<²ìÛé{?7ðPb¾+Îä8 “ä¾ðÀÐ 9ï$ÿ„(¯Eid°µ«Ùy­ô¼¨_šµþI¾Òó``cV?Tòôi2¿ð6+C´Ý쇚XÔ¡Ä’öÊÅW­ŸNJ”ZêÂd~—¿J–öSè_“/]¾ŽØt7pcï£-Aâ+WÙ¿­9íJIºÊ,B^ùPè)`ù¾¦4#ñä\O@'ŒÑÚ²t9^)哜¢9¥;”ùÉšPêŽNœ!@9µÏ¸‰úmnƒ¤óiŸ¯Ïlj'“ã³Ö0ÔJÒ;T 0-ðþ2@!@20îßöÿþ3 $$áçR,v¯WÇØLaÀtzkSÿ#ÃjÅbu½.Ôµø‘Ñ×1N¤P‰Ïa5*FŠ‘ÅBXF´ V>RBfZeB%gfdB@šbŒО¨Dή¶¼¸X ¶VRÐp(ÚÚ*dÉÌ bS(,ð,þ| 7¸ 4/–HeZ$$&+-¥I:<9˜.8ÉRDËÙšNÁÀX6\¯žplÉ*¬ä›>@.R D ¸ °ç—Ø  `‰!,¸‚„„"Ȱ(Aˆ„ L<ü#ƒPNèÐaBmD”•¬É—/©V­C&Á”{EDT¸Qïç•RzØ `À¡lQÿ)€Å&yDÍ¡4h¼ˆƒ —(Q¾(’Ü8 2`H@æÔÍ.[vªðÀž­{ßÎ-Ú„… æø!°"‰FŽe€Öb†ãÓC¬§pb½¡0ÁUµ àNyàKÔu0¼°ÁÚHŠSÈÃá-Á0A$‘…/,,b4¹¢r Ð&g–Ä%Ëo-—ˆŽð©C„4"ÞÂÅp®Hóur m„†ñ² ñ–üï&}€AgÏÌÊ)“@jÁ,³TRÂ,”.X¥—ºkë )R« › (è‹ÒJ `º-¾ZìCƒ- °€ œiD@ÿaŒ¦‘ÄÚÆ%Á Â>Ac(ñh*ØVP@„ `C4,Øk :€†¸‚,ÑP¸c Bˆ®/c”dÀFkp4ti¥ç R`I#I²ŽHrÎ[â’ÒDpH /+¯8à€ ¼ÀËJÂlT^ K,íºÓî%ÀAäÊ8}»B˜/\`CY ô(؇P&Wà“Ë(p’Ê8""Ç$‘¨,Ñ&­TÇJ?ñe€sâŒç ýlZ=eIà6rkb> ª\ÕˆV8Ã1W’•’ØkoЬÀ˜ð5‚ZŠ@Ý´Ú}é€U0"ÎÈÐò cÿFµ% @lÎA­Å瀈øÈˆH$ñ³”`ЬÌ•è%êDk í2¾-6`#<™0  îȘŸðMÁž ¶àïH¸ä‚R>€L¹WKB‹Ý ¶é@3ÐÀL^ÓJ3-±–¸ €@–Ä!5c›(d…UÚbO[Ê)ef' @aÆ3P¨ØÀn–L†ŠŽh†¨[Éç¥9^×Ò @A‹.>y7dS@¼ØZ–r¾&ãÈ se!:Rààñe> Ë > „M«»îIÑz)…B5`ƒ ¦„ €®NRë¢OüÇf@[™åº¢ä°œW8þ£ÿå]yÑ>!ïa/€“d,¼&•$•¤]”ú7×q¼aX’€oÀüóƒççÑv=YÞýu_º@ð2ü¦ UoÞÛ‹Ù*'QÛ/°€E8N°Š&¨€ò¡/}lÁÑBó¾”h‡M‘’Ÿ`.@¡Ú¥€c_:rb¸6@€…׾楆Hâ¸ZÛc@À@ó¡K!¨›((Áµá/21Âær:-]­pƒ^ñøIàÊEL|ÎŒ°@ó o"á̃tÈ.íÄ/(,‚ÔÊáäÆúYÿî!Eš«Ñ™Á—#+ Äð|41šºídq Ù¡¼ø#QáœÿÁ–EÄìý„T,—3 >†@$(‚hˆc{_€±øám==$?¤u(Ü¥U²(žeˆDB‰F©†ddµˆ(b²B`DR`ÓNy HB Ô´éϲßA¥),g ê!CñÐËXÁ‘¶èR˜*3™fȈ…ÊŒ&Wظî¥]¢[ÓŽ¶á¡AzT8Pv5Pà¡"€fÓmbS†S<â€WsÂH9$À#$«1 \f¢,´áX®(:•¤`‡íìÑzÊPŽ€À<71@T È x¡ç;bú jÐXv“ šlÔcp$ZV’"-0à—ÿH`‚“äHykZWòv˜%^ 7@Á P†Š«¥â&¬q©#µy¾"ÀR–ßS˜L°€D|•…þaÄN}!=rL b™€ÑÄ£›€¥M˜¤æ×P 9qU] ؘrS†møéM':²jd9/z#5.j’4‘Ž²ß°Ûºi†4Ly½ìÔÁVþU15_2 @>lÒT±74ÖÌ|õЬ']2)vé [×[I5…}âë±IUY—6¤À´ ¬À{,WW#µ–šBÁB&Ž£6JéúXδs nBƒn”,O¬@ÅþX±Ô+duŠ@ÀÿРÿD×öÒ ®€h5ZDOB¨lòÚ©"„-F³œ\§Xc,Èç:Ú!ú6PÛïÚ›‚Y-¢*’ŒÐ6®Œ†—‚§ñ¤\Œxh/7è`PaÙéD¾mî=R°Ý0q)ò$žsbj¬Í[™¹pãÅV€cA¡ì‘,¤Ƹ„ÇÈ|(Ö¾€Á64—µWø/Ö&  wB¦Utƹ_Êõ1ة۸€¦£Q.A 3$ ôã 0sšËa4ÈFžÁ™¿$/ ÙQÁKK”†i&x’-0/â$—¼`¨^¤{ÍDÛ¢Éh^ ,¦^ÿ:Ò1"ZX9K•)ž PÉ”e“¸@ ¸ pð–—ñ¦ÁMªå„—Æ4ÚFØo "‚ÝhÔŒm”‰óµ±–žóFlð6îÑ“$j È 0À‰àHâ÷æÛ„^2}]U\vÔN0U¤Û¹ @«mê€Å·K3fв+Ë‚$*O\à< §«xž®vè+ ïSA}Ý„HXáhfÎؘNp/bÎjÁíºØ¿*퀈 `è @aöñý‰œ°Za.™Ðwˆ`fwsÈXlƒÛ½ ¦ÑnΣ‡‡n1 Ë=bR›Ì«ÿHZˆV,¢·`¥ÄËfúp §w³o¤t£d` Ÿj„Ä\ŸÚ¼ATæ}ƒ¨›8L~Þ„C!Ç_èÅ_?¶t#œÌuiú⃃¨ë[~šøU¨ £Z¢¢4×öµ-v±Œ«GØùÍ ð%­¬h•ªHÒSTrwTéî–c>o¹ØÃ1oô·=‡«l~Žóuž}Æì?^7Ê(Ü3ªâsPO»¸o$¦ÏÒ :Ÿ‚(@ øZ9 mm å¼í#’–9õ-a <ãÀxBÐbfàì`•ˆ¤¤l"ÊHü–©Œ€ÁJEz @8hà6ÆÃpÿ¥ÈÊ"„¬ÿHìõ屎à0&çÀp¤`¸`ŠA ‚oîújô¡ŒŽãGLˆ  j¡Âã,ÂKæ „QJL:¨ÛpñT‚&Ø2Ê; æ`bþ Ÿˆ{æü¶€ `àjèO&Òk&2ˆ³ræ&aàÌÉ+œ,¢ yÛRl,¬MŽÀgfÆ@!X )V`1ˆ¡ÂX`.î`F`ð`• ~àxÃ'æc ܼ"βAoÊ,AΠ%/”Œb€í  [>`\€åŽUç¸TŽp/òä*`$ú† Pÿ€Ñ® "±)ÛŠ ] ¨-´/q pDåj6V‰4´JŠ À™äF÷ˆ€¨à›ÚÈ+\+wTñ¦t)Ò$h&åÉÖ¥GrMB°€¸q²N¼ ±GöG¤§ÞœNFpÀK’LÉ– 3Á嬞Ca|¥AÊ+ €äG é ?Rue_`ˆn"&aÀ¤_>€¤ ØOrÉ„ fä²¢q9N2Ø $=h¤ÇåP€È¢ò$õ%À½’¥ ö¢h€@$>¨` ( - d¢þ+õm-b-B²  ¿ÿ&Â0T¦2O¨²¸&Q_r_V`ÞÚðp –ñN`@$ízŠ¡Hà¿hÅð^HsÖû¼ðh˜™d1qöà\`^…pB®¸Î#&ס&€þ¬à,#É4'¬…#°+ðZ.([ÐõÑ;*%~0f ‘ çPG¸QKŒQ~&·Ò.\ÀY:98ÎM¡*$à‘_#¯íÄ$@#n˜8A<ÏLòf6@è`@*a€ 6^RvP&ŸlbhD`KŒî0 ¦l%‚adEÒf¥6{ó-=m| ->AWÌ:ä¯=?à=ËpD@*©{îÿSIF`ô“ ¤ @é´Æ`@âj\: 2%jm‡F³¶¨ |e· ±=ÁѸ‘ Ò„° îsè àèÜ „S.€ `6àæ|æR@|BSŽ$Ö$ͺT@d¥"B"4󊀂=Û³ ‘*ë3!UÓÐôæ5ñé n£ˆ (`@ir4€ ¡2`NL§pBâ@?"sBàô*Âÿ‚ÊÖª36á×lÀo6€*iÀÀPvŽ´ Ä0‹¡Iä¢P„ @Pµ# e4V¨á9žL¿dB"ÖÀ€NJùrТ“C\‘Zÿ5Ç‚ÂÝ&J•g <Ø$Θì­Æ…\¦Œh]ˆi·ÀÁ˜|íá(•i¸ï˜  î~°’/péÞð‰ Eä`Z%ð)`YRJe3F·X ©–Ç­ÚõB Á“ ä·¿²„ƒj•Çž´F (¢ ( ‘ÂôÀ a'F€Á¶ b–\ dÏ}Ì¢l]ÿ•‚ìRÎŽ%¤L‰ V£à®hà=Çà5K Ð0î S—NÝk69žÂÄaÞH ;X!DY36¤b6±dj?ÿKî”5è6¹ÅàÇ×6m [Ö¬¬~•À8[6=©Á’ØÀ´G<@ã w#š- gí õ=åÇ*U€ùj2Y 4R)h`YÂÒ¶u ˜B-@Ô]êÒ[y…ŶÐ)x ”†wå—¶ùB¸ (0¢-ýøáP'mà‘UAp¬ ÜÁeµ’T yÁFãúš€Á.l_@þÀ4Šõ].ksËhÜ68’V•J ñfYƒƒ¢Ê¤Nj #ž @p¢¥(†£KuˆXH¸„‰¡Ù…›1˜R¹áL“²2xsåAL–¹ ÇNØP8ŒÿÜm¯Þ+v¬5zö'CDÈP *Œk_r¢SAÀöÒ¬ÏÖì*hr„ÏÖ$XÆ@AÚP ¢òìÄ.ŠkJv1ÂWª4[I:OîúÚ_àÂÊ‘•’t]‚gÙ¥•§ø~É€)k`#Äï®Ïã_A›¯Þ+I`gez.ˆè_û¸ÝZîbǸݬ£-ŒLds ˆn螢ŸBYåez åÀÝú øÊ16¶Ò¥Š!ùj5÷sI¸å dGjMöžëY» €–Y y1¹)ã6 çí †£MA :[J{PkWAÁ÷9'¸`YPFv`w`c_t¹„1&×{5Eÿk䙪Ýí d¡‹\ñ \†®ø®-JóÀËæît)øà)b\ ñ@9Ûôëò@ ïâ 8õý A! œˆÔû<„‘½“üz"Ü h ´ %KšºR üµ]`)Ä.y î" Td ãEK´,qV©q¾§rÞ¶ÛPpl:×!Yìâ½XãNÊÞŒ¤Ü>ÂPY­iGócoÅ™.@3vr È¯Æ 3xœè’%€b4y¡ ó ºyqÍɻç3Ïñ< ÀÏHd‡V Õ¥CÉP0”âYñ!ž&?Bþ{•ˆÁòÂ;Ÿè9³;ܾC%k †xß–'€úãGžÒ猊‹úÎ–Š–( µBp€š1™bØíµ5%У“™rÈ67°çžŒMj0Ü)Xÿž"Dëµ£t¥¬Ò$·ÎÍmš,`&wp± ŒÐž-ºeÆXB bs7Èö†98R »°`­Yب˜š}v稖ŽÛE¹:>¦Ü„Dp I  H<~´CÐ* í%È­¡+\UÂ)£Ã °Bœ‰Ä\Pùp­"U%nRö b±â€½ñ@€aäC3ÐHB 0ß4¦†ê PK-‚ à4  è€r"”M;Üj¡óD8Ûò35á%ÀhÀÀ›@¢ DEf&+-U€¶\jˆ ?XxŒÌÛ/ä:ÌÜŽ…) ·É«G)òÿ¥2õÛyß,n‚Ë*ÌUW2 µÓ‚\} å<ަ €(Ιf+€’ ¥ÀT§ƒ@Ù^ƒ¡øí‡ê-pîŠà»;·ÂƒÉ}ê×? ; œã…î-Xn æ§ÝÌRDúD€·"½©è>g–ÐÐ_¬3Ád’£ ˜HMÓÛÞŽx„ ±}OaDŸ €¬­Ý®Am€íÔr5MœlK‹äZÀ•Ã1« ¸ïž÷€¥âGÄš Ôgg,Á!ÐêÕa   зŒ!ù@CR¥Þ0©8s JýË4&1Å;¨ÁéÑÐ3ßÓH¬ðÿXjÑkO¼dà”CÆ € õ ƒEÉÌfèÂÇ>ú1.ŽŠ‰Éžø ™¥@uLÉN8¬¬ ‚€:˜7UàDŒGƒâŒe˜b,Rtl¤ÍGIJº8ï ÚGhî8Bl4ã7°Œ¥oÒääÂiª8Ò'´¯mQa‘œ$%»ç½£T¢j€šR§äO–Дe-¡Ð Ecp­´†ÈÍnzó›™‚ÌÒ¦<š@Ê$’ºqÍ”„§”ìÖ÷ÀJ(“G0±BkXÊ~–&e@Às ‰+s¢D,dÂl6"BŽƒ ‰ ç1p"ÃÌæ~Ž©“%mH,Þ0ÿbŸ"² @Z˜./}5ÇSY„T à8U¿F&ï9ÀkóJEG ÐÝò4Üè/ äµÚ€¤3@ªJÁ£Î0¥Ø NPŠÀ¹«94\.¼ÅF…ò®m…tœà)ªwP Tž C ˆ R•êL‘Ná#O>€ Uȵh[u¶³$®™K¸êdSÀ±@D“Ažaø¼Ä¬– ªŒÙ4ž`x$£ÆZÝŠ?ÎFdPÌ&]E+‚TTäfü gPÍ#Ô{N¸*Ñí`"~ šÉZ!šZ´‡¯’}à‘d†[£Ad³H`+ò‘0¬ ¤=•D`2S²žTÿÊ­Ó¢Ù~̓Û{‰48wOçÕAvwc†ü¨œ !œàÝŸº¨`ûÃCJ»# ¤chç4v‰O ‡/…QÔu][²¢ÁIð.6Ö{ÏFá% Èëà†tÐ7”-9ÒRêø‹º£1‘ÉløÕ hdÏtÔ>rÂ9`™ÍT± ákPØ*ÞB)Y_sƈd܈¤nnqSúI_Ò×®¨ˆ5`ʧ ß œ5-«œHù±Q†œ…“œr´ Ž˜ÐÜðÊ ‚ËÆ€ÌD¸ã(uÍ5uN5 ŽLè±5ÄŒ]2cÃD³Zåêêà–$è=IÛÿ÷\é-=‘ |¹[Ò¬éÐÐ7 ´ S:²µ„ˆ!mÉo„®žIÚGqÑçÖ.ÝLyH&+ƒ «!jôZ yP‰cŠ,‘d‰€$XÝ*´1À SÐgDÛsw¶p…ÆÂWZ€¹(¿ÖÚ(òTó÷nšÒ'\êÀ5çQ¿ÒW*â0#Fü|ž‘4ø YÎrÙËdØõ2Âýé(àí›OxSyk_<³ eƒ+„)¸üŠd+˜õJÄh¼RÇu«s‹sf-\7*…äÍ–÷&ºæ…ËO'°)Q0_D8Âèàz"Ô£ÇùÎáä°õQÿ•.'XûóéàdšÁi9ÐY@:2RR*0úŽJ°¹ç€ÐªGAë¿à:½nö§&XOë „þ휨ýNûMæ£w3Hµäg@»/î~w‚}}ïÂ_iRw›žéˆ?ü¿…×bÝ ‚ïÅãÙ~„s)üó ½è¹¹ÝH0uô¨O½êWÏz*‚Ÿ¼äu0i—Ûþö¸w*h›ûÞûþ÷À´ fOÿøâ~å‘Ïü Çžö³o¾ôB|"OÿúOz¾ƒ¢ýî£\ûCä¾÷ǯyð³’üè7Gõ#Ööô»¿ë¿YûßOÿ;Äßšó¯¿þåp¸ïÿÿãf~Ø€ÿ ý7l˜€‘p€Ë§€8 (~8XùG˜\˜(˜Èø"Ø!8‚&X]'¸‚RP‚,ø‚ ˜‚æÒ0Xƒ¡&ƒ:r6¸ƒcÆy<øƒAP}æu 0v7—„JXhHV,÷.¼7B´„TJEð‘Ò%â•@´PýS…`ddF>W==bta˜†RÅiÓö€§†<ØkP¡W-°RXr¸„„¦G«¸p{„bFbÕ…†8ˆU(f³a‡4@)E>qѱQ0ϸà â:ž 1ç ”8‰0‚‰£¨‰Ëž(ŠèAÿ™¨ ›ˆŠCÇŠx Š²ãj—hŠß%s³Ø‰µH‹«X¥‹§È‹"‹Á^É(±VŒºÈÈÀX?¦iŒÒh’9 “Â8“±”é”  ‡'€ÿy V?,I¨ˆÒ÷zV83-dGr]¨ã•×–m¥Í0wbyfˆ–’ –W©»Ò•6Ùurù•ϧ\Íàˆ>À¾†ƒ}™ t™àÇ*J ‚¸v‡É|jCÚ§‘8XVN‘ù€l•g†O›ùl•B@s*‰à2/gV·° ƒlÅfþS'PSQ8Çr°EÚ"«)wÁMÒ@¯¦ƒ!’D7Ø,“"% n°. ô Ps7ieXÆ •6œAÀ—ƒ€˜˜ì58€Õ ^¦=eAŠ L…¤‘󢙿À#2W4[FŸ'ÀA$7Û¦ºq†ÿRÁ6i‚—E!žv~Õ!I2Ò"t™O«š™|åœÍÐ%šÑ‰d,—eW¢Ž*ç´8•p ÿ‰ÁÔ•ɱ—Ƈ bhž©Nc©gÅ@—™IG’|F¡ØœÅ_V K_ƒŽ!5"sNñ %ê=¡ás$Ò —ù¢y¥(Ê8 ¹YBWr+qYf ’3¥N#°NÓ‚F5: Ê‚ E ªÐeÙTïÑ€6£=hRŠX&S‹2¶P*–ó§>z «yª0QV‚AˆÂMŠ€ÚzŠʹt j"«6+€ú\nÊ£t– "Ð& ÿ8õ¨â ;!©tê¤zÑ£ì'‚JU. 2Qs_(;aW3¦hœQTHO]u.kš8"CuªŽ™ à¡4è~JåK'é«'°žZÑ yS¥1œ<Âí!6|A#‰µ¶"6©À&²ê=b³!6ÓF%—Ú}lõ›' &¡‰YQg@EB›Àù˜HX\¥F ”Qõº;°‚»Å À ýz™J™}"ª%`Õ%žÄ¡ ½É!qc^Ða^‰“,1qX9âà²ôǧLxˆÚ¦ùoº†Ø×±ˆ³ˆ"ªj ŸA{­"H´…™9z–Y´Ý7ÿ:‚N«´~údIЧD¬¡yY›¨¤G  Ô¶a[j±œ90oHÐNcªkûÙ¥"ù²\¶Y·¸*€ â¢&Ì4´ab50T+ÐQÇ8 Çö7d}¼|»¥W§!´°`Èl|||Œ¥Y|]#L]•³„\®òý;žÈwÉ•ìÇ”ÿÒöu‘Ì´¿$OQ #ò0>Œ›T´üT VEìÉøWȘË„„¹ ›—É, kÔp;ãʾp ¬±­—pÁ[ÇÜÆ –NÖ¡‚g‹“tp á|²Ô|×ܼv©ð¼·JU2Œ=„D|uJ[MuOP¬Å@ xÓRCÈMM<2CvÅ ‹Ì+ 2;ƒ5ì?—•……¤WÀE0!Ø›š‡Ìt¹y — íS(K`ÊXŒÍ, áÁwÁ­œÐ÷•U«òÐð\ £h ÌÝA³!kDÐÃÒõYŠŒÉ Íº)/¦ÐC žB ¨õÉ•`E™´³-Ùö5 »¬É&ÿ@ÑM [ßÓs%`§P†Ïp |cù<²ïÁ̧6ÅÀsoÄ0foêìÈ*°ÈéTj¡“q\C-É&Э ´¡‹Ä P£¢Û‰DÂ;@ÉW]-â2YqkzÑÎ_³P¬0y€Á) ‹±BsBç£ìÑÝÓ2À‘xÒ°ç4Ž@¤úb¤º2‘Ô¦SˆƒØ‰=Ê‹½ÆË¡²z´or$ïÁ'”" J"Ú 4âR Â-“ÑÃb¥ÚCà×&²BŽJÔ!•&8ìí­àÝb6¡MÁ½*ŠÝÇV) 0¥¼ ²n›Ý •ª AràŸÍB‡R(­²‡XSD®ž“9ksVç|ÛIR‰yO) `ßNîº |jÅ7¯„ãÙ­^Wã9?;¯¿”ÎîB§ü2-G¡$q=ŽË2=È=qÜ_s hhŠl€ch w3Ê Ë2®å N|-@}ÑÐáíú®i³Ä+U¤-kž¼Xíæz!Èû¡ ~­"ñ« ¥–ÓÖççÂôS©À ÿš‘åãÁå]׬.‰£ŽÄ×Ó•±^H‹wF“¾–3päÞ¢3&IQDbÑ¡‰ö²HBå±Þbé«¡:5n"ÒËÛíê1½Î^àÍ@µ…ßeÛS]äL—àn”77úŽ?’_gã‰áûUÕ™îþcïÐ{c^ñÀÕÎ0ñ˜tCÎæÜNǯîëšåÖãOés\½^—ñ™Ý2l}4ôöÜÆò4Í®w ¬ÀLîçF¡$ÎÑT#2Ìò¿‘ã’Pñ³Ë|.å ßñþ rláŸð_ÊzÐð‘nƒ Š]„."²Zk|Wa=V ÿG Ý}*“ 6¿%píÇ7õn*¡ùá=3›ã>öíÔ˨{ )î+À²> hnÿ™/pËæ÷¿Lé{R• ½ÍœãŠŒàÚ1Ú0Œ†O1ìÁâ1¨Û(áE} ˜n E?ñ^ã>ÂK5ß0p80mðV›Ma÷€Þ$ÅLŽŸ!Ñð>&¢2Q *¯òžªES­Dc!\í ¢bNÊURæ™ ¡øm;"–X.”*éUÑŒO}¨âõÑ eü¢"ÉIb1*ž&Þ}Yµ/…ÂYãÊô(¼ßjv75×m?xà…ïÙõ–ŽG‘±à?o©üvRúspú?põÿ™103>ëŽÎš.(B@é9ò½ÿ­ ±x DEßqéä9ÌGí¨,ÅpLŒƒ ñæL…j±P˜DÒ„ˆÉ‰Øþ8¸Á)=Ý=E&*.26òè8&šuy=0Œ4(ô¬@ Â@È@ !dEªM>…6¼B¶FÜDqúŒ0$ñ ¤Õ I6ð4HúóÈí°í´Fdkƒ‡ÿx³Šûœž©ÿÅ|¶¿À¿¯Ÿ!ˆ@ŸûÈ.:,/ÒNŠ²î€½jhH !Î¥{P¨áSnG“#ƒ°mKãmÀ|?‚ìïãÏžÿÉ9á`—¼P¯Jñ0!ãýÀA$€™O:6Bôè ƒ„ eÈ<¸MÒ>›Ñ I24’£Ò›V¯j›šïÀ°æx óÔ'—}´Ìh0HÁ s ›p)È×1€2 Ä2 %tBxhs@Mº±jò×ðƒ^RÝRÅ*y2#­âRôé!F€.oTÄ˳ èН\.¹H WHYqAƒÄ˯ª‘ƒ%ŠÙ9 à -ÈÔ4fŠ´&_G5¸Êç]ÚDôRŽžuÔòrX÷¸ ÁÀ€z0Cq?“fîâG8æï-Œ×H{”­úhæ’xN®¿Op7@ 0)4PÿÇr 0ÓÅ<Æ#òõ@ŸtþC›eÑ€N_$ ‚‚„á´àÞFÕ¢‹/Â(Ã1áŒGDî@ Ý@|Ü8‚°f€4ÀšÃY 5ŒÐävD‚C!Îa˜åm¨%oZ.ÑBôœáIŒg¢©ÐŒ`²yŽ•1`Ù¦œ\BÆæbÉ) c®Xb>oæè^qþ`¡ lÒy"†  (›²ðÙ@H€Z) †Vše¢™r*ÊcB¡fJ)©m8Ñ©‡6%dl£®¨©± Ú%­ÒµzÓ¬·J·+¯l¦ÀVšÃ[¬±Ç" c®“Âú«–¾:›åÉR[ÿ­µ×¢‰›d֜ԭ·ß‚®¸ã’[®¹ç¢û-5„`íGRºï7G¬S¯½÷⛯¾ûòÛ¯¿ÿò›H»òŠ-Á˜®Â 3ܰÃè ÜìÁ‘M\±Å*1Ɖ¼±Çƒ90Èk<²É'KW#ÊŽt¼²Ë/Ã]ª1Ó\³Í7㜳Î#Ó«N6©Óƒ½B¯ãN ëŒÒó?×›ÍÐ<Ôkt½I7}å:N×K´:R#LÕp^ uÖbo½J×MÊ×j¯£õ\«CuÚ@/muÐd¿mvÙhÇíõ:Úîüi‹ØwT…-®PfÓ*„ÛG>8ˆO®xL-:.ù…+„‹‰Ë°18æ†iXéŸWúåGeÎ:œ#õ:åíX.äèQÞn:䲞º¢•Ùã“>{+!;ntpsec-1.1.0+dfsg1/docs/pic/igclock.gif0000644000175000017500000002143113252364117017433 0ustar rlaagerrlaagerGIF89aÈ®Õÿiiióó󺺺+++’’’aaaËË˦¦¦½½½©©©ZZZ™™™ÒÒÒ$$$ãããVVVÛÛÛ‚‚‚¡¡¡^^^–––žžžQQQÂÂÂJJJ………}}}ìììuuuˆˆˆMMM===666{{{ŠŠŠsss111:::nnnEEE¬¬¬www···~~~´´´lll°°°BBB¯¯¯gggŒŒŒ‡‡‡ÆÆÆdddppp³³³yyyøøøÿÿÿ!ÿ ADOBE:IR1.0Þí!ù?,È®ÿÀŸpH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿ÜdçyJèΨµb÷VïÕ‹à2pÀø¼~ÿCH4(+„+m…m(%/lp„q9w|—˜™?8 ž:-##=o9“ŸŸ  ¢%‡=%(+µšÁÂV ‚/:o-‡((4¬ Õ ! ØØ)!22)Ôž9fÎ¥-ÃîïHó„#"l‚Ÿã)ÿÕÆ‘øZ ÝNhËÆ-Ň ž!ZA¼‹Á8{Q«ÇFæ |¸b€ ZÌ…¤µJ%-ˆÔ2€q-…Œmá"Ò Õ¢Å¹ÿ 0 Åã`Þ<5¦:¨vsˆzFcyš6í‚5›7^xðjµ~×d†ðçI‡ŽR:Ð0´­ƒÖ®ÀDZˆ'®™ÈA(ÕT¯l‚00ân­ÖOœ5)¬±ŒÄf·˜¥ GÅOBDÎÚ O3|ˆü¯_ä ZÑ ô¢DÚ(F” G#À'"”)™†.Ïl3+_¢Âtn¢–v»6®Šh ø Á½;BÁU›öiž g éèPK Ú˧À™£ ,ß_Å -h°[ˆ–,Óœa-Ô`B ýCN+ ä0DÚ9FŽ4¼ðÿB/làÈ‚ Ѐ ÂSÍV‚üù×Ó :L ƒ{tP ÙB¿‰öZ\ ÀL6y€7pÅ ƒ€EÔA¶]÷žIãle’?âS€0bfÀ m°A‹Såh×À\˜£@ 'ü†M(èW$L@Cl^©Ò‚#=B ™'*ö²gPæP4´A¶J,0ôØÂ ÙÑ9@&ˆà@ÐÂhD-ˆ B¡(PPC86õ”‚ÂÀLúŽ<Ú¨€B90P€Ï<dpB6.ð!@-­Àr%r Ù@wy’3è«;ž<úI°°“ÿo a hÂJ­,Ò*±R W v¥-业8°Âø²n Ñ\8¼ÂŸÿÀã J¶á£Ã'5Pp-töÒƒ†ŒI -$¼­ùÐ’hpø´Ôð 9˜ˆP ‘ L• =<ŠÂÀ&çá Ò@ 0xÀ^)Ôp }õ柤 >rt a9VÀq: ôôàA ÏÀPH-0;3æÐŒÙ§/Õ¥/5l»èx€Ê kwá@€eÛ#°—A æ°Á:\°ß‚!¹Ú‚ï[¿ƒxP5‰oAˆ ƒB ö ÿfüLÏ `S]è°]¤ŽED0T+0, ˜çòÉÁhxØø}€(ºÚµp o…6ˆ @»f€æ ‘}rA˜â^&gpÁ$¯Â¬€,–Q˜o ˜Ë:‚˜h€") \µÄ«UÉ ðÑ‚9 'R.7‚0 H@µèÀ7ðÀ 2Ð „ìì&³x‚ÜÃvÓh Bð€²áC-'„B §†€hA 2PàèÀÚH  ÀG$hÁ+þŸMi€gzKâàe ‹4Ø÷ŽÖƒÃ,ÿ`íXÛWp—áqUkÀ:{OJ DØ ú]OÎÕ€n kÀ"'·ËIN5€Od@Fd‘IEe0î)àuS>0*  0ß# PRDn)9b h‚Rà”¨,×Ò‡‚ MHI †·8îc Ì÷€x@0¸@'ñÇUràR_2‹° -Á ð‘  l´€Á¦=¥Îsç"°¬•3YïžOÖ9 Ð@ÜBH0>šÀgì­ ‚°žƒÁÈD0®À $ˆ@7 Á†þ@ÿ( @…ð€EÆ3Va]œ²€[Æë€ xäjPSÛu‚0ØÁ%7°†^%Óˆ@õFpGÔ)ˆÀ HP:jT@J ƒ²¡PëhÔ`¿d®b‘ilÁF@tù££èjT-x‰¶€¬xB¹ƒ”sx2Xç^ Óêý€ ¸A ‘À5AK5úŸIÝ@Â@ƒG>@ñ\ç J°Âa1¼Á6 ° Ü`è-u€Èš¦E”—A ’jX¬µ@mj$X¡”!¸s‘sAAlI€i@ˆ ëдJ}Yí X>T‚Ê$ÀÿäKmyºúl;XÀ-@×"²U$ðG«6Ð,Ñ>åE$e;ÚÜ!ŒÀ–+) ¼ÚSìõT¨ìÙ 0|,žÅ%D€“±Þ þB lPJì`ÂX€F‚,•z²àQtànÊ*ðHŸ¡"Ç"Iü€ž’ 2%@ˆ8@´-(0Pè‚tP[zO[¨×xsnAB  ôÀÑ«(–C\M‚¢%! 8À:ºX€ÄØ;@´˜Ä`cÏUR ‘(N@$©qék«@€Ls‘HA à›è¿’Ilƒú€Ü“Qèèðû-’ˆÜ J€l8¾I‚•Ñ×D\€¯“È3Ÿm0/0ÅZ(@Cš,å„p¦”`O÷\÷6€Á*¾ì *= ZU‹ž4âb xIY ăBÉ3’F&P‚bv4åp^!9$®îÄ値óX€žñÄØû87oéÿØ#\£ªíÁp#`K @mºÉ8 eÙs 8ÁiØÒ©zü:i À:–À€\w#8£U‰ýÕTòAOÚ!q†L 5ð1ˆ¢ëIƒÀ€IÐï¦à'wÜD¿: 1«}\ã}»š@˜ä(±Bh€i ã¥ð"pª4ÝŽ’j³P‚¯Ú×dáF‹Ô µØ¦(pŽÜ}\ ¨èœ0§þU½Ž› -QÀ>l,iZÃ3ÜSÖ¡àÊCè€Nýl N°Ì6°“1“àƒ`K,”{’2.½À:í‘ã5¢^0XCÿ¾2ÕodFö{r³B!°gð4)6µeS50€X³ ÈD7@Љ£at~O@EÃY$%00cîTYÐ,´d™ÁkÐ4¿!r ëVt\‰S€O§òSİu0\)Л%VMÀyÊa„oslî±-XÇxŸ€pG O+-H01(\öwä×q]‹JAH e}syB±3Ì2£â1m4Ø‘7(È -%åäðUz‡T' „Ág}=ó28®ÒQ/@€ ¿„Ê·ÍR*IÐÂö)Iã%ày$â ÿs[ÊJ $P·dMÌ…=ñ7§£’s¶óUÃWGPNýf2 Ù4‚U §¥`3<½vlP(ï @Õ TƒaøÄ1µ°6°„`2…0šc4€…䇄@pbFW˜ d@'8 à|$…592Dó°g0pè+ Y “ømðÐcF¶qu:0f7ð9Ô˜%`úð7q ‘&S'‚*Ø Å[¾Ê€)uhH"ð\kÑìBÊp]_e#—.ðW_µ¡BAb;®²)¢f@„ÿGЈ¾­âKyƒr, XügXƒf[o³ZøÇ. /ùH#€;²2†À:¦ñ 0`˜lư 5)ˆ€K–°l@EO™b«À3‰A"f{e† @c25#/ÀÜÒãx«0\!`”à€# ;˜rH^€ºñFg$qf°O-„Më(€Ì7‰m p;È3¾ Õ@t)sŠ·3O„'˜Op¬ƒgÊÑë0ÚQdgKω^®R@bc+¦€@ tB\ÐpPc¶X\¿8“K€_ï™»ã)4:€¤°3‡j¤‰^\(¦´¹¼_YŠª3Š9 c\ðtlÀ….jXL°gWD©;.ò³ €°]ë 1ë¢0±>]€WÿÃsas«¸J`&vͲN ÀÔ¹°‘ø`p‰—€‹=0aKaB[@²i¦\Œ5Õà¸D Ž…W†.ÕÙ¨2ܲW`0µ7  ì3E„ Žaä n{ÃÁ”@4ð \ („@R¼Õà¯"æ*µpF«0»L ó<ÛœÖb61G -)f¸aŒE1(µ™ý ÔJ ¦>Þ4'¼BNÜõóa+À½sü¯xÌõÀ‡Åø )£¹[Y°¡0¤»Èð o 1›Çûs—3Rg„ˆP’<#à!…2‡  ž¬5¤z>¥ #ÿý¤ü ¢ å*š¾~ÔQõ)R]?2´³l0f°5\`c¡"^@c£ë*·ä»6 *„‚º%0'H0ÀExšL,p] Ï 0Q” á]€< %ò*‹ô(ÜüM‘s­;ˆ¸¤úvTE ¬F>°6P š PÔlÀ ^ ^PÏZ€B˜‹+¤@P‚¶ßõWgÄž>\ÓQʧF _` @ÑL’0` Xð8ª)@½X`¯-ðM%­J{¶¡ VXÂÆE‹$–‹Ô—DÀ!‹ô’@b´“v]ð|ÑvÓ]}*€ÿ= ÐÇF à’S#F {˜6€r"P°;aT¼«lŒE°› w` pbb> @ÓM’*3ÐØ38í € 2Œ£TÓj_ðÓä»g«“< —ΟÖ3ª<ö¶;HÇt]s‘Ri6CÆã)¦m8}’íØ*0”í¼­Ó<ÀAéÊ@‚ i2ÐËNà£;a;­bƒ x—uYuFî”0Bˆ¶øpFf(T(×0•λ6 Ñ5-Ùí*à<ŽíÞ”-Ù 0ÐÐFpy^ãOÁÀ²×~JŒz§¢êè*¸‡^é\¼ÿî„ ILŒ$ z›v`,DgyH0¹Ó  @ß‘Mß @Ù¿Ó:-bípÜEÞ[)²c#)€ž\p,s}Š6àN:°¸Goý”àÕßœ>Û“R¾÷°q-ùNy§RT=.@ `“ÝÛ À%â%ž1‘]â=Ù @V Ô·¦ù'«w_ Ó¨H¹H Qê˜ÎJÌ—Õ­“>>:b´ÀtŸÃ!‰7)ZåˆÞå.Ü÷åÂ-&¾èZÜu@Ïq:Øã•sB'*ìKuÞµ3PמSçÏjð¶H;Q(פɬКðå &#Ñÿ‹-Ù”þØð=æ3 åY.¾}ß1p<  pÙ00'¦$7‚È7îÆ@"w¾UÉCRZµgý¬+´ƒ«nbPDÔj s{ã‚Ø*õÓL¢å^âÀ ìÂîå:-âÂ.}u0\ˆI%&0*ò ½J c38¨ß %Ó* §àf¶Ê7àîd6Ý´B®¢C `¾rèìÞÛ.03,àè<ßÀíØ,À¾èŒ-ÙðýîY®°Pà ô{@Ñ¢¦A°ŽB˜Î®MH¾UOyF¹ŠÒIÙF˜î¾üQæwÓ!þëÂæ Pòžïô½è._âÿ’þå ž J”üŒ…» .P7´àKÂÐ'É: oÿU ð…û$xdà @ÂËá  ØLßbî <òÁ°“OÙ#8ß^.Ù1À¼1£r * )ç Þ%m©Y›˜¦—³Bë¨f°ÎD ;`kð'ùÈ>à°fÏažÓ%~ò¢ö_î”oë ì,€C¤ý)ß<0éÀPÓb˜@ÖÉÁ -6l 00¥BYW´gz/=u›=±W€àW4‘Iå’Ù42 Âá ªÿÆåйª®‹÷õb0›K pÀR1ØKØ]*««š°¤``œ ™22@R2 :<DVD<DDH"H4"B^ZDl&'_t WY ¢bx jF!XÀÀ¾vx\0`Hb0rŠŠkzX€gî¬0[»™hVf>&A@V".VFVZvHäå£B"W¤tJÀi¼ÿ[ Å•=8X€€!ÁŒÆ ˆ!€ `¼ ˜B »F 0 FæŽÁ+<ºPAL+2„øÂˆ6¤ðŽ„N6¬H±B‡ +^ÿbfU'hb\™Í…4>(¼€a`"›]0`È#`P`@Ñ3-„8b^U$DëÁÍj«)ÔXAb…ˆ)§È óG:\Ò´Cc©2ø+<ºÈ+ ©Ta!@† j`HQcÁ!2,ÈÀ)Pd åI `0  Ä $\°h©ÂN V¤¨0 dÒ„„ð¨`…ACH~bƒÊ5 P‰Ä"nˆJ#Ä‚ï…è­®Á 8@€Jx†j(%`À`ˆ N¢A! €aÒ À“¨¡æ€¨°`ÿäqŽÿÁ·ÃÈàJ) €~Ð!.Fð±… Ô!á<ýR q&4¦¸ÎŠf€âL¯€ J‹0@@ËPá±HP„cØ#ŒÔ¨@;vˆ …Xs’‰d  d¡€0éχµB@arÁC!˜NôŸP£F8È€„ €€jj€„RH!H° .V8a9ÁÁÍ“ pXm /@…X^\ÀF!À‚@=]‚F(ˆ•'jðá‚ x€hx¡€È1‡ X¡ZVx6 ƒF5 P >ÿrAMˆƒ$ôÁ˜v€[H€¡€4`Öàá¬ê¨ˆB£0ia„t¸óïÝ% €,….¨Al  ò †©¨a€&˜¤€‘ ¹¡Ù¦ìB€4^ž àBà@@ º‰ñ‡@ØÆ6€„µjP Œby°&ÅÀvp¶3LDˆ`V¡† xn¢‚ 8AÛ HA€ÒƒÒáê„ >¨™a€€úæÐ@¨.(€…J P3L‚Xô.¦AÀ¯Zb¨ûlˆ@ƒ6ðX‡fþ‚Ä™ø „xj`*#ÿBá@¯à›hÖŠiüRak¨á€ 8€€‘R !¥ë¹f€Àjè  $Th¬¨äÁþ.TÌNžeÊPà#| Ðkh “ïü`R)øÚ×°#6k€ DÀ‡‰$@:](Ëà @Ü pAHÚp=Š@ [cÃÐ ´°l~I€ÔÊ"+ÈBc;ðîDÐìcÈAP‚,p‚Ià€œ2@„"@ ¨41hÀOƒ<ã„c؈6°€hƒÕ(çÚ€¸4`lÓSZhCЬ†1ÐC²£‰ÎŒ LAÿ >3$–`M¬Ê2@â Œ¨àaœL„"mà °–0€kmXÀÐÀX‰Ð€¨ '´¡ ¦A… èI$Ø ZP¸àˆ5ÉWO GÎ"PAKšÄ$qYŽ0:ɵ   ä:Ù†68È×ZtÊ& cY—;”…,1‚´ @¤"LfHdâ2Jˆ@&1¬ÁA/ùE…|€”mÈF,ÅÙ®u!$Ÿtà 4z²l<0vØ,íÈC·óØ J€‚³=  T@aº }Y’¦&¡)4y—-¬&•âDa,ÿëÄÎÉZOc†Š Lx¢Bvâ±´ L‚`–¨"¦]U‚ jMƒÞô‹á Á@pV@<`kjbj*LjÌ mX)Åɨ@ÞŸ”$‰`ÂíGD¢KWæUÅ*¡ Ô´©%É g @”&ÈaS¢,{zö VX–²c”Q´ LÁ'8® ³ù8`±µýX¥‰P›N–'hE«e‰‚-@¯1Pç„:Ïí\à62¶=uâ#A :˜ë°Æü§mc*›ÕtšÑ€YÏšÖœ ­¾ eÔ70¸µbžì$ <'ÿ ¦R©Xbð#5^4=ià‡TeJTÀá#~"qgàõêLskаF6­$¸$FÐ#}¦ÆüTÀ¸­Xñêš' ìœÉ=ç<`-€Ab,£dɦ$xj é0º$€ö&"àŽ¼*à®K““ðÚzÛ˜εe3la¹Áà&px\ˆœVÂñݤŸ7K;T…é<¼õ*Y½&`oNv(*„ }IÀ&Ÿà‡î@¤(U$rôÖud+hjø·EÖK ¨ÃôÝÃ;#˜q•À½0"0kWÝ»ë€ó0¿™‹_[‡û=<¥wozõ‚ó]CFÉzÃðéÅ‚Ý^º›Á„ŸVŽÕ¬&h SÒ/Ö0 ëä,ÿ€B oìÈkuë’`@ëyŠ 'ÌÙ¬ Óƒ ãoJɱ‚¸CQ&àˆ²Ñ·Ó À±Â¾(Ë‘-‰Ý^À×Ñ9 qM<8Qú(«É‘šÆjÍ`À¤h‹q¼’Ñ ’uË“ì~‡!÷1ÖïìG±$ò’*Üh #²"“qš¤ÑǤéð(«MrÃjëÊŠ WòÑÌ‹²'&yRÇíWÒ £é‹L€xò(‹À/ÈÒ RÍÖ¯²2*o ù.Ñ2Ò,¬l×`R*×ù0 ÍP`+¹rçñ¦ k댲,Ù’ìÆPq ÖØr.` ƒ$,<æ2/`ìlæ¬ÈR/{R4s0ÀŽ031s1‹ ;ntpsec-1.1.0+dfsg1/docs/pic/tonea.gif0000644000175000017500000002734213252364117017135 0ustar rlaagerrlaagerGIF89aÙ´÷ÿÿÿ!!!)))111999BBBJJJRRRccckkksss{{{ŒŒŒ”””œœœ¥¥¥­­­½½½ÆÆÆÎÎÎÖÖÖççç÷÷÷ÖÎέ¥¥Î­­÷ÆÆÎœœ!sRR1!!cBBŒZZ΄„ç””œccµssÞŒŒ÷œœB))Æ{{­kkJ))ŒJJB!!!J!!œBBÎBBZÞ99„!!µÿJcsŒ¥½çÿ½91Ö!¥ÿ­¥Î1!ÿ½µÎŒ„Æ„{¥kcc)!œ9)¥9)BZ1)„9)ÎR9½œ”½R9R91J1)sRB½œ„„Z9ÿÆ”¥sJŒ{kJ9)B1!”Œ„”„sÆ­”Ö­„ÿΜ{cJl”sR„cBÆ”c¥{Rç­scJ1Þ¥kœsJZB)罌¥{Jέ„µ”k­ŒcµŒZŒkB½¥„Î¥kkR1J9!­„Jœ{J”„kRB){c9½µ¥ZRB91!ZJ)””Œ­­”cc)11ssµµ½½ÞÞ))BBJJRRkkssŒŒ¥¥­­ÎÎ÷÷ÿÿœ­R{œBZŒ1Zµ19R1Bk91R)9c1ZŒR„Æ{sµkc¥Z)”!ÿ­µ­s{s{„{ZcZ191!)!sœs”Δ{­{{µ{BsBc­cJ„J{Þ{9k9)R)9„9cçc)s)JçJ9Î99ï9)Æ)!Þ!”ïs”¥ÎÞRsŒ­ÆÞÿc„k!BZJ!B1J)R9sB„JœZ!sR„RR1ŒRsœŒk”„¥ksJœckœŒ1!!ZJ„s½ÆÆœ¥¥BBJ¥¥ÆRRc))9ZZ„!!1„„Ö))R!!ZJJÎ1R!!„{RZŒ¥9Jc{Œ¥ÆÞ÷ÿÀÀÀ!ùÿ,Ù´@ÿÿ H° ÁƒI!\Ȱ¡Ã‡#JœH±¢E‰>¼0qþšðâBãÅ“(Sª\ɲ%ASüÉô7ÈL˜8`°„ÁB'Dœ3«œB°RAŽF„´€— LùS(Í™;2P€ÊÕâ•™:Ãì £Ç1*>©£óC:[F 5ᔂ ˆHA¤o_@œ âo •#f*à”BÜ&ÄàÂD&øQ"…g¾þAH@ €ƒŠü R,3˜?“]cC¤¤Œ¿@e¦±§Š8`àˆ#`¦œ/ %P€À $&X 2"€‰¤2dÀ€€LÿIÔ ž"âF **öòEâÔIp§XËD–,™ìÿ…Ð8Ì¡XSlñG[¤‘Æ &˜›døQ…€aŠ}Àžg'ˆ0ž 1ƒ þ ñágì•¢g%SL@œÆ‚Í)$`´0 ŸZ#@"‡JǨox°M!ºð D`P¢@èBHd€á!¯G—›òS` kX{¤DbëY!pP•D&€ÀƒÒ€¾-Ôá ¬‘䀇3œ|MCúðÐW<äs8k¶ÊØ3šÂ¨é;€Uq™È4€=ƒ@p ô¡Jê † kÀë.ö lÞHKÛ–ÿL€xpƒþp7¸aUHƒ†«14`²+Mm—ËÜæ:÷¹Mò§þ UèZ7"àÈC4‚2O1sèAD@J'=€5UÈL¿ëBºØ@\ì”±ÔA aÑ ô ÌA Ž[`€`+°ÇP¬; P ¬ÉjkDN¡+­²dlƒØíª°ÅcÈ$›îEˆ`u£ß±„E,c‰ƒ­â, ¨Û‘ ¦‚x†, B«ˆ‚ðE& r‚Ĩ /ÁxV †°§˜1Å%ÄP2a´2!/B<‘ ( ¶™Iþ°…©(hAdfMÎЂ4ÿ$P•gHP…t† Jð£ÄDHð üâ*8äì1ÂxÐ"†ñ%j'!EˆQŒbƒÈàÅ)¹<›4”A Ž¨GMj¾Êaq~ýkÖ°/ôAm¬Ä³Lä㙈 %XÁ D¸`HA¨¢`CD`*CÈ|Ø£ôp1T(À1ÈlšÓ q@píƒ<‹ >îAWe’`S™Ê¦6/G€‘ï|G˜ 䨀’D §8@Ú{àˆ4-2Ø>È ` 0 †"T F7§ÎÁæY²Ë¡´*©Õþ iÒ°Æzÿl@\n@ù 0ÆeOIW h¡D ú8&áÚŒËS€5=U@. äP…;åÌ¥`—?(pŠRYÝ1= [¤‘ gøš¹€?fÎ¥`S0ÀóHGö™ÉJ €Î‚ Üq"wÐ ¢€Ç@Ð1¤ÐšŸ6WÙ4€Ê‰;—0°6 S1 ðVÙ` œ7ß´2À2ºrm#P€ƒ¸¥.B&È@—(6§(oCpö&­•#E·Yòra©ê8UAÌP†Lù£m*k€~Ÿî°  ùîbU«A¿Ïè8 ÕEʲ­ì£÷™ô6ÿ* 2èO?÷(Ñ=p6Ä—)ê‘}ànRø ‡ †ä;tˆ "@ž!G0$â}hC§ð$Š"üTV’q@pÜq5`s§F™RPå^§@ &H ‰@ Þ4 ˆp‘ð‚/ 2 ŽÐŒÐ‹ƒ7¸ b pue”]U´®6ìÒAÒ ËPyà`u>7@q10ÝFãgoÌe Þ#13¢ìÇ=Ž@PÀQ¦¢>¿6梄ÐÐ Î úQxMB ¶wIZSp@¨`p°=²×ÇWFN2Eàñx} Ýÿ` ÕF© P*F~x˜9p‰Ä~PªÔ•à}ᔀ9ñxˆ˜N| b$ ¡$»¨£Q'QÁ#Ãd PDáx@‡8×,£A ðbDÑw‘`~™ý ñÔôðçÝÃa T ’ðGS‹±@ `$8´Xá 4`¤ }¤ÿ€(M5ˆ'…ÿA A0B@m† cDF2æB•% ° š  ÒôÌ$ š@‘¨ð9 dÄ4°Ô‘Z8U€UzB¡á àx¨3sábêSµÈ‡ÍÀ °@L’Q£ÿ¡{§ ss—¥‚‘5A c$U¥'¤0YPb%q\` ™DQUc{™|X à4]Å? ` !TÀ#ÑSà èÓŠ¬Á¬æ…PQ¦àe¶·0d™˜p € 0‘„UþÀ{ GrðTBu¡(ÿÐnPXn°ˆ•lù@и—¥%kɈÅZ¬!™ém ðÃ%wüƒ¥™®ùš°yHz›´IDXÅI0Ì,€#¤Šµ¹\Ú XpðA :pà`Áùœ²a À­¨8V©šûhŠÒâ\¿HðSb ÿ Up2(ePbcdàÞ²”ôp ¶÷0v4d 7wFº—w0¹q V ´I_2±ú…p ûUbpà|@vDA3Ô6žôÀªG[U½WD@ §`F @Œÿ00¦À¢ áÑœp7¾à×¹—#æì©už&ÆkÀb!§À^Î8,2ñ&0g%âŽÖ@!’-Y‹av¹!ƒ0DàdÅÆ", u0ZuP© à ´€ að  :(†?áxË&^¹-UgžÁÐ;–Yÿ ŒgàDd%€.€fBÚ š{TV0 É€ <·—?Guqá~NЙ3A!S÷&NÆBSpS$.à+ .à(eR^)ÿéf…A:†&ƒP«)pKÁ Ì€@!üQ ÁIƒà@  2Ñzª§'6bfÀf@`°!¬A"QÊUp<¹ñ1ѬB!<{q¡A¥7673!¦}QbPp!ip­Y›¢Å¦u`fЦmúm `ðûbPp@VpdpKðv±ä'àCbcŸ*p)0hÿàU€˜ñ, ñ³c M79ùgEU0&0›zZVЩtq R«^à] i€Ð^PZ@& b+ú%ŸÃjgŸq•*?(ð¬'@Qz‘þg)À± ùHì mPq^µ®I~QgP%õ8›»¨ˆâ¢``2ácñ6ošË,^@ya“ó°*þ’Y/®¹&ʦœ‚µ ÷ å0°†€1à1SFÕÅ¡"ï³¹ž¡+³±)âÒ—è*y++1•8šÏe @X:¤#(…‚‚r(ÿ9Y`õYÔ8)ù )“`(¦`-¯%  20631G°ge¢sZˆ6qÂ7 `”! °¡r=÷sÆ § ’aZÿPVè,fà¸ÎÒYt'û6S¤6Š'.@7¢3îT-€6JA'@0ºàa .¢‡y)­i>ç¯ —›£/ô¤:ÿ@'°ç,ЂK£29‹2eSA0Z; àIFþ0#Ô%©»F'Ðq3¹!0øˆO…`Æ\¶›<5X³T`J\š*Y”öÀe“6V‡y*ÓvÃIv[ÿ¼†\r%Š!.Ð>ä¨M¬ A• +ð'â #EПóƒŒBUðœqñ]¬A ëHFŽ!z¥à6Ò  Ù`7ÛP/Ø[*´ø¿31¿ãr2¬p¬!¤@hãðÌšx¸›ã¹ ”I¢»ƒp`°O-å@Ãn‘6'ót]bÐA3±A2ÑcSÐrw6ËS9171‚v¯âÌ„£<¥ºØv ÐW;á8,,up^Y6`O ‡3®«ÌD)/Y´E.£ `'dƒ3 , Ð?x \PáN&£¢[á—xÿ8râÂÈç¼uP|¡Ò“•\ 0CÅùÀR˜º¹ŽæßYàÊϱyÖ4‡ëq†r Xm(×ÕúÄ45f0OâÀÈB%Œí.û0T»ã¾S1¡!€h r­) 9p+FÐrˆ{LC{>ÜX  Œ°­ƒŠÙƒ Ù‘]ÙŠƒ”mÙf­ŠÀ· ‘ú{`ƒL“úŠ*g2Òg&ØÒ²a§kÇCä^—y™ñ¨OÂl#y$™>›âp n 5ûž"Â/„³UXÕ(K!FŠêWÝ0P†ëm Ï32Cÿ¨6¤ÀR¡kTŸÔAÕàT6 ÀŸÿ ¨wgp)àp=Ьf 7 [´ þ½ ³••\[·­x«-æsß@] ¨íq# }Ô ¦6¹aS-­À Ñ/ÌC¬  Í ®à ²à9€ ¨0 ©ã©0P㯺 ­P[›# ܃°+C“Ð=#z¸·ÞjÓÞ¬ÑRæâIÛà ò(7·ásºø´Nò·…`˜DGÛ3àdäV®Ÿ(.å¾ Öà ÝP €ÔwedþÆT¾¬V|[•°áÙM ÑY–xÛÏÀG}J(ÿ'2‘aÀsHú »•Àщ@)QE®çÞX ¹ ààá}óàÆ `­i·:×%òЉ ˆ — 0 c¸Ó\š- áé3± ÖpèÑ`áÕÐ 3a%f'U: ÄK=÷J0Q tÌ<õ©VÂ4®c½BC•‹gÖiÀ=‰} •°=`TЫ>£Yh® ÝÐNè]Âé6˜Øå,lu'ñ¨G1—ÿpVÀBUS Ϻ}F°ã颧1’pcÜ àÓ{ÛÞ®Û¢étª³sèd7ìhVÿu æîùÁmÀ#¤/ `†z P@`â„4%zt,,4â¢qÐÓp¤k'â5„b$1Z` •@àâÔT.éo!Uv¢"ÎA69^åþÀñÔõ±Ý¶DôQ²+¤y'Pù‚Pú£€‰³4Škr§ñ&‡PÎÙ(•õ%qøG Ähg3p  ëÁ*åq@'-]:Jù~I.Öº9Q¶D$«çÛ…t^úAþà…QQ0´”«þº‚¸Ž¬Ÿ~™:<’³KTI)zŠ×&“Âó<õzäW«¥l SÒ Ð'ïÿ%0ð`ù2ïxØù.u|‘Œ1›0à]À…°aÛª¬a€ˆªu§Ð|ràø÷Ô?„ þ£ð@ÂBˆ%N¤XÑâEŒ5n”H!B § JsÅßI”þÎÔIéÏÂ@ÊàÁsÏœ”SÎì¬b³åO …%ZÔèQˆ ZT˜Á”[hÊÁ“ç8p꼩#&M‚ŽñÙ(ÀÏ–i þ<…Ôí[¸q#F@¹'Z†*øS•©Œ¤úD8Á>iΞ‡Ì¹‘%O¦Ø§e;nvÞý) ÜtR&]Ú´Q ðÓEPŸÓœ¦]ÛöÿmܹuïæÝÛ÷oàÁ…·½ hïãÄ•O0·l X^w8€Á!ƒºuðá)^Ðè`PŽÌìØ!ä¥xøð+Ç8ád•8)aÜrbƒê(p²ø¬«ÀŒ$¼NÂ,ˆA"£€©‰H) ’˰@¡0€Œ–þHCŒ*Nü%.Äè©H!`´0ÀàL–3üI…#D +êcI&ÃÀbŠ?à8 ?cŠHn! zR €€ 2¢àÌÞøc‹-Rñ§“tA†•!'š@8<胳A˜ÄÂ'›\òŒüÖHc‚ Ê”ÿÈx„اJ+µ”Ò}üèˆSêÖ0c“’YÆ묈30°À"Œ6}ÕIW—i?&舰þ‘ "À{.5öÒS„³JÐ\ò—…‚%Þ¹Å(Ãc†Y¦÷‹H£¥1ªhµV&½BÙ9|¡"˜À  % 4ÄHAà"”(XÁ’à‚¸` %XŒ Ðÿœ¢ÝK’0R.c%¨ HxSôÅv3ôG1’QŒѯ#?àCƒªð†6Œ +J‰HDl¬r' BIPÁ b3p øã‚ ©ŠT` +Á*w„˜ Õ"Š+~‚ŒdäB‡$  `€‡ð€D â£zÔ¡ SAõ6fœ@jh @pŠf1à*Á ‚MGXªÈN €°!m°¤äôR”aäb½è….r‘ŒdÄbŽ9E´c”Œ »Ä W¬‡œ…A'1Ãûz¥ 0j(@Æ2F”àlIøÀÙˆ€ºÿT!c ´æÙR Q„’ƒ`刀*8ˆž#J7€@¸b³DˆšÀ™¤Vü›U­Ä@Ìþ dRÓ^JÆÆBkŠ` iÌ ’@„"­ŒT8 6F„y&„#@ÛXpHqTrg›B3²€`ðqHñÂŒD`? ©ÃG´Ô!?8ÀD0  (Ø 7æ Ayá›?–Ú4´Y/FK ,òTÔuó”¾êƒG°€Œ^$¤`ªÃ 0ÀøC!`P·Â`_0Ä ä:ˆÈu$x½kØ:…-9€ °6“–øÃŒAA q„#˜ ÿ%"e4Ç5‘.TÊ)ùGúÜRĈ&¥çB6ð»?¼à\xÁîp‡=8 i íÒ°ÜÞ [øƒ(‚ž$ iCšB@¾… ˆLÁI–4³!ö¬ 8Ö=þÁɸ`Û-]i5‚‡Ï à˜q@ªÀŒ†El ¨0ˆPiD%ª›6¶b pÛ¦¨HqøƒH¤ÂE•ºE»iiiI‘·ŠP` {@ˆÎJHà¼U„€?ˆˆÔPVªR`å.rŠ WѾu)@„ë(°)|ë]…@8qgø BtüÒ% Åç @‡·&R®cÿñˆ«‹ØxÈUŒÀ) €(À9ÿx pc‹‡¨€?Ϊ7 €[žHâ` ÛƒR÷ÈG?ØH œÅU<)*p&Ùü÷$h#j“%‡„]D2à @` çQ6ôg'à@ °IÏ*¤f°ƒ)äQ©xT@ÖEˆ«hSPñ¸™ÏTT1>Á9 ­1#8@R\ \@$¼2ÀúËÀ V:l+@Û§Ðöµ€€§Ü»†³ôq)v0‰RÜ”]f 0`ÃhI¯GŒÒVQnvh’<àÌÞ{‰ÿ.@í:U`q8½Œ¿A"‡ÿc÷°=Þã``ÊTD@J¿¶á té¦(9ËD0‚?$! (ÀX+ë*úÙ ­).€|_ä=`çOñR`W" @é( 8Gäxòœb¥¸$Qxó\cWqÙ9J4þ€`Z'€)؈wC x‹C÷ À ÈÁ ³†Z< ;‡€ @‰!4§j@(€ 0ÂE¡8€è”(#ÅXFñèa¤" ˜ ,Qœ„P *PƒSP¼L [‹< ɳ°«€†m8‰e°Ã“x3›9¬"”8€$  ¶ X •S4 ˆ(äÀ˜»ˆhT°JóK!Ÿ™Á€ƒf‹AÕ;>Žœ:Ï©€µK1R¸)kØk¸÷3ªCˆ S-‰ˆ”, ½°9GáŠ9³1C € ‘hÀÄ p›b2ˆM{>d<=R@G h›è€€ÿà …)ë´iPqšæ2…°¨ÃZ€ ŒJ6¨4àƒP 8 ˜’½ ?RÙ¬¼5´Í2Hˆȵ)‹€¢Ü8 PŠ-€¤x:ø5ð•80bH€‡Ä²º<ŠHÇÄ ¿gSÊCÌWk°ÛTÀ=¯@`cч¼H)Ê©œe"60ª×Ù9@ƒbê´p¡‹ â“1ˆP3gÓ!¡‰a–è€Q  ÑʲK•…X»  Õ° Ãc “&[&1é 3¨< X/°¸€ÓŒ ÁôÝ#³¨ «ß €F`„FøÒ/]„BX.à‚;àÜÿܲ-6mÓ-ðƒ)ÐM7pC?·Ø¸ˆ‰9ËóJcÁ‡“øµAP 4ƒt:‰ #à—A8ŽÅ¡¹ x€(8(‹á4pã6m‹1)€¸2x´ ØÀ À»ËÂݸ=rœU>º‘e¼“ŸÈ¿Ë€2£{Ø,ø‰²‚‘*¸#LÜ8¼Á­Ñ²c€X€SpÊîq€ð™†„ÕÝH¨p Wq¥r-WrWp-×J0WJH„D€,9Є°—)P¼iC ø‚_Ky½pžÊß8X„GEEXˆ]„FpGxÿGS‹­ØGØXH°XHèXH@Ix™×Lˆ€ °€?“€µh©dD*:€ª*4&Ó˜)¨Éá8Q;J i€/¿Ú¼ ÕŸXˆfñž+[>D)$4 O 7`€.Ø4«àÒÚ˜€]s›ÕäV߈t-[³=Ûp ™ˆ­©kÄ©ƒ5 :yÛ8{¬†mÈ[hh€L…\½)<”ÂW¸N0x8´m\³…„…hÃý‡2û²³8•íÛ\Ï ‹:¼í†Éô‡B4°;>åñ „P) ‘õ{­V­¹Á ˆÙ•ˆ 0d@†`ކ܅%ÿXêðˆ‰A˜ã=^vu\HH“’€“Ðè°ŸðOvKWÛH¨†ÉÌÛ¢“¼6lÙ¿‚F¯=…àÛ—#¡pKœ:•”X†eH)T?@WJˆ„H€Cx„H˜J°_è­§œ;ˆ·´‡A \Ü8²ôi†h0@xÄ‹>Š;ñ+jÉÎýTO8e8…S€vW€O(aX FaZ°–…å€FWȈpXqM„€ˆòB”ܵŸ˜J˜Åh¨¼ ]’óKıݘF×G˜Üˆ G×àÖS`7´€”5‡™Ã)£W¼ÕkhÈ[˜:®[ÿ%†‹³(p`ˆØ¨G0X… ³¾mÉŸÉF;‰2Î[AÎ[Àâ Yãçx^„ˆ€×$öžˆãáž[kC´à\*Zà¼5Ihèdk¨, ­È€ FäÒheÜ.žT5pýƒ,”®ÂЈWe>ZM`édÊdm` ÉC96e¸h;(€¥Š;„Jh®MˆøVF +|H ÕæY€ÀÛA®2&âr¬€æÓȇI¡”D †°cž¤ˆøÖE(Š3!ÚŸˆ]¶Cg¨†Å0€ñ" Zc €Ká‡*šAM „oåRR°ã…¤„•g´ÐælÈ[gâÿr$Š9¿3B»‡H4YˆÞKˆu†ˆz ¨³JÙ˜€´  „½„x„‘І®WS¸éŸKÊtǼàGƈ•ü‡¼‘ÀèãSK9‹µ Rص…;²L€´T@؇°êÀcÓ €v¸Dd³ó‡ÙƒvõžI(Ùas€ñá”PQákhm®çÉŒàËË;é(“¿nÍ `ж‡1j_Ñ”FŒL Kñâ#€Hp=P¨(ƺHH¼ÈáÂØŠŸ~GzކŸx€¨ÖFé;¸(³ƒhº¼”T„ËëÄÁKŒØBbËãô†R˜4†˜å˜E`ëqd„Cøßp-e€{ƽ hœÚ¾e˜L¯Üj€x@>°éèO¯®ˆc©À‘`4kˆÈ›‡ZÅÌèÞÔ±ÐCŸìN(BMùk†/ÈG°0€î¬Ý‰(€>ˆÐÿ*Â4Ý;.2…s”{•;!ë–ï…x(ãzhTñmgYßNûš;м£Ž/šˆ½f˜ÓÖ‹¡+Ÿ• ÍÔ‹Ú…±¦ °×úX)4VÁÏÙ®í͵9 1º„ÄëbˆÑbÙ àiF ØkX†oH‰x˜žE¦EÖ€ ó]èˆøŽœþ‡èO-/å™B ð-€F¤Ð •J–…àÊ΂0²ü 3>UŒçÕøˆ#TX Sv§SY`÷NYˆ÷NЄy÷{ß„MX0‹0…˜Ö¹W–"… ‹ I»Ÿ8·}€˜ö¹;àIý´ÿo;<Á —âÌFç fT4_¼aÛ* î—JƒŸ€_)!}p‡w8·KÁhº@‹ÈƒšÐ‚|ñ"mÃ`€gL( IÏFdW˜€  ¾3zR «£GyrV šjO…YH€gS 锞3¯€èFˆL ¤V]!‡cé‡yX>ʃH…;pe…8Q8c=–5€×xå€PTiÕk4ä@€/€í*ŒÌ. œ €J‰*ù °;ØNçh€N©k“ªcœAæ¾WPÈ·FTˆ¸¶„˜–ó›¨8HƒK ;`8Pƒèèƒ`WÛæ’ÿ éðÞY:¯q'wº>æ¶áÿ6ls §¸#=5€ã µÝ·˜J)\Ò –šÀ9`ƒ®k‰A › ’H8Ab<×{²ƒ©ç¤¹>Ô î 8`nl1G|¤±G~œÒL¸j¸!‡zø!ˆ!Š8"‰%šx"Š)ª¸"‹-b;ntpsec-1.1.0+dfsg1/docs/pic/alice61.gif0000644000175000017500000002600513252364117017246 0ustar rlaagerrlaagerGIF89a„´Õð©©©üýIJF ¦¨ÖÖ Ÿ ôøTWgfgwwwÑÎŒŒŒ— [a100üŽŽ„{{ÑÑ J]­«²Ëª[úJM™†W30 ”­Œkk+0{Œ{vq Ì­ovjI ƒZ[Y¯ö& 9;”/yÿÿÿ½½½ÆÆÆµ­µ›œšŒŒ”çççÖÖÖ÷÷÷ÿÿÆÆ½œ¥¥ýÉÉ¥œ¥§}z3 z ^}cÿÿÿ!ù?,„´ÿÀŸpH,ȤrÉl:“/W úzZ¯Xã pÁ\Ù°xLÎÆ&µVk>F' ‡ÌU]pƤ]îû›/8j4 uC[1 14j3;]J-5/ š›E j.'|C. ¢jj 95021SG'3 œ·~/‘j†ZR39 ©30¢EË©0H.‘ѸÖG¥/j 8NQ28Ä©!18¿B¾jéC01î×H/£EQZ¾/4/bÁŽN2è·MßíjÀµ¬š2Zà˜`‘È€„ûzI‘%$Ð. ‘$scA£ Þ›ÐK¡˜{Ëyq"Ù¹ÿ_/þ1˜·3jÌÀAƒÆ"Y\|jÁ`¢½K i’1AM…]R9ò5¤’š€6Äq¡qA­šº¶˜É0ê²l×®EŒE‡†éJ#ÖRLjdŒÈëÐeó^ˆ0BÆ N˜p¸’¡fÓL}´ Q«Å=a.á@[aBâA‹Yƒì„ãUŒ0l*¡à•p!.2ÁÁ`¨Omk1 ’; Ì 0%•ã0 óÒ'ùô‚HГL‹4 ¹óã óm´è5Ñ\8 Q B*4RCL?ì§Æ.‰áò‚•Å0Ï_lXS8)bU½tUÆÓ\Gÿšf‰€-È0‘~©Œ0B4@Ñ„Ž['=i4äÐHäÒS*5LS‡t@ŽP#38 ¥Q·ÀnÀ€CzOLÛiœÀ@UNÀ †1ȼÀY-©†r·ý÷¤# Рä^¦"›Dxa WT>É8æqagjÔ°sI†K5i‘Y/™Ê‹Ú­QÇ \Å M7¹È/ñL€&„mhIdHJ8µTÃÜ P) ]uô 3ˆö~4™C©èUF4"ÇqMz0Eò 3Ô¤暨-¸ã[{Àä(Ç&¥&0Œ°€+pà³ÿD²^u„ň·¡“2IJvNaJq„ꯀžX Lè©/>¡X{çpÙpâí1†¡/ÌpŒIY飂﯎xò'%”q’‘%ß1ÆÂ?h6p *Î`*˜8L GK&žc+-ñBvp‚$s}lñZ^14™]ÆÄxAIa3LÃé10G;±æ*iÀB2Ø,GI{5À(ŽåmÎDÈ•˜ÌWL0Â0#äiÅiãH3c¸pC  ìtʸÎUD 5\^7¸#C#ñd}øøÐdhlÐpÄ âÃßΫC òpׯ ¿‘”Ú--wE¸àwbVEÿC tAH=e<Ãq24ÄDÇ¡)ÐÇØV&¤¢Ã'ÀÎû!R~¬Ä]«¬¬‘‘ÍèÐT„I°AkÄÔ‚Q½4°˜aä # 5ñÉ*šÎ C4ÿB 5’Ãh¨ÅRþ ŠáèJ'䢂›Å ø]z#ŒZüE#¿p€Ë†Wª,P ld¼Õ™-táUÖc‚ƒ^S0:xq”1ÓùÂ1 ÀXj` :Œ° ¶1Ámr+å@7à@P€Fp€À‰VSÞT%–Þ<¡ ÉØæR"ãí `rŠš"‡n5a#[C¦Ä«àO Àÿ`0ÒÜá6e hÙ Ö‘-‹ˆ L0›¶‚:®L- &Òå÷Æ8@6˜c: (àE‰²R%–"ˆÙP–—¥aÐÑdÑ9æ¸Ë283àÓ(=*qŽšL¢ˆ¿7-¡+FŸiìƒQ¸ ÷Í!¦ÑšLš%Â~à ( 18ž "€2%ˆd#ÕDG dò—Tþ‰Aœ# Ýœ™$³ˆešEˆž~ !%ÉgA*'._ó@%ôêQj¡;Ý9Gx@žó¬Ù\ G+&T!`˜@v" ¶!çrÈY@9AÕÌÜ€;ÿä«§?ÙQ°©ê °£vG$Š€øÃß-‰À®ŽÚáÜ]úCƒiô„S©J:~¡nò!ÐY¡Xðâ™f‹2H¢9 ÏMµ ݇xNpœÖt# çsÁ  Gp*9ñèEùQ…W%ç0^KSÍ切9eç0;0;R@EV³š9Å-À@!œ|8傆—NÝá É‘ƒðp›mqçé”Üà¶T UÏ\²ÓБ‹çYAŽ£ cöèÆ¥‚ÿÀŒ?È@$a”Ø|4HÅk_if„ÖR¬ôLÂk«ƒÿ‰åfU€ :ÉÄ'ÕnÞÿCcza\±ØÐGŠA +»â¼jA EÝ7ë$žªe-ˆ»&P8ŽÀ¶Iô$þÒ†@TÁ…¦ Ê·j A¤s xd@î „˜D<»¨£À*í,¨…iT:™Éc4¨ø ¿R3¢G[t@þÂùŒØð U8‡”’³-)ÈàlF` ¾Ög…®Üß2Mv:9Ç¿ø#Ô×Þ¤6 k‹çðŠƒXÜ!ÉÉ%Bz#¥Xô8î°n à›Âzz‰h@órhµ;ÎQEW~b†*¡*L)ÐAp3b5'øè.» i©¨—È\\ìljŸ#a¸ñmÿúØ+¹-;)àéYÍzÖ(@ËêdÅ=Ÿ Mšf—PÇu§!š¹…‘ÀmÕ Ô‰¯æXà 8åŽ'žd6Qgl2ñ×I2Ä €'SÔ•VMàÎ `A“|T:˜G‡½Àઈ@Ùù(— <@†ŽÀ}dÛü•ËÛÆã‹G €:d"28ÁÄK4O×#Xc BP…ÎmÃèÉ]oü&…Òf÷6íb¸S€ƒ þ~gÎCðT¬xŽ•§'—ã¤ÀáKXíý:àNø`|¨3…àÌúaƒì¼ Ž)}<úÐ`ͳcÿ¤È*xv €4Ú70^|Š1½"ò¡ T(˜k”KÓWy$Vý »à‹aX<†øK”S,«– ìpçNrÂâ·6ÐùŠãT%žÕH)0)Á)L è@ @°!Šgࡌ¿P«‡ƒ:­é8»¤ ƒûëÁnÙb°,@ü<àñ6°-pp™Ô'õÕb’ƒv ’ÐF˜’–]pdê§^†ö°0ˆ?¹þfëtV·—LdI5Èvh¥¶y;øD ÐW=ÓLJR;ñ Lqb=@‡hfC ¢2%èV=)a¯Â Æ#Šð²³> FD'X«s ÐI/Gžµùx޵Sv¤sFâ]Aå‰l˜N[üRqº:°neÐà—iøÃX×FÁq_Ù˜@ÿ”3v¹„Úé=sBàÐs@yQ#?Y›àXDŸœTL¼Ù—ñ¸€Û6ÜaÇt§:"65˜jp“V£T0Zô‡š Y1!@9'pyÝv* …”Φ§*O  kØ„yy-j}9E¨‚))#S'‹`sù£° òÖFS'*1@DÌÁu˜qVc"ÎV.$ÙÑžàlèlÇ·ŸøSvEÕ—JÇy´IÛ¶jy4%Ñ2áekqU)§dçã tlêÀ#Ý…Nzp„Z`ì0¡†YD€g$sagžµiJOφGÿÙÆnÔQ”äyV'aV°A>ßQO×h ŽÁ¤£†<Úz ’y¡¤{:¡7zžÛ¸Dp?„J¢¶ª "¸u|* ›Ð4çƒG Ös p“½q"''Œ =Kq"‹,qŠV™¹”‰ž=–mK˜NG2p†@`³Úž°y«Ÿx*¨+æIùT-õ¨ød$"• DKö âã@ŒÆAe=ß!€V’q Û˜qV‚ŠžÃ¹ó°S®·ÚžÜæ¡çÙ°DP4Có›Bt6ùgyHÐAb‘ðàQEäT%æ è´FÅÅÎÁ £ÿÎq*r Ïa¯±+®h¥” `bQ ˆ°—ˆWHÕ#m´ |tòXlg5\"µhqèDŠŸ·x(7*’ÑZ ·G¨ÆG¨‡J¢‘TÇ-6k“ð*ÀXfmÐÿªÊŠ*ç-b‹ý‘·ë3kåµ;H÷4vê$qÓÀ#Ž¡¹ûw|$ vž«¹µ¹žÞøz÷¤z‚‹†Të¦P!W $Pfסr…#­×†‹ ÚzùjДm”£‰'±4¦À<"›“„z±@®ÎKR²ÅJ¯òô[ð v‰GÀPŶ€µÈ$ÿà2tÐ:ñZc7Å•L—mÝØ™`—%R±õÛ¹íI·ù[›#à¼é&[5LÓ DêW p EÑ¢bCÄã+ °2ê܈;@yé´ÁgÅ :¾bX…°7€¼s+ŵù³ùk| f)²r™ˆKJª0'› 3…K6 \a‚ê%+šW”S©L‹”æ’aÃ9±ü´ø®‰Ê—AƒÛPJ‚?—SÒS\#8¤“!ÖY-§HgÀÙá)§À³<Çgu‰Í±@Å&̹|sXÌÁ—-BôVSN°a‡W¦Bư9<‚#. ÿâà{6ÚÓ ´`sUÄ—ÈÉ ¯â Ê)̹úWÅ;HmŽ`$–¶ÔPn%[À1~$¸²A©° ƒ’ \fî…Ĝܑ h$ÅTÜ̞˴W­7uû f;!0°8ÂGc µFYª`‡nj5tU^"—Ƽ\ tXR¿þÏ {“‹Ä^u §‹wšd'p;¾<.£<,ø$@©0/±TÐ=12/P3 Çs Y‡‡€¼#:·WL¥ó„?x¦cì]ôl¸ÄÈtiGO0‘Ac35Á-Ð^—p'Q6˪¬¢Ù¢ #ðÒ ½ƒ<~“îüÎÆG|ë©@ÿýñZ"ÅrwÊ.”c%!; %u²„.° ‹4Èð#²ȃp^j p£ñZY?ÏvÂ0 Ï6±Ê{Â{‰?==t Q”³güÑÈ!Ä £ub"Å =vý/ÌWs:†?µÐÒ+B:»É¿)ʃ~vØÕÿvžV<·ðr—¨©Óà¸â` Ò1BX20§Ñ—ø \€8â49àç [MJ¸Sá ²s°¿Ùo¶Í‚9™°-~ÈkÓÚ_`œtŠcǰ“‘î=°Z/*ñ°´¢HœÓ9ޱgq3°´2,‹]ð Dd ú›ÿÇ«“‰+¨ÐÞv(‹*`•‘G¡vÜ <2GAÁpB9=’5 h0g^åß@–÷SyKMÐ{-p[O¦q$AÁË0-ØþF—åm© Ð&0²h‡†ý¬²% ;%¨’„qèt Š~³ +*q'HA:C° ‰‚lF-° `ähs³ßQƒÑÜÜT~¥ƒ›V¼ä°ä€Þ0< ‹À£—È'°°@16’@wàŸrß?Æ h4 3)]\Ð2Ép·ÑbW­0ÈÀ ]6ìê)M }Vš;ÊXz«pëÿ·Þ pëÐëðr,¾%p #²g—ªrÀ ÖÖÁc’A†G\¤Ã2b"’\Í#>¥a?Ié\Ô 5™møK®·:±`¸Îëì^á&`‚Nè<­ç‘y@$b"Wà%Iò G&l}ÕL¿ö1ŒÐLרz 5sÆiÝ|oà *b J1WÉáÙúw¼-ÅpëèíÞîïNΛf†FžÓ<\aM¬Ú¿Á7[Pß¹ôGh°RT@=[Qó3bẤ>ÁOÒ^ÂqÁ!Ç{I«ÿÖçè‡=!¯ë#ÿñAÕ¾ÉLDsãO"çÛà)‘õÿ.$k!1 ~QÚË%÷¤ÏÞ15ç4 gQ3­ƒÒ­–ô&¼Ç÷îÛ<ïñNï»NïøC»þäp€gúé ú¹Ôc%AÕ,. Ð;aC"ç’@‹– }'üÃ%@S°EqrO»Ä#´Ñ®?ÊÏ–ø~§ Pá°ëLn5ƒèw:­‘µ`)Ƶ#Ð{Ïql…xD€¦~5NbƒÕwP´¢D÷j€µ ÿàÖ¹‘ÅTÇ׹ݞßë¸.øÿÊî 0º?T„OÌ0­ÓŠ¡ &¬ 'ÆÀ1ü~1$w:Í^ŽÖ”J­Õhÿ1*ìÕrq8‘Ž¢«µdÓÀ‹6Ëh´E!Rx¨,ÀC¯Òë,,!   F F\pê"Zº¨dh./ &\\dn~&^–¤ppªp§``¨f@I8ˆ:g^&¦bf®Ž "æøþ,¨õò&M»'ÁÎ-GF."¤Z&j"ž 0NX×ad~¼µÄ24ráŠpŠƒ_ H 8ñ‰Œ1´˜ñiŽ2Ì€áÉbžl€ª `ˆ›·D’ ¦9 .Ñl7b4*'@8•*#ù\Ä8ëEš «Ì‹ÿ3CŠƒ¤œa0‡Á .lfà¢T •°¥ôCÈ‘9HáS”oDÛ&òà:©F,'¢á QÇA]p1 ‰ TA‰J­  dD˜PÂÅ/ŠÀXÀ¦Æ®-`¸ ÑjšIk'ÿ¨ðÀœL™ÞØÎ…0„¢Œò8Q#èéΊÅpQ$ø‚#_$eücë‚8zsÑX I_TœACÃŽ10XµK$ {RP „öO “*Üš©-m ¯š .ŸO4`Š ‰!†UN ã9Pê‡#¦ûAâèÀe†h`H%Ÿx¦¡60HÀèzN ÿXÉ@®©†ÆlÎAä.G¸€ÿ1Á„{»‹&UˆÇ*Nx!‚"€½ˆ!€ dÀÁ…¥fqø:­ŠâÚá •*4€§< Àà”ØXí8¬!6×Ъh‰róÆú;¸C@øÍÀFø™œt!X¤òÁ.ÁK$®¬¡©(“R¹$#*0HµÍÈ+Áâ€;ò¤ÏZ§@óóæ¦q(à››„•k@oiÎ)2`„NòQ…J[ÖYŒ1nq`-~¢K8P"`h U1`³Í¼¬Óî:­Ás[¯áƒ›sË¥DÔ©Ä„ÿf*ð! ¤«‹–Mã:ò!¢€p B¦£G£\"Ĥ€%s «€0ÈG òš@S²ÁäkðÌ>}KdÈn!§€ÞÌ)à.›s$Ñšö΋à‘gŸ=a艔“rÑ ª FH—zn‚åN¸µ€ÙZC+@f–Ƕ‘ë›|(±y_J‚çÈàTxöÈZØä„ˆ¡òÖtË¥… ¡Œ9‹ªÆD¹¦ÉK¾ùiÍ‚,ˆëOq¼É1‘#AÇ{y;/T k‚jøð¢Tœ .¦‹!¦§pÏp\ˆB7(1צ‘okJGÄÿZr©q,™¹óÏAŸ©çJ§ Îã 9!´pÁ•édX²¦†ÁÛ§03¯v/@†Lz õ|—r˜þ$;ó¶ §Ï£¤Ä(pª¡ÁÝ 0Æ'8û<ôŠ]¤ 0@Å)„œ e~{­^s‡š$¯W‘ø#‚•Cü¨^1sIK€“8\lë84€”b­%LøRˆÐÂåÅT¨”úd€AùL.~%K T`<þtƒBDZ ñ›º,ï^(ìYõ €‹!, pØtˆFÌÀT¸ÀN"n aP2 €’É¥¤Q#ŽüQ1Q.Lÿ!¿d–ŹH}BRp¦P!ji zYÅŠ)ªvgºá~Hª 0%kâ56ViÐL.è°ÍN S0šó_R5P±qŽ•„G,C¢pò ’Áƒ¾3Švà%!Pé®´d^‹„M%àÐls(4[\d2¤¼¤€ˆQA^À—Ñ€¯‡>e>šƒÔáÂS‰ë€ lÐàxM~ ´F *A¨—u„²&9@M‚-·qÉ…®.¸§.à‹d ²€ iAR âø¡=eT‰ûØlp€¼Ä/^~¬Q b‚<^!ªe£¢Íi9¬ÿÉÇ—ÿâ T±G—p‹³»›€bpp:TÉ Ð Cû Ц{êscÈFk(9ApfB" €äÐãÁe»\¹¼Kœ ·¢™Ö1‚PdèB^@P¨êP!A4Y@Åžä±$@*͇lp’ÆáI’0«Co2$èYÎ…-3Ç@]ò£oü(ŒTXÆ*µ¾MÍ⯹p˜<¾‚·ø€.uÜ]ËØ|t€6`\ðþYÒQš–µ:ûY²1o…\|¡°ÌZôu†Yy_ \f!Ïfp$ÄÀyA)σ] · ;)>09h¾K¨lWeƒãÿÍ I³w Ò®Î! BÂ7aûÍn ¥¦¹jµ×JÌ+#ƒÓ‰4 ÈÂz‹Þô®#=@0@άb@ ÀDÐ<¼ëVz¦ÐýÍë„°GZówÐP  ‰dÄ8ªèä«XZ…Ž Ç4†°¦íðibÇÒÁè@>X…1 øÍ¬ÆÌö8¼UÌÆ™£Äf•ÚÌ 8L Õ)z 5ZF aÐbˆ$Ú³p˜l@¨Ãb¨ò°œÛŒ€Ë6A "Àĸ©X_Sè‚SPÐvƒ…—@žC¥—¸  8ÎQƒ=ÿ!°«O+ÿÑ!pu´¨öä@€•óQEó@àAoÐeÐA(íéæ#Â|”NÄÁ©:Žg ~bbHªL„a‚'ó8,÷³€OÊÂK…Ø .aayŸâjE÷¸ âŽ)º\ @Šy‹O}¦ÅƒÀµÑÁƒ÷DRV-p‘ÜDÄ¥9(g `Çøxy/¶Ã µ#8@€ï}½õ<>«¹uÙ%/yŸ¸Ë È<ÎY0€èWVÉOþ‰{¶¥ïd!=O= †L¡BÂ#Xà àª_a ˜… ¸` ˆÏ@Uþ}<ìûŒ UK•õ™¯·¬o£ìîÉà\4 ÿÀœÑ> ©$ bœ¥÷â0q+¹±Mˆm d˜éR®ôo¹‘* \p­‡žEöh9ô*:`'q: ¼šñý?àH%F"`®¦H·åQÊñºÎø¸‘ ’ëz #!Ò!ïïÜ1`>%@ Cº„”" ×ÏÂpÏN úñwüâ ô0Lr1H%j$¥²'RŠßx Hq!­(oñ à j0h"ÄÿnS†ÌJêÀö¦?`Rà+c|LëQ¦Ä@Æ ÆÌÑ'{"?†Và"'ŸÅþÿ²t µ8w1X¤Zøê(1N`HÐÖòžñpÀôŠ  ,ªgø*uh&J"€-/à"åÒ'-aæa$Öòäz@ب 1ÁÕ` ©à\m´%kíyâ%.yBA:bÜ*0LÍz‘` Ä!òÊ‚#)Ûòþ 9­+âl+Ö±  'G]:0aªsÕàÀ› T„(B ò€f}(™pÁ=~!ë'j EßDŽRá#›9ûŽ”¢& ­Jƒn±u|3`604õ¨Â é,“vFnÒU8,/ 8qRœeBóâøðÿ³gD$4ô2`†*±µ²£aà 4;˜#q´'±Ž¡ ¡„”d>u)ooá0¡+ <'Ôübà–àn¨žÀC"îE›æ &(`,ÐutéFuÉ¢8”J¨p@EíIÄ èü˜€ÎëœÁˆ&n§ôÌ ž4ÜZ@ãª&­4`dK­ËÞè2³ÓGf{Hi)\€‚á´K"Zs Íätñî!šãàÔ—rÑ{æàIÍ`†4oðÔG=58ƒÐ‚CL£ƒ`è@K’¢ –3 ãu*†'p‡ÐÔmR[‡vRUððÔkabOñ0 òS…µgæa@‘Õ@D ‚ÿâ48t < $NØYç¢2´ä )h2¥ :Jƒ¼"K›#w<5­SƒtH cÀ7GãÀô:ƒc",ŽintÀ¢^GÕJ‚´'†®êˆJŒ€\Ñå,É5"Ä‚î–.CR#$Å"8ÝáJžJÝB@qòJD.0B¢¤kÐ#±Dmc> ”ƒoHIfà88T â´€«®&žÃa› ‰h@‰0”÷è@3Q1T!Bš´!œ…THeHejo2>Àt`é°À>75‰j€¢cj€Y d8åJ@K¥ÞmXL Ä9 Í9”)¬%: ‚KnÚ}XyYakà*¼°öIt ÁШJ¬Ä’B1¢€©†(R \r²‘üSëLäB"ð¯9Í|Ü·Ži1öaéÒ(Rµo%"У ¼xà)1ˆ ‚Rã0þ¼d‘T¢¤=Ï’œz‰OûMLagÓ|1Ì@[‚í÷ÄY/ƒR$b"FhžÅTxûu ]”eQ<÷àêL`åšJøÁÇÑèÏ—€}‚a‡’é€Â·“Ì*ÿ,1VݦÄx ùÁjE²ÒB ¢0'1AÝŒf.ÁÊ®ì \8ÂYâa”q‰£; &|‡\åüì¬Eœ’'¸àhˆàBx Ô+’à¬SƒÈÓ)$â5ë€óv0tTÂL<Á&”ž „ÜHa Á;àB>ÒŽD£JL ‡°$CXd@àº^S×#>ü8%¹yÝ·9„7âeþ ó¶7q”RRSö@Ú5‚K!~æÓ]™5Ô”S#C^€E.41òÂä…ž1ôæÀ?uΠØl±«ã,öé^U›cè)΂ô⩹¾®Wé]ä+žþTJbºì£"[J¤DXïä]îñ>ïõ~ïù¾ïýþï?ðð ¿ðƒ;ntpsec-1.1.0+dfsg1/docs/pic/pogo5.gif0000644000175000017500000001327313252364117017056 0ustar rlaagerrlaagerGIF89aM´÷ÿÿÿÿ)))111BBBJJJRRRccckkksss„„„ŒŒŒ”””œœœ¥¥¥­­­µµµ½½½ÆÆÆÞÞÞççç÷÷÷ÖÖÎ991kkZZZBssRkkJ11!ÎÎZcc!ÞÞJ11JJ!!ss))ÿÿ1„„ZZçç)ÆÆ!œœkk””÷÷­­{{ŒŒÖÖ!!))1199JJZZccss{{ŒŒ””œœ¥¥­­µµ½½ÎÎÖÖÞÞççïïÿÿ÷ÿŒ”ksZcck1{Œ)9B{”1Rc)ÖÞÆ„ŒscsB­ÖZ!)Zk9sŒJœ½ck„B”µZZs1”µcJZ1Œ­ZµÞsBR)­Ök„¥R¥ÎcœÆZs”B1BµÞ{c{B­Ös¥Öc9J!­Î„¥Îs{œRs”JŒµZZs9„­RRk1ckZBJ9!)”½c¥ÖkÎÖÆÆÖµBR1¥ÖsRk9kŒJc„BZ{9)9ZkJ1B!{¥R9R!!c„JZ{BBR9!1ÎÞÆ9s)!Œ½Æ½ç÷çBJBÆÿÆ{­{c”cBcB)B)1R1!9!”ÿ”Z¥Z!B!)1RÿR!k!RZ!”!1ç1{)Ö))÷)!Þ!­{BŒ{¥Æ)19BRZks{Œ”­µ½ÆÖÞ÷ÿZ)„)JsZ!!k!Z9Rs!!k!1)!)B)BsBsZ)Zk)k{!{”!”œ!œ))RRç!çJJZZïï{{÷÷1199BBJJRRZZkk{{„„””œœ¥¥­­µµ½½ÆÆÎÎÞÞïïÿÿÀÀÀ!ùÿ,M´@ÿÿ H° Áƒ*\È0¡øõ›H±Ÿ¾tþ0AA€D(0aB%ª\YðA€ˆýöUœXϾ™öÔùËVÁþ‚B3T€àŸb((¸Ö_0Õb}ù#)û³ ü-"³‡L—BA[#VX._¿da&vTÚâàXÿ Ó¼9¥´öò'‹ˆ’÷ð•äð_ÉŠ"€oÈ h²h"³E„ä±Ç[ÈÂA} òpÃõéû[›(óLPtAsl”§E" —C [ü±…EÔJàPÈ!–!‹wÅ%ç4RãÏ&l0'ä{Àâ‡C–×sçy€€?n IÆS‚XväÁSà3Ç@óŒ3Ð@#Ë3^mQ„dâv5{¸áÏ–‘‡š<¦¦PtÆRB…yL5ÓP#&4ÕÕC‰"±C‡„„í½÷ƒ?#HRá?ý±Ç {Œ6êÏG@ _E¸W7ÔÿàO@ôÐï!ÔŸ”?Ï@ã£?AÀçCªÆWÄFT iAÕöiþ Rré‡?1(‹Cªõí°Ãô½úPÚkA쩈UîbˆÆ™¦,ÆelAÆy¨»nB½qAÆ„å•,r°ˆPtÈËeÀÀ5ð˜,„¸áñÇa„xesìFP0§¤”+(ǹþ`qHš»v‡?eP,âï‡D:Ç"D4Û àgÅLûs‡ÀG-uA¡!@ b üã@aS–L3YÔ>ï쳎?Ü@ AoovS ø3O?îÔCÑ>ïÀÿ³Ï6¿iŒ,³œVÃ寀¸uÏ>àÜd‘?»# 3PÁB‰r¸XÜù“@¯¨™<–_ŠVÁÌ"Kšþ Fz0]±”0 ßÜݱÔC`6ºVº¨ù‡˜x <ʤøãʈÆ4L…§)5G XA²”YÔ õÁPЫ ¤XЊ#€¶‡/ÐaO €¾¥ •,a;Ðÿ1´ Qe|0L—ü¦i@cƒAŒÔ:}ÈŠ@›¢¼ŠÄœ0‡^[P„°ð.:‘LJ_pKX²DpÌx3¢ñDMC7á{$¤3XPw›qn ‹ï€§7Çx5•Hj˜JJ‚{†ƒø E@¡K¤+ÈíS]P†”Jõ X›4ŒFØIÑ|j “…"‚"@¡¬R H(W²vRBJÍ@é–ÈA{È„?º@¥æ”f<Фª’  %¿ [o84u![ð"sNæ,Ы kÐÞ'‡8Ì2`ÿ[ËÚ°¤æÐë`ÐñÇDÿ¯á"8äà @--„òÇ!ôÀœ-”¡E*•šÉ@…V™Œd$Šn±¢ø©V6=3_%s ƒÞŽüï¥0mµÀ!“‰äƒA)À €,@`$¨˜dl)›M1’ @` Y€Ð¡Õ6^’‡RÛáyÈáF š8@ H@g ÀÜr pÀA1Ö¼£ñ¸GEâÑŽvèUªÈŽ,¾Qj½b~ÀÉÎî05ÓTZ­?Ò¡‰ÕôÂ+³øÅèÌâIŒ µ@:Î!tÔ¯¾ÉŠb73Šã•¥©ë2@Qÿîdíˆ<üA‹Tä%/À@LáÚéhætŸZ@7úqŽ|̃Lì>P€]Ü¢•ø-Rg¸ÉðBqB!€ÿÀ££¦q¨»Y±Ž.†á `ÀŽvAaœQ“s É…+î%‹f’‡¹NímH;Àòà%°·¾€/-|AºNøwjÛœ…ñü’¸`£ö]šò0ÞF@P%1ÉH'°'Û©†’ F|ÕGš k‘€[|_bL¸š¦Q×Jh! u˜e]´Âš¬ãºY±ÅŠ¯êµ”á@d°hPhœ•aà‚×É /âû€ô0½N‹±h4HVd%_õmÿ%C‚4»žB€«>F¨ tƒ³€µ¬¤`Å8è“9{’EÂ`Fyy (NÝdñ £[xâ@ž%‘!H­ˆìdÁ;ð3Ø•óÞ›¦ÊŒFšÊ¯ ™›Š+›WsvQ%ù[à!šåWBâ1ƒLÑ í‡òÝdˆCÅU&$6œ'fÞå’} nÿÀÂÒüq1†{éKÔp†¢ªáŒIGÃÒð‡ ppƒA¶²xà•1ìaA±ƒ¡c_NÕ<©›Ý3’A8Ê|Ó¨t±7Aˆ, )^Á ”Á\Iä1d±Aa KÈÚ¤ÜsÆîG”Æ1¹Èg )(È|„°ƒPçÙ@˜µŽP„V D‚&à•…WˆPg Ê3Lå­ï@ñs‡å“ò`ÏÈXaH‰;v wXýà–œ|Mé ³M&:#(9ˆO@Ï  ô€‚„rÖO…/BŠ"‘‡àê=H¸ìë3ø€%оPÌ^¡_•é8£Ãÿ“å 'Ìš÷@x |ŠÀÇF{ù•?A‚2–±a ‡¨ß^ШPM 0{#¢„'#s%ça A2%U2ÍA1ð~Rsz² 9c›à„Pl0lÌ‘eÀJ—O"Qó$ð3K$0˜ÊePÆý'BawØ@×€:~°•EÍçb U42!vÀ‡àc0àr€ ØfMÑvÀ`PqÇq$³$aà‡ †TBEªFElІaPN_àÈ•7ƒ'[ylääy82r&eH!n@ã&a 4È2Ì¡~by†ÿáA¡H%õD'8(Ÿ¤%!° S3Å÷J Щã'™Èd¬ØŠ®HR`[óÞãTøŠãíøÕ9w0¶£aH¬Y{‘UA $q¼B ‚ÚäèpJà`:=Ab'Ñ_ °%q‹R36Þ¨Tüðò@¼r ð¢1T¶¡ŽQSAþ 7eÓðpî°çÀU˜hŠ.!#S„2JEZéë@ï¸QTÿ€@-2j6¦açpWß8Þ0ÚPý@`× ¯à 7F ÁÀY¡ƒÿKè``EìàòçÐ [ A!:®•830ZjsZ¡þ€”Z! ¶ ¶Ð è•Âá Ù8ܸü°IEð€ «Á ibb'æ޶.ð4{býþ8ñÀ öU ±³}q<ÂPy“ `ðÐð`’S‘ZÁà™ðrAñ:®U>Õ+uAà%pð˜ýµ3 €`y±k` {9:Ç[y0ðGá˜øÐïÀ±0 Ú…wp ÅÕ•«XÀ î€S›¡UA¸ »)©ub»^¦³.ë î@ù°÷ I%•™P ˜ÿ± –0 €¦1 «Id¹c5 iÑ.Læ»õ b1 ·ð7V [f5Óø_×ËS1l¹ ¸@ f”VŸSOò 뙟ý‰c›Ñr/5õÉA™~ a¡_j"Wà]¦¦wVÚ³n1=®õ˜ò ®0 Ä ÙÖXÀ¡#á²P  Ã`]¼€^ÀP €Œë¢Ãþp :(F f Ò©cn&J¶ ¶`¥ j¥²5ÿ“N–VI8¼ aÆ=f5 0ôX„AJa^¡Ÿâ,p“q!þ@eÀaúR  ¤ éãÿC¥¹À ZÑ•ZSQ[KSfdñ˜Æ£a®tU$–]@Ol n! (öÄp¾0 Ó³ ¦¢eð!@1A‘ Á‘•½ «|ÁSɱö„ƒ¦z;Â0 ·  W   ù?xw+u0ç‚©ZáªAQ3w«2*À‡ ðtê“JØP@¡™á'z>pg‹gMÁ6ÉÁ6аšI%’¹I`M‡ <¡R’}¡dL)AÀŠ÷“*5” ÊÀ àCçÖ†‘PNÓ䟦|Ô©SC(Ô0i˰EãçG€Ó„8 xº6OÅD6× žÿ€Be"0B;ðj"5<Ö “v A‘HBzÔgêGkAñŠºá³ „jD%ç† R G÷}â„À"Xjµ1 Ñ€j›À„d S *D0;*w)Ð7`.Ûgˆ ³²8©mAñDB'ñ¶Y@rþål\ðŒÀB J‡$ð#-ÅѲp{A'è"]€Nç‚XÐt§òK‚tàLBÆ1nVPlð²´ Hë)Ó8¨0Š /„hˆÃc ‚:`$H²a rà/E€@†A©# ‚´Ž@h ²€Fÿè€÷TOTr¦±´ë‡g4tð° *Aa =⟠]{$W¤2l8E]{%ð™¿æv¸eP-U›@1²d*nà‡rO($lnå±Ð[@µJÀRb'»K´HÓPs»ë딿L„êVOAr-ËgOvr'K;†•ñ– 1ºyW,›á€°Äëo'£‹!b÷0›qk_` Ã4›K$@Ì ,Âò 7Ç(¨Ë4Qiä¶vXp*ÊsŽ ° ‹§\òrÌ€(Õ`&X§¶²!¿”,@~`CãzðOS+5pÎÿSmTÇŠ¤H§¶ž›Gz´,þP_0ÃHá^Áƒäû GÂBs‹ ¨|›”Bq+@°²‚{ï-P!¡ÂD›Çv[L É ®z®ö|J +…T!™H›×vÌ€j” Ì•\w¢#‹T †¢H©¬D*Ã÷ç" 9Ð}z4‡àñ+²p([,&i‚,»ç¯Eà<§Q›+Ö< CÐ.ÛJñ‹Å”y'y”.ëÌJ`DIʲ}¯T`š‡jþ|@ÎÍ G + [!uƒzBwé'wCàD ̱GzKúrϰv€gl9À{ B5@óÌ >ÿ {cÎwì(ðaº”,°ÂÓÉòä¼ ›Àâ¹6 MB@Ѳ=0ÛÏ *„ðq›¡¯ï1.É’8@:À{yvRzÊ@ù'Šà!s‚´Ó8À­ñq× b¡æšÛ+@qáý¤þ@"ñ±z ’FÐÒõ±)èBÕ Á…,~NV%ev-wIÐuAa ñÚ+O¢¼,b 5Bö; ×ïq> ±|R àØŸÑ§†K‚«L£CïTÖë’P)|%Vò ósPˆ¸´!ò08¦>þ@°þK/T²%*c[-l'É­>u%„ÿ@aÀr0õd6‚2 ²qP¶æÉ·ÝÜszQB‡àXG¼fð×¶eSEtR°M{AÑMÍt0äÍ kOïmðu¶ss3xÐZ¤QP¶ ôG»Vò!i=ÏbñUà>W2Ì‘0²ÀPÌ»¾dÐàM3WÌ9CÝCB/£¶–E'%Gâl|¨/hPð¬+¡ŒøÁöÔ†mØq5$i, ]$/žQüãí1€{ðqC=(Há9KÁ-¼ã¸ºãk„Šè‡càdÐÊ+A($GsP `ð¼c…_°ãå¡Én ÓZ%‡äÌ‹»‡°ÈM:u@‚ç³ôkÐMç/k|€¶µëoâ­qa kÆ9¥ÛñàÃ}* U2 ¸OÊ>u–j^BѦà!7“ó‡"eä¤×;lE@˜rÜ+`Èþ }ðÛr5¯È%³Å À}ÛîâS;€ `«ã¾î¥;ntpsec-1.1.0+dfsg1/docs/pic/barnstable.gif0000644000175000017500000000560213252364117020137 0ustar rlaagerrlaagerGIF89aa´Äÿÿÿÿíkž©ƒOŠjJvYFaO6XX@X@@WO$F7!@@-#& & XXïé,& ÿÿÿ ÿ ÿÿÿ!ÿ ADOBE:IR1.0Þí!ù,a´ÿ 'Ždižhª®lë¾ð»Íqmßö6 ó†ÿÀ n@äù‚È ¶ìŠË¤´¥ë|ѦSKŒN¿¦a×ËÁöf[îÌ.©Çr•¸›ìÎжþ=î=‹P€bdzIMbEv€Œ‚…l\vuŽ}Œˆ_hM|}ŸFšS‰<—•cŽ„£8‰s‚¨Ž™¬†°˜¡ª«´9¶P¥±¢»HœN‚¸•³Â@=²±¡ºÊLÄ—ŸÐÑ2Ì€§<ܪÙBÓÝÞØá.jZˆŠp•èé,ë©yeðqò¼÷ŠÛàú”ÀúGç—±€5¦±£ÌÂÓ‹÷pDDF3ìÄH±âÅ‚¾Ž¥±R1ÅÇEp>ÿÊê(Oå™—S– ³1årõÒÌ$QªDÇ&q#93ã·sÉ-¼bò•T 'TcRtZÎÆ«öv–á`µË¥šÝ&ˆuƒ (PPmÕ®µøYÓssÙŠ¼«#Y^Fï®<ãf¬X?‚¾Ìš7ã^Œ<ÿÕ‹˜£Q©‹¯Í쉰á+™æÌXóhÒ;I/µ¸"ô\ÕŸ#«pÝXrlº¦Í¼|íºôª%¶sLsuÚu{&#Ùxp¸—ÝyjÚÍWGîÁÂëÓžGË®þÒ‚Óú”‡mòŒùóèÓo]|Æ{óñ³™q`}{VÕ,Ñ‚EÌWØzÔɱÿB3ßsÄ)èßÝÁ€æ'LƒÝ¡Fx¸•ñ^lî²¼g  z·A‰ò§•‰-º8Ÿt§9ÀÁ}4ŽèPy>†æpªÉc†ýiÂ$üp,žÁ—7’8ÌŠ„¹‡–Àa¸Á{k¾DftÖI§™B¸XC6é!Tö`ç „¦H "ÞŸ÷ùIÃgÌI褃jˆ‰>àw$£pÞwœ¤” ‡–§(Ÿqzª¨lZ:LŒ3‰âq¶m0騭&8Š“4ðè kr°À‘RbQ'®¹n€¡J ˜$°iöࢰİÿxZbè¶Ïb†Å&+êù¦ykúšá´ÃV‹íKÚr›%›\ ]+SÎ8®kâh~$ª k»9jIè»J·Àú¨ï½lnKš¼¶®:)žWìžÂ˜ZqFº%bñîKñF,q@¼à ú#¿Õ€ÁGÁ$H`2pž™Ý›=p1¿aö /Á0¯*Á)tX†ž‘œÑ-¤NŸëc¼‹lgÉ%ϱ¼iá ˜¸à* (°F½¯y¡ÊLóÚ/uý`Æà","¾34`.§ø }*46Ô›©ÎKoLß Ÿ‘X[zñ£“®Q½^*!nÅŽ­úyÔ.ÐWõG%5‹„W.“±v¼í÷XëÇô‰èÉ eLáîí‹ (üý’ßüˆ‡ŒˆK}êèüh`7ì |Šݵ F?Ù0;ËHœêG×i‰.ͪ @BjhìW,\“zÕ±x@éZà6‚TÏxŠ2Ãfà  Huík¡Ã¨(b-Í÷™"H¨€âñ®X¸S ¹‚X"€uN<’ µ¦á퀒ۀ‘ ¨„ÿ”=±yܳè2MZ)ewFüÍ#WÕ:ßû Á–ñðQ¾ZBˆ@0Àt0Þi°uEI€ràÏ(¾¥h‘‡v0Àæè„ëíI–·ôॠF€¹ä¶ì`COÖÑLPK–¢|…¾É’Yu3â”´„8PÎf+69ƒûö§ÌtÏMclcáPÓ1ºA„8G¸ g¶…¢FA°ˆîcÌ£EmE§BÚ`#yRÕÜ}Ì£‰–5- þk™:ÜáU|2MqY3~êÜV1Ýù­ZêHZ¸»G5r±'{6 Ÿ£Ô}‚%žý@©˜(³FWÂø«gY3N³–-:¢þÿ*4žºÁ:I>¬#5ÀaüJ¦P­8S_Z„€P Hñ=ªé¬Ö–¶ÇÀ1Á+Ã×õ¥¶žñHér 4[¦J‡% |2èéB5¦ÕhÕV"yN3HU‹X–î,žåI* )6*‚veÉ™gãt.KS*¼BÑ‹$À"v—wý4¤­+Æ À4ËPêsžT;€ä–·Ö@¡ç(ÅN8ÉXe–‘™igËKV¿Õ$‚)G"Î|émÿÏÍ¥eV«ð„=Œ0ݰœ&’ÚŸŽd·îÍêgj½¼ÄÐÜ£'\`†"™Û‹Ö}™i)Pa:_îê&iøŠy ¨ÕÚþŠ®Î€„á«R².æ­¹AÖ ÜN…b&]{ ág€»à£ëöà‹—<ÀU Õ.s$‡ÖÆw”&–°tëRuê0Mò,€üs"Âehl;°:5cs·_Ó„ÔN)’™€QcŸTÍiýwM.±ŒqÓ. óïŽÀÀ•‡Û @ýóàû/˜æ ì˜5Êiîhß–Ÿð,¨ Jå#ØØ«:‹93šÕsç dßoR¦^BBÿñˆ°)%CO˜a=¾d-ÕãË×iç ûç_-„\&Ó¹Îêµ [%š”`Ílnsa×S[úlYlðpÆy2@ÏHDìA íœc ЙÎwÍ"r„2X?º^ßSX¥‰]gS#ºylÊDœýÍÚËšš¶¥,ËSÁÊXÞS¤ÁÝ¿qç¸ÜV¥Âši›&v-̧&öN ï|Œ0x±·HîótÌĶÖñ‰-K0 Û7¤5˜ómë|ï›ß ßļém]º]á%¶8¹?ÐÙlüÙljr¾þqŒïz6Ëxº9Í™ƒ9Ì-/6±ï ©Blàä¬qäbtr&ßÜÐLÖöÿ€®ô²Í`å 7ñÇsŽjÉ3'p÷„nìC½ê»xxaÜ õ¨ß|Çe×€2~Îté[7úŽ/Nqœ¯é˜¡öÖõnç}{Ç€ç»Âm¾öœ'd¯;ß7`t¿ëøÔ-7:¿£ÁvôÐ}ð*o<¿%ukÿ;æâ½¼¾Cîd—}çQ7vÉw(pÍL<çÇ.=ãI,yÌëJE•'!z1/œöÕf4+~þâP²<ö¾?ƒïm-|«37q=²¿ïCþö…þiô^ýÁWîß9öמ{ã‡ß쓟ø»W¯ñâÿòúÝŸ»F_}öküÊ¿úëVnÇpl@ìõ¡ ]'yí9Å Äqˆ÷K,÷$ìóy͆ű>ýOoB f®'â7ˆ`<À ¢ Ú€öwG2· ˜wt x0VòsøhóàYÎÀ ¨¸uÐ|¨Ô1%øK~3ó¢>TñAh_‰TzeþfGC8„|µœP1;;S„dV¦°Â¥‰ðô0’SäsùÕ1fR[Lýö"mÀtH<žpDJH…t`A~؇!á“A]\MCƒèlx8tƒé0¨Ðr˜ˆ”Aø…‰Ø*”h9­¶‰žø‰á;ntpsec-1.1.0+dfsg1/docs/pic/sx5.gif0000644000175000017500000004776613252364117016562 0ustar rlaagerrlaagerGIF89aXÄðððááá’““ÑÑÑ¢££sttDEF²²²cdeSUU$&'466Á‚ƒ„ÿÿÿ!ù,Xÿ ñŒdižhª®lë¾p,Ïtmßx®ï|ïÿ@ b@ȤrÉl:ŸÐ¨hx‡¬vËíz¿à°xL.›Ïè´zÍn»ßð¸|N¯Ûç£ë}Ïïûÿ€‚ƒ„…†vyX‡ŒŽ‘’“”xVŠ•™š›œžŸš‰‹ ¤¥¦§¨©¢ª­®¯°±²e¬³¶·¸¹ºµ^¿u£€Ã‹u•ÌÎ_lÐiÔ]Ê\ÌÕ[Û×ßÞZÉZÌÇÙYãèÑ»ð޽]VVhÁaq Þ 802"Úuæ ,@ †`¦ÙSó°a˜ Ø2@ hÁè Ø†¶ÿÈ(2Ë6BpÀÐÆ,'insð Åx@Íû6d¢£`¶É¡€½‰O½(…s,SÕ0SÍ\Mc”i–”\Nrš…lÙ,à¸ØfíÏ pù E‹Àˆ|\ð Û*~é`Á,UpVSÓ_x5K€_n±2 `ÙNѨPf…!Bè: h¯+G ÀiಠH€Ñ[vŸ¸Ô´À»ZÌ€Å쀯¶2ð-:…n»Î|ǹX±3ÕÖbæX-×MÏt·¼%=^ªf-» zño¾!ø~°2ÚJîÎxðnìCTvVZ%q‡Z&Sÿ[]Oþ#Þx®i‘–ƒ_m¼e†@è^‡›…q_Y΀á{[}AÀpܽçÚA`­&ÀK-¡ö€aZ8UÀ"éÕYÉíØ_‹æ%YÇ\3ÕõÀ]IÅH¤…‹<å—…õÅ–8–È×{Ò•˜ Êg;!)&…YæQ›S³M)%‘Ýy(åDU6^ë5I•xbQ' 9P8 D#“Û,(¥~r&óÐkÍi”šJfú“öÙR^t¯Ä9Q3]°_‚1Åþt0 ö»Íã"‰xˆ< L57 ‡r‡Á0N» ÔEhÁÇJÀÙѦV‚SÕȨS§JÕny¯ªXÍ*P®ªÕ®zU\ýªXǺΒõ¬hížYÓÊÖ¶n"¬n«\×¹Úõ®çq ^÷ÊW¹¬µ¯€ ,ê*ØÂ–°†Ml_«ØÆÚ•±Žl[!+ÙÊ’•²–ÍlW1«ÙÎR•³ž -GA+ÚÒn’´¦M-á:=6ÀR½QƒI§ýrø5ÏðÚÝ´Vµžàj_ŠE2`6 ˆifsº2ðï^FLî±ÕŸÖÞÄÿ ¡b€Œð×l7qjN:"Ê0|¹’Köd’ìDk¨GRŒ;›`\Ìý­æ€[VôE Fk¤ˆF4`Gƒ´ !··‰P2/XpåC÷™À²ï6£oêùÄÒjrx+ƒzÂà=¾ç¥!Ü&ßúW¬üSkêF>†£àñ5R:%’î'Åø€*”«€6ˆç1˜S‚A"—À½EçT>¥bð1)0îåŒÏë¥ý¦/=ô^h»â%µ˜–‘9†BT¾mgvØ’klÂ#kŽ0o¡IþÈi¡ìš¦€ÉÐÁMy–4«œèVÒÃW©R\ÞÿMQ°¢f0=*£–Ù¸F´¬#–Ùp=ž`Äæˆ’Y)mZ¦ž\å-­-åcˆî S€2h‰§QVâ<,BÁtÚQ/ Rb­8 Jµ"u/¹†Äh”Á¨J¤?݇PÿSLd)ÇÈf÷ËÊM^éôR<“†Æè*Ði£5ŽgµGJ<´•môe ÐÓ­–£§°#mÏ{3¼$.~*Lm¿ú÷Íù÷¾Ê†€Æ1÷:?zQ>“}×;ljp»‰³[—¬šáê HªA8L;ã8Q›Í;@Æ|¡  ”BÿIÆ“œ|‡¡Oç8W€x™!\!†2˜ÑðæðääÏôãÿ*® •c\Y[Á«}fm¨Cán+5¦; )| "X„ߺ†8ܾúœÖá–oìkãzÝ; «i¨„/oã)þÄÓÀFÍ2°3†Ô{e»¬·½ryù:ZHî5É9í”SJÕqÑÜGèW“ÓŽüP«y«ì©ó‚à<èG¯)Ñ“þôå1=êWÕS›4¬Oýä·› {[WÇàËSõMñÀ éÀ‰‘ ­îòcBû茩ö²M&C?F ü–×I¿ÆU¶\7/ªd±ã±ÞìÑî¼Ø²7?ÆFÕ°ˆlÙPù܉p‡šµÍ\ê8p\îØÚíçÿù3}Fÿrgð+ëáç·EÇ\þ”bð~tÀUö‚e $ X+̶˜q)æIã¶&ÐáO3a’1THA¶u#ßàqhqo7ô{2IHñ¼F!5cÇtkRa±ÑKø8X€6Ñ hRui%E¥¼´+DÈC¥ Rg·&‚]£/ˆæ Ðr=2=£&(VMõAh‘]hðƒ_…'b¹±Sµ²3AREœÃHM(p5m"'_¾Df5u@£P(óFkr"†•ä“&ƒëv/A3 `[=NÅñRÚb݇h0b|ô%¸8È+§Šƒ cˆÿ¨zE(»gOfiâæ„F{qh؈å@Nu²–dMÓÄ?ß0o Ð b&À2È—ÇFM´X ¥Ha¬"•ã }ôŠß2~³bhVUh ˜C ~á‡AÓLáP#âAUQ‡ÔÁ!Xð@+UdÄ€[yAÑðRnXMØ„ ƒ¢kqŽ„TÄJuV+¬T&ÖQ~à8Xâø\Ÿ'%¶]%¡\qÀ/¹èjÅb<ad›!|"„6'é}ë5/=E{¯®$ à4EfS$CÅadJ–…´`…ô°‘Xd_~'¾'ºUb+á#áfX‘ñ%Ž`CÿnPOk°FóO»· ‰ †€•T)?V©)À·}ñc” 9i ŸIë1c&öˆÙå± •~G$«ówðŒÇè…l#]“áF‘Û¶më&%YW9¸5}¡‰_€öD$¸S„Ãàÿ–Sž:°HfÌð]É È¢FÊp‡œiL]B86ñ@ñÄ,¸RPÁ†îq5b0üG(`7bnb"‡!ÁjÀJÍ!ŠP1BDô$Ôhúqzm7Brn4Dq„Õ‰€M“—±fSRûñ•"cRjÐá<øàøùO-±.ÃaJ/Iˆ_!éàv1A—ƒfè£TºxF)ÑÂ1"1bfÉ-ž²¤¶øOŠEó¡†CEº ±ø_y‰ŠqCä–TDEÙ¦[‘?Ç@FÌ!œÑ)B“£÷˜šj3!¼˜Mƒ¢5Æve…š§nÙl‘DJ…ÿ*¨Á[WM¥ø95Zcž8&®VSç…‚Àœ*¶Ýd)ó‰©ÀàWñ'Ç'Žv†2A§d&нh‹“B«Èg;Ûw~yUµ±@kƒ£ó$%š#n°#âOš? ·~ö™ QAe+u‘CÑ?:ª'ºö¦ã›e±gURA!zÉh[Ã9u¡ÿ÷_¶®✀!±a® zš¿À?ö7.Ѫ® H kIƨ* —ù;²}€$îä±'y¤2;Ä5ka4›³Åu<«³ƒú³B{ƒ9´Fûb;{´J»y{ù0Tq}j0 åðo˜ÿi@6ùé³° [O[yä°´ž´2äLôÕ‘Ði4^PAIÊ”~‡AeRr–±‘ü4xa07åÌ5A¶‡ÿã''kæe|‘y¯D¶#Q__PûB˜³a…d$&`æX`\Aðw r"væq¬zó’Ù.aÇc0Vƒe€!üĺØ*i­Vl†ÿ2”`‹i ¦F*¹2)[¥®öœLÈ0•§)ª2¨¡/>4pÞ`?mJ”„fгtš1 hʪM^p“# üç½£ ® Ê»Bc.&^¦#æ*l “²TÉUâ)•6…n¦tÒiCÓÒÂOê—ì€ÿè@B°Tn—2¢9#Ì™ËÑ}²Ñ2-È[ú)óÛKõ«¥›Ö½þI™…ÉUÌeÀ%¢l…ˆ¨pÀwMv¦¢ a{Õ´n°˜(Nw"l¢*„L8%Äk»?å¢/4QB "½\àlˆQÑ6†ûÁV©"lg‡äo—«ôa¬‹ T .6ÀfyNõVк.Hƒ$;RÉ€ÆÜja’mÈ9Š¢²¨aEtk3€“q:4I‡D×uËÁ0cL暀<Ñö‹%Íuª¾b*ʰB8ç‚*K aÅng’æ ÉsVq¼.htà}ˆdHWJ*ÿír9+ƒ%ÀK˜u È}¹'ˤSÖ÷=<Š{Qö\öe³²…À®Æ"awÐà8̧{„öxÅ\› °ÞÓl©CYxiqx‘ž6Êx¥¸´)´E§„,žÐ@%A<êlµ¯ÐÄÛº—É#ð\UV:´å|»K›Ïú|´üÜÏø,϶°²»<°м`•@µQYËjG†Ø{Qi[§i[Ñ+MT4$Ë)B\×ÍÝê²l€¡bÅÙ×ÐÔ·Úª´Â5ˆG §Ç5Òíg/¹ä   ´‡\¯+Cc3¢F²{;*Àðʶ‚ËbšÑ¾Âª/ô€;r³¦|©ÿËŠÐ?,¶ÑªCxÀT}¬èAOKHÒGXH•ħ$.66rÜqÈš“‘ äù/º?"穾B3"*çvW Èd¤[­òc„K¨‡Ý±ÃËU¶á)ü!…M:wØÒ6\0”³Ì$Á£ EUô1eÑ`€Êǘ¦!ÑÃ;´i|{Ã=2ÓÖè è,£ñfó rEõÁ£r‡I(âN{–u³þAŠ“-2L}‹Uœä$m·ÊÍH'ü)xó##yR=Ò•$¼å“ºÑYÐ*nx¶rŠe²·«+u˜„uOµc&ìÕØg+ÑðÜì`qSׯÿs‰gñ[ÀF¬rr)à=G™+Äi˦‰Z /Ó4ÜÄ-Ž.$š°T¹¤2&HH&=F`FŸÐh-1Ýh´Ú+5¬£“É⌻2Á@, Yaþ4"áƒG‹íÑmàA‘2ËU<‘‰‘a”>}¶J)ұܲ1$]1JFr?¹R=Ul;‚L‘\˜'%àÒ…K¶×0F ņÍQ.Ô)ª‚öeÙâ!FΓ÷õ7KIM©mé{RÐI–;œ >ƒY: í Ë]pÙᨰܤ0—nîÞ‰nÕ”>³X]阶î›éýÏœNÇò|Ëì׸&d9ÿdÐ8~0=þKv0ËèÎ¥µnÀµo Ìäè<V$aD>Å!GN‰cBêmЫÜô\Håég?Þ˜^áð\@»6$ò#“J{z5¸?„çä¨Ù²n—‹¸.¡¸sìi»„œÎUñø²«á™Yc«öR¥´noQ#‡¾q`ûƒ7Áb7’Åû4x¤Ÿ[!ãDåekÜ'ñ^×]=ãúüÒ ©ä{ÞMa¾g`?bXÒ›Q­õÝ1òÑfsJHq‚¹JŒN‚îTXnâòòùG1Y8íØ à»[Òå›yÖš¾Âf¢±éà(\7r1iÊô‹iiÐBSËêÿök­þ»ð®n6 DhZÕFüÕoŽhj2)ÿéÝ.aÃÀZQÁôpÁ\}Ô¶»Ìiü³\5h 4jN=Œ{FpµZ"0o…:j\3'Έš˜dDc¹a:JÚµ²ƒ;Oª¯DÄë¦;HLJÌ,õ ˜ðkóÑZŠTìƒngã‡}2ùŽ0\ƒ§î¾Ãjòòê'ñ¶R$ccªoâì06Ÿ« ßÅÔào,ÇËEü䜖ÈÃx<Ó§ üƒÊ¡È#0è"äQ.ΰ"ü·Š.BÍÙëíÉ4 á4àäI=’ì.±Ç>&çžÇ•+f·úË/ÕGÇÇÐÏ–LÇÊÿOè €x¢©º²­û±<Óµ}ã9û<áÔ–Ã1€ŠAÑa:m€àl!`¢)ð„,a‚Fv(:Rk 1‹ÇÓ÷´½ŠŠ´9ðÔ™½0é ôE% 5áÌ 1Íù=@"õjnrvz~‚ròø8†šÆžZ°Ùl8¬¥žÚÞ¦âîòöúþîôü#Ï8ð¼&;?CûŽGW[_cgkosãL—v‡‹“—›Ÿ ƒ£³·»¿ÃÇÏ|“ʾa­ß÷û_Ó‹A‚ǨN °²† md±ó Ù¨e3‰ÀÁ⥠҈õ A]'4ÿÆJ€§Òê@¸X …E‚Cªð€é-f‹ƒplø°§ÆF#%À-YP+)“ÓŒ9\ªó ÆLGz\É¢€ˆÍf”PbQ.J!6–ŠLR(0#"3n4 hY4EÔ©#žÞt1ã“êl$v fUº,uœA õ°ågK'>›„p €^ŽþB P¢Ê‘¼<™A´ñŠnž)z#ØeÝ“"Üœå)Z4Н x%{&H.74eß.È\|õz-o&Á¯1{€kDÎØ™¬a¶ `uc¤Üä<\£p€ùð[~iÿ[Fk áEev¬µ[F_ Üe4i—‰€ª¥† €%€PKŸD|1Íç_@à§Ÿ #åÃ_LC(xb{7>Ð Ãõt_õIxšx/°Ùqxˆ#-A¢VézW–TDtB_ tžq¿9â ud)‚Cø÷”!A6—Ï ÙB,²ÀÙc?>‰-$`¤eÒYlÔáX¨3:Ê`ch&Ùá†Q8Ù—„Ç‚,EwŸDG‡B„E¥ܤPàAEjõ B¦Úéwt…„$ q.ªâ Ž®wšT¥¡¿úÞÓõh ~‰® ÀèÉorPŒ7ŠÎ‰D XY @ ¬F4O±Góz…'Ñ?k(žoÔ‚7ÍÂHv K€R†¥¯°@T` g  OË8B g39a'‰DT°Aƒ)]’(À,4” èè‚i›ž ò3 ÁÍ(h¶²ÿxö‚Í”n™ø2ƒ‘—ÄL×ÁØ …§ØNdeÉGD"#ª0ÐäY\ wƒ+TnM'*}òä;Ë€1&óã¥(R–:j±l ׺D&‹1~&‰< "2 LURûLX™ˆ&¤úÒT°c‡,€{ð1"JpDøÁ!éÑ'›(J 5Ìk…»L™I£mòVÇÁä·º²® RhåXÄ.Uʨ;áæïX™I¶áŠÙŸ ‰Œ2{lúe /^bó]&!ZŸé¶hþÄ'ÉÎÀ–G¸åÛ¼ü´‚($q‰y k(;…ƆbŒ Aÿïs…14‡¨C~t–ÏL<ì¡â9ºW`G6" e€ä4˜³`B̓ɀņ ()šÊò>²  æxà7›êT r Ðj` jáN:8•ú)/þÈU¤ZM©ï¨Z‘U.dsELœ?·P'°®M¬ý˜Ä îVMxåk_Ș;d®ü’«`C±×¾6!°…]lŽ¸ÈØÇBöŸŽ,e+Û5±¾®M¸k'~h zÂ`}ʈ§ëH›Óâ5¯9ä¬e¡9YÈ"uÞ\#‘ž@L¬5ÈÓ0!Üö'ù!–z…Û–Î×1Їœ€*@.•6âÿÇ9²j#ü´¡ž„¶®)®Ær«Ú,£µ|­«ª›Ûf0°qæó*É&ù:Z›ó”xŸòõ¿OíT'Áû v2.•®´©‰\ÖÇ^T¯d3§»ãl·"éõŽ îdưâ?•)œoÜ5¨«šÐYDZ&˜ Ñ™56µbYb±B·ÙÙ HfK#$‰7a)¿½³NL—XDyúèÃÆih[TG‘ˆà䆇R´!g¤ô"À,¿ð,o Ýç ù®l̬k…m4Çx‹†pÃ]¾5¡°¯%ó)y°Á¦ö5h0AD €8ËØ2‹½\$bÿÅ’’*®Ö” …Ù+f¡@h]ÅÉÍ4ó$oˆÂ^Í0^ÐœÄ |±Œ,6ÔÔÙ™¤ …ÄËsÏWžFMoÙÝ”ZÒúÒ´È™†)’gÙ‚až`™Í@„’+}¦ÊÓ@ )k™™¼Q|’T(¡•FŒLÚV UU¬ñ0cUúÉéo¢9cŸÑö¤¥qAκ(ˆŸÍs¯³£TÑ.‡uJBñ`8 # 5†5(’ Þ¤Ý"t'6ÊÒ˜YÑ;ð ¼¶HPÏ‘ïãŒhV£RI²ààš+Ì:Ch^^Š&Ô´Ý”ªà 8Ó»RÈ îÖ"6œŠÍá!Î3èØ¨iÿLýÆ‘R·I9l\嫃_·W…§4¢#3Y\(𽇝~‘âtuUyfb ¬úÎ’@#$¹ý\p f–Ù ÓêÜë1M‡>ºæøì^:{ ½ëHc»…j6áýaøÆÇÍ"_…|<OùËcž–Ï<ç;¯ öz>ô¢¿ÅæGŒ}˜¾°jó W,к¸)g#iC\?é¢w$â­è»/Èõ$ö5ñí_äûÔSÙ±Óª¨1Yc!$&k§eNÊOØ pÛpNô|ß´Ò”ÇG¾;øfg0Â,b×  à-•Á\™ì ¡D¸H 䯂ÝQƒpÇ´eÿDû)y ˆ—ŒÛÑ;˜ß7!OŠl‹äÇøQJâÇNQȈŒÂýÍÈHÁÑ£‰ÍM½"Y‹( ¬ÌHF¼]õ,à84ठ€’žX›ˆ´)I×Å2õÀ_x`ÐHÉ@ü‡”!Åœˆ\¦áÉÀô ø`¾×n½àÞ°—F’b8Èúiíœ}ä ÖÅq”Ëôð‰™ ¾U¯la±A®è…š`ðUaÝ\a~­Üڱ̋â<Åïô†ºð`Y°_Y8‚~I„ˆÖÕ´ËOUS¾²x×»Ø %à¾Ü¡9ÀÍÅ@ßÔ…ÀTBéŒÚÑÀðµŒ'êŸ%ÈÿÈ•™Dü¥†EX˜LB)Øe±I(â *JD³±ÁÏä‹ÐÉ&â¡…£3HÍ5Õ1ŠCé1#c|Í3 èIc5Šž3Zc6R6jc7R7zc8.–Ýx°Õ'xPÝuÜq(Î"n¬L9jâ&”#MDÆ%ǘ„ÿLWvõ ^HAœ…$EÒšI„vàã Xp]ãÌã4IGjxß\d-’ i¤; ¦Í@QX9A¾E¶€ZðÀöq”¨\0ùÉ éŽ`Ä](ÈæpSº-_5ÜÀDÜ«p'´Þ‚‚$’ï˜@ʨ†š Uƒ´NîJ¢¯P¥¼ÿ[|™ œÅfTeU¦¥´Ó—7õÄM8‚BÛ”0…Iðöµ%o¬ŒE,‰Ï°ÖggHöàÎÔ2$SB2 "E ÒÝ­ç «¤=f— B¬A˽¥ÑUžÅ£.¶’” Ћ‘œèa¬X–+Å–Ø+ ·VEVW‡¤›šhª†çl›ÍìŠR–p©žFn•ÅSl$SŒ+ÁgZ‚¡Ü Oäªn>+OìÓ ê³M–­¶Â8&•¨Ya‡K^²ÅŽu‘Ž P€ý—l}D>ÈT*ù%H`ì¬,)­ éY‚"[AÑH›ýî¼'ƒ$Ñߟˆ Òâl@±l%`×û4Tžÿ,apÊA¼Ïüñ‡ÿ` µìÓNЀüà•(hFPÃG´aˆ=Å*† Ù>r-×ÀYñÀÂòĉà„Ñ0½”°YÌ.ÑKÍÜÒUáQ£h¦?,ç‡R Ô¸ Ô£'Tß'\¦8ôçQH 0ÿ‚W!®1Šcérƒ[á-:€£é²®-ÖèÞjëÊî”­îìÚîÝÔîíên…Å®ëœÕ-ÜU#ª@´ÁæræÈxDVèDVœˆÒÙ@¨œñ²”Nd.©‚_Ý]|ØA#n”ËïÞf–Üyn“4^ —ñ6¢øÚÀV¹.C:§ ’îÀ åÆ,ÿZ$¹F^TrHЈ(CfTl…ol5ÉDôFyÄf¸ÅÁ5Ç¡™—h)T™ˆ ¬Ø,¨‰n!Ã,L’[¤ ”T¶*ÄlÓx–0ïyTd "ñã ¿ß\5A¶øÁP¾0…κ¸ˆj$(W˜¯ ñ¨]Ч®Ž6 È+TpÀáðÓ€äÿÁ+ „Äi1–¨É«³Ê¨³~§ÀÎUx ÂÐW|P°]LÊÙÒÝT&æDgbÌ— jS¹ž¦›k”ñƒS×edЗ=ìÉU¾Aä즠Ñ¡zîhq¥Io^Øí¯ ¢«aî«¡^RÅf‚ÖO«Àÿ†YPoSÍ•[Ç;XÕÌB·Ærj°X¦x¥*éЪéh‡¾®çÈ8MWDd.^¡%z°‚ƒbÚË k×Pˆ ûë'’N-³G¾°âÞ¤¨Ú¤t†b®(“!“-Èô®ã‘1œc1ÃÜèc^óSÎÁH鱪8ª¬È–wxå¼ÏiÚa¾då€a¡q‘RA3¡J2"?GŠ2Ómléà¨ÉF‚¢Í°qFÈS…®V Q1÷Þë™&«@jX4k g „hè)½E,Å‘¥j<æ]öÃŒvr]} jj*(Êš™-Ûº`*Ö2!®UÇ$¡ã†‚TM·<¬±ÿOŒi¹Òë®N2Gé ôFDmíÔç;ûXcI2W©@ô`|د’è7 š%‡¨­B´ ì¬ÑBŒ„0^òm²ªä4g›wÛ*QOYô@òJ˜|% )aèñʼ•½=2gB>¼eÛÑÁbö—„O]—ª{U¬w¬ "óh²}Ub{ô Âfd¡=˜fE‡”6¿õ=q%cÄÎf¾í€Mßôd= úäE`R¤Vj—ÈʇÀÐ÷ŠÉ ¥\ñÐ"ÎUÒ2¬"Zé0ù™I"83 šb¸¼â¬QÄöfãªÀR•|+…ë—ŽäG£}P’æi€½wì½ ¦ `!ϵ}ܲk ‰‘R±†I¿Ë /Ã+REßw_ûʃѥ€˜`_)¶!°‚‡Ÿ³x÷QBµüZm°d_`ð™55U'uK8 —½Fc{JÊÕÁž<‹Z¨~x¶Êá˜í­¬\8Û¦ ì5C‘W†LÊýþIÀ>£ACìŠ\]¾03Ò¬$Âþ`€Ô4!„Ñ^´õž¶q '¶TÓ‰aƒj,m9&R‹çVÀ\¶T³_^Úá “ȵ)PÌž”çU<Ï‘MX1Úcþ€/Á  @;ÐÿçP]¾©€ÂZæù7_z1ôÀS4>ðÄOûH70¶üGØ4»(8P]Ò„g_vñ½2Cr.<·ŠqÞMc2 ÷Úø!ƒ‰ *‘€lB9mD‰d’J.Éd“N> e”-¤!å‘U‰¥3Ô’e—^~ f˜bŽIf•ümSf•F¦Éf›n¾ gœrÎ)åštÞ‰gžzîÉgŸ²Ùég ‚Jh¡†2 衊.Êh£Žâ™hc¯µá$°Qú†ññ×LQD@·IÜ–é0J0TˆÃ¸±ÏMDô€T¤>±btµšÅG¬3ºâ§Å"¥þ°T$ð'ꬭ jʆ>ä8w§¦ÿÊ ¬«ú·‹³&K“?Nb*вÂé>Ù\ã"½-pB-Á”´då7“¹µp¥«/÷l@xãI]K¼% Jé{ì³S¾N8Àœr0_1MLYýžP $\Lð±¯1#NDC‹R$,Š53†± ›Á|œ²-G–,åœ6 'V²/hž©!1.¢6®,‘Æ#‚‹©~#„%ÄÈ ËÎ !Ä à†ÅˆE€Xq„nb°ƒ5[A–%5à€C—‰}9¹üp‹¤êÑ0W_€ýöÖxƒb2Ür;µ46ì ¸*˜ä@ߘŀ¶ÉÇB ¹WV6ËTµµÿÕµ8DÌ"at‡e¦pÛèÓ„¼ÔJV]àQМQÀÄ«¢"¾|Åq| 8ÝB × Ì,µ€ðcù±"àKŒ€Z-ÐQK–'0ƒ¢ ·@Qc™´EGà˜‡ó Óû"ÒËSõu¡ó‚Nc积?]IÁyö°ÛIÜ–¬ÉD#aYϸ֚°ÜdiHˆÃfÊp.{¯$M›¸® 1Á#qÃF¦a†ýQ¦€Ì;Ò¦fdeV4;Ip²’iô,< ô<ÇŸPábºJ€)PŒvCàÏ&dç‰oTn)ë[a(˜† zÒ@@HÒ Rÿd°-£ÑÁ¸‚ ‚¦,R° W$ÏMbC©Æ4JÇV,ÇV©:èA~®'4ë(Á8Ë $±ÅÒz£©­}b’Ï aA@^ì‚jýМTè­x¨¤@@Nìˆ7Ý&¡ ¥)í`5pŽ.òÃvá¢VîmšlcŽú·ÀPƒeJ MPzcDIJÁÔ:¡Ã@’˜¬lÅ7îñ -àR™W8äà $ kœ4üá 8€øÄï)8¹¥,;9%¶‘€ˆ,H& (Öö(¡…•¨Õ F°‹u ¦š‰'⎧›tÊR3,ÌÅ?åc®H—•±yq;WT‚ÿe {'Ï`K¦E!Uíf­õ4m(+^‡œ#Ñ.‹P£”Ä)Ú™'‰ª0£?*5C¤¡„a¬B^ â%-qpHƒ jÍ„²Ó§>ÁÍâH¬ÞuêÉÀzüÁõ¶ç‰ôî ¦¬ L±$RaTúB‘X#¢î ‰F⣡™ÒÁŸâI&šZ•`ª„ÆùP4ªi¤ª6#M%Õ1Ìt3$MOªÇŒÒ`¢Ù„fp×RÖM= hÑ 9äN¤ëbƒº'µao±q ×D©DîH«k¢£„Û™lα ~^1Û.Y#¤®Ý1¥ ÝèJ·QpÿÓ*t_;Ýìjw»y‚Ûn¯ë\îŠw¼ä-oš°kÞôªw½ì­IxÛ ßøÊW¾è¯}ïû¤Ê=¿KŠ€*D¸ÆŒûºÍ†Ðæƒ`¹"7ùo Ú’ÚC©K°ôì:ÐÒ0ÜÖJ"±äI¬iÎZ…ôÉ$±f“ÄHr“Q!`*–_R™šDÈ~: Ž)G†S¡€<iòSsô @@FLBé‘PŠ? …•K¹#Ï׿áf?Švå“ Mƒ»Ü–#îK†(c/¹ÒÑYòUTQs;/x–³ €ãÈFQ¨å’íô³Ûn¢Ð^&Ä–›Œ^ÿcòâ’îΖV®+*„:6ªXç$Ä׃J^ai–Vïk“7Né x ‚§³öV`æñ¤§OGÐjYƒ æhÜŠLó í#¹ŸÙBÁŠ¿ /Ò(™ бäA»Ð/ãpîíIºMÔ€¶cKK&Á–úàë&xXG[g¡#¤;o¼ðÕ6ü Ó’4u3 Oi™u2> ­{lÄäù† ëÂŒ4T\}tˆ3ǬPÄoÆ„' tNÀXº¸‹)Œ^8ÔЖÒ!mûeA‡Ðb‚R1)!§ w'¨ýFÃh *[{<¡òâˆÉª–R…?kƒºj á)CŒª þÞA‚/9nÀfÕË—ˆýZo8ïNå*ü1Î{à9ÁÀwRÎ ¶uD~g%(Q¯÷8Š&@ñ3hëSÁp3Ç‚¤¢EñS9)x7iÐ8Qi„1'˜f®v,F7êòTQ†àmRŒ0ž¡hiG.Äh9‚qD¸¤p(e€ZòLŸ¢zTÆd7b¨Ä ·ë6Ï´oPTµ:Dd7vT B³,çc;Ïryâ{XU×Ây â,ôWCaV"@Ù`KfW>¶Ô ¹Ós˜6 ’3Òòˆ¶`@]OÃ-ÿ¢X©R©‡8”ƒÏ´q³2ƒc.ÈŠQWKoUi? #Q#»8A* ŠâÄ]õÅ_Í茱Pq2†døAûõŒ×ˆƒ’Ö8.̘ߎáX'e(ŽåhŽçØ_䈎ëÈŽí8 êèŽñ_¡zò¨Gð0µ’ ÎÇ7J`mP;þ³†²p‹åSB$Ô†v±ð)ºXfaoˆ<&dÃ×0§* &µBeit €å3©fG±gǃˆØ‘†@"’æ#n:²‘ €Y¾’óa˜36PÁ¥ô] ) ‰_åò=1ðoħŒS§'|ú@6 ÙFÿùD Ø“x {0Ä'ŽAŠ n E»S®b‘ 1ˆG¾t [é;ý€8¥Q.¤/ØÔNa•BGE}dÃn¤s€•1El "@€‚p< 7¨¢/VpÓ–vÜH^`GÕ„©tuG$™Çãcü°M‰w}ѶŒìVÙ0wàYÒ§=° „™þÁŠ·AŒ§¡š]ƒ†&£šüx¹Òj° ˜`¹‰.š¡™*Yqœ]ƒŠœ Š7¥+—›»A•ÐpÞN6~šÄçs%Š b‰ÚE™2Xˆ—Jn€u°4Tf• ÷C~ukAR X#B8Uÿ† à×f,€> sc)'™Àë @칆· Sy 0R¤a²~b&"UáY\"+¢ÑT)‡Ÿ4 Ÿ%Qƒð#5·¥0 u·m€àÑ/J¡4à P¡wƒ¢")R—•©÷9êhz§‘&ãCþ©hïÙ\ÕØš% áò)¬=LoR±ŸÑRÊSë  ÉV‚e78HÎf"UÄ¡ Z©*[*Ø0Tl€AQ²#S Tàc) Œ³yq›E§`ð¥YˆQZT¤%)B1*£>©¨‘ÚnD¡ËB©÷{Iq¤ÇjÑ1k˜i×é(ç‰ !Dgÿ*VJ—8aúè'8a*„±7š·š sg„pª¯j¸1!âQ%gÖ€©”„‚¨Âj”a|ØFaév#P*áY Ñúø;¤eQ`)›¯–vÌÒ0¶Š¤iÙ4çy…u­dh~# )8WŽÊ7‘>x,Å@1ßQq’± 4WCeVÑ1­Ï‡ET—u„KT ` &j.`[x?ý±0ë °øžzüj±K‘%†V'‘k“õà,úM'ˆÔâ,µý1 V{ Kõ’âðµAÀŠõ†Òt¥É#ÂÑ1MÛq;T# "·8BÓ;jXUÂÁ8bÄé*Öâ;bºU¿°Pm<¹+­À¿G©Žû`4|j 4gLÛ¸"H1Ü0:”a““—°=0…üÁOý§AHD6¡je0”<€QçEåW;År¿Ó©.`Ÿ9Ô ;Á™à œf *Ç‘S·üQcxF>Ra¦uDä(l±*œÂâ!hÅ!($PÖFÚ³±2ºÐðÃaÇ»Ëh»0¡³Sqûs®dxÙÉa¥UãnºzqÇvg01¥Z2;ô㬖éK¹tÈ£Y„ÆC,Ç‚­Ú–Ù=#– —20B?ê4RGȼ@s#†®Pjéužÿ+4jh ªg|3AV#F]SžF‘6½1wK¼¦€Á&ȳÜØæ¸\( b‚E8êÖy(;²¸“ƒÍöÏ1>z …(`H5+ÙC··Ë¬€ü:Ñtî×#EKIíy¦\gÓ@ª*à­Ð+A ®ÙDj‹Ð/”;OyaHæùÆI 5ê|¬—:/{y¡@y´l#!jU»,48G©Ñä¡ÐÐx£?;d]e)H Ñh6›V .îüðÑNœ!;×”«0³ÄS͂ծ!2 öy"nmv Bê< (Ð m‡…ÃÕ"éTÏ¡sÏè}ÿG m|(§Ì²!¤Òí¢Áº”S¨²³‡áW½1/ƒÐÿ<¤Oì&Ãÿ¶l{¾gm©åbÌ`]S{“³k»±½ÑÒywW ²Öjq­%à†#)r1x6&j]Xsk3‡Z›ñü9GÉIðÖw­…ûIå38«(V½4ïšÖ,|~9u¦|î$}jíQ‰?"Ó‡Ak„K{{¿%P š—ºÐÛsv϶‘…/›0KÀ§Û{¸…&™Ø‡v3RTÜbÙ áB˜|O'ÀÙMÌëÉÚ:œ¦×=郂ÜÊÛõ4°7À.×`NaƒFØÕ˜Úÿ4ú.~–;F• ¥@h¡Nôh\Œ{¨HDëT³ÓÒ#*w½Sk¡Ã%°S†rÔœ 6h%¶›ñ;)Ëà5Cmª.þ½€ýÍ¡ö0ÚW<« £8Ê›<ŒyTâ³ÄÁ Öµ¥€â·!ÆY\!þl.W :J^b‹…ÜÊ`lÀDÀZìœmlàÇ 0P¢< Ú£ Ô;¸[NæÀTí]%© tc[TdãPmÚÝD¹UÀX:b¯á>º:¯À¦£b7m`7”ˆÄ óË>R ‹†/c8Õ-|…®ñœ«'ÝèZ#j[ªÙ¾ìá[ÀL¾ÿÿùÎIÞE§q3è4à ¡ˆ¿*^Í ½·ÞŒ_æNŠë½nŽ鯼îëÃNìâ»ÅŽìÉþ%¶®ìÍîìGÂìÏ.íD¢_¸î_€hX1 "䌸câEfÅ:öaƒèAø“5oÅWØ.ùa ¹dõáL( I"v Å1ï‘dýðr8Å¥J¶¢+±_˜/E&K¦¤4| ÂíßÓÄå´1AÆOðνÑR ÁÂ>$-L B¶~œlz\kÈnzxÔäQŒö”Ö"¿DòÎ ¯¬áš8—ª¶ ¸Î[£ið{¸_,ó1i^öò7×ÿÜ) {ÆÀ“ÐiGÊÑbFCQÊåÝ^çù×à]záàBtÞÎê a}ÞË@ÙD€ßØ ÖÀŒË I$@!—‹BÀ$IÎÖH&I%ßÉ#¤ [ÞH$c؃ ÛjØ,-œå›¡/Ñ‚ƒ€À& A ”–/°‹U‹˜“œW¶ö6·›’2£¡Û¡*àëg¶'4Seç'V\<à¸ýxïáºÓº¹£ @³ªŒèæ˜ÏŸÀ.û¶ti±FÑ¢„/$Y"¯šÆ>ÿÞcSà–~¤à9èè㟤{E„î„Í^À|!cÔ/zôèfô(Rd߸ °Éiq9aÌ8P%À"/ÄH=:SŠŒB6 x”†“/ÓvPñ$ZR*Uª£Ii•R{îe¥êq é±Nhœ""Aà0Ÿ(£´¨ JBA‹Š½Tœ)5'žzc®[û4·ðQ´²> "šÉŒð–|Ë`J-YiÜRwTRêÙ´£ êHœ$)ôèÜž{TšD Çà°X\TFËtZ«„îÀÙ¦éuÜqq @íÔÀáˆ*ŽøXØé—H"?ͯ `Ó!ÿ~Õ˜eAhfª¹&›mºù&œpr4Lœf¢Y'žyê¹'Ÿ}úù§Qw:(¡…z(¢‰R AuŠ:ú(¤‘J:étURz)¦™ö9×!š>*耤 á@]8À1œØj§zÁó âöàY#®¶ZA*†pÅ’URÁQOî%F%I$Öä#1ÿ†,¢RûÂU4Ë߇Ä·º*ûÀba|K5CX[ÞëÄBÇK8R«ªŠð ª±ÖŠ+#×zz¨.ÅF$°á€&%ŒE1ÝÍ"ä(„¨ MS@šìÜàÃO E³“àh‘,7È ÄXDŒ*rÈ»'éT„Á³"iÇÌÇæ³A\„x!ã“c×båD Ü[Œ eà ›[qb@WÊhDZÉ „ؘ@ kKÃF)hÑ,ɦ…VBÛ-9(p#F®´!Ù¬ÁÎ=H€eP×N|%³™¬÷8pch€à:Ò치¸pÿMÜš†Š'÷%Ÿ[Ay{É=¹.|AS1bÞ±¤'Qõ œÎMNKó½ÂQ…^— 6SŒ_b¬g‹9;Äâ\DÃS µÇå±@Ñ€äß £ô0—'Ô÷íÃY4NRO‚äÆ!q­‹Í Ùšy‹¤ß­ùÁatÛ øq"zÀ%€KËŸ ¾¢™à #×S)ÀPR-ÎV˜h\Á"b€ŒèÂÕÖ¶±à 3´­†;€,hsÒâò;3¤ÓTÁº¡ § €cžú€ð< ÄÐQ(K ²b ç¨® yÈŠÿ¬à xC€ E0ù4„,HIÿ ¼G2”6(J ¡D@Å"Bi–¾«&‹éRV6rŒ6D$B#<ÚhÃ0 eà 1H6#19°ãÊδ;qÄÎ…9ANŸ²jë›d¦rØ9:ÉX œ‚²!þ †ÒB@ÅF’è¤¯Ì v ˆ¹õâ%%e ÐF6ºDŽ1¨.Ç6‘ì-ss¤HÀ&Ø—€0–¦Àtr°)60D&¹ɦƒj6q”Ù“®¶@P–a3¾XË9$+“@HÀ@Ä)‚òT!g”¥åÈÀ5É¡AÙƒø„ ÀÙ Á‰b9Ë©4dÅç©  ÿ“9°ôQ.ýÏ;ôUÁ "¼‰Ê 0@Dбa6«{¥º°¥JqњϘ4…¨C‚6±@P4!!')Öa¸±…&@ ºŠCµaJ€•dž˜€h)Ê”Qph¦H^ˆ§00Á@£c¸£T£:òU5˜„n ðH"ÒR¹Cxˉ‰Æoî¡Û- ¸§’—ÈÁ$à[ö´Å*„66Ó'Ù>§iÞààM<ü«¦ác-¤ü Œ½V(£Ù¥0Q> ºF,BQGHÃ*8ä6ÚÿˆéDnMІCW46<`YÉCO¬u׈`rr‰ÀNc 8Oº¿KP?cg!QÞ4¶~ô_É֖ȦâÛx®¿Ä–m­ãÖÚ)žkŸÀRx‹ Źíd–ÓÈåÍdc0Q“dHf?Du Ð]¥ˆ\ˆŠgª(ý$p@¡ŒvÏÂ8vu‚×âÓfé÷(£á­ µ¨ëð8éÖQFg—C§7&úà “÷ÅYÙ`ì"~‚–#Æ{TâÐÈ”Šëæ+s&^iyëêQЈhx O0¿ÍuavÛ7JÛàõíÎi>V=XîóÕœPÙ;ãsæ6 È™ÿ‚,’úPlÓ ´gÿé:‰€o`ßÃYyCå…Oý¡¢)6ÁÖ8˜Nˆš½Œ^S¹€ôTÊ AÎ埼ɠÊ;ÝV¿|mÂôÜKZTƒ¼Ã±·(×$8G¨Œ€¬5ôÊœW=ŒÙ†€X`"ÔDY'hÑϸU5Bg†H0J,lŸà AOØëiQŽ5€|5 `!‰¤NNE¦@jNI”Á .4@ÖŠAØŽòĵqáÉE0|Sð_UµJhLö̘AÚ!„-Kg`B¸TÛƒ@QŸAÄ=<æ™ÀAÍ@d²iQE8Gõ Ê”&í˜öÿMš”XËÌÔë!ˆîdÌÌtN)¬ -‚¹M…(êLm¡òÛæ[½màZ`̹‘ÐhD¥±ÛÆí’É`vŒUˆDÑ ¡À\ÔĈaEx£ZÅHˆÊ@ÌR ¨­DœIÁS,@µáA¹irDÂ:ªÊ!1Ú²•Á£Á€œÁ4ªÀÿp]éÄ?*F%d ñŒVÈýµ"´Œ¨@ù ãÉ­ÙRDöæIˆEò@¤ÄÌ} !zœ9²Þ>¾á}ä%œZ]Í0GR£@_L²ÑüäuÁ=FZÙcÀd óPƒ/A9>›Á ƒ`0FŠÿd@ŒR®9âcó° ::‡nÂŒ¬#ôDÛ Edc<¼E#†ñ؉ ¾¼Ë6ÊX‚G­Ê¦U ”X’¡ØŠù„/6‹áÐ%¸ØefÝ0é‚؇ÍåˆÙ%ªJi„«¤  ÂãÀ]yÐ’ÙZ’iXªÐ˜(„F¨„N(…V¨…^(†f¨†n(‡v¨‡~(ˆ†¨ˆŽ(‰–¨‰žh‰*€&Ö%‹¶¨‹¾(ŒÆ¨ŒÎ(Ö¨Þ(Žæ¨Žî(ö¨þ(©)‘©‘¾è&„;ntpsec-1.1.0+dfsg1/docs/pic/panda.txt0000644000175000017500000000126613252364117017161 0ustar rlaagerrlaager// This is preserved from the old quickstart page in case we ever want // the historical flavor again. .FAX test image for SATNET (1979). image:pic/panda.gif[] The baby panda was scanned at University College London and used as a FAX test image for a demonstration of the DARPA Atlantic SATNET Program and the first transatlantic Internet connection in 1978. The computing system used for that demonstration was called the {millshome}database/papers/fuzz.ps[Fuzzball]. As it happened, this was also the first Internet multimedia presentation and the first to use a predecessor of NTP in regular operation. The image was widely copied and used for testing purpose throughout much of the 1980s. ntpsec-1.1.0+dfsg1/docs/pic/pogo3a.gif0000644000175000017500000000711113252364117017207 0ustar rlaagerrlaagerGIF89ak´ÕÿuuuîîîüIMVTýÇËrt—º¼§§§"""UUUÌ\ÛÛÛ#%”Ì”›¡ !&"ÿÿÿÿï÷÷ÿ„„„ŒŒŒ999BBBÆÆÆ½½½ccc111kkk÷÷÷ÎÎÎJJJœœœ½Æµµµçï÷”””ïÞ祥œ¥Æÿÿÿ!ÿ ADOBE:IR1.0Þí!ù?,k´ÿÀŸpH,ȤrÉl:ŸÐ¨tJ­Z¯Ï”ªTò˜°à0öà)›=±zÍ$g7t‹Øø<ª\˜ûAyƒa.&}~)„ŽU|Š—O, q’s•˜¢K)›žrŒ£«G¨ª¬³?1°±¡´«šœ¸r7e–»¢§¸ eÄ£$9¿Àe+ÌʼnєÕ—&·Ñr1ËÜàrÓäŽ+è  ‚너èÙÃól/ßàâ_ùy6Ázp-@6(º¸StŽNŒDÊäs¨ŒE7^ÝÕ'GÃ1sQÑxe4høKÅÙ$)‹xFbK*#eˆíO°ÿšeHܤB£Kd¨}1T W £ÅÚÊ ×ÀѤZµMŒ’ï²×I ¬ï5S¶UŒ¨iAˆ[K¶í=`㢤[× Š%:lØe«§^@| ôeRÂÌ:dXïV ,N¢Âƒ ȳ:”5îJ=¨ö¡ºµfLÛ”©¹‚¬J-ÔëÑ k×®{¸£¤ólÈ„±ê’÷Ýi3­/˜îƒ3Ä‘Œô@Á€ 3¸Y•©¬u®™Ì.˜éÁ¾&’§*p7PÓ«eÒŠ­Ùšo™'x€BóÙÐ]MÙ‚?RÑ¥šp|u ៰¶¹7 ~»áÿJ?…jþy@hò=fÀcòÕÒ,/””O ÄðO-gpП …v"h@HK¯04•C@÷`‰?öˆ"P7ÒâŒ%¼XÄHÓ•èÁ…N:Y“ öUSÑX Á¡–žuédeœ€9*  N iC“jnðŸ]JhÙš=Rð%ŸH é$ ÁwfÀG¡E¬u€ròeB ~pfx”þ°‚ c–C§¨¶ Â€mžPªü0f¨8Bj€.Àª«­¯BzêŒFQÚEF®»&Ûi h@z gü€Vë Êfû €B©BTÛ8h›-  ÿÌ]œ@C˜„îa‚¹Ùzi)í”@ o–µÇ£ô&‹Â ª+Á€AQ âÒâÂö¬+ œBÚB¯à9Q‘ LÊÌvóJ k¯ºÊé™Lp˜@!Dê.&ÀÐNQ‹Ì,ªfxjF¿Ct&!ˆ46£É"pÎe¤kF±C1Ë"T-´.ÄœIƒÈÈtÄ0Äù²Oy€@ÐBÂciº\{ºt§ÓtZ¶ 'ÜzÆÙiWBW£ÛqËê´(| êD” h³ôÌs€ÞÃÅ Àܨ¢ C ~£-Â2LíwÕ€{ï:g|€­Ä7£ªÂç’‡Þÿwí-³, M/ 5›°Õ§K®¶ß©oFÎëÀ ;ôm{ÚT×0\×°åÓpnn 0t1VS?}Õ:Äðù^Ü´]ÄÙz>À ‘«M¼Ði×0ƒãf#Àñ6-ÑÄV€¬]¡ z‘Z B°€,`ŒåjbÇUi("ÐÖveÓ% f GHÂ,`g@ÒP$¢Fé*`Ù€†;ã±L ˜`.E©Íå¬fÌZ`'€?Ô€àØÜD+"0`fð;A O—: ±Q0h"JA·¤mJ le  Ñ¥Kx𣭸éKÊêL•> Ý«@DÖicߌG´®‰è¬-ÇSY‡Ð[ºàfj!Ÿëæ'Y°aFú'JøY²ÿSP@sXo$ô¥GøÂµn'ˆwVüàu…Co…+VÊC²`¯SÀ¯7wä·Î:(­è‚¨EÆc4À`pÃÙe8¤Ljrz¾„ö°³\{J!êD3xðÈLª9·%`µ'ì:!aJw•69qSÕD®;I‚¯eÅD¨È˜éÕ‚‚—-(©ÞBºÝOaLÞÍc³Ç2å!ì¡¶±Óˆ«IÔ±` ¯72ýše  ¹ e(8µ,ö+HpÒ,Ñ]b³†ŠL7Õ‹,‘¹µƒ†üBJ]§dÖå´Û&!øgçWÐM°ŠiȉG Ì *¢‚äWÅC8P~ÿ…ÍbªH$]>ž0ÍMîNÒX¸~æM× b_KLÒ¨•€NÁL³ÚÏPq‘3ì‰ü2­¡°m%¼àd'B ÄŠnÒx‡QH|Ÿ¢¥(ô½P‰óZ ò`ë}"ù’Â!Òpø:±}Ž—OÂpš°‡Ó³"ê\¡Â!œàŒÛ¶r‡¥¬»\ß?®âüZ<…0Ñ* ½§%†¹`ÉpÈ /`!¦:n¸÷ÃÐîš r˜7ñ4¯BBšPr@[»`ZÃú…ᆯƒÆ8P"¹„=ÍØ¿WŽù??ˆÞú@´0&©× kÇ’·O7G[ÇyR´EØÿ®Âƒ }E@ !Á¢#ŽÃˆ€cÜ÷\§ `>6d5k³Pù€X÷nB»ä€•9¢3‰ÔÆ9¸ p˜$B€ƒCxø±2TãŠÿø€R7QŠ•ÒBJôn@нhwÍF?Ts= "5¡£’ˆ6à£Ð(gE`à7°(]9Ûø‹Ÿ¨6Žâ iSÒ‰,óFeð9YC÷Dƒ»0Q€B>¥#1~cScC¸S’¡øŒÈÕ@>"ðFµ¨„¬(©ã‹ ˆŽ!à…eБÕ0^49ì£{HÀ!ôx5`?Ûh’3F‡ ÉUóC5äóFRY(ÉÈ2ÿDC<6YD`–3 A„6^Ø2A£K¤‹‡ipeЋ #=çˆzHOS“¿X5^Ø“=à×C‘ÄÈJrÈmö"‚ÄADÝUVõ裉ˆÅ‰‹|vÖW^-J˜ž Jd£iÀáeƒHnÉ¥)JÆãOïwÞ”3V”T•z5¹ÙRijÙeœròå;Q|f «à9gŸ~ÖP§žTœ)è3Šèú‘”Œ¦“h¤’ª÷¨¡uUªÌ¤šnê ¦ð)“§–rJj©h‰ŠÐ¥¨öª©®v¹èªó©*«g¯Þêj¬²¶ Y­{ðŠk°[éº*°jùzš°ÊÿNÚB³Î>[‚ÐNKí´Ë^‹m¶ÚnËm·Þ~ ®·Dæjl¸æž›K¹HŽ‹n»îzb•©*¾Ko½Å[ê¼öî˯ˆønb¿÷p¾FŒ0º[pÂ+¬î‘ì>L1¸“zqÅk›1Ào òŸk:rÈ&“«lÉ'¯ìqʳ 3v*G:sÌ6÷Y3¢9ßÌó–;‹ürÏBƒó³ŸEôBGϹtÒN‡ÓtœQ?M5ˆAË6uÕZ'yõbYo ö)_¯ÛuØf2¶ÄeŸÍöiøvÛr²öHuÏ·¢w'´wÞ~ï·Ì}ÿM¸”ƒ3vxáŠ÷–8Ô/9.GN9ZÕÿ^Žy´MdÎyçWŽ0±¨~,º¨ƒÎHÝŸ[Qº§¤#›¬àZ¬NÉéMÍŽ ìÌѪ;œô‰{’´¨ž{ïQjüñ® |- &$4ñb$TLLoýõ%ŸÞ:¦¯'/# cÿôDRŸKc‘˜úÞ’Údí[Çam`=VÕuó×/“¾Wt_©ï/ åŠ_ýö‡–è`-Ñ_‰4$ÛÝ2±ÁÈ€:¡˜ 9? ì%ÀJ¡€¬©à_ìƒÁûpð?dõç? íÇ5òóWX4H?E‰€„ÚáE¤TBþ …3ä_käGbÀ…Üño3 (Ö†’i¡}8ÂÿbQ@|á shÄ ’%0þqb½´ÁÑ„EÌßÈB0N>oz¢ENˆÉŽÀ aÿhÆÝ2 |#•(Ãá‘/‚‰9Š´7MG{’¬dŠ†Èºå㎬È#bôÉ]¨KE?¢ˆL¤—Pe’és&%8™ñ§#5âD=y‘ZòQ” ¡šá%'GA;öø¦bÚ—ì©e1iLee.{œÂ/³pÍe½r6ëq&?€ƒž+}óFYäÝužX œ §ï—MÔý¥›¤%|à㤠ّ™¢H–Ô„%+²œò|X0Ñɦ€RH)Èé?ÑéL{þ“ÿL%hAvPŽ)GëLÐ@ïôPFôF÷\“‹ô9¨‹:,£#ÕLI—ÔÐwŽƒF-…éF©´Ñ ©£ái§DoÊ „Zyô<©MÃÓðíÔhtÐdŒÜ %£ˆS¦æ–YM<¿tšM~ónqR²ß¡^“®4 s1èR:ÔA"u5]„V¿:c²ŽGª¹ë^‡Ø;)ö“½ìßXzl·>Àµ³}n·“ÀÇ>w”Ÿ} pŸÜónŒº¿ãîj|W=‡xÎI+ñŒ¯–á‡v×aýýñ+‹!ɶIyn[žY˜ÏüR]ë±Í{~§ ¿¼èG_ЅɋŨÇxäEòúÖ›-öJ›¼ì+F{¾Ý>鹉íw¿Òß_¢÷ÀwñW|× ßÇO¾Ðšï8çïúD[¾ônýÚeÿúï¢~Û·ÏývyŸã¿ÉÊßð›ß\è·ÚúI¯~´Åÿýâš¿ ÚOÿƒÚ?øg럘F³tS rc_€7€n3€ÿ•€ð³€rо`b‚QJ!¡¶åiHy§ç ˆ ñ‚óÕY%Æ\ûƒ\Þ©S‚Í19ügJ6„aS”Aáuƒ-h8¥—:1È€<¸F(x9HD¨ƒâÓxI¨„KÈ„LH`bäÆÅ`Rx„™Ö„Wˆ…Y¨…)ð„^…*ö…GV…¾ðÀ$‚fø?eXa^(*T„n8†!ø‚ièƒxV‚x]+cz‡HV—(sˆW5•4^rˆ§Ô‡tˆ:Óˆ‹˜4@H33‰ÆG‰‡"‰•‰jÈy™¨‰›X‡ŸÈ6ÿÈ5¢87¤(¦(7¨h ¬¨ŠÙ⊊ôŠ£x†tP‹³È4·ÈWºˆ‹°Â‹`Õ‹a‹bó‹Á¨6¡hŒ–ˆŒÉˆ4Ãè%ÅÈŒXǾ4NVO㌥°ÙxyËè6ÓÇpá(ã8|åh޳¡Ž¬ÓŽëø}à'ƒŽÌ÷Žó˜~òˆ S•Ðûh†[(/3 Ä xÜ@xvúw\h{ÇwiǺçd©XÀƒMI"‘ÑwÉãWLôbt"’ÙŠ9+/—u!©‘XàUeˆZæ3ˆV=Í%h5)=‰’ ¹a\3É[(àH–hHj%ÒyOg@QF?@ÿy?â•e*(”I() i<,Ɇèõ>NioÖbR‰ ‚øX &„¼ÁfSfFøµW^b•<Ù_ZcV”–p¶–M¶D†† –—uIE‡h=´dÌ÷–XÙ;rù^„¶ ´—a™D‚)†tИ‘cb„O†—õu …I‘=™Y~ù€Œydå…™wö>˜FˆØ•fy‚væf¦i–—sýpimÙ6p° —d”˜–c_Ù‚$];Fd?è•ÖH@©!Åu[$A“ß…JÃcU¶ÄKÉÉæq¡€'¤4" È>©Ô¸åœDÙ1—7ò¢qGp{Òw4G±0 Y’$Óo7ñk:ÿ‡Lø‰›GnÕ¦QÏ$Mo•m]ös6"!C•ëRŸÑU,7W ºnlåŸÞ¤ëmã!o g5ò9{ šS…nIEWjMõSHµUz'Yn´vTUlºOÖin.J%B£ØÙ(+*-ºqõÔQFµm*™›6 ¤ãn:êc<ê<šP"T§l4š u£.šOê¹_L*6é9W jp):£DZ¤Yz¥g¦HçÙç"ê *‡fn tdš›lÒ  ¡Cš2ƒ@|ø'V,ÊejVvº&=÷¡sJ¢àVÃtVe¨Be ÷bWh@ü×^h!#É”SG§§A[Úÿp©Ø…šõ;‡eª¢ª©:´ ªuTšž×€˜ÍSŸY«µUJ©†/§v=¹•h¾5bÊ©e©¦ˆ8‰œª[vg˜ª{•«žµ«(ZA KÀª˜G‰CÃUZÌiž„EçÃf+È”Ï5œkØ\ƒ¹^°ù?œù©žÉ O®Å)®0ô†'ÈdöZ©øã\ÄÙ–°YceadÖ‡Ax¯÷•™€Ã®‰zvˆ™­kDb,ˆ`+˜ù:clé r®´°¨4IÿªdbÈb;; KmɰÔ% Ûf{amÔ—¬²«ªþº²ûEN”d/»bæ ™°ŠE¶™]'+\5KdùJaH¦³R©üÿ*©Å)œ\ •vÙYU6gwYgQÙ³ýy'aÆ kU²zT ÏàˆúU3Öfbi^—i³X›™êõ´ F†¸H¾Úg‰ö«Ey“-{­Û‘­ š_Ã&¥”e@KR{RsK*98jÑS·|©jx{”ƒ²††jûܲIUú)Zº£’¶KõFY‰ûª\Ú¥‚‹gµ¨úÉk#úµÌz›ƒTª‹oJ:‘¦ë–0Qãô¢º»úº°û»Zu¦íáq´‰»§+tîù£¥Pf¼  ¶-—%!ŠpcK¶ÉK˜hÂh/õ¼ø¥Ù rŠÚ½Õ¦ç¹¥«½Ã§e‚þðM堞ةÒÿK#ØKñ›蛾h·¾ì«ù¦š µ;¥í »÷¨5*o›ô¿›‰ºX¢å;¿¢û¹ãºlpÌ¿üÀÛÁ9Ei8Ei·ÆŸàÂqErá{½¤«¾Ö8©JG@a¦s×¨ÈæÁìlÕTÃ0zÃÓ¶<–1œ/£ b º“Ë ;³º‘©J4F,dHÜ‘†û³¶ê’e 5¨«ã«ÓÚ« ÕiÁ:–~aÉU$d¼ZfœIì³>’Ïúg«µ[^ G~öÅœX¬¯t¹g¬Æ{FßšcU–ôjƒåÚ¯h¶“¹†.¶e&«ÄÈâWðÈ{ˆ¯ûÚ†•|ÈŒ š½9šáÚÉÿ¶²$±«‰–9‰Èg¹ÆSüȾ"´Úñ¯CÊ«™¤LË}iÊU;´B4±Œœz{€[YËgt˨A²ö v­|^þ¡²o+Ǹœ3+_§ ±Àl¶nḚ̈|Íœlcú_ ßáWÅŒÁ{‡ÌÖ¬¯—|Ο|µ'ÔÍš¬à œ‰œÎ¯™‚j9µr–“ê•B3YBúLAáÌÆ:´0è´óŒ¶ç:FDµùì­ÿü”ÜáÏ”}{œ¡f?v|·¢&¬¥j¯3”WE2˜©ìÈmìtï*·K·m·|v­BÂÑA‚® Ó¬é3™šÉ§9ˆ™…‹§Ó;]Ä'4³/-16-Ô³Ìj8•ÿGÔhYÔÒˆ”ÕdI€+5T.—ÕI)Õ¹¶ÕU¹ OVM6] ÖÚÔ­8sÖe ‹x<‰ž¨Öõ§o 1q-×ì‡Ô™V×üòÕtr×y=Õtí×p-,{­1NQ¬S‹>{ŒØÞq[e Š¨Ç {}]WÄH­ÄÚJ«$žCùØÉµMšm9ÏéØb²Ù<=o[Ñ/{Ëî<¬X&;ƒ-›†<]§Î©]_«½˜=Ë´@­Î8œÚœ—ßÁÚK` IØ|ÛŠ|¯¥ùËM$ÜÑ ÝvˆÑÏ _ÇJ° ­ ݬÝÇÛŠôg$ËbœµñÜ´å.Šr€ñ3ÍÍ ‡[Üï=K” / ÿ‘Þá­ÉVôÜðM ‰ÏAHÝò¬Ú䕿,Zë,€Í§ýHÿýÊ)v"®=Ó!¯§œÝÜIÑ Ý[;ÚÛZÌ©ZÄÙÇMÐÁ‚Ž?¹[\¼Ñ¾Ú[üážÖâäJ"62N€M/4΀6~ãò‚:Þ}<¾f>.~@·B>×Ýmäç‚ãävàMŽä$NäP¾xONå·²ä#xå[Îå]îå_æa.æcNæenægŽæš‡ISîÒ!æ5Ͷ˜al~æ“ÌÞC}¶oÎ4ý¼ËÒ*YnÔØçݹ>tnãôuÐ`9 Üšµmè:Žèd„‹^ÝõšÐz~ŽŒ^Èè {šÿN®œŽéO\é6¨íéEXê¡Îž†4Î-én®êg_ø5é”ÎÌ­Nܳ>sŽÚwÞv¾^ÛÀÎëÎãç‰HìäwìB4ÞÅîìÏíÑ.íÓNíÕní׎íÖÙžz×ÍíŸG™ß¾lYаž‡­j Hèoxî}LÖâ¾<¾‰ê¿þÛîôþ\¼Yï» ï<››îÊí]€Ÿ¬ëŽÍý.ƒ¥YðO~ éꯗEÆéúlïžð _ñÍìÌ?ñ—¹Í¹Mw¹ñ®îíÄ Nš!ïÑ OñŸÌ-òèšofd|h﵃ïƒ\±Ãžì3ï(}ÑÕáç`\€EÿoˆD¿îFôMïôOõQ/õSOõUoõWõY¯õ[Ïõ]ïõ_öa/öcOöeoöWÍÖ½±ª?¯ögF'ÏÛûÐõMªnï&ßö§º>;JvÏÕÝîL¯>¤•[` ÞÊÌJl˜—=Ü~?ª<_ïyØè‘¿ò1/ùûëQÛÚŽO7)µ0ðö,ó%ò¸ë0¿Þœšy®´ñnð˜ÿòÄÝ¥ÿèÏnͪI”$ŸÆ)èF8(Þøç¼ÿ€^õ¹/ú§¯ü~Ib4Ôü5Ô@kgªO’°þùËÿü5–ýïLü¦_üÝŸúÔ/™DKùóúÛÓ¿üJïóžLùÍ.þI"©mØI¯Æóh£¹i˜›|r;ntpsec-1.1.0+dfsg1/docs/pic/wingdorothy.gif0000644000175000017500000002514113252364117020377 0ustar rlaagerrlaagerGIF89a§´÷ÿÿÿ½½½ÆÆÆïççÿ÷÷çÞÞçÖÖµ¥¥cZZZJJcBBÎÖÿÎÞï½!{91œ1!¥1!œ9!çÎÆµœ”ŒB)ŒJ1„sk{ZJsRBŒkZ{J1”kRkJ1{R1„R)çÞÖ„{sÖ½¥cRB÷ïçÞÖÎÖÎÆœ”Œ”Œ„ïÖ½çε{kZscR­”{ŒsZ{cJkR9„cBÎÆ½Œ„{œc!ν¥­œ„­”skZBŒsR{cBcJ)¥”{÷çν­”µ¥ŒœŒs”„kscJ¥œŒcZJïÞ½ÖÆ¥Î½œÆµ”¥”sœŒk”„c„sR{kJscBcR1ZJ)Œk)skZœŒc„sJ{kBkZ1{c)skRkcJcZ9{k)Œ{)”„)œŒ!¥”!­œ!„{1¥œ!­¥½µƽÎÆÖÎƽçÞÞÖ÷÷ï­­¥µµ­œœ”¥¥œŒŒ{ccB„„RZZ1cc1ZZ!ŒŒ)ÎÎïï÷÷ÿÿZc9„ŒkRc!cs9Zk1ŒœkRc1ZcJRk)kŒRJs)R„1J{)csZZkRJŒ)BŒ!{¥kJ„99œ!1¥)­!œ!Æ1­!B”9!½ÖÎÎÞïRkR9¥9!½!ÿ)µ1Î1­JBŒRs”{RsZ1œJBsR1œZ!µZ1­c9„Z„¥”{œŒJ{cBŒkRŒsJ„kR{kBœ{R„s9œ{J{kc¥”J”„BŒ{B”„!Æ¥Zµ¥R­œ9”„έ9œŒ1¥”ïÎ1­œ)½­1¥œ!ÖÆÖÆïÞBµ­1­¥)µ­)½µ!½µÎÆÖÎÞÖçÞïç÷ïÖÖççïï÷÷ÿÿÞç1B”BJ{BJ„ÆÖ÷ÿ!½½ÆÞï1)”)!œ)!¥91„B9¥1)„skœZR„B9s9){B1„cZ{sc”J9kcZsJ9c”Œ”RJRZJZkZcsckRBJÀÀÀ!ùÿ,§´@ÿÿ H° Áƒ*\Ȱ¡Ã‡#JœH±â@88`¬°È±#Ĥˆøf( Iâ$É–”0;¾éÒd—m<Â$F(/bþƒ³#È&EN2|㢉)1¤¼JUa?šä˜"e‹ÒŠp¢l#HŒ.]b¸€ùfœ¦Jr0™ÑejÁK–ØÙñÄITUÿ“£IO'=ë¢|1CÊR~K‘Aĉ“-r¨ÊI›ÃÅ'Sr(‰¹Eo”qº€èÂ!m“R:sÀáDÊŽžS”tÙèZ¢ "-ÎæÿhrÖ %.¾ž1ºC¦ì 2å‰è·íZ¼*¤~TÎ[ÌðÄ3DÑ…|1°Ð„ Ý5dƒA°ðDQCS¨ÕZƒ…$D ;4ÆAc;!ZcéÇQ›Y‡˜€DáÄOÈ%DƘ— s4¸ 8ô´DD4æ[[lÈ!BrÀÐ×NpЄ_¢ K(ñ‚zÍÄATÅÐ HD†1ÄÀ<=¡CÁÖ]@Õ…_[Ô·ƒŠK&tÕlBlÑ™_NÀ ]µÀ1LĞɑA”Vjé B!Z;tÁÞo-ìàPWmaƒ /Tª@oÌÿÃL—CµÞ–[ŸÅê [Ä@Îà]õ)±C±]؃ Vi`Ž8çT[m8ç˜Î7ߌ3;úäЂ ¢¢%Ã'"ÚEDA8Ðh˜D - D5 1f ÀÞ20ÁhUpÉ«Cp¼!Ç*ØPZN1Bü@׉Q@—–_€$‡láÄ?4:ÃøA’¡@OÈP§º šq`„@Å ^ÆhôÑÇ|ô¡”Ä&ÁB D(ñ4¾Hàðƒ„L¬µ0E¨¢¨EØö‚h$AaA@ÑYO(ZD­rD³É0ÅJN1u¾C ÿ«ElœEãˆcø9UP6P3äÖkÇSÔ¹là$ó¹5Aqx¼¡ÎÀBÀ!G+hP®rR÷!ZDÈ^½QñÄB€ÇÂ98ViÁ•dúº!ðB6Ñ@ƒË8ánýá ˆð)¢0‘ÚM Q¯ÚI¡Ÿ#$À«‘Й°ºðÄDh ÅONÁÁOˆ Ýîå2VbÐðÉ OøI¢…® ¨^ `Ä.´AÁ "£‚ÚÀ0¨‘ m0Ì,–C— ,pBêä%¥/"*HRD@™°µ° RxB´ƒå ïvÌ™Asÿrˆ/XPÐÑÀ 6d¡g †3´q k`Âà/aˆNt¢’bZJˆ–-Uä ppáÂä`Äauz¡v0„4¨@ `›, Y¤ 2žWAj#ÃPF5<‹ZÈ` 0†sq‚@Ž^A QêÕ…]½° ,Ú'ér)À Qœi‚Ý¢6ý°N/xA"‡-ÌâE˜ ª¸ …Yô¢Ô F2n!(¡Ç,B ¹ì€+’E(0!F'°àÄÔ¸¦ÀAÿ˜CÈ>)8‡&BèB’à g¯S× @МÀ‰êS ÑI¸@ÿ—É h‘#`0ƒq¢N+ÃJ÷Óœ)qA d`»ØE¡dëçF³Pyò *Xá‰D“` 4É %2)0¡VZ â`Fˆzä Op ”¼&Ø ñ\*,.ÀoEŸ‚BéŒjÓ¦:õ©P]‘ç¢“ŽŽ„ªR”¦ŠÕˆ¸@@O0èC4š£rDA˜ Ð÷ Úp î©“   ƒÎ@S3ß4Ë«AM­Sø2É¡?åÃûgÎ’C'œn?&ÛBŒ~P$³k †i œ¦Ô„"°·A6A š …4 ,BrùBf 7s;`.Z0Œ#"˜U" ãG4÷¡Ø·M@‚vê3ƒñô„ ]¸±Eäð„Âf ?°[‹XˆÆÁ$PÂ:“ƒË¶ö°BD ³ °³ç³EýÓÀ¢r3I@]–W\ïd€Semxg2`!‚bP€5IkPÐCÿ ¡L@ ™5™£ŠA“NOp/_V‰*à$C&Ò±X]wY00P<=B¹"¤X?kÒXÅYM0‚;Ð%0£]Ñ@íEðh¡¸"[°Ï´Zçq\Q(1‘Sàw<Æ‚ Õg±]INM!'9aãT±3Ð]V2°>*¹"!"a7³= E^ôѵTuR“2CðLB€Là1Ä?¢ñБâ(0°ñ©¹1™7+à¢]€LÇØToÀ-€`i!(¨•S u‚Ze"¥tJp ‰ÉoÑQ"k r21ÀÔÿ9åf:(Q}~ÅCPlKÒŽD0"­$dôÄUf(1D1°s–±E@bSp5PP1PŽKrŽ-¡õ…ŽP š[Cs–zG<%¤isè‘ ±Y×eYO0g~ƒ1R@I°‹ RKuHz÷^2°…:™Ð.°z¨‹Ç2S?ò‘d)3‡&‰¢‰bÒ“Áâ=]`Luø–Pp,<–!©Žé£Päá&YžhÑF T‡w f³QMÖ(ÛÕÕIЀ‘9.ÖD#ú³,?PRMUZ`©&)GjLH°J‰–&¿ÑpHðL[ %C„8'ÿÖMÚn•%îU—5BjŸ¤tDP!.A_%Бêp=†£Y@ˆ>@ƒ58#nPv€o…Áœ${1Ù™ ¨S£PÈyrØ%Q9’h3îU pø^9ä®Ö”¸T5‚/ç›!DðcÍñjô;ãp ÀÜ`8å€nPƒ0ö¹W~‘¾h’/"yÝgS»a*°2„Á”‘y Ö]¤˜¶QGQªÊ bÿ‚J¶²2‘_â{Ù…dªD2;À 4 P$ ´Ð ð§Ð ÞP 4 >DP±6cå2ò6Uv£*ê±\0Œm‘~ÌÁ_—{g; ±1pDðI _%Ž2¢2_d+D3Pô|Ìð Í`D4 ÆpÊÔ IDA=`Ãdúb;PY‰3¡– ôyˆPrpghA{\ 84±Ë*jB6DÀ}!7ˆò« ¶ÊhA” Pä  DØ` tàÒ€AÅÔ·`]‡qàžÁ8:˜®.°8"‡’=‚QÿQŠ®8áz'Jð!´ Êÿ0 Ú€ ߌA»` ˜ ¨  GàÊQÈw ËA2?ðõ¡<™Œ4C;P*€Ê ÌK-Wˆ²òÇNà¡ð ’° £@ ÍPAÙ@ ò7y«° xÅà‚ “ð)€` ¢yR¡„¹)hâ´®Zç¼BŠW¶ÃqÐ-p&§”_ó…Ö&wÿQ$ ð Ô° ˜ŒAÛ ÃÐÙ3€0?àÎ ÈpÚ×@ Wp7ÑÁÄšpÐ*w¶"½n‡Áƒ(%_#rpz±+†p°ȧ !t œ€ 7 ³ÑÚ0 LPQW1 ÈdB°³58h¶ÿõ†€ ¤E’Oõuì: ÆuÔ9@Ðb2̱ò4+ Û x4 oxÑàÜ‹— ãÕhoò49à¡À ]`I3˜À høÚho}ˆUq@³v÷¹½]'´—TPñÖ&€Ñ0‘%Qiem}(6£p°£Óv 5À³$ƒ ¡  V/ô'ØVÁoäMN¥b5A˜nwOæµ=Y-ÜËÃˇ¯O?Ðw¢QÑÑCj$= ‰ ..ÂÇÕXi6…>w¾?Ð?94@œXŠtáC r“hñ5C?umxü¡£s„AG¥æbXT:'!ËÍÿBQ´§wêjv¡„¯â:.ޝ¯£·%6 ­~‚¯iûJ¯}k®©ªÎ"ãÎÉaÛK˜ɱ=EÐÙŠ  M¤( NæaÞ… ?*5—!L?ÀY44< d¥‚h/à£.Ÿž‡¼<Ùì/½¢ì_eÛØþíàîâ>î½Ê‹W1í¼(*>^[o 6Fî+0ÛíÙHîÐû;äTRž¾ò•ˆEAø$©æ Ú¨ C‚&»Öž"È€€ù<Ó ¬"Ì>hqO’¤`£4aNÅtøÐ¤&µmCʲKntÛ‚Ff@›”XÅrÀVð‚™ìEØg& Ê„En1 z‚iš -lDZQ«Å”ôSrÚÍ:(R‚ÓZ‡6(U°&èÇç¢@„ f ®9©ŠÀ èiGÿ-QØé,„<tq°`°…Çæ¦Dø{BåÞú9 A‚"BLƒ.’Æ å(õݸJ§¨Câ8Ç9ÈAŽx·è@Ç8ÊxÈÉAŸ;×w膴’Å¡˜€: Ð1¦_ü¢4“€ÙÀïyµ”ÅÓ>D QˆìC·&tÁ7M‚ lå "$†[àÂmí±Ûw €6°Á>0I#À'8A˜ I<©e@û“²ÉÍl.€ªÜå^ðƒI%8"âÛ¢žV‰éÊ ‹€ÎMŠ?PÂøŽ¶$+Dv!pp¶…,hÀǃ8D !ˆ@B‘ÿÄ$*a˜@>IKÙ ü€nf? w‘ð8¼á K`A ü3åN1ÊOŸX…e àGA–ý$Ø$ˆøÿÐÊì¯|T&ÃQ—êð>”Á HÅ6ö`I…º¨crûÏþ:¤\µÊ@Q±Ò£–æ€Ã…c ‚ÀLº­nð0Çnõÿá”.Ô(€Œ˜2¥ ] ¨ö9R·`Rx;çØ´1Sáž@e?؇u¾ ßgPbƒiÈ*‡Q™ƒ^ê&l!ÝAäQŽÝî¶=`ïŒp|t" €HF¡–¤ `¹s³¾‡ð*h%Ò› º¡ß)›h ÓR°§àΑ/*µP;;€»[žȯ¨}È¿pÈð(( ` ö %.Æð‹.øê{«tº­€ñ)Òû9!½V‹ 8é‚à‚±Û!äð4dÃÂIñ"Ï"½p –$ð¸¢H€ÿsuЂ{r‚À Ñ «À0,”î ‚‘K/8X8`¥*”Ð,” x.a{ŸòŠƒˆ–-Xú‚œC`>˜°kh—³Š,Ї{(„i@†7‡r ‡@h pȇHT‹ýzƒ ë("¿°§8kÀ%2 )X7H'8 µ°¢ŠÀ ©-àSÅy9‘2Šå‚' ‰€óDÏA ûA¡yiŸÅ°ÄðƒiÐmȆa0\ðpØ-sqoˆ…]("¬Àˆ±‰yAÚ©/&¼œ8KÕèÂkjBµêØšÅp£ÿpܘªÐÕp "X û´3‚œˆîûŸ±Š'"P9€G™Ô†h€Çb@ðd°iIaûIŒ‹ø—çZ?P+eQÂVzÆPMÙ•fŒ€ƒ® ³¹ê•ÁZaæ²–8ë‚$˜ PËF)Ø'€¼X. 8™Ì†iˆ^˜Imxm†aÐ`°x܆T¬Àç*>›2)Ø@ÛÁ- #íHÅD‰¬ð)à🠞y9ýÂ¥”¡ @;3OÜîÓ²<@àKf@™D†l˜IgÐ…#8…l8i€Kg8*K€ƒ^17¼>µHÂÿ(!?y,ЂÐ=2Qü‘&è+pŒË[&(¾È@¤Ãغ¼Kºl@†bfIFˆÄ©JH‚-èѱßlB›J(âQ'…ЀÆ€¢‰øh¬sÉßa)lŠð´uL ¾œ†fàËlØ…š|Ëa˜†b K[ð(TA(0ˆ|Êù\ßT ’¼*ô³Š¥J­Ð y‘0ÙÙ!`s Ã[rú¨.…Sà…ÖœÉ]8k°l¸ËôœO…fxÇh0õ„ 'ȋę8Hµ°;)7c<â‘Мˆƒ¦x;Xω iiŒÿöQ’"(†ïŒGh€Gdà…šœ†Ø¼Að„ôœAx†gˆ…V8R™Ùȳ.¢}¦PF7Û³.¨‚ýˆ6ú ¤I¶î€ &@ø„X`P€„$•I\8…]Òº<Za°K™„?¨„P`²PP„龦x HB¥À®ˆ››Û¼•xFÜR€‚.ô‹B³‹±’HX….à^P…Pˆ…ÕfˆGVè„M˜…S˜U…Çl … @‘[ú‘²À¾9 °xMù,Ä>HE‰¦´!x`€¥7” ùìQ”Yà…w¬KfHUÀUÍYðBèÿ&@°ž4×kð…X<¥Æ&œ)P«z›ˆè±|U¯"5€ir’Ù 2»©ª\åVF†m€Çn¥K`èm †j¸ðbÈf¸P\x–€yJHˆƒ¨®¢©ÕA–µ ›Ìj Ê£éŠÆz®™ƒȾ¼œÉb°EHe¸™ìX €Å`„Exç€;TF¨‘ÓqÚƒ•U˜@?¿ÒW)(±”‰åñWŒjF1Û!ä¥оœSdX†i  Õeðƒ €4€þ)‘‚;$€!pÂ0„N脸ؼy+ÿ2-”PE½Íñ-‚»hŠñˆB¹ØPmØah†]ðNl†bˆÅb ™LŠ‚Ú5& ³‡‰@ÜS I„Eã„“ùÝ·zžÑªX⣷E*ËÏ(ۉةŠÂúR8`cˆj0†*Hˆ˜:ð^º”@L’õ=>03L„÷=c[‚P4N8©m’ ÀU{ƒ9hŠ5-83;ybŒÜFL8àR‰vzìš«ð:ðƒ(°F ªy9;@ x9LX´G¾¨ƒP Ი«©üðÕ6 €N?¡8§¦àÿÊàœ¤è*Xt!š‰h,RÑê 3§L’¶x‚X4CxB§xÅÍ 8ðÒ..°,ycðè)ÜÄÊÑK¹ñ‚(HÊÑUKNbé‚`=“H™.pßLpV ÐF &(?HÜÂMÏYÏå˜H䘒Šé‚ÀÐéHKv ñ‚Åíú5eMõÜVΉÈhŠ&`!΂3&ˆ«nä\§XŒñˆâlŒøTâ%¯ŠÜ¿)‚šs3Ì`áàåfnˆ Xs ((5$¿Q½÷¢ÙƬf³ó’‰$3ù‘9‰ € ‹©D¡yÊ“ãÿxÖú) ›’ˆÉ”v^¦À.ùÁøp Q!Ö#½§äÚGŒÌ¾ µê¨6FÜŠöù«”>ûí½ÊèEÑ9XX’|ú‡`êÐR‡€9ð Í*Û Öœnˆ˜9.7Ø\Ö*,¦¦¤7]¶Ò㡠ѱ Ý$…n™»hä‚’Ùá•Éa‡è*üEéB&àü"$›S’îcT-Ñé‚vŽkܠʬº¡¡'àÆÎëvÍ“&¹Zí Jô¡˜ÄX¾næýÉšyô“ÎŒCé…!(”ʽb«¾jÎv­Øº)ðAVÎŽ>ÌÄ Ê½jšâ1ʱ›í±ÐÈ–°t&î6c¨ìœHѦ Yè!1NÛƒ-ÐÈìoñoò‹€;ntpsec-1.1.0+dfsg1/docs/pic/pogo8.gif0000644000175000017500000001721413252364117017060 0ustar rlaagerrlaagerGIF89a]´÷ÿÿÿÿ!!!)))111999BBBZZZccckkksss{{{„„„ŒŒŒ”””œœœ¥¥¥½½½ÆÆÆ½µµÆ½½skk„{{kccƵµÎ½½ZRRRJJ¥””JBBÖ½½Œ{{„sssccµœœ¥ŒŒœ„„kZZέ­Æ¥¥cRR½œœŒssµ””„kk­ŒŒÖ­­Î¥¥çµµµŒŒÖ¥¥Îœœç­­”kkï­­sRR­{{ÆŒŒ¥ss„ZZµ{{{RR΄„ÞŒŒ÷œœÆ{{­kk¥ccÎ{{µkkŒRR½ccÎkkÞssÆccÞkkçccÖZZÆRRïZZÞRRïRRçJJ÷JJïBB÷11÷))ÿ!!ÿÿÿ¥œÆsk÷­¥÷¥œïkRÿ­”µœ”­œ”¥”ŒŒ{s½­¥œŒ„Æœ{”Œ„÷­9­œ{ÞÖœÖΔ½µ{ïÞRµµ­ÆÆ½{{ssskZZR­­œ””„„„sÆÆ­­­”œœ„ccRÆÆœssZ””skkRŒŒk­­„œœs­­{ÆÆŒ{{RssJœœc„„R­­k””ZÞÞ„½½kÎÎsÆÆk½½cÞÞkÖÖcÆÆZççZÖÖRïïZïïRççJïïJïï9÷÷9÷÷1ÿÿ)ÿÿ!ÿÿÿÿµÆ­RÞJœ­œ”¥”„”„µÎµc{c¥Î¥{œ{sœs”Δ„½„c”cŒÖŒ{½{sÞscÖccÞcZçZZïZ1ï1!÷!ÿµ½½½ÆÆœ­­¥½½Œ¥¥­ÎΔµµk„„¥ÎÎs”””ÎÎ{µµ„ÎÎŒÞÞc¥¥sÖÖ{ççsççZÆÆcÞÞZÎÎZççRççBïïB÷÷9÷÷1ïï)÷÷÷÷ÿÿÿÿ{{„RRZ””¥„„”ss„­­Æ119{{”{{œœœÎRRsccŒkkœŒŒÖssµkk­kkçZZÎZZçJJÖJJïBBï11Þ11÷!!ï!!÷÷ÿÿJBï„sÞ”{ÎŒ„œ{sŒœ{ÎZRckcs”cÆ­{ÆÎ„­¥”œÞ”¥ÀÀÀ!ùÿ,]´@ÿÿ H° Áƒ*\Ȱ¡Ã‡ÿIp£Ï£D—:QÚó [:tá²à×—.(D|(aeC òlj4 À N ¸`Iƒ‹ °öî¼t÷|àR’ ˜ Zº4(5!"L~œ0ƒJ $u‚¤Ó€¥NŒ Ø ©S'K‹6uºG#N"øy7xë¨õë’" ¦LˆFJˆl`P²KÉÃ.*9l™³åˇC!ÃOC 4?°q8AƒÅdT@ƒËf º¼ *c˜n6‘ µæÐ—#ïüY4.l¸Ø††l-þ÷à'.µeÐÿ ¡|3róçEkN¾y}ûö]d¼¨mt{l?(¡Áu 4v\e)hÀZeÑg߀ïÅwX}éÅ·@¾íçÀÁm¡ ï…È`h&Ø€À-Zx $lAB`—¡#kl`ƒ{ì¹'â} `y&0*6TŠ)§ Ò D '|Ðh:¦Ç”g `Á¤X$Dh0€Y\1ÂT°B„ šgߎœµÇ…·|9UÄø“ê¹¹£¶¡÷fø¥à&tiçC»0Í£Ñ4£Á *Ç^h) ÈΉ0€º0€,š=€«®:@6ÿçgˆ…¦×ž °“eŠ÷  (ॅ@@Ed‘Xd!Űö¹c 0€ ‚+Äè@ pÎ*g\ö ËÐ 4¡,+ì¥ê:uÀš¸É™aI6œPÁO>2 @› 10À ›K¯ä:Á4@/I  À«ê´'œ£qÌ9w©àR).œ£'T`BÇ®ªª“?q}t²%yÐûjKH@d¦s•57‚”³.اÔê•‘ç лª±ÐêÀQ µÐGà0€^Ý@ ÉjYaØ`Ã$ ¬ã¥TváBgØÿÆ{ª D†6XQELh#½4ÁEKTtµ¬=ÀèD†Á»€½ȱj®Z¿ý“áoçé?Ì>øç§: ÓÁ,ÍïËtÞR ”* )PÑaÆÃö™Ð€O:iP@, ‚ `x«ôâ\{žªÎN;Ç2:°Ÿ œÝÀÞÀ¡&0€54ðAèyzN¸§±e¨&¬B ® €¨ôçt5 €÷…¨ËT ñ¸p‚Ç\ƒEúƒò >4 nÄ °‡?´ìpÇ: P{ pXØc@8•7èa à ÿL\bxÑD5Xu·Pâ:xD'4±¥ðhÇ„ÂÐ ¼ *á¡D •(A R¨B³”ÅF62A¸‡$w¬<ÜC- ŒC"áˆDøq ð@ND°Pf3h€v¦’  =&À.H‹1,9ëD¸©ižñ Ž ù.(@y.É®p‚.Ɖ (€ >p ÀS:C@ž»ÙÊO*ëÿ¶b€ À0ñéÀÖ·ˆ  'ØWh¸¢O&àJû¥´¢'Ídò<[¤B€o"¸@+Í#OŠˆiíÙTŽrô7ÌÓ vXC"æpÿ¥ÜË$"8À`9È€ àrÎÁÜdÅÜ3­H…+hñ  à  ¡® &¨ƒ4Nl:(tÏ…€EOÆÂØ?( dâ‹j, Í PÒ…ÌÀx‚7°„®LZv&g`òð¤à…4„qÑáí= xfLI®]ưgM%ò¯dLã·xX´‚I5À@!Qµ ðÏ͈¸§&·§ <Î  @‡€º.È6µIhüš¡ ¦ÕRÈ1A6P* I@'!X#ÍÆ*CM-­@8ƒÍÈ@=ŠjRcYûv˜3§% °'^&æ1p |ݼÐÿ šA&Ê€LPN5˜QË L"´ñ!ðuXu¢·¸¥ “@à‚U©‹ÐŒ7ñզ˖d«ÚK¿&Ûe숃xFßH@8À à€q³ ¸À/aš7‹×9ƒ}Cm”¯U8K.A&ðXaYU @S©H@;Ì+Ú-ˆ¸Ÿè¾ÛµÌHM êňf¬ ÎAœmà ?Óuödg€Uö”Ü` éjî¨"`Ûäè>ÍœWMªq€³ÂghñŠm7eÚ­Ê|çkUMz‹#¾ÓÅÒ& €àz ¸‘cšÆ 2nÿ€yb ù håÁgx>ðølFÓ‚Xx« iŒ â h1¾á n°ƒøã˜ä F•kAÃŒ¸åq„̱Žt¬Þ0€<öѳ~À…ÞÏÊ`:— Ѐ¥91ˆÐAîhG:lû~F2ªSí ÌA~™ZˆJ€L.s „#œH¥2èñ…Ê|±±S÷vF€i[â®FD†À‡N¢c…pb»·`Âõ0ÊQ¶¡…†ÆÃ8Wƒ-áP¸³šu$lac@æKäê˜pK%Œ™‡N¨ûl_„Ls,À\ÿ£©¼D D€eæL`‚á˜Á ZžÍhAPê$t\0Ø”  ¤k?8ƒNpqgì n n…S’Aªïtåz¤c“§†ƒß-€ú¶ƒŒ®2&* B7;Z46 CÓFÀ¤æ0ZÔzà™ ”Ï!Àƒ.TK{9²AÄðBgÁéL FÏ»‰½MÊḠ½O)ì\xÁÓ]b Ÿ„ôÕ.m`dÄ"”ߪÉ\VÌfOø‘›lP‡NeØ9ä4³ Úç8àÈ*ÛýS_žø Àøû 2pL`œmv† ”)‰ÿ ôÎû#Ç4>•‘gƒl ÞˆQµ Æ´±ú[à0à‚4@ƒÙà¿Ö}À5Oƒ1J¡±6‘f'n  Õðy]À+”7âJ¢¤•¢{•ÚP9Ķ,ÿ€a°o  KC‚‡¸{íÑ\¸ˆaRS°, |¹5"ÑT%±0lÈPºB/#À]ÒrÝw0bÇÀ©Æ>t6Õð9‰Ó¸S'`½´# uÒ‹P Îð(Â@ÃøTÈñçä}Èá.Õ!  *WSPm2 Ðp   >Õ]¢8Ì÷Iæ‘ Œèñ@h%…|0 Í8°’Àåc#hý$KQ)ÕäpÐ….á‹óÒ1!à4t‹ØÈ/p%## +ð|y“{ú'IiRË•!dÂp`N.x' ÛÃ9Rÿ5.³¶hXERÛ&ôBPð. 'ÜØÄg31‚ ¡Cƒ²Pæ!²ôk¡cuËÓXÃr!ð`lT£¸; Ó]oB`ÕÂaŸƒ{¡uĈ–Ó-÷ó?hrÅ#úAN[0KBp\“C/ P¼P e\>A]>}§((ôèu]ðyr\ù°3pV3  ‘€3¸”ìa0@~Þ•‰%ÈM\W)ÀBsgè³*1±5”‡@qŽ€^pHØ•{Ç!Ǹ˜òˆˆ™H|Lf3o–›–ƒñæœ@ qðÊc19é[Ûµß.Èÿ!‚ÄX"°;:A>098Òù­'šÀ 8 >¶0•Ã`rù>Y¸$žœ‚\õ$' yr @¬’3CW0wgè¹¢+”úÂ` ·xYÛRü’WÕpoc,rÆ™ƒSzbT€>P °zEs™#¡CW‚#Õuˆ”yX¸a(6×’$8X¶bZ£`#0 gE 4¼Hµp[À[|5%‡ðØ1Ô1µÓbå#>D8a³¡W ±e-0@4 €¡ÿ°ar3p/¦K{ÿ±")°Øa@âÃ*³ó6+vgx&§éé*E}ʦÁ€ Ê€ s5Iÿ ¨@&¨«å .`!–¸ ] &8‡“+   ‡ƒ©†Ceqšg¢*¡Šå¨Ìð;¿S¼8 àp ºR`ÖS]ØXH5QÀP{š_£dV;糩ª²àÏø  ÏP k—1p:”f2@6$³е' nºY»sgNÉ1¶S;‹3*†3~x|Ð6õ7)pÐøð KCjÆRp²(kõ‡Q в-« в ¦I³ ÿ0r³mƒy Ðö'Ù0·ˆÄ¦JÛCãH´÷„t6 HJ«"o¿ÊŠ@ÖúÝ ¡Ru  Q KÎÆ qDŽ@ø° ×° Áˆ7™¬8¶¯ºjpº²2œ ‹ ë é€må ãâW)àvË£`òé\° × ˜À` çÀï°ã÷ ¿†^z´…fp’Ðg 5s÷`,  ðð»6p'$ ´ûx Ð ¡3Bœ` }°— U7oæîèð P]„•ù:¶oÐÿÒmÁ ’€p má™p3€Ô›kðcаzd+Ebm# —›ó·”Ð Š0@7Ñ ‡0@uàŠ@òüÀ=@ò€Ú@¨…R„ }Ún÷v@Mô=rga{ôŠÀy`ë ÁâðL)PMN«¯1NDpVÐ>à<À8€`!àJr€ xA •€ Ž–6ÇËnàëÖ› €ö %!^Wr0¯ „|@mtÆldS°pi ˜`ÅÆâ™@¾2R‘€¹Ó– ‰`Z'×0°íÁŠ2p:%ÿ0r €ÀÀ#0É1`¥!©qP#ÐA4?Mö9ø€yP ÷À%1B(C(˜éx[º‘ð<üG‹ÈI:nò ``„D@’²@ ‚Zõaœ¶1sÚ2¡´ ~÷€¿Lpð¨×&`P!\  P9ÂIÊu˜õÎxvJãggн.á0‹æ^Qq*k+îÎ 0‹—aœñax_eçI™1'ü° )bƒf…&ü‘W¦7 à­ïaxÒwË9b0'ðHQvÈ+] _óä‘]ÿp&@F p•BqpÁŠÉ_ÜUŸÜÐd6pÀ Á ÑÕÂ0ã¹)v©D×ÕUMpŒE zç'ý­'6h¨›Õ‚™ â´Y æúwïa3P‡Ù¬{býK6˜ Bø—1'n° 00ùG„ƒ%O Õs R BU'€`/%37ø &=â’&.`-}"ÝMÉIÍÓÙ¡Þ‘2$!Óo]ÍíÁ.X•A•‘çÁQÈá)¯‘#ü ]Q3Xè!l†Ú¸-Oé ¨ËÀ@ñ˜Vxg—ITX¦™DÃËIPÿ½>@­&»ná‘tZZ'LçAÏX¦·­•yO“È>@¦5}ß"”LhŠ˜AÙ h°'-".p°s]ÐB]ØMY¢"”…vRágîô #:ž D¶"ÓQØ:a {õÖ¾uÚ¢Ô ðÝ:yÐp—ZËŠmbfø‡v‡p²} ÒÉA'°5»…)+ —œ- -SŽW €†P@D=B/ Ü ÕÀtš¸“@xÝ%@~ aVh³U£°„HY@ D/rpÐŽ 5€´BÐd $ÿpA^R¨ ¢ fWRð ×%ž¶RìâoÐc€æ °€Ÿ@ ²à 5=¶6ãA"Àg` À%pÉítÖtJ» ¬  ³ ¿  ·³™œ.ƒP6@ºlšX³N0\¹U6«Ý#óuŠ˜#4 0›Ml'µ§ 0`/&z‰¾45 ¼½¸– Àëÿ€pàøØ¸H N V€  evšD:-¨Yv‹nî cÆX òsßaR>.\_ò^S&€ ˆP@1ÀySø[KIÚ ˆ=PDÿà)º’αm)Ôôá¾ñ…Ö¤e`ˆxÓçï„2ßdùAŽW÷ à*£”™."ÂÉâ}U p.?d?@Éð ј2ˆÈ€‘p2ÙR3Ð@Í-Ñ{qΰTc¯ò­QåyØáÒZéó%Õ ° ¢ *+ÜßÚú}Xì±1S)]PÑj€ Æ0î|„2p0Ü ÒölUJP o¿æ ² %{ôU¯. @¢¡õc  ±IÖ:¾“ )Ü$Ú*riÕ3ßwu+F‡°r,Bžxpg`0É1ÙØáCM—1ÿ´bÄŸmšÊÀŽ!Ö'  WÔoÔCCúQÚï˜Ð(²ršž%pU@1dˆ'ÀBƒ`‚' 0áAH$øbÀ ÿ4näØÑãG Y@ –,UhãràD—0] Ø#… 4v!’ÌË@aîìâ‚ ‹"|dÚôŸ„… lÉ’eZÆ|(±Ë…¸ !Ž bpê2èÛ‰\ÎØ˜8Ó@F§ym5°ðʪ(§аµ+LÄ]  €Á l0`x¨[¡.¹$XÓB€xó~” µHUÔWH ðÁkf˜\64Ðx@ÿ2# 88|ø²o.p´,zo¼·d„ šãˆ%ª ¶ZDª€—_¶Q€±ÈÚ}5ÔÞ@·mïí/S ÈĆülU( A‚ÿ&`\Îñ¾ê’8 ±ïp¡-Ć’ˆ† *Ø`¤´{-=™p .ÆcM€[ – €`àȘ¨Š’i¤ à‚  ª .> I ªq¸ôÐûÍ=ʸè†ÙFjŒ¨FÖØƒ#ð!†ü‘$ÞvÔδtDÐØ:’«ƒ^ Š  KÍh#iƒ$h2<@判­|»° \0ÿáÀ)j”MíÅ‘&.¸”6Û+ O~ÔSG:QÀt|ÔÐÂ0ÒíÎcÓF—€~ô¼Úh#€Ó:g€‘N:¹„’N H†ˆ"í-QôÜÌÌÐ5’µìÄ´ÎðØ(©6XNFÀ6PRLØ&’!.šÅ R#›I€ O'£m€]Ÿ2€¡¾ –’ϰ¯€á^jUA·,[X]ŠÚ…– ó5[Öê,Ó , ¶Ld% ¯Ò0áÐ"ëºÀ¼WŽ8e÷&¼N¤Œí†¾ì÷© úè„“J¨tRŧoƒ“ŒY4è å—1<Ì…+°ÿ¡ ‘€ƒ Èt²¶M5’@"YDc’XJj‘€š}Ï0hb‚RЪ͇6T †Ê;ˆL¨Ô÷ÞïÂ~Šš¯ùq) yO2\-h€ !Ö{Ç.dHŒ† pç8ás^opLùÜè/ifH¦"H ¥: ¹8 2¨áè0.¨‰C8Fï»D$PÆÛüëôÇ ²àTÆŸº…dä$˜€5kÐàl~×BVaHà„–\È:Ælñ\þ»°:b@qªZ¢ˆê눟[6ÚÞøçaK0€•ËXÕ…\€\|l#}GîËS¾T×'m WÀªðÿ @ÎÛP €_?bÈÝ$E6Ñà`@ñrõ¥ Âo‚xâaxò低HàB8X€g„bp›NÑF`Ȫ$÷d=„ èTà#ä½ïVŒ© áç$x(@:¨‚´Õ¦‘°6D 4ÚÉ2H‚SqÅpXˆÈ@ €6ȃ¾º”§\EðVîûN5 Æ`L®r¡‹‘TÏÈAÎS4B{ic!М‘n†’Dè ÕØ@N`È O<¤$á~HF2ޱ6DëHªÅ #ȰÅ@ô~…æ) 0˜-ê¨QÿÞæjïzAÚì#È àÎ4ã½(ØËXšsy@jÊ-pu€e,#Ïø€ Ø‚#Èž` T`k˜9A_Y$à6HÀ4ख़³‚vr 2†§øi`†<¥1 elbCÎ( °Dd0 ‰0ÄÕ8Àr du, O%wú£Ù zí$À.¤ ¤öb¢ìH0mÀÉJÙÍt³çù㛃c®rhN'ñò‡°"€b<ã¸@©öÅ8˜'s:éB?Ú5ªÕ¦À•ñŽ'Á®ŠQ¢úZH0ŽØâÖlâ~"J i"pAеu…èžµJ³ç¹îC‘\°€Ø+‚ +b™âN¤q‘$Ü(²Ò—0ÀA9ÃaOêWk!“{ðbƘ÷£Kº¶)öansŸÒ\ 0¶¹€nu­kŸì]W»¹E@;ntpsec-1.1.0+dfsg1/docs/pic/pogo6.gif0000644000175000017500000001341613252364117017056 0ustar rlaagerrlaagerGIF89an´÷ÿÿÿÿ!!!)))111BBBRRRZZZccckkksss{{{ŒŒŒ”””¥¥¥µµµ½½½ÎÎÎÖÖÖçççïïï„ss1))B))k991„ckÞ!!­½Öï÷1Jk{”¥ÆÎÖçïÿÖç„”k­½s1)ïJ9„Z1)R!”c!s{Î)Jÿ91c„!Ö9ïBRŒ)!Jœ1ÿR{){1ÿc9”B!!B!µZ÷{œRÖsŒRÆ{Z9J1kJΜsZ”sµŒçµRB„kÖ­1)ZJÿÖB9¥ŒÖµŒ{ZRÞÆïÖÿçÆµïÞµ­11)))!cc1kk1JJ!ÞÞcss1RR!ÆÆJ””1„„)ÆÆ9ïïBZZÆÆ1ÎÎ1RR99BB„„µµ½½ïï!!))1199JJRRZZcckkss„„ŒŒ””œœ¥¥­­µµ½½ÆÆÎÎÞÞ÷÷ÿÿksk9B91R1!s!1BJRZcks{Œœ­µ½ÆÖÞï÷!J9„kk{{)11Rcck””9ZZ)BB1RR9kk!JJ)cc)kk!ZZBµµ){{1µµ!””ss))ZZ!ÞÞ{{BBJJ­­kkÖÖ„„””¥¥çç!!1199BBJJRRZZcckkss„„œœ¥¥­­½½ÎÎÞÞçç÷÷ÿÿÀÀÀ!ùÿ,n´@ÿÿ H° Áƒ*\Ȱ¡Ã‡#2là/ݾ~ûÅó×@¢Ç #Bðç¿~ëdÓׯeFxþV…œIS¢„«ü1Ë(î¤>{»¥S§ÎÞ¹iãü¨É4á­jøÃ‡¤»~RP"鯀V²^Áö‹¤.\þàµ\»?z\X@× L˜ ÀÂ?ü)hB`V"Ñ[ë-€7–ýòùkb…d¾2kÎ|ó@®$ `~ ÀA ˜&\8Àª *8x!Â?Ø›spºwo¹ôn‚„ ô–Éb„<ÿ­ÛÂaߨIÏL!° ½lÿž°`‚å$47‚CÞþPÐydvÐ 4O€Ð ÿ¥þpXÿlAÇÑô_oÔvÀ2)à Ø•f6ÁgÄ 0_uOÝ—nôeößRë•èÛ*˜¥¸™ \šmA €tŸºU`WvD@~}! €$0› 6•Ü}Tú3@y2r7ä8„GºýÓ‘>‚™›}$±ØEU¶I’ô¡Ùv!ÛnZ¶Àg$ñ Ú`M¹Ø'^ùé&q2ædÀqlÆ”™yyúÀ4 ššLiv—Ñ•!…"È üqH%þø1p›Þ…bfúÿÃÀzE è¦ô xæÏ(©BÊ)©pÒ…š|I*SáÏ+¯`'ôuçÏ"¤ˆ’ʶÜBJ'„B,·…ø“LMäTeh&¡%ÛŠ‚ª?°@‚ *Ün»ì)ZÀðÂãž"I 9¼ï)E°ÐE*¨¸± T˜’J%œœòI*\¢!­9ÚxßRšQP`f ×f{$’Ƚ¦t²Wüs@ÌKÁ­æL¥H ëg¬VóG  €¡:'Í•ÐCs|+Ò*íMs\á¥5Iq(0ó}¸WõØd—]³ 0@Gf· Q…Ì@†Ñ3#ø@N7þ¨çöÞ±ÿêÏè¼#Ž5$À`=ôœÎZýPÃß QdOKüˆS2 Àô@ Ð!/Ãà Wéh“O?ð°‚6÷t³Ž<ýàÍ• ìEÁ ,ÐÀÌQBþÉTO?å”ðçœÔ’6”¨€IÐúÃK-Øá5b–º½À8)å;ç$±žIêœãÏ;î˜Úõ›YŠ]ô™œæ÷Q$€z¤ 02ö@}_˜©|,€³ìª7 È t¼Ç·/1"°UŒ¬c€£¹©Sš‘€b”%@l!q‘º45ˆ aÇüÑ®øƒg⊀ü‡¨¾¨«7ç‘¡?À¶¦Üð.h‚@‰ÿà— ði58ÀþAÐ @]DÌÍ­î³Ìð†9ƒO•À>ìiM«èÝ@E­ù4QÄÓÐ&e’Zº¨æ­ ÈP»`|†jš¡Tn’ãFÖHà½s@¬’Ý\m0¶‘?0—I-;X¤‰û”VÅÜTSÿ!βCÉ8€ @t>#€=f†"†*@!›"ÄK [ÁB$8q‡/©ÈD«$#ûáHÈZ¹ÉûÀºD*Fá&$•@EÊ -SÈá 5 &ˆ… (¤À …%¢U%7ëthâ°¬T˜‚X#t¢ÿ7Â,òcdÊ™$Ɖ+ØàT€(üADÜ"Å&$ŸU0€{ èáØ”§3&-ªá  `ˆñ{?¼ä%ÃF?Â4S¥ªMK%òÏV Ÿ€GãB™$ ð ÌfY³ìIZDõ©R—êdˆ-iÆ ™ê6Š €01åØáPulÒð¾|CˆFK`²Ä¯rlJþÈ@ ¶±–|ˆ£ï ŒÀ{Ä'©nýÈtÖÑ}°¤qÝ3ÌÁ¸~0vç0àW#pÃà @äIÒD’ăìAÒÁ–Ø¡CðÇEÖ ­íŽP™M&ý(2ÆÿÅÃ003H2€Õ„Õ·øE1l‘‹]°ªkiGd-Ò¸„’HWt×€ ³è)ßžr+´£%æ0Æ:öÁÚ{øzº †/hAŒ`ÐB³H‹Kîó%ÕšñKŠ$ À¢ðÜfÉ&­Ã¼èˆGcû{ôù½aèXĽ¤èq¼‹v÷¦S® ¯÷ØC xPtÀÃ\éäfàŠÍ!@<$Cf>cÒ¯†µJ(cn\Ø&gæV,ÝÐò·‘diQ”šÚ´€ÍŒòM}ÁŠÄĸ\gVfKŽzFYC‘Q€ƒš¹Z¤ªX!8¹/WQN@Ó¦$ºmÿ:Âr…Tœ™«eø>hÞÌH¹Ã=0ëHBڜ۹¨ðH€Vc•ÀS’,à à€ä93ÿ€Fg!…<AyŽ ÿ!!D95FO®R À!¤ô¹+#f’täP"µ?FÉ¢FáT ¢@:äãè’DUñSÔ,³fŠñià_Ї"ÈŒmJÞ~FÁàf8zÐÿ̦ý‰8Åès]HòÁXa9³¹U+uÓá6åÛ†ô€C"Éì$Ù‹ùµ@‰V±aÑ›ƒÃ®R'1$€€Û4±c›J30§Èo³> zö§™0šÐÿ†q~)0ô°6ÛižN$#Þô À ;¤C ¿èféÖB˜ZéU$ó¨s•+:;îAÑl&ï|hðXÎ XìBëòÀÓ[5€8éO6wÖÐ#€u‹IëÙA6H^g[ÿN9«x_^zÉLÆèo_|H¨Êùðý>`Àõ.»I·ƒäð‘º4É”cd(&ßT¶gd†7­àI{@¤iO/(! š@E%$ Š&m\ÔºÏáÈÜM“à„Ķ…‰¼àþàD*J1‰V­"ElR ÑFjy(€ g$]›jd~+$ QÄ ôäDÿ$¢°MØÓT„¶e דd  cÉ@³‰m•"1‚R†Da\Á" 0§©)ìòJþÐÜr ¤€ §ð A0T ¦à'`)àÁ—  P •à õtOÛâ€ø4‚¥p/‘€ap˜1RyAtEW%hf-¯•A”àh “@ ™À   1¡à– ©à ’pþàNöô Ài@,¦-¸-ž ŽàÑF6µ†gümØq1÷‚/ÁÒ Ÿ` ¡0/E?ZgDŠT›rSp×1oIçU-ucF§4ú]üF3õ†ƒ¨37ÿE^%ƒ{#†˜'9XÿT–åq4¿ÓwV‰…†‰6¯¦R€BŠAn:“îqè—ÈŠ1‹}2`WRrwY74€{c$wFeC™Ç°a¶X3:b €LˆôŒÖxØ˜ÚøS‚ Ð +ÛH+_R[-!nŽL‘!ã` P 'Q£ˆŽ!qcÒÀ`ïû`ôàWT# á@jµ×° ä 00Ü`ŽŠè¡.§£ ,Àò ßP-!SÅ‘j°cð  0ú £‘ !Œ60¤ÕÕ ê ëàP -Qçÿˆ’¡.ìpçõ@0; ÑXûÐ’(l²l¡ r:‘ûà  âŒ8i´ñ~c”âí;Œsê çTó ‚KY6ÒÖ 8SQ @t À Ñ’/PÚ€íàóæ³ópÇ–Ö!y÷=p¤ bèЗ¢Bió ³ Á ²Ð Ñ‚ì •êà/ø°ä0;Äa.Wq@dߣ)ð  Põ K@bô€MÀKij  ·P · µ ñEŽ-aþpÃ#\e%Àa‡8ã_\± £V6ð-àïÿbÝp:Çç€}:há Å0 ³`œòÅ8û0ê‰ `a@Ø#´W5rûÐ/ ï°¡ç°3-² ÓS ²Àê Ó àq2By  'Y|ód ÆèÐç k1æ£ ‰ 6/Æ¡^t3™Ã=Ö·7 ×ʃ©Éç F°tW-¾R]$ ý‘‡ÑVßcg{0ž /À)ïÐGÖÔF%Udu¿Ã7êS%r@i’$qìPBkôyCú%5$KÇT˨>ÃñqmBg>ÓNÊT¤qÉ žp¸6J@n¢bKö&ÿa)ð–Í¡$P;¢‹ t)RST´ÿp)e¤ñ…éÑ6r !7…pËP%GÇ7kó`DN‚Õ‡5fãBƒQ`a‡p‹z2aÖUÒÑvRið Ô¶ «ð%˜B6b£Gèæ"@a”uš±"ûa+—;{â#š««]¡‡’=9£c:?•–&¤p `©ÍQ`–q†‘mÀAÕŠ*‡pz†&Íê6ß¹CÇs$€t‘fn¢rð_sÁz—_ `·7QbGM'n’g“]·œ¹± *“¦éÒÑö›ÿמt(šÁ»Cw5DkC¼±6´‰GÊQc%4B"g&¼x&}DCš±®÷¡TKµÈËëÆê6F_[ Ò.Otà# „’7c–¥D~¨#9°k×KáTÐs¦nÂá+ë¶ `r±šáT”6˜š"  vÍz\¡¤Pi… ±|Oñ÷"€°A«J%Pl$€´2EÐÑ!ýÁµÜ”GÇ‘G ¡?C‚©}º§¹!»V3ІL—Uð"¢’ÄRÂRS–ÚÇ©·®ôÛu)CC*#ó@äº)E˜J«ÊµO¡}®ÿ0¦œVµþš_@%C’µL .…˜®È  °«pºÝùxUFç«gJ×p‡Ñ¹à7Ù¼’“ow’Âpý›­7rOv|¹Á&÷+-×c+~rÄøŒ˜'m'£›á"ä» ¬9úq+;I„‹­r‡;¸ÌX+´Ãoòá®4CÒ¯}±‹‰{’MÁ¸¯Ö¤”Q!®d)P)*7t'mò*)p¸<¶1!ŒF2Ñ+C€tœÚ4šSÏäd)&y95˜1v¾Ñ!0°QSÐŒ´2c*%y$Ã'Nr]|ÁÆyr·Ü³‹ô­´”Š›#ºÿQ"f²*S¼8ÃèbYndŠò)|5l£S\‡€•ø#4!ŒÒ‡Èi[³¯Ç¶` MY)&³3"Ý{IrëSœ'<œ_%‚eÐ+/ÔªŒ“·1E!WˆÜ*½Êoçøøzgkã+¾aRRt«}Å ÁbR󱺑!™$!åA%–!Ú øU»°  •!‘Ì‘§ð_Ç'–¦|RÁ:ÃÃ4Ú²5qËUB`›`†³-œp Å€ Àm'¹+€¨Lšš3À’ k`0 þ@ £° À¤1­b~ø¦v×4ÔŠ‘‚ ø2 A ¦À#æi ›ÿ‰É[Dvq%½brê|QîAüá°ÁqyÕò3 Ú Ïâ™/ùÔ –¨)2JWÈ-¨@ ™@Ó\ñ ŒÐ` Š05qHÒ)ÄD2Ê„ÜÒ Y4@S/°KÅÂN*‹|Ѓµ- PSLwu/©0,<iœ'®„»”@ •`*p“p ›0 —ð ø‚ ¥ ¨ÏS|]¬öp z•½-Ôä”P ©°iÀ- us aAê«É˜æ©]…MíR? §€60”.€ÂçB Ñæ— § šP ‘-¯€Œ š ~+À»ÿÇ-EÈN«@Ô _3sšñ·€¢pž€¯P c[ñ nÀWpbPO©À[ u0sÀ~`P` “ÚÜ‚ »ØüüU%e‡$ð©0Õ$áMÐì DீÎ- þ°„O],?@o0|WðK ö ¡0 œP µŠŸ×&7ëÏ a Ú¢ÚWÀ0ÀþP q áä ùR žÐ žÀp\6cL% ÏüáðÖ&‘àÙ” W ¦ÀÚk@ Ä¢Nïw—¾£Í÷‘åôQ™,Ì €lž/¤ áÑBÛß3¿QãíRp­‘2wöY†ëòÁ‰3åãñdý¹¼ØÁ+lÇÜç’Ê\1wíSÊñ¼mCÀœÞ°s‰v±=UÞRB\êTÒ¡¤¾ ³-G®Þ*¬FÞeCê·Þ&¢™C¯½^%†¬Tu¾C]Ì[(݈AæVÉ´+ à58eëœN_B¾ØØêªˆŽRæ>rI~nS„òu÷í¬(ªšWÔÎÏKÕo[·n¤L%ÀÎ>Eí\aæ¾® á²…O‘¶öŽïsHªä©´rHiS=¿Nª-¦ë!q¼¾!™•况O+Õ'a«“ ò"_;ntpsec-1.1.0+dfsg1/docs/pic/looking.jpg0000644000175000017500000027162313252364117017507 0ustar rlaagerrlaagerÿØÿàJFIFHHÿÛC     ÿÛC   ÿˆÿÄÿÄÿÚ õ@0@°‰dÀ¸À¸Z&58ˆÄçA9Äæv‰eÜÒ†L¥“bZ<‘C™‘x J1(ˆÜÈœNq;Š'C‘±¹Ðà'; ä¼é hòldÁ‚“M¨­:5&ZVé+LDK¬i ¤àcM¤ÞJÌÂNaÖŒ‹ªY#!ÄÀ®…'¢ŠœŽ‘aô± ‰-(ƒrÁ+‚`G‰ )*òPvI` ¯ ,È“2¢M¸¯’ÉB`ⱄ– Ð…#ÖÇ™ÃA-"¤¶ˆ j•ÁdÊó–j¨*¶ì¥å $å@<º*Rà< @K,‘ÉvÀVBâ*?ý,³Ê鉼…¤4›™2`ШÓn+\“äÒCðþ*+‚P@ɘøVÅ„V„ˆàsL`°9*aŒ——PŠª¾ÖSò|èj#)ÒvYä$ó‘а‚§/b¼ˆ‹<ŒŽ\’#‘8#ØØÈŠ•6⵺Yˆ¹má ÈBIiØZ\<–•XÚž¥Ä,©ËȸMê©`Ãb‰@FRÑ’ò¾*Ò@zh¯È8ÚFG¡¨ A=ˆ@JäV>‹HxâXGRÉ2s*´Úª°ž|LÔ‘Ùa)E1ä‹ ˬŒ£ÁÌŠ“â° ÍvUo¢4TÇ£†ñðŽˆ yF—+!1 /"6+# è",ƒ†ácË •o-eW&ÐVS v03“ÑÄ”áÂ,<•1éâ®%Ep;2ˆËv«ÆU<ª‚8Zn{¬ËTòàÒ+,!‚ñ<ÎED­-zIr2z˜ÜˆÔ² 6°ÚZ=+9³i3-'e|u&G3R ;c–˜Ä˜èùJ¾ÙZ¥Öì¡òžÄAÚm_ùw‘bpIÈ@Ì3–ÙçbÉ"E¤RÄØ–!HA`ãá"¤.# ÞZ”—7KOÛO¡¿ç¦š#å^;sÒ¥FS¥Šp#Ö0— J¥ä¬óMÊ¡Øz*å6Ck,P²í„‘bNBÏM8$¢3Óž¢Â"Nœt$edz²Š@‚Ž$˜™b±.ƒ`šK›†¾×“Ó:{.C¸Ð)"#H´ŒÃ)aqPþ!'ÃôF+.J»VÕ>­ó›^cy£<Ú;òŽš ˆ9g’Â21Ž"BH‰ZBaY0HI]rpXFÅbZF@ šs—–µè§ª§·yšÈÑd€ …±Äôp¤¦ 9LOÂÚ7Ï.›Â¼í§¯•çßg/;¥Ä™b8zÌ„‘ã¡'ˆ¹ &‚2z4• ľÄET'= OŠÀ´Œ€TNSTòsRÝ=³o¡¸eº¡”å Âv"¤ÐÔkGÚ§ ç5>*â/müÖ³/.ÎÙÛ{QÍwöÙ\þ–XU’e8èhF‰á°#¦EGs˜ü5‰‡3AœP5“qqÖ¤,Ém *')«xð‡Ûä¢o[¥ý)æ½VU­"6 URxÕRµõ1ÌZÒób[åõɹˆ;¢Þúœ;–d ÖY2MØ wY—dÄFd&®áÂ_’ëyKÕ<úê†:“»±ísËW¹¶±=$†R tÙfðÅ­ŠçÎ&š„àµgèbϤ ³¶1(¦~ùܲ묾‡ Ñ?C¬¸Z©"¢J]âkSù—ïxÒÒïÛIí¢U(l Ô²û8Ñ®’ªh[zÚ:ÒPÕ‘׎ÚyzX_KœËxØM¢YMAË”Zÿ™Ç\ož:£YœæÂvÎùšmzñÏhCaWk…ÕÈÉ~F)âUÑå¶å£Múc¢1Ÿ_,ôo¾œòÖ×óu»þ~îYË6Ñ bYCÖ&¾úÁ>»oâú ;ÕÐïsž¼í¬7KcIs͵³”s, 2ÛKÅ­gN[f±ry¤¶±ÎyQÃ>‰§Òe6ê€ !™“SåH‡OÅëV4¾Ön4Œ@ÄC¤ºsØÞ¦Zuø_éyš4}ͺ×jgKi3¦umú¬ËÔר¾»â+wyu’ptÚ>mÞkn4®ùß+ˆÒŠûœ_¼yçèê—^WºkÒÚÞeD–º¤q˜æ—OŽq/±GÜ3d™“FO5ªÜ:/ÔÙ ©±XçZëoŽWÕˆ¶m<êÚì]ŽW\ÆÍ°¶6Œ£~+é[µOб`–vx·Ë7“\ðƒ [sè¨éí/¶q,=n}k)ikëçæ—õž„ñ´z4»>sJÃÝÊ•÷óƒé“V—’Ó=7ô<ùKÒŠæô¡öÓk^wÕÈ÷ŽqÞ>æªÅ…^fm혬¢˜²g¼““[Òé±:i´HLZgJ×O’W¾FѼO4BcÎÜŽ•oÍz`Yz 2•–Í;xž~’ûrqéÇžZwÓ>s=pœlÚÅ= TèAK¹á«­l†í¢¹6ºKÁ1>üÞê^¨¥ÖZM¦<5´y3ëŽÚkÌÉë㜴GÉÖÕÕ,ñK.œµÏ_FÕè˜ãŽiÓ3ŠÍº"Îè¦ÉÉ€ ŠsO”wߟ]«×:âºc¦tÎ6­wɽg[1]žêÇŸÙŽgы˖ueÞåg¤Š+½’C”:b\*έö‡8žBH¶—¶“‹â§jžéG>\ìj7ÍáV¶zv´<žn=ÒÝ~ýçóZ»ýG›Dt_½WF+¤J•t¾qí-ãd€”À«Z6ß5!êâßJô˜åYΓšÛ|\3”š]6h]¦7ko§c>]‹„ð\pjÔíOc|×¹¢®¨äqYÎsr¬rE•Ƈt7¨e©Nw‹Ž„†•n´4ÙÐs©žœeÍdÕ¿h‡ûd†e•uÈÖtn;ÖéæWV—v—ï5S1-¼Ïz¬ÇZIy¯6ø¿V¥ú_@üÎóŠóé±ãëòØý{ŠóE}ÕÕrtWd€eÃ35Í#Ê¼ÔÆˆ^·Òg¤G9"ŠG*Ñʺ 5 ¶¿•ôoJoih›¬U°Ce‘˘OY&CÝZ*ÜŽ“£*xÚq:Ë?cùÙíjÁ“c)òçn±,õôgÂ÷׿cÀã|Yšò¬¾ß²qTŽJ×W/Lm2D`ÌÍyIóG58ÞÓ%£Df,­+™¸¡5Õ×ZYËJ,­w›0ç}¦qj«Š&JÉ Ïd>úö›¬‚êDÚçFÈSRkFÒs¦‹"]á7¦nôϽ©¥gscCp"v·šuÕ×ÑAÙÍq× 3 ¦¥Å£Xj­mÑÕutÆÓ F Ì×”y§“.ÕË—WC¦unÒv³íºË †äÉD;BMHD+Áhm“•Q«ëeÒ,^hšÐ·<×'S‹EÙyÆVµ9«ÚðQ‹ŠÆËlQ“&±>açôžüΘ_ÓùŽY|Ö÷9Ii5Êb³èí¼zk°¥ciff¼ÍãJ¶µ\« Ű][-ŠòGžðŽlÉ6Í›E’zG;BÔ¸e ¬‚ñ™l´÷Yâ.ËiTNró¼TõBÓJÓÆ]}îþoe“ãÖ3ìakvñØ9âÄõ‹áª6MW¶•w7m“óW¬>†–Ÿ•v¡á´ëœ‚sÖk¼ÇT]¾„é®@1LÌÀòŸÉÖ¶IhÑMkgYŽÑ •fÒÙ‰›eU½¹Ó1mèsÎÜÆ—gÌtѾÔÖ¾Ï.ÜöIb”5i=4žyYt[•mÖk`S:—~É¿ŸêÙŸ-›Ï )¬ç‘ú^k¿3¶pß´ðËM3žÑ}«iŸÌï`øz)å¸gz:~§ÁåMgÓÙùû­‹çÊ"ŽèìôÇC)OÜÌÍ{”ø¶ZÜëÏ[ÿÏÎ9½¨NÛ¬´¥ˆ°8ë2­*Ë$µ’YËNmËô0ÿŠõÏËëayÛuÖ´WÖóTÚæíYa“€Žeæ™,èÎ#^ÆùÍJÈ+tv—¯;¯Ò éM¼Î&kgè±åD¯–`žu‹Küý—ò^Ùù}`þsïÑOJfþ37‡?¼ã£~†²\¸¬L«¦•æ­ÑÛé£d€6E¦ÙMuGŽ î[žîŸ G-cÈYŒO¡+⯬¾S¥Ž’¯ÙËÆ?A›¥æ%½6O¹×͵|®¯AüÇ'üêo±Ê/†lšV/­ûV¶W•7_Î鯥Œ Üyë¾Ík¬®ÍV«3_P|§kpyÑÎ÷›}ûr¶Rž]«ŒS»øZÜò¨ÑNç¯/Iç‡h—½Yw˜Ó|Ý>³*¯é°¤Ù;'Ymõ=¨Û-Òß]7ÙÕ?ƒinùדqè§NŠGØág˜±8mê¯éθùÏéíWwmÛÏÅm¨Á²›wŽÉG=yR^µ¬Çª•.G«“EVç_V|—BÎmã>"}ÊãÖäõô·aî´[–KŽÖ‘Åè˜ÀZžúzKÕ¼›I‡#>‡ï%o ô¦<OÐ!¦"aÞ%ØN½x<}¦>eôëÓ‹]ÄÕ}·Ó¤ž#¼²šÇ)óu1µx4¨=ôoÉõyßéw‡ts>Ï+T^Åó/ê˜Ú‡÷½/©F-<¦Íó™á ÑW*M¢ærò½HúÝk›_4cª:Z"Q¿¦þsiç‘£'kΞï­íçwylêëŸg¢|¯ o¯ÊÜœœüþž=<ñÍbŠíé_m4·/HÑ›Ñyüvî3m±klâžÅ!ž…,¿w?4úš²_ˆÒêM)­z}y|·™Ö#2ÊjJM#çc7óú¬Þ ’yZU?O•þ‡/Ix°ßÑ·E=%ó^uú\£Çi£ÎdÑæcµ‹Çy×¼é5GÑqEº*›6éeXiê/žÙŽñç?o´ZïÑÁxÖýãôjNŸœôo™Ê§ƒ>µ¶ÛΙåãì†ë´—ΛÈìÂùÆqÛϧ¡žþ­ª¨Î?˽!ߣ…rt®J&$—Ÿ`éϼÈ"¤²µGœùã’}5óô¥¹bGõXy×çõgÊuPþ¤zSÃÒYÅxï½Íår’(¦¢Lm'Ê‘_Fñ̯;àß½&IJ¹éÇPz¢°Žöå}+ài<äëòÔñµWÓá— žÔC|^2™®]ý8ìŽbÌóù$ü>>yô<´¼ôóí´W"Äý†/£éWïøt5æàãÒ‡××`éÁÓ–­;eiSXú~éÂejkò^“3áµ½Çf(™]+æ_{L|¿TûÍ—Üg[Õ'£"}O#„c¤¯?›ÒÈòzùöáS}.TE“k ñÓÐ_?Ñ5àç¤>+#ŽUó&|=:ã0ß®òüÃÛ§h¯ºž¹z[t«-âó™“Zùº’R½+d|Þƒ5zš©n•ž7¯æ«lÖu¦ùþ{ÖÎ7/Ÿ%ÑÚ¦½-—9¤ÂüþÌß{ŒLej®×™kã‚ð9•ô™®uòDZY¦r–, ’c+y÷ÜåE½6£Øÿݾ…z4”ú9øÓé0I4áM¯ÿ{·ÊµéDŸž-^ +ë9éÿG×˵œ9ôxì,›¢ŠÇæÊ !Ò% c’s5‰ÖìV-´/©8î‡[™ÈÃWì/6yκSÔMÀ#Z¯«ÉùÙß úÉ›Ž|åëÖ®îByîi)‚Ñé˜'v-:ÓœÛ×Õ,ó¦˜÷ê³oB“õyYú1S<îøÞëðôéÏ-•v˜[‰?©X§µTXÄŸ9Ihz|M{á âÝvìº#Yë©Ñ+Eõ1Z½VnÒV¬ré­ˆaµåÃDŸ—iÝ/zsV®¾µ¯_?³w䀯Íã™rEÍçërùKÕW÷9Ë[¤L÷‚mÿ6Î9[Ë_QÊš·t¬8å¥ïò›Jq¿–>§©ÿ¦¿Go]îù4QR¶ôÕÂ+õ0‰¶‘Ä$Ò­3w:suÓ[îq—kŸÃ:¨pA3ˆ–%ø¥ˆ÷Å|YNF*^YÁºK]‰ªKiazR{L½Ï½7@êð•’æ—[OŠõÏ{Ž™öµÖ·x%<Ú=¾Þ×~‡Ú´Œ_¦wàõúwçôñ÷Ûq·FËâ:c´Ë–ÖÇd×2Ž»œúißšþ|ö¹i~ér˜lZ{”]›xˆë}+ìÝ–†WXÒ?µ¢:Ê¼Š…‰ŠJX§kÑÏ ¿aJkÕBzº¥¼]±~ž>•Lb}ÑÑË´€`Êkz¼q›†”åh/ 8YÊÕwW<ºÉytôÇËu7k_#ý7?®þ7¦ ísÎxôtãѧèøü™ïbƒXUØ)¦f9¶xÆ=ƒåh®ÑIu<ßí`õ¿Ö¬£ž;Îzs¬/iìá.ß =²—ÚŒšà¬‡g³ö2×NY^+}%«òqÇ94ÓO"ë¼^»X½ ›òµÆ²ŒkîŽêo 2š³7‘`¾ÄÔ— — ÌÛfÑ|õJ˜Wö‡ÄõI¹-Ez°ß¥m9›ò_˜G½•~‡‰æa­=VÞѦz톞ÜðuÔŒôR¸éóèïOi37²¾ë&RÒ餚βKUÒ'¥«Æ.¹&b3µpËÑ}š§^Ÿ=úÒ'}(Ë|Ÿqž•—ºG]-íž™ÚÀ j̳*7Ž¢^"¨o+é-V9ßšçÎ%]á m~xqzj£€ãjò±]¯ßªžºGúòŠé §´’ÚzÖn/[Cζ.ó?©”{ªiTs;©yöqÝF¢G·©´jß¿jüµÜW'+,Ź?6%×P°Iyq}MWt5­9µÅ›6ݽ‘WßÓa™—1¨m7´é§6¾‚®ÞØLðk¢¢dí¤®ã&§ŠÁ74'ØÜº–†*Û:K ÕegI4™Á*yO¶LÏVÚ]â“Gì¹° ÙÓˆVbãß¹¡¨1•† #N» Ÿ?kIq3ßï*N†­ô·Ô—›%Ç›bË"ÍMKWžc$šîk 3ýA¦Ò ^¡;ÁŽØ+ Ƕˆš¶N/ýLþŒÑùåÅçý½“ð±wž†®)”íÝÜß«êë Eb÷®wÎR 伯¢:÷ýk Ž€vwlc_Ÿ8Þ8ì·6ÎKË‹²ôÁ[lŽöí°ÑÍzÏN%±hµ÷ÏÎÒg Çð Õ ÜÑ]ZÊЉ~rŸGP%\~ûmm34µàs3tM„ÒUO§´ÐCq©nªþšúÆù¿°d uIúÊꙥØÚ±ÚГ _Y#/šKË_e©¥Œi­¿ŠÒÛMuO1:’kDÅÆˆ‘‘¡t’i­t&“c=T3o‡¡;o©häìõÉgïUGS6 ûs@ÙUi²¡ƒ—˶¾Ÿ0½Q¨™ôZ\õ5iQn­™ˆ¡ –š ºÒf†Är¢)2Îñ ŸŠó¾~“rDaÆ&îºÄΖƱææÓ‡ŸÝç4q ÔÐ 2:³Lñ_P“¨>3¹šë_¬ê–óhÝÙú¶ÒÒí^¨MÕ\`tèY“0—D6úˆ*‹ ˆtÙï$¬­/PhÀÏWeL°µÎbŸ±gøóv$‚³¨åH~F.§QÃ[›¢Y{:w鞦ÌJ=Æ“[[§ÊQki㮤ÐWQë¼ÆZ­ ;.)DîL¾öÊZ¬8* ½›¨»Ïæ¥ô QWŒˆcÝ]uwš2j§M Ô¡@TBr×Uú ÚM ÕÅã4uWéoät›¼?²˜¥jß³ÆÉX°beóOÉ~s]¾ëN^žµuZ›AFÐhíi²Çédk®·µÖù-Ìõ:;7%h ›¬†®«W[¨º–Î#tÀÞ\ØEï2¥ùº¡±Ÿ¨e— Xq\«óö7T_^æl©ì‡b±¨Ý¥¾ƒA¿!¸kMÒˆm ”¶ÕÒÄÉâÁ+ Š¾ÒŠ^Võ!æYiôàd¢¼®nj[È `÷Í(ªÍ5Rh—ÃÙÝeþîZeö·B™šjÃ}Pp×ÚŠZ̬YÜÆJ¾ŠÞˆ-в±u%F²ßN•šK®Ò¸,=9VZ]`Çkªj.¡+O–±¡§eK)¢eçúÆ9_àÏñçû剫O×ï%3sö5ŽÏ]eSÓ‰ÎQh,t}9éÎmhi5²6,ÕÕæ««,æj.¨ú…Fe\n‹GÍeþY!ÊÃYˆÚüÏS”É.±5ògëó䔾¾Mè—õÛÀ Šþª–UÚ—MYt¨2F¥æROQ£ö–Úu|)JgŽmë-.eðx_/Wu7ýý§tÓ ™ƒ9©Ä‘ö]I63stàm±Nn´!}æßæ­¦:ÆŠ„Ù¬é´óIiza“uB±»RmœÃRç+i¤Ïoßô÷ri]pßÓy©)¡RSÎ à§ÇÕ¿ãÏ÷[`𥼮k#:¾Ù- –†u’_[p–†}÷èxí5¥Œ™ÝP×õ¹›iô¢f*írŸsɲÇ]w_7)4×ÀS^7E|Í|PÛ—nÝM˜t6Y¼»!-êËÊ»ê!5TeÁÙUÚÝF4-™0ÔLmF3_z´•ù ?·èµ..c’‰ÚKˆªioµõòZwív©û›®¶´-y@ð®´–Tâ\±j©n7Z ¼ö{=X«Ímu hU¼½Š¯ÉIÊÖ Ÿß2Êž±µ–8¹›<õdz^m†‡3emÓš¦®¡µÊÝ‚.¾ª“WY ’[˜Ù{Û¿:wàH“ãêßñúk#ƒß$€H[„ –vœÊNù6uùàu:›!­Ìçý4û ¯ ?P£u{'®žê4ù[‰ˆ]<…To§Ïsñú^ÏÜ3üz‚¥-¨Âê–iUaïi¬) ¿Úš“ Ý<~LÐèÇ¡=@Kb÷oRB÷34Y_hÆhõnÑÒÍQ§P„¯>‡geaUlcÝXWÛJÄB†×ZkØó”ØMe6t£Ïz½íUÍcüн¶ãVœSa;OmÓÊú‹ ]%œºÝ¡º±ÑÖ—ƒÑtû4”T°…õJö!YC}c XÜêSA303į¯3×ÃÓ„‘¼Oª|}6JÑ3ïo«À‚´^B4P=ŒkU(pzñCå1Û\èv¬ ÒëKG5ˆç5àwa‹«>Í´§Àz‰¹ÐÍmOc²ûйC"³\B|:Œ ¨¬3UÖ°×US4Ñ«äÏå¦C[ñÃ+4‹}j®¾ã‚i:Ô‚YöAz5Ørü¢0pQ×SÚP‡Z-4ÙQõ¦Ô!ËµŠ²:Xké2ÕYÑﮄ—-¨ÍS<ákª…;Eeãíé«f€]}µ>¸[z|Ýã4TúfÓÐÛkU¤Ó®·/}ÔBÝåabE¹ŽNÇþ{g,V”ú>¢7ü+ÚQ´}G†k@ôØ= XÙ|yIk¨À“æ]™ K« _ 9¹étZÌa¥[lÆ‚1 ú¯.6•4«æ^iÔ¬¡LÊ}4Ü~võÌ»Œíë~:J·4C¶Æ÷ŠS˪Ç…@<¹K(EZ¬Ç­ö 'ëä2jý»ãh‚FÚ$]?°±ǧ8„rØÙß .¯g;³V÷ÔÞ‰Ø1êG'«^_·4¶="˜3:[$ižê£&œÁ#5iDã+i2²2<ü26X•yŽo—bŸžÖ4qZp%‘˜­¼7ƒ'Çãí¥¤ÕÃI˜Z+!!„hFà4¢WìRÀ- áYÁžkë*µ°œe!ÒÜ\‹H"î®,9=¥æ€ÐéèñÐÖZ n:q8Y„6`Yt6lø_©‚®¿;][,  1^Ø'£I^R|}oê{X¦¾¸\=UpßiÜüY€›Wu’®Ð—_RTw¹ªý"ÇÒŽ3(ŠÎ²„*r5yVêc©ÉÕÒGYÓê:µœÈxX<Ë+½Ý>?-œŒ†þ¸^h;}FêhqHí}“Sÿ7Q®¥Øi}X€’áxqs>wd ³uO0˼ÑzHšÿ2å(ˆ†nBÂB¿=«‘oõUDT_ÓOl€7"¯x‰î—ã$›æq›BÓ‹º‘íËÚ®ê ¬LæÊ- ­øú/ÒãP R\ë́ͱV2Sa{ãg<Ɉm kìXž®´K!ØÔy¤Ép>¤VMÄÞPqûj6qÛê¯ò6{³ºŸFœ©´óɸ¸*—<±n¤âϼ‚8õ;0œÞ« ¯ê’Ïr±ŸÔZ0Ž©Êñ‡êl²M/TÜÉ[Õ¨yü©hº²Þ½V¦N7ª”äýN¨ƒŸÊ9?‘§›žï¨³l8Ù›;P1>>±*«éÔº6¿é?·Ó㟮ꈼNÊß×o–«{$ŒrGF· “Y¡¶«"Âb §Mÿ—ÇìíÌB"±³QB„NOGq3>Ž(Úƒî¸ÊX¤Ib2Ù+`kƒÌÆ_ß“; M$3gëŠt¸º™¤û&«ÄÔ5ÉÔ†èªÅš TÌö¬ÏséüOI6¦2é×gÜ]‘’Rë©Ë«+‘°FÁç,|KR¦¥ä…t6˜Ú³6º'1Àmɾ—LD–BnY0ÏØ…êkV,tR놧÷(³™šã²LãããëÛ·éÔv§±÷çúïÙŸWCO™‰ÛRn(ø+›Ï{ü}?Ü,AyC{ådÑͪäÌj 6âÔtX‰lN"i«Œ FÇæ ò´ižC}ìisž[¬g‰³žÑ”†ªš¾Ê†ôÀ˜&(Œ}.ö‘÷nqž'ÇàŸêwξ1º¢/í]ÝU]Ó»‘‘§‹éÿÛß²qûã»=‘ö¢ÚÆ•H9 *.vïôíÎÝù`Ã&…1ö2½1¬ ôQÉTEB%fœÚ#-®Š¸³Ïí…*Qphì6U@šØÉg£†hɇbmÊ@E†VÕ+\ð¼¿Í Þ—Çå·¢¦°m CƒŠR ”W±’•™RîÒ×,ÚÈïf@FÏ&çeq_F‘Ó:¥ÃHž¥õpW„ZcU+y’ŸÔ=>?. '|ØJ¯÷Ûã¿·oÚ§$^üjvDíÏžwþÖ-)Ã>öÔE®Dw݃'$ÖŒæÕß0T÷"¨ÉM¡^'mŠñ¦Ì‡*lÌTs7u27ïÇð(çO!B»jø—»ŠiKc{áòU±È¾4gy _eO\ž¡“Ù_Ù[‹ª`ëÞó²Gx%Ê’@·Z¼Í†˜Êê03VæéoOÙ?íø0ž* ³s˜>:Æqj"ÆÖñð¶òÅð5D~=]äs@´³«˶ÞÏ 4A’Ÿ–ݯ“5Nô’©yÛýñ·>Så9áïÄú';'„ª1 [:I*—ÊzrF9¦5Ð(ÒŒB½ü·"4w'+˜“ ûQ^ŒlNá#¬Søœ× Q=δ8dŠȃ‹ÅX†-®ƒ“"Ï4F9žÁ:@úBÕ‰áós-½(Ùóí‹!Yœ×û™~lµXH}ùÔX[jÂóèH}Ï?Tl0ìs‘ßU€’jB§ªm\æ–ÀÌç­äÇü×eÓÃ|Ÿ‚}7åËfßæT9üññý{öâþ‘©ßŠÕN:vGÇÙ‰A]&Õ×GÉ6°7“í§å…ñ–‘÷óx×¹üNþlÞ^ÔrHØ’^JæÅ$³yiád®sfñ«UŽ|j×XÇEGÖ§¨“ƒD‰ã‘…,Â+в³l¨„3ÔXgdCÊÉ)*[.£†¯Þ9ô4U™ùvR!Š*ͯ5 _½Û&^ÀuQVuÉg–¬ý>))+&—žËûÆ1`зãðO¦íþ^L 1U¡®ØïÞg½"ÖØOÉôv^?|²{ŸhK“Ï&nJ"££zø|.3<1×$^ÄhQ­Êðñ‰ãàÒ;’:5ä³5fÀþ!<ñª¶YåFæÅWÀÇ"Ä­Oúý•e© {¬'„79åÌ8…”VßI*gµ¥Ê¸ $ÐÇÒ»IR£¦¦WfvSi"±²ÆY¤IUg$‡fð~7µÂM¨%ßqæ "Ÿ%D{¤· ßò¾ï¸üsODÙ'çÔýŸˆ×>Oîé8ïmÿšx}#—ôž$ð¹Þ5¨¯ì³qþZ«Âñü dsyjŒ‘"óÕÜB•Ñ’[ܬ‰Å² ¨§eet³4À‰­ ¯øñã­ÿ•ÜiYÙ;D"H¨l²õѸ͵#ÔÔIÈl…ŸŠäw<\ý¯?×~]P|5—L ¥ÒÐòŽä+QT‰†#j£{f:²ñu”`ÐÇ^z[ A}k‘¹’3‰ôw(•¾OË|ò.V¬}ѨÅXÕ®Xã9¾V)ŸÏ‰‘=ò4qÄö’gj¢±]1$;Ë{¸;UŠæ¹¼˜E€Xæt<ŽêƲ=ö<=Ž<^9SÞNƒ„:U)a ª ÀK ÓøÌt§±‘#ΞõžªJ·|G|h;›/<¶§=JÉ뼊Òâ¾\ßR<ö7·eú|ñ¿®w^kr¥ÕXÓûN ð°ý¿E@dH½4ûˆL !û+ÑMèôr’ÃÝ«>•­ê"|~]@UnF”‘Æ­>xel쇷™ÈØœS›˜æŽ¾sbñE,CÇ"ÁIÄŠ•ì”üzÖA ZþO0èØ›ŽP ñ$/ðG[*ºH§{á½ý@Ó `‘ÉD‚ ÊÄøMª|–ö£=L豪DØ×Æ9r€„•3Zùû¾DW9bòžââj{‹™Ì¾dÛûöDçëðñs^ SÜ— [N{B­Wc7MÇ&GHž^®(*®®cXÂŽOÝLOþFOË|¾ QS¶ŠRÚõU»b f4QÙÏmímLIö÷©T­*&’ ‚ÆúCŒ ³cÀÙ¡ª°–BÄ2JI Œk)S’è¢âÜ$}Ä5çÛÝ*YÈ#ûÌÔcdŒsîä°ˆJ+[)jñ“'é¤RI?MÌN—‘»¦¥vgJ]㇥FùzZ ‰ÓjP¦e(Pò1£ƒ‹ôO§®¤p  „Ã5ïg–šn¹òÂÀ{AQ?_Uå?ÿ2SãòÞÿí*â«¡¡”ñøU‹<³}—<åŽX¬FÿÒžD'ËÊ‹ÃèD’(*CUŽŽ¹ÈµõLVRÅ8À@‰òŒ=<,°òšÂl#Ya/$†ÍÄ< ¢ä@•ÚUŽ{Jê,Šp1ªŽ1!™bÕxŸ¾|q–VÂUFî¬IçVu.¤ù#•³³õÎÿ¤_wâq\_§~ü^F7ÚûÜOtŠë&SJ’7J%§Õ|UýÒ¢GÔ¤øú¢÷O¦ñ®vR;Ó ¶‡NDDù¥9ñ4 ãr=!±³îy¦iWS2’NK9’;×å­ÑlwºžÐÚ—#½ØæKÉsÁ=¥’­©ðÑ3+«$±0¼[a)BÆH£Aâc¹'b!"(à“=4PÛ'îz…|vÌåçÂh¤&£Oéi´0WQŠUqƒÚa¦Ìl…г·;ó·oÅ—»Ñ«äÐ×ÛÎí­)6L9sm1A:mM"åŽê+°»ý;ÿeå,ž¨'àßñúlëå´¡šo1“ P1FR_HÉŸáp†N%þ„=+áký _ úA_¤Š#À²±@ç«Ø‚¹©Ú×$Ü –{™áéÈë‚Æ$›ß(–XÔRƒFO$>{ M0o L?˜ÿ-ŠÉžI^LÊHÀt÷B?Cg<š °|ùÔLÓˆ-˜‚CdÎÿÛÚ^ÂdŽM„0©{YY2k‘îŸcƒßéZ²[ÝY‹•¡hœ–ççÇ?×ø©CÆp¾´œn‘Ix¼ÿyÿßU“ð{³éºÐeàsc|å!,ªª'DEoOk‡eÇM¿á_ñîðr6ø£§Çazf¯‚ò˜ªiàHvù,YeåŒ4²³ÌôP¨Ò3ÚØkÓá>z°Ï$®tµil)X;K%"%‰SP}ª&±„Êé ç¾)'—8þC΋ÆÌ•ðµsˆ@Ï玌ɦm†ÖÔ++ ƒ®%\˾‚6Ë«±¨!z‡#y>ââÂxãU;Dó 6vÄÃßô {·«5m˜\Î?5þ—Ÿ+²uI>>¡/q¾›×#rƒ¹½¥‚%^˜ ´»AkžÅ_ð(+t,aæ^º;[(ádM» k †ÛÀ œI %¨ŽˆW˜ðǹ¤:wØØåÄm}º|)1Dí½”%zÛÚIÖ6O R‹-VZ[y# 6D 1YyŠ„´*ÒÄUšÙÿqg›Ð=®Ç/këѽûhª½æ¨YäãG†‚¥ÁC ¼¯=ÆDpÔ¯QòzQ÷‹XBÙêùë”m';ðuMèÜÅf´Ú˜¦ÝÝ·‰¹¹‘!Ü]¬ØÙ¥—¨Íÿ¨ðŒŸß÷ûLsŽxÒJW™ÓSœ×RéH2hwSŽ$¦mŸœ¯v.v¢ð£‘®îæ×XPm¯Œqw,T•ôQ m¥A×[XæÎ¹23é#õاÀ9¢Ï6í©Ý«ÀcðEÔ«xì.Z§†)Öi¢‹±RV]f—Ôü}`o‚/§Q÷åDžËÐÔ„­Ê•Ÿ±Ë~ÇÝä`¶|§ùk €­*Ï’"ËÌž°š‚ö£6º ¬X5ÚK%•ŒöÄ VzJ±å±âÖ3ÔÙÀTu¶qÊ\SŠ6”ÙGzÅciêÙ=t šÒYÎß×-¥Šà^Z†)C“”6—Ž(Ë‚^s£‰óÇ3ÄÚ˜``4{I…;;ô‹ËZH-µ­0VTç&a/‡>]nˆJ£#†Ïh]}|5ƒöâý/C6F&И‡¾³š£?Ÿ[[«Ì5lÅE ð°xˆšº,rEQ[þ?_õôê\ï‡5£‚ÂÄìÍ:JÊ6ÿ×m~,k"xºlLN­³£–ÞÂÛ15k+kRl½ ¯ÉŸ(iÁêftp,²ÊÅ8O.K¸b7fKcš´ ž ME{íÍ’'i`P[± †P ©¤H pDåô ¼¨®E Š f³Oøçã\ØÛNVWÉ!«1ùáJa0x‘xÄý|qYãå¦>ºÍËÓq¼úÜEp‰9úâq{ñ]ÀÃg7–¾²Ïi–|œõE¾¶1[+VùõÅŽ5F;úu ¿—SKx¹Œ®†J2„ÔÔ—šjˆQºŠu[­Mhu0ÃáÃS6¶”YÛ*f‘m[Ù9wŸü ¢@É1¢GÊ4O‹ÂY2b áT–50¬Žò@-ƒÆëÒíàݶ¶øêhév1N‚[$Ò6F¿Ÿ×²þÕS¸‹øarãɻ؆ŸOš }hÚ˜tm‘¥µ¬™3“9†‚¾¥”.ðuI¿—Tœæç+\Ø%÷鳘xз ކ4]TÑD=–[KåjΑÆ»¿=ÐH$RÙchÁ&‘­œä¨=~>=6yhåe‘’«+Ç5XF@(ÀDþI¬š3Ì…óÐgꀀ.ÿMLqû8M|ƒ¬³ÆØ¯;ñš(XÂmã(ñdò9I÷ÓBz}8ò3+¨ ¯ØÀ³7[ {–¢5]e¤&åÍs6ÅL²ëm¤ã&ÒrÚc>|ñ›¨ ÚçóvשR^w3åKù|’uwxa†Zúiß([Æü~]H‰—éùuÖ~V®ÐY:b £§UâaF¢¯ü†—eeW$¿lPÙüÙ²3JîUc*ë¹t*ÒÞ1“½¢‚ø§ž'46Tò´–š àpÖ‹ JžBóp‰Ç"9“P“Ÿ½‚S5òÇbg~|󪥫„†êÎ8~巅޽²‘ìÔyKÕ+–‹Gž7‰){VGÃmîwÝš8˜Ý&³Ó²Û]'=f»À×lLáx2ÈwÙ ‡…›®H˶HdÍxȵÈv ÃvV…±ƒ¬³:(ä9Vs]$8Éô•/•ÕvåÔiYT å0`mNÍM¹‚ì’ `£ßoL±2&‘VbõM v£Ìçl£“§”Ã+£y÷ç–TÄ–Z1¯có÷±Û‰òš[H¯k'ž/b°òbdÑZÌxžQ#h®gòM’^¥€ÞEÔšåFõ¡ÈíüRÈ©D ä—$ˆi²-qt9'£ò´rBì•kÙ$Þ˜š¥B©, ’8Ç‘’úeäI Ò¹ïk½Ô·+ä!8²0ˆæt2 óÄ,“š¨æMåÆò\¼Gµ¼Ž¸ÛLÍËT|ᜠ¥?Ú¶¦¸µ}DeTÞªcz„Ïñüº‡gÍÍ7¬ˆ…dkR#,KÄ¶Ž Êê­pQ“ÜQ)C [¢°Ã.CÞåì뉩)ÎóD‰"I ʹ³šØ­qêÐ6@ÅGm`ÆMaa:4PÙÅ7¾@ìÈßÉ:Ý«>âI¹.‰ÎJònî%µÎ4h–F[U÷1kˆ|°85&|0RêËáwÃ^<Õ¥$ùñ ã±Îjôø’Nœ…/Óbt=ŽO£bI‰¥ÌA4µùXe‰˜îîf.1§°ÌC w7ŽÓewŸ‹ù^ÇÁ7Pôe¡6†ÏÆ#xN­Ú³ü-¿oµ‘<–Áj°['ÀX=C>¯¬Øèë_<‡ÿMK|1å©d9¸$ªªÃ»Å›×ÒIq^$GË.døßdÂA/Âe¿ðµÍ¾ JK Îyk5³ÐY¼h«7.ójíh®9ìuêïc®rÇL|`ÐÇÅùê.†&4RÖ’°ó¸@Œ‰r5QÝ[½½›Ý`l²ŽS ÄÒXÍüm^ô§‘±íéüªëœë¨…HUƒ×S"Veâ" ,kµ©Ÿ Ødg‘ŠóF0*õÙºÉ_ÔÀÝÍ™å_ØfAÆÊNÁ¥Ž’°U}.É¿—PX×ä½-o•é}0ѧýF’ ± ¦FúÜë›yi„§=”S»W>ÎhØãµ ®¡¨²·àm•ÁŸ?Ý#ÖŒ4¿ eT¨Íò“c_$q2\+wxÿ´öFÄÈõ—#Ìf`|Üía¬ƒ\LÀˆc‘kHA¡É[M9áËPsz T0h/ÌÒ®wi_Q_U´¯³°¯»¨·>@à…°°"Êž‡Ô¡¤®Is•r†&K?cÈð4rÉk™ÉÕq3ªpßÆÕ dšœÀZÞZš-¥5•ÃÇr¾Ñ|ák³Hl‘¼’š\ýû~?.£Êöf"ÍÌS_Œ·ðǶñ· jE‡ÐÈŸiÂý±±: MŽ•#••ÆgoGм¾ËXVXW ±¸‹?—>ßëš i=ÂÚ£•¦<ÖƒåYþ»ˆ—‘Ÿ6¹jñy>r¶u7§Àâá ¨ª[k¸aGé#6¶xˆ’g)Îl„òxŒó[$’"½|Ñ,'«%çw$…çiŽjˆÐžDsåñÏõYEáqýù¤ÝG ¤º·ŽCmÍ5³hùž<—½+^ÉÒÞ{Ê÷ õMøüº˜J›BNX—[<c|·÷aÄšCÀЛ[Ç\×N4ÆÐ+%ª¢h9Ðbi™*]üƒp?“eðAÔäjîìûͼ°…"ÐܚÃ"Á󒢸™¢óŠðy åÏ=¾(§cfV5[—ê¬_yÒ#åÛRÆÍ‚MÌ‘²DÙ¢I;7Í™±5ìï*ÊçO2£Æ1\ùÕóy"£¸;‘±0é`)¿æ€G;è§¡®û¦™8šŠ”åÖšÆá{ökV9çs™e7 IÈýˆ–—bD‰”$!îõg¦OªýtÔMÑVÒ[ŠN“Ú+W¥V‘±ý3³ó?Œm\ƒôÊåîgK-esºYt©/Kn¼Úþ˜Þ@ßãÝ#ÓøÒõ$û M/Ot™ø Ëügk?¥Ç'¦ö]פR:Fô“»béQñý&kÞΔV«fé0‹#:F*1:F3Ý'‘¼N“H‹'H§î½ 1Ü“¤6-âôšÝzQnØ¥×®.“Üò.žŽgI&ãúDÿº>ª¯é‹’tü^“˜ŠÞ“–¼NHœoI×ÿþ™Ò†¶$é7n3¥¶Fô§ÉOâWµS¤‰Æô©ŒéD t]'‘¯I`“ô JoÇÿ–OÿrïÎÿý7ÏÿÄ4! 1A"02Q#@aBPq3R$CpÿÚ?ÿò6çÙÿ‡FŸõœFIã¢9óä•# ’‡Ëýÿ¯Ácþ-–.WþŸ‰ûeüjM&“I%_è×,ÿöÙÃæÒjÖìÃI2’/#ccÿD¹gÿ´Ë¢^‰)22RWü¶¾¢mÑŠlr"ì‘érýŒ‡ÜÄ1eÉÄx¬¨‡êVÄx迼÷XYîpž¶zØ?"É…Ÿ ³S¡IyÐä)Ø3.GGþ;’Vh4”Q¹¸Ðâ(šFŠ(¢¹²Š(¢™LÜÜÐ=¾†_±‘û˜¹|¼3T–ÆÏ¹ñ>#®Xd”÷=Î8DÇš9¡+­ÉqQ“ŽO±>&O±&I ˆ#Ä$G1ê&E)§°¤]ò¢Š$„ùYdžæ¢Ë,r¾$gl”éŠf£Qˆ–D<ªˆæG¨=þ„þÆWî?§äL†M8~!MQ¶©1o¸ÕrEš·1Έ›5É!¡ô4ÍÊÔèô´n'Ü“j?/r,Ÿ"1X÷ðNQʩšÑÜ”Ó{EÞæ¦y+©rg¾GÇÉQ.(´oàÑ&S[Q¥¿Õö ™žÅ½äG€=’1pê%Wð$'ÉòEQ¤ûY)‰YÙìpÑýËy6aͯ%35éFNÈ} ¸º—$¬ÉÃêg´Çäöxk‰1p¸Om¡hKÁÛÁOÀµ#¿:þ ‰B\¬²ùJ¬P#‘—a¿‰Ã-¬ÌµâØœ?v:N#x£/d>…Ü]Kë?¥eô²ßEÍ,ÒÇ6V_5"3DÝ£NãûL2¦wErhqèd~Ñ6Šér¡ä¾W¸˜¦‘­‘¥2q¦WN¯ØÁcøª„ì¢q<’ïÉ‘ûEôì¾—ÜCú±ý (ÒiÆårB)’ÔO}‰üV÷Qä±È|™ÂúîF¢Åô¬¾IÓe–X¦‘­‡"þ‚LŨ–Ä—¨LcÜk•–1}½,\‘þCBBø¢ÔfÚF¢ÍD"œ°YÄq~¬µ–Y¥·ho*ì,ÙéŠZ–âØ–ZcÔl²,\ªrÙ#…ý9åîq¿¦Êi §ÜÅÝ®ÆD§%g¨£…(œNMÈÏsfIzE4Ù$¤zBÂF:vce–ÈBålâ#<(öbÇé­R$élF[ÅZzX¹#üŠàû OVâ’Ø‹"š¶cŸ{+b†Š4d½‹šîGKÝ•ø+bQ³J\ªÊ+’ìŒ\vl]™>'>o“ ¢×ÌÐé¸ö8E·¬šZ>&l{žÞ÷D¡(Üœ(Œ#zfš+’CC,”Òˆ·Va”óþßàÉ(·¢DðÅ«ˆ°ì{{CÄÒÝ \ѨÔ9dˆv(\§l†%äs£Ôf¯È™^ãÜ–=ì× õ ûšñ’š[ÄÕϹ“e±ÛÑ‹+‡‘qÖªÏp¥µ’¥½˜§»fYáȨÅ%‰ì~ߨËúg¤µÄ“ßLˆ¨®ÈWàÓ{¶T#¿ÈZ¿Y‹ƒÍÄmÆoÒý¤uM);\¸izyS?TkThªŒ‘Ãí8‘–íMŲšçED;ÊÆK¸ÄÈÃQ¡%D°jìiѱ¨s#+;’èöÑò{x ‡§~ )ìˆÃüY–&:—vhŠòcž•³=w/%¯"Ò‡;ÙÉ<[£Þæ}û®9÷#äd£Ø¢´´./ »‹‰Ãáž¶'äRƒìËäÿ†où6ò\QêÇ6)³Ô^MHŽâÃq²Q£s*ê] ¾h’˜‰vM1¡âLQÓ·,ßQ®ö5VƳ^ÂÈjØ\Ú—ƒä5¾æµ9,îzQg£|2l|"{1ð™øžÛ(°æBǘќќJk¹-Gîøs1a“ûEŠˆKI,»e—Ôº(Òi4’t)r±1>TS|æïcM1«f“N‰Ø9©ÖÇîÙ¦O¸ð6F65ÍËe–Yòð\Å)äwþÇÄÆ Š‹=ýˆælr²Æù-ÎÅ—ôlÕN‚Óå(ÿbö5‘v?tM®äYešèõªÐß'֞Ŗ2Ëú¯±“g¢Èá# Ei4ýÍRüsü¤¬Y¯ú¿£T¼¦i½Ø¶,²ËE—ôl²ù×סr¾›,²ÿø"Ëé²ú/¢ùÙ|ì¾v!±1òؤ\Qq-Qز˥bjcZv³ÿ²„>ª(Ý›y.%þ éÿÄ5!1"A 02Q#@BaPRq3SbpÿÚ?ÿò7?ÄÉ»ý2OÔGømrcŤڊ—ÆÀйBmpa±, ¢Xù ý?Ʋ[MGÄ|üS'êIã mO˜‚°Ïú%þÃ|!ÈïõØŽ®'†u¡•‚ÛÜ#¼³Qn©í-©×-ŒPQìs.åuõg°Òéº0Q¿—¡±'ÂðÎÏ×Eö7©Ø6’QjR%øb¦¸ö5Z5o(¯áÎË5T˃ᴩzÍÙ3Ÿå¹r'ô5>Ä»"2q‘©¾Nš:(pÂÁ´yLË3#23/™àœžÞ×»ÔA`Û–4¼$fšËlx(vèÞYWÄ*»îînŒ—§Ã§îoÝ.9F dýF¨&/ˆQ&-^Ÿ§Œ‘²¹óloN¤sµ’œ"u Ör)Ö×st?&èc¸¦±ÜÜ¿&ø7Ü•µÀ•ÐQÜKX«X#­Å2ŸRÏнg‡XÎÒU4ò…’9$!àÊ⎤GbfÝÇÛÁ“àÈërY#ŽL$$8²Iˆc­÷F£O;¸-Òùwß’›»Dz«ÑæµÕjîyK÷%+;Éš}|²Õ¨pm‹C› =7#ECé$:å‘å #»,’„¸,ÒÁÇ9*ÒÃòOI_äò•íîy.xdôOò[]•¼"ª-»¹å¤åÓü‡) I¾Þ©O¥cèI^‘DÁ ø4`U&:–ãf D±my'.½{M-n™mfôÆŠÖ ,²1'œ#lË=„k4’»˜‘Ò×LsgrÊewª'W|v±Cdz†ÿìK6zŠNy)Ûe[Ç¥s¿“˨¬%‚+jdžÝÈ꯲Fáx`Â6q‘< áäû‰C,QÁ!ý¤ž"‡Ù} ²¹pEe x$m'ÄÄGÆFJ;Þ˜ëOÖF^£<™Áed¤‡$<1$5áb·úÓÆÈzû”SãÓeú' ¸÷%¡Í;h–Âí ÓÓ¦he8GüSì4ÑÎàµui߬œèÔ­Ð ‘£%²UúDòj%¶%ÌL™#Ë'ßÝÒD{!ü︈³;""2K’Q7a`QÜvàf91á$CÃsLSc´ÜØ¥„91>á$.á‚0RîOH¥fN’ŠÁr<£,Ë0Ÿs[T'^§³ÊÃüÔ[¨~Ÿ´z¹iø’)Öu¡ûe—ÙöVJÝÊeÕü/b¸˜Ã'Ø]‡Èˆ}Ex2> ›†Í¹äžXàK<Œ]ü7#z¦Ž¢7#>86òm1áÊäÜdÞ;îC›—b¸ÊË=}‹è—eاý¸š™Rã¶]ÈÕe1ÝWb™O;‘crŽW¹?‰-œk<ÍJÃí%.G3q¸D>ƒ"#É’B2dÉ“q/ààÁƒg'#ã%I¦ÊÛ„ù,µ>ÃNSda¶|’’ÚU[êdø—Q¨Æ¢ý&«Oºr?ôì¿jH»œx¯þƒ"oÁÔÅ3y¼sŒ™2dÈÿ…“#‘½ÄuQÖE¶¬#¨“D¡FUO,‡ªÖj=/ØÍ:Mdž§n¡ÄøŒÜ4Ø|dZñš¿ û‹øÙúL”fÆmg> “gXKGIÁäáÖi=ωj%VºnÇ}º×²ÓM/Ó®ÚˆßO&ÖfHïã_Ð}ÿƒK L” ¦<0`RÃ&KÓ3îkvÛ¬”lìW¢ªn«“QT«›œŠu3­î*ø¶äUªŒÌçŸþ„гøC,FÞÿ_àÉŸŸ>1ãÉÏ9$°g‘üÐqQä”›­ªø,ømÎÍÖG&–¨ÕV"°~Þ6ع:Y¬`òZeØéUÇüxÃæ\øI™÷ÁÛØÎ|7Æãy¼ÝȘß>þ /Áƒpn3ò`Î`í<Æ0-B<ÁÖÉÕÛ739~xÊYI»ãR[ø+¶«}5ÌQpÜ%Ø’bÿ"””ûxÃå~2|âð]»?ƒŸÀ¥þþÇýƒsüðÉ‘¿ ¹äÚ¾UØcÎGòí÷#ÿp´Ê<ûÓµê‹&ägtpÈÇjÇÐOnM}r³O¹Žöì"¤åê.Óm—R%Ë 8çå‡ÊüdˆÄ’ÃðÇø6¯Àë_ƒbüÿmüŸÁƒF 6,Î;‰ü»r8Óñ!Ǩ½#zš$Õ¿avªÈÏu&º7,O¹bü}ëÝË<Ü1:†jÕ6?ÁwÅ+q{O‡|S­7TŠŸ;d8lxùaôqy0Ç).ߎlö?°â½ŽÞ Áˆ—QGî-ÖV£è,œ®™¤©Ã–>LxÄfÑÄP6›Hšº:«–K5½¨²˜Ý.'î’¯e݇upì=]-àó0ojì;«‡)žn¬n“<õ ó”'ØzÚ×câùM¸@V¸®{™šË*²I3GkªÄÙÅÕ+ Aï†Y’A÷ðXE·¨©ÅzHjU‚yðnHž¦ûˆêc.Âõx®åÓÂ+»/‘òoS{~ÆõŽ 3»’žÃxŽpŽ Ÿ6»Muµæ ¹[W¥ŠS©î÷(¾Ígb:jç î6ÂŽË#ÖJVmdû¸M G©Ä—ÔyÉ©…CžâÍÒÄ ôKOWZÃjºíÞÆ²Æq%„In[Qð)K§¶GÄÐ…á•x¿ oà×Ù³ÒCO+cÁM’„ºeI¨$ü5RÛërPÙ§ÔÚž×Ø]¼0m-„T²+cÜÔYÔ‹H•2O&’ß“‚‹ŠÉ³ÁîOòju»VÔy‰JEÙVÊõjþ ¿ƒ.Žø$|WJàòUœšY*ç”*”žôJM<¯”ÚìÛ›Ù¿"§¥éD¥êÁñZdëR‰ðíK'Äu[ìj=ˆ7‘iúÔ¦*ý_ð8îõŸûH® ¦Ói´ŠúÁ=&²Ž¢Þi$¡é<¼]»Æ#[Ûɵ·É m–Ò»k”6û•-°Iø9`„‹µ‹ÚÈÛT‘:âå“£Á ¡XìŠ%rkig$ÿÁoþ2Õ»’ &UZ²X™ÓÊ4ór$™“q;q#⸜Pªbƒ‹4w4ö²ÉB<²V¸Ã©Œ¢‹+²{•x-ždEo™¨¶0‡Iûš‹|žŸ¥ù"²½Cj%:î”6àoöR¸Ú|2­ÈŸÈÌý ŸjÒvcعWÓîUf5"Œb·DûÖNÃõ,­/§ƒl¥.™¦©®êÛÁà}¼ÈÕÁçÒ:Ü}L†mâ$4²‡,DZŽ !¸ÔÖá'î“(“‹Âîzä½e (áx¶^·Mê÷(“‡ÎE$ZÁÕ~Ä%cyö!½_V_rë娟"¯ ”y6$Í3Ž}D4°¶Î;”ÊT>‰¿qwq¯ó³YgNZØtR÷-¶‘ÛŽMäר¯–jïÙ<j1ØõJŃ­ ïeqSŽô/Pø2!އ.ʼnCÒÉ9ÿR Ç<ÙØ«¤—£¸åí!¬ ž|-©MÔ«e†ɱÉrF*=‡,É6.eÉ?Z,¼ƒÉlö‹]°—Åb’5_·¥·NO2"”£”'ŽæïQob£§É¡ÒY»Ì{Û%Ô"ñ,ÄeÈåÈçÈž~„×c_¦êCèìòVÈž‚ü¢ŠvÅ) öÍDÔîäj=> Œ¸eÏ(øuØŽóÈÇÊÁ)ª¢S|gÜj8ô‰g¹myg•ƒå÷=u}‡˜sIL—OjÀ·>ÄÑ)áaš™šyzÈÇ1σˆ¸2‰ú¸D!µ’­O’zbÚZìÌJ>åÛ™êL”¸äxɄضŽQB—á×9òU v.J*èÖ«% —C4«1&±(—GÐÉ}¥øãô,]™o1Dã/c©bdºï”JÙÆI3; jô¯vä;, ÄälxÁ¤‹Î¬/Ë&®ü˧%Â4ú¹Õ÷êÞ£fØä²¿Á©Îp;ìªk'^{7£ÏXÞ o–ì2Én3Ó[ ZÇi*™(4F8Y&9%jìÑЋþ£ÒAûáð?N«Üý2ƒôªr~—IúM=ÈètðöGíB㰆˳>UÓ»„Ë=†·Ž*Bùðj7{ S,ó„õ¬ÙϲN8fÈ®rmcs^Ä¥f;k Ê,s­9 –¶âɬIà…ܪ‡g%)Â{Hñ<¡ÈÖÃw¨²xÁ]ñ²½¬—N¯R'c›Üޤ‡d¯ý£C<6ŠÉIL’Žã{‰Æ]͘ìfhË÷ù_ϳV†±À‰¡Ïb¾~ƒ£îN1ŸbÝ:"ÄÞÂ2«ÔÉí²>“ËM<“®ÅÌN¥‘îGQ&ð8¹GÀ‘,N.šgSrc’Û¸ÒÞ£[™o!©Ràܹ,¯(òÛò-#qà_Ÿ»†xô‹áö³O¡TzäaI„ÛôŽ‘ܸeh•;ÇðéwG“µ@çßçCïò4F,”G,d¤ÈCw$}&só´F#ލ‘ œ¥’ÄGDÔEオdå–W~.é³¥…Á–I{–ÁN85zM…KHU§ Fœ j[=‹`äy<ó“Éÿò‘¯ì->?±µC»*Û8àqÛÁEçØÞë÷¢o³7繺¯x™§ý¦*1_äÙWû•¸éAöfȯq*³ÉûÚŸ(q¬“® $µñ Šþ·¹Ñ÷:|p7éý±|í‘d‡^âÍ;‘E{#Ÿ&áþKu ʵq“Á¬­ÂÞ¢4_ê%9-¹*Ûo¹*v³âmF%Ýb~“`âJ–Ǧ±v-ëÀÝä÷9ajä‘–dy0Ù³õ"Í Ò÷<…‘}Íšˆvº…ÞÔÞ¿¡æ®ÿa]ö¿èJÛsö[öΦ§Ú&uOú˜¿Ýë¡uqɲÆN‹gÆãÈKÜ¢žœIYÝêÀ¹%îE~ßÐhÜ×c—Ȳb>ç kž†±Él:œUåkÞN}id¢J‹7~K§UºÂR‹³tõ5×»w–½Dw ;94Wæµ.2.ÃFÑÀU¾ä©©!ãw§ÃO§R–ónÞÞ.\`Rb±FuÚ<ȵGš_j#ŽÇ^$¬®]Å*Ñ+©LëÓø:±kƒ«_¸íÓàZºcØžº„GX¬ûIL|¢õgæ¢ná´i®sm1|ïX—qÞŽ¹æ!î=Mdd¤²ŒåX5WÃam `’ÝÛØ£]]±éÜJÝOö–Ku]EÊ7úËz4umŠd%Æ àɸr1w5Š*µÄZÜ2ê-Þ<r8Š#HÁƒÓÓœ2[°Ì/Á?Â%§œûRXä¯J½Å§£n!_ÚIdö"–{ïã-³O8Ï;D±ò?‹° Îœ¿#«žEZ;xdÉfèÇrŠç‹?‡),ÂGéo?qútÝ3ÈUþñ|2¯»q +«ØŒ—²"Ì™2dáŽ(¾™·é<µ¢ÓË<•Sƒf¾¦LŒÁ÷ðÔÅÊ(8/ ÖHÅ`Ùd?'MÓi´Á(ÏþŽ”s¥ÀéBª8:q#ø6@é/aC ¦ fÖ5#dÍ¿“±“?OÓi´ÚcåÁƒi´Ù¹ ;úm1¶ßË–e›–pÌ#þ ´w0`Áƒ 0/¡ƒ9990`ÁM¿G 1ò`_'ÿÄV !1"AQ2aq#BR‘¡±Áð03Ñ $Cbráñ%4c‚¢5Ss’ÒDÂâ@Tdpƒ“Ue£²³ÃÓÿÚ?ÿæ32óoÜxU„ˆ4Ä-Öä©? #g{Bk#—R¿Ž+ã`ø©„߈K¦í7ãýàÎ_½xÁ¢?w8NÒ| ‘p«jÓžUãÐ~â¿(þþžUÙ¯ò÷€Wsjü£zeiàš´­îè¦ÑüÿÀT$8ó­uJØUº znúSz“³^CÙõÒó‹Jü «Æ ´qÙ¯ò…[ˆŠš4¼‡²-€*å³_å M¼Ï¢¡òƒúð]=(å^è¡›UÜ|в÷C‰3)ûgX JŸÌk°U!ÅKíÌÚ¤¸‚’=F 7¨iDˆS ›Ù:RãKM=¢ ch¬k¬øÒ^Ó¥Uc–²ºíPnÄ%j8mS¹ˆ0•iB¼ã)öêÎ?ÞLT&ê]ÂIÐBTBE=‘þóG>¢¿(Ú(ÞŠü h§1Qä×ùA>1 §&—ùA&eÀŽ -(Ø"Ý»Êí ª70³¥.ºü ÒpåÀ²¿Ê+ãÓBvKü ]:S\÷šWåxÅ<¿ ”ã*ÿô—ùE"+ÿ)öÅ|dŸþÚÿ( ÅÈW4¨|£ýä•w!_”PÍ*¹nì•Xi=%J –U°T—X˜T¥3K®¡2Ê%=ùFósˆO™G(=Й™GvÌ(Ð*„|~âuמWFÃPÝŒ P¬oÊ0®w6 =‡³½1Æx,K¥Zç§Ä'æüèmHS-˜ U}1C‡²¹4·´Bm¦¼;ëûOK³!5)mVS‘†™ -‹Âü£Œ­Nö\aFNA´:Þ¬­=ôã×a,~¦ïE ZB«O„aÒjÂX.NÞ”Úh-Î0é)œ1šÍ\¬¢ƒŒ!Lx6f$–æÈNa§*ÂO@–ÈPU¤å2Ie”;.ÐuÏ$J{éòéÃ×0ÛŽl“2©qe{8ñ…MÎËË·~ív@©ÃÈeSKx³bëM-š–Jw9avaû‰Ý¨‘¨×°i O1…‰—6)a© U5ùC1ðmì7jª!çå’*}Q‡aÏa’»i²B XM9Ä—KÃY@}{$­,&Ôÿ ÅáR¯-JKhBZH¸Ÿå —WƒŽ•]jk,UêõDÁ^æÚhŸE6žè3øCæ^}ͳšˆçÛ š[Ž.}F“Iw"Ú½ |ÑNK¶áæ´mJàr³Ó3…I²ÁZVp™Y¬_²ý“­#>ì« À|–\«JQ/<Œž§¢#¦xŽ\cv×¥ÞIæ¡ì†±i<6Zbmå!¤%ÆóJÏ݆KÁöé,àh¥ oóì‡cÁ GlyT„‘i<Ξ¨ VË[T«D]ê¯N…àr˜† ”ÕI´4;c››À$Šå, "@ûh8n~sÁ™A ò€É“¼#Î?²|eùv€Ú-éjsÒ%8sEOL†K’ÚÏÙj0öCòͥ­ŠiœdÃCü¢&ÿ‚î´Ûj°*Ų6°™tž¶€AÅ  à-´3OŒ%Ô6p3‚¨<"º~²þŸó÷:3FÕ¤ƒÚköc;\mì%$#zY$—2¹F-"‰ìG Ú¥Í$„íÖ%¼u›[”˜R殈IÓÛò†Û\˸|¨“¾Tf:ÙR&hOL–åu¢±`¸$òçñ‰,s ˜q¼\\¥U}|ýÝÑ:&Zu•t‹­yΙ˜ðv}ÕÐÃÇhàN‰Ýþ~øÂå™x.P!m©î¥ Ðû#©FÖ\lPYM4'»èĬ¼äÏDy„Ú¦Vƒ^cA¦8‰RÓS‰m…/kw«HéRw+ uÄ_.TwõÎi•+Hð”ÿq_qáB­¡3”÷}ˆŸfN]ÙÇ^C7¸ŒÍ{uá È'²ŸÃp†R±ˆty„¶š©Ô;!Ë=#*êe%R êÓUÔÂeÛi†°T7å**\ýÑÍ’†˜Nëm‹kÙ92RM´w“,åÁjL+›–—Ù d¼´¼jT³À‘¤cX‹%V ùnM`p9d¼¾.Òg¥LÉy W]iM´”¸õÉBô©0äÖ!''ˆJ<»ŸvU¨pÏ.íD3=+‡K‹Ð´§œ4Þ!àì³Í93±e.(“_gkÆ8l–/2þÉHbê%%:«¶$0ì' –TÔÓÕVÂÀJu9ú¡×pÜ&IöäŸS-¸³˜¡¦ïª&¦žÀ¬ÄtÈLË0ª¥B•­;`HœÂðÖÐG•Ó°H–ZØi´IÕMeDµÎ‘13…alMaì(|I¹ÓÍ<#K2,¾êœ/žWUHÕ>ºÂ¤g0”ËK·]£ûÔp=ÿc¬<¶œ©'”Nx<ûJ–ò’Ç›Gò¬ìZ_lÌšJµ<{+œ1Ñü `Ž,ìZP4&šèc3òX{S,(¥µ¼E]¥F|8D¤äæˉ3BT0‘äõȤvDžY  ©ëÉÒšDÔîÏi°EöJÂ'Ø—CO!/9oˆÄäP‚‡$JB”t58¹Ì¿-4‰b§B´šçþ˜yhe™™ &Æ›U‰VðЈ@JlE2HáÙM?‡1pßR­¤Kô >ùi¶Œ·,ÅlÝÎƱ왌NuÛni»B”æmöF ÖÌ»N¡ ›ìÊÀÓºÁ‘&1Ì´nêdtœr‰|:M†¼_†8‘NTî‰o žhùÕnu(»uIÖ†&ñ¤àM´RZK‹'iBsÏH3RâQ¬)ݯF•]£JmO¾1)ìJFùi¹Å<ÓJP^ÕJà;©™K*Ã%å–‡Öw=fÊ›šm 6¶{½ðÔ‚1T:¨¹Î?Uƒ3/ŠLIÎ¥ hZàEür…È8óª~{?,îú ÙCÒ>4•Ù¼6{ަ±ÐÜq2r2-¥¤!Ú_1—_øGÆ eÖÞ¡´Øª€bw"?^˜ÿùÜxPÝ4œ טû0§ß®Á©æÜ]N•ùÃrÒÍ̮͢6§¼šC—…*eÂoÚF6üËs&­¶ÚCMV´𓘔Tt[wh„.]µ=ÑÞKËi¸›N¹)y#<ér`Ô­Fìè4Ò‘ƒK¢ir¥çž˜ruJÜÆèyr«ø”áEνp×XÄ”B–éu<ª+­JeæZ2Á³uG×¾0ö´ÃzFÚl«Háë„L+™˜¨72£ºµ:ÀûÇ%DÞj£N³^p†&¥ \›‹BÖpˆl§h‡–æ;+¤xHää«J´ª,*'P8Ä‹‡yhÄ5皣›wdßGq*Ó—Æ1†d\+[M¥J½"—ˆÂ到2íî×¾<&3Ó•>œ‰»­Ï]½7Š#f¢nº§ß —iJw[É}ç³<»óî1 Ô¹c£”­S!Ãåi»AßM²DÄîåGšž?”Î(Œ9¥¶…’ xW/„L‘…áLùGNuÌ“Ï{(Æ<"v⥻sc]Êï®F1? g|¬Â%å‰Uhª[îÊ&Ðò‘=Šâ›x·È“ß<ãði¢jaî”ê+B”>¡w²0ï°ûÓ†È~(ê19à䣊L¼ÄÐʾhΧº¾è–”èÍ)©à EiÛßUô°q+¢¼þÏ|xÇÄ%T̹E¸x@,­5ЊR0ùÆðÖäžšu®£iJ“Pk !‰d5åÍv-„•RÚV$”…$ô9ÞתH#?ta²ŠÀÝ~jµ\Ò¥êŠó»ˆÒ?´¤æ'›nI6²Â ³¸òÐDËXn1†¶$>Õ—’#º·‰÷»ïû w¿jÎ\·>Éé´æ¦YRÀí¦PÂÇ„,¹=0”Ù!+*•æF…\"Nô¥ôÂz©Mz¹¨òÓãa)Úì”PʦÛßí«³n„”ÊKË¥T¯ Æã™TtI¶R—&æ;ƽ‘)†àH—no'f§RÒNÍ®^¿ËœOÏ0Ê:HeK*·5Ú2¬e´IOJÍyw%Ò‹TOWÎ$ØgmåtrâÜy"æx[˜øDÆ 3(˲ròâ`^‹¨~ŒtŒ•c J¨¹°¼;Ò×. ¯HZÂëiº”€â%[ Õµtn¥tËgÌÂ]g+’s‹ï¥Äöa–eÄ«iOà7²$°†%Ãnõ§fÚŽq+†»*1'š©r•µ®÷~Pë˜Ji5£¡°‚;Ä5ŠLV]§”¤¡¾²ÔBˆ Í!…Nx>öÒ³nre”¦§…OÄMÄHT9QLÀ¬xÕm7Ó6»7¤×/ôÇB‘‘sœm5},uZõÂ&”\’É ´£XSN¤) xˆÅ0WFÑ2”&í Rb³îá­JŒ-⥯h ‰VœÁ¦%¥§kokqÓ—tHÊ+2õWkI"‡€‡1–Ú—ÄMÚ&ê“•=ùÆ1&¬i-/.ƒ0ÛZ¸¼·råò†‚<w¤³ššK¥Tï†qIœ ̹™LË[ã†q;†1†´Ê°÷ö©9S®" Ku2iIS‡4ª¼=YF)‹JºòÜM%­sD¤z=ñ5ˆ8Ó隘èëI^êìÐÒœÙmÕP„·Zfaé&< x38°—Ô—ÏÒ#׬ FÚJe‚,°iHÙ<Ë’ò2î‰bŠøk߯ˆ:Ýè6”=0v†¼)ÂÄfÄüÓŠ-?8ÈCi¯ ý™ÄjÙ‰žºêMbQ÷šRqYñ²mÔÔÃߌ+ éuÚ+Š}}b~®’’ê3×4ba‡g¦ñ>ŠEÒ¯.¨n£!ý!¼6viV2âWVRlI*>Q#77:ä¨$4Ã̵ü4„ͼ۳ìªMY¸’sº‘mÅ:†ª—®µ‡q=¢öŽ2-ù¹ý˜Â‰¯ö›ÿ/Ño„ßñ6Œû,û1`)øS ‘Áð¤b«¬„ÞÒ(–•LÔWÙjYöÙ8l’dËÓ)U®•'x¦¢1œ6yEµÉ³}†Šªm¯L–„§Å` –¥Š¦¥]†:.0zJ›fÊWUù w|¡+}3³;îs€ŒMN\ÑÕÔ×HÃ0öÜÅ1]•,e$w“Ê0ÓŠ:–WÑã΀T ÔU”xFö.Ãh•ЮŠ>±”I´]P˜i„ Ê¥£}Ã"G‹–YwÅaˆÅ-%Gv'°w[V ì³V^Πµô÷ÃI\ÄÞ-ˆ, vi(-å–UálÏ•t ¢ÍJEM²0¦ðÛÕÛ2íOm¶ŠNY]^Øñªü%Däè¦É¤—G³„x"¬M x½,´8…¤uÊy{!J•RÓƒL(*Êš6±õ”¦”X­ÃŒLwS† þ û1Ä=0R†ì_’ÔÔiXð²A²§];6YæJª¯z¾¨co†¿…IN0™o.’П¬ücÂFÜy)WŒw+ 5¦oõ´#É(zÃ*›œbbQk«¨$ngÙÐ&^q2Rì¬s|Š 9Dƒ’óm~;.¨PÓª¾G*ˆN#9á$¬Åª¦Dkwwò¤JML3°yæÂ”ˆÄq”©hc ™e„(wï{þ1-áS•>0C 6SS`ãñœ¡Ó{iRhAn‰ ÝeEWÏlˉV‰ÐþPËRl˽(´‚¼‚ªi{c c ˜dç›mo¨4”ŒX‡Ea[{YJ5Us÷}³..ðÞIYF¡$ÐÃ~/[j—NèÙðýöø@§fYbâÎKXMhç3òÁ=®¦Â–œo6•¥YwC(ðuM¶£še­E°ûr¾O>ó&Ž!YXbfEéE´¶Zmn_šj±Zz£Ùá›Dµ=ÐÛn\€¥žÜµÊø(¢ÒIQ æÕ:ÓtèÎ1Q0ÒÎmSœLL¿,†ð÷Id«¬¤sW832h“uMmR¤6—9Œ¹G„"i¤;% ÚœjôP‹uöÄ·DÂU9Š­”©Ùyؤæ—ï¯ èxœ£{W˜q¤í9äx±fZOHm²yTq¶¿“c-Ë,¿2æ¶ù¡'C¤2‘àœÄ”ÛÙ !”¤¨òC‹™ÃÃ@"ÀñÍQ!:©Ïb.ùC9ªœaeÏ&åº`Ø»5J*Ÿ½Ù™V”© E™bÒ†j5¡ó¬0”K4ìK©Jp§!¼JWj]J™èêe¡i q­3áíb8s˜v ×àªq#Ɔò[J]].]¢ªåÿ4]&»Õ"½ÄÒR(€E%ÆÓ˜òRÍkUÃÎPÌuÞXó–a á˜cs)ÄŸP{ln½jáñ…âx‡ì2Û ä.”UiÌt7Œ™D¼óA;-¢hSv°p|NH«q íQ IVb±‚È4Ú‰ÒQPh”¦¹A𠧤”lË€o[ÊFeÚ×öž²2’3Ž1ˆ€²ÓGÍz¢q©ÜÉ,Q DÙ räîä“þ¨Cs¬Ð…^›Ž†‘k [ÞPªaÞ ÌÐv寲63 *VKn—lrÜõÀ᮵/_ÅÚ—:C3o¸V—ÅZKc5ÁjzMæÄìŸ ‚Iãê¤Mã ²§ž£à_»ë× NLáaX›À/Åí¹Pß.À"{›”T›2†•A¸/°F-Ffd$”ê–ꪥœˆ>Ú B0Ó,¤"RÚË­[Ùh¬¹ÇC—Ú~ëfa‣¡õyBðif^˜v~›VG”Kc÷¹ âfyÅÍJ°ÚJÔ†ËÞ"Va´ºÄ„³¦h…/;“­üâG—Ø·.à™CàpývCˆ‘j¤$)I°§/\5…ìÔã«d¼¥žØìŒh!;/×Ü«v[³Ó/Ñm«a£t½Åe;ÄÖ‘zÛm)m:”ii Ì¬¸,5DÞ£©îŠG…öW£þ )9<Ó¾I䡽òŽ—tN±R€1²‹üò„¤ü+k—ñ®"Bê•-uëeœKÊa®-¹‰äÝ:†ú–zFœá¶[¶ÚBR9¸ì»mªbAyßŇ×8À V3.z¹å}ÿù£“Äî’uÉ¥¼‡T‚Rã~m)î-$ÑL’pÇ%êÅ¥åu«Ü"WÁŽã²!¹Ùp:ÂÚFTÙFé§"cÁ”-V¶6ª :S!<nH´™£2vjtd !­¼þ–k½³iE^øÅZé#ÆŠommk凱\ À‚Ö$ˆÉ¼)Æ<qGyüH¾A ó‰|7 o¤­§.rjÓjñ}p„"¥VŠUF§ìEõvaÓc2íõ–¨{Å”—1'rJSÔe“0=¦È>ú‘w,µŒX559>ó©Gã®ú± †pù 2qK¹«Ôã}R brs-_,ü‚Iמ þÚY•)Ë_šéîÖ&'ü6PVD;‰ %J*˜é½5=sN³Žœ!U‹ÌÉ*fÙY„0ÝlU3ã’bDx/3=36Úü«î'g|=:±z“º„=\¢iù·\üÓµUˆ¹VðùÆÜòÝo -%U™Þ÷RÁpi~•0T6A¦©²ì‡Ëbi[²Ò-!µf³Zr¸ÖƒámœFiÇAI 6‚>© á’K§–îàÏÙOUaÙLJQDZ•$;[2ÔϾÀpʵ$Êí«]\ºËË”Ix)…'e+.A}IÑù|aJ“urìL †žͦc¼~Fuä~»0/p‘šy&y ¡=ø‹…Q%†.ò—Öx#]˜?^Èð¢BU$Ë)§&e[xPÒ™gÂ6Ì©SØTô¥Ö€<‹ÊHÃXÅY_DÄdäÒâNUʶºCøÌé/ÎΤ>µ6Ù¨M2@\Ø?/½m¯¢ÕF:5tb »î0Ýj¶Ô’ tÄž•(K«}â“¢D"^Y½“IÑ#ìqM4†Ô³U:Ý𢔄•’8ÂCòlº«ÀR¡ç«Ë4Çü¦Â~;4Æón¥;#8ê-Úþèî¤HH<„¦YôU׸¦¹„a¸KÒé~WZšwx‚"Qœ/^,œ3&æC»6ªR€ž´ –0OÌ!Í‹Òî¶ã=÷C²r> “kèî…:1¤b-Êì%äÜèÔÊÕežQ.â%c³ÆEk?óCx|Ã7OÊ&öQÊêTÛNqâ‹ÐKíÔïç¤"m¶]s ;äf-A¨ÙJK¶Â9 kßöIôü@›hê¤ö˜¦2ïKÄõB<Ö;Û,ûÍ—UÍæ˜Ã¤^gÈÎU!òr åÁ"åµyq<™§°F?0™Vë‡ìåÚq]gVxÊ´ŒšÃÊ¥ß&³»¢«N_T‰L)æ“3':Éy÷/¢PÐó«õ¤l¤ðgŒƒ"Ä®ë@N\(a1‡6ܬזغ8º4«R÷k²@MbY3O¼†Y%[6M.% «mWâʽդ;;ˆÉô´Ë€RJÌÓ#bò’ EÚݪÈ=¦Ãç6å’1ÕrVq#‡Jáémé–J„Å×(^|ècTƒ]ôLÌ×ÎW>èsV¶Þ›ul4츹N$fM2Τ}ŸzG^¤n—œQ%Uáž‘:‡°çd'Öý«Îþõr‰Ig|}æRIH¦Ïêƒ(Ä mJ©i!G3Lëë†'’žÒµnµ¦pœsS³);)¼'H!2“¸rÔ’ÅÛ*m’°@åÇ<àáuyùt/ÑÃd­@q‰I\}‰!Šë”z&„Š%"€}ž¿Þ ø}ƪ› Zrƈz­ŽÅZ)÷IMí } +s*F‚N¯~Sj‰in A >¨—eɦå]•ªC˃XDZDŸ;³B"kH¡¤xFÍä„Ï öÿHð/¿4Úš|†WhÏúGƒD(íun‘ß*Væ'eVÒØxúTùÒ&Ä%Úé=fíÕ"šˆKM6–›NˆH £c³!ÇŽA¦·•X7ÿcáüµubÂ]-6¤ÐÕ{ÐíEK€?âV¾è)W„OÙ2x„ÿŒd§wµ'} û-{•;vHÖ£_®è˜ðµ”þ±$¶ê×-NñúíŒ% —\ê±,AÌEÙF7•`4§º1<’rVZh©ùi9¤‘äÍj=TgZ˜-±vŇf*Hi ­2ç MÌã¿0”ËHÊ ’( é^1þû³¢ë¯e}Pûòˆ“D³FÖ¥Vj_ëÃÛ“ŒÕêºäРÙ~ç?t,®y†É²òCÓq‰¶V²ûˆaN/USœL6ÔËN8ÌŠ6ˆmAE;¢$ Þ'j™÷sC+iÇ…xž0œknµì'„¡)!;5õØŒµÊ“Åå‹NNЦJº{ãÀù.˜œ1hiu˜Qê.‰'”Ot ]ÜViÊ)⿾0üÒ3"ÒyA™wÂGç'YdºØBV[¯£Raì…î*¦Hv”¢ª½Çã˦¡LL­ ÔF+.qéÄØ£ÁT4øÂ•ÆF'áÊ)Ø´Ø] ­jN‘ÒF?!†6òÔ¢,«õ­ Âf—Š«iupÌ¿º>á ZHRT* t?g„ƒÿÌ}ßq†ªpÛÏv7,T—[ÞÜÖ¼á2óŽ4Ö"œˆÐ9Ú?MX‡G1H°»Ù ̸Ê0Ïḡš!S/aèSª59¨{«e†ÃM# ”Š Ù6†¯7*ÑJ˜›™a»_›UÖ%Yè¡,Ë;·mš_ÌÄäûa]"jÐáQË!Â&1d_ÒŸMŠ©ÝúÊ$—Òž`K»´±•P9Øaý²†Â¼g.0f'Ù´2Ê.ð?#J‡ŸVéèbMÙµù¢™lQ´Œõ·3Ï^Q¶yHÚë·~…döÊ6Ò[БÏíu÷•cM‹”L~q²Ú6rM~ï¥övÃ’²òéj]ÂnG:ë »+(–V†öI#‚k_‰„Ì8Ëj} ¥.‚R8Á”èÍtS–ÆÁo²̾Ëo fÕýä\qm!Úo£]k2m2¢™”ØêÔ­õyLtgl·Òó‚£¶g-ÚWç“α%#0•O"T‚…=­FÃóÍ©àÊJRÕôDäå[—AÔ6+ æ¶¡“T‹©¬tá²û5kTTŸYŽž™t¦kg³ äžÁÂ&ß”gd©¥^åD÷zâU§&ÃL¹z‚jõð…¦VX!K*vµYõÅâI/»é¿¿î9Bå]eYI±Mp¤!¦ÒÚ©€û<#È[Óþ÷mÃ"µ ×÷cxW¿Œ:TÑIY®ï8!—¼g*ŸØ=Ë¿èFÏ•sÒ)¹0ƒ-8Ë·è³ûõ¶\ ©n 'Û •–vÜ=š6TŸÚ«ìÊ6’ÔSöܧwXÈwÁ3N^éÍO¼h*nB.¼Ó#;íåqãf]ΑÁ._lt‰Ñ¼ãªS]¨‰i‹ƒ3 C–'Í„€(@ý±N:êZJuR´ŒÌ˨­…Ì‹UΈH?¸ÃIÿŠGº=ÕHÁαE74‚T=ãõYÙ¹Zé³s/a€ÄÛvŸñ™\ãÊ3"åºäò˜Xÿ,Àü¢ï»NÇS ®þyþ"LUX;Á<àŸt;*™geÝm7oæ)Z@ýJ‹ïpe‘r£õÅ!”\Zp~OìøBÙlpÖ¬¥G5sõkÂRòà5uåYÓ¸˜ßRžYÖ¹žP§ëH!%À7S íˆjA pš]û°hX¨ ºƒÕ b^i.f‡[̶®Üó2Q9GÅ|Œ¼ÛòË×y5×âMÂv"ЮP¡ã&Í3È(ü£,Bÿái”-õÄ2a´~²€µ[zÙÊ™“6¿zRš¦ºÁPš×:y!섪ä.œ[9@aišAã³ÿ´ÆÏÂ&eׯßÈÀ K+>kûŸ vu†ÑÍNˆÊdÌžL$ª~KuÖ4[Ë^Iï¤4Òð9£‰¹ maDŽÁ B0I•„*åЧÕCì"tʆñ.!CÆØ£’3­”~qøsDö6?8O÷„ÝÀ·ßùFbeC±ó€¦dæ•¢žþȤ¾6£ûÕü£ÈJ5"8mFðúî†ÜÆçW>¤hÊU¹ë‰ô2Óm7Ò—ºØ§ú#˜ûpò ƒo¼¨:ý™üc»íåœ ê#-~ÍaVëH`ïGÚT­Û5<; =ŠÌ¦f@Ñ*(Õ½¤8椪aMÜÊTÃl»‡¾&éE§ÍºL¡®ê‰º¾ÚA鏓޴f‘j}‚Á»O>& Z¿XXSª­J»!NÍ„¸Ã`¦^\ „œ!ÅIJºÒüÁ΂öص"ÞêŸp~)ýØ=„Š A¢QÞIÖy ÒŸ¸\µ-2—ª]—EÙi”>ê%\—eÄ­ÊoAS’,’r&ÞUÑì'P•k %…*ì¿Á šõÏçnE¿]LQ­6;:Ã[Š-@çlíA¢™qUât0•¤õ…Ýñ‰½" ™“¨-¸30Y„¢¬Í¸[|ú þ¿Úä¶§% $}q¤J᳸z2JÛé(ÉWù±ºãÛ"+Õ]uÿ¦ÚPZXEµ@ëŽØœ“ný‹ïHÐ}SÙs RÃÂ`\N½Ñ‰;-ÿˆ›­hŸHÔzò‰Žâ›pLªÓBáb´=!ÃÑ¥xКű7’ã 5`k*g¢i!ù w¦RjË^’yiÂñ4»Ò€ H ©0D»ÊSYn%&Øèê4j÷Ÿ0y¾³†&TLÄëŬƒ˜óQó1šªã( HQë¹ýc ÂÚ&jqõf •øcU|c²¿ß¯}Ü!dÒÇ’~Ï\oŠAte¤¶œãHRMÈ:¦*FÓ Qò™æß ˆfq+óîOÒÕƒG hC²9§‘w}¶Å¨hõÛ1.¤Ëƒ¸üàxÂ~¯À—+ߤËH“ÏQ¨ï0Sí´ÚAÍÓ’Gg8è½%n…š©aXèµËóæ_ê s¤1Òæ•¸CaJÊå@ŽØî||£ð]Ýf/zH¬n’s ‡432 ©µŽE#(viÔßÒƒ!  B×y¹Ë’žËu‹fµ ¦èž Àò…µ%À£»¥ µ*…ºJ¼¢6ÏÊ8Êt©M Úk~ò®Ey÷ø‘´Jô®ZöÀMj§S® T§—Î.˜tìR›Ü5ÌŽC´Â]ye“fâF`'²6l\AÚXiÖøCèPKŽ•^ü¢e…¦çÙAR=D§R£kaw%TL¸Ò®(˜›DÌÁmÉa{êp֪ѯu}±‡„´áHèÒ§0Â8©]°Úm·©.ÇüEyË=‚Ë.í^/ÍÌ'*'ƒ}ß\#ª«Y箚}úd¤œá³Ø>ÙXåǺ+ z¢°8Fµû4û ò…%]ZC®¥*{r›idù¼.œk VÒAóE£JÎy¥U MGØCe(´µ~Jq^hìag.·]åïŒ&q²ØÌ#õBëˆF®/vµ…ô‘10î©—kq*ïTˆ†¶'ö)ü1ÛÌÀÄ^YfÏ ˆ)ë„¿<øDÃIò ‹kÝ ÅEâeA AàƒM#¤ô€úJì g2LL¯D³©£IVInœûá.2 ¶Õ¢¬6ž”Ô¾*Ó‰m•]½S 4‡ÛžejKâÇ™4µÑÌ2‡fÒ|ƒnl‚øÀöoD¶&â’ɘqIr¤ rkÇvyi¹Ë56ìíœ{ ÅF}ýâe[N>vÈf…÷r…´â ^ µH=Õ›-¸ñ:&´0—j‰v.¨)ZS„H"MŽêŸ¬ ò~P¼6Su7+¤º3Ú9è÷ #P±~RÛV­àO¥®¶Àù?ø¹î§Ú}в0ô’y*e|t%éׂgÊì—×}ÓçS—O¼‹±IÓFÚô•«Œb8ãÊýY¤ìÒ}#õñ‡æçèí8LÈmΰ@ó£X7!SeI_1b~âd‘rRR£ÝX—ím?×íÌAÊ2çŸiçHýUiC¿½?,š ï[yŠMFm9‘³*~p©g5d4‡%ÖVîé#£+2éÂYn×åßYSK¦F°QÑš»Ž°Re=àÆë +°Ö°Vh‘ÀW8þêÑÇHþ䨍ÿ‰ò5N¦Ž(Û̲ã펬¹]o8^ÆOfU¡.ÖžªA´lôÌgÆeÊV„$W:å R˜y Bl!VÓ–…->LÇB)X$ ”驯ÁAÍ)Jj²E}¡Ù49ch¦€šú^ØfeK[s òŒ,Š•a–¦TØêèo¸ýg˄զÝñ¸S„ Û¡$ÐÖÊîRü0GÊ.U.rPÒ%&îªòóN¦Òî6H@FÍfÚPðöü"[¦º§q9›…ê¶žf›–˜é««oi†¤$ÛL©x\­ž[±=76«0ô‹…¼HÖìûÈK kvF\&»êus …OHºÜí7ÔÖ ãß 4àJ^J® sÉPv(zmß(Þ(•×ö„šD¦Âöó®8[‰;ªqZû¡·)Z ¦[®A#$Ä»¥½õ2YJU£5Ôû!™7úµ ò;·pIçjœ[ªmÆv`'v‚¼=‘‹0Ë›D6ò@®©ÜqˆÍ`ãJæ"ZÓQ³±¯Û‘ÛöeÔFQÛŒýð¥Ù²t޲GÊpòJ“Ïå Z 2ª…2÷ÂJœÈòÂV%ßqq̽‘$ •XÅZs-4‰¶¨z8 ¦ºg¬8™d²—‰Ì$ïUõ0›e+L° ‡fŸ í:½±‡¥,¬Ìï¢aí¥ÅIáÝ ‚1䱓GE¼¾?¥á iã±Yö}Æ+ÃÉ|Ä$ÔŠ{ ¨ Ôë¬=QÊc "Ú÷ÂAPò¡GɬW^1»Z Üàe@s å[ÂírABxE©»(]Īß|&ëPóëõ²TPr?:Ÿd6й"Ôs„§w#•Uð€ñ¨Ý ]}з È¢R”¨eõÎaDd«tìü¡»èš+<ƇX- šGœÛ)­?ŠPƒu‰±ŠOmaRFqK¥«M=PY™iM¨¢P­L(M?¬ ”SZ|¡£uÊ9¦™ÁUdæ5†Ên¯´Š„êmÏRkÃÙj”š Þ_ZFí®ZJ¶k5öÅ&p·Õ0T^Šûb¸™`4 d¦žèâRÛË¢na§; c=?Ge6ÕÜ–œ”#õ †Ý_ÿ‘Ð’%ûUëJr]ËŒ…k.RÅ3B© ˜¦h9r­xĤú’6«ãΨ1&ÄÃ[D(Ñ_ÄaÅ0„´VµšÃ“÷4rtùÃÒ„áx£j–pzRºŽç©„­µ%h:)'#ú8Òi™a£îûŒS‡’ùˆ *TÂFéO ¨è#…>oªÕpU5åR†…}ºÇ5™§v±k)ݲÕ+JÇfýÛˆM7Gl!hmXÝ%äñ‡UUšHáê†Ä–¤6‚wŽ‘´Í\HÖ°»ªSõ턹ÁUÌÂMér uu íõB—b–š[Öíí‚Ûk ZFT:KÊR©jZQ¦è:Ôs€Ó.¸ýÛÊ%t9BöŽZÇâ©)V¶ˆ¯J^ÌP%;QZds÷EŽÎ.½Râݸœ¬@(‚7›aFïm!Å=Ҝٜ챇L³ÙI‹Ç¥3Çþ˜ØKᤸ¥Ô¼jâ»Ê„aÏ1[†±U¶Ì¸Q¶×–”Û¬!FbUÝSäÜ >Èudur­~¹ÆH-^)jM*bÇ…¤ËúB–¤ÞkmU©<≦æWBvJ̲”5€¦f¦GbH>¨³m-›‚vÍi륧¶5ƒë[œdpíí\˜mró$ÕM¢ˆ€J¬ š!ׄI¢])re-³@k!,ãO/ “»4!µgÙè¯2´º–Ô2Ð[} ó|–+ ^ˆ?%n[0wL[ãT’“zv€æ3¨9Ãe!Á˜NŸn-Q’å[:r§ÜbYWp|D&®aiyy“6Ú–à÷e];R©ÕnLüÓtöÔ}šRröBjEýT†ÛáÌÆÄ8–Ò›òÒè)6ÕÇ5¸5ÉY8K·¡)›iÛ ) FJòm›x ÂÂß+§“H»úBJ1 ¶«¹›F(&ñ²« 9ÿªYs£€¥A!°MÚå¯PJ_ \™@ÿË4‹p7©ëÁ÷Oµ.ŠõUu= ¸›Zš YùAi«Ÿ4¥È—TZ$æ h RYÌœ»!(\«¬8ØŠ‚ÄٲëÐqз+mj¥»ÓfCˆ¬:ËI}µ´„üa ¹‡¥‚¬ÓÒ'~0”·+.§ö›Å‡T»G êuÎ Ðm!]rÎU¦µ1Ub/Цì!:vz¢ç™þ%8Héýaܶ–"¨)ïÔG’ò†Ýå;s…:Ù*ZµZxëL¡.L©o ÅÙú» ”•)¾¾ò³†—x*Wmsî…7u)¨Ó× Iót&j»²ã\)¦UåòÓ¯`M?Kó‰•)„&Vl\‡NWqsãnN<©Ì%.€¤°»¡)fu´*™!Íߌb-âމÙwmBZX¶ß4 [X‚‰5¶ñÎ ”žR“¢R‡ˆ÷@K«š) ”­$Wœ?ãU¿).´€‘R“ÊÃB P·t¦ô4&ïgÜb‡ü>=ðËIžÁÐÐQM ¬f{ …bx-SÊOÿL)^2ÂÉ×ûþ˜Wöž“oþçd ¤¼³Ý¨RÿH» Z ªù=ü½NÀ!·—T¥*@¨õ®:C8 ÈVƒ¥ t"ߥêŸIûÆ7<•—Ï@ân>øAV,ÐÀÆÃŸtÌÀ:êT±êºÉÂðfÞPº…C?UbC¯º ½àAR§p¨enÍ9{¡³ã¼49Å-´œ½Ðã§œX:0”Û >4Æ -®JB~zÃi3xËÎêí.åÆ2–ÆæR¬¬.å_ìþ"¡Ašæ­ùFÓýŸ™ùlB”ïΔI`ì!cC4þhq£ÑI{4ROG|%Ÿ§Õ¢ ™ 4øÁ—^ÝÄŒì.••«Ò¢0÷rJ‚ÜHí÷Àé› ¸“jÊT£Nø “J¦[B‹iÊ•ÖR'^ª4*h~p6àqªhG¾*qWly¶gñ‹Uˆ¹jµ¢-ùÂã)Œ³ ¸ìäÓª¦Z\š¨—%NúV9•8””Ë X8”TwÂ&eœ´½ý,Sz§¡¢‰ÿ§î1?ù1AMHªh\&eV³¯4ÅD¬‹g…¸jˆ>ØÌËÒ¿þ!f~]—io÷BØÿLgŠÈÚ9ËÜOú!cÇåØÈÛV0½¡ËËJ8j]¬}ä°’Oá-"+þÐ5)tBÜÿh™"´ Xsá™BÿÄiyvçïö‰ à­Ê ãh¯nËÍeF<ž!<鿆(¾ â/(úLŒéí¿…OmN¥ºðå¤+¤àŒË§5$LNT¨÷V†pì‚|ì—]9Ÿ¬¡tœ.]µe{;ˆ §B 4þø—pOÎ6k›Lò~mIÿ4y\2]ÊÿÄžÿ× )ðr@¶¬î.Õ>Û£a‰l°©n"I ªr„Ž˜‰·I?myz0ién Jn®PáÛ—¬·U§T4®týæB 7R¥ žèP{a°h*á»×”%.ß&µ?1퀤(-'E'Hí"¿§¥c¢¹”«êÜ?ºz£ÔbG gÿ½ïÊ'žü9I4lÑO9ZfJp6ãdQ—O›ÙÔ¢h–’´­-ƒ¢¼úvWô±tŸFA5ÿ/Üb6”Á[»á¦%ç–Óy€®®|`æf^Ð}@öR½°¦Þ|m֛۵쾃LÚFÓ`Á>ñ—Ñ„ôvpÔ©I¾©”·Þ¨CªzEJ9S£kë¦ÚˆÃÔ)å*È%¾ÓÙ»ÅmWB%I öˆ[’ó#«P­3IW0\rg BÕ¥@ƒñ‡ÏJ‘9ob­ÐTA@["Üêo?t~:’F›¢zcÚoté—Ðâh¤’£—¾…).;0¤ºìÊÎñJ4OºñŸyBúfiŸw-3]D¦ŠMò„²µ»± )pù0¥û!µÊK:®–oh¼Ç™¦=°[[…éÊ^¦Z@e9f¥_ •± nÚó€û,õÅÍS[n΃úÆÍ+Óötݧ٠£-7v­µ¹k–6l$[é‘HYrux#ƒª´¥tõÓç”4*Û®un9T÷E¾1—®™¬ŒQ¹–V®IXû2‡¼b–çT±šÌ_t7ÐG‹ç”»êõÙWC.L…n¼Ø´Ó¾’yOÈVàšeND|âÏÀ›ðï¹2²ié³½[EmIíþQ!‰ÏlÂö­„´zÈÏáÓR'õ©c]Ýi ”yŽ-µ½Å¯%*°€ZMiØÆ‹ ½Rè#Ÿ®P—›Ýt´iZ¤þŽ&8;/κ[÷L2îÉT¿ø© ,€ÍMà  †8C©RV\;ÍÛ@i‘5ï…%W¾•Óf¶ÕÔΣŒ\ÛÔPV@k¡Ïëœ(6á×zœkÛ˪¤Ú: P’rÞ†ÛyÅK²[¥m¹I×*B\DÛ‹R TÒ“Dcg@2ÝJ¹åõXp7nË+2¯a÷BLªÊ @UÉKCCÏ]!@©†sªŠœdVÒ_•JkRûcá ªSTöÓ(ZT¥ßÖJm®f¤6»ëi¸#ª¬þ¿¤-+..Ä…Ð u­2çH[rl©Æ×K¦ÑÖué‡ÉVÊ…r„4Òh””§”/ b]HmÅVeiÜ.å¥`·ÐæÒQo¦ Z0 ]&¶.aG†°êvM¥°ÕmnãÝÆ/DÚI « è—9S¬´¥WS—wm帖–÷’S']u…”ØRõä2üã¤)%«(Ç{úCm! +Eµyi­{²…:*E7”šç¯gò€æÁù¹ZT´èϼ*›STS”%µïS²òm³1–i `ÄÂ*²3J“­!‰œ8–žqR†¼ÓÙI’Sïiz›We!JSe€Aµ-†î”TºÚœVˆÜÝÓH]jÞVV<žÝzÄH¶23ëKPœgy©ý³]^ì„ye쥕»r7@<‰Ž˜úæU–ÐÂBÐ…ÚnMÃO¶°ã.&æÜM!i]W,±uÕMI5:þ†$^IUÏ^¯Üb*OZÀ=âZŽÑõ 1º¼ $]^u€Ó>jwÔá§=tϺ*ô¹šyZ©F‰õqéM­W{¢  ©às5‚§’jxªs5råXm䥵%fí£ÄÓ^C3 ÚM„*·XÛtOÆ&…è9¥Ñq ‹Byõ.¥× Óå ¼\wwoôaV%Iu"´â#g2´¡%5ä¢v—“O”;ùœû3…!k »•-M¼sáð„ËÉL´úÐ7Š©íûaY6Ч0  ËͶþIµÐ)ÄK„:2A˜q´Sý\¢ˆa…††J}¤­j{!.ØÅŽP¡]T6TÚIã³mOu3‡J p[Òliû°·TRëIMJÁoÊøÙY³+¶ðMxû!#kbéL•™î5„$¼v PÇëŽP’¦ˆ6©5¡Hî°JQk€^«ÆzPç¦I¡à¡“©é„»c€:.±!ßß³tŒê=ЇÞB[½aªS˜¬<Øe½“Jµ)JkZqÊ%Û~sŨ"å•rtåô!.=á ÊP*^ªií€QáËÅ&âP“P=¢ Mb"awùÍÕáœ[ÑÓÍqFƒëº6êy´Ê¶¯(Þ\¸S㹦ìÌ7!„¼ê…Î;æ69wÃm­Õ>¤‹v‹ÕQ>™¤€zÄ}«JÄ´âP6a¢Iàt‰c½äªÎö´N‘¯Ø#¦uJ³å@ŸÑO¯ãöâ[ª&Ô€Îá3iMVð††Ð/…bIi0çËðŒEhgc¼”X³RÔCH™},©åX€`ˆ›CkQÛZí äŽ}°ØÛ6²â©j\ºÚöƒ—¶”^ÕNƒ{®m7@R´à() e•[åP¥çªB³‡d^KnÖlꘛÑwFeÚñ´ÀIvÖÛ$8áo23PW# JSµ#y@p/\4â¥ÌܪÅÉt„Žý"]kj^Iĺڜ]ÉîËLÄaS–4ÏKBÙ)`å}+Ÿ°ýžQÔ'ø# \›‰™Ø<¥¬¶’±§gÂ&[e¥Ë–‘¶T¼ß–hög˜†ñ¥¥Øiv)L-5Ra1¸ñW¦¥é@3õÁÛLM#“D‘¹Ã<ó„Ìbiµ¡¼Ìª‚oÿ9À@*H·T“˜:A˜“ —šoðÃ`뉆üfÕB… V¤86–С-Ò•æF°¥ÍÌL)I¢³`÷ç *mjYªäõ9˜KÖ¥Ô õ¢¡‚©FÛÚn[ö Ìׇ(zVþµ;Ä.ŽzйnÛÜk—Î6®íYœI¹™u§"8×” KÈMâ“DÜãM¯pÓ,ý¹G“Àde’• ””œ½füÒ%eh¡lîƒÃv•þ°ËïmWDT¥ÍËO aaÆåØZè åÊÂO—šª2:&’àýiíåž]‘”tB–ÝfôÐf¥}eꃔW! Œé]²)Xu¶g®¤<êèsõCjKò¥ æÉ犄§OÁÌg J¦%ySg§."'0ƒå¥^’kžîŸ¢öâ Ý÷ˆ±‡¦’(†åÆìXµ¾¦k ò+­&Qvö·”›A]¡jU3¬!másM°Ø<á ­ZŸSÊ®®&Õ䚌¨}PëˆÚÇ”Oß”K-ÇIu¥SÇ/ž9EyBñµK)Âë“>fÈêtNε-1°™R­»*ŠERÉq ÕÊé”5-<öÅ…io¥°¶±)3'‡¦dŸ ®ÕS|/¡<Ìô¡p¸Ûm?m¢œ´Ê&Xǰ‰‡¥ B‹%À‚;´ÇS£åL™P#Ý)ÄæÔIIZ¨}Ñ}†a^’™ZþP¶ðÿÔÑmR㲊 '=8DÒ%gøPJsb¡±þq?FÃ%Øq“N®ç 3Èq†Ô¦ñ'˜­i+T×ùktoe§_E[œ^íÔ{ ¯Ø§í.8M@⨑j|ˬN[r^»ªôUò†ÔmÒì¸ K©*O["@Ö(:¢QÏÊm¡ÈôEç­šä‰b‘ ’Ä5»©áHšIGFh#>p jcJÅí[Òj ²¯eal:8©¯¾°Œ.]õJ)õUÇR(£LõÖ¤¹Ž ™rãïÖ3™é´ª¯(Þtm–Ú›³;WæÖ2‰vÅH<! ÄݰÍFÍzvŸÚÁ–&UPäýáç I •ËÉï9g§”MÉ—i£gò³Þi”æh;Q&ޏŒ‘®÷¯ëX_K“SN.›7Trí æ}p·ÑÔØS?ᢑöÎÙuÂÞ©ýáÅLL¤$ï©FTôa©RÔ¸œšªŠ'±J©ës¬!ÇRØy;íöu{½Pµ¶ÐZœe.„¯‡»a….`,Ø-Jý¢)Dž°[K¡h¾Äì´sùCî1(¹–Âl'õ‹\]«l›®ÉWq÷Bš˜uoa£¬TjQÝu4­Ü) ³*êæƒf«m´îžóÄJÉ˺‘Öyê§ÙÅ S)tîþg>0ªJ=vi*^êbž0L H͇'.¯:Óá "rMJuj*LÄ»Å+?—v{’t¦Ý³™Òžÿé8¬â’~M$Ÿtm\Æ*ÒˆB+w ÐV•éˆqÅW}¦Ö§A>ˆ‚q¦¦»)—à íÈg»Ì½rfRD”ò̘m^)ÄŸ'"’»;´€ÒÙq¶[M­¢vnÄÃu0*áZIÜ$ªÚñ¦p†pÔ$^šRîÑö“DG›QJûâLÊÌøÅÔ›ÃIÉ>³œ.qàÌʈ©G8e’Ý+îAPËÛ[î))ýï…a+é“Q™X­¾ØI–!Æ›Fí§—×Î'^*ò‹s:ÿ/„@çÝÝÙ^îqc¢¥EÓ4p€¦6INèvTš/†ôg&&›sÎÚ*À¥BÙ•ØNO<š6õ.µ<=Ô‡çÝÌLRš%)Ò7Š˜•mY©TwzFL$™5ÕGŸèmð÷Š&R(:/8±Ù ÍÝa¡ Q&™´!ä·¥kE\Á–DÚÕyõ6¤kùÃÒ‹x)¥('­´Hãצp¢Ã)*²YbæÁ¡Ì.êD¼ÔÄÆÝ•¨´ê.µÆ\>’iõœMÉÌMI¨dÙ]ɼƒj“õê„!—Ãjh”¸uê}á5±Å¥Py¼{»àºS´L²EÛ¼s©çHž˜°-$I…/¨°E>Á™}]+po¾±î„P'.PìÔª3M&êy«ïŽ. u’‘ÃÙ^ô©ë”éJtB¦½ðBé-dàçÛصÜèBˆ·´Ò,iê•lÊ×8ZZ¶Ôà€({bù·¶ÉM7SÕöB•ÑŽÎÓB¨ö÷GGœ[Œ^‘¨ Q½P™fÖ’äÓn_GH™C8©cºá ¯0¡Îu Kˆ¦ê¸RFf-§«”T‡V3Í\`-Æ6njTÖí{øAWK] ê–àmnœ?âu{28H HÈú<þQxBP+\†¦$ðf î´$ð†ß•¢ y*º(p€ÛŠ][Ñ*ët4©l斂ˉQÍ9Ž©…+etúQ}Þséçü@òÖ:kráO‹.«Z…Q@éÀˆBVÖÅckj€Qîû‡­JJ\PBîà5ËÙA·•]6ª>h®F6ˆŸdWÍ[$EN#-èþ Š F^ºuáÓÒš}n$„¶Ê‚нîÑfZÑÕ **?Ò´i¯({š=B/?Ñ‹­Å~Ð.¼çf¶¼ZY ®äË©=Tp¬SHS.§z›ªƒÉÎ…—R½ûD¡5¤;-’åüàËŠq]×lÌ$’Û ®W-I#ÿÓ†Ñ,ÛƒµÉ¤8æïn€Rò&e^‚J©Ùœ¥¶ˆ¦òÛ¢†\øû!«œK…²¤šÆÌ"]¶ì*ÝRTåyfiõ”8–hÓ)¢RÓvµß[s†Ýw›%´*‰E8”§5*—jvUI;%%»\GeahPyÊ.ˆy5%BS&öbÓ¸cËJ­=–ñ‚Ëjo'$wÀ™Ú¸³f¡¢UÛHq·Ósj·½&V¶^ÃXiô:¾¹8êj´ð0Ìɸ’ ZJ—¨Ç•rcaJy6R‘\»a=Ì»ÏjJvCOm”´h¿&°HŽ‘/2†¦èS™ìþ†–›]Ìu”Û•§«—«¶s›7‰ü%„Xn¼çXªrËH8§éÜòéO6Ó Ü³éhžØ[UéH¶ŽðœFm L ŠÞíµ)î퉹EK–^CEVkTéô"VÖÑšIC£³*e߆յ–ÎÔ€8×.U¬aÀån×0GV•§¶±ÑšU¢flPß¾¼hw«ûŸp-¨=!ŽÞÒÂØë6¿Úve Úá“L*™ì¦*¶Éqb_Ò/Ñ=ѳقz•VÓ8ò nU3)!.,Úçªè˜–SVêª{xÃRXšz­¶,uÜ’â8c¤6%_]kµMªTǶô-µ>ØZSyMÚŸŸÙ­Òµ•"Ú§`åHTˆÔWOõ(A~Ô e{ˇ*ÖÞYÍ÷+ðŠ”f?8V Èòí¦¶‘’éΖV¶Ê›¡U7j3Ò%ÜZ¨ØÞK×ÔiÀ "õºæÙÄ“B>¿¬*m(JFÈ!: åzò‚mP'у³•NÏK&ã<ØTƒÊEm˜4ÙsÓ”2ô³¦eoí•çŸKí}ÇvbY)ªîUy=TŠ6ºy†6oZuRÿúám¯¥9CrI)ý0¿Ôߪ‡XÎ A_FPËd-˜sÊv@T¥Òê+·f¥ÖêöÄ»ø–&‰´#­(“~Tê“X%´9,®%ä"éliôò …”c 9É%4ùAÚI¡Ã~ß”s ¯“|g » œ>Ã>ÄókËvÈFm_»e ¥)QÌ@ÏÕg)9w ÝG²ú›­¨äK®[ìþŽÚ5Þs?œœu‰KŽŽ0ê:'7ª¥q'Û«·²—ñÝ@?\¡j3¨œŸsñЯùGg|\‹ ¼«ªÑ<–r ÷˜™D³…m†‚ã™TqQå©´¾Ê©b´Ÿ<Ÿ„6êW·š½€þ¾ójÞ®ÑIB\¯k_q2«ì-©'-MiO|?)6ÓjSê²âkS\†¦(Û`åVR¡ÝHnne 7tļÚ|­’J¦†ž®ÈjY·Ô¡J CíK%ÙrÖ˜RR²¤ÖšžêÃx®-=(•U>JåR¾È%Öf°zé’’…z©µŽ¸n9\º|bÓŠ:T@$%ôˆÚ¥å”Û´uWåð‡å6n¿zîMŠ-·Ÿ 5Ìåu.)qY{bO¦I5(•:3H!Zé¬[öZGHÄü6GÆx().$*©kaL)×6džºnþPÙB™@S«ZmÑ5÷Ø{®m|³eG28ˆ¡Ò”0ÔÌ´ƒ¸ƒ« JýДÌÊ.K lܤ¯¬÷gt ¤dûe0öÎòÉuIçM!hDò·Z¸&‰×.ȯLiÁ­Ã)zRUý®v–ùzál AiWî|à%ß›Ië“ÆðO‹‡70m¢šRƒ”í§l\Ã*¥JÛJš>èOŠü$˜–½:)ËÅ9çnWfyµ¨&÷[O+ÛaîÚšÙQŸl0¡†J8§}4ÿTºeRi®Óù¿Q’®¢‹þpÉɃªÒ.)Ø=¿]w[´øC5Jªy|biç†é)vë=ÐKÝ5ë”kW”}}°³lê[$omWC ÙaâeÍR“™ï0”ÉàR¬ŸH€xvY[É–Q'u5¯òÊ< R×™æ9ÂlpÚ(­™F«­XV†%ªpÐwÓ¯HrZJEç$ÑåR«WOïvvCs3l*RMUl°¹Ø‘ÀvÃ)IJ<78}ÄØXB‚ÊS¿ßÂ,µ¯%𒕝”6ý³(˜¥&M[T1(Ü«ê}I«†Úó:ýVyÓkhMÊQå –Â¥KÓf|Tªü Zq.´º’+ÞOól³æn™3n^Ø-â²M0ZÍeÕ'!M°Ì²«ç0½äî¼·ƒ- ©eKkÊóc22Ä´§ÅPžT&°·:LØÝòiâª7z¢Ñuç>ÓHK‰M-Y;•¡Ë?gt &P7Ñóû%‡¡XwVè!Ù ÐüûªS‡Ñl¨h`ŽŒ‡¡S ²½°óV ÜÕAïÊ6†ÔAŲ›“íöå ¾ÌÈc^n^êzÞr¡ì‹'°gÞ9ºÖu‹”œm_¾Ý) (•™XM+8MòóMÝÜËë(­ÏwvÒ6xv3<åößÝ kÂ&ÅûËUÛ,ów ”•ašuÚ˜Ê/ééYs<æ8Æ}¤ðiñJ¶x²fMÃõ XÖ$£È!H0‰VÕ!9.ÉÝ#JA¦" ;½E4‚‰‰©…Øè=æ(§Y@ ©I¹?si.Ž%•g¯.Ïœ$w6Q5R÷h;"ñ=uªÿŒ°å:ŒiUÐ6YÝ"‚àêÎpLËËJæ©nkõÆU«­UVßwõºfÚÀ¡§ß SŠÚ­CZu9ü` ”¸âzŠúúÊ.4ÌÐé;+*·’£ElÑ»MMk¬©”¤¯¿ò„®fgkm7: ó‹[m !5¢QL*^BÉ—“­s0×]µ/8ã%a<éôæo•é!;¼Õ—!XR6)e V@®€sÔÁmÅ7º‹e9+×Êž“‘. lTÕvD}¿*C’ó\ã*Ù›Ðë[½Õã ˜i±†7aÝZ,]{¸À[ï>ò”›•#Ô×”Û^N€§*×:å5±ÑýU7j8öCÌc=i´¤•*uó¢}R³¹sD 70Õí­š©9‚!m¹*«NRñÚ9Âî)M5„4„(œÐ¤„gç\ 6Ò„´ÎjB‚’)ži<Ä!©™­™ž±…*QöØZs+qe¼ÔÞëƒh¢R+^ÿ®P·™qdÔFtõBï`ûmÑ>1¶œRpªQr©M8œ%y–/4½n®Í5&XLÌž •Tû ¯a(ï$ ê÷R:$–b´éB û!;YL9hà…Lç !¬? !*?´­´õCŽK¶m±DôScd÷ª{—uÕ4%érï±S ¨ê"eC.½‡K¶¡.¾é閭%Ĭ° ªPò¾0q‰„äJP•$šiݬt”Ë¡Ìé ž¡¹¹¶æ¥¥[Q½–У]h"Å)RªùVÊa+•Ÿhö‡‹œnYÅ*@&($åÕŸAÜ›ÌÖä¬Âë.àîtç ;)šÿ΋Ûzq•Ð…Œ½Ñj±'³W~¾„5ˆ¨›EÎ%0¦"rÏûÀ‚™¹]lÞ1²½4Ó5®‘[ZUj ùg ô7¦¼MÚB¹r0”Ëø4R‘¥ÆØGNÁXm*;¦úˆ 9”Óø!Ie °ޝ¶ ñFbqiMá¦^¢Gy•Êw­AGYJŒ ’ÐeIS%H´=¿qŠUv ÎUåº.Ͼ£†I­±»U·¼±ÌÁmJ•v¹ôÊËîKÍ83Ú…ü!ÇßÙ©çQa4"›°¾„Ü hoZ”‡öÊÝeÁTí÷ãnâ”rµJ5N°ÌÞ:ƒ>ÈÙ¼ÜÅJR;2‰ùÉ·„ÂЋNc>q,CJeØV¬Ô+¬),+dà5¸'=D-¦±Õ0<ÐÙ¡†Ô—ÚRµ!Wgî‚™¦P‚´Òñçç®Q*§šo«Lí¥+Nj…ÀäAå0+—É­Zõ³‚V„¥è×ùÀ½.:Ý–ú!'ߤ)Ä$)KPPR³ó(ÄXnf][ 4×Wü¦?UèÊpyŠ@ öFÓ Ë_Z×d þ¡.kþƒd”ºIÿFëHÉcøk-“3Aµr™ëH£M„ªà¥7U }p€D´Ê¶™^G3üâ÷\)Ùõ¥á5΀vÄï!Õ´ÒB›K•¢—ËùA%I¨9CjRÀW>6òrïŸÞM`¨!ù_ùJŠ'›IíÎmÇ gK(~0¥æ¶VÐfF~Ø`LøU0ÐY©I¸ÝÝC '·•Ñk•õç;áK®-GÏmZúá(^;30S–C/„9‹?1•w+Û¤U¹ç¹:)ò‹vîR”»—º(yJ9U_ÒÒ¶]Dï| ªRARëÌ]ˆ)Y­¹S?¸Ä®óP﵊ð2ªœ%^4mÄöK® ÓOÎíD¿»XÍODÓ¾I,]Mé‚>7ðW TÍš| ,á“háºýÁ1³K32‹Ïõ‹j¡ zK—˜G.óKnïtJás µ/.óà8†‰½@vÂRjF@Dܹ¸"£ºæ-u£jŸVVÃBnÅ?nùOV±&ʤ‘1<êM\ì€Bße”¡Õ€’yÓAö-l%ÔSŒ>©yGç ¬R£eƒ…!º±­ÉBÀK™ù q…³+ Qi™™vÅeÈp…2CAiQ¢¸ƦL»‰Þ'j„öpìŠ7ˆL2’)›§_l! bJUÇGJT÷ñ†DÉuð´ŠùC’’3#Æ7 ¬AÇ׳¾r[¥Ì•ΦØÉâ*aCt¡Tΰ±+Ô-ò“*@G‹\ª¨ Š’“턜A)žd¤†ÔV²Ï.ÈmI³4þ{Gvb¼=ЗfCmTØÊI®ÿÎÄÏLž|çZ…ˆ(vTHK!E÷Ÿ¬£¡aó<éªï ›@ðÍz×lŸ—uÍBPà&ÒkG ¤›3#jFMÐ*‘{²òiiI¼( &T³2Ó2R ÒÎUî0Bh©:¶‡• ¡ùË•¶Ö¹^¸BÀ[Õλcœ‰@¡Úâ¿8« sV÷Æ­i'°A›vÖªºšÄÄÈ[ëeÇÉHnì‡põB맬¨úâZVcÃŽÌî¦]µ8DN>¥>™[+/¡švL;±@B\:¼nXGʱƒ]nÑÇvÔfUi÷H@pXªðþPÃ’ïK­."ïÅ (ΔÎäØVîï•Fœõ‹z Uè¥iüàf9Zëpüþ©¦اšŸ|tS°SõÐ;˜‡/Ÿ”mhQˆ¯ý0\L±q9¥´‡W4Ûʶ‰¹»G0˲ba *íêS.èé U9Ñh:¤ýŽb8 ªgi›¬$üÉ΄ÙâªéÎP*~iÌÜ}ZŸÊ9F°miÙ€vMðï0ËJ;9šÜîj%ÊçÊ•€„ËXi´)­;”¢õ[òÁ³»´B~;ºÁ[Ie 9…¼ÈQíÖ““(·`ÓA*¯åHò XiÂ@¼!5I÷ÆflS)v‰ ;¾¼¸eö¾U$Á[ç}e&…J¤W ¶•m!lËÌb)[+-Ô)4ùB‘±Ä' «Pë‰çX%rÊÐ]›·Ü’!M­RÈ /NøCjNÚÛFy\…ßd+ÉÈÝç 1^Êç¯FÒÕ˜´¦½ÑµP¾ÜîçÃèC‡h©wnÕ£CÛ¤\ñ/g~ò«tä”e^("äÎqÙé‡u†å“õÊ(ûJeJÑ7RºÀÚÜ´¨€§ÎyiíƒN(6³•mÊíƒÑòœ´…†MÓ–°‰œ^– ÃÕJþ(¥¤vAµ²aÜ>I. Ë#Hôù›Ði}Ç((œœ™˜o‹WdaÔ¦ri·–Ëi ¦uÏNÈX% VjR‹w)<³†ƒ/L)C1kikß­HMâ 5RAp¥6Ä«Rx´©¾ŒÖ—vÿ8ÁŽ"è}å¸Ù à/ÓÛ÷ @ZÝp6;* O²±GË?á!$ûL$µÓ\uYÒT›mâ2„¸Ì›m<7àqÊ|ŠL¤é·ÏÙçföï¨ Ã·SŒ+`[PQª–±uO|Vomofã Ù×8 â ¬§y pQ>ØÚ³‰¹eCn+¬yCªéJ' µ5Ä©Ÿfz-ÛÎJ9ÔT8Üö›“¢“\Ç>0ŠÔ§y\s÷@O‰.Ò  ¨}Ы0 C­ùAJððô—»ðŠŸaHôÞróñ‡f\q3m„¨t×Sb­ ¸R(‡˜p(ÿáE¡*㽕}±³-¦Òk}RT+–¹Ó3XIÛ%ä±jÑB·ºW$ˆ¼twÕšv.*‰øM¥HalS¼}~ïTm.;Á%l×^óE]zs4 󲓅 Ú©§^Ï^T‹|bÍkJ À_Ol×€©0·ŽÕ–äД/*p e[´õC‹Ìz«õœ=µ—%Îí@{chÝÖ—kÙ­ŒÆV«¿ó€„%)Ì“AÙÇÙñ…8QAmä ©ƒ?Nêå UÖN¯™–QD6{ë®p ƒ«e+LùÅ5?ŸÂJ6žŠ2Èל)J‡NuVYÀBf¨\ÊçShOn°†¼d晴µ]©úûJ»>È$b ©Îî0[[›&uÙ2hï…%¤ªÕ';UŸyü¡°êͩȹ¯åüá'b£™Jv‰Wgó„É K¶Âw­QGט}m´HÙ¶Tkõ]bci38…ž«ŽPÔö |ãb¬Vuö4 MµË·ám˜i¤6k16¾©ìªMÔLUiNâ¿î”S¥ŠRêEJaE‰¦ÞI9(Šï…Vf\•'·Û «*s÷IŒÒ{~QäÃJO%ªaWXÍL¶9gH·h½tù@ ¡€=-®±µLûRÓvGâ!Wc(5ÿÈ*ñ„¹åU.¿ÜÅZÌõvËü¡/E«Ò<²ÿ(F×gpÔ%n¬„ ¼äºÇiQ:i¤í*·på+Jš©ESã &}¤¡F¶¥¨X¦€Œ™¯Î-8£…<@k_|íe”¦Gçó“¥\ÂÂG™Ÿ} 9¤+8¢±ÕénŒãs~œ”€ªÂ‚qÚÙ8'ÆA?ÀÕ~p¢œI<ÛãÎ q&yWfkOlnbêíUßÎ)ÒäÜMjEUùG÷‰5*‡+•ìÒ,Sò¨%]P³—vP’ge•2»/tUx”¿©Å‰·hôYöñŠŒ_¾æ?œíSCþ¾øÅMœFÃùÆî+zôßg*{b¨ÄÚ?ÙYV$Я¢Éüâž5ŸÏãCŠvœeŠ‘•2cùÂÓãEÜFGe§¾ ñ¥VA̱Ï×ñšy]±ÏÿÝ ·Ìsk_õvEÈÅõâÇþ¨7b5W=‡þ¨ÿz.§“?Ά"»Ô´+§|xÍg0k±LobN[È4!‡Õ:û…•^ ÿæGÿÄ+!1AQaq‘¡±ÁÑð áñ0P`pÿÚ?!ÿú#þÇjÀòÖñÀ™Œ@‰",®O9xV"&Ÿûƒ ¨LûqQLe•*=c›M²”–µ6@.B‹`U‡×ñòë rÙkx§ªI8dTÚk™Ôy>²3aR½¤ÅˆK\Ǽm¤Ô%%ý£ ÝÏB?vMæ_€9Õ©›úŒÐ€_Üáï^rGíù¤:SxÛŠ¢+¤r`¹xþ.4ñÆN?×1³6$‚ýcnÚèà‹–ã!°¡W5†Å<ËSäœÊñ8.Z!6&£I$±ÕãÖK£jvÅÔn^-2yd33à' ²’»ÂY ÒJ¥¢ØqüÁËÌ`+½\—^ßÎ!Á7áx1½(Râ ¥”ûd#d2ëÑ•"ÅC4òyP;&â`¾ Ën¼<þ쉪Ìíq®9Ë)•M):ËEùübÆ‘+GÓ¼Ñö&Üäƒ";3K#ý\›Æ^ “ÁÑâþ0b×OÚ²‡µ`âí¼"Py¨¶¼w ™Ÿ9/£Z©}‰ñ¼ ¡^p ôŽjLZVg‚¿©Àܧu‡4éÎ1}Ó‘AåÌÿ×,àIʆ;\A"Þy¹G¢¼¹*v¾åÑ«þ¹;cél–·-å§“Äï ¿zÑ·–# ÅCÕ±HÔaÊù$©Ac?9Ì,!D³Ö(\•(#ëùNðŽ£ôÈȈTÑø¸‡ò†)}Z¿íOÂæ9w°ärO#ÊÝÃ,aøÎIJxúÂ%@ƒ õsÉëÆ~@Äçs2:Ú7®ôþø fvôB¦+ÌQXÓ XÊÍjçÃ%ä\ñ‹‰ñ—5òrP•3¥Œã»K¨F|£Ó¼}É8b£ytÂA—dü9œXÑl(’€ƒJµ‚ËæðäW&–¢L¸~PV[•ºëÛ585UµDý'Íï‚°l@C¢j\äw4°pLŽ|ñŠ DÕ߈̃'#ƒÊòM¸¶œD »Ã¨ ÉZòŸ96Áô~¹vJõÝ\ãBÍ(a¢ñ·ßƒ?QÎçNO›¸ÿØÿ× Š²Çu¥–ÀõÈÁËe–r]ÔÁŽÄ’-P,‘5•(pÂŽÑgšáÊ£*B…ìóÚ0'Ýá ă¹Ï£úážûÇäF†‰Vüb9?Iwpíáõ†=%E+S¸PÔâÑwV€=ž±Y/¾Ì' }†1 H:­íU 7ˆ ƒ!‡XOXŽð7üoãñÍ&Ô?9[sðÁþ¥ìó•Èx9Y.ëºpHQ€ð§¶¶c-0î"ÆÏÃobT\ë‚u?°(ÁŸœŸÆQ:ÖB ,Šç,9fpˆôÖ+ÁÂcËB\ÆÌêy¥f^G8aAo\ký\³Œ¿……‰ÞZà·âòõ€¼–Uûg°©§n;~@š ÒVæòí0N‘ ÐöòŸ¬ÂÔ½}eaö¦”~³çã,|`CIÁÈŠHD1À×u‚D†¢KoÎKžŠòŸœˆD$ª#c©ÃˆX0讽gf™MDè(NGÊë†ÓC±s¸pµô#"éí:°ÖŒž=í¾ñ!&i¹¼žmä÷š¾0oØKÂ2âÏÆY/ä°çg¨¬ˆˆ‚ú éâpAãÆp~+ûøÆ Ówr>XÅI@I§¬–<Æ¥Ž2| ØLgE¶´`@ˆ(5«Ýbka>ÚD^œáOBÆA®^[MÄÆE^0tÉéã/z¦ŠH%Ó½âËñ˜_€n®Ÿ8p€GÂ=a§ËX`ÚL9 C )õÜ`BKF¬÷»Èƒè³ÿq›ÿ&Õ<ÿ[œ©h»J>½ÒáäÕ7¢°¤^÷”d Œ+“›^°e;Š „UäÄoÇërR™ÿ÷ 2¯–¯Eá1I:sHgçÖ?;«2С„H­‚9ö{áÊ’ÂRHB ÛùÅ"p÷8íåix˜ˆ ‚åuzÉ‚ê˜ÈŒRÎõ«þ2×#¶ÿãUŽf´ˆ ÑšÊ\)}MQšgB&að¬í¶ÿK“ãæðøšÔÄË0RLB/u÷8è§Nk¨«\½dð5à%y «Èg¿íy1}Âqà:Ö* i³;p9\;Þå¤õƒ4„‘F³ÌEzrA9yt®ïsyŶ=Å©ÁúÎGïÄe‹‚ü[’· Ô¢  ž×“ÆÄ4>óÓñCùòÆ>¼ÕÑQKDwõj´& î¦b³µ5¿ñ"Ä{$‹èf=àƒH®‰ƒV½aÂqXÁDgçäveAñ󈑑‹¾ }29,ö«‰_XмÀ¾tnc ¢Þ¸pÆ 3-sõŠÓ¦ ±€ÅE=f‰uvë³òeO’»¤Ë¬‘ud³Êô±ä,kÒR‰°…j†7ì‚@v”]Nb’¾ãEY¼ü3ƒáC¼ r¢T§ #̃+©Œ”qâäq8~ ö_•†'µï%t.àÑp”Î"@NRo,h2†Á^ûU^³Â¤t`‘²² 0ñm$ #]ÖmG¨£hÝÛâpÂqˆ‰Ç„€júA㉤Îí¹CêohZT«1 ‹Í5d±ßÎiþƒ ã›§_÷!} o4ÔxÁ‚´¤‚eóŠ ´ÀÐ…”÷}Ä@ ¦oĨÉ?l"2O ï%»ê‘íÇ|ßX´MÐßášò“º —?"ð­(¢.E°‰×sß ^JêrùÓ"v/x꣫”ÓESŸ¼Ò•ÀygɃ½:r 1¯<Þ&”Є9Ýþ”䞘4-Ã}ó»Âû˜²m»<ÎT¸| eÉ¢ra‚øÂðX2[cç'Ö4‡!Ò­Æ •ˆµì¿xéÉâV…íøÅSì÷ò0yÿÞ0XdB“ uÖ1†ëš£óŒJœÚŽ"~p",ˆÀƒ’Ì–/oÖ,ì‚x3 -º=« §|¡¾õŽ4àÃéãÚm„Æ>”è+¢w¸n¾1CÑuúà`ly"ñðcØ#,ûJÍ.7S È\Fs.²úªFèžBu‘ö,èE7Û „·ì¬“!°†9oL-û "A‹@hÁ!¨J4¾?jjŸT›+iQ"r`ð{s²ÂêØ§Ã²A Góf<éÊ”‚^?ÃÆô(º¦ß®zK°{e?Y°ÝR 1ZŠÃŸÈm+‡¸Àv—Õ€êfûÉ>Ü™A³ÄÃô__ø×¶jµÁñ3š¢#Æ ÓÑ~çy&€=%j_8Éñ eãŠ×œš•ÂD¢„©‰óŠ^HÓȧé`Fâ‹'Ø=bȯÿ&þq=Š¢‹À—$ä%fËS´j+yþ¢qÐc‡K´€¶òIúŠ‚L‹¾p™zMû¾XóŽ|ͦ‰ïCÃË·`i@3(YŸ8 ’"®‘\fˆ·C^ÐüV(s£“܇yZ“y4¤]ì98Õ>r@V€ô?Ó‘˜”’Ü'¡øW. U’,˜;>ŽKL`ž ðL*oÖ)ä›M_=öÍ£€4¢ RýXn—W\Jzjc‚W[ÑÉEÃç%;ÄšÐj—-[¦#ÐÅ`©,îݦŒs ©GJ²¾±†2g;?×&üår›ž.çMŽÛý²¸…$CшøÍà±™À>>ð7a}–ì>gôÁÚÆåÚ!IzûÉ!6(W!<˜·K©e vtÑÎ^#'SÏÎãÎqL ASÍ?vò§)eqêU.äˆôãhÊ‘|~~0G„AS/Öĵ„‘É$¤Ñ‚¥ŠEþ¸`5ãü9ð (”®…Âbc…ʇ¹¹ÝçÅþŸþ›Ç}ˆT­mÅÁŸåeè å-cO°ó‡ÛÃ>W†šp<› ¨þ}1Ž9Ê6;)Þ7ÿFB.áWêq¨›còèT®úÈ*×MF+%ݰÀš|ä'’q¨3 Ëœ ¦„. ªH<¸‰Š:Š%ß§ Ïh°ŠP”wÁžð¨¾*&”2 ]ÜhqâÓ;•©'xF¤%¥šÙŠÄ“¡ÞWÆ=÷Øãk)"s‹}aØØR~ɨÈ`Ñ‚nå¿tfrKâyÖIÛbÐò$O‡÷äD¼N´ÆcÍÄß"&}áúBѧ˜ÿSð?ÂNGlÖ²µ”ÈÔ×δdÔg3zð(ò¸YéÁ…³Ealrñ’Ú¾ ©òç‘c9”´ìöàN±y)H&ëÍéYÛBy_GuÞ °@€úÃÙÚlX{¯ž:;35“ t6±ñ‡3–!BAýs¬ì‹SÀn­ÖéUÃ@ŠÏ©ï#>Ea ‡æ'ç.ÊðÃù+)ƒ‹~Tnâìæ¼d5âÏG%£ç+9JuVÏ{øÇilRƸ|ÕNH…ª§'ˆ©ÙøÂßJÄÇF~Èf³Ýñ¯à’°v££Ís’BEçHójÿ,¹6¿œIÚü΂YǼ9»Ñ$j ƒ{Ím•a5̲]VÃd‰d2ñI Új)iI“ 6Çà2Ðmð\©ÕuÛÕTÞ®a O;¡gèX(]0 ߬&‰uûŸX°®iÙ€µQ.ñĬÑDsòV )<€Ï|ð˜O¨Éì¹¾Wκ‰}"ð»€4à’ò¡ÓäÉàpú‘™°øæ1Zâ%h)—#`'Ìp—–àwòqFJ¶‰¿Cíqc ƒ ‚^h3“«ý¬^Ö³¥‚êz†]Ð/ š˜9Z†¶‚mÞ,@íí¡ùœc!+.ĵ“ø,Üf¯ôóTñÈŽùÆõˆŸIö8 Ë —ó8oléÈr â£h·«Èg"]à9J9À鉒ƒá€=II›‘èãŒO†lʘcͼ'XÑ ájáøq1¼L® ÐùÇ;O/™£èçÆI÷ø?¼FaáçÎVO¦Q¯]?sà8o¥EÌoÞqcPzqG(‘î'˜fpÙŽ$™M>:Èíq‰µ]ó^çxpL|@Bát°”GÙ·ÇY¹v¤íËå2XÐÈ™—å;㌔gy牧¬"k% atýÏäÄròêv3<âí8¨Qž£QåÇ5dÜù#ÉÊÜJø¸RH˜u4þXG®Ý¿x‚-Aäzâ5YT½†dŠni¿w5ÊjlÄ]aˤˆ†"Ü Á‰£'b}áy€N^aOCüä™ïM!³KMõ÷= ®5sY¿„q)“.(ògDÌ< Æ/PËæXZ†5ê$\…`õVF„&Áù0Aþ˜OEAÿÝæ¤y&c7ñ8t,5`²ªy·œ’࡚¢Ý? =ÏHcÔf’Íë ã`]–q‘ЀpÎS¯ù„¹Ó¬?ÝÃ8 o›Ñåq`’UŽaî#ç5T»ÑJO„o¹Ö÷þk|±4=k :IÎ+ä|~¸$ãg v6ýdg ¥ÉÛ¼Ñx©sžtõ‚"% ´‡Ï2øÍQk M=ùŠÈ¦ANF ‚½ÿGoÖEËÉå×Ì`’OQh5ãûN]@>)¹d‡ë¿†Hƒ6!Šˆœ& N+ªo³(rZâ¿HöOÎ…ÛÐ7Páé…@LWèöúÞ7ŠžˆÅ‡‘º\ÑÃì«ö`£xåPXD%%ÖûÂo CºN'¥šß£z-ƒ©BV}÷šKrIÇÂwàÄ^HB¼±9?ðaD„ãûÎ<†Î [A£÷d²pî ½˜Ÿ=àOÁŒpd;÷ÏRb[Z}Éa­àl€—rõ&ÕË‘ÆPŽPmwÍdp#ö¢¿!¾0`â]ÕîÄòe#r4$œn0xÔž3ß¶õžC5Þ´ðs£¤ö:àäcï5m t#X ØÔƒ¤È'ÇñŽžÿ0¿¾iþðgô~©Šíìâï]aÕô<`rÿ´»>;Ä丶›p䣷Õ~3ðǃ'wä}…üd)Q„Æß/ÞLcàâgɼÚpÂaúh kW§Ç¼Ž,NA„ý§$h •Ñ\Ñó·¨sŠ÷* …<îÄ£OUÝ2öô'”à€xÇ8t]×@d©Ì4,Özþ5Œ‘^Ýg8·C¢e›Ç‰yvÂ|˜Š^tÓš”,¥—&«P>C5¡p¬³õÇû÷ß:£àÛ2ÑPQߢò' Ïñ†^Q޵ÕcL$žPçç?Y–yÀ_lª|ŠŽŒÖIBçr.µ8dÕ£GÃä>Y$Ù¡k ¢›¼1oµãñ#3Í^rîóíEd4ñÁ€±z  úÇnBënjÿ}{‰áóûe ¢Sˆ»ÂƘÙýŒÔÌ’©ƒŒyôÀNÓòb¥DŸ/â“ÿ²Bv±j©ƒÖÖêÈx®+ñÆ4¼_8­¦õ0eG³ø<âþÆp¸78VK=P‡rbI’#5+UÑ£ÆM¶ñ ºøúÃì~¿û,b“ÿ¨|aX`Š@,ŽâVÿCü½Q<ÏÑŽG·èÃ&BHÈ4’dzÀØ'1ÿ¿9¥Ü‘ÌŽA›NŒ‹B‚»‹'Œ]ù®ƒõFŒОWó8e¤½€/,¹ªV%¹¸!6&M…ÍãïïÆ*ßC`i/s“謯‰ã0MòúƸEÌKçÆ Ãb ¥ûþ™+µ&XHj³Åû^òX¦ÒNBôE,¬¦O_T}ÏÖ+ DÛ¬¢gn'×ñ€Œ¡ðœ-ã£ìþðäªÊ“:ˆ—Œš¦•8‘ ˆò"!ዃÕT ÌÆ¡šk—X …DšØm–%ÙÞP2Ê¿‡IBZ‘¾2)³„Šä—¾üb”,—’ ÜenèY9ˆ£ŒÚ‘BÛiYk’lÑ`ð¸¯Ø–úʱ&ñ›Hùý¿åÅÙ×¹€c †#@r:ký7DfŸãó+¬±¨óŒBYá’ N§'Ië ‡ HXqÛXú!ÖXž±¬¯ðrÈv³v°ü;?|¡ô´õ•ƒÀœsŒƒ¹Ï9I[7’ë69猥qùvùqžš¨Âw<ç‹ÎG õšVƒ‰pH\éÜ<š:Íê!}—þcâXË1ŒÓ‘Gʃ÷ç·©<àã&^†I iÆØ½cL•c‹Øù `edh5nE¾u”ä -Mè&yœç•ÔS–ìg2Ê üæ´,²Y#ogCŒâå—×ñ•à‚Șà¿S“Ç–r»Üúù0T–”ÂËŒfl¸ÂLºqí¼ :ÎÐsO ·éŠtŒ‚ T¡éD¸3Ä¿ÆÂñQqÄNÿ0>l‚Ö]ðie^["°„À…”ÎYbf#ÌÀW3…ñ¤ˆèýð¬ªNpt}dJ}­³6áÏ:ãLIA=™¯ÝˆÄí!áâgœïµÅY×lùÉšrEæ85öãQDG–¿LŒUË@øL” ÒGöÁ`T±ëŠ˜åpjÀZúY|?|}øþÌxx~@ˆøÁ_jUB®qYߘik¬^H1ÒњЈ’.ßJ¯ èn¤ÃûÕdõ}°Œm0NÒc”Î.[‘ÐyŒ… \R¼måY׬Š¼á¹“1Ï.yóùÁ­˜"e?_üÇ=?ÜLêš'_ÛÇ:&S<0©8–p âcc”BQÆJX!˜[˜dÖ[$yƦC¦½dUO¦X=뤗D‰×òÞhÚÄ.Gøù¬‘¶Y¡P¢ýd=x•V„ó$`˜’HWç&T“njcY.f@-ÈzŒí:=g.’ö<ÁžœOé„sI’=R ç˜_ÌÈz¶üzþæe’åjzç*EÍ! ¤>üNBÁ`F£þó›@”¸ˆ/ W†‚‡…Ä÷Vk‚Š“Ïα±Ä  :®ÚÈ×ÔReò¦ÌW8ÖA,µÜïzë;E›$xÓY1@žV#·^'"Ktjh¸ÿ˜ÌU¹}gë2ú£¨üãÚš!šk¡“|€…‹ß¶°¦$(l·ïU ­BÞa‡ÆÓ$fI9YúÍÊ §žókËŠV´Ù×hù|gš¸®R9q¼Õ”RÀËå>™âÍ…lóCï6«€tƒépñ炃ۉ "ÉÖVycÎÖAÄ8ª ÌáUçZŠùçsÿÌŠ‹ä±®ýá‚¿êÕäŸ?yO–Òà‡y `Î9>1ãs‚e¬âðŽÜô 5"1{#¶slÆH¨œ‚g! >]ǼæR “0OBÛ‹÷”^ |Ò7,Îrd$‰¦gfl,!8v#g;ë= óÂÎ\&YÃ}a7ÀG]¸Ê’Š~ÑS&5aÞq1‡^´XŸ~£ ‚ØÛ/íƒVE‡0‰ß‰ÈQ¡«>¿?f¨Féÿlãü­*æy Ø+ÄéÈkOŒq#œÚcÊ#$£U\. ’Öãñ$x9aÚù‚Yô¢L)S~òöI†N†aðï#A°f‡çˆó’Ì áiÿîH9ZN¥­d‹ ça?'e!‰“õŠ.sÒbùë –kߡǼw/’ÂïIüá“ú&žîg÷Ƽ,¤€šÓ޲tÎ’+É!Öœ" —xS“ë(Ú:f¸¾rœÈH.«Íí¢òz™Ž˜Àú%(&+Û(ùFk–^•#zÄvN "„ëñ~0L‚o,Oë÷‡:‡E6üÉÄ|üåI3òý¿Ôuš‚)ÖNG Ô0ÂæÄ{F@!Î ÒNy'iÁ¹Œ§$â$­lƒ÷ÂäåÑk®ùþÆ"í³öFDGY ÎãO½ëÿq˜kxyŒœý¦ÖdléH¸¿¥œ,wjkZ|0K—[¡jDO3ÎZC:ßÝ4󓯣Hv¹V¦ð´èŠˆ9þÌš½‘A®K¨Ä~žB`ñïÏÖL ›5>}åôZö÷šA" ƒÛöþ3ム§g/¿8}®N‹!À3tŠQBŒºšÑ<æ£äìÏÇîÀ•Ò “U$<µ‹f–`£…·®«"Р5ž.ŸÝº£€ò‡„Ò ióuÎ2½©´9GÞˆVÂåó‹v$mŒ›Oë‡È'\AŽã\Ž?9ju~ÔÙ5¯zÄéšms¬Úfg:‹ÔäSàÑÖO™}ä¥6\ƒÎWç ’¶ŠìÇèÔD SU&KàçÐød:É‚)"?_ˆËj¹¨ m#l¨±"•=[ÓŒuG4%·)~¹cO"4ëÔÌËÖÊŠ"ÏUŽ’[0&ÝŒ…Ùá›#Óqãʻѥ®˯Î!+Ucåw#Þ¸Æ8×äˆ4ô×J>ÒùûÈ©b[‘M ¦Ö1mxO—–;Ëa¤¡">9È!Wîo¾Œ ‹[s^0_"F¤$PkW29#4r t^õçyF¹¢lÔÜÉeáÐ~//@s—Æ §‡.SàPæR™¦þ2IÑÇ)7ªÞ²¡Dš?–3‚<=ô‚2€UTº~çùÀóšTG’e1e"É ¼ŒQy£v\åú$±B`n0ž4õvó–þi¾<é¬K&K~ŸX†Šf—}Îæ’ l]õ×8RšØHÔ*ò'°¬?y%ÙŸ>‘ùÈ©Çy8k'ž÷Í»³2ZÿSü ºŸlËK+Jÿ¾~3\ v^§›ÅMD¡á"ƒ)_‘ÓÑüâ]Dg'C0w£yåý^lt`¸õï;š'ù‚Sɯ u7—®ñ[˜#Jçîf§QÁêO†öãé)¥lvE Üç †¨ˆã½à4œµñÈaßFýhOœŸ‹€ˆ$TFݸAùrÅÿ !2ä´ 6Fâ#ïÁeÞ©¤»ç$À0D L}âÀy©­íÂÚôE]OÌàP²x|tä•ñOŸ`Ó MEîÎÌãQ¢:øq»+ˆ·N&Œ@‘&Þ2”ôs8°6•”«ˆÿÆIæ+"‘ðQ¬Žì!ÑìÁ•3ð)eÁXAŸÓ¬‘X埌ŒpÉq¬T­žZÄ’_Äx¼L”â]1¨ƒ¼NCºš²Ö«ã 'êDt7x™¨»$íý2HeoQæÓSs%cúR‰öðoÈ\L ÅOç 0LŠÿë ŠÌxq+(âòÄa:Í}÷ÿá$&D°dt3N±¢ä±P2åüþÿtgo²joˆÂ¡HýS¥æw>|dŸ˜ÕKòWZÀ2ô ¶ûý±ÜLÊXdÄÞp˜q«œ(ÓœbÏ–G§„€)NŒ˜.ɧ_÷éǽjN:y:ÀF*‹(•ÜŸ8ÚK5@irQ;×'xR;å>c=jÚ¢}ƒ¶«’5f‚ìüFᱺh’üã% "côpŽïÜ¢’ØzL<ålå‘ Gìȹ¢»L .ŠÁ)-ú')d»=‚|‰2)«$üa¤Dœö!Þ<ˆg^.Ú¬Ž>Ò)dD\2»¬Ø,Àô,­@é’ÚÜâ_Éú ÖòjÙu_»úa¸š* ¦&/&1Qˆk¨¹Ç³nJ7só”4Yiä KäÉ37šÞ_9ޱ}°ùÄëÃÒDþ óˆ..´&cnÒw8xÈÎuü+$g]…¹Ád¢^·¬ñØÕÆ}(Œ}ä­õUŽÿå³^«D[Gšu‹.^E…ž$ÀÛ© ?ÑÎ9/èÿ—7WR• 9T"2:$÷‰¢ái`çù Ê< vO¹r·s°‹ï©¬F,! äïï^rp8¼OÒ–ž‡Xšh÷Æ%A¾’gF•ÛëÊi»iÔ;oŽè`x¤=yɶCœs4ûr+ô$Äþ]’VwC‹É´J«k[¸ãYi†|dpJ&«qÕä o jKñâ1‹xú=wûdÞ˜U±8X~ò&Ä C‚ºjº3EK¸ÐIð2VïX˜ûÀíL0âyŒƒ­6lB„$Dq¬ŒÊ‘Ë…0þ2eÐÁˆýbàU1;éX¤h±¢Ü»¢Þ“ Éø ¯úçïB Vû¯œºG$IØÙŒ†’RÖŽ½q¬/€hñ¸~ÇxÉ…¦Ú·Æ(T2?wüã ÚA¦;Çi}kb‡ÞŠF§¬p‘š£ü N˜åŽ–”‚‹A´Hštã~ šî›°®2µbjŸX¹ÞîØ‹½sy8Y¡ä?¶ÿ·Ån¶e½)¯”ß»û‰7’ ×Áõ–¬­|ÿ†ƒùHL¼Õ?èÿ˜B $ù ¿–y‰æv2!û_ë½ÖWX IHðÁ ßþáJšGæoÏ5…ŵ ÉEåup$›õŠžÔ¸o€˜©©œï-zç¼ . ¾—„®[ƒñMýäQì¯"±'–4Aÿr\ÿ‚ûaˆÈ—×ø \ŽÌЧt)eQ;Ö*Yƒ_«wn‚zîåG•wŸÛÉšI–Ì$­ó›Î3œ•zȸ+{Çù&¯s§Df¯Ûý,?É«½„¬€­æbŠÜÏ9[Tçt;k†SÅ6â%Î3ÜâjOŽºe‰@@î?'¦!¤„’æbñùI}`Yš‘!àf›g•ùOôÄ]4 8…¯š}1‡(õë&ö¦§Û°ó““fP1€žu¬ZUa4ªä:ÄÉ‹ð:Â#$i ï8Š´ë†iN’°Á»q’ûsç–øÃ9§üÜVOYd.¥‹ÓJ/Õ°‹UÕ±j¯M⬒G uf.@þ™¿÷4‚€kÖ]ï‰h¿Oîǃ°,L8©ƒcƽ5¸ç#N%q~¸ ÿO×\5þÍÆç”³0‚»ƒxšU¡å!³ËœkÝS¶œ%qëOmUµ#_\°ê…8å ƒÞ¹Âæ$ö…uÉàÆG(”)RòGxãd‹0g6—r&C¿? €6H‘Ú3åUì”ì×¢Ì>Èuà•ð¥Áê†^J;úÀD±&åÍo'’n¥ø¬t§g‚ŸÎV}¤Ò5»AÄq“F>©,^xå` ŒÓ„V°[ë4ÄÊÌkÖ² T8 mY/§ —>ÂRdm#'±ÈAÕ¯sQ‘TÄA«¡„ðF „ïQñŠêì –Žõ•Ɔ*°aVáQ0 ”ß9s\@]£¡U‰:ℎç !p0 R]^õÏÆ:¶Ô(»õüóŠÜqþ¦Þ°IÚvg§Ùœ¤_ ·•(y$kçAԢ´Á:‹É~)ZXZ=ÇÎñK°™$¢Ëâöd”ª, rUV)G}~•ñœÈCÉþÎ’ì>íì1HjÉØ<áCSÈÒt摼l`Âxo„Å‹ïãdKþH}9Èå_ð¸õXÍ%ñ éßú_ù\5þ€–Iø¿Î Ò%àu%dlúíÆ¾âCVòãÅþ•-8ßH/°‰ÔJ_Î0ö%$éÛ•$1¬Aé%žÌ=«›3_]ÿî-Í$t½¸yÀ(îôsH º q\Öj@ Jý|Öj¢Õ U“‚Ù@}– ½d9± ¾äe¦ûÖS9P¿OŸŒ Ž­”&g¥úe2¹·¤œï (1 TfÚ2>Wâ.0E}›pí:ýóæ|çÖ6€C¿âió?œp)¹»ø„B!‹ÌãH"[w‚§Aû¬év¿©†¿Äà”Á*rn‰y1îpLWvsi"ÒqÓ¹l(¡ÌäצÛrAZŽqcP®L¡H$væ…ˆ%ʼ…2=8¹ˆod‘Ôa>€ d&eü„Lž ŠöH/±Ž'½áÍ!JBTWÄ<|å`MSS ŽbhÕ« Ñýƒâ˜MË,‹G{ÚÇÿ4š X…¸Ê1Š“P°Ó_8\|oÎB¤@1¬ì¾RõYÆF&yVjæÚÁÊkÀ¼Käãè,ŠÏÒµ‚ÒǬ U’èpDQÖ lBõ‘îð€XÝO-QÔdÉ ‘qÂ/÷¬{–Ä!îºï¬'ÖâE<*°`ÖAŽÒfïWÌ‘‹>¢ Ÿ– Üäuh|~¹1H%"‰ûd+IpŒNÊ’ýÇÃ÷‡¤hòÅ)¢x¼5»!wÒ©‡÷2t!ÈA×Ù®0±" PÊ¸Ä°Š›6 —àÏLaè2÷­sŽÊJ¥UÉðׯBxj¦‚þ}¿tB X6[ãöç6eÐAb^t`6¤O/8vCӛ޵œ”ŸÛ1üVMŒ‡,ˆž±Å±ºlòs§Þ³Z…¤ì&un9 PÎLR,÷Ö;ÆjÿOUOÛ?æ_ÀÓãèÛ ¯ãS”Øî—ÃAX4‹ìSã5õ€r$ÌSª¹Â%J ®ñ€Í(˜#syHÓA>Nv.3‹Í„åñ‚â \b@&“½%DWÝ4¯Ÿ9&Ž®¥!rÚøÇÅ[TeÀß½s…;‰–˜õÂ3ˆ Z@S‚¾qR˜¦Gà|☶G†Çfªü3e<O>ðÛ…‚ª0¥ÖB•Ü–¢.m´VGd.è¥ÙvÜÕ8 rØR:ôbæ-“ |ˆÉ0–<`>Yà"_à6øœLXÌNFW›X4¬˜‘HÉw#°Ç\á9Øôý1BR‰ÿ×{üyEëÆðÈÁØ—ÌcÜ`JìuÖEG˜$zÀR¡t¿³ËÔwìs043¢5N F´yÒ]ó vÅš_üÉÝH4É<îúÆ#ã`×òg;¿Çö2,¤'fFÝä•´v?¬%ª íó“zËþܸ/gÝW||^!rØvÜ¢ZµÇ™È¨©Õ¥7“bdЉ¤4ñºµ2F 3 A&ñQ—F¨ŽXÿ¥sþyN#E¯ñˆG–{2½ñÏÖ:Â’BRŸç%¡éR‚šUÁ×9C¿;BT¡Æ.Öãæ£tÅ»Äv$M°Ë—¬2=Ñmª`©f‘.µá¥h…ã0}³S«äÖ5/¢shWŽ“iOuóy8•§š›ë$Îè¤ÌÇ…~ÈÇe¤KdêkÏ8ˆ¡ &Iºétßî¶ðLóƒWzt!c©OX¦h~®ixB\o%©ŒYÊF0ÔL„Ii=‹jÖ£Ax½ë=X`í˜,ÃߌúÛâá Lýg àp+RÒÈžˆÞX.é¯KnD÷ó€3¥qqKÒÈ æ4þx;âÌÓ½¾–ìÅ©äÌØžÂrï5™‹Ž°á"Ü,óÝ~Š¡˜>ÛU¼^¹ÈR²(„–ÁÛï"UTùa ƒ=!Äù­øÑ™®¸SË‹0þ\4.á#»Ç~óK—jð0ÄœïÔꜢ–žÃ6xÍ9Åæh$£a÷ˆ£-÷¾òRR<ÛŠ“2ÉÕ2Âoñ‘*’d¨r}™±8)›8J'tRnT#Q r>ذ³®Ì¼M˜× lLÑ8†Hâ#5 @ÿ:¯³1Üö£hœ/uó‡àœ.T0"¸Ü^ó»yttµÅZ4šŒ¦ h#Øàh\ì,†ÚãÖ]xNGAO8|°¶a¨­Ð8t 6™ùØ1¢/Œ‚Ml¢ýäÎã|§®'W’òV˜Wnµ¢ñc'«4B¬4¯ëˆM:”Œr€óY,%%›gÖ×íœ a),¥"“øÖeò4îÝÛ8EKÆ€xNß2õyÊYP™ýqyûÿóñó€Ãú1} ž0žR?ýýòÏ L™ÓkÀš@ ‘h–c—¯Ó H¨ Q™;í”=çŒ8§X\d­Ì)þ$9† Õ8?('À—"5P?ïfV+fëÊ/¯Ó%ÆA{m5¿¬ÅbÑA4"tN‚H¦œœ6…o+Ëý^0×øFÉÿ)'ÅÁÔÏÇ^ÏXLð<Ò±gœLûO¬­³™5íÇRmΑ¯o¬ÝÇFDÕjÉ,}dj(¦)ø>óCEóh/Oj¼G"‚'»$üà%$Teb@ãEâOÜa³×xQ2Íè‹xF(€×‘ÛzãA7)¯Iñç-–hÊöNñ`8!êôúÀMTP'˜€u]äZ6È(;4òN ½…MQ•_Qˆˆ³À›nyÈR6fÉw¦užªÀЋï8;lCJýUîp‚ݲDû¶õ².µà×8”§«OÆwH%=zĨ™NïÏŒDoQLHåŽöC³ÉC¨ûhˆ¿Œ½7qä1±­D^^è÷j›„ìâµ8ž™z×¼OèX ÷„ó:DIøa®Ã€XýËÁEÛ€²O`ɱ@·ÇýÁ]Z20Iy8qQÛ›'œ€ŒwBû¬}u’ØX„gƒ+AÆEüV'õ§Ï*ok‚œ®§"À(®Í"L¤¤a¢¥—’¢>;ÉÏÌRãÑZþñÓ8%2x@®.UÃ#b GÏÖTÿ£¬5þ±ajâÝÿ[ÍËy.ÁLϘÃèæe†.曑‚Ž2„~.qϜ׀üá9JŽà$ˆU‡xÕ 1)¡ª|˜ZfÈ>7ð`¹„„0³xµš|FÃëËu„¢aÖŠ[q̸¬·ÁÒÖgHrñmÁõŠê2ie/i® HùcM—ûáHMÄÞÇL´Dfi)ê¤"üïSïÚŠnœœðsáA­üâû&Hœ8ž³y1­Û$Dä+ß8Nâ19ÀÁ“%xêô«‘*b:>]á *O DkW „©Î\Qc®ÍBP\ìÀÕJiä´bX7H^A‘`ëgzŸÇ&:kœã¾"¾é1\‹(>‘Œž<Éúp†ˆ<uçƒ.ŽÓ7ã¶S]YxìœÖÙu®uóšû—F-¨×pHÖ°˜òÒ£{^=ü`ñ‰™$°-ó†GË(Y9CXJ âX²/t‹"¿!‘çLÇ„6ÕFòGqaå·:ÂÍ‚b²P¨E/¦0ÈÛ‚ªÅ‰ygõßÖ³F<抃ýa¯ðœd|’&+©ç&$¦É+¾ñ!UâA9#%‘긲C w†G£1GSwò"çãµ`˜°Õ 1$¨l: œ–Ј¬I¨|`ÕšZ½ƒÏ¨‡×YgZ&çæú&DðǼgeBH!gŠ3´·‡¤î|<;ΈøÈdÇY¡Óߎr<év#Fî_—Œ¬J);ɱ3Š3äÇD¿‘ÆU¨5 z·lÕŒÅf ±ãƒd §A'䯍u”L»j¥—ÀJûùùžp˜Z±ökóúby€[l\˜—~q÷仑>ð^Ù©«‰×ñ‡ª§(iÄÕà\¶F íEý±qé8“¶"2B> Ñz8£5ºïÅåL©äÙ<þùŽœ \QøûÇ‘˜ÊÓŽï¢xÇn¤ µ€ËÂoÂã9ñ&%‰ïO_î~6"ü:U;”ò Óõ\­€wˆ¼sðëÆ]&T@éyåÞWñzb|=8Î]V˜œÐÙÖS5YI’}­Îò.égM—Ô`(R‚Ô@tNßÍÀêIÙK.1Å9šíÝw’-ȂǙ|ùç(™ [P‰ˆøÃÒ§iL‚ÙþùËI!‚’®ÜÑQã¯÷ÚQ(cÓùÁ|äRâ„…?x(–…)=ÇeÂSZÝ]ÀÍæç©c"PŠ´No¬œVÿ)bºÉh"BÓäG¬ÉÂÿ8¸ç&µ‚=<ä̰`R½qï&ª· ûÀ€CÀïX(cj%4¾-¹Í×е¼« %H­²ã^# 10®IXÕì;Oœ[Õ™'×ÄdÕΩša«TÒ{ÕEB9Éäõõ†Ë)Úžc`ãFÛ ‘+àÿܽhëß"%øÈ1Ç¿ŒYªäûzÅL¨¶FÔõ²¨Œ:RÈ¡í5Õáû.#CåÛ {)¢ßxfµj‚ž{¨‹¼#IÑ€ŸøÇ?`)ÌõƒV¦?Lƒ>Ìøï쌕ÚHþ¾ñ$y-Òzâä™Ö]w›£• ,>¯Û%:d)Á™~ø÷©%s£LÖAÈ"+{ÙÉ­tоYÀ9¤ 6ya<7…Ž”±xÔw®0D|Ú¯ó––6ßY<‘,„¿êwˆ<žußo«r ¸êu@´ò®w‡Îè"›ŒžOœIv¦ ¼èÞXÿ£¯òüD6{"ýòs s²Ù©’[O„Bv`úa6õ„’°©q¤1|IœŸPñ‹%CýX ˆüJp9- rEÃ{Ô9'üú/©O¬ß&ݵ¾?"pŠ vRu§†@ù-È\yçñ‘%€\’p¥G"8LûÈ¿ˆ¡ü˜u›MD/ŸÓ¸ÈI8¸„™ÎŽàÕ"«æ=á*cÜ=€‚^¿9ÆÏѵ٪Üd @ÍX¸xïh™ƒ„ŒÇï‘5ÉA@‰t$½•¶iÃhqx÷[Åsf„HíŤX°]0Ö^aöAïÎüçMÔâ]ý3?œTªˆÛ'ûZçÖ’¦Á@»TÀÈ éç0/èÀ IT`@ÉÛÉåá¡u!€å±ØÉF*¡ê^_ßm¤XÛä`6‚DùË2+â_¼¦ãKFæ`Ã\ Ç,ÉuÃ4ù"L½žÐ9^ºÚ‡}ùœÕufRÅáÌÉ!«ó QœZ›Œ—AŒÊÓ©DǶMAå ŽÃW¼Qt¦  ˜¾rrÆõp#SÙ¸¾²_4ðÜïY§ý8ÿ+±8 ÈggêÞ²»ÿ+ ¸Œƒ@”·K䜾FjsÈ€‚}kà §“™±:Âx|6~F·î·‘ù°¬:EoçK€‰™']áôÀ/3–ìéøÀ¶§½ér{k¼£b‰ŒJÅÍó—l#§µJãrÖò ËI«²¨œzowE°ùp ð2\ãI]&ëÞÚÆE$fîŽ6øç*)ÓJ°#Ÿü2t«7 d@×oñ’¬Šr•5oZ97‚Ë'j$›49®XE™E…Ï푎y?EÙ(DåN7‡þdž`;³$§ÆŸOÇYl¼,C‹þ¦û ”T`¶ _èã»ÕfE®[ÉÎy š7åžb 5㇜§˜G$µÀZœX0a áðbÿ·d6-ær!QBJù¼I€E §OŠ StbŠM¦ýŒª¬Ó‚a4Ü÷:ÍŽ/­©NP—ªYHŸTJ¨^VN ¾Q¦Åóëžh2#ÃJ`dL{?$¯c>Ùy"n2,šm<Éñš°ö!¬i8Ji'o2O÷ç&ŽÌ=Š Õ·~2Nš 2œÖ„„߉ÿÞO=QÌäÿ¨Ý}àÉ?㟹 Py—¡Øù•¬nѾ@[Ï8–pùc-`ÁB&t™wލÅ,}åÇcWC{»ˆñ†ØŒ­1½&v§Ãöñ†Æg’!'óÎ+»|A?‹ñ–F„/‹Ž?OxÑ#$Ѥ#-ìOÑâØÄ‘B¬¾Æ<"EÑ|Î îGÙžàÀFn‚Ý\öHž b¦ç+ºAØX-”B½`&ðÅqïï4œ«2M÷ÞT–¸Iñ àô‰]ýõïal\‰ÓÅdbÁYb!@sÑkÆk÷aDÒ(q^õÆ" s–‰ðUŸ¹7…;çjÙâ±8†bñÖ¾2Mõ²°ŠÖQpWØ>ã OfRò޵8mUÜÒdÛÂéyJÉ„DË*’χ’ĪeR!·Æ2±èNPªNë#¡†tìnÖæ²U "MѱÏý`9 oc"o¦ !±$6þ1+ë¤ùÍ_èNð×ø_ìØ¸<’|¹ªs^v˜Rƒ–ÕôþøÂÁ‚Èüïk2É-½¿\$Hjq{¸Î4k_”kœQ.f5~¿®Pùóý^£i_Y¼XÃSŒŠyË+S¿S‰Ñž™MH!Æ‘$WÂb¾¢ý£$÷yÚýדŒ·”ò“dâ¸×çÁõ–2Bp,ˆa7 ae=d§c™Ìz,Æû~ŸÆ7ß&ÑøŒ€×Ò #°%.f2eî†?Xèð¨^§KK)h;åHÀ^¾_ëã_9òë!Ð¥——oYpšúðÉížïOŒ u_· ñ¡ƒyQ‰ƒ9É0ÑóǦ ¯§ìâ2´TLÒ>Ù’œ»z?¯Œ#£Œ“;º‹cx($1#€ñôÅúY,_ý?œsoåOõãjýQŒWw€nšð‹É×iƒÜå­Ã™>Íü^¨ $B8ËFR*IñÿbJžÖl0¸Th‘§ÿË$àƒÿÓë\ˆÃ_èådÿ˜2²²²¿Ò¿ÌÿÿÖ±Þòr&G7£5Æÿq’'øœÿ<‹ËËÈÈ22òÄdÉÿ_ÿÚ I €H€‰i@ 0€ 6IÙ € $€€$¤‰‚ A’ $’A€‚ $‚$ØIIDD¤ $@ €@ €A ”  €@ D ´I  I$- hHA I‚€A$‚ˆ4A€IŒ ‚â $€  qt€’ ò° @ €H$A$€G$ ´4VÄ@‚ ÈŸ€µfì@$  € ß­@$‚)¹®Bµ‚’‘`‚ H™Ò:ÆÀ(@ä,”ì{gh:í­. c!†x ýëC€ ‹ÄÙ{!t—µ6µð—\â/ŒpYÌàñ7‡;Ê×ÝŽšó¢cçܪùnË›|UD&³û v”ϧ–{ùŸ_´Ñ\’ †"â^­fð*ì]3¡÷Ò=? ŸxK]®,@ãȟݧ·9teÞ{·lc€"rd¹”Ìh&’(’ iŸwþ¾ëûðB,‹ÊµI…$Àõ!”¯^ƒÅò&áüì|mÉ€€ üu£îÏô{Ebt‚ä·€Œ²Ih ¶¾h1=™-¿7Û)iÐñÃÓK]æÒyqa:N¬ßõeRX=¶jŒÖøk+X›õŒ-ZÂgý©Nh±À…4e¹Ýø˜¥Âé½5.Ö'âoöñLK»@°*íÛðôÔ]jX¾&É¿âŽÅ>±x& µÈŒiiù;Ìýâ`JPV¹žW‰µz€ÁsîeÅ¥+$ÉÚéMK…NP6¥S œÑïrG‹á”Úff|²ÇwD ÂÙ!@mè»2 Ç|?¹¶öÄø±†1ë3Ðá¹%ú‘sÊQ…þªrpÒgórE«÷ ½|er õÝdÐyµ&Rƒ’=$ìÑ!u:¾ÀG‹ÌP³[’͆ƒ·½äL4o\z(³îî2ùŒéãGÈç}¤eõr°§bÿÉ€Y‰ ÔWûR¨©YBØ:Šo&½Ý   ¡Pp@1ˆäÝGêc«´¨¿¥º7ì€s’Ì|7»ãØ òÎÃ_Œä„5É@oç1å¤)dÓ‘º=<†Ô§#Ø@°R7¹ÐgÃu?äù²»(ë'HgÝó]:ØoÊÂf'ú˜»•’ˆ°‘>PÙ3}` …iŒf×~g@âÉTaÛ·PJ^lìnE;¢– €äz¸ÂÇoeŠJj†–À€I € @ ùP–íþûo÷ÜUæð Ø‘û7ib2;@ÿÄ*!1 AQa0@Pq¡‘`€±ÑáñÿÚ?ÿØ?ÔǯóÿS¿ÏýLzþÀÿõ1ëû'üoÔǯóÿS¿Ï¨ýìÞ¢¬xvï†g> gøfª"¼Oõl÷àƒcלí™ý§&L\¶ô–Çc“þ^‚ßö‰¬^>>Ñ»VO#§øôðzÀMgƒ ~ìëê÷–i™…Ÿ~7Qþ½<Õ.eõØnYðÒÒÓú^aõE˜1°,<&nÿc׃±öåÙ·)ò÷Z9âžÐÇé{Ëà?ƒlÁénGMƒê=‹OPx06ÓpN™ŽÑÏ|\8[ø‘vÀ´¸\<:|0³˜Gw=Ø´õe ìjk‰^,·Ùé1îx81ü®Ün~Ý:ãàgãkDz€ ÛC €°¼ž«‚Àyàƒ«–7m€].VCÏ./ÀçÇÖrÇDý%ÄžSàG$ [2Ãê_!ã©âw°Ëß—ÜO'é&ŸP(¢›l !Ë‹‰Šls’ ¾±2|ï¬Gˆã’òÀìÓ§$Ù6|xuhÃ>¶îí’ƒÔ ØÄÆ€ïƒ$ºñQâ5©ytšNûB2„ñ:ÜÞ,)keíÀ“Yî¶Ž´¿æ˜=K\½"íé¾g¨b“Àö1‰¾ áí Ö%ôœðCvL÷ÒáowóÁ‡Ç6ăa<¶2ÜmÞÛ>ü ëW"Ï>íõúäfD£‡–3o,=°Ä]oOŸ¶=ííilzC’|y;î/´¹üDa\Aï:à¥ß«*?s›e¹9|:í¶ÂáË|¡ÒAkl刃íâj‘”z±Ö3¢½ÌzOºß ô½F‹3ŸOŒŠ³ï2pe¯ÔÓ‘ô%¤=SËè#÷Ä…‡ô–Ï¿’XÝã¬9#±É×|Zî ÐËdoxæÃÓó#õ1HÜBí¶Û(¹?O”WÛŽÅ9™~k?NGÐŽCp*ÎŒ—Ñ"µ‚E´z»÷ýòûò@›Ç d .YòØoožÏ¿sÄt·Á$yâÝv,Û¹/Rö7ÑÚíð[aÈs·Óðëf yÉ1øO‰ýEì?5í·¿xúå±ê!â/Ë.Äç·…ú—äÜ<=rßöc6Û>ÁvÁ³¸3àŸ<$ÃäÛ|rYí0ÃðY@%­¶Ã=ñ–w¯ ¶ ëfO²üÔká±}± àêZïÀú|ñœ’xÃ+¶°òÙ‡-Æ”Í÷ xYaäâ5m±°dí“áÏŽ|ÇÅ„Û9×e_$z½·Ø8L~#Óáé¼}FË/O®=³Ém†ÛÝ–Y>³ÄŒaäbOÁ/oà‚ßkS¦6—ßq>þØW`ühÝ÷Ý’ûS3áù~‘ëÏRcg“fÎlÏquÛ1àL²Ë$-G}ÙàhÉ„ååŒèÚ¸°G"ª|'ðÛ òKúF/O‚ˆ¯_‡EÖáÈÄûçŒÙ—¹“éØN¥w¥aО”˜'o`ðЖE³…‹¶á†¯a¹=yKܹ Q7`dÀÔclo>]gÛeǰ Ñ’çnêQ^É`/s¤HJbâNÖa½ž’Çλcõ;Ç+ü\‡ýb2ïó:ðDêðþ¿‚þùdfr°|ÅdâdûŒCâô½<{LÚ}—KÓ†Äa¼jÙ,ªÍ…¿V™ÍŸ„òAŠ.óÄë µ—‘ô”¼™EQñ¾} p=J>ïÎ×Ûa #alx¿Lwrò2_È, ý‡ë!Õ AŸ_KÓÉÙwŽW¶Ý[£êL¶cn‰ÞˆÃt·°8’‘ÍE}‹‡Ô 켎Øú*W]Ûr$ ‹ŸYx\·K Wµ!èÙ63IÐómÂz¶íÊâ`³î Íú˜GÕ“9zE½K'cðô½|o&mƒ.ž½G>¬Ûð)x˜/ÄäêC¨ÅÔ=Rý÷ÖÏY[8“ÿW³ž³þ³e²+ðèÆäDµÃœ¬½²Jöà¨×Ü“ñ,d_²Ÿñ?ÿ{ 3>I†_ɲðû¿ð) ~¶dß±GýG±øz^¾“ì‚èÙ1§¨Ë—EÊÉX™hìjeêiïê1 ßz@AQCpl{2´»’lÂzî3ÆÏŸü%»%÷…4¾Ê>§·ßÚó ÿÙþ„·r?Üÿý°{ËýÃê—(öíb/so¨ºÆ ?èÛüˆTˆÿÔó×ÃÒõòF Âo~^qìñû5ì¾8xx˜üŒá—/­µºŸoÕq”ôÿa ·ž'|9ð0— L*kàø4NF[Ó89{LÙÍxá*Õ÷ðô½|‘ Ò|ž¼>üV]® ê`Ùû—äÿ=—m·žüvÙ=K`SHa‘Ävs/dz/gébï¾-ƒ³ÃÁð.Žç ÷·È|v ÄžÝïÌÅÎRºvxž£3ÍÞCܛ̇µE³`Y; è&¤{=_zðÝ&>¢[ܵâk¡BË“X=òœ$ç ùgê=ÓãcÌ›óBáåøåi->¡\Áq)hëra!™¦çä£.Dg]G²™fP‹ê“¡ }F;{dÑe’ç ÷Œº|s&M˲¢>‚aeöô/ÄÀç° áä99üÍù.8~Ëë¿SþÔëÚ]ìK‹Ufý€v~«Óà»jÖÖÛING.˜È˨êÓ²SŽ"ÅÆ]aJéu©+çÌÝž½¼`z\Èü$Ï€+¤ÿ;(¹¬FxAúcõ€ýÁ/LAo´Sl·ÆÃ[s:÷?Õéðï¾³iËF\nf]¸GIı¤FwbÉ=Ë©@8ÉOŽ»ä“õâQjCvõZad¨q±g‹^— #ì§©n½ÑlÖ »; kd^Ä¡‚IÄ…éÂÍ·~—Z†=/ô¹žö× Xx‡À¼ŽÁd|c×Ó&,ø¿%¸‘åÊa±eœ1øuâõàÏ›%¯K£ÂR}åÚ}l¿Š‡•Ÿ¿ùð^ÙeY> y=ÉØÝ™ò~xH˥Ļð㟠ïúç ý1¿þÏ¡— ¿<=Ùnx{í’ç†Ûð]¶0x®øpdµ€÷ÈÛK[̶ÿvœiì]K`l¸¶ŒbªÏ{kohÙÝ»vì¦Ý¼ ]§ùY@MŽaÓ~ÿÄ,!1A Qaqð0@P‘±áñ¡ÁÑ`pÿÚ?ÿñT²K,²=K#÷²K,³øY?½í¶Ûå$ˆþ0¶ÛoЗÌð¿¯ã￯ãï¸ýû~•_¾>ã÷Õí±ëè<¸rýÍË‘[cÎÿ<°¥¿¹é¨eÏ lzþyÒÏSï÷D×Ç.ø O‘hÆáÀ8bÊn÷÷=ƒ²†#Ùqd¾&>cEïøŠÁ„íö.Ë_{`ÍÑ„—Ö]·Û«?Ûa…éûÓu~ÆíWHqãlú‡fA¹ÿ’I¾å°åÐ2/±¾8ÛCö°óx?c§¿~×|e‹{ßÐy\›cIœ±1øm.ÚÇíÂ>Õ÷ˆÜàálfIu öu³–4a…±.x®£A@½[_x9Ö_c%­Æ½{Ù ¶‘zÈöLíÆ}„o »›²// ¦xøxæË³˜õ|¬|¾ìÏæ×̱ÐyÍ5€øxHÏkG/k'²_"ÞÀ•ég±œu’¸¡Ä)3`x”éÒ)Æ_¡±{è´ž-‘ÖÎ*ѹMä§·í‘.üÂÁþÒ´}dŸxm&6‡eä4?.qÓ÷Fúò1_£—¸IÃåÿ?âß§«SÓ×ëõÿw ·~™ñ£¶ž÷D9%¶|z-ó–xžjì‹ìòÀœ·k‡ˆ†3-Þ-ášñþ84Y_u•‹S麋]Kf9b‰vÜÄ0=Á8äý»dí—«¹Qi¹¡ 2ÁŒ€7_°°- kp›b55¦ø‡]˜‘5ß2™ ]ærå‚Ý =ÐÇЯ“ûÜIe¹ÏßÉm†}øøŠÜÄÖ~§2©­ƒäaÈ~e€º 5äqœã¿Ë! ¥²Þ³ý/ŸÆ$ogÜOÀ…øùð`Ö¿²}ÄY>‡•¾aò7Rd gãGñpÎ,öÑ6S nsYz1¿viÖ{ýtÙ^áé©!ãÇ̧é 3ƆËî`ä ü§m‚Ë,•=LtwÃì†_£SþųÙâtÛ-ÎM–@9Ïp ߉Ø&LûØAžÌæQˆµß§rÝm>¤˜ï`;‰ùÃî_+¸?Ò: Ÿ“ÊæGÐð–7ÆõêÔÓîf[8å¯ ?iß·d1aþåŸG<>/ ¬0\‘îOÊ ä¶2d'm-Ý)‘¬¸l}B#bMóí—Aé!´/á÷ÞÊö=~ô ^2ÅÕ ’`›•¡ÿÔ‡ùJõýÖ1>ó¿M´û2 }@èØ\9 ¤þô›vÅË¥¶Y 1î%œÙz’è$üCò~­‹ï9è’«îGð²_ÜŸÎP= <‘úVG#^Úd—ÜÛü¤;ýÖ_ådÿ;>_ò†;2Bú2/_¨2¼dr» q·½‰|+%ñpø'gÕÎ噸[k~ÈW;¾¶62Y¶?@%2Q)Áþ;Ä%¬‹÷íbþoSµ<ŸU_@yH,‘îNÙ`礔i¾±Ýºì­îF HõòÌ>Êëf5‘³ÜÏæè½¶Úá⸶V·¦Âdo….k é—tþñªAa¯-ûdáþ³ýתÖTÿ¤ÓN®Ç›/»ÖþÈÙÒpöN‰;¨õä%Ñ„Ù~®ñÛ!yëÂ½ï— ,ËrQ9MÒe±Z÷ºÍëÖzÞ¦€ÜX–ëm²k±ËE¼½¿©éÓ#Ï/ÚгCýÅé¾þ?0ÜÃöܾ3+ÎýÝ'„sóµ·_ŸõkÔ¤îüãˆ|ÇèRS]€sÒÃÉ‘ŸG´ûñíàÚ±nƒáåyÔvé_]ÿ‘ÿ¸F!ÃNzü%|—D°F¾’0÷óýOå6þÞÅhy’-cYζÒFFtúsã£/•–æ£S¨sÆv{´8ß*‘Øtž¡Ã—„ó3;>bZ»3ß¶Çþÿö#Y%!òݳêQ<KGŽ~WôÚ$àÄO‚L#dœØÿÖ £õÿS`N‘öÉ~Ïׯ×ö/ÖV·¤ÏÒY® ˜á`\vé0½v̽>ƒÜø·K~íž)ÄØálCg©òxU÷®Q'¸d 7Ä!ÌDù/fáz¥¤Ée ø.3=| ˜õቅƗ-³ž#·~G e<ü­îù!9È6= ön¢Ó¡™êSƒ>°a‘8/'Š&§¸€ç¨oÂ`‰0K®lxð9ÎâÇÚzÝíèd[EƒÄìÆÎÁ³Òo¿0»‘õpi4AŒ»!…ÑT{¶®%÷'a#³]Ô W'ØtÉùƒ£nÏ]›å'˜ûDw¿[ðŠ]­ËŸ‘á Ì[‹ ­å·q—Ðbr° ŽŠf,–³ºö?0-æ}É”ý»l‡-©µ÷¶¿¶H²Ž[`ç‹<&*˜ñ¶d‘6~C7ðú•ôìý¨Md‘êùDÖ:ˆø§ëõŒ‚âM K½Èq6ˆpþ7¯ÐùJl¨šÙľÆÌ꘽xêÑÐé €þ…ë£ê/u^'ÔIQÌ¢õ~ ÁÚê6s°.=C—"úë'õ3„õ"¢GH)ÃrÕ‹b”»åð­:{m¶Ëu,ÛÈ6ÎIö€Ä>àåwîø+lj3ŸCå%éeû‡ÖXcLòë–ø~/ÅŽ®³ îõ{$ [¸ø7$OP¬sÏç)ÿº>?9Or¶5 #é}ÌSå‰÷ÂÙ¥Ÿ/}*~ëmmgv ã'Ü&òu‰¬“üBì‡qÆÆõ²íÚÑe¦U{aÙàÒŽDI ŸX'2×Àõz´ÎÉ^Zõc2ÉÓ}3;äs‘D]츆ՋVmf>a¸c¹Š|3ŸÎÚ¿§ùËtçáã kå?éÇv¾0+=It‰óqüg¸"ÔÐ= /Pèµ§ìÑm™2šàúFä“Ö$ ÷l½¤‹!=?­é‘‹`Œ!•sÆöŸ/€Ëmkï6ãxU¢¶ëaÇÅ#Hù·Cãëw'çnèÏ®O°œÎÚ´=gç?æ/—^¢.Ÿ<¯äƒtm‹¦X2ÇPà'Ì}b› Áô&ŸŽÁŸO¨‹ƒ–O¹ñ©ûH—ÇfK‡ÃîŸrŒNÃã:ð°¤gßiØ•ð— tùŸTD÷2•¾î>ô"ÖB#í%iñ'Ý-Û0“cSÖ ê׳S¡ó=†Tí‰QçÙ\ŒôX8äçÖÕ³ü_z³>ëí£~«‹…ÖBÉ“ÿºa¾Ûwëï68þæ~e,ºÃ¶x˜níö–°}¬£ÎÉŠB|Âz`ˆèðŠ ë£Ͱ=Í™ !mV]{ª^Ílö Ë¸¿òƒð -) &8ý#×… ©4bC×L?d‡$±ˆðm¸ 6zþÛñçåéIùa¹¨˜`}¦g¯ •+G.½.ô i:ac&}M‘á{áén5â²ÏØÁ!ÎÆÉ€gßÐǯ¤f>V!ÃFcæà쳄ßÇl˜/±‰é?jÚ¯·-ÝzxÑnÜ„½2`ùûC:íÄ»õež{ï“-ƒÇ2Ë,òeìæÆ /Ÿ¼áü_OãÔ”y!òÃñw¿Ùø2ÙóYäÐóÀðwÖ+âDõb´{°ñ¶Ûm²oÐÎZN3¨aŸGÿÄ+!1AQaq‘¡ ±ÁÑð0ñP`ápÿÚ?ÿü \uÿè=¹¨"§ ’6¨¤7ã°œÑ~wƒÆÞ •ÇÕ‹ÓÐ@{âÖzÔA ±5ŽTÔ¯tÚÀ›6…÷!ûòKàgy‹t<¢¼Êï4èOêïÈ$EØøå‘]òiøÌvàc˜h‘ûO ÚŠ:æ9¬¨˜PUyý8z=¦ÐóOÖh.£Vž½ÿV#•„À¢šòÏë- `‚¯ÍCþXY±ªÖ«å¦îÛöšqÌÌW\IB•4Š©¼©høI_»Šb¬£¥¯jºã—ìÕAáâš®»–„(Š MïF,‰ Xé·ô8úØ.Ì\}1Oà`¼ymV©mIën,”Ð%«ù¸½5ø”Ö&^B]Ç<ˆin ýYGÔÕ ‰é lø±‰‰lØ\7âC»“#œ@!»o¶àCÚá)bš‚]L(ÅÊŽüÀhó+wgúõ’%ÛGØÓ µ? 7ñÑúÄGè]›peTÀy×{q\ôu­$õ6Úº¹3‚öðgó,qb#vÀ`î!0¾v]ñ@ŒÅ"& ¯ž1+N®¦ôÔ„“}Y™o°¥ô„¢pm:TÝ$ Õ D‰a©°T¢sƒ$Òii´¿—ªú-*!_†±J8BŸh1XÎ|Lp‚F¸Eœ;²Ú›5b¨ÝÞv>Àñ<Éâ]Ž@« ¤Ãß6]§ ûˆ0V¶*•‹l.Näí€.á»Ç@‰$î¡÷ˆpÇ Uâ:×nØ5ùqb0 Š-´g>VŸ­s°Ið0ý"'ç8ÕAÚTú™rðs o@ o D§nUo1ÂE‚ Í¡õ“,¯žC`üØB±ðSÐø¥ïÕÈôȞïï.·ÄȬþ²WÞqrD ˜‘ á’p|‰AH ÇÊì’ªJ«YkrÉJåD¦¿— r+So†áÖ¢6\€ˆ;IýÀÊ–˜<Ù߇︌Š*€H> ƒl‚é °ž1¸?°¦ØB* *Ð8( D‘’lPÅ¥–,—a‹æ„ÜeèD(”B+¶÷>¨‘*êº$É)f–ôÿŽåv‡Ü®üwφA© JlØ6Y‡9c S TˆŽÖ1ö–N€MJ†>àƒ[tmµ·#¨¼~?e0b>à:ýeÅïû7y Ï™b> þð?ßçÜ.2¦@Á # m“A;ʉ©uÆ<>?1'» œNÈJiñ”1£\<£]øø¸YÃuÓ“ž…ÐìŸ%ØÍu…ã=Zc¸?,(Hù˜kmT÷ *ö‚B(_®ÅŠ‘ÈÃb#ís dXÎÇB@þ²Wb*!„7`xÿB£Ë¢|±¡6q¨Ø1ÒþÒûå£)<Ç‚}?we8/Æ=õq›h5@’36‚gzÚDFŠÒ­Á•ä‹$åöõªà[ÈŽŸ'ÞRJèœZ"ÑvèÃÚl3›@ßkÈJdØ‚¼óYÔÉH}}~±J玑Î=8 >hX}°÷$……AùféÃ%h%ï¹k¹3ƒH–í¬°óf%] „JqaM–·44ð²» ÷É1ó§ª( £Ù‚ M|Çå"ë&±!ÇEó#µªmH>°¾`àCÇÆY,ð­Q†jcÆ¿§+aÕˆ#Rï³€/žeˆCiµ!öÔ<’ï)ˆ‰#à(#—n¯3©mB„ÇÔgbØ"¹¦ÞöŠÔFBc0B"( Ò{›™0!ˆ%AF.I&\•cQ´{šÚ ´%j<8k€F€T Ïz¶Ñ{ â´F“Gä@Œµ¯áÁŒß€N F»g„øÁBpuºù–¿DV–!®÷ã7¢BÔÔÕ߸d!Ïõã¬îŸž#ÌÐëìÚûÿ9æ-ü0î|ë/»ÿbË;­hìE t=Í×ÙHWý—4è$© ~ õ®\ÃÌfØõûü½Ê"½Ü«6N:ÈØ‚Ô+±˜z™ NvÝëìÂ5„mI¸âE5ªÕÇç˜j/¼MŽ)¢. ¶Y!Ô­¾Êc IüÛPxjPd»¸­ß‰ù>ãu9atAÒ“pùÇÅÇõP$†*œ3)kY³eÜPq¨{Ê©,ÈGÒ©túÕ—Eû{†«gV »¦5r¾žw{ýŒ<݃~û P2 nÝCUi•B,ð›p¿Ÿ{þpnª“Ñí„vv¦é*Äí‘È"MˆŒ‚ÐCWF‚xœATOÆ9œmPž»Ÿ{ÞTÞ§^-4ó`vÁ‚T­ a†·Ø·‚û\‚÷ ®­ €_ÎüB†!|—4vY²DzDÍ<&<øû€L a;ÇQÄ]¯>H2¼D°0} ¯œG¸±1i´GE2ÁÛdŒJ‡à?ªð@ ¶”ä8d0†ÃvÁ lTÄK@|¿2ÚfƒE¦¿q8 碟PIê»È8½._´6½0Œª ¾ ¤Vô5¸Bt €©°¹¼ù΃rmahõbϤɄŒÚÙ Iñ‚²ýeÎH »GA‰ ¿H:Ü MaKQnàÀ¡¹Ž×ÊÿÏúœKþ ðÉå{ÿû/?YsCøpË#àñM"w S¨ž$ ¿±”mÈ+ .?@Î"þå0 üÍVï=t×oû™×Ô)Ù°¦ ì˜6*$ì1àð#fØ u4‘°ƒ2h@¹…ªÎ‚¸qŸ‚~-Ò¢ÇeLõapï ýÀf`Êy»¢‘oKˆÕEŽ!dDªŠØf‰ o[‡`ZÍC`CHP¡+yM­­U÷-»Ù2>‘©úÊù|¬rÆq¶1V_çHP«Ú 4,ûj„Ý IYS=×—*\(ëœ!ÐŒµñj{‡%Ø'¡KÑŽÞæŽ>œ²0ÏÂqªà“ ¤·N÷.±æ€¥w1dQÓAíš+y¬”†ÕR.m…Lˆ…ª7 å`’,’š©Y’åú9²Õ€‰ünã f€2ªgH7b‚u•xÈJÂ)°&¦[ ˜ŸÑÑÔ50NpýøD]£’°šq„ ‹Õêç5D„‡zŠÕV …è—È&Ø9ïŒ0Uè N°MežE.Åoµq¿Aª 6¢ƒZ bÙEÜ" $7É‚•ÙÒVà ÉøB»Ö· !JÃDOä¬0:ß{ó8¾`™’MOOh=g«  & ÃÄ»Q PDÅWè‚G4Ô8+=MYŠ]Žãå‚l%€`8æ¶ÿÓã¡TðEɺ™òý8ý¿éNλIuþ?ú鎹IñOì}ÜCæ5TžLm°Wóíý8zÆ&¢A¢){bäV/c-Åš¡G`lžÈSŽ1(Æ&†6miÈÄ7C«´Áfœ\(6?_àDx¬>Ž‹š,z2Óû…ÉŠh—A¦;—V [ª•ožÃ‹”$Å€¤RØ »†¼Â7,%P@¯”Çö•dèµÈDA ¢TŒ5µ Àý¢Ç¨ ç:õLÂQb0º6Só ÑNµ‹HÚ,M7™ƒ¦”×ùq¨õ”èH¶îŽ'¶MÌ@NÊ̇n; è©§®ãûoCÚ4 ­€Ž| Lr'Èç~¬i9N,hБV\TãauîJ–V.ûò—q·.h8’azÕkep&¦?£Ó¶ÞÆ£¢§’ 4'¸‚8}ñþ##€¾¶Ÿ?Ábà¹ý½WäLå­•–ûþ„!ðÿ…ýe‰_+T„ï&@dAÕ2g€²7­_Ó^,P#o€? 6ÒSíR"H‘­¹EKæ„ãcн“%$¥KÓñòîšn! „aZ˵wA(h” +äˆè¿4é*`l-þˆÔ*.ÃÉSª€±ˆX˜•É£‚„OVƒ¸«žœï:pè´%÷Qj dPY¤,Ðú°ë…SK5ÀF[«ˆ)V=Ã3åõj’ 9á‰êœ-@«âpýÞ ’ª8€©XQÒ –qt õè€8I~ùhn¯fœŽáW=@‘4ǘ»é¬èÂ-蓃yVJ0 V€:Ë2RÜ +½:«»‘oH @fù 8Ø…Oáî2Toj\ìƒGâóì$÷ÉÊð!ôášÄ…6ÕwŽ`i­Ãé„y«†0€ÉàãlÆ-@$šy‡tð(„¼S„h˜âõø+Ô3Y¦ â'¶¸Ã©xÄfjZáÌ.H:Åz;çŒ/zœ¥ 1m¼è ¢‚„iÅ@ ‰2½R•š¶€'˜ `i¢º(¼¦uÝÒî‹+³xžÿgÙ§¹æûZê_"ï 0&„9Mt05V™4-qF¶Y+;Z¨+ SˆÂAì@bŠ¡ Qur5‡ŠÉÛÜâ€ÕF!6Þ·ÃHÞ8Oä`·@dŽÂ …'M‚L»È¿\mkŒ&´Ä+ŸuŠB³ô䏸©q;ŧ =š›ÌŒ+Ìc(JÀ)>n¨tw2j€ì6N¹1QŠÂk„]¨ðO„Á¼µ† ‹d–s ,«šƒáV¥lÆM•L.Éz­‡4_Øv6{›¸¯ aƒÐÖ ´T‘¡ ½zÖ%ۘ¾«Nÿ, ³D- r1Ä»UH;BN÷WJAüL͘XÂWÓê4ƒv3d}Ã,#âHîñŸ×ÿO¢ÿ*Ml P˜j°-$Z;1nUÅ’»U%vªà®"ià÷ØExæ DJM¬¾ žŠH£NE@BÛöëžáÔNdâ-¨»{Xਿ‡Fú°¹ÅJ vèn‚Ã¥"@×*+Û 7ñE&Ú„`–¨@fwàÞ1h\qÊG]­ï ø²! á»w¤Ns £$ ~‰½âó‘´û@Çy_Ÿ›jZÄ3”Òì¿'ßÞ!ÓÛõe¸Z±$GI¼˜«‰Ä5U¾®Ev¸SòþÌeÙ•o:|EÒ\†èmWð9ÒŽeê[)Z$ŒmUHN•WK¾ÈÆÇ-MVÿË%ÃkgèÉû(ˆv«¹0—àÏ¢ŽCnúaKH %eH²€d—°“@ÓC&K;[P€w#¿—Ù,éˆjFRYÏË%ÖE„V·™5œ—Bcià;1ú2»0)Hì—ÌW Þ-Éãîp"´>4Æ*É ÕP£xû•‰r±ÀÕÜ4{(:`Á=ŠªP åT¨ínµ¬“ ùlêBQ@VãI ÿ8†”Gº³ Å O,Rü, ꣴK<Å™ˆK £µ7ŸƒP‡‹Ë0aÁ ™ M4àÔñ…òv¦©¬óS2¬±A}`aÚK¶‚#ÆÔgŸûùÝÁè“äŠ*/ø Œ~’†V´âÙþæbO 8؃M¶ì: ©P™µElÂ-o CfŽ_¸Y¤%-µµµ»Ž@¬öÃ@Ù{ÿŒÔ_ÂiØ/šaÑ8€_è²±¬µ“TW_€P²ÀwP* m]"µPm]·ýdŸ+I*¿æIá !•£}$¦¨ l‚6Y³SäSp™n@[~y„Äšz.¹ëÜ]ÅØ¯xù¦ùæÁàÅäB2ö:dEª7U³kÿa´@"ƒzF RA1é “5ÂvÙ_âæ&ñ€ä€&ÚÀàZõòb@‰sØÝÒKARE©l(8ƒ‚,üVÆÝÜiÈ1y¶€ãüâŠâö6ø4ŸŒ¸µ$ël^Û(”OÀp1µÒ¼¸x†¿D+Ɔ™v´ ¨6 È$^¨ AéP+}F†ï%DdJb61µÁ–`Þ_`®(ñÛ‘ñ&µÔ²½Âh]ψ¸îtš¢…ÐU\µ|!"Dh,EUµ?…9"i|8kjÎ:êTöUÃÔdƒÄ"G˜rp#Òw#ˆ†¦ ¹šZ èˆŸœÜ=©ú¬ê›=´?èáÏñg2Wh§é~èU­aØÍÁ:ÀéèÒúHô¤£2Y_U¿ö¿ó…›ïø9þtyK¼„_ˆ5Ã÷†ï?žï*ž‹v¶õôÛÜ:³§Sn€ê¯Ê½rPfr3ªe§l¡„BŽ&!Z8‚«©Û«8`W +‚bSÖ¸z‰üD’wÚãméC€5¦Ö¶}².Ñ×üÿÏúé’iB‰‘Ì)&TJà HoùÇä=ë Í ïï4EŽÙ•AoÜhÏdslNy€tó(ùf¢Š1cTÚêßO[‚&ÝPIÛÌ#uÞá’½—èÖ*ëlSÜ$È¢iÔŽÓÊ_ oYº‹H+¨p~°9{Y xÐhæØ+ä?˜~k\€xjh¿8ª]Ìà}|Óöd§3ÿ™[»?ÑÖÿç×Dh»ûî/–Aìùûwy6A\¬:äYüŒPøD2ôe¶b(à¨ÖcÓ$¿ü‡¸uǬëJVü[†¡FYœDÑV0g×fÁ‘[¡xàY¬H‰A¹£ôÈ@äÈehÚ8"N¨åÓnš:¦ûƒ¢ é)?ò8b O_ ½ø°6ätñ }`®aÏþYÿå4Ö}~Uáùׯò¿Ù^=%]¤râ÷ý8ÿ6Æûý\27Ù¼HÏ'ñÖhâzýâur ¾$hè ûâ9Ê@æ!¨F¡òå}4;tf›õÜŸv’¥Ûj$ :>baÔć“%PBhnoÌK†Š>SO%sà "D@i_†)4”4Cjë ‰MyæhßpúæS刎ß84ö§Gâ ?ˆÁ把`“À d¾Dú%ªÄó?²„Q—@¬aÁˆ“EEõáÝôî€Dª9Å> ps-=6mlKºÑ®àzé5«$Ö°=Ñ„AÅ"â,PJÑh ¿&š ÖŒÑj *6Ù…j %WT){ûÅà‹¡Ñ­¿\&Qi¿´T¤”Jý&H)¥I"o_‘³ÑÑQR¿/Þ?hŒ]d ½ÞèÁ§fÁ+ÄÖò{›Z(ŠžàxªJ>ñÜ2«A€IÖÚGÜÐËý@x»…cãöUN]V~b(ÈeÉ}švIʺs˜oŸ!€ÄhÒ¹2ˆ¢L”´kŠbÅfü4¹M\”QNŸŒžâ À$l"×0¾ [ªDúL©³ìlŒÀhÈ,jlÏþ˜ & B¿ûÁõ¢œ³ëŸÀ6OÃHõ¢éüb‰ 9X¸i^¢ª˜Ê4Ñm5±Úè6§åwéB¢»Ø¥ýËœƒüD›òáÕý— ÀÅú~¾²Kûq$öM?¬|Ó·ûÇûŸçx‘½ùÁ‹cn9“>çŸÿÏêbO»ÿ94–‡èÎñô_*à9,dÕº`)ŽÝß !G¦8Ížâ‹Ä=ó R6ô¿ž§N˜´KÑöÆyÀÍSeɵ¯¹Pµµ‚K?X+dˆ1rSAi[vë<ÍHª#{uÍI›ôÊH´+Þ”,7¶J¢ßiM¬¸ÓL”ÅFàEdF”Š*¾76‘šÄü°4”àª8‹ÜD¬¤!uAM5×¸Ñ ¥¤v7¨?X¢V™öV1-'„HÉ_`ÁóäµÃàðÖ`%é+ŸÃP¿YPØ 8N|¿y¦qôWË8;ÍÍ!£ñ5úÀùò º—Þ÷å0|(}P)TM‚kœiÁ¿…®«nCIÏãS_ÜQ>\>Lþs‡ˆD%ËÍ×@'¸*d˜ @AÛ¬B  7d˜ _j%A@:Ÿ¼7ýD‹"K é~pNA¶qÀ mFˆXI ¹ ³†o€b± c׿¬qäF•w¸öá]‚'D²xž¬oeÚ6§íp:-#Uy§™„Wô>¾ ûÃ}[ìÂ}@LÐ×áþŒ!UKó÷œôÓùÿ§çä¦kúïp”¶+,/&:î:P+xÊ_»ý9÷þÍ3·ùÆ×p‡QÀ sx`9qG7ÊØ÷?JÛß8vI‹€€ë]šM›8ù­ ótF£¶.Ö.p.·AZÂcVú×™§N`¯7ü?÷‡mòÀºÛÙÂ,ÅY¯ 7–j¸ Ó¦ÐcIi’ôl(M¸Z E)ЍVJ¢"…Ðúlg/aÖ0ôzŠëžbgûÀ‰DOÂ÷¦ é’ ´–yqdjK¶þw‚aU¸ß\ÈoÑ[‹c†BÃh>œ+«-Gb KõÖ7—Ø’,ôCpÅÇ¥Ô–bí,#‡\SòÍæ=6£¶.à ¡å–˜Bßþ˜ÂJêNÚ©Ž‰¸ñ®Ò“ +fÌ:=ˆgä‘©¦І+¼Â&!àÌÞxJoB䱘´ÙÇ"¬"†Oˆ{ïËÄöœÁÑô3Oe÷6Ot7ûË#$t¸Þ€=£ÌŠžŒ ™¾as…RŠ¥2²;ÈE€Ý Àí* dø›¢»N×SÒrô†@8€c~gÑ çZ>&hVÍQ¿è)†<õÒÿÿÑ+çüÿ¯sƒu×þáçœÆÏ€Ë¡ü-OÞÜç90¦!b™ƒúâ6k#‰$"øfÁôeºÃ¸·°pÆÈùòÃè`.íUR‡öÈ©‰0Qz0@> »‡ËlTv,Íý9 •Û}†-²"?‚|9,éÆ6ŸQO©ƒz‚eÄ©#2okamÆÞoÖÇB¸#@Ø®Î×6‹³ÔÓCÐÃ!q ,m»^¢.')o N‡ÁÓÜl˼CÒO b«•*¼§ôc\HÄ ¯ ߣ%ü€ò£0šÈ’ó ¥€y…ÌÔ~L>ÀJRèÝs/ÆÌT"‘?òînŽ"[©kÔz ×ðƒñ¤L]ŸDe¹Í#ªåa™X´;ñNúV¹ÇV<å\‚up¿VÕ„ Ÿ¼zZE 4ÀÛi©ÁUn…Æíö hö+õV@«`P¶k“éÛÀëpÿÆ4ˆ¤ú^™8ÇÁIRÝD%›ÇÝñyv° V±=ßCbGðüb .¹m2Ú‹¶- ˆmPo­Ú68KI*ôd8ÑOkqP©ä! ÅË5 (‰¶B.GèÇUÐm`¢.Þ³vK,úT4¦4 &–.ׇÑŘ<ò“ZPHénóbÀ* []ÓVd×Éh‚"ßGÖ:dr'a‹Fö]eITF-Ô!øPšfØn4#¾÷‡2gzQÛ…úGg™2ÆY–0£W#M˜—rºˆ§èæ™F÷ 7ì7ãóìb†€†øŸøÇÜw§0%"Á¼3ùïâj±µ ´æôõž¾D"|­8›JÑj×é…º}#‹‚D`q{Ç ÐA°-t5…'¨ Äœ‹}ýqkSmùy@RTÚ¦…ïq‚£¡¾ûŒÔPšÿG˜‹€‡¨^/¡ ¬0Ö‚ K³±Û·ÓhkùɆ0ŸÞ® ð_Ö?&¹¹;_ë#CÀ$üæ÷á1‹ö9-°Öà’%ùÖZVPØrK8ÊêöËB/€ªßæ¥(º az$œ¨Ý’ˆ47xm‹Y¯l‡ÇÙzzÄù*!iÀ1¯vg€J¶/ÈâçdQˆ¶ºýeòq ±n÷ßp¸€²u,KÑÁ3¡|µ€S†îûšÛA¶RÂ~¦‰¤¶o‚£ÍVCìÆha"7GñǃRÑ'E‚2ôa‘”ŠÐµ²«OpÎxÄ(¨:—ëŒP#bƒBÍݰiâR.ÐÂ8„.UÜŠá? ˜$Ü„ ¢NœiA°ƒh-’á@ý@€ÔA©ü2Ë“X)FªË kCak¥ÁÝhl!áF‚[–G5B…â Ò~q6êÆ¡TŠ.˜Íh*ÐÑ/ì0@ Š à#‡®ê‚äö z;Ásû_*Ãñ>7ŒÏƒ\@{îóyÁO]H‘µQlÂMz|A²y£€ã&¾¤6­ *{+f¤^í#Âà)ÖTPÛ\ç[Šš8ºÇÓ@4kˆC•w)U…E®ÏqYº àHy€h7úa—/¿SCmßú(œWMí?¿ñòÌ¢óð9@Þ\gëwˆ|¶_ûÃÁþp"B¼­6ŸØÞ~ÖàÑ«õY¬—ú AL8ŠßFBÉ€e.Êæö";|¥Å à ­4»ÌhMˆ—¹6‡»—A?úcÂ@îÒª€ªï¼pFb!Óæ%c×+-§ÉøÆÛ¶Wò6Iàæ;øu‚Uû‘ÞQW6Ûc`&28°]Xt(™¾…Mi· ñJü½êò„‹¡©£¯ãL "P5T,_ËÉg” ¯Œ Ü$ß ˆPŒ½Ç¶húU@eŽóð´ºGGY±¬H«á¶cåÂø˜×žÉ²]òcDðèÌ%ן‹Á3VÏÈéŒ= pôÁǰ\´ ¸‹'Q"€`á©fNŠÒ5…70ù2 UºÓL`œwÄ![¢OX¥Y§Qòb ¼ÇhA]Í> =`$t»Ý¹jÛ«ÙþdÅU¥!¸­ïÃMLCDP´µZîòä›}„Ç÷b?ë$« &ûa^Š»ùù!XÐ!'¢ßÓ`w…¯ŒÓ@Ö?:ÿ¬%:e˜4ÙýLE[`|¥S÷„•°’ Ÿ!¤?¿¬6 U!¿£ž÷ ¯Òq|,~51³EæÜàIø5„˜“ᢿ}_®Пé°çñ à‚ÍÉÖT‚ÿ‚chÏØ„ŸŒAš†¥68“ŠD uÿ¬fìOëÂüܨ²¢I£ƒ×¼ÃIæGá!£É<ÈþU*>ì/à6…”‰PЄü䀰€‡‚ø~¹UaMí«Þy’qÐßš3€ï§wj11Ð S]S C~Q ºÀ~²9ò.ã!>!êxHd½0àÓ ¹ÈRr°³“Œt»zsXbmQ%E6:êmU½ú¨D4' a“7EˆRkT…žæÀœ@Гa|•Ãe‚š† „„! ±*ÛšA ‚öÙMC$¹´›ë\†_ñ¡;iTéûdz*Àª€8s(l0Ü‚x«‡­`Y<À9Þ¼Š ÑFTª¬p .fAĈeÚlNyƒ(øó4Áª%Ý2™g…ÖŽ\õŽ'ÒÃNùb“ÎTE² G¿µÞ:€‚¸€Ô´Õ²âN¯½‚'׌ÔßbÖÄþÌ/ –ßn7õQØ6 ¾·Š ¸á@4­˜Yþ/}Œ‚¿;¹s¨tKàž† ³JŠ‚ìðvàWQÑ{ÿ'ßpž‚!L-jM[š-]x”´.ŽÛÊJ7ÛRêN›0OŠíÀÿÑ‘ýùu|–~.³¶2Ìu8©^oéŸôäÌþƒ?PþÑ㜂ñêž,òaön|°¶è‰Ö¥Þí žâÉÐÇŒ¥ƒ¨èÎ%.ݨƒQj~˜_´NdØ£ ¬NóÑgh£ö,ÐÈ|§üxÏ]$úŸn'sKúF–о/­pÎ7­B0¢ñq dH¨zSDNdk ÍÆŸ#9X]qTIã,LT m­év¢¢ÉSR:ãF>:ÚºÃë–ÆlFÄ] ÓXPIéÊ -†…¡òV½ã’µQ·eP‹/RƒQF„IH¯ƒ†ª=&ðü'¸—%yç êw„ˆWF Цƒ3cÎð8_ѹ6Þ‡fNF<䀡è÷d ­Mä¿"ÂLô? ýgNbžñ6M…Í¢fÎÑET­˜!•ìþÛðÕ7€Š‹1ßß]OFŽß 7^¦ð*–]9º"Œ³fŒVä:]EØÆ,æ Ù=bU˜ ß ]Ü-9ó€¢~–ÊÑ^°€9“oóç Oôy‡øº‡ë^÷9ÿŸç<ž’| ÏˆrT¨ÑRŸM.nÀˆÓ̽øÇ¿½ìS­×öÆÕ' øqQçðšæÖ×n¾1,¸ˆd}b{öò”$ˆIQuÖØ—á‹Ãcàd6âGuqt“kÈ…Eƒc&lÏRé¤ä5Ý’¬ò ‚šLD3bWcrx¦"(ô£òõÓI–až±(`pJ.a·¢90nÉ>P$&?³ºdÿKI jmÍ[m’ÙÚŸ…Eã`‘¢2â©°T(PâÍzH ¢¡jz™eÂV‡k¶8,eJãM5fêuÉB‚œó4£|:]†»¢1ÏôŽ«IhŒùÄ•„I[H¦¾ã]—\%=(鈙 ÿ{t2BÄ"374 *m Ö¾óãÒZ=(b„Þ s”|æô¡Þl÷@°½žó-ˆÁ:èŠNyûÂBá16ß/¬ ’z¥‘ð`‰£³~éîmäíòÉ+¸ Niˆ 8§÷”I àA´EóCˆkf Ö!è%î"Ž W_ùfÈ4t ™«1„Æû• ì@n °ÐC¼9»ŽŒñi8Iæ'j`w6‚¡–ˆ‰¹ã©/"Õ!¥O¤ÊnþxUö‘iî7E‹üÿ§süQ˜82’{| ¸ ]÷Ó™ñO¾aWUjShsmqý0qÝöä¦áMxcIÈ ¤r¬òL“yŠUHöÎ¸æ ‚ö°ìA(ñ{’~Âk@ݪ=Ø\ªóe q2X"¬ª`_” f¾ñÚ¯ÜL"2ÃZP i¢‘·E„0+Hâ6ÈÐ5‚é×4M¥X&&B– ‚Гæ®Ï½Ãc1¡ˆßÍ]ÜP¬ú… ÃДÔ64÷¸LŽt êOXŠe¶ H¢ëW˜Ã>~e¤´ÔÖÁ÷Öic£¦Ye¢UÐI Œ¼à %&À© ™´î5{0`%À‚^ÓµäM ì,ÃŽ¹@¶… ÕÓS&”mL¼m•Ÿ™TÁ2¢+£a æEû„ƒP*«›1ЀŽ3e{8©¸¹*Ÿ$·lv5fSÞ‚lY‡pðã½Ã5— £æ@Zùû–-%VË®a|üQV®›Ú}³oÎ(‹bÞÜÿ®cëëSÛ¯pH ‡~²^âÀÁ¥ÉA®ê¼°ôÅU¢¤ëÔ®ZE!àpÃÄ)„.²š¢/8iÒ•ж;ïp{¸›>u…ÑÑ)Ú$!!—Ì €îjO”…Ýc -ÀU/óŸŒxåË€ÿ×¼ýbX#þš˜sü*Ww†ž#þc¨l6Õ>Æ‹C÷ÓOòc3ð7 àûcÀdúáR}uÌj v]B,iÑïpÖ€–"üVJht¡U7§ß2êquZ'ÉÆMÝÉ¥¨m[RŠ-ëôÆþß‘AF™ÊvB«¡âq®³X­‡[ÝÁŒ†“*†~Æ?C°JT§6æÔ°ÝÙF½Ä't¡%)=ºíïT"èM­†ÕÔ5¶N¤/€Gáðæ+Ä>ƒd@èšó26wzaO‰ñ©‰6ÙAôZO$Ë ÕV“©=lÜØódköŸl2Bõd¦Á†Æ4ؼgYǹ¤'Ðy¸¸J1CG'©Þ«hß•¸ 0eÃä(—?n$Ø­`‚ÇÝèB6:}vÞgÙQi92  ;Ì5|ªû‘ÚÔVìÿŒµ“€+M'Kyîi‚ʺW9õ0¨uù`ûlÃmcªЋÇë QnÎ5y–,¶–9D#‘[0â!k‚¶\bU¿ƒAÕ6×õ/º%B.°ÃÚÍmØQE ‹˜²°ƒÚ€ ‰w¼.(_Ð ì@â<Ä…'ïü1µ¸¬1a‚OüþñUn¿ÑS‚§üž."»+¶]b †¤ÂRr.±Ô±×ÉnuÎð‘縶Û@ ZÞ4BÉAEß}9Ø?¹çr¬T-A|ÿaÀ95íF7®Ê]_¼u’M¦Ø$Ô›¶Ô¿²×ÕtŽ®'ì ù°Èà°®Ñè§ ³c˜BÝ¢†·h8ŽŠõt]ä1ø ‡]°lï2ƒE1XnÆ(G«Jº "T|yÌÔsÌ„×*èˆ(´8"T9ÈŽ£zâïÉ­šJÁ.ÌMÂPåSHè<|õº‰“oZ]÷ÿ¾çÜ/=?c…EÉìº,Û¨sR bCA@ŸÈ2:ZJQZS€á54PõL¸¤¥EZ»\ÕW|øÍ¬·àÊb‘ê„xÅx õ‰ZªÐ+¿Ù†vÛ±ÐÚmüàœù¶¸ˆ·.ì.*zÁé ®´ÏúÆŸ,wy`âþ1¦i°+ùÆÿñûÅ#L,>M¯Æ)A—&’/Ñq0¦6¶÷½a×Ô$ -ZqÑÓÜq ì7% bÓ¨ª§ïFHÜàs §k€‡95:•õ/ÝÇXDv*“Éþ’zÚxsü8Бˆø~>ðôSP£#½1Ã2¬ÇhP­ùÅá ôË?06i3EJÈ  ö#µ‚–¾¦¢æ"#c{¦ SÆð)÷$‚­Y<–lZA>ÔÈ…fç1Áši­ívÀá²öUÍ}îâÕ`°4NdN`ÛÔ¡Ó¡&Ìq[Žèð>üð9«#ÀÄ k_e|µ±(ܔᩆÇ%L4B[ÝÜ9•Ê‹mÞ<ÕkTÂÕ²l„n7LÍ"úBÆÛZ¢0¾«Êê  ":^"à ‘9Pª:Í¢Ø~€ºH½Â%A¢¹híSÔýN²‡@“Õª†;Vä¶Õ¶îªÍädV1б`\h¡3\Œ†Å`ÙVoZ˜bI¿Ô¨Øó-‚¬½‘ãýûqj¢DùV?Á†ÌË$GP¥Ý®ª¨öÌt#ò'X¹4B°Ti5I°¸‹oåê5 4EÞGâNé©#§Ziæ4íz×¾ŸÎYýè`Ðqî?‰‹µÜnÛ&ÝUÆ<ÈD›R³‰èº±ÃèË4‚D«>wžfÛ!jéáøÙîPKÛ ÐÑ­þð‰ê0»V~8G82 GÐ{þ÷|/÷î+®â¯ë4<®CÝh÷ºoþþ}À@’®—ý9ÿgåœ?ÀË<ÔZØc´¤l˜ = j$Â} cZ™¢ôxB9t/Œ¦£ ˆ Â÷¡© Déœ&Æb¨©.ïjœ1y5R€ZÚå¨Å9X1¿òkx‹¹Ñ;¨àb”.Èøu¢ê}=ÍZt†’YLc`Jv{«¸Iô&”H’}8„.J š3Tï¸V>ކ?`iÜ êleî…¥òoŠ7RÑKE´ïh|M(•(o>qR ŽQqTZ|ÖbÕ!`€ úËøçEeX“:œI”¯X+“W#CòÛYŽ€xÏ ¼VVÚÑ´5‡L‹û‰C×@¼ +NMò›¡õŸƒ•sP°æÌ¾˜9·c­{"a7ÊÓ‘ Íß'Ʋ-R¢„¤„Eü3j –JX]©‹SßÖžâ:Ó& AK~‚\Q#=ª\W§Æ±=$6EiÚÁƒ|ОµHìÀ)ÖÛ¼”‚ ÆÍ¡’Ö°í|ºb• Wk ¼hQ¦M-¨^³à¼h!T¡¡€,,Ô>Xô]cWqì‰Ä%@!‘%„’v]/¸†Ìô!ObSã ošÝ `û½ÜE4ûFºO—Ã5èzáø&¼Ï£JëxkÿÞá÷„càô@!wáÓï„æ QSï"2sʈ¾7XàgË3ïþF³üëè‡ë>ÎþÿÐŒòÃ3‡øÞ·çJ¤Þüé`÷ªµTH&÷•ˆ´â„©+×k‡;8÷¬*%ûÆ;4 ê°P†Þ~²¾Öð¼ @Úw Mr,¨ê’ë¡)ÆHv†xy=@—^¥F”6w€†i†…§L7av…Æ,4r±wn*­tº¨Oäîoæ IAÙ©ß]Z Òá)‰RAH:H2{"õ¸z¤ÓŽP:Ú%øÀ±kÕ¦’ø.;R"Ø€AE@jdí¹FÚmÓg1VNÛÃ[p¨y„V‚TŒ¸tl÷ƒÖL´ÆÈÞÝáÞÊD```ÚD–…e¨øu0ìzìÕJ¼x·&ÊËùùÅÉÙ bY¯¼ÙĤòQþÕ–èL¹4LQ-”tJѦ¤:íœkR’¡B‘ÇÜE@AÝ-e‚ný a¢>¥æJ{ÐhõWNMãSºêèj3MGÖF:£og/¸”ü–  s{s”BŽ‚ì Ú…ÏP6¨»Ú 1ýM¢ ؉L:¿ø!¥:÷ [¸jÂ’å;i¬b$óú‹Ÿ Hˆ1@ |D¢·z%¦µ‹·q@ë°Ñ椓¦‰J“—èCò `’÷ûùÞxý SOðlHÈù_œ³õó½Qûƒÿy4eùq;‡ÁÙ Uƒ[O]bίMàãt&õ©ˆß2%?‡ÔÍ­Ãú •p1;ó.Ç|0q´9rS44˜„ÐÉÒÔ´]:Áþ’§^²öÞëkZFǪ(Ò/­¸Z]’Àe+‹ªª6øÕë|&‚P[–y§™ 1QPÝšÁ?¥áHݘ!_G@ Dþ0™¿¾Î_‘¡œKëY§àþÇþ÷8Wxã„Cwüþc¶xWÍ0W™h¢›Çm¤p«¢I†ÒµkÎ#(y2™¦t3¼jö!lƒb,ŒÍ^0 CùåMÁ•—<1>š~¾¾?ÐEV¿gý° ÿ‚´.¼ŸÔ2:&5~ñÖu1–Õ&‘¤&±¿ñ’ÊVìMõ9“«àRƒB¬ &’4 mzÜ4£E²ª8 í1œtm Z"ëeò% )üõg×1Ö|÷÷Žt¡üQŸÖ%ÿŽí3æwc[XySa~‚7]/TMìA@‰ŒkÙ"DÞo©TqLñÑ„q/¤@Ç6a(ËØ 0ƒF*F&@vˆìÖ([eùÔŠ¡Ï9æ-?rÙ–‘¬°ŠÃã™Ð ýF2BÿB ñ‡?Åï » ZxjË*±Ð5@ (Ês~@éióŠÄÝÃ}× C“{¹>ú"†5cn!‡L¡ðiBUhØw.f¶IÛèz÷†‰Œó4îrmÂŽD= ÝJ§qŽÀd0Ï~UMC^Ð f×tU¸VÞϼZ ø,€P ­ªÚ|\&­,éÓS"( ìÀ Eó8Oˆ· €<<øÏ¾9`Mmë ñ¶hÔ&¥fíë™Q6Ù^ÖÜvÄÈÄOE‰l.Dg¹NÍEÞ<ú1%l"Žp·$V#Pœ9Ö ßÝ“¤*%ub§uNŠ!˜Q¯r[¼Õi$Z¸y®¨hžƒ§ìžcQedžÿ`Ô6(tRýcK`ÛUSެ)…£ö+ózf(ÃÇÁÞÑ´ù”Œ5Ko¢9  m±ç.%6ºÐòQu‡¹÷Á-bZ¤è»!HÇÔÆÂ v ß|û|°[‡g?OŸ1ÿ¤îá\ñ³:>Äî·½o ?¥„¶‹©yîZ @ iJ´bPRJ•=w̦7§†€ücmƒèÅ>0ɤ"ÿl9öÃbþ2Ö÷×``ÿú8<ÛHpv"ºÂØI•›+ klæ;65³|±KÍ …ß2¨aðÕä½!A `Ja 3˜uѵUUpÁ©·Gçý<þ3—øìƒ£Ñe€Ëá‰K0ÒÓ]óí¼tàV:Ò|qª‰•oaî?5tj[éøc Y<}‚ ‡5¢  É^ÒêÕk¼5éBÑõË GB`¤X…ÿNdŒÔ4MÊúh(¥}…§Ã8q„î€¶Ä W¹FrŒ»½5ÍLùJj!Àú‡ÜØOõ †’®Ñ‡" Ó@Ò‹[2ñ˜YH÷HÖ[³n)"ð0ÅiÛXˆ(rVÂaDCaÊýãŸ"V×4í;´5ÝÙ@yؤF²AÎù(ßG€Þ*I4s—réÚ)¼&èѦS¡µ àsUîp\ Ð·˜½¾ÀòQzµvãŒõ½#w¬fì@Šíѱ¼F]{-2v€;Æ‚2¬ª‚5õ¼ SM0|Í6vá¢A¢›wMš0]Üy¸Ô(ñc¦5ƒ¤=F2hé „ˆrD Z’£Ô‚‰·W"„-‹§ƒÔ2v´Òý”|\4köêú7<õºµˆ£ÇI–ßsæÒ<½†ÿ³€ÛCBAPÚVººc™‡x k!©„°ni¾F*+€p’l‰Û¼æçp “ó3kéMp™ £ÔGfô08ª¶ƒn%­òŽ’ß6Øâž• 4P³­çËíõÿÙçú3—ø[²¤´·[“(2R±µƒÆšp¾9¶cˆ€Šy…;Ñ8Fƨ»CSPÁ†ŽÑ*Ú>âŠx¡6<¢µÛ‹‰Jšj¨µ¼i R¤íý0€tL+5©ÝIæHÒ8š›ØÙúÁ'‰ÒÞooqS“Iò5~<À²uñ d °0‡z>ßtíе¶<‘Á´± ©«µ¼6>oã†WYü?ûBÀ**‹ù`-v Zb*ȑۦºéj0—¼Þ9ŽŸY Iþ•{àˆ7ßh¡ÝÃö8Κ†¹œí^ ÈzVQ6®èÄ64p)`5I]$hÏ9æ! *u÷ïJ €|Ý:«7ìǂÚAÛ¸ —˜¼ÃÚ%DUWŒTïŒí=C€€ð  Qâó¯¥üƒã‰F0wE«&¤Âê$(z§ðn¹ŽZªÁN•wš³ÌìgHüi{úÈÀ nü&hÿ³L> wÌ‚ëÚ]ÏË‹T¼_ºÙ·›À7D¢V¿M~Æu*öEv7ßr$†³Æ6—ïƒ)¹{Šø`Xn¼õ„1à‘]fOV0›¡^þUÇ…½;A6J†þ T2%´°*T쩚6ç¦ À†¬óY2’šÍŠoYü¼`HÆZʤw_N2î\€«4ðÈ4rñ«/¸é¢þv›hÙæ)ge~õã¼¢puðoá•S1V@”"::dq šèÕ£50ÁÉx›H G˜/Ñ·9µÞ“O3Ú&Aj9Qv´¹åç 3¯Ö¾å[[š RNÐÞ,åb£@E6ÂC¸ÃC¡ô_ÄH¤nQµ@«Ìj—KG ^tjëßr)4Á¦Í8OHsØu œ9Ëõf.FŒBKUE¼T¨›$ŠuI7–Dð1ˆ(tQŠœªI©é"+D€·Ô$b-)R.Wµ¥èŠTCqôIŽW0QvFˆ†uÂI"ŽØ @¹Eò HÔ †¸w&VÔADG!Š¢¿ u ’üÜŠOchˆz2'Œ#®Ý€ì+Üb˜ àø©Õ¥Ï»èa•ÙTx†ƒÌ”2 O:³ö“q¥à‚“M`C6ƒëãëýäB„´26M+½£'ƒ\¥@"!nÞ $Òî: ÖßÎóuE1bÓhÞ˜ÐAÓ6LQ”ÜP¯CBhOŒ¸–q˜B¥¶„<˜Ý­Û’>DÚ°Dù&i&ThŒTq)©hW{œ¾æ3jlTèoÖfy âJš—0¸©fÜiBÄF,ªÖtòÑÁÏíw5>*7Ñ0€4¦¦Ì0xGåG.°Sì¼²T‚¦¯tóQfEH@¾¹—«Dˆ.<ùžã˜j¤>Œ]¡¯)³ZÇÀ=™´$Ô.þCBµrqÐÕ''>pÊ„‰áäS6‰(`·Dýß–ÍdÉÂÊ;GQòk¸òǘµµ|™“u ª32ª,ÕŠpŠÅpbT_7´û˜¸Ó˜(ºXsÝ?—ÛŸ¯ôyþQª+" íÌU˜R@¢$_^Ã4¿ ºAûî4‰”#6¼Ö¡ƒ\áF„;5<‰Y…Z ªƒA¯¼Èêœgl•Põ„—1Da@; =f뫺Œkm|sxßö¤ÔÃðbë­Géá õ"ë '@DC dÄóšÌ Aáñ2°Ñ Ä¢)]¢û‚àé«NÐÂVbÈÀ€$#Â`?Y¢‰ö 3­I7Û Å„`Y il9@ víp~¹¡H%¾OË £åiF9^5 BµšÚ×À=f§Q_Eù:¸* ¶ü*ûΑŠßûrÕÜ+*ô$ÀñQdëÌÖz„})^–RTh€‚h50'vô„¸¡#¦Oá¸À$nid—1•eb¢B¸_:i­»ýã°yí»‡Ïÿxš€ƒ2R¨ÌPÊnŸuê4þ\_I`%é vû“PÓâÌ)ÍgÔPDsõˆ’£î¡ÿ†s‹Tæ[õ÷´ªÜXkML6f!"]õæA{b|M­ÔÅï ß*!­fÿÂàîÇp’FÕ܉üOÝÍfÏì |·Ìó#wrns/“6ªB+äÓ3#ˆ+¢Ö–*©XIïÔøæRy䛪,Cà6iHþ¯0lDˆ ¤(ÎômP—hÁ`@BŸ)<Ä Tv/§ú‡?Á ÅMZGÍ. ÁÂ(û.ëË”ëz\h¢5® ®ª:çämK<¸ý]!Z·Ãû …Óíy•uŸžêPn»æán)4•/èx`µ4Y"íe_'}Së MdÓqÞ`á05!ß{ˆÍxiSM»0s~©CKVZiÍ51„ØÈ(÷žâ-•húÛæÖû…=^y rSóó¼U‚??x¼ò·Xi¿–E?\e:øÅöL'ÑOE…2Ò®"–VO¤*M ݰd®”.ÝFðœáX8ëF¡4˜™9Ž©-Ü‘À—Lj$Ê6°EÚS\i 7A 5ø8.lu¯iýà1Vú€FÄÇ’/y†RiƲ 4‹›Ñ©Ì9¥¥îßGxæ_N©‹ µv÷rÙ•88Þîà`? © 쿌1þ/p »†=œ‰E5 “Ù$I‚þȺ¥jÁ{׸´”e¾½Ây«©>NþX¹ò]‰'|ýb# ”ÐÖX4Evh§N=Èj“äÄpMPß¡ÍÇ•ÐÀ4Uñ;†1ÆôU7¸ºž\jHx†€t8÷ MP(:Þ“øb£qgSGCÃãi@¢É•1òFYóäiË!º} Ôª‡Ã£‘úRû¢Q­^µXÞíŠ!f–¥#ý®3»AHV°•‹zˆF¦¿7&ǵ+_ôpsü- -*|öìaTzK³vÙð3Y2Çdúç˜Ì- v/Œ>BT€–à*Þ`QEøÐZ±y1†½Z-‡@l> yŸ×©›MCÐCÙpÊ‚ B'E/·r ÔwÓòÀ©|+ŠI€J8sÂ1BY…¶Œ‘&±$‡àèþsÎèJ®íEOTôÁ «WN—ÊÎì`õ·¸ú)ò½À¡§¡õƒóe†RZ Äe,ÂÁèA—ü8ÇIÄT1 ¶W/DªyUtQ®»Ì:`B^—,(@Pâ0¡;[›Œ«’5‰üq$Hajc/op|£ù»ðÈÄÀ¿„F¸³˜‚¼ y9F“MX{Ö ø‚¶Y´´Øü·›’8@DŸBû1|®|_°½§è7qe::h‹è3È“£P!¡¾ƒÁ0Ý|¸À¢:Ä# DˆkxVT«Q³hQ.4K(€z+¾cwï–/y§èzõá#è«Cìç1­74ˆR(ÆËœ[•4ÁÐüÏ“5G0êewp›x…)CEÖÐÔË‘‰T@Ã}#M\U¨mׯù›°¼:ëÂe&ªkP&@†öü*l¾à+§„„ôMôœÅ…U€Ô¨³{ö•hÕœ¥ÁΊTêWb§ ß¼A¨²Ë0¡ˆ*†¡Ó—`é#t u}ä%5üï¿¿ôsð³ðÀ!ħøc܉ÓÈ0i«T7Ð>0h¥;¢3¬®sµ H‚ww*M¸uá/‘†^ƪuŒPpC^e!†ïø"€år• \>±®ýÆ-QH6¡[vo¼]ÝӤ倆šÎé2ý”‚²µVÖ?-F‡K­=™÷‹ÓÀ]“Sßq-)J wÉo·C)€ºu o³ãþ³±”áF—êåAð3 š„‰îm–JaGäL®â5×M4ÄfŶ?0›";6Ù€TµñDÛP •(ã+r%É˜ÛØÑ‰ÆgbiRŒC’öº# )Víå”JÙ¸ll’±Î3Iù…˜çØRµÇPî1OrãEôžsÌ£V^ôÀ²£oÎ[ôÞT`ƒâmÖO&ñ¾6ÇÏ¥Tu¬j”p]˜tÇ¬ë ƒ;k\ÂCVW5ßFñDS1@1Ð@ÁVñ°©y÷°xòëžaW9Á„ZÒM|'9ƒa`û¿âýfê:tçS Õ¤°P-OÉœs€ˆPõcôÉ_’‚„ˆ§`Ûy&Í*„ƒÒi®D´^ëM˜„÷µI7fPV×ðü ´Øø<ÓÌØ ñâµßïÜ TŠ !¥Â “d`d½z\j}VeÔCp£ÃÌ*×X(´Ë3oƒ&„â"d’n°0ÔG]Ùª½:Ke߯"U À{‚YWZü8 ~ÄuFªPÞ|ßA-ĵ/\vªì- ݰÂoî×ú3ÓÆ#Ó_ãyØ Æáª›æjî3J‘ýýã A ùlMÒþX QËd.ý_²W¥iú¿8`‰²×ÃÁS¤<~´Ø×0Åt•$ÔHzïœÍÚ—ßÏ`M }K?Y3 6·ãžbÁ6hAÓÁæl£¢6üëÜ7)PˆéQ@ê]â]X"Ðv'ÉR$ rÖšÔÄvа§b piY0AàËÁ'â~}NoEÎ Gà88«n­Ÿ²¸?8(cÚFÅyƒÂ:ó@!"C‰ùSF5òÿb¤b¤˜1ÊÅlI‹š‰}D–‚ÿ£¸}|ƒíqìï¿—¹fǪ›õ1AM C”þ8s”[-ÿŸž0Mâ…ô‚@M^b¡Â%`@.¿%Ç´ÅK=T#¾dìÞ(¶>0%7E¶%Vïç•dèBJá_5B€§å’X—©VÖtƼä‘õîG±±@rd7õ‚±û@„Ofß—ê=؈û÷ÿI =åÞ/~ЋëÍ ñèBÑJ—ÿæqôˆ%ò‰õ‚q½¶•[¡ßŒ2žð›$ø×&vPq|›®ÿèñÏÿ Ÿþ °å?Ó¬ž±†4¿áG}ÀúÇø×¿áøÿ ;ïø Ïð#pmpÿ÷ü.3_¼YàÆGzWGltjbEr:£! iŸY¯ßø’‡šÿ@…ßÁõæCvo{2CnkƒKþŸÿÙntpsec-1.1.0+dfsg1/docs/pic/description.jpg0000644000175000017500000006515213252364117020366 0ustar rlaagerrlaagerÿØÿàJFIFHHÿÛC     ÿÛC  ÿÂÚ¾ÿÄÿÄÿÚ ûø™UrZVU•P¬v×c¿I×íÏ{sZ¼öÏߦ¸Õ›é[@E¾O¹Íðú27¯âp~o©‘¬ì/(÷É÷$_Ä‘=cxÖÐS̾p‡vcÍܲåÍb¶€üãsî`,×$u  †e'z=0È´—€Èt™JƒÉP¥ ”* €j x¸¬‰‰´ý›±oVúw@ ~* $O ñ”§u¬âç~¦u:»‰‹“Ï^úNtÝE¥•$õt ²i2ÀÕ96€§èã3ðêPùøú¨,I>‚ÎQ%œ^îæ7—õ®ªãÖ.—,.Ø×c[¹zž®o4 ‚k3Õ96Ä;š±9¶Öec§;Ó–ÆtÐëžúnÂeå"nt8¿?[ôPX ’|’‹ê€ ( ”€±M³Y˜¨‰É¶!Ûjf[‡\ÏÏ®ãÓçØsﯼë5IrޝxÞbüýoÐA`‚IòJUU ó-lE˜Ó¦^¹Š€QÛ5™€jȘ›@ÈŸ ‚Á“èµ|íòÿgµëÎhö~Rñþ«g®Zþ¬¾¾|ž¾iƒÙù¿t‚ ¬Ì €Ž`¶€5Kµ@/¢w;¢ ²ADö<ÉEÀÏkmìwçõ%Ns—·¢ëâ­ž”I˜ Y§<8£µ¦Y¼2<™ 2øhè\%2a/8c¹2c³,å¬*{.€#r >º s™. É`Hô¶4‡puE@<)ˆBá —Èü´T&ˆß€Cêά9“ž(¤Ûº:Ò  b&ÃÉ#À¨I\’Uˆ­ F'^oÀ$Ð µbu§B¶C„ÎRAR•H Åua²]/€1\œÀòAdä\>GÄÂs»þ¸±/šfæYÙ¨aÓh‘9²R¤hs®¯-Î5’`/kftµ "ä:ð’ '’9™ÀÎìtÎÚ<®ÁšËSÜ´ìÖ &s0#:“7¦ØÆ,Ó¢Ä!dìã›^δ1….ë|â©©Mn˜äKDÄyBYÝÎܸ[©&Ng:éä–¢‡«bca‰p»sé9tÄgefÕ¯œ‰œ”Ï2(s\ºr]qÉk9vÖ÷ãô—-€=[šÃ¯2scíL$˜.tÓpÉSéÒÅÓk2LlùõÐbð=yúη÷<¬×s©-Ëó‘!d,}û3V3»‘ªÏ~×Ñò±5{ß:,:‰.Ktžsz~sEßœ¥ŽÊõ¤¦h8{y>KcÛ†ë^>{¿ÓWÏÑÚz~W)Ãß离-ü½§_.Ç\¢Ã£;‡——fþ¬«3q¨xæO¡Ëy±/ƒô›ÏG›·ëñ¸~gÔi¹{7ÞŸ•®]‡o—êØ,š Â&cØ Óœ6Ä”v b¹9€ bs/Ÿ#âa9¾¾wŸ¿}×ÇƒŽØ³®v¸çk†›Ÿ¯ ëáÀuë:øbÃ¥:às„¦jq×SÇեϳwÓŬǧeÓ†.Øzï—<ø—¦û~MÇO6Ç\¢Ã£;‚ '“Æ\o£¬Ï¯«ëótöì÷絎œ¿¡Ðz>iÛæz¨,š À9Ó’;3xÄ!bt@Äæ^€ ˆó R*P¨CÒÅgNu Hå‰X¤”V‰R€Ub…JÛ1×,Y=d +¶ &“,Ž@§[‰(›K€ÖŸ?œ‘à¥R€:èSvÒŸ<ÉR…J(T¡PP©½>‡7 BE&)@T¡P/’A6€5¦° ›S<¨<šÓZ3Í¡ìàÖðqµ=€!¨-=›c0¨ÿÄ3 37046P!2A`"1'@pÿÚÿÀ ñ©^’^1FcÚåúo™ªÝI†Eɵ2|dÂÄ9Kú{ˆµ”q„Wo)’0€V)Ò¬rñbÆ8-‡3ùøåÛ7#Xïê_¯„AÌiñ”“2Ü%É¿ªC}ÇýZL¯O\øW1f›¶ÿNþKá³ý{X×¶ {`×¶ {`×¶ {`ŠŒ†öÁ¯löÁ¯löÁ¯l JÌG š…Pžc‹ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£ˆ£‹ÏXÜ(Ä)«ñ'ç1)® ­Ç?¶eÃ¥&z}àé÷ƒ§ÞŸx:}àé÷ƒ§ÞŸx:}àé÷ƒ§ÞŸx:}àé÷ƒ§ÞŸx:}àé÷’ŽØÞ‹Ÿ2~5×Ó×ýÆí“°²ˆ›RÁ‰U³Á"ìPÄî£ý‡âIé¿ä ÿ¨[;é Uíâkf˜ê–†o!sfÞ2Á2â6 5—™qgf‚f³2ܘ[м­M—V±2ǃ¼ù|cGÐük¯¦¯{‹Û*ÒgªsTzÞ6íºmÉ#;¨ÿbxŸÒyîW‹ÆšUÙ#®ò©.•aÜÞf‰\“l²uDŠ{«ø V]¬Â¾fQí«ïÙHtš»Chñ2ö”£¦EÇ ~=äGÐük¯¦¯{ŠžtŒ£‹vZÇu†N‰®É%_emJC ÚR9×¾0Fh¶Ü•ÊôfêØf̬rë‘+ ©‡O±'Z±#|ãg%$¹-øð?¤óÜ®ÌþÅ0àÕ »&æàÊ8®’äšê‡ã”lUcSÎv´†ÖÚÒZCkHmi üjf®mi ­!µ¤6´†ÖÛ`Bd„Á1ãŸÔ¶±n‚”t\˜”(’†‹ :,t4Xèh±ÐÑc¡¢Ä]6)Íg¡¢ÇCEކ‹ :,t4Xèh°Zu»<4¶*q¡ÜoÙ"®MzùŠ)§ñq<ù¥ó–ôZ{ÇÛþñDÚ^Ëû<ý5KÖñ{>ñ;¬IË'e„W³Až?©¡v„çáÖ‘ñ¹}‹ôýÒMMû²QîÛ[BÂé,Y¡6´ì ÌÕ_;ë¤éeËìXϦî?¤÷Üq1`N&mìûÄìO¦ç[MOJÉÄWR°$‹–ö(wA͆ Rà ‹Ž¥†ÚÓŸ‡ZD;÷‰CކIãtçâsw‚%l¶&‰6,ìJŒ±Â ÷©àöØ©J×üVôZ{ÆöÄÕŒ»{v¶'#úO©ÑÓZåšQrޤ-8–‹ø–K][ƒ«*ÁKu\ØüU­Ë¶¨þªxÎÁÈI¼g 7ëå[H@:we«?VÅâ&GÆìr%ðúR2AWVVÖuíéâ°k; -Œ0Ô–&‹NNK7ƒª5øYškMû²aš²#Öš3«ÇVå–qY”saB¥%”{n†)(1ÖŠÑ¥g­¬æqúÑÕ×Vg%¥9›Ÿÿç±øÃ¬µÆîå~)×cmhKGM\ÄO22SrM6—ó¬U”¥;ˆ~¬ªi>GåDµ¥P«f¯4b@@8‰‘ò1ñÿµÕðVU„wź˙$VIÃ6‘61 ÀÉó«Õ_©P’lî䔉ÅýeÓ¸¶•éˆé(FŽXÕ|VôZ{Æê'™`¦¶ôâ½&h9“=kVÃf±ñÜ‹†^äÓ &U[²¢V`T}PþaVow—…bë.Ù-3¥eNec²B˜e¼ºÊŠÚÛ&VÏŽáÄ|Šïr._bÅý+Èì<–é*Á¹¥ t#ë…cšš¦°È2JJ mr¾Ò›ö(‘yÈEïs%,«Å<ËDó.ë&ßòu[K»O ߪáþ'\£¾¸âxõD2ÙS®È\¾Å‹úa+Úiêu•ãÎZÑÍQ.<Š%i5ù‹Z›´½Œþ…RÂï0›Ã‚ºUB"×xu†ø˜pu7g‹I'0s¿u$²R^ýâGÐÔ ´t–1å ì„=îIüÙèÎÆÏ×ÖqRí[Ñiïs/r û<ý5GÕ Æ¢ºÎ¡N¤¢ dÝXžiæÀ,„ApHÔI)±"Ra‘K–Qed¨¹}‹ôýÒŸMMû;jGU‹IG±± 2bá’ÏHh"gˆlTC¥˜øèÇdw±“ ⺖^7Xí+f"åö,_ÓwÒyî0ÏúÌ#SE' ¡¤2GJdÑm“uqß§˜°© UàïÞ$}ÓÆ·RÆÚ§Ò†v)ÃÜ·¢ÓÞ>æ^äöx>újŸèëæ\~ÊŒú~é?¦¦çó®_cÆ}7qý'ßãñæ9ÿ/‹èú9Ly¥.Áæôgüš¸jà9«€æ®5íæ<zè¹/ìðrN4dc£-—>fà9›€ænš¸jà9›€æ®š¸fà9«€æn£ ²,ÓàK¹Úzˆ¼Ž|Ò_.n|ÍÀsWÌÜ5pÍÀsWÌÜ5pÕÀsWÍ\3pÍÀ6c$ùû4øîÏíœ+Æj)lK<ÕÀsWÌÜ5pÍÀsWÍ\5pÕÀsWE­Æ4$f[ä˜òOç¨Ü§rcnLmɹ1·&1ž2š'gûˆq·&6äÆÜ˜Û“rcnLmɹ1·&6äÆÜ˜Û“hB cËÙÇžjC¹1·&6äÆÜ˜Û“rcnLmɹ1·&6äÆÜ˜Û“r`AŒyc¼é`ø3ó¹1·&6äÆÜ˜Û“rcnLmɹ1·&1ž2DJOüÿÿÄ< !1AQa" 02B¡Áð@P`q€‘Ñá#$Rp±²ÿÚ?ÿ€–©eöá?gjXë 2ˆ„hT¶èÂ:Z €Z©ìêÄ<‘ïGIRL ÊÞÂ?RÚ cIÀi̮ޕZuÚdzπZªn¯YÕX0S5ÔëûOöø$Í}:-´“âø…Þ ó{nà?õ?ô´õYÌx|wÏë+dvw½>“ûgû*õ{J…çðŒ¬ú£€Ž ãÃõfãÕ3ŹpEÐ=xOÂk¤Í £á6ñM˜éd©ÊåÕ)æÒ£ˆCï’2 n6Á]ž9úðaH@sy>Ë®L0GEP½æžIŒ´*‚è@"‡Þ§D7”ßPâœ`Í[¹]-Mcœ`+„€S°%:Z`¦ðM?ƒìž«ªn;Ó²AWnAØMs‚Œ£”óq’™”Ñë@$ùO‘´žDøI¾ÐTßKέÒ! /ùL/¦-7Iþ…E­5Û—š›ÔÀ9·¯õUYN•'=Ìã¿ôSJæÒÃ?%SEþš¡ü =V“HE!m;Œ™úààþ*S«½ÍðGYUÍ‚UÆTž*Sµõ #Oš¸þ§¸}ýCrpÌ)Ħä‘ÉN'ìTÛq…9òq…Æ>Âü6QA5ñDñò ð€™è½Û¾É(ˆyg$ÜùLO5t˜õAöä*m e»)ÃÃ6Êrâîi† SjE§’=FfS­L9õ-“쪛]Úp“ްµšaEöƒ!RÙ7i{{¾“¶M1S²í<\qõ*¦ÅÜÛ߉ÝòMÙ4aÎ5|"8sT¿ÃuïsŒ}BÔh›M­‡I<³GN€†¾HßèJܾkÞäƒ<ú;?H55…2è]×@RsûIÆ1Ö·e²“\YRëwáhô¢©2` èlº;Í\Øú…ܬ SÄg˪©²¨Tu”O/ˆ•¬Ð²“ö>Cºr]ÍIõKXýÀp滢­a|:HüÖ›Dsžè ZŠMeBÖ™ôg ‰(a÷!è´ILØ”Ek;Yƒlšn`Š™39*tïph]ÕH˜woÆïê†È§iy«áç »&ƒ[i2éùJ©²ØÚn7ø€˜þꆎ¦QöÎ︩ë^Öµ£†U ¦"×·‡ÎU]Ckºçª{G²e¬ºj§iÚ@ž=U}¦ç°46^zóL³‚ïzžð˜ÝÑI9Z­wmîÇ­ÓêE÷ÍsšÛÛQ•žèl5ÛúªZ–R>‚»ÙÓ–Œnè»Æ©sIá?¥ÚnekO‚Ú:ê/e:tÆ?ݪ`´¶w|Û.9´HÊ£®, ïU«®/>¶a §S´/ç”í®ÖÒ`cD‰Ïç*úm73zïwÎ ïê«m½¥›‚ïJ·‡}rOÚT|Ùã{ÜÝÉSÚE¢-ÝÓõ•ÿÄ1!1AQa "02@BP`#Rq€pðÿÚ?ÿw˜”Úì"g°=wF³qÕ…(Ä ”æ‘鿩ֆ’DG<£IÇnK¹7ÿÜ“¥€ˆä¸™Ã†©‚âXò@?ÛF §¢vFa’'ß&;*Ssg¢äµmÃDZQ©j# ñžŠÓ¢cK‹€ÙDà $žŸzf$sNË¥VýL.ûàéï‘(ç*÷§tLÄl˜-²y'D×Cîì°C‡4çÌtTÉa$nƒ †¨çî£éꎃª¨Ë&vGe(º$¹Nô¶J±0\è Â2žÛ]~Ì9¼‚<¶U|ʜѭ™ ¼U=z«“=&Sp²žë(|Âv‰ÀŠpã¨]öL;*³œH]èn…ÎŒáS/7¹2¯­¾­SŸë>¯áoøˆlh…0<‚‹A’£û?¿ßZéq 2£t[9£…’{ (\–Òº¢>B˜½Ö é0¡îJ €¥"4 3oʃ-šp´¢œrB¶´ZžùŽŠ‹ÚÐBcáòQp†„~"{“efr¶ìÍÑì=²!;HWIoDá-„*doäŸ.øGâÂ:J>R cTìôO|’¥ªº’éæJä2ò~U­†É<ÝÙQ·OT_-—±W‰sI†è…grTŸsn^$^Z¼A²øCЏzBX_@ éùI€P·á]ùaT}­•ß»BÜ”î$‚nPâë£D+’áQ©ê€>Äê-3Õ>/ƒ,¨\é+Ã6ÛS8p×J@^)â*vϺæHN¡%S Z9Âu2sºðá ªü=ÍÂ¥N •á³2¼?TêRAä€éÂxsõæÝƒ¢î»¡DMáÚê›A׈щû¼d¿ÿÄY   1!"2AQ” #56a•ÑÓÔ0Bqtu´$4PR‘’¡±²³Á3@`b‚ðS£Ä%Cp…“&dr„¢¤ÂáñÿÚ?ÿÀÇT–Dƒ£yÆcºúWLèÁœÅ9ŠÀ»ŠŠ$2d¤]Mg€ù©Nµ*;%YT´¸ª ìœL®6îF‹KIùκ T|î¶Ë#:[ƒ5%²¤‡.}äÿ´Ú’´ MÁÿOxFB^$í«uò'gsûQYs T¥̳Wo”•:’¼écnº‡ C¨Û²ÂP»uR Œ5 â—!i˜¥‹¦ki¾ü  ÇGÍN¹ðwdñføºÊ9AæV†Ö¶ÉŽ”³ð‚¸#šcå¿ëR ÈŠÓ²•ÅEö¨%¢[^ú‚E¯­E}€˜¸Š_S1ÂÔ.àW'w›š¿ôÓšÜÛHOÌ?Â7îð )JPN&O4qHçúžâ\R\O5VÞ(#CÕþágÖ¨ûœoð¼ôᮆ1¸²È*íÉ×ÏXìŒJfÖ²¤$°”•>BPBÓü ÄÌ.n"úJ›a&Ü‘k©Gä§xý5âžú]ßw¯ðÒîû½x§ƒþ—wÝëÅ<ô»¾ï^)àÿ¥Ý÷zñOý.ï»×Šx?éw}Þ±%ƒ`Iã’Õ)ìØË‡”­mø>žjñOý.ï»×Šx?éw}Þ¼SÁÿK»îõâžú]ßw¯ðÒîû½x§ƒþ—wÝèÉŰ˜ˆ‚Ÿí]ƒ1O–‡ç)%´nù¯AI!I:ßkZÖµ­kZÖµ­kZÖµ­kZÖµ­kZþP5Ž_~L>(k¹&ÿ¸|yJ…ÒuæQR¸ƒ7?èî‡Å#ãST‡]a(+ÊyvÒ¡ªGEyUÂý8žÂ¼ªáêDöåWR'°¯*¸Cú‘=…yUÂÔ‰ì+Ê®þ¤Oa^Up‡õ"{ ò«„?©ØW•\!ýHžÂ¼ªáêDöåWR'°¯*¸Cú‘=…yUÂÔ‰ì+Ê®þ¤Oa^Up‡õ"{ ò«„?©ØW•\!ýHžÂ¼ªáþœOaSðɤÌI ;Í™(h)%Kx+˜„îä'òy®ý'ñÉï¢Ä›}‡% YänÊOʽú-KuÇÛTk7°SžSÙÁP²R.w î¾êŽê±Ëo7´J•,%gU‡!>uXRpÓ5̸;-ŠF··ž£©˜X¤—Úœu–™ÈéJò+0¾û(dæ;·_¼—õToæÉ¡ù8×~…øä÷б,%¬2FÎ#Ì8‰o©®yl‚,…~eFˆÇ°Lp3Èv2ŽÍµ$©.#{gŸuÇMI w šôè,—$<¯Áì·JHäx[m¿-òùê\v`í1h²TOöml.îw‚?³¿Á~‚×ðð×þœçÝ_î©àmưÄbxÃ3âaëÍñ;IYÊR3¸To¸ï²Eè´Þ&™ÎG€˜PPÜ0‚„í9Í×ÊU/ 6Ò âŽö(©¨+ˆyZe¤2øHÈ\¹ºœ7å|¯5qÈXË(Å n½ ;ngVnJ3Œ¹z9Gv·¬BRÜ ÈÁÚÃ’T‹©ÁŸÎNÓMÚT8¯É@y§ØyÇPÕ‚Ën%{“}×ËÖmç¨OáóY…*:ÔBž¶BI[2}2ʧ*B‘Q”ájÅD›çÖ¿Æ0u§dµÅ|"Ô„!JórR „Üe']ôÔ„Ó´`,àÍ)x:ŒÃ7+x±Òþjc=¹“3Œ9²³lø4‘˜Û}Mqq> FÑõ…¿‡gq‚ëÊs3*Ì 3ZûÇ$PMɰÔ÷e}Uù²h~N5¡Dþ9=ÌAiŽ©hƒx|héPO+ Zœ&ßæ·ÌŠ%øpáËjJ£ÉL¼E,²Ú‚R½ÎʺTäüö­¼\"Tˆ¨Â‘ˆ¼½  B•”¤'¥WGÍ®þ¶e<Þ—•°eiÅPb+‘›6ÚÛ†ëoMïÑÓPœñ\i×$È2³Ž–בFÿ(u[QGàF޶Üc%ç’‹¨“ò”JU»Ìwõ¢)Ll¡L¡à¹‰KÙ ##Z¬ âçwšô—†ª&§¤2™EðTVÆÐ«‘nm›VûôiÓR˜lÉ}(mÆY‡ˆ6ùP[‰l%DnBî¡»N¥õØpã¼êÙmmü"‚ög »]iï<ãIŒpˆïbŽI•‘±+#a¦]Ér¬§~ð4ß®îŽ%„Âz:Õ2)å'ðr­ Jr|£™5¯¿KÀÄB6|f:É~na{w0_ µü¼5Á§9÷Wÿ¹Éúª7ódÐüœk„?B‰ür{&,ƒ3Ø›3¶Í¹•Ö”•`]$|”úæU3%¼SF •º§e‚ÞÑí¦\Àò,9ˆ,O°ÌÜH · ;µm¡²¼…$ FØ<ô§Î+‰œLȉöd8L–°FB2“ªOî¦\%Éu –˹˜®êÌIóßìóRÜÃ6Œ©èf;®³,4U¿rWt+“½[Óe¶â1E„+d©1ØRRÛël$Ù†ä¦à(ož¢Æwlã É}üŠPå—ƒ`îÓ«öQnN+‹ÎHÙlöÎ#Á†Ü$Þ‘rn|õ.BqN*d¾Ü‡d·—jÞL«Þ‚~Bw^ÞjÛEÅqXRCÏ-´[% yAKo”‚2æß¼uÔ·0kñÅ@Df!ÎGƒÎRI×U›Ô,=¥-mÆa %JÔ„‹w0_ µü¼5Á§9÷Wûë“»»ÑÜqæ².•Ññ~ªüÙ4?'ǯò Å·Ø¹±ñåJ6HÔÖ • (Bjãý¼5Á§9÷WûÓM-É“2ð—\x)ËÜ€’>mHóôÔ–)!O!öS  î`´”·}ß*÷;ú5§”Ês¼JÖkÁbÓ]mA¾<²£ànw›üƒÐGGš¸º'Ë^Æu.ïq9»>¦Ê¾ÿ5.BŸã©‚غ•¸øB/n½Õ *{꘹®"Dr­É@eäôhý4µ¯~32bµQ*½‡Ëù;¼ýô¯ª£6MÉÆ›ŸCÐæ´K­ÛzN© î"­Ç£v1ë¯Ç¢ö1ë¯Ç¢ö1ë¯Ç¢ö1ë¯Ç¢ö1ë¯Ç¢ö1ë¯Ç¢ö1ë¥;Ç"¦Ï8ßâƒä¬§úWãÑ{õ×ãÑ{õ×ãÑ{õ×ãÑ{õ×ãÑ{õ×ãÑ{õÒ¡bX0œ^i–C{Tþi:Ûæ¤¦ÖiÞà¿Óœû«ýûî!<§—=‚pArh;©“wÖ¤©JBVé)A:?Ý»ù_UFþlš”9¢¹¢¹¢¹¢¹¢¹¢¹¢”¬£|É?xr¹¢¹¢¹¢¹¢¹¢¹¢´ñ‹5–'¼”í“d_V`¶×§-…aØsD'"ƒS¾}ÖÊ:ºiVÃb›œÍ^,ÙÓê¯@ìéõW‹ vtú«Å;:}UâÈ>ªñdΟUx²gOª°éJ ¬f×µâ©îokn½x²gOª¼Y³§Õ^,ÙÓê¯@ìéõW‹ vtú«Å;:}UâÈ>ªñ\ΟU(Ç)]³ìZ ÍóÛòÃ~yŸùËø´3i9Ââ®.3%²›_:´N£qßH3ŸÐ[R]zÞãû[n›—M˜i-¤ yÏÎzkJÒ´­+JÒ´­+JÁÝÛq¼ðšVß&M§ r­Ñ~ªÒ´­+JÒ´­+O˳˜<•ÄÄZgjÒЛžO(´*tÜNk¯ÃÛlⶤyËVŸæýÿ§^q 4‘u)fÀS¨Ââ™N'-œví²o¾áVå¦ôU:[²Ú2Ó~ @ly_nï5ÚB@Ñ)ã0gxÇÏ¥mòdÚr*Ýêþï Ã!C–ûÑÝyFL²ÊR[UÏ„ý•â®ú]ßw¯pÒîû½x«ƒþ—wÝëÅ\ô»¾ïR1 üxèÚ<˜øªŠÂF¤2‘¸yéÁ‘ÈòVWãº,¶•Ô}cqï— †™Ò›HSÊqÝ“M_@Uc¼õw}—ñWý.ï»×Џ?éw}Þ¼UÁÿK»îõâ®ú]ßw¯pÒîû½x«ƒþ—wÝëÅ\ô»¾ï^*àÿ¥Ý÷zñWý.ï»×Џ?éw}Þ¼UÁÿK»îõâ®ú]ßw¨ìc{1êÈÓñ¤íšÍù¤”¤‚z7[ö_â$EÁà·5qÎWÞ}ý‹)V¹seQ&ÝCux«ƒþ—wÝëÅ\ô»¾ï^*àÿ¥Ý÷zñWý.ï»×Џ?éw}Þ¼UÁÿK»îõâžüÝßw¦pì;àôxm_">tÚæçXþzñWý.ï»×Џ?éw}Þ¼UÁÿK»îõâ®ú]ßw¯pÒîû½x«ƒþ—wÝé–'À…H%¶¦3 Èi·>HP)AÞ}WÞ+m-Ç'¼[VÔø=Ćù£x½íˆÚ­:µ(!¦š[Š:$UÑ„`‰OS˜²Â¿c~ÚñWý.ï»×Џ?éw}Þ¼UÁÿK»îõâ®ú]ßw¯pÒîû½CŒôÉu¦R…½ð£‰Ú-šÜ_ux«ƒþ—wÝëÅ\ô»¾ï^*àÿ¥Ý÷zñWý.ï»Öü'·›sÝéæžaÈsVWØ^©ê ô¤ôëqýÀÔ!Õ…Jþln÷„*: 5óÿ-UÃøO€FÆXFT/áò¾ŸøNoÞŸá;ÇH(”É”­9È l¡qçï pzÿÚ öÑþ5+§‡÷¦¨wæ–áç+™s×øS£ãïÕˆBûÓT;ó\o£áÜò¯ülô…Å?ó$ÐøóQ>ª“üØÝÿ ™ŽÛl3ðŠU³Br‹˜Ì•´žð× ¾¶ÿ¦ÞKÃðÜ:,÷"4—ms¶N¯6ÿŒ§7ÚR/LÁzNÊbòš›W!Jæ¥Jæ¥G ¾¤”ÏNFS«Z›PIByÊI"Ë­7©2¸ú:Ò‡Vâ‹fæãx= ÐЛƜÙK!YÍ®qªvysÞÛôÓ}BˆÌöž‘-­¬t·ugFþVîÚ÷Šú|?½5C¿U'éó>ôï{ˆb=·Ž·²^Ù²¤›^›gƒ iØ«‡šŸ¶HJ-›=Мºë¼S³0°ÃjJ\Û2¶Ô’®o%Bûú7o¡2ÿ7¶fÔ”’bœÄeÌü·½I“ð“Hb8Jœ[‰RU%b㔂tPÝMId¨´âs ©&ß1ßÞ«éðþôÕ'¿5Á§9÷Wû˜\5Fqñ)Ë<âM„tfJúÖ‘úzªf‡aÑg¹¤¸ûk²uwßàÑ”æûJwÔ±ð(/·1e1Öî$[VäœÉÙhzM;‰GÃ"MiˆêzRW4´Q”^ÉäÝ=Tˆ˜Ù…†bJAtGiõ<6Bü²¬‰°Üuþ´R÷—€im’W|œà7*ÆÇCGo0\8¦ÊPÚ–n›fÜ‚âçAEµâ f Í·™ÑW£®¸ßsg¶Øìø³›lö¾]–\÷¶ý4ߥCˆÌöž‘-­¬t·ugFþVîÚ÷%ýSù²hwŸCÀøÆ ·È‡„“ÆEòíRÖNmîy×)ßnŠVÜÄ®RV¤s­;Ô€»e+)â±9ð¤ñõD‡ÆCImiÚ§A”åÞ3rJ…òôÒÕˆ­ˆÎ’Èm’·”TZC„eÈ»7V‚æÚå"sKŽÌ1-Çì9¬¿þ…~Š›Ùí¥èˆ ’œ¤ìï—-÷j¬ÂÃUtSr¸ð([Å€Ò˹ÓÎInÙÁ›Â£b 4ús"Æûº;ÃQ~ª“üØÔäEÅÄ^Ùlöî´ÍÛd,Ù$›þëÓÌ»„b|g=3M%*/ìúwÛ_ çw‰ÛM™Ïšùre×6nMºè#à¬gá"á@ÃöIÛnEWÍ“-ˆß›¦Úî¬íÆÅ%$DãO£bÞe%Ewµˆ)UÓ®ã¸Ö8˜ÙA†âaeÜ%dß7ù†ë}½\&LüJ*kd Ýÿc®‡þÒào×^RðÒ zék"À–R›åN ÝÏí Ô²î,ê—ùMß̱ým\%#O…¿é£÷“æÁüjÃðeOä9 “c–È9…ùZƒ¾œŽË¸l¸’eéä•Cm ò-bNÌÙ·_¦ÛßL§b8û¸\ˆN¼æ"ó¹Ë™| B“•±tóשµªL´.0e|C($ßÀ>§ÑÔEªN&ÊÛuFrmˆ=èS ·½ÆÅÂoMâÆ¶Îª5ŒÙ!µ-\½£«W>ä ©7=㮺´¶Ò&Ä+Z€i­ôâ b0f­gK¥e?¢ F‰/DY-ÛÂqJl%²½ä:/§š¥Jb;éxaîI„솲3,!7%;îñ¹V6¢¨ÍLœU$ÇŽl~° VÌ’ÆêÜ7kM8ÄLRaSKqM²Ç)°Ú²9˜,Rz?EèCb4÷Ñ™ \–Ú»m©i HVüÃqímúÔìRBš³ ©hCŽ„mT!õ+fã®aRÈ”9?¬7~›R~Ÿ3ïN÷¸¤T„½"# ¯@T’ê{8,/ Å^ˆZD†£¥;íÒ@ÒŸ~Jb!§ŒæUb/JZvEWå8.u¿GÍÒgeqˆd©í³±æ:v©[e"ì‘+Þpwåóׇâ8'‘+0CèqgÂsæö÷ËRˆJDè„“ÑøSTøE#çžß®›ežào<â‚P„Nl©Dè½8ôVÐìå©,Äm|Õ:³•7ó\Üù¬'G€©ì¦È}¥:Ô%$+>`’>Zrï TÜjÞ IL4:²ãM©æ¥%"à¦Îr ƒš¿í7áÈO:3 m6ùŠ•ûéÆ$pƒaöÕ•ÆÜœÚT’:½|¥qØe¤¨N„ê^@&÷¯ö+‚Ó0ÉMËðƒƒ2zqNæ3#“%µ>€ÌN+-Ķqœ åg}ú(µ‰aüÆËŒ$É%&ò¼‡fJ“~PæõÁ²dþ¾ÙÇ/™ßQžæõŠáÌ)´=*# ¹ÍI#}bòâIb;²0”ÅŽá-¸ §1Ó–+/qf\“‚•‰?-M>ÊÔ¶Õ™Á͹ÂÖ¶›ë ~9fLÖ㺉@b/à qÕ‡°¶÷‘šü“׿©°o{<6mgJ³Åï™>crFµKŽíe÷ PÎ7!9Z-Æ@ÚtßxÐڶΪ1l¨4¥«—´ujçÜ‘á5&ç¹ ÏŸ^/y(Íáduп 0ÿ˜7ë¯)x?é½tôYûl1IuHC§Â4±{™=7ÛMHaÄ<è mÄ…¡œ $œ=œ1Yœ@8xËlÞù6y2•|œÙ¼ö½EŠïÁÿEÅžÛáÅ–V]PFL¶M‹¼ìÚ 7¥è›Vx0ö¥mV–@:s|ó麜ÇPb8_’öD:É‘lFO)hB²+4}Û·ƒ­éb3xkoâx…%Ô¶¸¹»¦á!'þ1Ümk}•‹¶khþ,ÄæymÜ6†R´‹ ’Ú·§MÆ£â±ZÃ;höÚ3ø„‡C‰h_n°¥‚>H6è¹…aŽe6 °¾Ï6§-ú;ÃQ~ª“üØÕŠ~“Ž% æs2þú†ö9„6eH|¸ìLéo:G!C8½Î„ZÖ¯‚Î#qý®×‹ïã;]¶×]3üÛLb? ÆV.Ò–¥A<\6°Pžú *ùµ½OGS‹—‡ñg¦¾Y[«SŸitî©ëÚíxËÉs›l¶i ÿø_í®}9¿º±Ýq¥sVœ¦‚¡áÈzHŒÉðŽ_¯¨}€W ¾¶ÿ¦ÜÓPL„ÇŽy{KY;ôë<šÏCÊ2Ã1Â]·ýÖ~WûéóVÑmJÊA7±Ò†lïÌðJÍúR“Ît©¨C-sj9Cüß›}ijn;o<—ÃYXƒ~ªa†ðìÓ´Ì‚ý’œ„tÛÏÕM+d’Â^Xqà…Ù] (Ô¯ÁËl4¬¡Â®ÙM­pW3­ms=î?ÍÔwùûŠú|?½5ÜÃd—rqG¬¹yùQýha³1fÞ†Æä(Ar©)RBs8srÈH¶ì½?gŒä©|b)PvÌ2T’œ¹…Òo˜ ÙOè¤2‡™üUÆHf*ZE֬Ą§Aæý¦¡K3aåŽZ!Î#i@ @t+˜«oqû'aϩij*:Ùp ò€P±·é«áØk!ñÿ~ï-ÏÒtû)?O™÷§{Š‘±Sç:R ‰*POõ¨Ñø›m/dí÷lóŒ¦Ý_ºÞzµ„¨ÍIkhÂö™¯¦½ZÓ! mä<æÍ”^×6'yè¶MáŠT–ÑžCeëe#’~Uìm§ÙJ[0Ôä$¸ÚöÓþ M¬?Ô)oMΠ§Ù´‚›Y JÐo¿ûê•ø¼YæP•9˜×õR^”ÄD)R@Rå"ÉQžŸWE?-TôÄnB®°’€sóÕ¸Ì 2^æ ¯ °Ô“Mºã+adr›V©î+éðþôÕ'æî@ø@5&u)jˆãY’êȲIù®­ÞqÕK8$œ1±$½/Í!HH( †–&±ü=ù­X¶Ð¾óQ²6‚¦Ã|”fêHéÞo@uw>Äã.lÒ[KksÁ‹_}‡ÏÓº¸(ÄVŒÂg9•¶‘ø+ý¸iRQ2\†©ŠÝ½òÚ÷ý ùüÔêU‡‹.!¹y¥A'p¶ð3oÒœyÃd!9”h)Ì1H[¹x²v¼ûô+óOI×í­‚0ûÍK¥µ·¶äƒ”*ùº¬GE¸ÑDD¸Fd“˜ªÝ^jaMIŒóêe§JÆòœ×ÝÕÉ4äxÐW/bØqâk{õÇ»/ê˜ßÍ“C¸ôÁ†¢d·.m%øK\ßpÓö^¬7î¥x*!8þ$ÞÎë Bv‰°¿'5ÏùsW1xØÜ˜O@“%,¥¶R”¯“|È=zÂ0çxCl‹!׆¶ûËRTÈH)C&ɲտ-Fwq»¬]ÆvKZÈB”’¢œ¤?|j'ÕR›¿áGÓ›û«á®}mÿM¹5kS€ÉŽ]ºÍ§Ÿ”i§£Ê}«É¸A‹4QtîÿáÖƒ1Š\MÊ–·O)J&äî ì‰/åΕ­„Q™:j.4,™sÈa…™ÞíÛôÞ”êåI}Å8•¨¯.©èîÔ™aNgN}×ÝËËá¤!™sFÍ-¸¡át¾íße«’ë©Râ…ù×5$ÊyFÍ–ÜPÊÚz…†þoÜWÓáýéªú¾jOÓæ}éÞæÅ°¢Éÿ*‚‡î®2—^mݸ{uµÉ’Úijˆ§$-ù,0BFFôÍk7M <àam«;N°yHWÚ-¥ÇÛ\‰ÓÛqH(}iP»Â÷ß»v§KkRAq(uÖܰ:dË`<ÜJKYÜpr/(»ŠÎw»~•2D©2BÝ-Ø•¤ªÈù…¬o§®’¶æLiI[„[.àá̤é¥þß=„Ù¡‚ÂYu»¦Î!7°<Ÿ=·PZeÉeÔ¯3kM¼ë‹>zm„©Å„ fZ®£ó÷ôøzj“ówæ¸/ô÷>êÿu˜™ßÙµQ¸¾U?Nꔩ$)•¼…äº|.T#z·u§¢¶o&2šVåúQJ¦ÏZ†]ŠÔ±vrémß¾÷ éuçŸÎV·EÖH÷J廽°Þ½Y¿­0³vá³!ÇB6¡Iåh2Þç5ÍÎÍI-ß!BïùÛ¼çKkÝ—õLoæÉ¡ß#^ròb®6OR¥%GwúEJàä~4Œ5ò»¥Ô€³{ÕóÔ|H©Í»1Üa"üœ«R~ß?o~j'ÕR›¿áGÓ›û«á® ó¾ ·›‹²?¡øÐ”©ñ2Ž»HlþàhwꥵÞ!(,u^Cб@ý¿zøüO¼µC¿5Á•É×sóÇxÚGÆÍ#xdd1ÎùýÄPøóMb'JÃå¶ÒÚÌÒUÒ²’AΓù‚·p‹ì±½•yE?²ÆöUåþËÙW”Sû,oe^QOì±½•Xð·Ñc{*•&L©eIwhó®„‚HBQ¢@$w†ŒÜ:T˜òå.3nPê!@ƒöŠòŠeì«Ê)ý–7²¯(§öXÞʼ¢ŸÙc{*òŠeì«Ê)ý–7²¯(§öXÞʼ¢ŸÙc{*òŠeì«Ê)ý–7²¯(§öXÞʼ¢ŸÙc{*#+q“v¶Á!(=a)_Ïñž™…Í—‡HpYÂÉ+êºT IóÚõåþËÙW”Sû,oe^QOì±½•yE?²ÆöUåþËÙW”Sû,oe^QOì±½•yE?²ÆöUåþËÙW”Sû,oe^QOì±½•yE?²ÆöUåþËÙW”Sû,oeQžÅ§ËĔ³2JPt¾T$ ùè|B›X$kplAè ôÊ×q ,0¯Ú[½yE?²ÆöUåþËÙW”Sû,oe^QOì±½•yE?²ÆöUåþËÙW”Sû,oe^QOì±½•yE?²ÆöUåþËÙU•Â,BßFì©Ç\qçW×\7SŠë&‡÷ õ ­h+AZ ÐVî÷JÐV‚´ ­h+AZ ÐV‚´ ­>'x­h+AZ ÐV‚´ ­h+AZ ÐV‚´ø­h+AZ ÐV‚´ ­h+AZ Ýÿ‡ÿÿÄ+!1Aa Q¡0q‘Pñ@`p±ÁáðÿÚ?!þ/¿Nó¯úzDÔrB““MMù›îžæQ)ÆIÔç’5fÏÞpíÀ²<ìל&¼;P: ­„\P<†¡ezSÎ.<_À*Ä~Ì1¡b ÿÓùM øíÔ4sÌ7·~AÈ(ÓB‡³¯~L%É ­q‚HúXê­Æˆ‹lÁ•…ÚîÀåÙ8±jN<ÿ~üàRI‰•rtHkÎS¬ ¸][†µj?õÿQ‰‚œ?Z]?„{?+ô½Ôìæ8ò{a¿Ãÿ0…Š×HáÈäåÝ3Jh£\R·þž²°—³ò8w@v Ï ë¥J•*T¯à’Âã§]t߬©R¥J•Ý){T¢O+`kØTQ=ý(9sÆÏ øìÌ›¦šÝǪgÇþ:CƦ9t ® M ‘.'ë @@Ðïùõà@0êøçnœ")GNB‰ ˜ÂuêØ{×*ÛÃ{bwEv¿û›†‡Æ@!ìg cc4(tKžõêhúÌ?§øä¤Â¶YðgøþŸàgøþ0(¾0ÿ?ÀÏð3ü ÿ?ÌÉú² =†R!2Ì Ï´Û¾p1.Pnªyq¹Ì«ø¶ðx5ö4hÑ£F€Tþì!gØhÑ£F×<æÙå:Í•ýä3ù~gÿ ¿l P¥äy#˜«¡Œ^ê©‚B £]uØSkÊ®S¦x9à烞x9à烞“*ü·²çƒžx9à烞x9àç /` ,Žÿ°f«Å”šƒ™Ogؘ4þêè3f±Ï"€‹²›_N)'´òÚcŒ/üûŸ»UùoeÿŒ°¹‚R~^‰èåéúõëÕZݧc'ŒE%ÀQ¦ÄCb˜lô\•†ª[êZFÌ¿tû÷ïß¿~ýû÷ï[R`;¦óͬ’Ô`*zÎîô¢H"¡jQ[¯__¿~ýú° y:¿«Û‘v¹õýû÷ïÞ«¬hèñn,ƒA{]º÷KØ ëìlC:žÝ÷PT͈~OÐ/Wß¿~ý þn£–u}?~ýúSÚ÷ú£ÿxnélQ(&ÇðÀϾâb¤úSÊÓ0'0àÙUpÿCô·`vi"iôPØÍ5àÕòþý}z™L}´? b1}jãÛ9ЧdàÀz;ûs=ë6bõëQ1^îþäSæ?g¿B¬D‡™_Ôýe'üd±OlKã °í î××áçÅú#ðJ„nçˆ^Qºµ F‘ž—4Þ÷?)€¦Èk›“^‹Ð¸!Ó|I^™~¦ðhdºšXCyP°å=(þ­þJ>³ÿ‚Ùâ2ÉcŸŠ÷ò5Qïñ¯g»N—4g¸Z ‹u¢ „pOƧƒ[éÃuöq@„õ#øßb/££Á%¡ö0õÊ퇑 Ü!É8^Pˆ˜¤\0†Ì6"¤8G÷O}K’Á] z! 錦n.@ |R1q²#A:ŸÏnÓ9ʪûB'Èh®Nû‘§Sáßû22]NІò¡aÊ}Z|_BK khA$ŠL,²! à‰ª’i§.*+jô@ñΜ¯>ê¯RÜp:ÄJö ³Ûßé…PaA«ç 3^)ü•O@ò0ÞÖ5&ÑXrŽýIæàazhc)¸G)7€ôD¤jZ“Üq@ª×u¿„ÞÅí±þÈ2øèÅìŒ`tbŽ$xóaØnÔœuTÃ'‹…,ÖŸíúv¯÷œ÷ ÖAS°<œ¡` ‰è†¤D,ƒO@%H_I€b^,EmÀ0ˆ±™P6ˆA„Jê1¿šl[slç)©Ñ´rQè›7ÅÊx_¢6T=‡ŒŠ^«Â‰™¼‹blèåã#G ±³ì›%Jo•M' qª)2©]‰°òcá^õ:ͪ>Lí#K¾8Œ‰q= >ñ™)ÈÕ@²¾Î{@@#lvß¼ÀÆ%8°ï’8°q¶–"«ú73Oɽtq" ß3êAö¼Uq¼‚å3ý «¨eUn#; Há>DĵzAmŸa!ñ"„ç6r¨'$+[wúbìC¢‹éÀBL©b»þÏ<.£j­šO§/Gãpô:ü³Yé_’‘`(²H¯ví©üíE·qÊô9¸¹œþo½å£¤‘V² §Hì å—­lF?màT#k'SdÚ³¶D19¯QÀTP¶)Dl ¨}+Š8±YE”ýáR“‡èÚñµáãR#¸/.rZà ªDFâs5F󈢞¨Þ˜}òÞ,ð¯ ÛºŒϾû½É¸p°ÒÝ’‚ `!‡Ùãh³·x-Ú -Ö!Y Þ²uÞiÜxXŒ7úo˜]ìꞤóåòïžWÄÈž†ô¨Ÿ›Ñ ì²Ð¸”iÝá¬6Ô#i²ý–ø ^F‹ŒÛq“—=·Ú×-¾Wð},Êȱ‰÷zr|h> ÔÇáÝr1áý‰ÚÐ^Ãk¶Ö6 ß‘¶–ÇXí¸£Ô{|h\ÓEh'´mWåá„yU‡aÇs}Hl¼b%QŠìÙ î«ÈcUfÁôÇ^þCÿ¸'øƒBº iå1µ€|„â³\û¨YuhX Óó• ä”ì֟ϤNe±ið¦¹-Ë‚¦|k™Ȣ–^,$ÔÁ mzÙ¶'ÒÇÌ`š4«Á§CXÔzt}Æ ¶HGâ‹pºþ‘O —Œ.{+‡x¨Þ’h4p,bcÆ)`1tmím^1„|¥q\3ó¨Þ½êO^÷Üíg#KHï¦;Ï?ZŸè±á6õÝÖÿ ´O}ßú?¯×ñ2V•‡_.÷÷eyô¸ý¯ï>?߀›1˜;ÈãñGŸHpáÃ"x`G/Á×Ãõ‚ñqûÿÐc”•œ´ì%öÇ0àᇹJš#¡êib—mŽz惬:µ9'#ƒƒ‰«01÷qÁÇppÁÃŽ¹…Ï„a4¢•(PY˓Ϸ¬Öb­ˆs@Ø ‰±3…øNýµý¿mÃ88páÃê>@oöb¬Ù—«–ì%>úRgU¾›»º³ tBs•*ý»»»»»¸˜Ð0 zÂ.ÁÅ~õÝÝÝÝÝÜÌPr>Ä"UöæîîîîëLÐÿÇÿÿÚ û4PuÅl¬ˆ±Œ„ך¬  ‚$€H³@€xdËAl@/È o¢hp@§mX=‘°Q xñ@šiÑ‘‘¦™q`±@6žÎ -¶Ùm¶I>Ò¢] $’A$’ ‚I$’I$’$@@I @‚ŠH@orH@wæs‚ ײ@„ݶH–>‰$€@“!DIÔ< ޼H5 è´,ðƒ=½H$‚ô^† JJŒOñFˆ"kˆÌ\H#€@#+s ’ Cë2Ô@HÿK¨H@>,±t’ ¶×Œã»ëì@7oÙk’ ’ ‚ HH‚@’$@ ÿÄ+!1AQaq‘¡ 0P±@`€ÁÑáðÿÚ?û@[˜`ø_i‰œÔl)1ð×ìúÕwS^±Ê( Rì²h0fûsXõRÝÛ #aÅàãoâÙTtT ÁP0Di)ý lFjúf{#ƒuÍØ& ±,âë721”ÑÏHIaC¢îÊÅYYV±›R°‚V`Úå¬nZ·$ïgàÿeͦÕw®i°•¦ÈZd,­8=i*¬ºýŸÆâôN‹‚j*ð\VÐ ÎsþXØTT^vá– Ä>°ÁlìâUùÿ™xàæRÓØu˜â%u w ÞžPÅš€ «˜Šèƒf+ªûÍÀâýá¢<ÌgŽ!N±ˆ`úÖ,”N†ƒf=?È#ªªí’¨ès£Ì¦êOW½’Àåæ ¶Ž&¹ˆ®„4WÝj é¿:ÿa¾Äh*vÄ Ï ›–]A‚é«TÿP¦¹–{¸ï¶Ó,>ƒ+Ÿ¡ÇÂþ7ðfþçF½ÓÚ¿È*ßqÜí=D=`›ò«®×172ÕÌCÁ¨©Q9î"(×Ð`Öþü)>´§D^”;J†~KGì²d œãài±¾¯#Ã’^t*£cLôbºþ¹3áqvƒ8Vo—+t*ã…¥œt& ¾×{ÛýJ‡]rÚ¯<Þlãæ(/X¬ù›ëé,-}ÞåË—._ÂåË—/êŒ6«ùXÓÀ€Ú⊩n°y@k(h^˜= NYþA»œËŽþ¿Bä—Šš›Rñú²_2³.fnq/sSŸ±¸ž±`Äɲe‘½¡aÁýŠ¡`u˜/´µ•@Çè|ø Õ9ŠË†Øˆquã´é¸¬uSÅ8pFæ4Bv˜{æÃÐŽ>Qá¢%|÷š•–%2vˆœ–]’ûËýÅe«¯iZEeï8²gŸ‰2†•›•_FâRtA¤é‡Èƒ7,HÇ]DÖ›SÙ‹WoµE¥ÙåÖ%»•<~P-©-ʪ}eŸ-•F mé &4´†6÷”S½ÿRíù™Žc/|Ö®Wçú7-à@xV%3ëÞRíåÚ5û¡Û«žùŠ‹6Ð弨Œ]`3›qå¼ÌÈ€Un%:Úèv)¥âÏ8fš4-i­“8 e M7kðÌÁNksâñUœ±Êih¢F¹SN3øÌåtמ£Ë:…%=—`âñá_!¡K¥]Ây§¼ Ú%(ª—T.¼¦’(«°sŒþoŒÜ ÀaWªëÑïÚbOÚ«£Ãm\ꘛh[/K/~ëY"ÎHíÍ5‰˜D.¢lM[Á}µ-µ“b°ë㩆¶/Fƒj¸ÖXµ“ÒQ^Çœ?­«ÊÐý”1N~QØ1p52Æ$ ùhHP]Õg9)xÞf]ÍXÈ·‹¬o›îÛÔ¹´[uÓ¼/f&¼A,¶Ôª¼"u®ðU©¼NJëÓ¼Qâ•5]’üqrª$˜]¦î´hçz¨€ý‡Bž8ÿ ü[óg,ñšªó0 h f(Ø!•Ó»žÖ5EܪèLç]Vs` ÒÛµæ=åªüÅB”–דºÍ=syŽÈQÛþÄÁ mKË×oµ}^rÒz•øaÐW’÷u…äË¦é«·ÖªÜÆH¡Hé<‘÷ƒä„#4€1œèÝÅ…iªÿ,<”9QxøÜ:OAiÕ[+wÛˆ2vå|(pœl×h‘”!ÎiŸ<éÌš&óM‹MØÃb—§Õ|+Bñ5ù„4Ck-jÍU8±¬À4A“[€*S;nó~•€´Qxy^Vå˜67ç_Ò7!ÓmÕQÁn/P*k-‡'uŸÍæ9[û çá|° ÇÊMÌnKª0ñ™w0ÁÎe@̧DºÌg76}[æ.eɨ\:Ì Æ·s3_ÇÿÄ,!1AQa¡Ñ 0Pq@`±Áðñ€‘áÿÚ?úEÇdæ$Ä£Á²Wå 9P—4.ÕÍÙÔ5/²#˜K#ù?5YQò+þÍ¢®Î\{u™ØýK:ß¼uÔû8ea®Wô~°äêÖ¿ú½Å€Å£½ãþùLdçòybE, æ1eòJØÀ¯òÅ/:=f6ȱ-À7óò*kPFò§ïŸf#šn ɇ3 8Šî†c­¢)Õ¨ [„~h”ýeñƒk÷Œ`„.ƒÌ ¤Õ>ûÏ©óõX­Õ'–/¯¼:pÊûÜ;7/\P#Wë)¼¸=/Þ#¶°©¹ü Ön$o,Vú¢ÑۿʯÚCQ‚^érÇ0 aCÊ1§À¡“rût;úaí*6׬ts>=‘Ã^)ñ×@¯ª:–GV½ûÇiÄD¹Gâh4Ìɦ¼ê6lѯ!S|÷1ÇïO]À„ëËŸŒAÇ€ø(ø^j ÎbYÍ|[ü—{T¸åò¨™ Gj?Ü&m–‹m_gŸßnzãÞZ7t~²“Ù6vÏüˆé`÷}æÿ'…• 4™ ÌûÇ¡*n2k´ÜÈ:ðÃÁÏàn¾eé.oçé€î…b^'0ðãèiÐ"ªBƒ“욺˜®Fÿ—3PÅzFϺTÂÏ|ôNÄ^ˆGd@#fãeÛûBÕÞ;7Æç˜M¼þhXqLƒ,%sG–W˜—ñ†!€œ£OY‹1ú¢°»¯K÷„ee'š$!L—úÄ®wQÃ~.F]IZc["M:‚6º9•‘¾>H¬Ô¹Ƭj.ÇÖ'ŒÙÑŠÜu¸øUÃfDz&?y¡29€€ÜDµ¯H ¶M~ð§Ò¼¿¹™¨âÆÙQs_„ ö n\_õÚ üa*ÿ*˜^>3ënjw{hÏk”è®ÑF•UÏ^‡=âoOß´r-Vªü÷-¶-óª™Ñ€=zuŽj†»Ëö—¿ëÄ*K…¬Bª/‡¤ޏò¨ü SkšsÚȈ:Fb+ 6²:µïpý‹7Ôéç6¢¸}š#À¤¯X’æ/=†ïA»ÕÂ"î(¢¯áÁ¹cë.úX¾KqbRË3Ò*·‹7-8’Šé ïψ²¼^«¼Y`õ¨C¢K¾¢ŽÅo2ìúyØÆ—ýª7 —?™‹^µÇë_1‘é~°rõߔŵn~i‘ îþЪ¶¾:ÆÆÐÔís¾ÿÎÑy¯OêX_®b»6÷éå(‹1~±çIIÔ yB>m$„…E*‰ÓÈ ÔZiéË%çïÏâîøK¼gµElçB+¬ïãiPâ-ºˆÇ6K—4Êçíæ'Ilz’úÁ.sSwóW!á¹u5<¥ô–ø ÿŽ?ÿÄ+!1AQ añ0q¡P±`‘@ÁÑðpÿÚ?þ#|ìäŽ_ ­f]{ô^?ìÿ/R¨£E i ¦áLãÌYK‘ âŒ„ ®ùT8yRÂM<˜ÆÇ…6lË$‚í»¨è0® PRØt„S²NF Ê ¨A'’Ä›ä‰î?áþØÈa(†Ø‘"“‹…åB Œ´h3LÓš¢é¦š8ÃñøYG.ÂD)i²$0ˆ¾æ2:RS¹0”´êá ѵ3GÌ´¬D%SRŸ%îÁ¾ùØœÿ‡ðeX„Ø1Kø?Ö ª‚ùÎúçþ«Aø$Õ\¯B"(á!A9ŽñÍ“%D;)§=Œßgoñ3=¿Å´gåFÀGŒày›[ÒÀUÿ-w¨¤WåeŽA~ ù½aƒ 3ĺÚ<¼­¶ß¬`Áƒ •ı!¬ÝÚJ”á¿™ŠÒ#DôðAˆÿéÿˆˆˆˆˆˆˆ‹oý˜.ƒüzÈœNà•¥ñÈû|}u!(H²)Ø¥¥ì¾÷:ûpd³áA š®A-Å]§ü2$H“2L‰2dH‘"L˜r˜¤“áBA‚CžèŸÇ)÷؉Zú‘¯kYqƒ^J0 ‹çoÞlŒK›.¾… %Î.Sqiþö6V½KÃÕO³‰³ ºì2 _bë ˜_-b4iJ$ÃÝçº'ÀUÑR%7 ]Æ%œ8`˜‚O>°Æ EDEÄ:Uja§Çì)$¯1ŠÛ)ª¥î‘cëA“üŽ´ `Ú®e-\¿ŒN¦b› X+JL"©Ž]{zµú:Òˆ€"—«Â…?ýqÒóÇ~Ó×±£ÓÔG;z„nɰB ¥½€'LjY í ÑÂ8\àxvºÀP·kaÛ׿‡¯ê¾‰n!!`vi;ÅYHÃIƒ¡fãÇ£D…Cx¤ÉsNmÔ€»v˜>¢¤`î’LÀ.20©/™:“X]І¿–-/¬eaM`%iålq‡ ¨¬˜äB'¹‘EÁ ³hYRHß1]×m˜a'ql¿;uÀ­¼¸¦]וóèƒúã¿iÓgl ǹ¦<U$ªg&y°AjLÞùM HŽR‰åÒ³–1ÒH˜bo„Mf|٠ߥ÷FplÈü’â²ü–ŒÀd”Kt÷Ï ÀºdèLˆòÙÈH”fè6’ËÐHyˆÌ ?W*ÁV=€‰–µØÒ¤Ÿl8óÓ_ÕzK‚8q;ý ÆÍúéûà"òÿ¼„“^3¿Û¥ÔûN›;ecí(s²`0±„r[D1o¶FÖpfZŽ…fY$´u ÆÔ3¾ž*(õ%‚ôØó°dÐk"èœ1âÁ°‰…"19»R¡ƒ#Ò!!¥#žpÜÄu”‘±îÚ¹TÐ†ÝøgNayt®‚\¤ª¨–$ULE`pãÇMü=Ué/¾R‹Ÿc‘2˜>2 ï”Iߌ™ÆˆìϾñ² n±¦ÌÏH¡%àG“"Êb(9zðüçN?¨þ:šîøYÖý£þ×Ûç멹U}€\3jLHÑη_Õ}RÚ4+¶±«Á(°à‚[—kÉÇ?µ¥fð²®Bé“oæùÅgÓU±!FY/*Î?S¸A`nëj"Ç/ £áa!è@ªL,fcžkh!Ê®÷3ßÓõÇ ¥Û+ù™´^¹V­‹_^Í›6lÙ¶¾• uç´íg«6lÙ³ ØÍÂc`@ˆŸ ÈPLoÍØÃ€êoÙÇÐzoï>ÂAD< †¨¼®U„BwŠÉ•ò9`Cƒ@HfˆÁöŠ멪 PÍ‚!ª7NÆÇÒ£-áüp:áÇ—ß&9äyõ„!Bߊo ?©ê„!ëPLÇ£ßs ÿJ¡h‘¸*™DZÀÍ|C^Ñ}¨ÇÐ4hÑ£F™(Îò®àÙgÐ4hÑ£F=[lQƒz”Ÿ}c¼TåÍ7!ü¹ÿë÷ŸLa«YPm%üðñÂPdPaYtÕÎÁÜZ…UUÅÿWÑÌÌÌÌs?ñ»™ÉÅØíô3330þŒàóñå‰^eu@P´ý”HàÊÊ&€”OZÜÉPò°+12È[¶ˆƒÀ"ÉÝ#˜0"@½°]þ¢#ú‚s98»¿ã®ØÕ¦õ$ І*ñz  ó«ã`éaƒKËœ!š,¸€Fž‡LŽ¢íì.r ƒ_2¯PÁ†¢ 0`Áƒ†ï¥–QpŠ"3Èaõ¾p†2Ô7§IFãÐáƒ:–$D#ÈœŒ‡Šï§%µ<ÎÖÁƒ OB$P r1ÌœÚ7cNÁœ¥†;E¹‰óCÚ°ùûS 0åïü‘B²jhzX0`À)·U'° âƒÜç åªövöü% ¥œ?]Lì8¢_Ïþ‘$üZ3“AÄà©3l¸AhDU…ÐíAí®¬ª"Æ Z;%WYåyz¼d¸•É»qðÂwÍvôH†žš˜ö¨{)ß.Bž¶"Ê¡c÷°x}à¢m‘ç4çw—W>– ú×Å©õûXch mCØ„Ý=ñ—ñ²LñÛ'úÅÜÁÓ?¼îk;aübèJ—¼ ೺4úÿ¢ÿ^µ/E푪hªJ¨•W=î:©ÉÏoDLlŠú'l äÁ`:« Òh§˜MNŽ ¢‚ –Št<Ì»i…½i]š›°ÖKÛø$¥ O^Fj¼¾´w#y²z3~_P·ôˆ¡lxÊd#î Xà$C9jf "IiþÀÄÍFo¬Ôà xÈy™ ·kKƒ–1á'pÅôÅkö>·>ÎPoJ¡¤µ Ö–Âd߃“¶Pr‘~‰­3…Q`¨q=Ð7Øœ¸3•(IqlÓ[ U€&\¬UÝyôO¯A×|éíƒÀ‡‚€b@*§-mS“&Œ9ìHi…$¥ N9³_´ôr5fÁç1"qÀ¥ºá !ÇT²k HôSЉƒ®3€ g4g]W[‘6økn·OÔƒÓÔ¨6¤ãa0DšÆX-›*ѨCBiÛÑú/õšc+$•Y&Eà7R:ñ #à[Ot:UOT•Jñ;ë. ÕM8[,ïFŒ‡/ŸDâ€9hrÂ?ãap.ŒW£ó):”ДÕ8 Ó•÷þÙD.på‹8gía0¢0Líž÷;Ã!dóÄà¸Ëž9IQ?¸ê4ØÓÓ˜fuèV& QÕŽ¼ k•Hˆ$@è· `'FL=EÇºš¡ šÃ}µÑga%Õ€ªÀÅY6rD±Q ˜’€UÜç§Dß-?ÈÚµå1í•ò[•·îø¢ äToȘÚÎÞŒþs’A°±èšEÚÃoôø­¾:åûäÅÔ¿lwÞ½À"¨’Áã#R®"T;ˆp…sˆúÄG&æÑ0‰aÔ²fuBœ9 ^è¯4Œüï¯äß:ÎØ#Ý © ¯2‰«OÂ0?Ð €T*1QàD*‚L¸ó±R È# ˆf–Êîs¨ƒgiW™EÉÛH«šœ´C³“ZÑB*õ‘OÂ0è…H|í…Ì>î‘R ân2C~‹í<ùyàó¦YYç0{ÙÈN¡¸‚Dàq ®c¡ –Ú˜z‚¼6k¶†rMíËŽ&Ù7Š [Ü5àÄ„!r²û¤Ò¸Hg/ #ýMP„éÞ‰N¥ûàœEäÃÜE ?ßNXºqÓì",;@À¤OGú¨@4ˆâ`ô¦¨Å”ˆ-¯‘%Ê8åŠð7r ÛÄ‚¨‚¸6ÊßâPlIÆxI¾åé0÷½ía®OÅEp'r-¡9âìUã‘61à h8€@=¢ÿY¶3Ï/ü²aØï`¬ŸÈ°nb%\NÈMVÛ¼ÑMNÞp‹eÅ%žr2 ,’Í®I MÔï|^ôßz¸p=¯ÕèU•¶³x~ ªƒ˰{tQÛ†poÍ€Ò`‹Ì9°*° (ë3cIÏ M (£®r„r`H¦à¸Á©N0ÅCHÕ”J„ À,Nƒm³ÆÄ6˜=×ÁÐ+H À†p„"€=À2à(•F²¡ •…c·L¬žqìRÔÞÖÔîV·€C®Pbeº/I£}zLªa.>äXEobS²¥¥s€ åp]ˆ‚\€¢€2‰¬¿&É}MÎkxë”X $€jÚäçƱÆl$`€ÀP% * ÄQ`‡ÖÉ(À ¤ D?jÁŒYˆ€ZŠåÇ7³ÒT¡EâÁñ!"AC(Ø h )²p…¤-‚Dêuš¥K êÛÉV^3mY…†u\\•ŸÁ “ÀQA›"¸ª`ŽŒHšs·L¿ èc\,Ãhz-ÌDÁs·ûê&¥À)ÌÆÜŽ—@ÁŽË Na/NhÆE½¨¢ÕâyÁƒ¦µ´gœ1­¨I€'†r LŠ, AÐä<Ÿ€\ç™o˜‰E1`‡Á;CV£ï ŒBÛiMQU†¼Ðûk4Ò650Ú©—XRûÖAº 5Õê:\ç‹B€»À’ LnÂÜ; £¦xö™¿&€‹yh b™MHª.¡\#ÓÅ2T#5ú¯Ñ¯¢§Wëõý¯U£‚Öe”ä?`šYÒ\Ü É1TÃ!ÔϨŒH26¦Ee¢QÅN RÁ¥ý¹`W¸XÖ!E(/‡­ÞÖÛÆØŠa`w\êtr"ŠX±³¬‚SP#~ÝklB€Œ"`qêË]úþîSöàÄ]¢P„ªA‡(õÆn`w%.‚¢sH„!p£D€ œ#ÔR¦ù«YPÝ<ÅxR"jÒ)‹¹m¡š™)Ú2áØñ ÞÉ­ „:\láo Óy·“ɸq?`€ˆáWðáïºUWÊñÇ\¿ õþ«Õq!Aø8dÈÀih&pëqŸL#8äÒ ›Û‰å|ÐȨÄÖ:˜ÑV AH–¨÷ï­Ä‰;À[ö-цØ Ê›²£Ñ®º»µ„puCúRˆqUÊÎ<TKid;ç Ll‘>†Ýâ "½oѬ¬[âÊý~¶Ýýax}Š|&{¼í‡>\ÜÎ9gk°!¸Ü7ƒ;o'3¡Tª¨¡g”û[ÈŠOYÙ|˜(}Ò¸wAìvÃNÛï3¾>ÅÞuöÏþq×¶>Æ#ˆy÷É¿9«¾ŠÞ;[pøwˆ×a{c_X«„]_› ÷7ž3¿¶&^&nw2¹'Û8<Ì»=ÃAZ;áü1Í>Ãë±=Ç(ž¹šn€,S¹tÕé… *¢ŽÓ¿¼’­çó ¢ª¸v:¨åXÊi‡(§ã‡P :_§fÍšlð£g"1ï•#B®2œÝL„¼HzÞ¸«¤ÖgBæ¢KÀÅBÿVÍ <,гB iQ†V6ÿ¢XXƒFç=zýê0øØ¸m#2€pJj‡”_t}þ˜Q³BÏ (P®Æ£Hy€O¸‰Ù1¯9qq4¥d>¸*ãvžLf­öχχχχÂǶ x0®Ž 0y½³áóáóáóáóáóáóáóáóáóáñaýXY•àÃÃ[rÄŸ& ¿«>>>>>>>>>>>>OêÃÊ ñKäÄÉØÍÿõgÃçÃçÃçÃçÃçÃçÃçÃçÃà3añ…@OØÿóïÿÙntpsec-1.1.0+dfsg1/docs/pic/boom4.gif0000644000175000017500000003743513252364117017053 0ustar rlaagerrlaagerGIF89aó´÷ÿÿÿÿ!!!)))999BBBJJJRRRZZZccckkk{{{„„„ŒŒŒ”””œœœ¥¥¥­­­µµµ½½½ÆÆÆÎÎÎÞÞÞïïï÷÷÷”ŒŒœ””Ö½½B99911cBB{JJ9!!çBB­))”!!÷119{”½Ö÷!1BJRcs„Œœ­µÎÞçÿJ9)B1!!)!RB1cJ19)9!)!­Œcœ{RÞµ{Ö­sÎ¥ksZ9{RÖŒ)ŒZcR9½œkµ”cÎŒ)µ{!Æ„!cB”cZJ1ŒsJRB)„kB¥s!R9„Z1!œkJ1Æ¥k½œc­{!½„!kJscB­”c¥ŒZZB9)sR1))!œœ”ZZRccZ””„sscssRœœ!kk­­!!))99BBJJRRZZccss{{„„ŒŒœœ­­µµ½½ÖÖÞÞçç÷÷ÿÿ9B1B!91191!!J)!”J!J1J)!{Jc9!ŒR1½s)¥cBRJ)91Z9!„R1Æ{B))­kkB!”Z)R1{J9JB1!1΄)µs9!!kJ)¥k!ŒZsJ„RZ9!œk1!1){Z1)Þçç½ÆÆ{„„9BB!))ZB{!)9JZk„Œœ­µÎÖÞïÿŒ„ŒZRZRJR)!Z9JR1B!ÿ­Îœc{½{”sJZ电µsŒkBRR9BÆ„œÿ¥ÆÖŒ¥ŒZk„RckRZ”csJ19΄œB)1{JZµ{Œ÷¥½­s„Z9BkJRcBJ{RZÀÀÀ!ùÿ,ó´@ÿÿ H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3B¬ ! 4ŠI²¤É“+PHÉ7~ùرËwºàð‘G“&»{æüˆ7o¹ P*]Ê´©IBø›J®Ý¹v2ïñó—oܽ{1Â;ógM|œª]˶mAt šK÷ þ`À€~?c²k7Õ/á@)^ ê(S¥H5pK¹²åŠ LýPnܹ}èf~õ¸lؘ_Ý©Rl‹5¨Ö³^ËŽÊß+,Øýà²ïßlíÃúµø=Ïáüq’eJêT¯Z­r<»zëÖ®a»{ñ©[˜žÛÿÆ ¼¼yŒ œ‹ŸÀԩÇç¾»ýúÕßRe,Û6mÕ@óÜ3ÐÈÌTÑh³Í2ë%PAF8àÀ 4à€çe˜Q%¡œò^*³¨²ŠbÝ©R }ÛÍ'›ŠòµæX)Òl“M2þø÷_4ëå¨ãŽI%Ä€Hu@@0@DFð@%A@¤“N>I€?„¤á•ÿØ15Ùt¹Í—Û<Ý*¶”RÊ{Ôå—~·$fK+þTóå5ÉlcÍTr8U)$Êc€ò2â)¸ø“À<ÀmI& ¤ 4Ùh‘Q>@”“™äD€%pP¡²XlÝ’J,S1£Í4ÐhƒãT¤ˆÿZ&);ú³Ì5_VÃà4 :S++Œù èw¯¥¦š. )¦0€d ÀT H$¦O^»i<•8‰7¼bǧnUPI+ŽÝ‡_}¯ìèL4`~©Í3Íèx 6ÛL£#4Ù¸*ï­Ø˜*¶Ù PØ(`XA £í±Ç"ë1¦N*P€…Tb¥8»Às¢»Ô¸ÔÂË+© ¨v àbɃ ¬·Ì3Ó|IMbÖj´ÑªP‡ó|-¦ˆ‹(Çr0( p’P­iÇ€k³$”“Lõ¸DàÏ.«åÁ+¶„luݹ J)8 Pšÿí÷ßâÍì ‰øáÜZ)ÝHÝq¦Hàx$`‡ÅÙjK¤+?7ù¤e `Ae»…¾€•åIp‰™°€Ê}Ø¥((µ.ûŽgÖ½¢Ý8ßâ^‹ñ~@ä®x LÐè¬wÀ¿;à¨K•¡’Ä·? t v>ܰ 0È0 0ˆgÀD@ÿ 8øà¾í× ãÕTAs£¸IŽ·ÐwŠ.›(˜-HQ³ÇÐÊh•bNŠV8¯è}Zçš?iÇ>Æ8*rt€TâZ ˆR”àœÈ]  ˆR$p£q €Ä0ÏY@¯€ÁÜwƒÚ /˜ŠÿêgݬMzOúÑ”ž3ƒ¸`*5ðâ Cð‰b!xÀ ; `=/Øáf@F2Žo,ð ð€úñí6ûÓvl±ÁçÔB¬Yb~áþ½©=Ž1*RÁÀþ̪¸E»¬Ã¢ zˆŽ~kÀ“0‡oY«gSÀ ‰T¡$Ù¡ÀÀ.€L‹ÓO*'"dø ‘Ànmmk’Dly¹ä‘Ç Ù,—-l‹ ¨Üη€'Tá fÂ+’v·Wœh;±ÐãcZaR’‚ÛìA}èƒ"Á8\bu¡XE(lÁ ^xâu4 n·¢V¬ÆEú…‰s ȼ¦u­©Å+^!Èxå(ÂàÀ±8%”ˆ«ZJ¢iãÀž`’· Ë=ŒHX@0÷‘ (ª2=H”L%a´²Kßf[Œ„ö!H%bwé­Æÿ!  žHÀ¥pœp…  _°‚Ì`…= Á Wø‚´€-¬a Ø%€0»õà BC@⼄È*Â*Ö[B=سlå:ò à¾øÅo*‡ôÃZÔQ á†t­¾m‘‹1¼”0ú[Û©¥2zˆ¤R‰‰iÖÄ` P‰•£@†7t”!H”¥~D¤\ªrò…ÀJÙX¡ hp,ªP…+<5aðÇž)(áÈk`B´Ë„5( R¸D˜À„"<ç m8BÆà…,D :Bªà…'°!op¬P~àd20ÁÿÍiÜcì±rL—–#Rn¹ÃÀΩ:˜ÉPîð­ C =Î9ß?œSb N’’Yé }_œÊdê€Éèx=Ú›*à¶‹hðUÈ€€r¬0<á XžÊÜL†&{ÉMHƒ~-LáÍdè¦â7¯ cÈ‚xœ°íjÙÞ÷·Åíæ$t¡ dX‚’ÝüñÀ7G•Ððj+B"-)—-f’@ŒÊ) øÝÕPZ<[NQ’.®P"À‰¥92&ž€ŠW<Õ ù µ6~‰O¼e£R&Nò²Ý—ÿè®ÊŸ34œ! hÀ¾àáæ7×´§Ò†'õ @ÿ‚VNô¢ûÕ*'€§Œä¶9ýéPºC°{|&ûø†¡ ³ç'üˆµÔÇNö†P`]—É8drŽxÔƒåLM¾b–°ˆ&+caG<èPö¾G½…8¯àÏË>c*èK0çA÷°ð¤±ˆÅ-f1‹U ‚è¯KýÎù = üy‘xLÎÆÞQÑŒå+í¸ÄäUd0u’¢7ÏýoLr¸cí1iljNÑŠß×D&21‡80wÍÇvö±'0‘Æ׺Ï~IøFÓ°]ëìºÿ혆"a51·àE,ÜéW£ùùµÚ¿C&°ÒZ‘bwä7Üó)›ºNEÙp þÐ ÖÀ/`¢ ’/Í@ R+ °yAÀ<Žƒ5þ¦\”r~6$('rµò b0øMc‚s êäΰ Ø/þÀ/óœ€O„³V² NSGb ŸP >%0r"d5SBA”To0¶$TÒ2(p4Ï  Ïa f‚ Ф&´w ª/Ç Öp ù ÒÀ*Ïa Û/ùƒWk5,¦Ð ¢@r·9“2Sq¸1Œƒgv0 2…§p…!s£8£ƒÏÑ*Ûÿ Fs Ñ †Ö0 èôr r 3˜#±0A‡D ¾Õ( pB P5PŠsi‡9÷•@9–uQPòh\ÄX€(=5ÀÓ†h@‰±H¤b3Ýä}3Ó ES+Ì Ô` 8A Ð/«ŠŒÁˆÏ ¨ðrt «P ¥ö ¥1ãovPo ³0’Ä©¤Pe ‡…¿˜`dŒlÅRQÁ°7Hht³S §` ·NMƒ3!‚—ÕŽJ(×(v`€iíÈEÝ ¼D$¬†øØ ¾ØÀT ˆÕѤð 19“¤P“Æ ÿ©r´H¬ƒ"Ša B!“º ©P”$RéÈEC!Ò”MY[¼%†fpE2%ð9Ma/Dþð:ëñD !v 7À>–9BèQQ¶Ð®Ã4’Gܘ“âÁ ŽQŒ´wHåw0:"KwÆ 9À”Jy9 ÀîCÏ¡–9BÁXÜ“–QÄCâáOäðƒ@Dð pT‚Ög ­q ÙØ›P;°Ã'ІDW­À(ˆ‚oÈV´&k‚ y@ÞÕ`I‚+„Q«BÐ3"% bù5ÿ9 UjÅà:ÀC0ðš;ÒJ 1aà Sv@Qâ8»%Ü0oÆ h.¢Uôv9z–rbÀšñ |B(îq3Ùq& TGÆ ¦ ƒD·à¤À ¤Àe"‘¯ @)±ÀNï›~µ©à@™ V¦ð…¨ ºÀ õ<ŽƒRÞ Õb1Øb[T•`£ %Iôõnc¹+1<%,Ù( à¹â£‚*–_+ÆðMlµÞ”Ç|ÉÓ±8c.{4`Rî¶,0V$PØj‘9]‰h9%Öí ±‘k)R cÿù0 ê­ùt¿GžûS qþNΜ„#Nð *ü™øÏ1•Ð`ŠŸ°·¸¦™Â"D(`‚?ÿ.dØÐáÆ*(là‡þ4nôGC‡ò Ž|xH;|Àà¸qÀćŠU“Õ+R·NÅÓ¨Ÿ¶B• •)P³NµjÙTc«T³¢¦jÅ “ST©ˆþ$ Ôk×Y^ƒ½õó”*§•"@Ø–-8h»¶„$(` ÁÎ0@°`@% þìü³@A 0\ø `gL’ÄQ‰Ã†køÈáԟ̈v _ ”5zLv:€Bÿà ¤HñÜÚS¨Jqíi )ÑSa‰¢òêT-æåJÜ7òÞc³_'ÎuÖU§,ȶn hôVñty uëúm¹@ ànìØAƒ†iÀì¡ü9 ¾*ñ‡”^À´bxa#f°È‡nˬ‚ 8‚94Ú¡”|¨A#fx†]Ð!…Ū €Wªû ¬¤ˆb…çd)¥”Sl9e•Xº:%–[«å\x •¡r´Ž:*m”r;³Tyå‹y`­½°ã °ƒ#ñ"`Jàzë   ©¾¹‡lJÀ‘&¸Ó€ø Xàø„±ÿÃÃîðTˆft ­8ÍÑd°á”vøˆ…8(³3  `¬)sÌ‘*0Qå*á(+Å”[ÔÉTx:¥ç@Ù¥–Þ¾’%”…ìîJâž+8XQ%YÎò„– ør p€€›†)t­¶$H («¶&HÀO|OI‚ض.¼Ü*o-ó hS¾6c"@€·6Ám·-4°5Q«@„¸“¢Œ2…·°lA¥Þxµ%d!¯ËqH¸T…'T´™£W†ë)”-uÓÙ)Uˆ …•b^æ©cHA”%ýa ­þ5¸’¼¶5o  ÿZMÈÀŽ;   WØ8õS‹`€&À¬F]ó„Y 2À< ð§Jc@€¥…QàR‡ñÓ·Ì@¡²8ãò+²ðÇj—TÖ7VJ ë,^.Š”RT¹¥Ú A ¥•q…K§¢T™'å®r“RœÛ‰¬‡ReS0©% &[wHÀ` fà˽]€Ÿ˜- $€@ÂU` ê¶#\ I¢Àì*#€[ÆŠÚ€<Fמ€¤ PÛê}j*Ü„€(àpC„,\árv ]lÂ'7:…'TQS´â|ÐHŒc !M ·(ž"‡¶  2ñyÀžøŒ‡nÚ¡/‡z]žº‘S&AÀšpÄëaO›šÒ×?¶ú ¬†#cxšû1ˆ _P˜À 4 ZHFà! Á B¨«FÁC8⃠D! Qˆ>lÄ iHCÞjÀR¸xÔ¤ ¨ò·a׃@0€¸oØHoËs'Ät™aœ<9E|Ó@ YíF¦†ÝÑœ>@øZ‚š©& /hÏ®¦ØÅ"øÃ®Ð¸À gÈ FÞ„5La ×mŒL¶ Ù Jÿ`”P†4pä f@k¬à,‹Ë^@ë>%h3°! S@òu“üƒl M`¾[^ˆK¸f0Ãø»Ø£«¤s|›Fè"¿¬‡#ãÞ  :* ᔃ…+˜Þ./äŒKò˜š _pÃÌÓÈ ŽKÐð†Râx#[`ë‘‹\d&LA ]h5Šl 2PAþhƒ®`ji ‚™™°)$[ÙR`õ‘Ü5ìš#òèZ^7ŽRˆà@ž( x-„žÏAØDÊx£/ñ™^p×€dXAqVˆUÙ#¯ÑgäÁ0™„!n : ›ˆÿxÌ! ©Šjl€õ ¸4 k¦‚ ž ¯ˆL$PʸRÁoPÂÖììW;[èEº‘ÙÊÖ"o!OÀ€ì÷/n h‰ýW9”ÏŠu1-]2ì ÈÕ‰sfZ¿òÂ`Êp˜Stƒ€Ü–¹ÌsJ@.~H-ð³:F ¡À_ä,˸äV!õZ @Ô6R`]~j‡®Åù gøÂ„P€.La Æ~¶­mÍ„%$¡ ]@Æ€…ËÁ¼ Qxžð…RÒtôaz}ì_ÿÜØ~õ­)}ðóïû‘@Ó¸QŒ#´ÁôChÉL߆L/áá§?¦«üWS@þ¥Z @@,@<@L@\@l@|@Œ@ œ@ ¬@ ¼@ Ì@ Ü@¬”Î*¥¸»A,Á6zp ‡8€Æû¼v€Á{xu4AÌÁ,€vˆAv‡qX‡oÐq€Á|`‡|¸L‡¤R#B<ŒB)t@bh%¼Â´‡ïª‡{ȇ#¼Â|04ªK›B39Å xÒ–ù˜VQ™¹ŽÿÜŠS…MÐl †hØgˆC=Te€†fð‡h˜§H€8m# H€ŒJ@ €;r€[½Tœ?é?í #½ÐîˆÀZÕgÀ†C­ÖmÀ†h¸†m¸75 V†0§JHu[ I“ƒ0€¨TbýËyk‰W¸…Ux’VÀ!$íÔ‹ç@SmHÕf ÖjgˆU8Ûˆom#ÃCóx€<ƒaœ¸“ÀÈvªÿ €šS …«Ð %)ÞÈWW±ŽeEŽ¥†mHUghÕ”-XaɆP€ˆ&ü¹ 0¹8…•ð6¨Xé#U¨à‰²À¥L íÓØ‰Pz†CMUȆCµÓxˆÊZly”X°ld °ÁÑ›M€o;€l𢩩­ÁAO }ˆüi ^(ÓVxÔN¥Q¡<…VÚˆc@YiÐVmYŽ@…žW9…PPVàˆV –S¸…J79D±ªb¸Ÿ±=[¿ðÕ/a[øàÌ[‡È˜†nmŠãÀ’@=Ò ðÛ†dØjØmm NPVéŠP0…ÿ2}…T˜\Ý`…»Œ˜€;51 prc„‹"b0Ì4]Y m­ÖlÈ© €áýЮܦMŠ}õ‡H­ÖÛõk؆dàˆR¸W±(R×a…¡ØW„«¯:ì-¸(4uZ:7qC ð¶ÑÞ†ph†k¸ªVé ‹)ѾµÝ@ f@Ùm=†fІÁUßm XZ ßÞíJü%Šu4v™8#‚¹:t±ÅB)Jåd`6÷a(…j9 ¡´ë`ÂÝÖn ah€ÔŽ–¤ÓÞZ`]¸bLè†à€)šéž;³Þý ŒŠûrë6 ØÿŠ}#éÀ…¿åhІjÚH’M#Ú©Ýk XªÍŽH†h8SŽQ#MážÀ…aèecÅ ŒãôÇBk˜ô.¾A¹HtQ4fàƒYßà‰Ýf°†IÕcˆ¢¡Y(Uà…áˆeèÞhÕÖlpÓ …#•b⥗-þåH¾n‹a° ¸€›£ nò´`ffNØ€{!”db;°í=Óé¡¥Øh h8ÔE¥?9†î­F݆l8IbM*Q[¸ð_À)"-.-S HŽÜº<ã6fF€¿À¢«æ@lÚ(†º‘ÜùÛf˜cÜÿº*åª%åHåV?É fí‰U…JˆŸëI¾âó g;A €þ“Þã^ `‹Úò°šêÁ«Ø (’>-–YÐg0T n‰ex_%n ièc8†i˜†qn‰£ÕDn-0ñÂûž`¶æÂ3Wøèž{1ff&z I[šB€“ôð‡ì•S X•Mº’bI Õˆ8>Ôl°¼>Tm€Õ¦h†lhß–¨ ÞˆW0*!(e€²`;”8€qýgýê—ža²)1Ùb€ÃqÞa¥ðίÐÛ¹æfXmÖgÀ†f8å–˜†½–mM®`x',biÿ¼°p“¦y ÌnX@[ôø7¡€€ã¸]N˜ŽPÅ`)™k\.¯÷½vf¨i@TŽ0lê‹RHí eô6Vø„Vx €è†1cž%;5™;Ó €\²¯yÀíY ±"_ P8Ž m9†hÀØvY4ÔlXTñT¢°¡VøðVðXàU(†lºd¦¹£»!¨®ZꋱÆ6®ý+0.і͈°œ¡±îŸ(…—ø‡3áˆc°kjHòC•µð–8†øÙ æ["µ_T8©µ 5GS?€ûÙÙ¶õ&ñ69  ÿÌeî “S ._‡þqê €Û˜àêdpÙvr?ïúSï8Eö/`&ŸJ(¨¹onZ¬ò0[2ª‹y nðk ¨€žnÙ¼˜;qŽŸøO¥ŽH#ÐT>g(Ó YõT²¨Ùr·žžøøU£oÒd(‚ž°# n¬Üv•‹þlxî ¦õ…øÀS·ðݵú„k?Uo °à‚—"ŸñÀ\ú–ŸwE¬Åb°„WÍ ¡Z0mwK¤œ‘ewrS¨…Uy]Ê$…°Þ/<óU_­êp3ÎŒž«1<ʘ€â"V‹^ˆ…Ü©Ž!ÅŽVøY…ˆñvwYÿ‘Qg-£ìœb—uU/¿ÿzæOc¾¿±vóX€Sš„n×æÔˆB¾ŽP(ƒ×Ýxs‹x''–’ÑTÀ„’zï´ z¼\G[ލÞÅbØ>S€”oWnÔé/• ÑŠ°Hªx\›/¯N`õ"…M`…¬PÚ!."k&wÓ@/†¢/4¯Ù¥oWŒY¸¤h1T” …«Ï¨ž YÍyêàBÁ=Ó 8|Äg¾  Ç¿ˆ#è xû—¦ø€ƒM@bJΞԠH¨¦ OR*—’Ýû–Xa’ÉÜŠ£À¡T”té] ¨«4ƒµýÿfœL£æÅY ¥@ÀÜ ‘°– [qO`08À>¡¬K \Ú¿(€(™ø®àñS0Õ_}#&R‘µ¦ V(SØ:3{—°¢X°ÛžÝ°"†Дáˆ,hðàA t´`ñÂĈþ\ôÀá!ƃì¸áÃG%J¨€±‚„”˜²u ÌY0gÒ´e,¨SµFò”HêçÏžcÉ„i TQ£ Ž"¥¹ôå¬Sªzˆ`B¬$$0²€Õ°b!88ÐT}@€B V 0áç‚ &3"¬ð`€É PøÈÁsÆŽ3^ÿØ8À÷ñ?/r|ü(²'`ƒ,@¥Šæ+ÆLÕ"Í4g)T/—²:zŠ“P‰œœÎ Åjd,œM—òžyô´S[­_¢¢ZÀ( ­@h°ÀAƒ `l€Aƒ€Ey«°àÅŽÊ;Bt°²@þhØ÷d :|ìêþ@hAÔÍ?ôP™8ØpÙH™T|–Ô,³´2Ò-/¹dJ†FÕÓ)ŵr!O¥ÄÔÛo0²b-À5åbŠJíFŠ?Å@0pÕqVI°@UQUV)€•vd–È7@1Ý €ÂpCz6Dô 0DôBpÿÀ˜˜€`^ú3ƒ1`€-àO üÝPçG<øƒ #ÍЧƒU @¦ðVS©˜ *©Ä’¡k­”Õ¡ Ü‚É-9¥ÒÉ¢­ÌT¢¼¥(SR5·ÛnGÑØÓرVX=p€W=Áþ ÁqÊùêk¥5€¼¢OzÍ›?€iºf5äD3¬ÐHj Eà Ïòe¥§•- Ñ =ð@ƒ ú©'AÁðÊQ¥ÆH)¡œâ’¤«ÝËS¶eÚT)&ÂtË&ŠÚÃ2ÚKÛR¶ØbcO÷Y8`G5Àk¸,°#<À ,@¨? Dtÿ@•¸^}2뜷IàÌWà€|yä—Åàƒ 0|TB`Ö °ez;èpîÌÑ a4pSP¯©÷.å)·Ü‚ʤ…”À3¡¢ÚRÆ„¢âi¿­xZ©¦žHq7/iU#V¸)ЫXË€8 µòÄØÍ9‹ÑÎ<_t ´Ÿ6Ø ‘8|[À­[yÃÓ•8øQ4Ä ÃG+ŒàO àO-}§ÈS¡ ¼T¦¬¢?ÞBJ'/ÕÒ N¡l‚ËjE!\°K¾A,ãK¥L+­ÀÆÓàLÐ9ÐÜv𺠨Ԁ’c‡ÿ«?•D`Gs2›œ DA:Àü> 4@G 8Àè0$, Í"VÊ °.+[iÀ‘æÔ“Ô€3XŒÇ â;`‚6ÃûLTq Y¬yEq~â—<¯:éŠz#“S„bD¥ E,º—*§œÂ$²E-T¡ŠPȥȄ*d‘ŠZð³JY€ WlÉz$Tb-•ƒ7ìp |‡Wá`O쌌0­²Xã`Õ8^q :€´J%à¥Â$’Gð‘ã”Ô€(ÀÄä$Ѐ‚Z¡hr²‰ ñF'¸8‘*  Ϭb9‘MnO-eÿ`/B…1Fâ‰U¨ØoPÁ cðbUþø (BaŠeÞ &ËäP,ü!?¬4 a±#¡'°@ƒÇA€Hs+Ô,¸i$ ˜ÀöÈ— ø,b¹UÀ#±´ï*h$+E>Ȥ÷Ù¸´ŽIŽYÀÌ`…0ˆæn!jSNQ Uì+'ªØÄj210Rõƪ¹ IášYl"4ùåßRq —ö„Ý*B‘Š£„âcE/1ù%4¡d9Æ<+ ht[YÀ4º„Bu. xÂÎp3D¢«xI.›ÙUÐ )M"RPAŠœtÔ5¦øq>R”‚'¨øÔ¾“žj¨­È(L:‹ŠM¬BžðbtxÄ@ Õ“H°bê0x´×(P;{-ªJR⣕=`€L´:Y–J  ¸±jœúÖ2J’©í9Is 0×- G‚nŽP{ZE«vøGØÌp†õžÁ mM)<ñŠVøR¹dÊ*ŒÁQ˜ÈÓ×)dÑ Îd*<9íÂ~SŠ{ÙB&·0Å*tQ°‚åä¦ðRh!‘Á­ž˜@@-,€€TÀÿ®ŒG>·G±’Ú¢†€pG;Ák8 óFöc@wð€$UÈ(@Ï~‘d¤Œˆ‰ÐMÂ-É v81à,€ µÂÀ\/<á gx ±çÌP¬³>œńҊSÈĦè,/:¡¯‘†ÎFA¦Ú*›“™¬¸¡3=·,^Ê‹LZ7$0ãn|L%VIŽë†ÑV¥rz›Z‘Àúv¥•W­ÑkF@oE(Zþ1Ú§è`µÆ‚éÆLf `Õq€V]ÍF¯V‚¡I…À0º¼‚ɨ¢ÍÞ0ƒ™½^°Â ˆbøâþ`Å.tÿ± JLbME+^¡îW˜B_éVI½Gè©(ï`þ˜,£ïƒ±ÂÆxE/óŠ\JD~Â7¬b€b`t ¨ R—s€ÄÙ¿€ ÈXÁÕƒ¶ÉÈ’•x@¸rXD棳£ïx'‚ðø¨7ÈX! ‘z1Ȱ@íõV› q8‚z×{…1À€„"áF0"؃¶pCÀ°ªE,\j¨:»h7¿â-xaX^Ȱ™RÑóJC1·ž‰°»Ý¹¾œYÀ€ÈÚZ@:©'l̲Ùi5 HÔ±FÑx^#:Š4m‡ÿC:€‘œ¹A"Pb´®,h$ê_΂‘´Á 6ì§}…÷à¶?Ä "ˆ7X¢ =yE|» Ý"Â.YŒb¡/Y˜Âƒ€Ä <ÅZt–'¸8´Ê€ ‘*yÀâÔ¸ÿA—  ™Š»¸œW @€S–² ésö{ä´LͶ(§»qè´ Vè´’4w8D ] ûùC|ÐÅìÍ×!ÂÓ}à"ÂŒhõÄÜ|ÈjDSDÔõ³¼‚*Lßm\Ÿ‹ùˆU€DÀ)T“tWQÉ­…¤Ä4ÀHÿ€ÿðŠ„ß>‹Xi„Ë,¯¬ùyë4‰uè¡„|h\V UV†7Y>ÓðùZ=@–¹Z€Ô¡\›€ÙÒaÁøÃ|DÀ0ÁHÁ Ó} $‚P„M½B,ÐÈä,ÞrX¸\Çeů¸Ïlʇ|ÊNt̉=1PoqÃwÀ_l•ZË¥UpCTýƒ|{ÒÿžÒ¡A|æ#$”b!^#4ÁLLX¦?Nrˆ™’é h{yšª©LãzaAH鬬A\cØ'ø €>m¨n £§¨R%I1TB%ÇðŒV×I¡È=•\\P"A„@LŒŠÉ«hC)(‡Å ¦®Ä‰ÀðV¼UQ©tˆÇ?pƒ’ž“fDLhžZé•b£ÔgtA(Á DDÁ\ˆÓñDˆ$k`=t—Ú)6’A€æãâež È<.ÇïHàj~h‹5À¬• É…E% ¨â|(Víÿ##yE>£ÙÁuYÔ¢X€\§AʈNCihXp„†?`Œ×EH„Øç®fë•NAd]Øç5f,ŸÂä4^¬DÁ¬,XBOŒOŠ,!–lD\!*Á…&b}hŒf1þ–VŒŸÉ(€=ar$­–jn€7°hÉí«S]ü¥Z…@ñ賘GÎP€T€(!9%Ã6¬0Y„ç´Á{E„ "Düèê^iÝV&6檅’Áøä@P,DT&ÝV&â¢Æîê5n©@D¤AâÀÁ ÀèʆE:qÿêq$€hSkúÃŒ&SñâÔÞ`¬P\E„¾F ¿jÌÿ &Æ=@çUBuòŽIÌšy0˜î{TÀ”ØA7ô¥èn«BW¸k’¾A (:¦?ü€Æf)ÜÁ€DAˆZ€¤Á¬%€Áîøáj¬®2.þæmþââæ©´œNDXNü4@¤mZ‹NÇ@‘±nQuCøm=ÙÑ­0Ð-šf‹µ®$-­p]¦âã+,N ƒã‰d\© /\ Ä®1€7œ×lð@X91°TvÃJi°,0ÅiS€0Ä_ÉJ£H¼ÿÁˆ´]A(k4ÀÏÑ…¸¬?¤¢•â)Çâ/ãïMni¨Á+V…ÁÀíHŒ€)ä "Û¬È+A¢Ú–Ú2žV €|¼‚«U…«š¦|ÌaXyð‹BÙÇp@“„…°¥îšîD¸Ô×q†€M`$ÀŽÈZ^ÎEg§“^jÄ ¸‹úÄÁdADAŒ(äÅcLDˆÁ{ñ'ÜäÆê/ãi!&Á„@ÀA„2·ÁXAŒïãNI%|nó"‰ãäSætÕe¡pñÊò„(áÕSUpóVl…tðJ*b¦òÄWêèZ¬…7 ú£¤ÅÊÐòAˆóAL€tùEÀúÉÕ{Pdqd$±PŒî¬‰LÇ4L?fÖô%Û1×ys)M ‹è ²yH„&mQoÒ^T@Qo’,õIJĘ8 TGuå¸U×s,µ’Ø0l`õRuV'µX5YoR‰•µXÂŽX(EI„¨gy¶A8—5^çµ^“uâeØîõ&=4–ru „÷ b'¶b/öcˆ裫å–UÈcW¶e_ö?;ntpsec-1.1.0+dfsg1/docs/pic/stack1a.jpg0000644000175000017500000007172713252364117017377 0ustar rlaagerrlaagerÿØÿàJFIFHHÿíþPhotoshop 3.08BIMí ResolutionHH8BIM FX Global Lighting Anglex8BIMFX Global Altitude8BIMó Print Flags 8BIM Copyright Flag8BIM'Japanese Print Flags 8BIMõColor Halftone SettingsH/fflff/ff¡™š2Z5-8BIMøColor Transfer Settingspÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè8BIMGuides@@8BIM URL overrides8BIMSlicesoØ£stack1a£Ø8BIMLayer ID Generator Base8BIM New Windows ThumbnailyUpp]ÿØÿàJFIFHHÿîAdobed€ÿÛ„            ÿÀpU"ÿÝÿÄ?   3!1AQa"q2‘¡±B#$RÁb34r‚ÑC%’Sðáñcs5¢²ƒ&D“TdE£t6ÒUâeò³„ÃÓuãóF'”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö7GWgw‡—§·Ç×ç÷5!1AQaq"2‘¡±B#ÁRÑð3$bár‚’CScs4ñ%¢²ƒ&5ÂÒD“T£dEU6teâò³„ÃÓuãóF”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö'7GWgw‡—§·ÇÿÚ ?ÍéW°2¾¬d»+3í¯{0€;©hÙ[ëË®s1¬Þë=\OìYB盉Ðc«n>m®Æ*¾±ê݉sësvûÿKvÏô«£ú™EVôËÜ_êÒ÷00<ŠË_EnÝe?BËê¿FõŽ>®u|‹ï²ºCi±ö:·7Ü×9Ä@c‹Ûíýô؉@Ö»Cͬ1~®ƒ®.x?øj‘ÿº cêég¨q:†ÍÛw}®˜Ý¶ÿÉÿº®]õwë–›O®÷ðúÛôCkfŽu˜Ý©«ÿY=/Gì§Òßêlõ)ú{}=ó¿ó~Ôî éé?ËüqÃ÷£ö†‹©ú¤×m}y­päʇÿ”_SAýl“Ë~Ù^šxþÌÚ®3ê×^a.fZ÷^óed¸´Vàë½=ŒþCÿé.èXî­•œ*ZÚÈ-Œvj>Ží®÷r— ÿtý‹Ä°P¼šë`F&?Õõ{Ÿ÷.sq¾ª¾}:3¬ÛÎÜÚ´ž?ï5AØ¿W™ß¶UÿÈå¢ÿ«ŸXÞà÷â7vÐßkêlÁs·< vï÷¨ÿÍŸ¬?÷ÿ«ÿJ%Á>Çì[)ã³Ã+C.Ëü^)ÿÒh·êæƒìYóå™Oÿ#U®“Ѻw\éøeÈ,¾à-ªü–¼Ú×Ygôl\K?7é¶åj¯«j}M¬ë ¸?í²ÿ¤®ý[é½C ë^ ·Õµ¬‡79Žömß¼„£ ýЉ:ìñiÐGÖt__/Ðv³ ÷×ênm¬¥µnôvú;ÿ©[çývn©o¦Þ–nßê¿ÔÜìÆßüöïSfö}ÜI/±/ÿÐÎúŽëGFê;K}6ØÏT.;ª²¶ìÿ5t=™x¥Ì°~†Ú7 t»ÜíÍô×açuL\¶áYc(-ß”h‡:º,¶C¶þ‘Þ›ª³ ÈÍs.ecôÛË«Káìö¨ÎSŒÈç@uùP`'ÂÁFǪ»=@ñcœØˆ¿ÊÜíè·Ä}ëW ¹Cù¾ Æ-u·Ú3­ç¹®þÂÉúâìÎÒèÊé¦óu™§ï²û@ae¶6,ýúÛîN‡:4Œ2ï¤VVϤÐìÈø½<³Ä}ë½o«ý‰ùËslm{Ã?J=Ĺ­c‡­»óQ1ºÏS¸0»=Í{˜Ihì·Ûs·Wd~oýB“ïCOFÿÖˆnÿ y›ŠŽ-ú;rÎÄ}覼aÆCOž«™wÖLæôË2Nk†[í˜Ä]%ÁûYþû}/Ó]ž ï阙NûS®¾Šl±­ºÐ¬cgµÅîkç}^gˆD‰dÅþÏÙ—÷½ìyX2|>x(LÂ|BÇÍÿq(9ïm@Klkµàr©a>·}eÆap.ÎÏóo[ç§[¯èó=¿ðÏ!ÐcôrÃÿ‚¬®}>Γ€Þ±E×וNM-ÆsÜ·z›ÚúöÙô9!Ì\8 œäK'ÿãQÇùŒC K‹A]#ÅÿuÄé ³¿ñÁ5z•ý¯öHný§ÓŸU×} ßôR\?üåëŸó”uh³íäzhõ6ìû.ßG~íÿ¤þg×I·jÝ“ø¿ÿÑÏú þžzø÷ÓM™väå6§YPs¶¶ªìQÌwÐw»ÓÞ±ÏXËÃÈ~1Ç¥”ï¡î·› .ÚÖ»Ð²í®«c=þ£ý_ð‹sê-Œý“›Cœæ8†Hî£i-oçmQ£êŽkŸ›nEÙsÝPhÚÓ=ž×µîá203”€Zê‰L@Mtp¯ê]B‡úVU‚NÖ?Û‹Žá1¶·ÜÖvÇûÿqßXºÓñ=w¿Í6ú>ŸØñƒ¾ˆ»²¦»ÓüÕ·ÿ0úyf]ÕÌ1¦Ïr_óŸ·_>>Þ¤rV{ØûþV?Ö¯¬ö:œ¬:z¸?„úcuGD;¾²u׆Œ[]k¡ïm8¶o{½Û½µU³sÚ÷-¯ù€u9·âj¯ûÔ]þ/pÖúÍù$½‰ö ãÌràj%}H—üßnN@úÑõ…Ïu6?¤7Þß²PHŸÍvÆý-®Sÿœ]k’üBfDáÓðì›~ c2M}JæO…5ÿäÓŸ¨ŒíÕoÿ¶Yÿ¥û¹ìžcš$ Ò쟶¢ãÝõ“ª¹ÍÄ~ó¯êÍoýCÛÿEk}T̯¬æÞî§F;ñpq÷>§WzŽq‹ý-Ö1þŽ;_ï»èz–lDwÔlRݯ̵çH% j>“ö‡þwîþb¹õ;¤WÒóóC-6‹ƒ6înÝ¡®w¶w;znLrŒI"‡ša–24 ¡ßÒ?çöí”}‡ì^¦í£fïG¯»ÔüÍßÎ$œÛÿ¯h7þ ×óûÑ%ýã'ñÿÒÆú›Ôºn+2ñó,ZüŠ­¥Îa 5µ½·¼Ú¶–·u{÷9u=¬—à²ÁMŽ®Ï}O®»^×1Þæ¸=•¹Žoîl^iŒÚßöÙ]–²'e1¾v®næØÝ¬¾ßoóKµéŸZqÓðzuÝ©Y•Ó1™M¦˜ofÎc€vÛ6~zŠp‘”5½í1˜©tìôÂŒÍÛ6ÿé%\1ÙêdM 'h}­}m$þnë+kw*xß\êÅfÚ¾¯õFƒÞÖ’H¹ÒVÖ¬¯ôêð¿du+©ÿŽhê>³~ÁëŸÓë³g¤ÜmÜ¥ÿ¦’æ7·n—mæcß§;Òíÿ¦’Uùp¦ÿ7ÿÓä>¯Ž¿ŽÖ½–¹ŽÍpuw7kØïkš»¬+ýLŸZǾ°,,w8„²>®ü÷¯9ÂË™õfFáOéñãù?¾»~•~C½ezxô±ìf[¶Æñu%›ÿE±E1¨)ŽÏFËâ~ô­½íh÷8Ñ5?Ööª¬¹±ý3§ÿìDßQ ˜àC²º{ƒ´-9ƒò,LµÉ™‘5ïkÞᮽôòjfåÄùöÈ“¦áíÛÿ~CkÚÑ'¤ #åµ8³SúÖžÉoþA Nnx nwÆLh—®óÃÝç©@›;[Š~-ÿÈ&m6´Lcu1ÞߨJÔÊÛœ‰{ä qÛü­Íÿ3bÆéÎmù·˜,³1ŽmÖ¸»q¦_Ù4pg¡ý„pó–÷_MD[è×Zë*if®†²ê˜ßSúÌõ7^Æénå²Ák·7,°Ýöý¡­Ùéoo§úVÏø4ê;uU¸‚Áÿ=‹ôÛöðß-¾±fßóT†K?o;3Ýéý¤_Ç»n÷d}ÞØ’“ø,þ/ÿÔàz´u¦Üݶ—Ùê66Ë·8mŸoÒ[½Fמ™Òï!æ¦àuÕ°³þ‚çm{í¸Ùc‹Þù.sŒ’tçî]> º§FÁ©ŽpuUµÞÆzŽ;Kê€ÍÌýäÒ>U^åÊÝÎ÷}é‹ÜàCœHw ™ŸŠèp~¥Û’]¿%ô†F–ÐFéýÒ-Vþ/­?C=ñšœôbdˆнCÉ6¦8†íÕÎ µî0GÓÑÍÝïöziÛUcXi2á·a Û].ýæíö.´ýCÍo¥³/®¤\j¸ÎàëOæýõ;e9XîsË\--²Zíí ú?¥Ü‡¹ØÎfã· ¯žòñ{ùÎ/ðWÓ«÷÷¡»==£`qxl¤@awù­]?üÁê÷7üÛ?Pz‡lÜsý›ãØéå/k='££õ™¥½; Ö ¶6‹KXI-lú!Þ›¹¬ÜáïØ´s¾¤gÓŒ÷».‚8€×Ì•›õ­ÐzMè±]øØ[ÿ¢ÑÈ¢ªÐ¸þ°ãü¦úI7øRá@ÿ¢BIýV¿ÿÕó‚àc]ƒð½곫w¦64Vú^Ý€CbF›a¿Û\FnÒÌb×0…® ä÷´ ¿áv¹ià}bÈâ±ÓªœŠå¶š^×~àc™îÜ›0H ¨š}=ŽOVK,¾ê\@n÷ünh®Ï£gòמ·ë×Öfÿ€Å?ÿ}¹L}úÌ48˜„§oþô(}©x.ã‹ßÓk u·h¢Gaû "9­%®"K ´ø6ÿÔ¯?ÿÇ ëç`âþMÃÿG)ñ‰×»ôìSÿoÿéD=©ø(Hw{»2iªÚi±á¯ÈqmC÷ˆˆQ7cZë©Þ ñ¶ºæÉºz¬q-Ú¸qþ1zÐç¦càÓÿT¥ÿŽ7UÖzV>¼û®Õ/j_ȧˆ=GRȪþ˜/¥ÁõÚZXáÜJã:åxÖW{î`u”`Óögï ,{ï»s¶oc¯ÞÆz^“YwÓõ¶„RÏúùÔ2éôÓ*¬H!ÌuœŽ9 žSÁ»"¦ß¾û1ðë ,°=޶Ûýÿ¢©žƒ¬ÿ ]µßþý*t`AU‚~}óÿ ÿ˜¤›üïøIÿ¤’“ø¬ÿÙ8BIM!Version compatibility infoUAdobe PhotoshopAdobe Photoshop 6.08BIM JPEG Qualityÿâ XICC_PROFILE HLinomntrRGB XYZ Î 1acspMSFTIEC sRGBöÖÓ-HP cprtP3desc„lwtptðbkptrXYZgXYZ,bXYZ@dmndTpdmddĈvuedL†viewÔ$lumiømeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ óQÌXYZ XYZ o¢8õXYZ b™·…ÚXYZ $ „¶ÏdescIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view¤þ_.ÏíÌ \žXYZ L VPWçmeassig CRT curv #(-27;@EJOTY^chmrw|†‹•šŸ¤©®²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿÿîAdobed@ÿÛ„      ÿÀØ£ÿÝÿÄ¢  s!1AQa"q2‘¡±B#ÁRÑá3bð$r‚ñ%C4S’¢²csÂ5D'“£³6TdtÃÒâ&ƒ „”EF¤´VÓU(òãóÄÔäôeu…•¥µÅÕåõfv†–¦¶ÆÖæö7GWgw‡—§·Ç×ç÷8HXhxˆ˜¨¸ÈØèø)9IYiy‰™©¹ÉÙéù*:JZjzŠšªºÊÚêúm!1AQa"q‘2¡±ðÁÑá#BRbrñ3$4C‚’S%¢c²ÂsÒ5âDƒT“ &6E'dtU7ò£³Ã()Óã󄔤´ÄÔäôeu…•¥µÅÕåõFVfv†–¦¶ÆÖæöGWgw‡—§·Ç×ç÷8HXhxˆ˜¨¸ÈØèø9IYiy‰™©¹ÉÙéù*:JZjzŠšªºÊÚêúÿÚ ?ùÙÿ8éùÃ6¥§ùWò¦ÎO5ϪE6¡?“$ž(bq ~þâgeXœ']èÃfV‹ŸE Ò2$DŽ¿±„&@Ùè?ŸÿóŠwú‰¦þ`˦§åî¿­_Çg¬ùÑe•e—ë"[i%†>J›¤d­NÁ7âÔpŸGˆ÷Æéž E¼:ùÄïÏV{ŸðדÛÌ|EZˆ½±³åÍ÷†îæ)ŽT5N\3aærD{ÍŸWA4þp¯þrjM—òÔTn+­h£ï­öŸÿ+Ÿì_ }Ê«ÿ8?ÿ9Bæ«ùqlßâ󂿯P|}5×çûùy×$zÎÎQ˜–#ù}§ªƒZ?™¼¼7ÿ¸ŽFYtçü´>gõ/5cÿ8/ÿ9D꼋¤ ño5ytSïÔp^œ—‡Ïö$a—sCþpGþryhWÉ:Ûò>mòåýÌpÆXùh|Ïê_JËÿ8%ÿ9Z¨ûõ,#òýsCæR|)*/üàüäÙ 6ƒå4ùùÓË#õêHÓ×÷ÐùŸÔŸHÑÿ8ÿ9*Ѫ>‰å4UbC:ù`u¥é`FV|þZ3ú‘à›RùÀÏùÉ#RºW”ˆÔù×Ë.Ú†?¸?åaó?©F?èB¿ç&eѼœývÿyf¿†£‡üýZ3ú™ EB_ùÀ¿ùÉhÉç¡yF£ÃÎÞZÿ¼†qŠËÊ>sÖ#ÒSX‹QÓõC pÌÎ ³¹‘OZ|2ÍŽ"Ä„«¸þÄÇŸX]Ï´¿ç t]Zò)h¶~xòu„rKeæ6¹±±’ú&dõ¼×¤|’q?j•rèb†`%}ì Ì8Kåßð]¿^„Ýz”â+^\x­ßå‡òÒïëLÿÐãÿó‰~tü¿óGç?”ÿ1`²o(yîëNÕSÌþL±¶ytíZk‹Gy¦¬ ›w¨ç,Dq;²ÐìuóžL0”Hã±øÿëX‘:#›ë?ùÈ/7ý~ÆÎâ_/Gq}¥ 諨ÆBÁqèʱÜ4ípZñVÚ´=³W†S”êêù÷·§âϘ5¤Ôoo>¹kq7š“WÔÌ>fžéå{äi8Å„ü)éq;÷§@3r1€p×*ßæâñ^éÕ¯—5ytˆõámÓ–8ç7VÂBÍ'¤£Ð2 ~ÖÕ+Ó~›áá+Üž+ä²þÂm&îm>ö8>µl@™`–ˆê@;K :6dz1<·d±0¢ˆƒýˆ¦øx@@,»Ëú ëvÚíÈ¿±ÓÿAØ5ñ†èª5Ïþæ~Ô„@ÇŠ î›ïJ£š¿wöx铱ÑC—óL´½žÅ´‰%š 6ôãCZx¸*IÇy%ÃI¾@Z4~hiö† $òåÀGU™=H,‘‚7­ETô$…ß-þ.NM£Xä…‹‰ç ·|“ËïÎý&î ˜“È–¶[¯¬úöÖ…į …åzD8“B ¨ûX8¤z±å²ïè—Èþ¯#ò) üØÒnÛô „)•Ž2*Ý7öÁäÂe1`9r'~ŸáÕ4Ò<Ëgæ+{™bÓ>§õvôÂʉVîHË£æ DÆF$G{¥H˜ŸôxOà§ø`?ÚÅ-–(Ÿca·a¦C‡tYC¦Ÿ*bާ¿ÉpŽ‹T®ºg& ‰¥?e¦1€ä©Üz$WŠ6*+B‹Ô}N!‰·´Î'hzv½ÿ9Cùwõ ,XE¤=¼º’3ú‘Ï2Pq $v5¦Ù7ßvQ;ØMÖGaùmsqÀ!Ю\W¨ã š×2ôÀ“Íü¡}dÒœÇûÉË¡ùæwû¦—ÿÑùëÿ8±¥]y‹óÉ>\²Ögòí瘭µm&ß[¶¯­l÷:uÚZ2ŸcB ¡Ê5ã8HT׆^ ý"Õ¿/õo$~^é/™|×'œu];]°)ªL¬m¤3ª@ž£3‹±c]óO‹ƒŽ (S™Ø~Ay¢?«ù¿Í°ØÖ¯‡Ûý!öͬ~˜ûœ+¢VÃ)â F†‡—>*[oAÛÛ¦"}ͼdãá­¯ãóf^póž±ç¿2êžm×Å’júÓÆ÷ɧZÅekÊ(’ôíááŒVƒsS˜™Ùø»?O>à…×2;’MÈîw,e##e$YÉèA«3ˆ@f^XóÆ·å;_5YhâÅ£ó¦‹.­µå¬WL,ç‘$cnÒa“”b’.ãA‹SÐ:™Ô½’?&ù©ròÖ¤¥A¶é’þQÓªGæ·?ó%òW)ù’â}P†Þ2K3[¸DUêXÒ€ œ5úyž’$ûù°ž4E˜’V ‡Jeü‹Œ¸n6äé„2¥Áq°Ã}W¢å Ð ü0Ó iT#W¥r4 Òó¨ëÐá(kÒR “ô õV½.+@6§ŽIQPi·—IëÁ(¹ú^¡ t¯R7ß4}­í7föLÄ5y£ŽR»#•ì ¥ìÝF¨b §J½/ 9 ÍÞ?k5£ÛþÁ<µPûS“ü‡®ÿR.}6õ#2IXÁ£9+Mýë’·‚h ^;÷±=‹­䤃–‰PºÑ\¯PA V•Ç6½›ígö”Œt¹£Ï äѨÐçÓ€rÀÄö%æ¶+¤Îz \Ûe>–¬?Sü’¶k¿Í8¥Á _È ñôçðùç'Û†¡ïznÇ_¿ô>Óüìüʹ¸üœüûò¿èIí-t/"êví¨5[ê¾%z…`C)¥#zÔ ^“,üq Ûö;iF…¿oTýsì÷·=þ©Ó3·ü}Í4ÿÿÔùù ?Õ5/eo³ô²G±½ˆ„…ŽíÜÓ‹ŸÅû1ùõo§Ã5¤W0Ç{¥sÿTñüνCæŸÊf?Á/‘U¶Õ#BÕa?“Í_D¾J£Ëúè<†ª{RÆæŸòoæ±8|Ñù<ãø%ò*ƒËÞ`?gËú±>?P¹ÿªy/Íaþpù¤èó0üŠïðþ½Ðè­}ìnêžGó¯ê4~O7ó%ò+…®¾…ªÞ¶7?õO#ùŒu´‡Í”Ì?‚_"µ´mb•:¤8õ&ÊâŸòoÏùÃæ‘¥ÍüÃò)¦uæ-& --ô{–ŠI}R%±¸'•Ø…Û9?h}ì¯h2Ã.²Ì¡Ã.®ú;^Ïí n†&8£±6n$¢¿MëbC'è>/MÏÔî@ðÜtü3Ÿ?ð#övBªéÿcŸþ‰{HÿJ‡}kR Ü´„ZÔºµ¬üwÿ$팿àGØ—‰þŸö {IÚ ý ÿšRíGR¸¾·ŠÞkE·Š=@"‚D«R•5Ò{3ìWg{?’y4¦W0â—ùÀíÓÕë¢#–4è Êüó2&tyT„cBíÓp3ªÏ/A§O†|[ƒòyä/› _Í N¡ÃOH<µwêI+¨ IÄh ˆgZWlã;_,Œ!ÅW½×½êû7•]__sô§þrÍÍŸüâçMÅEdòºÂ†»Òk‹xé_öXt¿P>_¡Ê/æ‹ÕÿK¯#ÿj}U¦K~öž2ÿÿÕù¡]Ýéš•íÝÃÚ_iúˆ¹³º…¸É±°’9ŽŒ¬Ç+±ðjܽ‹^üéüÎó.ž-uO=ë—\T« îÚQN$;1õy!ZtÌ,ZhDìt¤dýÄò¯äO–m4&¼Åçèa[v‹‡œµè¿H¯nXšO å²ö–N) 3ÑÌ¢:2Áùåy\ù›óTÿ~<ù‡Zuôë¶Uü§ôÈ2ð¢‰OÈŸ(ÖüþHÜ·øã̵ûÿH`¥“º?éBøQîV_Èo$°«k~}oŸž¼Íÿy‘í¼ª?éC(ãéö”§ÌŸ’þIÑ<·æ-v=GÏwO¡éWÚŒV­ç¿3ªÊÖ–òN¨ÄjŠPœ”{C,¤§süÐÈb‰Þ¾÷䎓ÿ9=æ-NKXWÊÓÆnP8¯ž|ðiQ_ú¼ï›|Ñ–8“coèÅßö_³§]˜bŒ¨ËÉí:_ž¼ë~4ÙdÑ ¥¾§kw{ÓùçÎʉmaËë2½5–`©Á¶ãSO„«O ùøxx@”pÃê&;Së8?à( 8²j¹#eÅ’¸",€L¬uu!#×?4|í£kZÆÚNtKÔ.®æcW‚_LÆbå¬ìÂU!@åC¸9vLY±ÎxäcphF«nV?PÚ­ÐëÿàY,Y¸qfâÊ<'™‰YÜ‘wÃÜw Ü¿˜zµ±¶¿Ÿ@½Kk‹5ßü}çcX.§kxZƒX­ZD V›ôÌyÇS~/á n£ÊG„|ÏNíÞ‹ü¼Ypdx¸ŒHà;J1¿(‘¿+Ûš—˜ÿ3|÷å[xî5?.ê!Ôît– ùƒçPEÍ—¦.«µBW¡'cÕÇU¤æˆÊ<£ÎÅðß›ª×ÀŽ8c‡Uœ|ÀEG-ðÏ^·1Õê¶óyþMJ *aw§ÝO¤Ùê¢Iÿ0üõ?îA€¶·Ižô¬’?.±òwä㉣ z¹H@ØÁ­ù§sî±æÝ/øèF—óS”*8ã#xþ¹W‰q€þ— ÎÕa/ÿœfüÂ?ž_˜ºß‘5Ãçm-7E¹Õ­µ-/óÍr35µÌtžôŽ,&¨ íL¯&¯&Ó\?^ߎLv–k­‰îêÀaƶëÉzM«CóŸóŽäpggµó¶£2­;77؞ؔ²Žb?hý*q@/˜¿.oŸE7gçgæåÄW*VŸâû¹AñV6ù„ö¦^D·õ²Žž6üùÿœ©ü¿×&ôß˟̽7Ï>eÕ|éëwÚyŽâ VÖæhäh®-ø1Vc@êÃzÒ fdzsxüB@m¾Þm¨€E°?Í?ùËo>þa~TiÞFŸó#]Ö§ÖÛþ`i÷ÖvQ[Þ[D°ËQª4D\)Ù$<€©à(§c†2ŒÏ:èoæÑ#\ßúRúÞ§‡ô·©ZþÏ£é×ïÌž!uøåm_ÿÖù#åÝX×u½cLд›ÝsQf3ǧØA%ÌÌŠµfĬÔ^æ›eRä TæžåùùM'”tM'Uòý¯šu[)´ƒuæ¹µ}ãMLº‰_WãFˆÔÌU”ìþQ7ªª=íÒr~ÑùSþr¿þqîÿ@ò°·üÐÓå¸{;ªEm}$‚ñ-ä¶";fýê…bTh+Ó8½Fƒ8œÉûy» r·ýožuo0þRéÝùŸSüÐÒ4¯,ùŠùîìuMCGÕþ¨Ñ_1–! ²H‘0u׈ßr3+χ“ 7¯o‚gŠQ¾'þ]üáÿœAà—¾`üãòÝÖ£e9¹W5uDã"ºHx±]š›O ä§¤ÖéÅ/ýMbpï}£ÿÎ[ÿÎ9kw–šf•ù¹¦ëº¶¡)ŽÊÎÂÎþi§’œ¸GVÌÌÔÞ€e{[–bÃ2OJL³ã„lOüßùËùaqå4Ù\yŠóL†ÿFÔ-$Ôn´-m ‡ëÒB$•ͽXøfÃý ö®/\´Ù(nvå_¸öžšD’'âüHò×äóÛù‚ÛC¶üÎòF£®ØŽmÞ}a¯9P£êÿ¢}AÊ €Gq™Ú&\€ÇØ?Õ{Åö“‡U Òˆ÷s}}åŸ%ù¦9ô‹‹xôù“LÓ.ôx¯t˜õÈî„’\ÊóOçF,èìÑÔ© i\t93é¥Á"#ŽP'†CˆÙ&èôäCí§þ }“Ÿ¡8eRŒÈ”c8zb#ÀcÅ`jêþ¦ç¯É?Íä󯛵]?CÓ´{øÆ¡®ê–šôI¦C‰¤œO&’?…hîÆœkÒ¸3Œ¹ç—4ñ›Èo•ðòª7Ïj&·y^Õÿ‚†‹6CäaÁDÊ„½7{ Ý{mÜ‚ˆërùI¼§sç¿*_isouc ÷Ú“Gnðzœ½Ú}KêütÐfºYõ?•:S2aq"ïÓÃf€î²õzø.ö|'OårŒâ&&B ƒÃõžg‡‡Óï)w›íüÃùƒ¥èzü¬_&j÷^Py즿}bòyÁ2ô&v²%L[(SJ :ÍF}L1ÇS#.h›¹q'“¢íø$èsc#C§ž3,‘œ‡€¯OóŽò¾¬ºÏ\üàÓôá§é¾gü»‚Ö=tÈbÕVS!õc-¦Õg&Wå-yšîzeÔdÇåUDvÿÖþ—6à—§ÌNIèærqq’g“ym±[ÃÒ+Ò+’cÿ8s¥èÿ”Ÿ›>oóOŸ1<¸òôºH²Ó5o»º¶¸Q4Q[úp“W¡5ÊõqŒlÝt¡·™|›´õò×e3á&;Ôæ1äçY„‡0~E&½üÓüˆ›O±´Ô7t˜ôÝ&±X¥ÅòÅ&wgwv"•=v¢`ÄVï|Ëù~ž\òÇ•¼Ëm¯E¬?˜eÕ4´¶–Þm6XDMÆA)äê}N<øªòVQË®má>;$Wèq厅¼¿œ”¯/‡ëô¥?f´§ß¾N÷øþ†‡ÿ×ñŸüàµÛXÿÎNB¡Ê}oDÕ£åÒ ÙóëþÃ5úà ~:£ÖCôçó»Xm[Ⱦl·³Y [Xp¨f«\ÄJÇ+Ê  ØŠæ›H}CÞåKh¿7µÏùÈÍo#AæmËÿú«å­z+ ȱé2Gú2HZYn¾¾ð$G!xK?¥Øâ£ O=ìü4q‘ÈÑH#ÿœÆÿœ©’Ö«ù©«½Œ@GNÓÞãîlÊ-+OlIDﲟds^ÿó™ó”ÖéMù§¨ÀåC¦ÒôÄb­ºš54=| y~gõ¯—Çÿ9©ÿ9K,‘~oêH†¨éa¥©¶"ÌS ÆàŸ™ýhâ$Q)æÿ9ƒÿ9“­Úë7ZwæÎµ¨Zhfÿ\‘-4¶öœ¸z’†¶ZšlË& *ìŸóë`":ò :ÎkÿÎS«‰ó~ý%­}e°Òƒ×Ç—ÔëÛ#á ëó?­°L«Gÿ9Ëÿ9`€´?ÚǤüKiaÑ&»‹:V¸?- Ø™e"³ÿœèÿœŸ¹»µƒ]üòóÑ%•SU6¶:Tóz hþ”SÛ$nÔè€=ñ£ÎÏ̲à—sµ?ùÎ?ùÈà³ýùÛ¬ÉxLÿ¤WQÒ´ãTçûƒ GlìÅ“wäÙ¨ßjûKiž@9ŸšPŸóó“ˆXÅùÄyLy» 3H«7‰"ËsL„´Â·¿™Ùa’g‘?4ÖÏþsƒþrÖö_BÛórF~%Ø>•¤ñ ÖËÊø°ñò8üÊp?ç1?ç.aif_Í‹häœ)žTÑt~R(9°±«Pm¾XtÑ 7Û͇;'ˆüÊ(ÎfÿÎ]º¨—óKO”+,‹êhZ;Q×umìM=§€äRsLõ?2¯ÿC«ÿ9m2åüÁÒ¯ªÌ“yEeªšƒF²¥Aþ^·˜œÙ:ÈüÒùÿç6ç$¬#ŠÚãÌ>WšB«ò¦‚È0b¼~¤·§Žù*•ß¾lxÈ ºüæçÞ© †«¯ùË:uìrÇw¬Íäm HÐp,¨è–‹UwMHµ=1É“ Å#ñe çO®?ç¼É­yÇKó¯æ'š´ý"çÌ>bÕm¾¯¯XéVzxDQ::Ú XbôânJ ¶W0õ™%+Þï¿É·'¾ÿÎ]j üã‡æi’O÷²ßOµ,w¯«¨Û}=³³ÍæÊÜœ¿I~ýyùqç·éŸOýTûóqÀ7ÿÐùÿÿ8¢o[þrKËÖ:v t«ý^ÃS³±ÔÄk1·š}*é#›Ò…ý7£q;Pæ¬wøÜ1ǴߤŸ¡<å¢ù'ÏV¾t×­üÍyº_Õu{xM¿® ’%–gƒqÈÑ H¯J Òá” ‡êçKpüžüàŒÍï<)ås€Ê‚3›œV1ß›…/©'³óµk£Ýèê—©£Þ79´¤º™m’ ¶W¹bªMGa‘”xˆ!˜ @Šß¾ù|wægœt¯>ùuý#Êÿá A¦ØX êZ¡i,àI;\Þ%e`[‡Ù^ƒ¹8šL91C‡$¸}Uý§ûylÂ#É猜iÔË;&ÙÇ”<Õmå}3Ï}Æ„u—ó††ÚMÑÔ.ì—N¤WF+fUº!y(Šoƒâ­;f.§L²Ç(LÂ1•ÈP£ßÉíý¢ö¯²3ë<]–± £>†I˜Žõ;Œv—!»Ö<ǧjÖš½µÏ“’).f¬/•bi¢Ž‰êFH_Ø÷8Œ‰'µ=¬ì½V N:cã”|9mÅŽ8“ÖÌ='ŸªWÍ£ê~FÓtû3/“$»Õ,á¹Q;¤T’IHô݉<ÛöYG‘ßc×å·“™ÙÓv›GmÉ–È T§3è‘@ØuÂÏ-Ü\]jÓ^_°7SÇ,· @^Nî7mß ÀùvY ™ß8ÛŠìÀÈ"›ž™3*¾æ½¹./ÇqJPzbê¢Ô Á§ZšÓjøIÚ•‹yˆzU$Ná=E)¿¶@ræ¬3Q~6ìŸíà²J:?k?ç ¬¦‹ò?ËßUá ÒÙ[z- -­ žD.¢„Џ$R3]««$òý®N/¥ßó“p~géóŽÞyo>êÚV¡k{©h‘ØÇgYc} 76ôhŒ@aϧ¨Ñðx£‡¸®M¢_‰Þ¿Åö‡ü¤>äæÛâáñùuÿÑùßÿ8Åt,¿ç(?+%®ÓjÂOM¦ŠXã|ÆÔ‹Á/s GÖý†óŒq¿—|û :Ù™Sçÿç5¤>¡ïsçô—Á×ÿó‹ò´¼Õæ?8;þ‚úõȇôqÓ¾°SÑ‚$åêzñ×—ZqÛ;}fG6##¹/9­í_Ëå0á¿‹=_ùÃ}YIj5mn$…bMKô~¥ë#C(_ÒÞ™f"»­+ÐS2ÿ‘cÒgäOåêþ·ö/¾ÿœ-Ò¯…÷Õ<æèæí"ßVÓ¯¤ú©CWh„ú¤œŒÔåOÙ¦Gù<ÌÏÈ$öðþgÛûEÿœM‹~gµ{¸qÿexOaÃùåËßÐûb·ý|cù¤ÿ!£û+Äv,ñÿ/ÿCíýЉÿ8!Ðͧ‰ÑÇ_¢ï%üüE—¯ø>ߨ®¿ó‚‘®Ãó8ŸžŽ)ÿQy/äˆ8²þ_¼>ߨµ¿çËÔÌõQ¾ß¡úÿÓÞ?Èðþq_åïè}¿± /üàdö4£=¿ì¯Øñ¿¨£ùx3í@Éÿ8vÄÓóNNµÑÞ¿GúVv4ñÿ/ްûPÿ8|Ä•üÖ¶'­N §Ývr¿äX8ü‘ü¾?™ö¡Ïüà®*æÅ ¨I˜~«œ?Ȱþr?—Çó>Õ#ÿ8®-B~kØÐýtËú®1þD‰þ/±O´þaù¦Ú7üà½Ö›s<šÇœôß2Á$;{nÚ¥-As…ä-@âE7­vÈžÂIýŒ‡oÃù‡æšCÿ8]g¤1^\iZ…ôPªK¨›íV/RAö¤ô•YV¿Ê ä/éíîý¬‡o@ÿù¼ŸÏóˆ:¤ZM–Ÿæ &Â䨊þì›Ûrä(õr( ÔÑIëí”˱ÌÕÏɶ=³ á/ÑùÅMO+þUè~]šâ;©´ç†Ö[ˆÕ•ÚÀªÎ€îÅs’í(ðNQóý/A‚\Pïƒÿœó¸ú·üã½Â·žfÑb§ú­4‡þ!‘ìÝòqe—é?¿Xè?ãµÏðÍÅžçÇ›ÿÒùŸù7Õç#''­ ù‡LRÖ¹TíóÌl›á—¸ýÌ!õ‡ëï×|á¦yÚ×ÎZ&Ÿ¢kk¤Ü¼öšlò]ZÖ³¨ ;ªóF®Ç‘aÄqߞÉDÀذçâm.üµo‡X@kÆô1ðøíá9轎oJ=åâ;hV£à¾«° ý¬Ú:„\‡‰Üxäy)F€|G°ÀÆéUi^›žµÀ’¨z޾#±eàvðƒI¦Å{רá¥Ý¦ñcô ‘ „;5€ïˆC©×·zW"ih¯ÝØà¦6´¨­|GL›íi_n½vþH¦H9‡q¸ïˆd1çǦ£b?–P~íó?Ô=îfè/Eüœ»ºÓü”5 [ ^{¸º‡G´â'»eYaŒ¹ ɉW<ßµ=y¤.®_¤½þ”V(û‡ÜòïùÏÍvIÿ$ü©Õ&µŽÿÍ:l’I/&T±¾ybP#é• Ð€ötœ¨Þߤ2Í´_‚þ¸ÿ¹—,ÛÓ„ÿÿÓùmùktºçoåó2¢Aæ-1¹Jq¾†¤ûPå\$<šÀõ‚ýÕóJ/é5C‘¿©crGVý‡=ó•Óš÷‡cÐûž/ùFžfÔn./,¼½q{¥êR[˜VVa)—ù‡äTùŸ÷Ü«ãX¤­qüÆ/ç˜cùL¿Ì—È´d޵<–Ÿä7ôÉxøÿœ>a¬é²4üŠÖž*çµ:ÐÿL—ùÃæÇÀÈ?„üŠ‹ÜÂ:ȃcâÇùÃæ‘‡'óOÈþ¤$—6ä|›u¡Ëh£æŸ c¡ùοššÌz~¥a,kõ¥*¬ˆ¼õf,Eæ¿WœÄÄÆŽûî9;-8‘+ŽÝÇsÜöïùÇ E5o+éw6Ѹ„µÊ@V°2BXS±*ižuÚfòŸ{Ûá@'ŒÿÏÈîÄ_—_–Gf¹ó5åÇ uX¯ü•Ë{3øþ sr~ úÛôÿž_E3rá~·ÿÔø÷|Õ4W©žT?JÿfR95aê>WüÂ×|™gq•ÙtVíÊO¯Dë :³Âå—CÄ|$Ðu¥s&9‰]ݘä¨×Wíä7‘¿/µoùÇ/%þfù·ÉV¾jÖî|».§­^ÝÌðËu,SÌûÆ•"ŒQh¶súýNXꥎ2 X¯“›Ž€Qg¨Îj‘MºåZ‚tÇ,e1x‰ÆTHØWN¢Ý6øažp!’BðëˆGŠDP½öÛ}ö ÝüÓùÓkq ÛO>‹e™o­,´{©õ3Åÿ[·[^)?M€ñF#²ÖðŠåòìýp–8ÈW‰(Æ=Ò2ƒÃDh×ñlîáÿ~Ì”rJ:¬’8£)L q&<21á«ÚDŽ(ÄÕÃÔi‡êß›þ^ów•<©­>›¤]y-å¹7šŸší_O[‡* Ò6¸ eTs`FÊAÌ|¸³áœ1å<‰ßn3CŽÀ#—¸¸Y¿à]¢¼xðj%—øýQŒnG³áÑ"|_LOó¯¹é>qüÄüÇò–|é¯O®D.<²Ò6›a-÷šÑoíâ{43 ¢ó‘ ‘¯SÓ› j6®Ç7egÀ%9΄O§Ò=cÒ.ì×Ö8FöÇ_ÿ®ÏŽ›ÄÅ“!ôL*2rà”xc3^„ÍG„(îõ¿ùÃ9ßÿÎEy?ÎÚßœäÖ´›ÿ*ëi¶¢y«Ì±E,W¢â²$úœÄ2ª„vÍ6»Q—¢#.b÷õ>'¨ÒŒS®o¬µ"ùwE´7sy›Ï­Í„VÖÑyÃ\ç$‡¢)kÊ ûæ$uùÏQ·qåŽ#¢Q6‰å› ›‹-_͘¾_½¶p¯gsæÍežŒ¡ƒQnœAîrèk52«»—ö0ÆE¡âÑ´s5«Ÿ4~eÚØê[éúľjÔþ­3GO°MÁ&¤Ò”¨=q=¡ª‰æ>]Üþñj1ã'Íç^qü¶¾×u¨ôˆ¼çç[ØÙ©é]k×s*‚Fç™9L»O9úˆ?&8`:?+8¿2|éùyçï7þPùwÌ7Ú7–ü‡æÈ,5 *æ{-NrP±ÝE .¾¤¼ˆ¦ä çK¦ÅâbŒå¹½ù_¹Öç<2áƒç¯ÍÌO2yÒM2ßVóF»®éÚe™šÎÇY¿¹½ú½Ä–Y#ú̲]UjAí—i1Êõg¹§Pc#éäøÿ›R¿årëÞ´ÿ3™ôáìÿÿÕø÷ªÔ]hË¥Ñ_ø%¦Uwîi˜äôm;_òV™å5i:ï•Σæ½VH¤ò·™Öy£{ W‘4•c:«±Ø‚+˜òŒÉ<º·qF¨¿@ çâß“¿–¿’ÞKü°ó_’<תßùvÖâÂöïNVž9.e™L“Å"ü2E6#®j5Ý›6c’2r1ë!ˆõÏú)GüãG–ãò»Îvë'ÂѰ±äµÙ~»Ò¹‡üžþ¸ý­ß™ˆL’ùú¿ü㺄Qä;D±€«Æ×O4jmw•žÁÎOÕµF²É’ÿÏÖÿç©CåO< î~¥`iÿO¹Ø:çE˜ÖCÍeÿüý/þq¿RÓu-6_.ùîõ+Ií$tÓì‹*ÏFYA½¡ 5pŽÂÏÎãó_ÍÇ–ïÌ;?4Î;ÚÞE=¯æW[Ð9¼ŸjIP(9Ö€éÖ™°Ë¢Ë8È >±Üög´'G˜eŒA#½ìÚGüäwåœð³ùï^Õ ·Ó"Ò"²½òrz?U‚à]Ä´‡[FLŽ“]F\¤…†¯QC+ì6ߦDé3Ê3¯\Œ¥½\ïËÌÓÏk?à“¨Ï›ŠÄ j ߀ Hï^˜ß¹sù±ùaw¢Øh-ù³¬ 7NºšúÒòt•Y§H£Ôj„ÓŒKAZ¤æ$û/<ðÇ ‰0ŒŒ†ü‰¡Ïà=ÏUø7êc/iâ2™q”bIŠ®r;ó;_&=æ_=~\ùZÒ5Ù¿;u;{ýÖÚÓN˜ù6à”ŽÕBCöu©P£s¹ï™_•Í#y=R¨‹¾G*ùº>Óÿ‚Ž}N¢ñcŽ"$ƒ)›™ßùÄòå[/óænƒæ]RÐõ_ùÉ=jÿKÕ®~»©ØÜyWPô¦¸ «/×X pËç‹S0FIJ@›£+÷Óƒ®ÿ‚^«U„â0ÇÆŒ¸?š#äú‹þqþr«þqÃþq³É¾nòæ»çÌ÷þiÖãÕZòÃËsÛÁ q[%ºGInÙ J™ƒ®ììÚ‰ ܼm|2Kˆ¾°ºÿŸÎ!jðÇ þµæ9#I‘«èW+ÅÀ 5Gë¾aŽÇÔ‹åóqΦbRûŸùÏŸùÂÛùÞæóÌ`šâCYgŸGÔ]ØÓ©nLN0ìÍdÄ0±wª/üç¿üáŠýMÍÚÚÇe'«gšF¦c‰ö%•* #z ò'²µVHÏ>[³ŽlC«-³ÿŸ‚Î)]K$°yÓQfŒr’eÐ5U|IþHÔq0Ûù¨w¿4ÿ1ÿ/ÿ1ÿ;?0üÝaæ–<׿KÍCN¼07ÖžÑåJÀÛ«²@b)ûYÔâÅ8aˆ<âøºéäŒæKüÒO%[ù—V‹È:…Ϋå˜,`ú–£vë$Ò9¶ *³¤P«pv)U@ *2ì&RÈQcväùw‰á]¿»¯ÑΙ“nÏÿÖøó¬’™!Ø%â|÷Û*2×.¯?!ÿ*´ßÌß/ëó6›¢_ßhºþ¥.¶÷èƒHšMÄvâÆxH˜²ÕYê;f6l£]Ñîo„xƒØ|÷ÿ8åQòÏ™´Ï$Úè^HÔç)£¤W÷wZ€áêg3M"ª£áUZî*jvƬ’ÚLÆ]Óê-#ò3òbKKdÈÖ±Çé¨E<$¢µZDv'Üšå~$ÆÖÎÇ6D¿“l?å³Ün}8?Œ9 g˜äSAzÿÎ:~Jµò-¥)_îí¿W¡3Ì ÿè\?$E?çF²$to«ÙŸº¶Ç>L¸Pÿ8ëùÍ•¼™¦+¯ÛV‚£ç[m°D‚»Gþq§ò2jΉ§²°H´°;{ªä?36TÑÿœaüˆ#øÄù„±ýbØd¿1$pÒ[üã/üãíì·QAäË)%±“ѺE¶³¯þ’‰³;åÿœKü„vßɰ©#öaµª„j%É9çÿ!ZŸó©…vKú¥æ&¤!üáçä)éå†?¾ÿòKÔNÐb) ç ¿!vÿzQÞœ ÿš0T¼˜œ` æÿœ4ü‡ ? d$?À ™‘aá†?/üâGüã鸚Î*W»ˆVX=èëØá:©2yßæ?üã÷äß‘|µw¥éóÁ®K )bTFÕœòo€wßæ8¹„Œo3òüáf‰æu-[ÏV~jò¾°Õ½Ñ ´6ðXjv×rAÖa%‘‰n?m…p dë~]ìNš$ÛÀ:ÿ*ÇåN‘åX®tGËÚ¾½¥j7Z——u û=M­VÚ_B6IljÄR£2±ž>G¯Í¯,xCä-ý*ïþó׿_V™‘Á÷¸\Cíÿ×øí®ÿ¼¶íZñ»„ÿÃS(‡6©>íÿœL¼’ #óNÝ[oÑr€6ír‡0ußHr°ÔOz÷zfŸ²ŠÙ$ôP(ØÐ ÔuÍ\Ed·(—½h†±·§Ú¿¨fdšt2° µGo ‰ ¦+.Ý7=²¹QZó¼iñ«âM–%«£]\ò¸Ób6··ZG©I ?­$mûåhÒQTÒ›š³d€Êmn­g··žÏÕ™G¥Ç`~¶Û Fé´<ú¼q]ÇefyÙ‚º© éŠäKPJtß z©+bÕZi.ßOw–ÞAÊ,‘/@hÇ–ÄL„#l¯Rî7”)ŽHÙ£š"C`z6;o¢ÉRK¸#’(žEYnz1“»pj|«¾wP@©^Ø“ÕM¤RvÙS‚éTdkÔÕ„°¹/¸Ô`}.+6ÓãŽöÞæêYuD•ϯðô#h˜qCU~Õk•Gâ³óßqû‹YÚX2écŠ1¢z ŒoˆñT¼K¥ôÖϼù$zμðÝV8å…6;|S"¾œ¸ìè^«ù}ç-wÌ×/g«k·ú­‡—-VßK´»™æKegâDAÉ 8Æ£nÃ$x¸h‘°/„ÿç>®ó_•ãV‡Ê·Ãþ2^H?†fiO¤{Ü}Cówîz÷Š¿ò[2¸ÚàìÿÿÐøé®šXÈWJhÊ“eæÓ#O±¿çï$úçæ-¸–}*ÆbzPóùG\󯕢ó:F³Kåû{]Jêå#h½ew[t‘T4gäEFfáÓdÉÄŒOy 5Ê`ly»Ëš‡˜¼ûúF/+jšåÅ©ô嶺·¾²f$V±ýhFSºÔø3ÿƒŸXûmÕÉñoæ^±æ+k7ÇQú’3ÎÝYò“ÑôK9«šâAÈF\WÃͳéܲ)ç(?)ü§êZ'—¼Ïws¯ßXOÌvjê¶vW*Ljd†im}S½xƒJ½²X4ùOª`UòåhžX)ñÇüäWæ$™z”~aŠî}JH´-3LÔ59ì“N7VîæYRÒ9g+—ÙyœÏÁ½î6yñnùçÒýÏýºëÿ%rßÖâþ§ÿÒùQù»¡h¾Yóoš|¿åÇôKDôa¹œG$È=U,åZ­¸©§Ž`鲘ã"oÍŽj„ˆ>y9騂XIò‹¥I=³&·¶ýt¾óÕ÷(*tK‚¢//yn´ q5Ÿ ãT° ´ßç×5S¿D¹üâøçËšzž™ Úi¯¬yš;ëy~;û-bê1Õ~‚^ ìÛñî<3*qŒÛ~ž_­Ç;nŸ¯çO™#4Ížwdfn$ùŽæ2 Óˆ ˹;ŒÇ8ǗɳÄ7Í0?þa@Í›¼ù#PqWó$éÇsS^]¨(G¿¶DáÈ|”d*Kùíç`Í_9ùäB@ ó<À©¡­XÀk_ÄâÃäž:IµÎßÌéå¨~byºÎÎDTkiõ©î°ûDÈ¢.§¦ÛaðÇóGÉ"hüÝü×+ù—æÞ£Uºÿª™žƒäÜ86¾ÉüËó9t:¥Ï›á'øGÉ;õZ8?5×ùYe¯ý,®?æ¼L#Ü’Èÿåv~nÛ±†/Ì]ucáE7ˆl “÷à0‡X’ñ‡ç¯çÐÌnƒþ-_â˜|8_ Ž"‰_ùÈΘ”üÉÖ›€L,vùʼnÅæ…ã%‘_ÿÎH~i¶åјzÌz ³y‘gŠÚŸY1˜MAŒô{á8±ßÒe°¦*ÿž¿šÓ]I¨ÏæÁs¨H¼&½šÃO’V^•(ã_ù+\[ ûŸÿÓùIù¡¦K¥ù£Sµ’ÞþÕ§µ‚Q¥|º•ÑÉt•j vÀÐäãÃ{ú ûQ™ç¿›Í£%ôè6,L ­Äô¥í™rØ—ß§Ÿ—R¬ÿ”ß—wÊIe°ŽÖJšn¤Wý†j³ÿ{+v›|CCÅÔ5þêâUýW"Ÿ†fVÎ:(1§}úÓ"•P›öñÊé—šài¿ŽÎ’FÍ é\ÀLB"Á´«[0Ç«èÚ¶¡«Êo[G¹·œ}_÷¡~ª­ГQ™šIicœÑ”¥ÀxxMTÍp“ä=Wß`>ŸìÞ¿Ùl=—8kðJzºJ‰‘Pˆ¹ÙüÔKéyRh?Ck¶Þmý‰ɸõ#kà㔊” TWqÞ”Éøš3ˆú$2pø®&|[íB¢qô܉9?žöF}“(Œ3޳ÂýFò÷ø|÷ÚU„ÆH¼»ú¾·.¥vp$·.V&p \ÜH90¦üôî2'áK„KŽ£WU|òH÷oQŒÎ'¢öÎ_d#ÙÓ(ÎZžF$ñÇÎy7ÛÈŽ]ê¼íÆi(hšöüs^I·ÊÏ5Džrh¿µ{QźÞ[nzt®O5 e¾óÓ%c¢)|äA'ŵ:ãe‰ú·þpö–My€?¼ÔlÔÓüˆç?Ã0uÇÐiÈÒSãoÏ ½OùÈΙC¬ó¶^D¬À° GtʘåišÝL?{øîs±Ù‹åb«ëÚí½?¸Ôn”w Xm–ƒéhC;Šm°ÄóM¦¶Ú^¡t¾¥µ¬— IKPnMiZtÊ%–#«7ÍÓØ^[F²\@ðÇ-LNà€ô¥x7¥Ei‘Œã{œ±°/š( zÔÓç—-›†{æO*Éåëý¤’HuÔyíÄžU§!F'±ûó[§ÕcÏ)ˆ04|‹™›M“Œ¤(HX÷)Û(ü¥&œº„RÝÏwÁÓM‚ ‘㥅çÚàUþÍ|>ù•[ÛI&©OË~V»ó,:äöóEè¶R^0–Hã˜Ô±=GZ³S`*IØ àœ„¾¬£$‚I ;1$–5åãï‰kµ¡MÃJâ@åÕ"E Oa¶#™µ»k ~¬‘ÛšÄRÃZ×€GdR_¨[¾Ô>9dzµË›ì_ùÃe2oIµ¨•¼6†oëš¾ÐÛߣ™¢ÞO‚ÿ1ï ïç/æíê̈n|åªÈךýraÄn7 ÍÔîáýQ÷89eûÙ{Ø'™Ïúšõ-t*>J®K°šqéü¿Þ.?NU·Úž(ýÿÕøÛ,¯<ž«¹gTÿ¡0ü˜Ü‹MPÿÑôßóVLë2y$c ÿ8aù8vX5ANß]šŸñ,?Ÿ’|:A?üá‡äøû1jÕéþ÷KýqüäÈä@ ç_ùÄŸÊ]F¹º‚ VI%C_J7§^ù/ÎMKü•òž“ä]b=+Hõ…›ßAwÆy=F ñ? ‚  uÊõ3ñ1nÓ žÏÉ Và_ùÛΗ¬ê ç™u)—¿+™\^‡~¹ÐÕD{TÏï™I|ËZiQ®ü®çÑF Cš2r¢É¸šôÿuñê<2šlÿÖøÊ+lj4b*kSâ0¸²ä¡d"M 1f{©(”ÌÌv äžÀdHåg›0¡ÿ–¾^Óü­åäÑô™®e³i$¸Ý8wY˜¨TګЊæ¯$Ì—7¡Aú5äÛY¯ü¯å)àÔg°6Io;¬nBQ¡”:·ÂCV¢„9¨Ìxf[ŨÂÝ¿)'u4…è|?¦Eå`FÝŽÙ¨-RÒþò+Q¦êÍ£OowòÌ!Žáf‰ï!t“n. *7q(4SPWéè;äJ©Ôn<0Ò¥Zž£ya$F¢ºUûòך.5Ûæ±[‹µt[÷¹€Sú­á´†;‰Z0Œ÷ZdT¡7Ç_¥×K>|ؼ3ã S±g‡ÜëôºÿÌež1 Aïo#c}žŒ•UP\»(¹­îi¶g—bàÀ†oƒeZZ½þxy¤ç?é^Gmô«/O[{tá^ž;e6?scÿÐùTt¿.ËäÏ3]-<ɧܣ%ÁF¶¤¦ññóäHj’(vÈÌOˆt,xLyîòÄ ]\«…Ä ÍЄ~Œ)§Ý”çê:-ËG¤Ûé0bšØÇ ›ë1zÒæaB|kÓ5SæçâÙõ–?ç*ÿ$ü›¥Øy{ÌÞn’ÇXÓã¿¶‚ÆæácQ¨¬ð£(4Þ•Ì|º,¹I”@¯2ÙâDd3«_ùÍùÆæ ·æ£N¦]6ýv÷ýÁÊOfgî4 ð#›!¶ÿœÉÿœj}›óRÊ:ŸÛ´¾_×Dövqü?hO‹ôâùË¿ùÆÉ(æþŒè.”~0d“óÿ7îOð›Ãÿ9]ÿ8å'OÎ_.RŸµ4ˆá£Èf¿¤ý‹âG½Ÿó“ÿóG¥ùÉåjv­èMÏ 0~G1þ“(÷„bÎH~@»U9|¥O}NúÎÈgþiHœOTrÿÎB~Dµ)ùÅå_û[Û¿7Àty¿˜~L¸£ÞŠOÏÉ9Áù½äï£Y³ñ“å3à?%±Ñ }ù¥ù­Éê?˜¾EÕ^Ù¹Aõ­SO”!­j¡ä#¨Äé2ÿ4ü—ˆw§þkþNêMkqù›äû˜.Œð¾³bÁÕ»ëcùl½b~J zoŸ¿'4»ªhþzònŸj]¤ú½¦©ao´ä$¢¬i¹;àü¶ZúOÉm ™ÿ&F¤úÔ>kòqÕœµu1©XÇ!Å©'ªH¨ØÓ®#O柑I%応^qò}þƒèéžpЯ¦?î¸5+Y¯‚ÈrcúÄü™D‡ËÞ^ó:‘óãZ:Om¡é—/#`êìl˜¸IS\»,v„|Ùaç#äù§PšÆ_ùÆï-è6Þo±Å´[ºò[鋦³]Fuk¨õFZòeýÈ4"§3âxOÝ÷ú9Ç÷|÷|u¨Q¼Á§-h¸ú*Ìs0I>nˆâOþ>}CÆ?Ùò§Ê»æ=–çÿÑù}a«ùN*ù÷JÔdH<Ã}2hó´®Dˆ"!­Ä!H.ÒnDìðf┭҆üÞ+Òí–ŸnÞ*‘aˆîóS»:òïæ‡æ†Ñéú½é[Ê‚Ü1ÍFŠUB†RE£®Pqc—8ó-±Ë (!äò­ü²Ës-Ü2Í<$ÒšÕ‰,NÝÉÌžVÎ9œO>«”¯A 4¿´OöcRçLL€ê¸yJÿþ]Ïoµý˜8fG&Baßá àvŽÜ“ذÁÃ.äÜyÛGÉ÷ç—î!#Ù—"D¿šT÷¬>OÔ´„ûr\'¹—§½iòuóì£;tª\¡Ñ;,>M½$ÑÈkÞ©¿ã‰½¨ô’´ù*øôµ#ù‡ëžæ[w¬>K¼5Fä)Ø/_¿‘ûXˆŠ»SI½ ×E4;l‹÷mÿÚ¤y­ÿÜô:#“û4Œ`,è!åòMÙR‰"ÿ”"ñÄÌí¹bšKaò–³m8ÿp— ´"?Ó¦6J€mô?åïæ}×å‡üóåóä‹Í^ó_´¹†ÇTõÚÚ+%¸·’fˆFÞ¥ …¨H‹—L2dŒ®©ËşÉ‰ÞÐú¯çšy‹òsÊÿ”–V:M¿—îMÐó$Ó .®!-+›fU‰B¡yæG»W"4Ü3»%‘Ì 8kâùòoÌÈ?º*?Ø×¿Ï.¿Cül‚·û4úzÓ)r_ÿÙntpsec-1.1.0+dfsg1/docs/pic/orchestra.gif0000644000175000017500000011726113252364117020021 0ustar rlaagerrlaagerGIF89a­Èçÿ- "/$ #&"7 *%$%'D -(6&&)'$, /%0+0E21,1!6-.?+.D+%8#6B;4&<6;R_/[0k-I7 E:P65> 8?'DM<;BF@'G@1=C1$Hc+HV=G>HCF9IH.N8(OE;IZƒ;~<'NrgA;‰;$QK1“;uCsC04W/iILR(BV[N)fJ1VN>LQ?2Tp_O5UdMU!JT5bN6RWgUM]]V;0cŠœNiYKŠT«J`]L¤M5™R3Aft=ft[O´NXdP]a_gaFƒ]PfaŒY3Rdt‹YCNjSch4{a4}_Egi$ƒ`4vbJtd>‡\WQnD`jJyg#Qq6ug4ek?]q)€kygYnkYÕSslQÆY0ÒXÊ\"·`>¼]U¾dÑ_½d+`<ˆv?\}”Žsd„xPŽuR°o+¥oS€y_}yhp~_eƒNˆvhžu=×htƒ1’|!¦w$t„@‰ °rC‚‚3D‘~5}†&›yVr„p¾v~ƒYu‚„¥wmœ„–ƒK•†^Œlž‚tØy%‘ˆnÛ{‰v™†wž”7Û‡) Ã7—™V‘ž<”Ÿ3ª•WžY¯˜6‘ŸK®‘ƒ—¡,¿‘X¤–y —ƒœšs˜š€ÕŽIµœ ‰¡~Õ_Ú”3¢Ÿo‹¡§¨¦"®œtœž™ “¾™wߘ!Ç•‰–¦qª­E³¤†Ü¢.¿¡’¯¦‘¦ª›¯‹ž±„ܦQ®³jÌ´/¨¸“ɲqÞ­iɵ‰Â¶›º¹š¾·¤Ó±¢´º¥ß²{µ¾ŠÚ³â¿#çÀÙ¾]Å¿ŽáÄBËðÄÉ´Éɍܯšã´ÒÈ­ÔΊÓ̺åÍåÐ’ÕÑÃÙÓ¾éÐÀÞÕºå׫âÛÇíà¸ìâÁéâÎìäÉêå×íçÒðêÕòì×ðíÞôíØÿÿÿ!þCreated with The GIMP!ù ÿ,­Èþ$è  ƒ DÈ¡Á…#24¨ðaEŠ :Œ¨1ƆnÄ(rcÈŒ$;vÉq¥ÂŠ -ŽT òäÄ’%¾¼éRfÍ™[µ9èGˆ’âÐ9~ÜyÑ%Oª59šd©²£@mïŠK¶¬Ù³hÓª]˶­Û·pãÊK·®Ý»xóêÝ+WÛÀwýüõL¸°áÈ+^̸±ãÇ#KžL¹²å˘+ûÛ¼Ùð¾Ïû2‹Mš0gΟßy\ºµë×°cËžM{ñfРõéÃÍ;tíß–OßÖ­ÚkàÈ“+_Þº³iæý}–þy÷¾ÛÓ…;‡\»öÐûŠþçN¾¼ùóè ß§[ßiÜÞÓËö¾>õjùøóëW,x»ï~×é–pmÇ}õgàÔåf]uð]ÇYû† jì‰Wᆖ‡`?ºáƒ:Ùpσ2x!ù# t/²·vJ¸`‡-®(#;÷áèã¥6Øw0êƒÏ9¸à’K(™˜’‹93² o¦M(Ø”ŸÝ#£ÚEWÝnBØ›„^‰£Î-øh4êÆ£q@Æ)§d¶O>ù°§.›$’ 'œXa…ZbJ9ôÈußáfN.¦hStïu‰Øší)ªho+š¹_§:bØž†s–jê¥)‚F6¦°!†þ^*è‚fBsÜRÎ=›68núÜsËS,2G9`Z× j,‰¡ŒšŽÉ&³Íêê…eÉNAãê휔 §è=ܘB‡°Êj FqE»FÀúB -ÐAŒ9‰fš)„ô˜"Ä!ÅôÄ)ún*&|:Øoaf7íŠÝ]{­×±çðŽÜ~«±œÔ¢¦<Í Ò ± Ú. dÀ.Ê)Ÿ¼î ój‘ˉƒy¿¶ôâÇU@‘oÁKûe²ú²¹èšÓ±ÄLSã—<&ÐíÆT/Ý4£›}\îLÀ.¼ëNàõº2 /l1Œc¿¼…6ºå£o¿SÌ¢syÜ Äþ‰C×,à ½°¯Ð»é‹W'®øâE*;ô;M]õäó1N7t¨A×À‹²Ø^£€Ø*·€H9Î Âöð’ ·¾ù˜pxà}C ìüí7àí¾€÷ÝpÒH?LäÄ–cKø—F 9œ”G?›åÓ‘ËÉçë²0A [´z Z ’ "¦èêf9ˆÐa 1¦x6P¶w3çGí<§€ìî3/¼îûêÍá†G±úé6ŒsOÿ@Å¥Nùî€Ó‡j¤&½ ÆFbÓMƒ´Á5ÐA@ˆÐ;ÌE‡'ë„ú›nè,¾Ã x"x¥›[p!gy ýò·¿…ÌiþÀ+šàÂä7„Õ(\˪ÖE#άê͘‘Ž 8EÿIËM± _Ã4ÿIçåȤ ¿±=åÐF¢ð$·{†FxA.äv ;Ô¯x´_Þ4ÐÃí«H ^ 'HÇ¡¨br— ØÄ÷è®·¸«$…@ yŒ‘ë‰à˜'£çIn‹ ¤LÓæ¢Ý™RFͰp šIp ,øšÔÑ ;CxÃ[rI>mh¿2Ø ·t±ßm˜Bª˜‹jd<ÂéFÍ$<èa‹kðJˆHT¶žÕÀãem‘؉ 'CIÎàŒR˜Eä]ÁèQè¡ j˜Ãëܸ>÷½€\þø„.î§Ë]â­—} ‹HH`)L˜ªX@nꌇ Å-Òg‡4t›‚)hHEÂ)¯A(2`'{TÎ’FçjŠâŒ: :74, 7ØC<{H|\Ï *.ïw?<ò @;¨˜›ß=­” äM)E1J„`:îqmØ¡Ñ@G°L¡SÐŒL‹Ü&‚ ÷0Mð“&M«zFH ý²`å°C°™âD*,Ç T0ˆòô¯÷›Ê¡ÂAº•FÞ/Å´/=çȆ3&;Y1æÂÍh7ʨda# ðEVõ!\˜`Ö¨¹ )4C›ÄÃþàð–—R©õ¶*ªdš7LQ7åhBð=ì€`÷`!"(]4°Ðl@}+Ä"ö ¼ÛR{òÆfàâƒHÃÚ°„%´á¼è=ïÞÀ!С…ê­1Œá‹nØ" i$– wØCtˆÀìÐ -=L¬Þ™Vó ä4ÛâöÁk­"|ü¥þ%öw·ˆÀS54ƒs€B ÊP<úo€Í›^ÇÔ׬/Žó‘7<"±ØÅ4¦ŽûøÇä²8ªqˆ(Òƒ]X†1–‰.@µd0$–,ŽyœƒBØ H!Âmibœ*%139›Ù0 ljþÖ‚G#ÿÕ P8À@*TA{ØÃ$ü@»Úé1RBí¼ƒ ,âÍèì]lF¯ ãxļã]XúÒ–žÆ.P‘†ràCKPÅ|›,êeøbN^†ª—!w¨ãMØÀ!hØfU™ó•3væ^Wq0™Zl 93 Tx‚îp~âOT¸ †Nt˜èD1Ša‹Cp·È£ÇDBêkr°´'$!òÁ! ¼@ïÌ{ú‹ï”¡ŠPÏwÉ¥^õªS[ŽClà`:ÚÄü!hµ×¾f Ö,Æp Fð°ÚЀœ5ÐhëÙwtþ í2˜¼ I‚t…'TaÅ`Æ)  „[üŒÜ¿%ª¦v'œàº¢`t ]@l\£ ¥~Dö;ße¬‚êUW²1¬ay ‚ZjØXHÌÁ­ r„8„¯V†¸Ñ†¥Ç" [´3Ï{0DÊ€4 bv|{…xà9ÈÂü CC)È&ÎßzÊÄfÊ-à\Ò“žÍ§¬óÀÅ'’ÌoKø ÔJVªEýoU/¹ñÀè‘ëfRª™Æ´ðpvvµ?[±ýß——š©{ÐÚÀ‡0@àì  M@Ø g(éµô€;¶þt@ 9ÌÃð܉4£°úÈ~9\Ùâ¾ÓòŇ9^ô¥ÛßþC'zÑ—^ |â&àd0#r  »à ž€uJ&p\— S[ÂD˜ò4Cõp¾÷{‹“ðÀYë—p‹bTêp:÷À‡€ a€lpYÔð ¡ ïuBùÀ @àÇOy€g6p yu t ­n/–{ð‡÷€ ™… ¸  ¸ð ¸@ô¶!PEp'Yx' $ EÐJ ð:vi’°&`…mP†r° Y'pÆ õp`g0I£P’]â"A$•˜` &#Ü€Cþ* CBÃÔ7¤EÚpv  Ù ‰ ¸ð Ïà ÔЉl Ä BùP 0z$3Ð`P7å` C@*@0,F]EhXî` m  ±à »ð HŒ™vi±`†7V†ž0Œeè†r0rà ª  ]°E°Tçzÿ6_YŦ°`)õ,õÑ;Eˆ¸%RôQCB0t@>ˆ0 ¦/nç&¤ÀB• çЉÏð‚›È‰ƒ^Õð ¦x7àB€hëG°–¥Yid ) Óen+ÕøàÓè fxŒ’` íæ‹èõÚøE0›þ› ip‡Ð„¤@ ¸`  ®· ªç“K¦uß8‡×€,¦P¿RëÁfGcÐÃŽXk  @Ø ð@Cé‡9€ìW0‘$,¹š¨‰/H ”Õ‰¹@}e´4ð¦PFqź‚ö€Q*t @7wfJø Ê@ ¿p ¸0¸ ò€ ê€ Ø ç ô€ jÄÉ••É…BÁ"pð‹Æxªæ ª ¦Y”ªf ¶°7ƒ[€„b÷e µŽR™V»Å`.2Qê ™çpÓ´B¹yx’X7Ãðà dY–e‰–“Õ‰¸‰à ù…70»ÒF èþàí íÐî0u3E %P-FCÕbè‡'ÛõP¦@o#0@І–Ðð¶ @z­× â(H¿‚kf7&ÃÁLáñ‡µ J„‡ŸWÙ‰j"*” €p53BI˜ Þ Ð œf ¡È –(º¡ r¹+l¤Wö íd“FíPúP)p‡“·;…Õžò“F„õbî \¦` õˆP¤(€™•RF”Ëð )ð:J)VÆ3f·6l~• ºEi&Bˆ° Ò°¡ Ú‰æÀ+73I‰·pÍ Ð@ oê¡g ¡Î0¢ùÒ B€™qS‚‡ð ä0i/ê÷€þ* nü³;÷`Ü`¨.f„š‘wç)?T¥\Õ£ø (pÑ`—kŠD–¤IÂV$㔥 *aB)Ú€¬ Èð¦cJ ø’Q6š)’tu:cÊ«³j‰Ü Bþrsl*  ¨/:ÍPž“ÇÚp 9Y>ÓU3å@¥ Íp|zÚ~ìqìg¯¤§jŸ€扇cçHt1Ä ¨*=¶–{°öª° «³Z«xš‹[u"K2«Ù«ušæ 7r7C™B¤Ðàာ Ûcâ øèhÕÑ ‡Ps E‡ÂTì >®Ô±^ITÿS0ø0ƒ ®U· @0_iþ{§l²ée¢‚ïú)žbNƒhP¸aCôÊ¡þJ«rc.Zù1E™ ³ ƒ¼´¡ ð 7­j¡íA  =ư“†µ Kó0wˆ~“šÍða Ç`mÛ° §@ ÕµBˆ@ »™)å0R«X‚9ï6e@ù‚a–0êJ@3›E5k-7"JéªPÛe \ ° ±j¯ Ê ï0£¸˜)ì°¦V¢fÉ ‰à« Z}4ä/}”ô0ÓаZÛ°È*‡0x•Ní‘a î´7m7 hÐG¿¤èÐ è ZÙ —›Bó‘ºQŸÐ©¦ Ð, 8éHdþW„ÏeV¸†+ñú%ù€MÀ ¬¹>ë¯Ü(AÊ+ìñ¬ƒš¡Ë Ïp‰½ú‚¡?ð0[|ƒ ˰X‹¬äð …êhÅ0Y`Ѝ(9àj@052è` Ñ` S…Q[µQ³eaú‚èP]uÐû(€p¶MK¸ lÚ‹¥Ü›7«œdÇ®+4>@¾È°Ã ;¦P CúY ©œè ž ƒlð¡Ôдš¬ºÑ *EC{‡` ÿ{µ ±€ÓZ3ø€ ¬À3R1…_9£¦€ í jÑ{õ€ÁWÁTaDãžö =`šÐk þMà— ÔDÔ•B¥e"#ðà®/¼1 ""»57S0•°Ã;Ì¡=܉”k|@ˆ•ùKH H€¡ð H°Äšè¹H°´š\A¸Æúp oÀc[œ Í òè^t@ ¡KÛ¹ÐY}ó,¶âc~ˆJM`µfÝcäðjí7ù;S tûÖQŠ Ü4@x­êà vþPó° >Éd¡ ŒpÊPSô¹†Mqo…òà’K0m@P·O‰`9;|ú1?Vœ}*Ö«Ïš«_¢ à¸@¾¬`Ébú³“EMØÊ òû¯˜ü¡”‹ßǦ”a p¬>ÖÐ@û[3ÜÀ=^ð*Ÿ  07H4` #â ¬ð ÛÐûÅd]° ËÀE@¯%hså£0 ƒ/m^Óžú´yb4£ª[áT<F1Õ¡Ùô]߃ Â"Q¦ Q?#±#° à‘ §~ܰ¦™E ىЀ æËÄPÂSЕC[0 ‰Õð§¨ä` þvð—™B -àôBA<= à}OÀlp Ø  ã Œ€ Õ°_¾ > =@i€ ß ôp!¤ÁFõ%ÙÀØç-Œ05‡¼QòhJ´ä"EPn1§Jå=V‘DøPU¥ž‡ðlð夽æšÉ´:3òЫπ ‘P Ž`¾JrW¡Lu30V À;À³·;¦ Z@/>opèsÕOTÑ„ •>éµZWwéŒ  €{U%>ù˜ƒÝ\°m€’F.yÉHœÒM"µ@Il´‰ë¥b½GrCŒçÚ ÐåÄŽÔñŒì—œûþ ŠÀc0”Ð ¬æY‰°Ë£\àx¾ÅäP ß^3÷ @/-@Z0Eаð}w°sðˆèpu“Îûµ T Ý ™õÝ Ùšå w…N¤u²Ûh^€ Ó]4à\ޤp‰œB/ñqr!vËN '’åùBlÐß•ðå¿òqŠì˜æ! LpcP ”À ö %RüÅÅç (€~zË9¿óãF ´¨ÎP ]Ð °Ë}TЃ¦`ÖÐd$Í6î °  À Þšµ0UÄ@ ö3¿#äkX^K@f_·´.@ ßj–IÄkpo*þb¼ðP÷ÙÀBB`žô Ú}O¾£ HÝ¡Iû³M\?ð‰ß÷È¡•[*ÑLeºËÕ`ù:ïç É ØÚ Ø0m0ú à}!厇S¹n¡[ÕE! 3«–=ŒVk °|÷ÕBw'vˆèéRß=[@Š,AY¨CðD‚ô·oÈ}þlƬySçΘúdþÌ)óæL}ï8x×OéR¦M>…UêTªU­^ÅšUëÖ¦#›Wï^²¼J`(qdÌ&J¬Èr{—Þ”)_>ÖWN;âÀ]ÆœYÇEæþÖ‡¥gÜ¡…Ý¡fÕ )ìr¦ˆCˆ˜¬PÔ§ŒÑ7|ß™r)S_¾‘·Lž\R„Œ0ÖÄEæÄ)T¨Wê6Ò´N(¼£I¹~¿ÔÔ›áÍŸG¯µúÍ{tpd;÷Œ®lܦ˜BdÊ”–plš‹.º`A¾ð⋚gžã ~€ŽÂ ä&âLA¡Ï^ºÇŽ(ø„œÌFܬ³ ##ă: ¡Å'n¡x¦ÀFœ×"$H–ñÅ—h@±"wôQ‡ŒnäiŒwnÑ&¤}*2Τ”Š ”[,t.¤¡¢›i½êˆÂ®§ëô'¼K/M©tbj'5ß„ój y‰D¸áÓ‡M®¤8ì…àüûmE eÅ@|†£‡‹´$Ö‡.^"Q/ 'p,çØãÇòád $0¢Âxî)ge¢¹W‚d•Z^¸Â :¸d•nèÑ¢D¦hòè4NB.%.šqZ8}†ŠZjž¬Î.yŸ|‚º¨­Çölím¾ûïʉž\pI;m6Ø~ÛÀDp†n¶Ø€ƒ•JÌh£7pH=CáÅgô 4`¡(hœãpú¸þš&±ÄÅÂ!hCÆ*g¹âœzV@Axv0‡{䃪c]Ì’Ðô@¾°^ .¡ º‹‡)(€ˆ[ÀC¸(Pb¿$ÂBÏ “Wº5¡Pï&ÉëN²v=ïYe{W¤NµH%öø8.äµ 8¬íÐ@ã3à…sB#0ƒ$<1GTø óâŸÁDƒ‘4 >ÜeЉYvx9ä 9Uœ" 6¸# À4ð’E&!ØÂ­;à M +â^Ø@º kÄCM!®ñŠ7Ô/9CLµ ã-è|­—:¹V0¹þ¦wPq‹ëÂb2¹wLf²ëO‡¸Â6ÁjòâŒp ð’l Óç#PQ $ÜÑ.ç\Ô^öøÇQC÷¨“GR`lE°„Æ^aÄqnXÀÌ!¹0ÇHèA‡C¸F…£\…/–.ø€…º²Æ;¯Š.<–C”&Ò"¼žQžù<&³yUI1Ã¥EeÖ4™ÌÛzþô‰WŒ#‡à&.rQÍ3>c}烃1]x¢ #PÁ…„ɨ@ÆÿøÀvRÍh‚<§Ç¸\¼AD–(Â4FõŠ[\pCïØÂ 2ÐÁ À#nÉ=ÎæþP…„r”š0†1XɈ.ô€ª€á;ÑQ ‘ÊÁ±°iÃIÞ Œ—g‰X|Ú–ÆdHnÅ´;4õ%0™85›† §]«Ž>ða‹XƒópÆ-ÔA¯N•šþЬÌ@‰7(A’ð @-hAªwdÅUY±Ç½ÈRkiëÄwGìBõ¸nÇ…¨Ž`'8cA‰Á„f0T[ ‚þh!Z šP T°#K.è€<éNj%O3vrX‚È"ù"³†Á€á Ã;ðz! ííkFyCcìê!ñQ<@2h¨â5(q,$T‹q”PÃêð®ÕZšÙ «t&æ–òÌ5u%ó²Ì J…½ráBS$ðÈÅ9êçay¹€;ı‹]0Uƒ9ÀR`šìhÆT‰?o Ä/œË\²„Â,+²H´Ë€#¿è5“?±@ÈÐc ,àà8Sh)Á#n•„ÜÈÌEs4Ð1}ÌCËxsL°„6âù¹E3"–¥@þC'Š8I¦Ó¤¸b>8NŠ®·æín>+šÑârÓN@¢RŒ¤)vÇ8æáŽv¸–àÜÅ| £Ò ?GÆY:W 8Àް‰®æ/¢C.¶\är„ê2Ó†;–œdmÀÃð`=L¤ p€2 †ÎײòȨ`m¸Q( çÜP¢»ê‘îìñ|Ô£ÑXÅkÚ`‰_`£· Rg‚ãk€.ðcP7‹¨hX†¬ã‘hè†èÓRH2xƒ(|ƒAR@þ„!  Brá襗*~ƒ¿?k"2ù,ü34µ“± 4šp'ù³Ì­ê¸?4ä "á?x!µW<9Øp˜†X¨£hµŠ¹Ñ^€› Ä‹sp‰|0 è¼!PhDƒ¹± 0´(z º¡‡[ €_X2p0ÁØË±-,.(‡{P‹t3n D°ƒl°…ÆxŒrpÁ48¾¥À2†¬‹¯#Œ(UX…h¸†bH‚4HØ€ix€Ð<ÐÆ\ȇ~Øw“0´S) Ç;,Š3T“9™Ãó ¬™§8t©ðó’:Œ»;t. ªZ›‚qp­9ºŒ6þÀr’A¼6 „AÐ&GÜ#nøˆ{¸…0-Hkú£j ‚ ‚@ .8„[è!‰'S° †T¤@‚{€!І”@nPu8ís†M°!8/‘ðEm8„Wè« `¡a4†š™(‰RMp>Ù! @€JøÑ<P mèBè¡°Ë¿q2ˆ¬c282 ƒjֈʨ@x€-IsžnuOv¹¬5´²ä ¾«)µLSÆÑž4‰Ï7Õ Éø< Ì‹GQ-8-HĘDþ| rJPªB…D âÈ °&^Ð/0‚Ïဇý ¨0¸TbC¯MŰJ ÀÈ…j Lˆûµ_¸†u‡Xx"»sØ‚-Ø‚4„OøPÀxAtÐØÐèð€$°e@a4‚˜}ƒ˜Mƒ48RÀ†Nƒ3ˆ‚BxC(h€¸€é„J0…Õ8½åa;v„íCµK1Í e’ŽmIWÆiKï¤CzÜŠêà ƒVhXÈDÿq†Dµ!(®}½Æ`‚„ƒá(Ta^تös€ÜgXØ+ø'€ú'Ï™+à„rÈþÍiœ&„jp‡yÀ°;;à‚(@ø„_P†qh8lƒ Ž{†Ѐ7 YB L ÙM˜-S‡è - x²{ ‡ÈË8yÀ^zø"t0„*Ð*ø^5„ظÆAPgˆÚ-{7.“úc[³µDƒš0…OÌjÃΚ۵L°-ͼÍ[êà†0Zp…KVH›e‚ &H@ùx\]QëÀygH(zxÑ‚†í\¨A«dA‹‰|ð”6ˆqh‡y°|À^y8‡™œ‡v(ÁŒ©RPÕŠ ‡a˜€àþ)PL(ÞOHåø€Òè`‡,©‡yxE·Â‡yx…ïõ€*`‘ž=_!„qP‡C ;«_øV)òK[wó,òü³ 8äßþ ‰Q”?u´0ÆŠ©á8`T¸GÀäHƒM6ƒ#8‚ 6LŽ8p?%à^à…DP ȄɅ|Ô£ö :(./ð¹‡ý§4½[°ÎP€¡a·E«"ðUÔávPfq(♆kX –¹‡Vmbš…bP F`„4P‡l@b®õZèâð€RQ-{˜obx†s°ÕB‡4è7~ùþ¸*ˆ*¸4¸†k !h†aÖ7¡h™r`eá¥rô31¦?3 B.WCWE† vIK©íä·U"É8‚KH`L¾ä8€1›ŸJp„H0ƒ8¸`@}€ÐQT iV“eƒiQ4àD€¢T¹òbp‰Ý€F Qô Rxƒ%8@јG¸z8„dYY0˜h‚*!^B ÙãQe(½E˜‚Ø¡í€x${ÐÓu€U†ës–j0¹¤¨‚{¾ŸÍ(t;ÎDäði†E‚ÀDX 5 ד¸=Gr½Š+â7‹¾h—P‹–À¢cÇoÍ¢Žžlþž¸Dx%éKÆäÔ¦„Õ^mG€1%8‚?ýÓ(€Ú~ D¤í˜xå…‡|ÈxÎý§ò†¼‡)°«C(ÛåaRY?„êqª:QíÈP€ ˆÙ*¡b;èk°t vð:R°$ØîÒ‡zÐSo€k¸¦z˜Kè{¾$˜{~‘èë2°Ó3•r¸…Y`†¿…9‚¶J´íÙYôR0EG§°.RÛåécË ý€‡Ö%…f¸5m¸ÌÖã+‚0tQ"ú|0ƒKqwÖ^m7é–?õÚ.A%€¸Ð èm¶”|hþ†‹ü\H¹ù-›ƒ£€)ØPȸ‡CX@üÃêÆ Ns|8„&øRØ‚ Èh4¨Y(þ ø„ãô‘kPa` ð 2ùvëQ墢†vèø€XX€ÿ¦ïkˆES¡‡S`†xˆY¤Þ[hÔêá¬nÑÉî‡ G&°ÁÎRd G ×%µ;lRpHÁ^è¯éÀ{ølð}8Ø·8Ý¢„¿ÖNíLŽ„88;B € Œ¿`¨ ðgXÐKi† "/SȆgxz˜òýPu‘XyX2Ò¬î×*‰˜þµƒ€ `!(^7sR’(%ì†n°‡fch¶^o@£…—h¨p»ž‘‚è)(†Œ[8J_˜¤& ²/ü¥ xÃO'ŠPSR×ðí9õþ••¯É™¿µtcxZµR¸…¤öô@cSìÉõ»-äçñõ!мk$#PÝ‚1ލ‡…*á‚€g‡gÇÈj×lÏ@†ÔÈ"†±ÐiE5w*O!ó˜\w†Ö|w/¿ŒX§…‚-/!8ã¥ÝOèˆ"F$…¡J'0øáHãuHxop|¸QåMè'x‚,˜Ê—Ìßë;ÈþÄG)Ÿ0÷IW3n#Š[ž9Œðvl7‘¿Av[ñË~0ËÎ^¢tżûhm(‡Ý׆fˆE¥Exè}lˆZÕ„ÃeñìGÎ)¢àNLJº$kd€êì¼klÅÓ€VŽªÚ“* ¸Æ€I"‚ Ђ\áAŒwH x  ÷ 4‡ÅXnPTsŒ/’¼_ð^*¹çr9ˆ"EÚ´y“Ä2^èÓÃ3]|ù2fѤUÑÐÝ"…6}ùôÙ›çnÊqÞ ñâ•HD– ûöù»gŠ<}þ¦R]ÊÔ*ÖªJ“eþ—ÀÁ»~bÇ’íGõ,Ú´h—jêöžÛ¸÷Ê!Â¥Ù½¼zá ½¯Y¹rìHq¹E/«>¦j—mìø1äÈbÑJ–¼8)=n‡XQ"¸Áh SFO¡ƒ‘©fÚÊÁ£÷Îõ;m¦†€Àb‘ ^r=ëí››9ztF¿“GÍ·osð†h®¢ÙvúðƒØ†Ì› B,y´k¸ðÓÆOÛ%©Í’Éy#Ä… 2Z4dôé“°4,^ÄØ¥GÙ4PøÔãN;Ž7-e‚wÈ„…æ!=%PÍu”PKéÃΦÐsÖU‰MÅUbŠù“‹#~–YŽ-v#ŠþW¹("‡=ö%›)¦ÜX^}õÅÎSÍУ SD¥T‹8RV•UÚx•X’•–>ðh“ "Zâ E< à a=÷´©O‘?ñ<ˆPÁ 5Hð‚)χ7ð˜r<çPó§oÔ˜£‚Ð7B”SÏ/oŠ-ÊT"Ú…PÄè)1ê Ÿd ªPÂ… .À )ˆ}¯C†&üãK4¾Dà=ØØ7A•4=õ¸K™¨°Ã:Ð@à 9<ñÙR±ƒ åô8’>ͤ° ˆrÎ.ÞVeU[IaÅV]Í8Ù–RÚ»n‹<òèã†@q3h3­í%”þ6ÚÀ5g^ì°ã× †…¨˜‹7Z9ñÄSF–O3>ùSU#6“K.¿°‚K.ìÜBŠ2c˜AI6ðÜ"„Oüº%ž5dðB&~"÷Œ3æäBǘˆ>ÃK(™­‚(0„9B`S͇¨Óæ<ŸÌjIE„ Ý[›P„¥¨Š-¶.Tà€ ±¦áÐ'¶’±Š*³ E]qˆ)¤‚BÌ"ü>ó0È‹³[l1( `BI€DX qË=þ¸µ¸åqÊQ|¹ÜÌ-³C‡)R­ãÀR¥ã¾ðÌ;U½÷®µnŒVÉÌá=¹  J¶¹_ÚLAyœp©‰BDeûOìÚKþ1ô–iù˜Ët1ü>ùä³å=gsŽ7Ò´"Í9úÜ"°fH3=¦Láíí@݃ž+0Ñ;ûæ ¹„²3á2!@æ(`i‘ºÆ'Ò€ 8ÙãoxÃ'16‡ŒMl™ Ì–¸ dHCBh‡O@¢ ]Ȉ*V‰hfê8n!'m"$@©G;˜• 5HÁŽ`vÀ£5=bÞ=èftî'·ÀÁ-à׌&4ƒˆð(GÁn1(:pCZØVâõv8,“q‘Yd—–w­Ž+1ŠŸÉ¦ SLíwÀs ~g.½&/·@Ä“BÔ<5rŒ*ѻҽã|þ*È…6ìÐ ËÒØh†=Ö! Y´¢°Ç;L§lÀ²ðFò?9Ño q@&þw(gÊOÎHD(þç›†É Z ¢T ô˜Î5ˆ±}¢‚–hf%ÞеOØç™P0[Ú\„44aÚ˜ HØ…Óæl€RPl À¹è’)š€IÉé@9äņÀÅEp'8¼¿U *)8…QîÁzxqCík’)Nq Sâ)ˆÀ" Ñ´Ðt#Ø‘´°¤%j\Ñéö¥"æ±_@aÒ´QD;î¥}ÄÓKPÔA !Í¥ ”»[N»E²þ‰ä<šP üÍ(ŒtsRÈcÉè¤+Q‰sp#sÊÆ.!‹s˜£D« "$À dÀ¸ä…-%´Þð>Ђà‚¼OÂxƒ-ò‚@<° ¼›ùÌOP‚E`€€P‹Z¼âtÁ3@ ¤!: µCL(Ú˜@„pÀSÐÅçÈÆú–Éyƒ C@Ä"aþ Z ƹø…Ez@§…‹È˜—1'z,b æÐ‹6è ÑŒft£¤íX*R3štcÝ•Ý»ØØ¼ì¶T(ô8kè¡Þõ²—¬¹#_ÔQEShƒ’<"ÄN*±ÇLO‘þ²S$£h™ ÃRÙ´€¯‘²E”¡zXmpXª^ 4aŠm¬ðœA«¨Å'®™ N€iš¡|(#&X9Ã܃Hª8€Jc"x8#¶BP;Ʊ‰!äv‡ Á˜ð¨woÑ ¼U=!4á.£;¦Š¶1Ô†ò¢ î£#ÍÑÒwEcéwõK»ñ²´yg•Ó-nQÄÖ°—Ôëž6B„QŽ»6dãEé<,¼þ*"½›k)9&ÀfÕøÆqŽå"òØ©="‚› “¯N@‡Òb !> hn䬸7¹`&aW° ÈA”TOÞjÍÈNð&0€ Aba íÍH#žŒÌâ)-H&¡ bׂ Äl‚ ˜‘6Š£ :Ø[~öÌ¡ÌMƒRà€?øÁZÐç 0WÍÕF3xQjhŒåÀ ïK<¤Ã"\T@u€ÝQ]uQÚ×%™‘ßUÅ‹°Î¶]üÀà€°þ©ƒ<ÀÚÚÕ)DÒ1X3ÔzÝB ÌÁ,0Ã)=mņ8OªÅöˆ…ÑÝí=ØÑ ,¡#t’4À2 A.ÌC2¸‚Ò‚+tÒ/Ä`3ÈPœ%¸‚,P=”U߸KèéÃ)¬À·I€£hì \©ϸXìÁÁÀ€LÀ8Ÿ:у:CØB9˜ Ü |†ñ…À'¼jA>`C“aB«lPnX&  à@€B2y™Èib”ÀûÄÝ´"ÈOIÀÜ8øÀœÆS<@ ü@ ´Àθ|3œW‰ÔÝ0ÉÐTȉɬš§%à-Dà£mÔþ™¡ÕEÀ ßíƒW$×ý ­RÆE9‰L)Œ:˜` ¢zåÅ ÂÏ_`ñ•H$-‚Q…ÓA[ÙÎþÑ•C3œ 6èdèÃ! $ ´-Ђ4 C6C=TáZ¡âB\<ØÃ8ˆJƒ9˜TÈÌ=p‚+™žŒ[ P(€ 1˜ì%Â&ÀÍ™ Aø¡˜Œ:|!¨C9.0@<“0ØÁ_dဨS^Ø5•Á%®@Åi!Ül¤‘U‚(šSˆÀ0€Ó bgÊtÜÃÌC;´e;¤jìÍñ@þ€“ð¢ùÌBçÈ”)D@ïh ¸—[$ 60cu1£R:…ÑÍK¦é—»Ôc7vc\ `°Ã8’c9ž`{PÀ6x‰m6ÄãQÌ‚:íÝ×¹ËÞã=È)”t€”¨$dâ<ÀÀTB'!Ã&„ÜB²]aB&d+È‚(.pÆ%,[+xC>0”‰ÐCÍ ‹Ðìp€£@êÅÞIÆa"¸äR‘€è¡`d€ØI3àÃ/ ÑÃ98Á3BÑ,l@ Ý=àÃ+Á{¸ AÅ]Ü}Âh%t ã0@ˆ€4Çþl€-dƒ-Bl‹E>Üñ¸e;B Ø" ¼Áñõ¨†™ÅC6}L‹xZ\æ36ãt9àbJš AɈ”Q6ÚKvŒ¨”r=LA ÜÂfŽcg¢© b Æ—:ŠÁ6 æQ¤Ükòà»H)>’{Å mR‹ô¾ÁÃ>Dæ~A†?D l@oŽÀAím˜„)çrZa’=€4a'-çXe† Á ôŽ[°BHÀ·ç¸¥'¹¡§KnÂKúÀá™°À@d Ð ç'Á–%h6<@,SŒ€ .üB¨À4&èþ<°J«<(îÁ&‚& @¤A%dÜ04@sD@ü(   ÁOÕÓûµÃ'l@ AAú@Aê ‰œœBò=¸Z“¢N_¤C:4CbT3FZ ¬âbrÖÅÈF©”ÕZi•~š66læ—–ckQÜ¡†¨Í…vÒ¨ƒ2ØÂ|ÁÍ>AžNùN)®e qU)‚-Ø ¨CrJär>ª+ÄÄA.0‡9ƒR%Ãúà ¯>ˆ˜Zþ­@Œ-hèÓi¤§ªÂê]žÞP‚«ÆܲÀ«Ô ˜‚ŒCÂ1%¨<Ÿ£ÞÂä–Á :.B«X@ãÞ ¸F.!ø”6pA…ŽÁ''2´DK¨j(D®CØA ä‚öL>ȉñH.PÀý•@ À-–ñÀ4Å,ÌÂ1¤Ãín3Âhã- €^œì6@AÐ’n©k0p‚Á6¦õF/žÔ6j§YæYÑÃ!ø÷š£z¥ Ç’6 ï¨×îÞ‚ú¦ï3Ã}Å=Ȇ”;0ÅOœÎ‹Ä¦2`ÂDÁdøþ–Á)Ô—>ðìsZÉ=ØBþsÂ$LÂáâƒU)0*ç%˜Á%8‚#0-6çR]*PÂ/ B<‰w–^$À$™D@¦æÓ¨@@L´çŒ¼0 tØ;-Ü#rÁ9(Cˆ@耈0*6´ ãÊ@=ƒ!DnpÁªÉƒÌÁA%l/0ä“4.l‚èj@²Ž|ÁÊž.1±ë2žÙ_ ÄÀìËPÂ1l\\èôO9ô©>F¿Æfì,,h,æ"ÄClò0/*Rã¾¼”úéßi k¾Èí\iÀƒjC—²Ã ÎÔÙ‘£ÇžàÙAǤþ€ÂhY3(3Ì‚D‰Â,ÜT€K*K`ܬɹø…6ˆB-?\H<Áà[9!–Ü#ìTÆTÐC\K.ç| ¯ÌXf9¤‰fŽ£ÂŒZ[ëÍ-°õÙyì’pÑBåwÆÜÂ)¨¬D-Bí¦r¨‘K<¨W9,‚ÿµ6Ø‚!àoLôAøA|)ð®ÿJ¶®*UÜ‚ÐÀ(öÜÂ;ß‚àT¡·X$Ä,á†aárF \Â%ŒA" ÁÑšlÛx6Ç„Ç.x¤À<÷6²álר 8À«ä‰+ „š‰(lÁ$VtÀ ÐÀ €z¨ë@¤@«T€ äÆ( àÀ!è…}6Ä €BqæR(¨ê&$¹¦-Øþ\™’ù@<Ð*øÀšÿ€3ýýÀ @€ A ±Ã"`A:ÜÃÉœÌ!äfR0  .3Ã,œYSoøSÇÃ0|8' ¨"_ŽW4r×eKU&Páη¿¸:ĸÇv{¨•)˜–š©é#è‚DIÔÔWÒÑÃQ<¼§€‡»¡ R '_Ä1޾ðh½ìi/7ÍøÂ‚‹sœCø0œ¦`åÜbð%p`‡Cà"³‡7¤‘Œd‘PÂþØÐ„aÈ`"Dà@ `‚"# àG A' ƒSÐ)®a -¥ih Ha4 a¢(*°f€F`Á`,¡ ˜H®‚CàÐQBþŠ‚ €\8Ä!ˆCh#æh˜ˆ&6ñ™fè_úâ·¼ýÀHЂgâ8ÿÀqñNêÆ(Ÿxo {f±6V˜Àü#œb±ƒt¦óM¾Â¨GЋq~|Í=Ê2S)c‡µé»GަH@IK¦£è@l0c·@Ä´±¸^áf¯©ôë*´z“d! iHÃçp†‚‰°x‚Î(æ<汎Å&¶®ð¬+â Gì ‡˜f’”` ä¦88pŠ3‚œ(Ç¡ŽjXbKeHà°‰Mpa§…!þª°ƒ ¸ …`Š2à€„ÍHBøäyp‡|: š`‡&hà¢v)ñ†AÜ¢£- ýd7&æ o)Mi•p„¸t€z˜½f°ÃS6ExȨSnc)ðÌ[*Ô¡^Á¨Hõ#;pG§þ&t¾:¥÷hÕe5B˜]´H¡0È2;Ûù¶Øx B¾L+pŠxˆÂ4ÐA,4SÜ¢®I*Yç×T–A¸‘G.¸€†",á´°2h  -{ÃãXÇ—5+ Y(³³¡ýì™[‡5¨ à @‘LµÖµä˜Æ"À•ÐÖI+ "†þ w¨S'Mn"šR`¢ OÈ‚vPͱ¬ŸOAÀ&!ŒY$tÀGVpP„BÝŽ\0ÅÁä1Žà’€Ë;¸ñRŒ`!0ÙH`ß”öM¥c¶ „ aBPƒæ (Ø éaÄ£àxœk>§8ãƒ+ÇŒSÛž@¡í:V&ÃïPP*]Ç:ß(UħºÇ-Ä'»èDë\à)æÝâni‡ÂÔ"Ðh€S åØFЀ mÄSzG&P)®dÓ1¹Éú¸6p±>WX™ãWÈÇ[¡ÌdtÖ³´@3ÉKÚ4³AÚp³DX@ɵ ¼sHpÆþ眭€¹°ÇnÉàÛ4Ø!\°&^™¥W!Lù&ˆ €v@éOÅ,„`ŠO«ED²ADØ!ÚÉ9„®@\ÁÛm݃œ¡!(©è ûâ XPC':¡†cÌâ…ü§@õšrÂ\;6­¬ýxæ3¢™…(¶ð#XÁ Ó†ãî±»qW†t»Þºí%Õv·Û?Ú€ßeN-yDçZÜ‘=ÊAd;øh8¹hËÁ'Pq *q~Q<°ð8>ÖA ‘o¼ã&íg“ñÙ+{ä)ˆ#l4’ ‘­Ìà‰šÛ6#€tž3þ A%A2¼avHÞ ú`õàøÉšôŒM²fðòà NAnðá²Ò`ÉT€ Ha èÀ~ì€ šn-° ’€Ó@` ¾@Š@ðn×NŠ( „Ào1¶¡Ó¡ /ñf¢èÁš`Uæ`„éZå Æ@Â"àê €¹8¼ƒLÁ5\‡©jÃô¬§zTÏ”Foõ”åAÆ %xZÌZ¶ƒ[n¯‚¶ Æ®÷|Ï÷p,è$3fbO_’Lù&Žùì`’!D®úªûÎìúºO"a ¨¨ŠªŒFþ€Þaed ›ÒoýÄaD‚"’$þvƈav˶¢Küï ¤`ûÀw€ $ê2 à nî šášÁ²@è°AQ€ `‡Bâ“  „`P|  šDà ‚ Žà™èkŠúR¸`Ul€ÙÔ%T@Ïñ°Š]˜áßá2´á2¾}ž0 !La¦ MŒ`ó^@~f ð §H/ ùÄ¢ 7@ì ÑðTro Ô&®¾J>²öä¬àjjìÆÞ‚’øPWdbïÁ[Ãìˆ6ì…"QÉ÷oš¤!E‚¡ãT®¶oþ»ïãd!Ž€Š$!E(!ZÁ‚\@"V@D1HdFfb¡ýLBâLI‚^@Úá,ÿx‚¢ pó€ Ÿ.ê2à ár4$²aš`Îën‡@zÀ‡È†ï†·ˆ!¦@/ŠÒ ¬q2‘ ð¢‰ÆÀ¾ÑS OqÜMO¦´A|é5Ì-"`Êa Z sü‰Ü«Z` Hå  Ì-"éè©Z‡z,òTBìTÂg ¦  Ž‘ ¬Ã®°¡HÁ:áÔºàЊ’ô0<@èÀãB„À§ˆM ¡`£¦þªn'„ùpã!01(G.±`á. Ž2±˜)˜Àn”ÀbhPÁ Ž F ÈTÈG€´i<áä@ÁI¢GIJ2ÀСPÁ>¡*þ/ l‘ ¨xQ%šbêÞrùráºñ퀠€  äÅ9éà¡I¦à‚ HÀRHàO:Ó3á` Æ FT@àÑÀ°BTº…:˜áL…À(à<ƒäH¦Í5}IP“Mƒ(NaRض Xà\€75¡ÃÂP s#â¨_¢JÝ’ó"ýˆ[ŒPPJm‚ *þšÁ¶3S L>s’âr£Æ€6n™ËF4óŽ` *!´ "!àÀQà´æ¦”€T ¦I2`À6` ¦›2"Lñ$@TW ®a·P** hqm± * è{Q)@”.üà ¢ |D®KN!á ø. Þ±Yè`àÀèvTeøT °t `Fšàò”®š€ aD¡ÅTrñŒg^“MÕôsÈ(Rà5Qó3~JR`Jˆ^ þµ7µur8OÇ8ûªQ§‡u µÝªäàáBÆ®šA&à¡®`lì÷Ò*­@¸4@À“’Ry¤ & +nciiu>éó6äáœá(ᆒ ús î-ÁH ‰ eèâ”  Ô/¡æÀAdáT€†A[ãŒè ZC IB"œÄ©)è ÐRËÚõ Ê lñ <À¤æYÀ)œ¢„ ¢@ÞJD 1‹@î’@rUêÌP   v`!pHAîÀ ¼À ´@ Ø@F.ÈÌn§~ðXÅ\âcYª£:Ks>˜AþNMÕ—ØA>Ò4fuÖT=#ØAT¾…äõŽQwClÄH €ŸVÄ2C<@ÚT¦ö°÷D5­` *i"àlç >âQ>ÊáUáà-ùâ{l•¯,„Mnäø|€ òJ9ÄÀ—šUq1ÔˆqbÆÊj,±R6€xw:÷*'B,‰ @tµlR´Ú’uoñE³à fÀ*€7ŸVB w{@t@îz`Oæ© <2·qÅá ¨ ÔAr÷:•ÁòŠX Ÿ¼À À€èà`ò7@j>˜ æ€1L¬yÔþe"cv ¼C> @dbö§ *ÇÈdêžíTÀi…_õXVg9!<'¦8EJöð;ѪÀnàfBUè@%`æãƒé!°®¸ªãØAQy#{ð(…sãp@¤¡…÷àVsL)ð®èÀ˜2t›wôˆ!®òý@F±‰ I$þâo^ ^á>áÜõÞà•^Ô•î‹i `kÏ ¢@îD lM‚ ’`æ@¾Ã$— ˜æ¡ª  NÁ¸Á ˆà ®à¥SãjÁ@|  à’ê5þ gGmÅå-^3¼#>,™x‚(ˆ`å’Ù´áŒï5RY€“Ö"Ø€8«9¥4à;í&À¤  @`()JÁ³\’ç郮JV>èÁ=kÕ_¦™Évƒ¸àl(Áû¤ºª¯Šða÷œwAók† (""°iÐÙµ¦ŠÃ’ÏV€è Ÿ½*ÁK‚ÀÑàR餀 ª@<@:@îÚ¡×8 ÖÈ&i`>¡v€J®Bäà[haòʺá h`y=`Ø\`ó`Ú 8!¯bê*lz‘°¹ÈÄÊ[J·  ´¨Ýé¡þoAg{/ MõN­¢qF©„“ª ä^h²8?Ì ÕM«­J¯ÆT"àÆ|ÏÀˆ ¨ > >ÀB5†‡’4`Œ>DAÇäÃÚÊ¥4Hâ…íúz¨97´€Æ€¤0¡³qð¡°;±å`·‰•@‰ µD+—¨²¹éÀýÞÏX` ÒÕŸ¿$£,á :Àî@V›µÕ8¶£  Z6ÀC’  È@g@ ²à ! < jéðŠ`HxØ<àÐüž ‚Àv@< Ï—K¬À 8áÏ9Æ;8嘗GDAa’0N°àÑýþN f ÈQ.ã×מð!Ì Ac‹üÈ8RÙè{€ÇíÔ%2ŒˆE6ü›¿­ê¼ókÃÓÀ‰`î` T@T`­E!ylÁ¬®àáC\•F`èÁÿ3PR†”ÓpÊYCiü‡kÎ H †œ h±ÉÁ ‚Ü$P‘Ï^@H×K¾d2ª@;@¿à ¢à ÔØ­(: ŒN! ðý - DD`FúÔáD`H4ÞÎ:Š‚Àì°\ t`(\€œ¦azó€-;s¡È¾c>ìxz°ÏTÔ@˜a=Šºr´áLþI#MAc©;ýN… §ì8¦š89,ÝŽÏQ˾aù"ï.‚@$,@¤`ÊúSÛb( ;S@ ´Á>¬àzc7°ävÄwòj!¤@¤j©}vACAý<ºÆÁA–h¼=[ÁÝÆ}±-Að™"ðYg@b Ðɽd! zwÊÚÊ{à­:`yÃ@®® á h ܲ_ð tà)_4) D ôþq_Êà <`ähHáÀ` :€ l@äIêH¾9ÁÞâWÚ>|i¥%f @naVÎtn>4L4ð÷¦¦Ê0à¡þôŒõ<¬”ÌÐÕW–dž êù0ðÀÖ?`>` T)ÀM­·a‚ν[BNÍ:5Q¾}ûü9|Ø/¢Ä‰+J|ˆ£EŠÂ;Ô OÚô™<‰2å½Cf¦MÛÓ¸páfÖ'iÄXà»À= î 5VHÀ¡†Ô©DVp ѵOŸ½6M/]–ôè€vÆ“'U a…É¡*3rdÉ“§Þ¦‚%ÝcJ )ÄFÏvÜmDaEÜeTaDÜ•R_3`ÓžJ,Mƒ”'ÓÐtTM¨ ±ÓŒð€$HK€V=µÂT+ìˆUÜ WŸ¤Ö oÑDEèð„OL‚I1Ê03Ë\OHq—^^z‰G3ôpF]2Hhp1š€ì28íTóÆ’!4qH(¹`£ )‡´04|æ„h¥Q`¸°Hþ:Žn3‹ ILaJ9÷˜T=ˆ¤€ˆÐAo¿…Aŵ¦ÁÎ-\oÏ tìG; $ðÎ…5äPCÕ‰gJè‰÷kˆÂ‹¬Iå¤ D óÍã Ná@YÌ0€8Aˆ¦ÀƒR9ìç„e˜¡®±Û‘6âñD3ÉêƒKÁ…"L$ÚN,,’@‚‹ȈÁmìâ‰'1yò?á÷#dBR  ’\¥qÈ Ÿ‚Æ-¤€ÒÃ$VbóM1sí@Ã]}õ&Í}Ýœ`]±DŒJ‘€2+¨T#Ž4EøÜXAƒr)¤hñÂXÀþA¡†aF°ÀµP¤O<Ž¦ÃŒ XaʹùÐvKª¾M‘‹¨P`Š)ˆ 2[( «Ì¹z@””zç5t]vîú³8䊛l>÷^â=ÚÄý,´?9‘Á\.€ pÀ)ÜB=êñª+»>î…îš’_¤èã±ûÜÃÉsâQ ;üo,>èDð8OÁJ¼±r@‚ X ÕÅ0@C (Ã…È pa‡È a \¢Î5.o)…7<ÁW8ëu3šåáABÈÐFüŒ€Zà°‰OTƒ>xƒ™˜$BâCðB>(þ-'b„`°B †¿E`d+Ú°Ð Q` ͸T9„LxS¨ À /hÁ D|ößüfU‡k­öÁ8Î]‹cäˆU,ö°£Í澨¸ sžkÊh0’,€ X „dDKXVìÕ„d'»ØYäBüðÇJlᇾÜ! 𸔱èÁ…M´‚x3áRj¢<¬`4¢ˆb—h² K¹JÆÈ7x!-€O(àà°°Apø…0ì‹r4ƒ_ˆ‚îÀå)GtÕ 4t×RÄ¥,gža)뮨ð%\‘ŒácÃÅ*Æ@£a€@ Z`€%t»Ø@ð À |f%1ùÞ˜ª!D€#@‚ @h :ÈA.ûR‡<èþ/†m²—òpLcÄŒæÚ@†"bIƒ€H± pIÔüJ€@!4¡q‚^¼1Œ!‘€Ã«9”ƒê„³Ü¹ f0c¢ §{#¾\¢¸´8n$2 `xá9½Aè>wú2q…Gv'z!ŒZ1<¾Jœ„á¡=LâøP‡¸ Ú¤Yš‹@}ä³CúŒ:à € !`A¡®ðä$0úShwDv EîêP‡;èÀ‹!j†˜Úh38¸­0®$ŠZÄT¥ @)\ÝH™Æ °J¬¡|4žqù& 4$¡þ…KN| ‚H/¿ÄC¶©`ß½4¹xò0‹0`õJ€†:ye ƒE€$4Á+jALñ‚5û XC˜`†â×p €XG›Ö½FÐÌ8[:=‹úyÅUŒ«óMDšx®ªzxØÁ¡-<€« õ*rc÷Õwòö´ëãÌC!@ÁE}È£äž5²š¡Cøê‰ Ø¡ ˆYlcð hU2ЂÐë…Ýãžm;[(#áIð7épÿ=²F{J L 3€›ân…#Açq»Dq8U'IU˜b XLWc±„G$à þü6«Yc`„ ä@HB@ :$! 2‰Ì°‡¿Ò`â}¨ø—ðBƒZ›\`I\  M0*FplZsPùw?ö· Gõô €°u[.t6sÀTu7÷?à²pz;—‘öÈ*½!å`·0+¿òÚ¥9õ+¼BjÃò+·€DS 〠ò`Œ° ªÀò°ƒ™c >_ò!8ò1³u6Ì ‡p ^¸SÍ…{m ‰™F è0 ™}}ðþePëaìÅà¶h­Pa–†åfG08Ð$(€S`.[@\ömLâ>€€Bc<À3f 80ŠC °BÀ„Ð707sö…†•9°p{¡d_° =Š  r›Ð@Ÿð4ƒ ¤ ·0íc>) ‹òg‹,cLànЋL0ܘˆô s€ïdŒñp  €°Œq0—?‘psq€zÅ (Viª²Otp ÑÑ… ÖäXE6,÷p =š  ©à âàò€ ´1 @–@ê`÷È^\ à ¹™*pþ‚6EG6ØÐ BÐèÅl¦ 9yëB…1¤½àKY(3ßp Ø ›ôPÚÀô† é‰) ²†å>Kæ ê`ÎÀ Ä@  ‚[@#P fvÐ æÂ æ`Ü ˆ00 “<|,PŸhI°À›ix1@j„FPæ3àU 1£Yð1Å?}À|Ð ˜ ]Р¸ƒ°    ÊàJ¿à¢¸à Ùà ¸ Rs=J v Æ6ÝtÁ÷?à:9—lŒ'HB° x6e“@½8—nÍ:©ŠÀ ’ö8’©lþP°l¯ƒ¶ŽÝ!a85,·¥é Ñ ÖÐúÀQt (°u³I›$5¯5ˆp Ü€ åð(Çp[Ì¢ÐBØ€ ¦p^Ùlù œá.¶Ã–J ·€ßpd7ã?uƒR_Уjv0Sp v0­ ‘°<3â› ² ?€ÍÀxçpÔð ɪ¬Î Z@ð¶ã‚o Ü ç@ê€ H “1àC0!€÷Éct ðê¦3xAú³Ð PÐ|%¦³íŠ~P èÀ]rM ‰Ð æpãð ¿ Õ0ç þÙ  Â0±Û Â`r@‰ÒfV%à%P?°ZÀ Ó!Œ)¥Rœ€`qðk°nðö³I b™Ð\ €ãûDCÀxy™Pôbš¹™Ytjð … ÑÐ U‹§Õp& hÐPåÀuƒšå0,À Í  …d©ê+¥ê´ íyˆpSÄ…ª!ŸºGó ³@ Í€õ  Hæ?7ƒKšØ“ð;Ð ò€æ v0H€ p€\` ‡Ð÷ɰH`ð€¬ÉÊ ¼ð £;º¥› ­(·ø€ç È𥊠È@ Ô ‘°bþ 8ð Õ0•À ›€ :X}ð w;Ð40ÀÀ Í WwðÈW|àR† äQ €=j z9º ›ð «ç°°×€ ¸  åpÂ0ÀUBW¢A:Y< »_«ôÐ*˲e£ cp¼¸<0²L€¤Ib ­5¦Ïöä½Sðµ%alª´”Ç´¼£ƒhמ§ð¦i Ukµ¯ $5¤°^`«ÍDФ ÷@ÍPtjk6Ž2ÄêTò 8 ˆó„g·ê²áø€ OpUÆQ “ ]ÜÅUÆaþ_@ÆVü=ð Þpó ð *Àlà ÆÚÆÍ0##` ÷À ÎpºÊjº¦û ™0´Ú ò@ Èð‹ÌÈ Èð«°vð íð E° wZ ið‰!Pš3ðx¡ùJñœ;  Xà† „ ËÁ,P`Yv@ ê Rc‡`£ÙÀÙð ïkÌÙ`Âð \0¶üEZ¶ø±ÿ»“™‘NZÀ+Kije >€¤ k0²nð;9Á­åZû$ΑάPF›O$ÂÛUãaê` ˜P&£é -ìÂݧëuÕP!eÃ_h À[€ ò€þ‚Í0 j+¥f#Ñ -9·âQ·vëÄqh%~ÐÅT ÒT `¼` Æ]¢:Pf’Ò ÆàÊ Þ ° ã€ð` C å ¡ Eº¢+È£;PÙ ¡ Þü¥ŒLg‘ ›Ð>Œ E ŒP º  µEP{ñðƒe:R0.P`À ¦°|~УРÅ@ °’ rt\°ËÍ` ¾Ldy£Ê £ Ô¡¿Øð   ‹¶¨5¢p - Ó7dÀS:[„0<À‹,ÎIJÎ1 ”’ ༴Ww"¥†´mJ“sTþ\tª º`µýÜóPÃî°î\ИB[À,@êpÈðÀ'H6FçBe³ÐÙp Éi­Ñï2;òAÐ&½zðTÆ4  (ð ¨Pabè ¸ê ZðÒ`žh( ÞÀÐZ0ÜÔ™Ô‰@º|º¡=-A=³¼È‘pz¨Çe@gÀ"ÐdÀ“ð‚qgÀyq¯hÔw -yàRx` œ@¢Y×08Ð7S0B0=*«\”7<ÊÆlÌA³ @Ý·Ù°ØÌì–¤µR’½“C`œì0{8¤+[„f’-—sù¿%ào*€þÚƒ¼´«² ö¢ƒÖ%9 a+¸Å”Ç]) _  ª° Ö  ©` {ÞÏî0ê@Ðõ»3Üakxȭ܇,±ßÐBÝèñÐ:Ô†{|Ý´óÄwëlTx—­ ÍÀ£yÍ[s ê‰1§`¶¤0£ß­ >P ɀ߸Ó*àO;›þ4ÔEÍ b`C ÎÀb0³ö×ÈŽz#€áÅ”ŸÈ•ˆ1IÖyâ —O°L¦¼¢<4à+@úëâMðj}›¨Ü€BУM€¸@ ˆ 5þÄì^Ø:z¾çë'ÜÀ°BϼXQݺx'¡ spNç¤ÌPþåeà ²"ë¿\>*p]„ ÂE¦eºç´¼£¦×u+Úñæ»B,*q :Ðo‚Ð © wÊÏÝàî@Øäbi@è….ð9̦èÊ ÷ÌèÆ¨Ùež%µ:ÓÑléréÚ-…Áº8t™¢àI‘êb߯ $€ !yžÐ0Wc€Pl ÊZº¦;¼Cà ‰ ìÌàðàŠð!ÐDy È p6àáÊ+¢zѼÖÒd¡¼ +î[ üÉïðÄ t0B ~5¡° \¸° A /z¾ `Îp P£ÆV‹ý. 2Äa%þEOpNPšðñ@ ` ,²Ò,-À·ÀEq€s0?ûkÉį#9'ñ™‰é$,Ì&‚àÑÌ`~P …`§ü|µ~RöðÂ=Ü÷Ð ,ðˆÐó= „P m+ôžÔæÚÂ’ô—ųÜ1ÀôòèáÃGO½{úôÁ³“­-W®h]ºd& T®ZElÕ±¬#Ì IT2›PÏžñbÙF$¼ÄˆYóc›?9u:rôGÑ"DDˆÐ€Á† ).ô(Ä'ÏŒOòôÉ£ÇÃŒ>Y5ÊÚ§Q $à@$P --è{ÇMË;\p"E*Ô]6þl6mÂ*.;é Bd .DZX\qÒx‘€1¤œ¾|ú´¡Ð@4‚QñD½ M | ß%pðÎøsèÇ÷Mž]?º¾õÒñd”&_«¬u³f­]5né»–æòpøñåÏ_nJ‹!¦ÔíçÏΡb¶Ig@ TGyšáb½ø¢sº~"”pB +´Ð8 'ÄÐAêè1žÙ)…î¡çgd("ŠÌ8‚„X’ÙH#Ž@z@ ’LB©%yQ¡6dÒâ‡x¸IrâéF`€:þÓ…R £TFñCü˜êŽ*«­ÊÌŠ.ýxb2Xá /†È›\pð‹ºîÒ3¯½6±#nÊ1gÐwÞ1Ǚáã"»"ÈÈM N³ Ì–f´Ñ†›fpù¥k6´hú#‹,(¥€›áè1e (af@Oã°˜ 0HÔJ€Á 9…ޏ ÊÞX5@ƒfê×r\‰ó§ß|–S`W^o]8|ÙÁ(’(ç ü(„‘Ð(¦tС‡Ä…±E[Þé»g"2˜›pûSG¶Sfafylš?Pž\P åÒáæu_ì³P_ý˜f.Ÿf¦•þ…VâVdÜh#T$‘dŽI‹DîâØã–@†ƒ—Lì&nÀIÊt‡P@A’€‰/B¥  &t€€<L"ˆ·B¦‚å Ô…-š0˜’ bv˜Â!¸€'=í %¡@>ñ†4€¢.¤¸ …°€F0ÂŒÀ‚HQj7–Ÿ>L¾‘&Ç(`R±¦1xÍ" päFaì ¬~“}\WÔ¹Õqrõ¹Ð.ðÁ‚[Ü£BØA¾âÊ8D儃CÐC[—ÓõœÈ d€Î W9$°[ØB“Êhñì![à€ þ¸kõ ¤=TÖ‹{ûê6¾w‹*À+(ÒŠ…™a›X‡G"?IÁ$0à †yL%*iIÅàp˜Œ€ô‰ÝPP 3 $RAAp" OðÃvÐ>”âƒZižð„ø¡¥HE1ÐÀ…D¨P…T;ÄÔ²f’½ü)lÀÁ Þ„4b{9ÄÀÆ8á +" C)(i ð`<9Ñ#Šé(F €+ªf5<€)X…17@5œ[Ùj¢s£>–Óœ~ÄqŽŒŽ?²qŠfGMPC1ÎP†b`BÔ‘ÈÞeu[mþË€)¸€K`cEЈ'm¸°ªñ)Ž)Ÿ“J¸Rh•ûÒ‡YÙq çÎPù(‘>žœ ãH†,fä ù½ 0ƒ‘?e.“GHx@î¢7 '4àâÀƒÀ`Ê7‘FÁQܬ*AˆÂ(ˆV¦(è OèSQ Bl@@ p;à3, ÚJ‚ ‚oP¨ˆ- V8â 2À"¬àm”jÕ„ ÂÄæZh¼GHï!‘'Å@áxÐÞ-Æ)nŽkܤЛß(2§ qã}åSu^:å]Z¿1žZˆ`8(GpðaŠEhU«øøþV-8€Íh†XÝõaltr\·ÀF­ÚêV ÅUÅA}ÐtüQW€©Ã¤ÔG9†ð âmQŽð[¬ãa‘_6` ˜!Ȉ¬d[BÙ7$"(묒þ@Àžä$cðÁ 1ŠRœi7ÓC‚P¢™‚OèVæIO]tc*HÔÃ…‡0‰ŽØ0 pa Th¸pˆE0 ?CØ‚®ðè @RÜeU„ÐÑf˜Â¦ÐFfR^PßC JUê1h÷•msß›~4ssdëæ†óÓw°øA¶Ê*)ŠQžnCØÿJŽ6Òp GJ[ôØ‚þ8@€ `Ø…æðÀäA<âíg‡‡È#¶N ~ðjÅ*­ËêŸê£tÀ± Hñ3üÕqp ¦pq¸—ò;‚F0†8,¹c=b‰P„MLÙšœÍÉx ”E"EèB:à 曥Bz:ËNp¶„¥ð…v@@ ÉæWé@‡· á3Ÿ³8¨0.ÑÂ!²– /0N€ä$§ Tîù() €ˆf|Ô¡ÆFF€„÷®t&bx‰ä¼Þ@IUH>Ì^¶Úê9ÂÁõöZ¼_Þå䱆;ìŠC(Cg`„;”ÕrdÀƒÏ@ þ(0…&ÌAñ‹@•âf1‹E$áÌXD‰½ý pÓkÜpª†îec$ ý†‡1(Á Ž0CE@ w$ƒ#ò“„Žì‹D‚%8/ñt-'Ó$þf ¸”ŠâŠðA&ˆ´1k¼˜°Îí«†äø;m1°°(B` ˜‚(@4QAÀA0ÓxC±·Ú¼TrA…¸л{Ø„88‚# ÜSG¸ˆP‚ !ƒˆK„G%È&€‹ƒ5è‘Þ¨„M¾â+D7x‰PDЂ$ÀÔ²­^À4p p@2`F(ƒ„‚„Bð 7£§nøl…&ЀЀ HX‘š˜.+ˆ ðŠAP@$`¢€€ ‚+Ø‚ Ð@€Àݨ´)¨|@ ðŒÏ‚) D¯+€Ñbúñºþ`€p B[ÀBXÇ2hG2èx€‚Ë`$~¹œ¶Ó5V"*mÑ(H‚.ðD]HMà]¨…o¨…WøA ¬xx",B‰|Ú8€É³Âs‘B( ±ùp1-äÂ.0…p—¨+/y€ƒ#P‚•ÄÔ‹„ˆH¨˜J˜ˆ&I „€›È‚c 6x€1¨„%¹2Cœ&ä+ P.¨™3]À„fà#°.ÈÊ h&8ƒ.øÊoÚ p²[  G ®ÕÓ%ý£¨CÚk3`É&6H„ûk(!2‚+0€§èedÆVÑ¿0lø…þ ØøºfdJ3€ÅÒ³µDŠ2F¨…f)д†Z¨…ì‹‚(˜°C²œÄvÐ}„Ë)„4ÂL0M︙h@‡)†A`ȆŒ‚ 7©‰, ÈD Ø›Q—Y…\„9H6ùø6·Iíi1êñùAuðÕcI\XTðp.#ÏŒP;|K0ƒ T8Xƒ5˜‰dâ‘ß+J „,Ó âCÊœ@‚¦´L˜ Ô–Û²m#ˆ60 È€(ð`„3PÐ7Ó…bp¡ `€H2‰‘Šˆ6x `/ÀO8ˆG@…`&9Pþ‚X0çÚ‚Åx´è)‚ eDpL¢pLX r„)f$@Vc Ø€ÎÍÑ MЬ íÝ|)È/½*ÀEz1µsÍ˵y~QS~Ô‡»QÐ>¸_‡v uÀ‡o(NÒ‡!p“ä,Bà:!ØÈèÜ(m‚n»<-ôîìÎ踇[¸…¨««\pxh†=0CR˜V õl…8ø1pY¸‰‰ÕÿüÏõ€!8 ®Ë‚CbúÏH¨2Ÿ@™MPCø„O\Ða…bX„a˜P "  MØAPL[„#£LIþhƒ6ø«JÈN€Esu4éò1`&0ƒ¹h(p” X€@ÍX€‹e\,ßðÅj€hÒ&åÀFÍ´ÁhÍI˜„/ ø€È<Àƒ:ÈØŒ½¸ÎZ3S‹£L£ž|˜…0à>X…lwè†k‡{ø† 8@?…„0… T#Üxø"Né”t9—2˜™…°YI ÉJÅžK%ç*¤lØ8` nÀE$nȆJhžG`ÃŽUJ¨Ô;8Š €^\ l‰P%ðUŸ`¸“8Ÿ`Rø›¹8×JVL¸þài¿h]hÖ*M8ƒcÝŠƒT2`€Ç6ØO¸C3€8@üÄO–²@/˜¹P%h9x„7س)È€Æp‚h€¸×è€}õ€~]¬%Ç(L‚…Xñ:7 Ò‡„w‚§øÑ,ȃ<ÐXëÍX<ø™ƒ5M£×l”M6å5'º… x‚FÐH¨ó˜‡k¨w°‡o ƒõHŽ|X:?Õ£ž½0b!°È9—b‚Y9‚>ÍÎI¥Ô¦µ¡R  ¨¿18 ²[ˆ\˜æg¨„Kp„‡p%ˆƒ‰€UJø\^ìE-ÈK)þ4PF,ʘºå6 ¦6ˆƒ¾EÊãCED†c…¾Â…©<NˆÖˆÖh•8ÖT8ƒê>]@Ç X` Ø…iãi†] =Iù1p]6î‚×õUPH(‚xåh'° ˜˜øø€XÞ`€d‹"-XV)äáè€ ¥èR‹½XìÅØë­ƒ‹Å)¸X„àP#9ú^Ð1¥6ÚF"¯S¸ŠtJ……ut¸»«‡o(R ‡l»–üE„!<‰”€-xuH8`!`Ø›YÐ&Êí4¥†`èÀ‡º0ÁCÀ‘þ¸`8(¤à(/vx‡[(Œ%a…!Gp%ëšü9‰âE1Ða @‚º™¦Ì 1È„L…\‚$3ƒb¿]DP`îs­0Û %.b˜P"¨'ŽÖp>pú‚£Ë,¸€Ý1i‘ã]ˆc“>éÎ CkBb|«˜XAþ€ðe$ Ì=ä€à×&…©dÜ×6ÓÝ{íãÐäMFꋽ)@Í‚$X„r S5RåË1åðµêŽr`eRè)x‚tòмu¸—µe àBè (2°¼¿+/mØ_p¶äìª{À¢€ÎE0—þ9ЀÄNøhà›æ }¨‹{À‡wA64´%1Gb:!PÚIžð$ЂCÈ„’¸‹Làle:‡s€ž¸Õ):È £ôaZ>1 ÁE-5Kh%‚a¸‚ì‚è'ö–+³Ið¸yZ…kP˜Q^@h‚"Ñ‘†`ã]ècÎí\]r.. m €!rRÀ„08ƒ3 ƒ2€êôðŒÝjš&ä„ ÒJ&ꢖ‚£Nj<ð÷ã¤Ò†r@'ÂN×Ô•4Íœ…2˜„. ƒóíƒH§…•3[`„hhtø†`­(è/èPÀ4’þ°òòågu6°0 m°[ð EUAm`†ðXmQlÆÎµ»¬¢‡\@‚# aX¸?q¤„(¦E@ð8˜wˆ¼|6@‚ý Jj ógÈ…€am g€h@Xˆ†Ë ˆ[$G 3ƒ¾Z€‚&Fî'¦(Šž§Q€¡™'_@l H(@Hƒ H2ùÑnK·ôÎ}M7c9h[Ä6Ÿó¹&0€àsu”rxumh†q (#ÐÙ Ò{}‚>žxÊëŃ,ècš–ä.Õƒ2`†ûElÌ‘x€p‘þ5áh†$ÐíÛ™<Ø/w €„eeLF€K(‚3À.‡º¾n˜Á›q|_@„` Èq¢m8Ê‘öÄò!—+ã¸.¸ sH„8‚Ž`…Mà‚l¸u5ø€‡sÈSà¶OÅDÈ:0Sˆ‰PPDy –”\H:…qpmצ†•èdH̲óü@\uêóÔúó@ô'–ˆZ` pZtlH `.…â‚·5¼Ñ;•T%½Šy€Æ¦«|MBЧC`8õTÏŒ| ‡BI7} ! DˆuY7…vµÏ@þ^mIÎÝ{íR_v‘䍨:Ó2àe2e`7ÂêÐ!0®è€.§‡í†2È‚R¸Agç;÷=‚j¨e¨‹ 0,ä¯É¯Ù‚ëZˆ6BÈ€¤‚‡pNGU—mH‚ (†ï*‡“Yú@ø„ß“.ب\‰oE‚\@I»ц\ÐTxˆl”D„l ƒDèçDð= ØOÑ^àN8í»À= ÊœGÊ÷‰5ð‚§\z±D-€H•ŠÑ0Dj $²âEM¥–JU«Hˆ©º©K£áM@ƒ¥àˆÈ :ˆ2iy‘!.Y¬¨AÄH&bÃþŒ´€3ˆÐ ‡6ÙA à n÷îåKšOS}ô¦DРéÑ{·"(Pp BSÍR{ ÁŒ,f¥ä˜1£Û3rH1›ãƒø8S +ÒÀ1¡´éC"¦˜s;SBA"‰T8aÉjÈ¡Cð‚!0ˆ‘aˆ#K˜!Œ¸á"ÎbÐX“¥Œ¢#У¤¢K Yà 4tc)¾Ô’¤Eµ MÒÆoà°0XÒAŒØFÔ¦ÙFl‘I™-Øåšµ9…/l1 7å°ó‚Ú•“žìP¥6Bü‰;GÑCG¡ä¡h\y¨uÇ£Yä!¹7|C¢wÜÐD½L•O_‹yºO¨†Z¬v|ÝþCÇ-µ¨":èØ3Ž-¤ˆà‡/µb¢É2²c’†c$²ñ   'ijB ¾¶‚ hÁ&1˜¼½°àMH“šþ¦Ô“AÐantÛëèº{ô-BUHA¨hÀÁ‘ >¬gÌÀ.xÈCð…ÌQîw‚$y‡´Àes;ðÜžøÂ)Nî^ð0Lq‹fh#•·Iб r¸ƒvò† là‡hÌ£ñèÝ2ÚÁš% Bòè#D`(XBzà ´‡ E3šC|ß“ºÙ | [9À"£œm0# €ß9Ï™fˆB‹"p ¦ô;Üç¾£l˜"„# AÇpÚ˜‚f8˜#v%\‚ ?P9„ƒ A PA&Q‘µì$Á$þ¶ ÀzÆÊ2Á¢›ñÐ 9“©TD§åÈ"º˜BòŦÉ„ˆ,² e€{\P ìÐE'\Á Ïã„Èø¥@€a7F`Á€0%5¡i‡˜Â 81 ` uœà\v<%t¤?´<ÝbuÚiÆ€¨D¡%R  T%ï@*`ît¹Üîbˆ2@ú0é>  Î;û(åY€Ö„.z™Ka”a†èL=쑎/øbÕ¸F1$CˆOÉì&VÒ½k=s- žmTà=„Ýt€  VbžP`F;·Á£ߨî9Eþ†$èo³HÂ)ð™ÏÃð3½æéŽ_šqˆD°A 5Ø!v˜Â úm`+.ñ%8â< (UL‘=ÐR@øhFµ R” $Æ d$bÈ„"`ÊCœÉ”1hÁM!’£œ ¤@"‹•æ€.¤âHº(ƦȤUÃ!8à “D´€Aâ—ØZ6«ZÁ Ì´ ZðU@lmp°C"´À‰´ª 2ز ¸fÚ¹"¸’‚6ÆoX@¥ê€‡²¼¹…(„ª°$ "ðÀ $yƒì! eD^'Û˜O.&”÷rÊ-Bh@:´0„.| þk|Á„(Æ5æáéot!¶±xE7¾Û0@-˜€Z0…å¹`+±–nž¹-B/Xo{Û!,bíjç°Ûù æ¡Ê^v1f! êÚ I!|u^ôª7Ûáé>²Ñ„4Áp€…,Žpˆl(ãc°`ø›°”Z¤~=„@@`!‚‡ g¬¤– D%H0‚D¬Á þp$:,Ó?¬!6"QÜ L,B%á“ÀJ„ÀM,I Pû$bH(CÇßJpèÐ/Œ„ªDðB™¬ê+­LnÁ´.pË3ëZ›·e `Pà¬Ò„×þ€êNBˆ >|Å”¥ÌƒhÜ¢nP¹¡5"v3àÚ xÀw¾À)®#ÙOÎT§CϨöd !Ù‹€ ª ‰TÂM …0^ÑŽhø¢=`„* ÆÀ)ˆŽ \‡ª7  Sõ¿Ç#ƒ C7»€ ÌA¢…¸ªkìb`âI€ß²™-.ä0§Ÿ°öµÓ£mmwGv@ šp„JÈ"fE2Ä‘Œº¢¿ mÅx ¡<èÁ bh£6·ðÊkÊ¡6à ØD%>Q ` cÈY‡ë X("ŽD"¼ a˜œ‰‚.LB”þ¼À6AS¤Á0Â*ø$0B”\Œ\º‘0@‚Q̬€‘™Ù(YÙ0Ùè×à€¨@ ´€ (–m‹±lhÃRܽ0_løÃôC$’›å€æP4 ¸Ždé²ÜB6=¡>dáèL§fõÜÅ<‹m8—ô@¤€pA”Dh „€ €ÀDÀä]åÝB9¨6ü¡0ù¡<4C °Dç• €ÐA@29€ àÏ`¨Þü¸žu)lE€ÀìaBíi×vuC-„\Ãdõžïý? 6p4 ÂþB9DÑ-¸‚,ÈÂ%˜\BÂü DDRhÊlÜ àöØ.à<À()B$PcíˆÐP´À€$XàŽ\,) $tÁ$AœAPàß9M*@6TÂhà Fµ“ÁÖÍq]Á?z Ú@4Á¨À´À#Kà ‹=Ó ÂçèÔÃ+tÀ_½YpNW”½%_|‡bZÜ)†Ü)@d–fmŠîEm <8‹)pˆÀ`Á" DÇQâ@ Ð6ÅbÃS" Ê=hÃ3Á0 þ¼ A«m"Á  ¤ü`ÂÀO1P×r(#4"4C@¥@h@'Â'Ö‚5èB404Cvʵ­bziÖ-=ØÃ/\‚+è×%8æ%0P€•ÀüÀãÜÑÃ;h1˜ÃR4Nòų Bö`” @Ú$˜ ¨I©&€–TÞ˜Ã#„ã(Ô&A‚.  B€AtA g”$øB*Ã%\Ã<@uêã†ôãVu –PÕ $ Bâ£6B Ö È€ÌØ'C3€&_<ÅläÃ=¸#0Ž]dA#ÝÁh@v¬Nþ"š_˜—hrád±ƒˆÊ¨˜ÊGÎ=<%\!÷Ëm„ŸV<¨;t=ÈCÕuÛæq³´À¥FÒœ|Ã(ÖÞŒ.ÛØ©Cj˳å‚@´e˜Â=fï &azG3à@3؃4´‚+8B/jÐ%hð@€ô°€°>K3,ؘB}n „ê‡)¨¡è)4#Þýæx€ÁˆÙ€(¢éƒrJb$²†a±:kcXíÛVEUè;\ÇÜJëQ ŽD@„+&|lˆ<"h$ëh‡¯ ¶zR>Í«zuG?$7 ¨ À€õÁÀdRi p•7pƒèЉ}Þä^Ä-Æ^-¥vh$Öb,m ¼Â+œÁ䎤&Ȁȴ@|Aàæ(°¬ËÆ̦‚/Á5XÍlÀäãÁÅàŒà—¬Àˆà—4“÷n˜A„@l"Ó(È@ Bd¼;4 ³ŽicÜÃ!@G .*½ÆÂþvÊ'«kb((s%«²ðÛ&èK’)Öέ~£6hD@n—Ð¥LA)1%7Űƫw<.½þ…>|1ŒÉ|Xé0AõÅ€ô¸§{ŠMê¦î?Œ=ȃS “l¬$Ü:+;¸Wv&4Bèˆ.‚ ç à€oAÊêÂ7Ô$¨¬@¤BÈ1)ŒCl@Ðá´ , dÀÏ mφ xç À$ÈÁA”6¶À úŽÏûÃøÑNÂ݃ڋ?TO÷¥kœjŠ_¤-`FÙ~Réð`Ð$xœ—–.™"²;ë€6Ãp Xˆ…\äþTVÅÂ6+$£bÿœ0+"F9œ(6|ŸY-Oú€ÄÀ €®Ø°°ò¼j+›J>àÃ<\Ã'¼Aè@x@4ƒ¯àÃj|Ã5 ì~¤7ÁRùÐ@D4 A gôæ(èÀ 2&tA ×)œ(„@¶Á#¨Ê.Lƒ;€¸1w¾ÀvnÕo‚'àó4Àƒ9Ì #Ç\=òb€æ5 Aï$4þJzÀ!(Ã+0BDAÁ¬#U¢¨)ÃÇM,“÷¼@ðÁ(<ˆ”QíѤ‚5 ƒ:àB4@ÈÁ#<>“9Hí÷z äL6–$‚'€ÃcïÂ.t°B;¼B ÁÀVèê¦E(„Šah“ŽF6%ÇdiÊe‰t>y6­‚2„ÒÛkg$²øï Í4(3ÆyÝ4N{7äB²Oƒ\i ÐÕý0vˆé=<‹˜2+>\C,LC8„ÃcB¥DJ‚hõ¤)H¡¦à6‚W(÷-Ø jÑB ¸€ Äšˆì(D[( D.tC=|þÔáôõ4<68*pAnX v Tjö† <9øcôXB;\CbìR|Šh{‚ná'{UV©k$ÃjÝ9(©thŠfEËvŒ;ë!Cr ›ðwìöÿŒ ;`Ç^hØ”D.‡mˆ)NmÀ%mpËQøC|îÃ=¨Ã'ì8H·t«IN‚-$B†Öïö©RB]F¦ZN¢»Ú$qÐ4À9ðˆ.à7:ÔôÎs È€?v`?(´ƒƒÉÌý£vž ´Á.@x„ƒÃ.luðÅR°±º6lPV†fMot¼Žøhð&<¤âþF_:·¸ŒÇø!Ç]©ãxŽPÝ=(mäÃÝ <*A vÜ À‰‚LÁ!ä‚6, >ø°2ÂPùt[¹æ¸Ù$œÂ>0mßÆÝ²N™Èˆ!”<`Ëùœã÷7 C<Ä6 ‚Œq0€cÿ¹8ÄB ÂT•UÑÄólj¡‹Áø€%LC`7º€46“8§º‹öMN2k»r%O²b`2©ßK%ŸöE§zëúÛv4±¦-¨ë6¬Çºf™ú³ª4)x7m Ëò,O5U¡)( (€Â/¼‚%´(Hy•?û"ÍÜB—‡ç(€TáuGê‚þ ´å°Ç#(Û$`½¹×ƒ~£ƒ–k¨¦ˆ} 0À#Lƒ84¤ÁHöÄ€\Ù€IP•—\Ę%pÁ ¼%@w`OÃ't9¦—-±+1»ø†³¶wØ ÄWüe)Æg¼§Wthcz+þVŒ¸¯aF‡)Vô(qbH‘;’Ęñ¡È‡ì8€7¦Ç‰O¢¤Y¢F kîô‰RçGš%!Â4*²_Ò‘I™6uújÔ¨GCzÔ ÏÎ!;t¸šºeª‰)z¯µñôHÎA8P:$ ¥GmB$Asë>xïè±ÓöÕ«×[ƒ›•«/ßÏ…‰£WÎk3S“ÑAthò­fÚ´•+gŽß{úîÑ»µõV¹{öÉnÚ®³rÌŒ±ÝÕ4Or$…¤Jº[cé5£ƒ#¬Â S6ÿxÑêF™3-FœþIµ£ÅÑÎSR\ÈÒ%vê «Þìi=gÄŸ=Ó7êp¹PŽ×»—Y{S¤Rù÷÷/Þ¤Œ›‚S´ívc¨R!åm$£CSÊÑ › ±)'¯è‡vÍ=Å|"ñžÒèY1EJF}ðÑG ÚØåµ×bÛ…Ç]¦gU„\fqª!Ä^„ž—9‡:ò©$•¸»ï(‰Zñ$óîü—$ÉÊ.£‹n=›6‚î¹ä³ ?2­3$§öóÏ<•¢ê=R\0¨ÅZÌ'±Id(±Ä΃'èü1qQ%eÁ…ðÍ.L0ëG ‰üÔeV±ä .þrQ2M÷˜#J:ŒÎ›o$3/ºŽ>Ž\](;[ïk¬%1±cŽ<ïprõÍH¥c“Øî¼œ•Ø9-Š“Nõœ–Z¦~­ÏØ5Uõ©=¼Óˆ=e“u1ÛI}bìR}äÉ…‹ ’’xW™’³ ‡C´Y,[`?2Éß™ØäI¾XÍ”X‘6ʵ¼xuà:›Í=3Ÿ{U½‚s½²Ø8u 6ZñªyÚk^¯[‹òyt[ *JjÑœd½ÈÜnÍÔIx´1ÅŽ&P@ßBX .¹ÅœC!ŠVƒN=šf9ÊYcývc†åû®¥ˆ%Y%f;.ñßX¥üW¦²§”8ä;I~[ªþ¶Á%;YcÓ«›»¹£nµÄ¾ý¾ðŒîѳÂËPoc6¦©oe´½/O†–<°aÝzð¼æìñ ÄJp™>9#ƒW©ó¶E†›u¨Øf™[©«kÕËÙ·–NX™ý®N÷À}?‘Ü]ššV¼‰…Í/•Ò(XËü˜&ÍÅsµyy¯ùo­ž¸9d-¯Jõðël|kÇDSÍáqM»X‚+_¸ì”Ÿàßí7W{Fy²ýgy'Jöá“JÊC38EDz¿ÒçÀ§±µ‰;ÙA¡…°Ô‰o|å#ŸÉ6¶œõ|«M|—öü—7©!ë~)LaÌD·,ïUílÛƒžóªw±ùþe®kºàժĹå1ïJÍÃà¢AÖO\6³æ¸ÄÿuPvŽRbÔš•K…ÉŸ50ï'{dJÛþ¾f@¦U„% ØÅX¹Ž0X Ëéè#Âík¡ÛÛ™Ü×=ëL¤hذ¤¸7äéO[ˆüo比+@+X¸°>êü±z—”ݹ±UP€Ôá®D­Ö‘Nw$YøÌ£*¬}‰va{¡ò†B±òï&ðëÒäìV?½uÑ”`äT’F‘щh‹£ò,YÊ95Ñ‘ D"(%ØCƒ¡›âSåÈÚ6ºëåLUò×,ã§¼õù‚ŽÌÕ ƒ™Èý¹þ©QŒÜ$¶ŠIIô Ž#t&Q¬ùÌ2™N\šëHIDuf3$ü8%·Y²O6ðMdtÚtœW»X^Ó;Ë'ÇàD¿àA-x„,ÕäD:Ž´Ê‚~¬'¶1;ntpsec-1.1.0+dfsg1/docs/pic/boom3.gif0000644000175000017500000002544213252364117017045 0ustar rlaagerrlaagerGIF89aÅ´÷ÿÿÿÿÿÌÿÿ™ÿÿfÿÿ3ÿÿÿÌÿÿÌÌÿÌ™ÿÌfÿÌ3ÿÌÿ™ÿÿ™Ìÿ™™ÿ™fÿ™3ÿ™ÿfÿÿfÌÿf™ÿffÿf3ÿfÿ3ÿÿ3Ìÿ3™ÿ3fÿ33ÿ3ÿÿÿÌÿ™ÿfÿ3ÿÌÿÿÌÿÌÌÿ™ÌÿfÌÿ3ÌÿÌÌÿÌÌÌÌÌ™ÌÌfÌÌ3ÌÌÌ™ÿÌ™ÌÌ™™Ì™fÌ™3Ì™ÌfÿÌfÌÌf™ÌffÌf3ÌfÌ3ÿÌ3ÌÌ3™Ì3fÌ33Ì3ÌÿÌÌÌ™ÌfÌ3Ì™ÿÿ™ÿÌ™ÿ™™ÿf™ÿ3™ÿ™Ìÿ™Ì̙̙™Ìf™Ì3™Ì™™ÿ™™Ì™™™™™f™™3™™™fÿ™fÌ™f™™ff™f3™f™3ÿ™3Ì™3™™3f™33™3™ÿ™Ì™™™f™3™fÿÿfÿÌfÿ™fÿffÿ3fÿfÌÿfÌÌfÌ™fÌffÌ3fÌf™ÿf™Ìf™™f™ff™3f™ffÿffÌff™fffff3fff3ÿf3Ìf3™f3ff33f3fÿfÌf™fff3f3ÿÿ3ÿÌ3ÿ™3ÿf3ÿ33ÿ3Ìÿ3ÌÌ3Ì™3Ìf3Ì33Ì3™ÿ3™Ì3™™3™f3™33™3fÿ3fÌ3f™3ff3f33f33ÿ33Ì33™33f333333ÿ3Ì3™3f333ÿÿÿÌÿ™ÿfÿ3ÿÌÿÌÌÌ™ÌfÌ3Ì™ÿ™Ì™™™f™3™fÿfÌf™fff3f3ÿ3Ì3™3f333ÿÌ™f3ÿÿÿ!ùØ,Å´ÿ± H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœQš8sŠ<Àªg @ƒZ”a%™£¬ ]Ê”!+h@FH%ƒèfÓ«X±õŒ*•ŸYà eeŠ«Ô¯bÓâœQŠU©#8ŒV­]–2rè@M Ü#Œ ª{·pÉh=“"’·ƒ¹d÷4¥ç€áË­ôìr„1Ü€ã"’œ¸ æÓ5w⮩¯G™=› VV Þ 1PÏÖŸƒ ï ïã ¯%å|xܸ̣÷DNýàâr=;v,µcÏÑ9àÿ®N~ ÜÞaowν+sV{Ê—_ÁêÇrg1B»¹Ô pÍ!ò‘GŸdÐ$XÜS=-ÈJ@ás˜bUÈØ +­pˆX‡¤5¨Æ[@tÁ Y¡†Kù&+/šbJ V%¶`+ÅEÓŠŽ‰áÈ b!ˆ)>©’Ê+©L“Š*€øF‹/uq… «¨âŒ•°¨RŠ €¬¢=E˜`.¸Lˆ‚¨¢f*¨¸ÂŒ4m¢‚ 5ª%L¬¬‹*rº"§œK®"¨ D¶bèŽÅ}É!Y~¹J)«üÑ'*ÒTZ©›•¢òÇ“w¦)}Zz©¨R*+pþAdˆ«$6Š4ÌÈÿ™i¬–¢*ê©prÚéI]¤bë­¤– ç›§¢«œ~”ŠÊ4™š**3¿Vª¤o¢¢ë®%±Â¬¥ÎÂI©Ÿ²†jl±Â*j´©¤r+¥Ûf*M*«\íJ¨ÒL´™"Égº€öIì¥ÐBÛ¬·¦ÒŠ‘¨¤k3Í$œ 5^;ïHV<çÃé¦+¨Æ¥hœ1¿Ã¾Ò ’—ü ¸•¼æ+ªà†âÄ+ýâÌ=µ‚‚`ñ (¸à³ |€bG|ØÁÇÑE#mt(VìAä…0ÃT…oŒ&øT‡8"H;»Às €Â ¡ÜaôÐv¤ÍÇI÷‘¶Ñ2šuPN'ˆ ‚ø¬@a+ÿ À  ð‚D¯4n-´&ˆÎ+,ÌÍÔ”xMÀØ^[þÂß§¨Í¶áj¯}(}|ntä’ eÊ š­5R°ž‚G‡‚ôèA»Í8§°‚8Ü+¦®S+. @ë…M4(¦ðnÇîIÝÞ¡Ûa P@-X €…¢i"t”žÑ6a½@¨Àƒ¦ ” $fB˜° o)Ø› ÿ¦´MÍsŒKÞã<€ȈÖ€F ¡‡&õp&=ñZ С½ïh øœÙŽv‡1щ/ºÙÍ….hïŠô‚Qå@ñmaü_ “ˆ3&¦*€ ÀŠkðŽ!±‚5ð8½™Ïhlóbî¤w4£±Â À €‚xò“ŸÊŒƒÈ“X¡‚` À†¼}®’kóœÑ’6 VTA¥.C9Rš„¬`'Wé,4}v0ÜLÁ<Óña{h wIM,Ò—$D¼¶€ÀuíEd‘Qbr–…Þan³C·³,èrš™sÁ!±™‘@La˜(\þ €ÿ7p&†©äbˆö¶( Ö(¤;ñ§Â¸€ í%=?‚7Ÿõ =[ú­]AgÄ,˜6(<.—ŸtA \ .ÊâšI*_6üOˆËŸ0S¶Êeá}p[ ì#›yR…h fS“¸ rªä™à|Áâu“ª>i(6ñÂSDaK9û¤ q…¦z„j=AÄtʳ¶².£L°ML4¬á#VôÙAÐ@Äà²jÅWyfyÛè–¿;bV$~\ˆ6öÎSMÊUÉÝ:üfûÎwÙgö§¥çOËY¥uGò”ê}Ã*êðˆùú唨§@þ-!r„ä_¾ó’± ‡ë½ù¸ó„¹öߨÍ\¡œ¦/ÿœg0ƒ¼ÙgÑ­= \Àif›¿Óà÷Y?ýéZ AdÐô­\œ®'6ê^Qepq]uD²*Duã8]Æ:Öä D¢\‡%QxÂ\@~Ggеl Ðlèç3ÐF_®—~F~,¥-€ùÖ\hUþåOW'’¡(cB| "ƒ¦m[§m¢ÐÐx“‘3ß—oäG~8à †FU·ö"wtM=A@xóQ¦E9£ü&{øÆz x{®—…Èa°]&B|@ “Q“§q23¦`( H@Ð|0€ö%2‚aÒ¥7ËÖaô5àd #c{3¤DBX$ýkxÓ šÿ†…í~ëÇaöõsìwdIt³w'w±5ydЬPK2Ã!?(b2hI![lXhX0_(Yp•8QhWa6—õv8AzD2…,ð‡ÌælÒtžt p?7~H¦gÀä ¥0¤q–X7r5©&’ñÅÁû—3sFá×l³tú–»·JESÅ}¢Å ä·oH&g~øi*Ål¡¶~>jë({ŸV‚€_>Âg!|²1@¢ˆ¨ˆ#ò="ƒþåŠ ‚9ÃlѶRõmö……m/b[YU9­  }<jê8pþxŒÌf_œæiëÇlüÿÖaçÈ~ø¦Tr8ø#@¥ (ŠuÑ #×PKˆ±¢±mã¤iœÈa·—Ùið×^°M]VLôÈz#&{3À‰AçÏöw+•œÆi$h‹ü! gg#¬q>²€f#ßu;RHÂ!dÐr ¦àñw{†Xè7É–ÌÆH»uY©wb‰‚tça9YsW9–ÍÈ~ì(’+u‰møå À9#¬°†x’Xaòƒ Ø!NIrÑyˆ€Ëf“{ˆ~üœü†‰þ¸‘eLjáD<‡“q†{v7–X_˜(‚ ˜“*Õ>ó™ pŠ[á¾ÁŠ0ŠÿB‘6Ø Ø¦xБAe‚7(Øzó•{·mó5‰–o—%åYÀ@ia´'’ϵ–hI{ÂÉŒùfi‚Ò¸i-Pƒ@¢š+–08Ò $ç™mÌá|02wüÖ~}˜ë—i_€‚pÖÅ:,äŸYqX‚@_ÐU9_û†…ËšYX_¶˜Œ—ˆdÙÙŒÒù#w—P¶±F´©€§x ‘סmÈ›—ˆ~H&_H&] ™ïšˆ7X`$‡e žF~»',Л1éF–¥p‚~÷z=Š‚ºhƒA‹áƒ Rƒýg(cÒ!×€ƒ‘':¢‘žVwì×£%ÿ š?÷¢óN_¹,€Vx…š6pZ–Éoò¢Ò‰…T‰…õ™x1h"ˆ¢¤¬Ù†I‘×V¨#€g Bê¨p¥Í؛෫mÉa(ˆ~‚à—”œ¾q€=ácéÐm°r)]TyŽW“  s’â "}Ƨ òÆ÷#<[­à`¡@2‹þ‰YŠd鈥X–‰…-0m•šç„,¡!d¡‚ôUd YŠ™5g–&˜{"hw `M ¨­`BÀ}ê!3XŠWÖeP¡‚¨¨ Ú‘¡æ¨]È‘*%œˆ2 À$÷*B‚_ˆ~í'g*’&ÿ™>·¢Ɖ¦º°!‚¤’Ñg{™Š_Òª§"Úæƒ b? úo,esÍ†Ž²—«rÖ–õ…‚~ãi €0Êš[Oó—¹~µ‡µG†~õ‰{vZ–¤I¶˜(gwuˆxÝjy‹#‡ª²§¤ˆo;KœÈH¤Íæ–W*¯ÀZ~̆‡D«€€ÐW™%±H×)]+•oõõzñ9Y²>£dI£u–qáIŠËA¬ ¥E{ P"Ðð#„—+¤—»‡twWŸvǹ´g»V¹­Õ« $ó'¼õF'_ys‹â7œ·ˆ{¶«RÁªŒµ·Œpfº>Ò @à^€d"â93_ÿ2±Ä7 CYe#™«ÌF…3€˜ Øa]XŸJ¦¢ ˜¢€¨ÃE‘]¥å8·úö"‚вAsÑÈ~WpZѨµËè?ê™6ù´%˜¦K·gýW°—Üfu=[Ñ´‰áœ&’ìxZTZµø¯rvµeI“¦rg¿—•*Øaú6²·l7Ó Ì.B ËæoÙ™“D §î»ÀžË‰k‰­©(ÌÊñ—嫀݈ŠNб³:Wp–vŠ“*µÂȹËh‚v·Âô j ð"wãÍÅs& ~ [_œ¶~¹šÆ]K.»œwwo +¤F“cI¹¶{§WÓ@0hØÿáƒ4ƒƒ²ie­à£?Ÿ[‹+EÂЦaiëwP;š9‰3ýÔi«d¼Ê¹T«Äbˆòc.²wÖTQ€…ô;~ ì˜ZÛ¶Y‚× y=1˜·u¯&"†R3Ü»º@y˜àµ˜ˆ¤Š ¶mTË¥> g.ÜŸÖ’.éf(jÔœ¥FV‰èhZ(ñµÑ‡˜·‡¾·WsèØ›4 t–¨…i܆Ž7· Â{iвIžX“ƒ™î¹¢ w¬ç©3 ‚=G¿'«“ºZ¢ŠŽ º¨pV{ÉÐ4Ëi?Š…^€!Òlß¼RîlsóY‚O‹wÏ6–˃Oá™kˆx­z&°ÿi|ù¶±%9_r]'3 :š ìi mmz~¦šÉ5·lççǼÀüÖÊ%A5ÅaFËGÖ–íG‰”‹°ùlÑÆauf€s›QQä › ÅŠx Qg0¢¬Wà.ÐLj8{T©•L}Í jd‘ø3::~iû×vZ‹˜g8Œ¡ö™Ï%Á•û×UÛ8«‰Ï6‰× N‹¬`€‘‘Òy*3×¶gPÖlög­7{°wµØ§\ÉÔ¸¨;ZŸYkËœ¨Ã^€dTÓ}¦À›%Ê¢s-_Ô™õu‹ßüÍfìi0(&ïqŠQ!‰…5©®G& ˆ×ÿ*p˜Éœ9Z²þJÎýèʼn™W j)LšXùžÇHÉ)X”=¼Ž}ZÜO™¼ßégôj–еJ¦R6‚#Ó ÐRnF9|E+3k „=¡Ð¦Z™3û¦¬>}dÐÜ£³g{ œÛg»Ð†w#i“^0_O¡W0¡«/"ù™6‰¥•¢}Õöu€:{ dÐâq±¬Ð;Rƒ¨j ØyÐ,Ñ*:‰)xÀëŸrç¼ï—~µ8ào¶£ÁI¿sÇÂ¡Æ ˆ9qD¦ð}ÓlsìøÅ€ÜܶG‚ŸÆÒ}ÖÁn!çšx ò{×=|3Û#ÚŽ—oVèÌÜ®tLÔ´gæ-{fÿ›©…˜Á‰d{WÏ,q¬7ñIq3;´á§|Û»‡gK¯D­!<«ìxÞ;ƒ:’(â¤©Š¨˜\…#ÌS9×l¬®=Z‚Í«£õ™ÉäƒñRZ™½o40+±n4NâFÚ‡o4Y•5y‰¤zà NÅa ˆð_ÉW¨Ö}q§¾#ŒWHEÛ;D›¨êoý”æñ ˆ‡ãíx¥B*»Aä—©„¥"݆'K32úŠ ë«aÔÛ~:ëz˜}o ‹ÖþÚVê’rÌ:‚(ª.ÊÁŠßº!_ú·t(ÈÆk©ÜíÔò9•ãgº¤F§•µH¦ÆAá\_ t5羜ÿ>ÂËëN ‰e 9Ŧݧ€Zàií#½"Ñ …{.´p7c¢i”oò{¶¯RœÆto4m³ø‡Lñå¨ïŽdùÆæ Ûle öufb­ÚC™#§Ì ¾‚hÑ@$©¨‚Qx¹bM~Áz¥u;'mLK¿L^µ‡;Üw©”Lµ/ÒØ«b±š!áÇÛÿŒþl®ogéå^á|9!Ç]v.kd#H‰ Bï¾o Ý©Ñ7éÓ ÖF³ˆ3“ª ºØ‹ q÷ÍrWß aç0²û ¡¢Ð"Y°÷öÅá-„‚9Ão‚P ƒiݯ¸ÅǽWæËÿÊ—°ÅÒQÈÆÃZ¹9üÀÑÅa»g׈Kðö…7Ä Vo,ŸÆLêãöèW“T \ИÑÂŒ^X± ´U3™âÀ4‡Ö,ŒÖ Z4‡¬Z9ĸÐTÈ…#Y!ä¥ÅI‘%"Âò€F‚›.f(˜Àœ> ÒhÑÂ…NZ(´‚ÍéS¨O º²bET¬Y¡ZAÉcI­aŠL #A vF<ªVAQ¢4²ŠF¦¢¬µ˜Aƒ‚ âÚÈU²ÂíðâV aúÅ‚SFArMõÅâEé[Dlá×EO¿FÓôâEP °XÁŽ]Û)ÿ×®!U6µ};¤)•#u™Qy NFg¤&4»9˜äâmQË0ä- 2ÚÉ‘‘–„)2à@j%¬·(U,Sg(%Ú÷ÁN-”Vë'Q×9‰·Þ¬ê-¶±HB)±‘h«Í!Sie¤h@2k¡êø:­´·üš!7ŒÙŽƒ+Òêé´–±«Á@JéŠɃ 1A°X(+¹)·Áä#È ØÒ-®h!¢¾X°"t2¶çx$k!Ã"Á$ØQãVf™òÆ ghk§ÒÚï9S€!ˆÌ :(¿ü¦Jà¾0ZL19Ò늆*ûðŠ+\,iª×0$´ºþŽ£ÿ!>¢„ž”ôA“Æ[¬ÂIJm……Vé"8 CA Ó"³4¤®037DDà`*,à ÊãZ˜ª«ò*òVúú‹ºb=B6°[óØ9ä«¥Ö›tÚ°x¯«’d²«ÃȨà•,œÅ»ˆR@§ ¹–#•€¨`ºÐë-ËTK`N°ø0Y Ç[Ï‹“jdL¯Ëzd‹€ïuéH,âÕP†ÆR—j+Ž*WÅ"›¥«bãt!/~qàÊp*ê\Ÿfü*S € c Ȥ/[» ',ºw†Íö`XÕ2Å ö/ä š@î"gý$ÊtÁUX¸ÿ¢Ò+,ÖÚ©(òòNŽX°MÊ<"о›ŽL@J^›­"/f ä ‚ ú>@YYT¯n%*¾‰X1…¯®¶=•ö;È›\+{¡&·–ü)Üt»ìȵ ÉÅ‘ž3‹Ì´|¢‹‰¯ÅLî@º@Ýpd}Eh½´° ['>ßr=ÃW2<3ë r` ƒMͧö »¡R2Ÿ|ò®»<x=z˱… "ˆ#€x žŠ{À TÅS¤hP7?\£ÛY£êS¯?õ¦¡ Bq„f†:à ‚¬m¸ÍýDp¬Áò ÜȤ,_" 6d`…=tL6Œ) ޝ–©.B‚@"Jÿ‘Ñ´€~™M€p>¹•è?øK˜òçD´@].¸ÚRôr°{ V~QÊD؃…)E']Àˆ)xD¬p2d cQÈ Ñ fBxý ¥(ÅùVŸüGq£ Í×Í,?¡ÎÞãDZ":!J¼`$pP"@2ˆNгä!QO!J*2ž)ï) ÙCE€€ˆóEep¹ òs53žVW« Þb¸ýä}‚é )覷µ±â >éŒIøXØ 1"XèÂI¸0H\b#!CËtð¼l fu ‰"·âÙ½fE±•óÒkƪ؉’L’é?»Ø‰VÓ±>IMi ‹(Á°ð8ÎÂÂXJR¥°ìa,n ­m »®˜Â z€Í<ûÚŸ ¢Ò½Êh}zÍ›žf?Ç)Ù »FÚ&¤!Rhmp#ˆ/´¸Å jc+ÓÄæÔ½ï]mu_KÖ=hwªø+ÍAâ²(~ý´PaKQè€P3ð{eSX‰Ê÷=cK!QçR˜¬ÿyËZ$‡-h<ë­§’,ìÁ%ÅB+HZÅr!¢>ßtëK“U…û Ò ‹¼AcÀZÙƒ^¾@ƒ#{¡‚ÿRRLØG™µ²•òM»º½ ®F1B¶½JŠ«Ø’•Œ,¿ âɧ­²†åÛæLî4W›ÊZ‡ÆY:dÈQáJ j@?× 5`‰_Z±æçÂYÑ‹NÝÙâ¢1E |RŠk!æ¡¡7Å4@°2x²—Ñ£®ò3 Ò–¶àÄ& ðÉèõ“ô©òI¬XVfv³LJq‚uqIýk¯Iɱd•ƒ”äìçj³–ˬƒAsº%Äžùf`gû¦õé‹ï ¾€¯>p:Rµ´‰ZÅ4ȵϜˆ^‹ZÛŠha»@Ù­Dúi«OæÄV/ÆŠÿ)s`vŸYÉ-1ô? *Ï[ÊV‰øM›2%…$E9éOiðÃßÐHË6¥hE`ZSšÓ¬p6 ñp>GòµÂöÌ¡¢‡.p °'oœ;)=‚oûÙ ÍøcªâªZYÁ 8 $dAJ¦öÐäB1˜ó¹ç‰&k“j^Ø&Íé‚Oc{kce‡ ÊOøCšˆÐ¶ ê‚Æ!Ä’ä#s:g5h÷¯PЬ#0baÍ\NqÎ\rÝš ÉlÁ #Œ`ø©SVæIYÑ:F©¤R6ƒ@T! j_H \Ón·Û/YP•_±ŠÀg7‡Èdb„ºO±VnÀ†®ÿ!Ú ¾ AÐ4¢Ë{®X,§fކ b£ .r‚h…’i™à¨O½(oM‘X‘öÐ.)7³IhD{=Pæ>Â+0€tó:MÓ ½TC)Žq¬S‹?aÜo’?Y¸Ü 9Z3½†´AË™/Ȱº†ˆ7y[’p†?ưÕ² VÀ<ˆ» <y±±Ÿú>Êa S8V°‹8¾‰Š¨Ša !ª(¶×xhû³?\:¦èÓ FÔ%t,&!ÄLª°?‘´ªk1#Æ‚à‘#è ‡˜–ä‹)š"Øl\Tä´n”‹íKÅÎŒœÉšØ{«é)­2GtŒ)½ +Aƒ-8„i˜†É»‚Õ‚=„Z…TH…XCAÈò‚P#¸ÊG$h„©£>„“ÃC Y7N£‘èŒ-ŒH+¨'ÈaÄ…¨‚ŸZ'ø¾°RUHTPi†i°†UàrÌ$V MTøÿá…?…U …?à‚ ø}R3AÊDkÖ¨ÆA9¾cKü‚R¸‚ôJ–‘(Ä+¿¨ ³â”›’MU@…ip…W°ÍÛTÁ¤’%9A/jOi@…üôT@@Î-(„BP—kš¶,V`À\”ëA×è‚æðÎxâ”%)…i"?®¨‚<³ñ{¨Û”jX…?hi„Íφˆ„/¿þT…UU„h…±0(VxT0¨\BÆñ hÈ»$C9$c²/ˈì=V(…">òTH…U„õ¼*4#ø”†TÈÒ•Ñä£B܈QWPSøÐâÔÏT˜†(eÿTÐ3A˜A«ZˆAËNô‚8ô‹@,ÓìÂzd’U€RB àÜÑ@x¼¶1#jøÐÄìÒÅ %E‰VX…iHÜÌÒFÕÒÛDW@SH…‘Xºê ¹ Ã¸!%CÅíÓôÚµ$-ASr»lˆUXS¨ÕU 9VÍ=DOó¡R.uTø|qŠÿÇ“ÏMÛõ×BðÙ¤†j\UØU?(„¸L>ÅŒA ¬u½Û¾ÌΙá4ýЉ$ R )ÁPó³‚òCý ³ˆQ „ÄlTù]$VxGUÓ|u†ød<Ø«„e«‚=Ž”ÕRƒÊU&ÎThV †à\…œ‰Óz ve…ž¨ÿb©]iaS8‰7žSMqÇÅ«¨ÕR0QƒZOƒú?y5ài¨‚†­³)§…€bGu…Û4¨º™›d@ÀäeWÛŒÒü´¨8„IÝW@X’hc”DКàcŒâ ¿ïÜ:™‚ø¢cù)'‘ †Ni^SAÀQV …‚(mˆ@Ȳ>`">QU  ¸BºQ•©pŠ?X^ùL`ƒÒRÌû[ݕҹ¡›AÓÆÔuU•DµWÕ‘"æŽx†å‰× >3i^†˜QŒè‹A0ÊJ¥jPä™mšE½/Þ ÕV Mà(•ZW|<9ô ¿ðD<‹Y(XÑÿᘠV,ç¬0AcgT…fp…g`?þÉÈæ- O+@µÍ†µ[iØ †ÖÆ0½9>‡xÛþdäMváH„AßY¿ ’åáCŒ†ŠCFdÄ|…ix† N‘h,á å=ÒS+ øÄQK¥èÁÌ@À n ¬ÕQhSÒ†¦ ¿k0.ê½jÆ)ƒ Ý ­k0ÙNÓÓRU@í’¦³í\\”N,,`n‹›†X ±Éœði†–«‚k«¦~œ‹Ç°† I”.†èG„’»:U›ïk µBùAŸTí^± †¶š:»£« g§¨+`(§ò‚ÿ"“PËÞšÙ`h… úÜÔƒch‡p 2ÄŠY+Â5TdC‡xš§Jë†ñÐG”X’Æ@Òà.®ðQÀáHdä3( ¨yÒ‹Aë ¸T/Pl¸ªpl¿ŒÆ8ÕuYˆ_\ ÙiaÊñf)½X–⊴¶•XˆC€‰a®‘@؃‰‘ŒÔÁv\ÄêÑo°úŒÅë%Ħ¨&´«¨@.,ð#*2Jd†8‰¥žBš,ßœe*¤ýv ëÓiÝÀVq¸æ”5ŒáÝž;,Õ Ã* 0«8Ô( òë=–¿[&Q•r-ßr.ïr/ÿr0s1s2/s¬;ntpsec-1.1.0+dfsg1/docs/pic/hornraba.gif0000644000175000017500000002112613252364117017615 0ustar rlaagerrlaagerGIF89aŒ´ÕÿßßÛ¹¹¹©©©^_ýýΘ˜˜ ž350 mëìëÐЪûéwxwøfggg¡õõ†† FFE±±‰‰cVVU l ˬª ÕÕ ÎÎÎ33 ††(¾ Q„s{V )kkN; 5 wwfDBm22œP!M’M'-)kegÿÿÿŒŒŒÇÆÇ÷÷÷ŒŒ”ZcZbZc‘”€½µÆ‡ =¿ÁÖÖÖ„JcÆB{ïœ)ŒµŒ”Δÿÿÿ!ÿ ADOBE:IR1.0Þí!ù?,Œ´ÿÀŸpH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬Öù2€Û°xLåär”ÜLn»ß¿ÒÀÑj¹Öð¼Þj@ä\u-3L92{‰z/€1MilŠ—bh-9/L8%˜D¨U 9¢Ž-«ºU-/¡J­90-»ÍQ~ɹÆu1ÎÜL3 °ÃKœ-9ÝìG½ }4K£9- ëíþB3D‹g Ç’ ¾p`k`€€þ¨Õ¨@€Œ J Èaa‹^Û ƒ!O@2z1+ —¦A0(¨£€CÈn(À€1ÃÀÄÿî<$"ÀÅr +t2€ã¦39òUp0³NQ?E Ð14;ë|jêtײ¢)à ‚•B@øü´ÑÀ UÁ-‹ª•€[>źp›ƒì> ØÐ@føîr1‡8uá ÚÑ¿0 P1p¶~’QáÈL2^€ˆ àós@¼Èë@RêUîA¨ ‡íѳ±qíêÀŒèúüÖüO'„‹·ýð©`ñ™ 5”03´¥é—‚§CM#U%òid¬‚€.u8ÐBʵ£4ÂŒ iÒˆW€ &Cñ—žµ€ƒ î!6² ¦ÿÀnúíç`zvœ€†ø4ƒ†pÁ}à‚ÅŒ˜%DóRú8CQ ¸0Ó'²Ù¨ˆ& ÝÑ2w”ÐB£½Ð–»‰c’—4—Ì îéäàÀÁ“”È0ðè—dt‹ª¼pÁ—æðáüà }ÂGp’1¦ˆCI Ñ•ÃÅ‹ ¨Q…†q×bìö_ 0 à óEfQ9 €P[¶¥qJ¦YpASÉ€”Щ~̸J€°VAü´?°Ba†\ o]â‘g4r°ëAÛˆ ÚΠJ²Oø±,/,aPåPÿÂßä@G¹RXÄ΂Pç«.QØà3\€  Áp[a ¡‘ƒRŒ¸Ab/”à^šèë„!¹@ABŒé@2-à‚gºB“ÔÙ(ö·9bqZQ0.pû°Žò2 CÀä„rl-œY˜ ¼¡ˆ3 ñ-¼ÐeÁÉ„..Á  .æl€]™™&Å Ãý«/3¾å…; ”0 y5@>+5 L³Ñ 1w•×(Fú±!—Œ{DoêÀà *ñ2´êðúÕ`kÅv k¸<Îã+<=¢“ù±"F£{Ag¤uô ÿÁ1X‹ØL#Z>-8Чha«•êQÂÖA2ˆ·„‘°”P¤õ1àå$0èÇ8S螆÷Œ¸ºÏ˰Nè}¤ù–¾•Š™%ˆcZZãCLþ% ÀEŠ4 iˆiÓ¹€®˜ƒ¨o|ä6B&´UËÀDLÖ$t/8ÀÍbð‰a07è€vÐà 3#`y7ÃÙÚ‘@6à•®|%4DYÖ x€šèžsƒ.’’U\ÇÌ‘Ž‚ÀÌ„ uðÇŽ`À¯d6©! 2ƒA¡.%°´ ¤ Àmü@šÅ=Ðå#;0dÅÍt&²~clÎòš²<¤¿¹<Ü`œ¸€Ô€ˆ’àŒ¦Eh_#â)ƒN èÀ~.ÏQnS“æò ÐÔLjÀ,»i@2ÿ›€60N~tèÈ0 ˆÀq#ºÜÀE50P#ÀgG?`ƒ€à(5‚È T:€A¶Ô€¨–7#uÎqœÐI~„xAb‚9Èå:ÀÉ £6Ø>ðØ•™MUAWÀ.XŠ6ÆS®¹‘\•–ˆ$Á¼)H  70¦. ²–]'0]Fö"àlFéŠW5ÜZGþ`ƒ \ܬ¨éO#\yØFrŠ­ê+ECÏŠÊSe} |JLà4%çB€‚ 0qPji?ð<ɉD0£:¡ì­¢³½mmºÒZ6`–iÕÿ #ëYãrö­î5(Bu)l@̬nx€‚ö*P Ô ‚ØY €* `)åŽ1"j¹RÂÖ¶Š-o67"SçB¶³ó *9šKÑÒ“èSE™OÐÕ(°‹©K]»2U@‡‰È 38¤as`á«æ ³Õ"³Ú@÷L,*Q zÜC–Àò[5@ÎÖxÆÍ¼cîØLºÒ8À3è›BˆÄ H–ú± dÅ9¹m©7{Èó6â(çÊ•£qSLh¢®U½¹t`#Ú¨`u•xÅI ãŒà„λϱ—­•Ô‘ÈÓöæ,ùmj$'™–~Vx'JC÷¶'QŒB´µTÀwT]Ç7é»^| H€‘=Boü ÙdmñJÛþ;¥~’!s{ÿ?àã'¬y…jK¾P×´½ >­«†¤çì-½GÜr¼PÎZhÅ´kiØÅR•ê^·z‘N‚àd';ØðñoÄÌÖ5ðF`¬¡dî†×PW!ƒ<‡ºªgìJ©Þ÷ÃÂr ÈÀ,ŸÆ“ýí8€ÒU@W2;³â!pO€5„ô€î„BÍœI˜ XÑb©Ø@f•±§Æ­aGê@*±¬O½ÒãŽø¤7"Øev±îãýõ $‚Iv4cÀ`Qs$Î+KÝR‘Ñ·-õ¯õ«&€xydxI‡xðná7JãcÛgy]ÖRùpNöVÿRÓl3 %uW*%KæPÙtMîÁrWUHû‡}怼g~ÿ÷$xÕTÁFWp~åw+ têe@@á#•ð?˜8ßâ4 p›Ç†¤HBöMÛ4HîxFFjè7`øq­‡tp—{#z€ùô+9‡€'Àeò&!?È]XÄ(Ò!``"ð°‡{X®/`‚‚ÔJjK¯…³Ô—xµ5 à‰ p zþ€Nf{@‰²wxÐ-…v8ðl2ÛA|ØŠÀ±EHKØg‡x^×Ta]Q…Œcÿ à4 ‰‡G‰ÛwB‘˜7´çLÿwžÈm3H×ao}„2Ñ7®èŠXa50Ù¤R¹Xgø—u½x‰‰©·†XŒ¼wŒÉÈø¥g‰X(Š«g~8´‘3EœÆ&PÝ芽`rN€KfHפR‡„@jÉ‹éØZX‰Z€h@Ϙz`©wqÿ7‰öÕ#Ðzj¸}Tñ!/5œÁ(â p| P^QS…u&xçuH@^)°ridXì(‰‘ø‘)‰œå‘Z؆k5ZH‰ PŒ213ƒñ¨(7‰“Pÿp6or/PŸ=@HÿGIjIi@‘XÁØŽŸ¸’ w'‰2…‘¸ n_'‰­7ŒŸBEÑ–.P–8©P>%Áç¹Ñ?à'x‘Z“ˆ} ˜†ƒ•ø $ipÕ†§…æW‰œ¨F5#ë!#”Ù °“ `€°™{€—u‘a˜z@%°•çqr:·*øuÈ8bMd@ç—z9ðŒˆ§žØR_ÁŠfY` @GÕ+”©x[8й•³@«{A8wQp{a¨tZ¹î‘1àÅYf¹‡/°ÿ à" q %“”Í9‰Z8€Ÿ'à}ÀD‰ùŽ-%Š[ÙRøuĘ)ziä¿É‡ P`™ÐîñT˜^Ì锓¨+0‰9pƒÛwkQù‰‡Uš-U‰çgyðv#ð1ú@5E37ݸ“aÕR>²u‘áé”h8Ï8ÈV«€ ‰ézÞ©¤²×2Ð@^Ô®¸Ái ŸŠÐ9 I›€©†@šp’ªGl¥Ù‘ðh…Ú§¢‘¨!ê "‡¹‡p£|(—¹½€Qž³ ~aø$†7Œ–§¦“˜¨Où•¦Ø‘˜g›h@êÿ4à ”@`èI£*ª5wUQt POW¥Ÿ+†‡×‘|™p’˜" \’“X¦Ñz¶É¦ÒªtÈ4 ‡0¡hÀ‡ZíY[jð*ŠPX©Øë("À,p•ˆz& Š:ŒmÕŠÉ‚YY‰•8‰‘ú KîÚŠ   ¡ÁŠVæp® pRŠ0¤è—†°ç^YDõ^ÅYà'[T5˜«Y¤gH‰·iƒW5®0£{¸]Q„vrXê"±z´Ðð—q§†‡·ZÉ¡­w'€…p;5‹šÃ+p Ƥ¯,€ših¨ pUÿž˜,À³4*êTC=±j¢4@¦–È} ëª@›ª†'à\–—&°&€+௩®jÊx›zÂé‡á¶]q™4p¹Ë£y s­çpLj…ÓÙ‘:Ç‘²—ô$­‡š¸Ñú¨-5¬+ªP!ÄsË òÓLÔ¹Þɉ [Œ¾‡}˜˜´‰t @z¨ û´w»³ê®>vUØ˜Ñ ËŠ•w‹‘ÑZ „ZyHI»° Qpx秺[øu1¦ºJçºÁY q;Íà` »ˆ9®L›-u’໚ÆYh½{Œljx‘xjË®–¹ÿgsB21YÅp|Šp ¾ÓÙŽœèŽîñ{‘8ÀNz·v¬‹×¿ÎyX'¹5Š“`£{UÂ&úð~À²©G•Zh¤­ª¤p'­ OùV9×½æ×¿iy"ð éê®#ë1BDÄ uJÄ“¨˜”¨½Üžü€°‘œÈD8'І·ªÁ;qgfID¬¢2½ „Ra?À9 ¹p€Å2ˉªúÅJ;®03°¦œDGPÆ¢jè¤_ǦK€“%¿î‰(j0Ab¿HÀÇdð €¾Oi¯*¾ÇH$yy]&ö©çq¸©¾ä¼òû¸{œÿ«"0¿äb9¸]vlþX‰ ªœ¼6+É2 ¤Èx `\h¸‚Çx—‹H: —7ièz– `@"ÁÖR/¯³Ç± aÑ yUm€€Å—÷ÃY©aš¼Ÿ¸¬©ÁCªŸÚ©ÆAp“—9ι“/p§¼LV–R?÷A Ž„k¢Î±*$Ñ!8@'¹xQê»ìÏ‘ˆ0¤kÿ‡ÍØ· ‹x%à® ¡%ÀËÌžúº@ ¬!~@§ˆE?st8|_FÀñØzDÊN‰7à‰æGÏXŒj<ããq °“ j ZÿR#¡P缋zÒ!€ci°C3QtÒ`­¡‹—p¿'ÕÂY¬Ç‰&Ú‹H¬õ¬Ë”¯h"PœDªb)ƒ ¡³]/ó9u;thÁ@ˆÁZ`„y×O’Õ´ŠÒ‘x°•Ý*‰¼G›F–p™sÓœ"`R!¹ÔH7Î@ü€C9R$@óZ_ªÝRÄøŽ!i‰žØ‰‘(Y¸†¨l€}U[Y7œ²2 Â*Åd+ ½Ðâc @©d%_5?Ø Ãéë½ ûŽ Ë4˜¤Cü¹ ˾‘ Å˜z@œ1<ª"p ;yXöA“ë<ÿ/v7!X“M¦ºwû~ù¹Ÿ€•7†šBú‰y{­½7¨Úû(Ðd>†69&¹ d-žà`ÃŒâÀ.E0~5 4!M’Né{Òz²'­p'¥k(âF Ý@ê…(.ÓZ[£Òë@P\”`C2²"Cs;–±$qŠdÒ"t|ý‰ây’Ø€k8ð¡?,ÕJG­šZ{“ €î -)vB̆‡E0)á̈± ‰>åeCü´«Ë‘Lº}HWšsn›€¸µzŒEº¨Âˆàœ®å\‰²F>µà0:Èj'/ÂåÜÒ" á™ÿÈç~ÀVà KÚÙÕý)çNë°®û­ùq7¨À@Šx Ð4p’^«.A&#Áݵ!€E Ámk‹c sœõ´d›tת!¶i åÛ­HN›N‹îÚ‰«Ã¨}"@!y9ç/ð o!sUÓ7 ="ÓÜEÏo@C‹m\—Ê.ú”Èþ‰©»Ã™†ˆ›7øŒ)ú«¼Ny\`ÎÉwƒ—Û7Ã$Á£ŠPÀVº}è‹Ê¨Á¸É‘ÚóÀž¸‡z`4E5ƒÒ$'T»Ä 4ˆ±5pT ™ÿ_°æ‘(SâÉõ>ßéÿ&0ö,àÂê‹]_.ëÎåÃ!BEî—U@æ`3ö2`ð‰E»p+luÏ"0ïÉ«¸Šy~¤žz~|²‡IL´ë¥öpÖnæƒ 8/æo&'d@9Ùsƒ ¬ÁlÜ€™iÀVÈÕëh,@¡éýЇ÷84Œlï\T[t «á³Û º —›ãÀD¿øB&6c/Â#,-ð ¿ rðXœeAuZƲDŽ\²9ŸWåA5P8àSÔ0‡0 &=F!t@¸ÐË„….œ­ XA¸œwy—e$2Ž€à‚ \^ˆëâ]d0ÈåÿÀåR‚œ€c(¸ßšÝv¿áq9œRµ(Wì‚(¬rî ï(òªf\@º !A^üb @f\”²Ò)fd¤. ^¼æX[çp†KJ8@ QAZx{y_@Z 8® —p†q*`4)\J¿¿8\(¢Š esj* ZÌ\Ý]Ë †VÛhª(fØ9Ô}ý}…µ€P¡¢QàeæÅ @0%4 \(5À £@UÉA!KC PâPóŽ%Ë18 І5üÓÙ+€’w®PPg@Î<Ú‘%rp1ÿà  bô©çO)-~pÑ’ì±b!C !a%Và¥ÀA’€;ýK¦ÌÎGÈ= Ñ€P @’JTl> "Tp«Ÿne1Ï©„Ö½`(a¯/]Š…` †™-l0²"×´Ä€ ¢‘ˆØ¡£D02·Úœ–í‹CÄNS‹òW½p\ Fé’!˜QðÊD o ‹a îBêÇ+´2ËÈ0^`‹ƒ•’ƒ#@±ºéI-vhH£ق᫹è(“»üQn DsÁކ@ðh¾Ág6hˆ!‹0ÿöËÃr$”°€ !Y@(1FsÁ€ j@FjHk*˜„ˆ-·p¸) 4`Zb'n2B¤ %.èÀ®ëhÐç#dðIÒo²ý *èˆ.€ |\ƒvLã(d*à“;!BF,fp@€u2áÇx&è °‰®€ê •8Ü"Í>f$hFÿ¨r­$aŒá bxAŸÐÒ –‚J{t5DíÁb²‚H6sí˃‹QØeæC Hî,±JŒÂBK ‡ã’*#+)œ}!¢ZÛ‡vQg*Bfa(¾+ˆ41g`QpxÁVNñ!p`†U¿ØJ¤[ÀL!ÈL’i¦è…³ÐšHЏ˜K’ô‰Bî2‡€"‚ |(@# þÁžÓúÚ2;êÆÖ E¨š±D¸Ž|€¸2«D%—Ðé!H" ŠFfQÂÎH˜ŒÄÁ›VÂ@j˜*0ä…,¾Ã5†nj­ÕiLjÊ$âV.01ªÿˆÅi•^¶µtŠýEp vKCŒ ûr¦10±ä èƒhÀâ!ä eG¶ØRßâf¸€¯)~„;¢%O6ØQ¿ê9ðX'@ÔPÌÐ €$hC`Y­,ìâl©ðELr d`Byì øh)=OxˆÂ×P¡¼ýµäl € `‚63 +_«PØLBã+ E$ó…xFî™PƒX—–#POQPÂ$V‡1©IeSŸ b$#ab^0YƒO^ÀÉ|Š4(A YöƒL\@K#ä!‘ ¶àN° PÙA)y½2žB48¯Éb:ÿ KJ6!ÍoÀC¢Ø!ñÈ•„ƒX$¥0À,AR— ŒKvÁˆòà $¤“tT&ùË’­l“xQ Àñ&°Å'Y‹fM ¦LHÕñë„9®l¥³|ñ;ô,4"“K.0/ Å‘HNeö,À„*Æ,JV!Ëç\n•hˆrp8_8Óš 5˜tj6rÚ9Üí¦2çÜÑ›d‘G ~ ´°¥. É 8p.[ €YÚÂ%+8nÌÀ- B~‚GKq!aù`?ñÑUôïÏ*ésPÒ=™°¤,]2Ï´ÄÀ¡7èô Ÿ°-¹ª” ÿF8OƒÔÉIIÆú¹†±Ì ,a(”Ö°š¿¨Å_cÂ@eñšóœËÆ`ÖQA7äà &dÙ 8£³u¬«Š¡dq³ΉHضP2}ü@­rD¶äÔ DÈ—ª@ ¥ùbpÃ-ð)Sñˆ]Aü ƒÃí‘ e%€€’ €? ÄTÎ"Û¤4yc‰¬MxÑLB#*¹B¾æ•+b Qu¸È扅 L"y= ?“ê ~V '.À£UÌ 'aœ¤j,£Žâ…o“—!³’\“ a£E<"Œ°¦ìq 3ð!Ã"ÿX %X½Ä]ÀÀ31Ùä–V B!NQ<õbÆ&XÀѪ„«%”­Èﺳ¡uŒ%%°éQ*Z„¢²÷áHÒ»`“eâHû££Ðx¥EyD]w˜Á9ÒïoJx¦ŠüZ„ir>€7<Í’¢ÅQNrŸ¹™Ê®’nZ°0G Ò …ùl`ð‚T*R¦X«œf98«'-P/"Ò‚sáÐ "“E• JSóŸß ã]БCç V¦|I¸ŠEcàI4 ÕL¾¥“$2XJ iM¯Ðf1¨À^¶éA§œxÖ€TLQ¯ÔWô à;?cÿuñI¬vå†tw«Ñn ‰! phsþüzMðÕN7:òh[+'TÏ‹‚â—‡½éMU8—VF"¿>xÃ)î‰HO˜Dˆ üª7Ý4# ©4 “ìÄ¥%ˆCãa£~ 7,ÔóEFÄCT¢âãc¬vLCašÐl†¬œ ÍèN*¸K” q”ѬÓ_ŠèJWã#yèTÆ3¼Éø!ƒ’‘þð‡˜€`èæ éœÂs@ FÈÃÝjɄý¹!L£R6;Ÿÿ†«ñx|2ÈÂŒëi-à×us_Š@D´)–@\½Ü ØSfžESÌpSúÈ£Y¬5$]„¥U«? O ±vÙ¤[Ô,S^«Â ‘d[$MyB ù¤—РËOz©× ŒP€¶ÐWÀæ·B&p¢'b‰Ä9ÿàG–…ôÂe²0$À €ŽMBh š/(ôÙb˜G*pŒÀ€}y¨éˆaÂr‹=C1)¦±/û¤øâ0´ú,Z)Ðj¤wSº°Da@Á²wCˆsÉ^6gk7n—2VN)Ó¤USÁl$ÐÑšw?µT;ÐkeÂ!€KâQ‡¢wì·G{lo‚Jh‘FÀBìp@`ÐG”•É |(ê™›ù…[Ç™.­âJ·#@M˜Æbñ/Bs´, ›H*È´z„'„un°A¼B޹UŽÃŒ7Fºu,‘NŒnÖsË@‰cXdqE4êÊäZôy,¶ÿ{Ø 0™g¾aÀš•²(iùÝñ‚Õé›C˜±  Gp€‚ÂÊX`½nTRH"™YÂYb†I½*éqßÁÔ#øÕQ¯  ‰Ÿ5ñÇÃÁxªÇ ˨ #S‚äp¶ ïp¥Xl9³ü~Ř‘®|ö#©ì Sê@(,y@£rK x²2;Ú8œˆ²¥»–dÈ{°Ú¾,ñ5=@BÙ‚öt‘ `ï@Ç[2 X€®¡ÌC0üÂ!Å#…B4„šÀÄw¿+@¤ÞÀ W\Š(jÀm(Æ‹0q/SQ©2F€©”%sJ¡–±£È)ÿƒW7z–a•ÇɽhF24L0:Ù1aHt”Rð”Ø‘Cà š„Z•Ð.xiMÒÚ£—öùÃ5ÃaJ€P¬XkÞCB'?(Â1üa[ºPh@* ¨5°@>€q}BA"ù×Jˆ7ÅRÂéw@Sï°e“`êC £?6¹õübG#N‰|S› ¾fOÑÁó£` ‘®r' ›¤#ïÒœfÉÎ딀H(dÐ:T† ¬D´!Ñè’^:JíöR&lÀ ¼ã– Wš<`Š{Ò„FËõÈb@‚ÈÈ &  €` @ TÀÿø€0|ï1{Ø!ÿ3°‰ÆâŒjdòÐ÷¼ 8™_Ò à¥þ£2ÊýP£·ŽÞ±RF÷¤V áFa°X¿pÕ;JE«Üé ]^†–õƒU™Ò$•Ñ€Ð-aHÊ —J1æãÕ c(GÐÞ±„”T ÅM1³®Ïl¦$–IDÖ MÀ»€<ЂÄàØ,L=8à-hŒDv Ò9uÏxƒØ(bÊ7B‹Zl|rÍÙÀ&'žRi0L¤PƒœÅaÊÇ”@¼2#­Ê @–‘Ó¹.!¡qÝì.#€Í-‹ÊîÙ3hÈNKÿï2Ù„EÖên„Ž;ŒÐ¤ÇñµGù ‰<•)£Æ‹¦GÅšd†¦Jp*"œ¥ðXÈ´–0=Á T@‚´8ILBQŒ À)Á½Á¡wpà$MˆFYädb#ºM©Š‚¸á¶f8¥G6 A—ð%+VŠz©$dð,‡¼oÑ‘’œ^ NC 1ƒ=ËI'‰„÷í+‹€³_‹/¡0P‰gQ "KtоˆËD`€XàT`˜€-…9,I " @ €Mè+#©ág€:ŠRï%  ÔQû†¯ObóRo.Æÿʈ¸±Qc.R#˜ëdïÜèFóXrx¨¬ÊYR„РZ_UÀnhC×ÎÉrê2€>É®Np']ÃŒ_áÚF¿°°T‡ª-³DÅŒàèÀ6ß êÛ±D%Tð €@&Ý€|à7H€¼Ü2H@,g9±§[”fßÑ·ªXCe’†=µŒ„;Ñq'. ƒ9ÈðÜÉüxŽ\™®t7[YÆaWå›i‰„ºî€TžbÁNʆ–’ÐeŒ/á×Ì3©ô«€|´§/fX SCÅg xÎt6ž¾™™ÊFÓ˜À/9 `¶°L Ýÿ 4 __ËRx˜ßNÔ‰1j¾Ô¬ÒƒI+÷>÷t*b@UíW¢'chÑSÀLÞ=ÚªUBCY®a@žùúɶ²™6È'ìFÂN«,¹ˆƒsÚÈ9=ô±2šÈ&¬i…™$ZáàËœÓ Ói:cBGÄ ‚ À¦ M‚@@Þ_—w$pü È@‚ñ¾øÓ‰Ñ OÀ¦Iq}~ P>¡üÇlj Î2* EcCmç‹pÈ4ø^î*¡GÌ:VVð/ȹ¼?þFÂe¶’.p#•FRKb$]%_óÇq1WláAÿ&àdì%zd*ûcF7¡) &J®'bpBb&ÁDtBCàSÀÐÐ3àp`$0:€|Êv>ÉÂH0@úV7åo– ÑÙ‡è350öÁ B¢Æ¹‰ÃÑ*AÊ¡ –DO dxsõ\–=¢2`†Õ…Ô*ûta˜î=ò,‘4ah_裺°w&Òº#iÖzkTâS("Æ6¢A( ð*0õÖnZ‡…& ÉGÊ—|aw|2Z0xpÃ3i„}‡.æ_í#¯ !2?ÿµ&?aq¾ULN¦$Kp@ž#çp€åõ?tko¤‡D;‚9{3LjUÐkÔõ@ò%P±ŒØ½ÐsCÁ_{ðÁE¥ËT}"&ù>P' hðu'Ð]ç‰+õ$pÇ×r=XŠ<È| °$ h0‹ˆB}"5ãb[½s&i†61w˜F=Q?P5\GNÆ‘*h  pU.@Ž 3Ò+J°1iI+y-9ZÕG[õ€.sU$@+ÄÖ“ï_¨’$àdòPŽß"`2ˆm`MÓ¤;‰èBœf ²ØoÃSFˆ`-Ò°ÿ' 'à (àuéƒ)—ɲfùŠQ=X3 ˜)Ö}¾ƒ& Ð>ñ!LõOEFOÙ 'R  ;¶JÐR€bÑ2OÐrä8˜išyI¡9ÐÒV–Ä”g°†£Iì¡)Šù.a0á2g²ÄC}PÖа&pk.åéƒÉw¤(oÍ v_48S  ãÒ<“[…@0÷(SÓwª's×)6á6¡ˆƒ ×cBa’m¤šôYŸöY¬Éš­Ñ6!X߬wXÚERgqòˆ%– P¦E–€ÿ¤è°nà–Ó—:—ÉðÇ©àrãZ®Ä„³•>W‘Â0 óüåò”8†"ù@N^èl÷¹£;…# s-ˆðc"¯ðP÷"ÖÔ•»fW`%€u˜ÅÀ2uh)@ ÔŠ³€ö"ÐD8œñϨ¼Ý؉PDM}—(5S*°» ~Vë}ç4¢4øR%UèTW½ýÇ¢ðáI4‘;Œ)Íí ¤kTàWžêТ¸ûi~³ÀI‰‰pÚ¢u°&ÿ?¥×lÛ= ÀòÒI]ƒa$ Äy¯ yoÕËã¶œîÖàØ?½o)¥&ànYŒÕF”¸ÜR,мW @¦o™x–}ßÃÎÀ‡+1`¦ûÖ7ƒy )wí¨E—r$i §Ö§êß%‰lõê‚XfK¾ºž~X*}«UóÍóˆíöK €Á4Hƒ# ,`¥U|hˆ ‹f¦ -Pò>}hò&Æ µãê,SGdZŒ«ê¼‚œZop@Zÿ0î§ô¢|Ë ìî$Šäqç4æYž6AÒkå¾H8Å0ðV_'Ô K*òØ?øk¤ÿ‡.ÂÍæ×0Ú6hÚ# Ê._LœÎ)u$p¥û¸R D=I=Í{1ågþY‡n,hÐ ÀKô&>+hZÐ,@nk9´%%–?‘bgbï ò±E,£¬‘cªr#9ÈWßú׌»ñ‹s½w›{Í}Áz.ûa²ÞJþðÁ÷Æx&蛥0PÉw|à‰äÊ‹ï`1÷Ž,ÃÉñ9=LˆK–·,ðá aX·VÀŸ”Ê+%0ä–¹4hZ' ÐýPd¬/,LÅŽÈ ‚€H0‰Bá‘p>¡Qé”Zµ^±ÿYí–Ûõ~ÁCÁIh qR±PuA^XÐñ‚¡/. -,0&02TT*@*<@2:6 LN'dT2**22Z>>:(Bj4BX&2@"AD\W1L4†A>08(N,2%@80$0><`>2Š1<0[,$zJT"èîàßâ芖Šh’†ÔÆð‘3`Aƒ&$8æÌ Êɧ@@7íä¼Ë“GOÇAz1€ÈP!e˜à*Å '*s ¡  P0a!@ƒs¢RÈøf¡‚eDXð°áćV„Q:ÿÑ A 8l ÌA •V:xäJl ᜵˜Ð@’‡ "€p¦k ÚDdÈVABˆ¶’  M¨$Ä í¹õâÀƒJÜX` 9õìâŽ8„°28(+"ȈÌÁ!Œ°1Ë@b ð1âŒàH°‹êÀc:<ÒCV8@HBd†ÿ°!„ fhE @`DG<€‚ 6ÀI§ 8!„FI@‚D©€„2±€ƒ&x­S¦©R…•T0+ ¨ ‚¬v¤4©@€ 0`áWÐÀ©X+† f¥P:à†^| þâ¹Ã {lG€ (`²‰x€ŒjBÂK1ÍTS(úÉ Í8sôSÓhÐþH•ÄAD2±DHŠ€øà€\`˜  ,¨A„ ”Ôé„%mR²‚a?ñ-Å>Ð`ÀÊ@ –K¥V2˜%B°ÀJf˜!‹q@ðD&†f,¸³(¬Æ— œà *ÿ\°‹¿ì8‚?£ ® ±2Œ¨°²M!ŽXb…pX€† ÃÙÀ7>{ÇÑüë((À¤ J Á¨…Ÿ|‰ƒ(¥}£àØ(°À6‚ `ƒ X’(9X%’ ˜•àä²Ä`®3³ò‚™©M Rø1 DhÀƒXÀ LLœÊ@) ò’À ö<† <h0xøáG1Ä”0bâÃO‹†ÍÀ§|J% ʈ ÈrGOã³VàÈ€:Bà…‘LC$†@Ý©SÊjÛÀpÇiLõävS`Š–™ØÚ¥]ÿãÂ¦Ý ¤enXP¡Í7!0¡Û¸á Ê…­=aÄòIî°>6Þñ4þ:8á;Ò ƒ$<ÍÇpÅñÏqÊÑ ÈÊ SË%¡#jhøÃ¹?¬`Ezè!ö`š` 0úê`Ä<DË&X’í€DZœ"=Á›ˆ†$±h7ªÀTÀ¸ íMÈÀÖ¶$ °=º¡Xö¤¹I`G J&ê€VÀ(€š”3“|@.HÀ¾tŽ"ï˜_æð¸Ša÷ÓßÑx©%4„A Õ+‡ zăDìÈ^•ÇBD€9ÊJ€‚œ.±ÿN„¢-É;9Á (‚ ép(9N (àŠiðŠÃÙàJ2p'Ä@à ¥&@‰ ”"4™„4À+±Tò„Œ,ÑLHKBÂ" Œ£ÂWFK¥‘™ÍtŒ$'i²ñbœÀ>ØpªÏlNdüéqDâ4 `@ÈpópÀˆ‰ÐBF ƒeišYÍn¡p £4MÊ¿Šø”0žA•:2T“Æ$çÜAb{âðÐj˜…zìšFHív@²]°! ¥0L“hA»â"–€N0À†¸Ôƒ£¨'b³¦ò 8%1p&VÒÑ’À3ëÀ ˆt‰H §75‡Ša~IL¥bºÿYýî× me0FÀù”ÃÜÜ6ˆ*£~„E¤#gÛ¹iâ•ÔYN* d@n޼'Ñ”(å,å+ Ø(C[2â¯0r (†Z§ `clT©@B/q‰ºØ -©l…D˜q”ãP¡ E„%óóc©bø›åýºñëG4“pδoÉœ. õ­ÊD}89[+&Aà p‹0è$€ Œø…I…DqsŠ „À8® ø ’H -àÕ™!–f|ΙnŽVV@’P9.&ZAŒº‚߀QÒ$ œò´½xÀæ õ—xÿ€X´ì(›_-ﺙr¾˜25ƒL1^Ž/š£1ß1o.ûdƒ€H ÁÁ–έÜT`6©r  0Nà¬ZJm’N,[¤9€,¸D~ø KÄuGVÜ@pM:!±8Û™8€ ŽzôèWë¦Ø,2ThA™qG1‹½€›Šúìàn:^oÜ™J`cf µÉUnÖŠ=Ή*ÑàÑdŠhð!øS™…KxÂ"˜’z*0–=_K!€+v…VVH•Y˜ðÀ¯ÀÁVWÄ€á°Nœ`מ|ÈNhù¨Ôxp3ƒQ a4™¹ wöSüôæEC9*ÿ0 6aÊq¹£§‘¢ˆMkM3T®šfmJUŽ@Ñ¢Dˆp3jˆÐJ= ±Õv‚$!h°©ƒ°«L Kå™à ZW,6i è`'JKÎD @€ÊáÖ]ðnJì8Þ¦@ÒÕƒÜä£>@a! !!(4ôøÌ(. 2N*1B˜ûóñ‡L5Th >}ÈÝíë¡7`D¨HL¥ŽŒîoÎcÌ¥))II€æ•„0Z1– ЮJÙÁ†ôD CH„ …ÁÐ|al`©N`%ØÄ²áF4 ߘG.ÚÅÑê‰ H¦@: 'v¡léê‚:‡ŽNÿÊ€À‹JK xŠŒúXðpÄÀb(¢!Êè2>…äâ+ð¸i<ÂÍFÃUø#1ƒq(…æ¬ÎÈ,¸ÄE~Å"a%Œ(Ç\¡l.aG´C_0 âBŒÐ.ªMxƒ„Á(Z"®k|z‰ß HúEˆÀ.`Ú‹êKš°(Ü 1 bê®îÔ€ &Ç~.T(¢CEF€/2‚#†J4t]EÔg¾, ¯Î|b.¤$>Z êD†0l¯|â-N@9Æê ,^b-x*ÀÂ4ÀÑ´ƒ7êɬÀpŒÝZ)ÕèŒ7°ª—:ñ 2F(ÿhüÃ?$‹Jpƒ©ý.K×qDø|Í2.ƧÄläú!Ž>£3‚J#T«dJäÙJ§Ù>& .RæÌñ€WÈáèÞäFRÉm~%¤G9R-Nf‡R€<à2êÚ a¢"A,@¾aÀâÔf–ê©•h¯• Â.=ÒM‡Dï€$ωP †ó²Qÿ…€²Q õY¹àP‹Ô˦u´ÊŽjÍ`„ ˜“˜ˆGM„9Of©Vd(MÊò&+†Sí•baL]Ϧ! ‡ ½æÉH˜"7ID@ÅP€x€IHJ€Ve@2àèèøý3±BÍÔSNчVàðRÖÚ@[ùa~‰Œ µd³ d½¬²uäÆÑšQ¥äŽøFÁ<Âå"¨2“ê4ˆªCrjS94‘DØd`Š>PÀ;€VŸó$ 'l£Dh`"6*`À‰œèî18ÁW9uÎ,‹Nÿ /þàöÆÚA%ÛA@è EÿÖdí¶ NdÐEål2¬óZÇHÖîä~2GÙñZ…UáðàŒtJC ±ræH‰&ìÏ,—&C¡ÃD¬9¦¡%ˆ&N,$À‹€d€„B(Xàb8@=î.çŒ]qðô¤„UƒÚ@Q¥ì:ãn‰7 `ŠŒÇ2òNÀÚ€"ÜÀ˜$í̓ͬ\7XË_`œT+¾¨R‚ý  ;J,`&ä’ÎP `€Î 6À%¸’ ¹ð,0¦n5êH`ÑMáæT¾Õœ(Qq A}4{uv/“R"¥rŸÐ–6’: À(Ø*ç̈rå%¸pÑ FP@\@uiO °Y" Ôo— ì(ï]MQþëš&x‚Ʀ>†ÿîeçˆø†)cåsxÔœXåð÷„U¸Ež… ø˜„ñÆ÷ š‚vKÀl@@c@‰p" Å,`æÉÖ"ÕºNJ`OÂVhkCD=|µQèA©úÂ?èè?sDÅâèÇO·PóV÷nôöâæ‹ä~·´TŠoâ å”jÁ`œúàðáðä¸RI‡…û–]¡@Æ®Nnrÿ¡_ÌdݦÄß¶…œ€ÀDÀ R@Ôœ > 9›l&×…mÊSåÞ ˜L0QÎ0ø²íN™‚»ø‚æDŒùNs¥ŒW’zj—ß …—tT„µJ£Š²!*dñ$Öñ [,À`@=‚Á%4JJ‚Lz©G²Â8fÑrL‰ñÁapê ™@¤ ¬}ÎIâ4â.Ý66cY3¹ï§ '*£B ’;%ýÁ§6wë€öí@%÷4nÅÀ®MV pº²CBäÒ`Q1z‘@¶ ür}¼©¨we ={ù­;Ú¤MÁWb¶'´Ô!d¡ÆšÊ,:’,°%ïâY¤›j+ƒŽFôá€éw Œª‘Ö°ˆ·K–¾iúרO3<«rÀLcF`âþƒÖþ`ûÎXcéÀåêˆ9ˆð23ŽM£œÿ©{ùŽ>ëv9‘Úž`JH_*H@êlr&­v¡¦"N¢Å#)%ñ×t:“#… 4VÚ å¤>dYóRÊ$ÀY¦zœ qCLeÅ“@R @ U. ˆ9Íi{•jœ”ÊqA¢Áô¨$þù„Så(éqćñ Â`ÀÔ…‡¨âMŒ"Nˆˆ“¶ ·™½ºyeóΦÀùôÆdÍd”5Ñ”‘<&)EÌ2æÀ/¸”$•€Ç“ûOüV dÏDTÆáR&tÈ|$. —Áå M—O&#îá7Ô „ka¡š*Ü¢–ÔB;Ï_±Ê¦å;X5 h@ÉUÿù/LD¿¥LP›OyÃb T¾Ó(AEŸ—f9ðú£X#u©œ¾|tÂI…S=ÃG¢4Lª±)ã IK‘;`&V£ê.AjœnL›’t'߀P†ï¥öPžG¶œ$/YýòÙÿ”A¨ïß Ó¾þ\æ|:Žwû †¹õIR6}¨@œÜ Úûd`űÝÙ]V ŽæSRŒ±»ë}nÔ{ƒ€a%mÜâŽ=³îªýT"`E̦ÝÝÖò¡}À/’Ý ªÑ/×á“­!}aÚ«0§'àMo(ÂXz?&ó6½QÊuÁί±šÝÝþïÿkæèÌñ<É@ Š¡'xåIÈšE¡O©O=ûcüȹCÚn3Ð6È‘@æšö¡Ø¥~EèeÔ¿ôZ ØËÌ6Ã_Êìc@Ù.`$»‰mÑŒäÑìÙÈ ÚÈ©4û—ÉœœReë;“S£€7L@LÞ#Rà–œë×+ŤId\!Q}>ÖoJÂuE8cñPE…Ù`òµsïÒºá*æ×ƒšôÎeA„´æ¡ÄoÍ>”ð>'E.“$º¼º/ _Õá?{sÙ@eñn—ÄŸ@] R 0 <IAÀ¢•Áˆ…EÁÀÝ* Ì4üÝrÜÈWðx2‹ÿò9½n¿ãóú=¿ïÿ 0$4%((,2!:1=0ž(>½…Œ€EaaDQ™ 0”0LYQM Ð" ¬ÎÒêîÚîòúžŠ=@$=<@$CÈIp4„È• @$ &b_¬vŸ^1@•~© ,Dˆ©¡(œ«—7-9Þãçëïó÷÷ hCIÑ!Go)x# Œ8%9#Œ™ì$†Â‚.ʪ.«nyLÕ«•­Y©d¹PRW/–*i]¸‹V©(õ ]»–¬3GNŒÒ¡¢PÀu£eåJ„䬨¨€Í00Œ2 =5Mü +v,YBÿ$$2ÔDZD”(!R0âAuÕk‡Ýˆ…é x¢š1Ì•QTXÆ5 Í”¶Ϥ©˜/—.«ÔÄT@’É„.+"!Š#Öˆ¦…" B«ÖK9bap•Š­+gÔAçŽÍ6Àdš¼)+|8ñâú¡­‡v‘@æ 8qT×P™µvýF¼E@ß0Q>B‡ëpb§Ì—š"«Ö[-+«ŸÙ뽬Q9+Q²3t ª%QT$ךGKY±.¡À€:áù¥†SM˜SáÆa˜¡†¦•MQR¨Z4Da™,T!' 4âICU…E]ŒÀ ,]g…*²`F°§ÿËy·Ð'_e®h‘Q=Æä¤Œ2H³ˆ”G¥‚`+ fv…M±VÀUžÄ¸FU,nÇ…9òı¡šk²ÉO œè¡”DáB Äi§\M4 L°ÀaŒ°x^FÅ 5µÖÚ-˜¥äè1­™+/¼LJ’Å7%sŒN Y£„€E- £b­¼òZG¸€ƒã–^~1Uê4$WæØÓ¦¯¿«ˆjQ(§"ÕEPtLÈŬdL„…8@iltå"¤R2e:EL‘åRn¦±9 }DÖòBMÈú†1¥J™-ÁÒ¨kÜð{Jª5¥:D„9¼B¬Â +ÿ§6h=—Ø@Gì"”4’^ѵXFne\À¢£Æï2ɪ.-YyØ.¬F¦ÞªlZYm2×â'šäwÚ5ԙÑ-˜Êv%,®”‚T+]¶f|+«õʰÕWçÖ5ÆRÌ™3x¥5Ý#þ‰P_¨Æ];ù5rx]pq˜ŒKKAÓ¸/¬€tcæÁÔ2§”Úâ-}ô¥” = ´NOxY®»4íÛ ¢!‘2Ž—Þ Ì†ŸY! `ºèc¡†~õB×Hx&BIRâIJ¨×³YtɘwaŠE_º‹#+¨’˜IH÷˜J¹¹´dî+Aºâí.X«±‡Ù«ÿ€*1Pü¦r+l_šL«lïð[N…hŽÞ¾ûùœ.@nI±#œ°5gŒˆa§OÔ({óXH¡(ÂŽ¦*U‰B̕ȣñ(¦HšÌ."Å)Ë `p˜Ùjl²z="|+mªŽR|IWiP(¨¤–ƒ+ x kè‡Ip† qjA¶¶±zéeÉ‚w¢Ã"ßðåA`ÚÈ(À1€¼°qä©,PB¼€hµ0—*Öu‹šIfG+“ˆCšcoÀê#z” —¾R4¨ƒUyÚ47†]ΆzÜ#˜Ð–lñ×Ñö‘èÍ ‰`‡$ÚQ0ÕDÁ;«J"p;(,¶Â] 4IÒ¬øÄ™uÃÛSYeba$æ±Ñfž‚Q½ &Jç±V¯hEø2³%Ö4¥0šCóÌT-;ntpsec-1.1.0+dfsg1/docs/pic/pzf511.jpg0000644000175000017500000004762213252364117017073 0ustar rlaagerrlaagerÿØÿàJFIFddÿìDucky<ÿîAdobedÀÿÛ„       ÿÀ,ÿļ!1A"Qaq2±#‘¡ÁBb‚3Rr²ÂC³ÑÒs$%56ƒ&’¢Ã4D´áñSct„U!1AQa‘"q¡2±ÑBRrðÁá#3b‚¢$4S5ÿÚ ?úf¹€                                                                                                    9ò Ç…¦È‘!…8¼’0U—6”NOzön,1Í“¿mÐÃ)+²eÀªÌ8½‰«FÜžH‡$‡1÷oÉ’-ÏT"êÑÍÜ{´“M°nBgºvmõWøG!û–ž›#z“»ö•6A4¿ÕŽßÛ+VôÙЄåoÁÄ•í”O¸½="=Aï<’<˜*§ö¥¿æ*}47}Ãß»®&¹ž¼;ñc2°ERÎB¾ M‡ØÜÊ^íÝÞ¢fìŽÝ¼É‹’³0y…Ÿ¾F1:ÕÃÀUíJ*µI‹‹ ^ÕîþòÏeþežîc&쨑ê*mb@¨t¨«¡®mò<˜8òHu;F¥‰çrùV2̼rT@@@@@@@@@@#Õ×+ÚñU8ÒæAIo•c•,ß³ÇÍî«Û)36Çì¨ûƒei¢šuhÞ\I.‘ÀØÄºoã"ñ"ÿÕMÄͪ“¯Ù™›\r $GPÏm àO *… …x{*åN…P“Û_æ¨$Þ¶¶ÏÆc*“ÀýHZEÖ†êU•—‡V#…CD§BCj“è0<ˆeÓdŠ&⊠»°¨ÚšÄW°Sc:UUË–6P¿1,yTmIà+|Úïü»üúkÉYK3Xä:¨$(€(€(€(€(€(€(€(€(€(€(õ†)öa ª$†YUd¸«)V¸#ÙW·™K™Ì-¿nÇ\m¸}&:[L1X' Û‡Û[, ^9 ÍÈDÓpx[ý M›Q½©PyjÛP“Ê‚O ² ƒË2^Ö½@fƒ·¦Œú(æ¬e™¬rT@@@@@@@@@@'wmiþÙ>ãW·™K™ŒÝÓoÃxãɘFò› ž|.mÈVÆ#›[ŸÛRˆ=$÷Æ„…T›ØÔ@ܨ3'ÆÖ¨$Ð6ÐF ó ÇÆ±–f‘ÈsPHPPPPPPPPPPP}ßÿ+OöÉ÷½¼Ê\ÈÇû¯··,þéÛ²âi>Ž5euŒpmjKŸászÕ™D¹ÚÖì-mªÄÞ€öô$ã#" h$ÈÈ‘b†%-,ÁUG‰¥'uvê(cœ¬.º˜ÕßT`uÒ¦üM­Î”$æ^éÙcêõ$‘DÇ,¥¡‘B‰ˆ©¸c¨yyÔP”=ÃÏÅωæÆbѤ $óFÚ[÷Ô2G0‚g@9ê5{•ê¯lì¹rmY«”ÙÇKÉjèoÄXë¿#쬦±8®õ;V¥¶U¨´>±v‚í™,^çÇ›ûªÕ\;H]_Žõø1Âú¯Ø ?æÀ|`È|t.º¯ó}¢Ðz›ØS&÷Œ?®LÛ Bë©qßãCÔï^Îquß6þ>ܨGÞÔ¡¢æÙŽ>(];£¶d6wÂsû9¹©Bë“må(ø¡ì9¸S‹Ã‘ òÐêßq©£4W"òhZ¢…ª@@@@@@@'wÊ—ý²~š½¼Ê\Ȩ“á[žÇÝR¾êÚ3Þp¦ÎÚ2ðáa¹èI%7þ<¨ ‘Ú9nÍ‹ÈÛgÓ¬ò´‡$1œO;ê]+ªVO ‘ÆGiG&f\±È‘E‘Œ1ÒÈZn䑘™ùoí5¦Ja`®"Ρˉ¦’sqk¢>ÊÞ=ºéî<¨ ÇÔb‰Þ[‚‹(ºX~à¬nè|—U_ÏeqH5™ç4Kö¶&>_sí™%‚|ÈRXÛ“)ap}Ôg_OŠwàžU4ÞâíØ%•Sn‰Ú?æ’(а-*ô Ç•ôÕÒGÙ8§¢ð!¢í8Îá¹õörb׆ð69ßE‡ô4òð¥"døöÞq‚#Óµ6ó'_m*ÍÑêÝyãä»_÷‘i¶&o…eþø ²{_bLy_éŠ:ãI 7agU‚ǾF¦ÄQôî?äDÄÞvô»Ëàc±ÿÜò'ÖRÖ’(ÕЛó[“qM¦W:]Š:&Ÿµ™Þ6á¸Å´sÂH¿áË"óø0ªÕŸ+³Y7â]÷?íÈ‘¿´MEYuοùåâ?Ãõ'¿ÔÆ#ŒºõR5É|èyÕ•N‹|ûî¿Ì¦qú½ê0þ/Ž67莢¤®«È_‹à‡0úÍêdË‚`9‰1Óûš)Rˬr;W€ö?]ûÙš ¾CíhfóL**jºÕîÈþÞó£ëÿx/< ¹¾ 8ÿâš“UÖ®öGö÷Iÿ¨žäƆ9§Ù1%‰Ø­ÒiÝ~!êñUN«]Rä•\U Ÿ¦>°/{î9'mL1âiI\ž±:YÚ:qÛøœïVv鞇’îÊ‚N… G´¶áPIážãõ=¦”~þ“<îížHƒ.4 "Ø)qÅ9 ÆJ²Hù.]§>t£Ý,ÀîM³ 0Öa*¥¿ÂàsäMVVä‹r:mÈ*üÞÂÓ çl[ÆMâú¬cn=]XN*yÀÔ:œ».qî¦×™b]ªûsä6Lݵ]Ÿ¨ÍK9:‰±OÄÔÔõ^zÃâ+•ê¾Á™,åì9AñÅ‹.À![N•ñ¥M]ް~''ÔNÍ–Q3âoP:é·K)…Àäµ;‹®·oXÈ—ÛýYì¼d’9r˜LúØäÇ̾U] †<·øÕ[4]nÏd¼3}dìyq3'|õ†XâVÆK:é·Rü˜^…îuk*ÇãD)©7 C䨋$$^ ”„Ýàj*]#¥4© è?’¡~~î’hÆóHfço %Shªº ·)—éÁ7U M¼xSŽëfóÙ’cE&ÖØñªÁ[ ÒúH‹"´K飒d÷.ýºçK½f¾4’dH˜X3*&8sÓ,â̼I¨}ƈÐvŒØ>%wbÒ#1Ô׺{ñöTL’ê§K«ÃÓ¯WìÚ÷ü•ªu@/wðÙÉÿ÷cûêöó)s"•ª¶2:Tv I'bÜêíèo@yz‚NIöð¨% Anº|h '¨±I6gyâã êË‹¬w‰Ðmsʲ­&š¹õíéå2îÇí=ÿ¼vy3£‡nÆYK¾^t°ÇŽ1¨Ý˜µÍían5¬Ó’¢ÌúzumPµÅÛ›æý¸ægŵÁ¸ms¾cãçZ2<,…ˆ4rYb>À XsáQéLÎ{Œ¢å†-çØ0–>¦Aþ]4% ƒ*G3¬JB}4u¾«5kV{Èòÿ¶[¼÷Áí‹8È1E,«¿U I¢,˜‘ÂËËŠ¡» £XÑUÞ2õv[{‘â0‘©áÆ÷àE¹êˆûj¦·(JbGM¼á.KB[&Ú ÏžÖ6«+mözmÉÃv]ï£ÎÅÎq™ŽøÏ<]h’@hœ¨Wðkp£À§.Ì­EFYµ­Î ànf…ÃrÈ’Eb·+^‹3«)$Éí³´rw(:ØÙÚ©¤$ãQ+kÜèÍù« ¼˜EÒŒúyt(Î>Wµ•œ-Õ²œô ’HÕŠQK-Ç…À®ŸN˜žΛq,1ää}<%¤¼m§S_Uöññ>©ËgŠÜ©$Õ`ÜŸ#—<.˜åë*;Æ¢ËtkxÜêñ«Ë°ïäÒqÙLV´b„ݲñ±ŒTXÙXš'c¡YÒΠ’xsªêˆãñÔç-e æç•µ€.å#ÈÊ:МbMrKd[˜©Q=(ñ – šcæm³Þ-Ã>r¬d2K™l,„8:}—jP²ö2I—·¶FlI)2•ÆÊ}åŒ($°ˆ±6ãU6EÂ7ˆ‹i°·)·Ýjš Ž¬>žÞ-oݪê_AJ‚B€ƒï/ù#Ÿd‘ÿj¯o2—22é{Ƕ"b¯»b)â¡û‰­ˆôgù_€‹zÙ‘üÛÆ7ØÄý•E¾žçåbMê‡c'=ÕõRCýÚÈ}5ÎÁ&õw±ÿ¯vþ¬OþŠÈŸ¥¹ÙñB/ëGc­í<ïýXé"•Dý,û¼FÒzãÚ òÇ”ÿ¸£ûÔ©?Lûcâ"þºöç4ÃÉaïÐ?I¨©?Oþ¨¥õ×mÿl”ÿZ@>áQR~~d]» »bî|÷±Ûu Ëj7þLÊì6ºV¤{ý,Áßb` &Ç/| £Kz>•qÇ’»6¯´Í7 ˜bãa`ÇŠO©9Rq˜ÆQ#HdU½¬ÈXXžuZª\¼šxP‘M×1v©¶Á}4ÑÍg‡\gh‹ib¾·ÏjFãŠÀå"‘Ú¶ÐWjÞûwoÅr»}7ècž?®99°’/Ä6$qnW…ZIbuÛä¸ÇjJ„†Ý•¸‰±vøòT쉸bI•»cÇ#”–Hïnšj~Z”€ ûª«¸ËÆpšQu†êå­³OlŒÔÊÛªã¤+‰6ÙÁ#D®e\øçRòÈˤ«+-5i4tß¿mKZ÷Po'jaïRçï] Ìî;ºk‡%ÅHñ2$þge`uËåEÇ/u”Ž‹W·ª¥˜ã'& Ó¸qñF<û\X±.Û,™Æ6`ð½ŒÓò­ý—ûk)B‡™Õ ç(·HÆ©TŽÜ¤8iT¬½é«•¼ú|:ªJ´êÌ] _dõ— P‘€ÆJ¯AŒðñ<Í{z`§t&<Ó,Sâ±0ÈžuqªU eÇ Ïæãz«kÞy|ë©NŸ‹´a)eÉ›"8ß%#Y²ÙÐóYôéJã—è¼Fi&s;1)7–1Ô@~Qáj¥15лa÷FÛŸ¹fìÐúý½æÄÊ@ˆ^Ñ’OÔe·…tèc\GŸà~çè®}Nê  ½íù¿¬¿}^Þe.d|‘ÂGøÖ±Hß™9zUž)6©¡Î¦ûNŨEOoËßR*„ßÝBEŠ€/µ †k Tݽÿ·þÃXUb/¼EûÖ4ÿ|£(±ƒZücŽõI?2ö3ÎÿÑþÕö” ›nÜw<Õ‡$wHÁy%b±¥ÈUge o>5*±-#½XÖ…Š<(°&M›x8ëºeHÈ ‘}*˜@ýs¤”.×uñ pöÌiChq-Y^”©-Ì­n’†G\Ldƒ~¬Ò¦fyô¥”ò)qªÕhBª†V8󕽕^Wž5ØFBKƒ.iŸ§ÇÍ/•á%‡YbŒ’ä<øøxÔµOaoNV$¥'ä®9’rì=xäÍÇ—y…"‰q¤ÆˆíY_ª™HUúîêAWÀp½Kœjkw—a¿Ì:Ú1ð— |¼LFÀè˳Ì]“”ë0 9uÔ‡…¾6 KC \ÛunÂ9ã§pÆ ,)çÃ-“•ãºäFð›Ë‘ RO ,§S™$‘$û ûê[N†÷¥ ê•‹–DlPâG¹ån’eÉ…·Mu‡L[CqïQð«K#ѽ ÐkPÞ²2 Xƒë0ÐDV8âE]Mä‚ñ^ÎOº©[Á%U‚4.ÆÏ’M]»ƒrŠTñmÓ±¶‚zM}<¯ªŒëƒï5oMâ ŒÍÒš6w™™¤¿JC¦1ª¥lž Êþ<ŸZÃ#^gõâð,Â'KË :<…Ö y-®jB^ o‚E#ù”T2ðÌÝ}¸í÷÷d0ªD›ùû‡ž¥ÁÓÜ;Þ]W3àDÅ}šQù*µó£åïܯ9.È¢£³õ“²ö§°Ð›ž<±30q˜«p †òÜñ÷ž5ºÈöÛó»ñeŸjs—I›d,¨¬2d\‡ößóÔ²±vî&å¹ãŒ\QFÙ(OÀ—@ŒÄ¼žn\+žt÷œü®<ç lÂUδOÑ\ °!â'6Ü#} SÆyƒsãjËsG€¹Œ\.¹×àFì¡›¶@£xÛrÿ²)IÃc ÑV2 [ÊÍ…tÆê>‚<›n‘®/î&öü|àÀ…éòrzƒ%ócÇ|dĈà¿Qî)v!|Ü:ÉKDq[æn¤ ö´ñz ácí˜8pîX’>í—Žqà…W„eó©e 0p-÷U”›y”âse)m”ê÷R”Ó´‰îLhsWfÈŽ9²7gË;Æ\¶ž|†|·hN¤šðéæªÊi:’äæ«¸u‘“oîP¦$ø’¶^4ÚZº&÷úoËÛWrM:DÇ^¡—¼ÎA WPKØrIÁTHà±Çr£Ð_“u͆fLæq€ Pé%‹_‰5j£ÒV3Û}µ‘ÜYgj†yr˜¨Xâ[ê<£ej*‡Ó¦_£ô_ÔX¡é>Ë#-ŠÝ2± ³|^¡Û9¥Ó`庬ïnô«Ô Áòfí—Ê€³ ÈÆ»)ÀÙÏ«¶e.›-Ë(­TÍÚ0q'Ÿ rÏL}ã6Ù“†C{…¤Ü›pãTqÄ×û}¸GÔˆ ËsÐàm±#b¡GiƹC #\ÚÅmR²6¶›Š­24>Ç){u Ø¡K›R"_ÒòÕ™Ù [Ói¢þ[4}Gy„²^"J?$'ÐóøÕĹuŒƒ¿ä‚Ü~‘UV÷6½ïoƺ µßð¯ã¦ÿš¹õ:4:¨$(nïÿ·ò~1ÿ˜µ{y”¹‘ðfXÿx“úƵŽF¼ßê¿ÛC…ä*Ç:=·*uÏß@ ´ Cˆ‘ÍšÞPyû*­£{väüÔÀÜýÿIÿòZ©ÈÌ’õ=?Þ;»Wÿç/ܵGó£å/úè”]± ÿ¢°˜Êˆ±îɘl¤‡1\K(>]!W“<=õв=×óý@Ï•²Whæ€î™,±I]=I´iEbÁYaa¦­" ˆ˜·œl†L0£†áR͹dätcèC Äh§Ë¬H/¨óÖ½`ãí2¿ºjVÖ YwFí—‰Ù$SÁ·¹h#,œä®³f_›EþÃí«($RÏbНË_‰3¾`îxÒâG°áíÓ…ê>Lz‹.—ùT!ƲŸ•v”êwvÃ%‹em§xm¾g†y"N¬ ìxÖDvcåÕ{{k7Txü´áFLÃÁÞâÊÛr:‘a²fA‘)é–|e˜Åŵ»¡D¸çWŠUT+·¨[%劮:÷Œíç'+xÂÌ“?6|Ž©I`iY2sø ±ÈFìá[5Sé!ÑêÌÁ&>^›ËfâÇЖh]ŒfB…ÀfŠ3bÊoæacT¥2+È‹”][È î3Ë$å2Ú(/]GK)ÓÃå'ŸZAÕâʰöèœ7§ý¿âš²,mkùïUy"ÿÿ§Â¿õœ·æ }®Â£T#©ôvå±m!äÊ|xT12K#¼‘ž§¨”>>5«!£ÍŸhÁéD#l|)°,‰¥#_™µë<ý×<=ÔA#æ¯U1òßÕ¬ÁƒýoZñ3i ’½¹Vrɉ֘fPwS´D½lDf™¥:çf-øÖ¿—Wô}µDa–Ò; h»u5lÛv=ã6éϬ8'æ,ÜþµÔqöQ0F«é̽L3É3t^uúr8cùc=5mO¨xÕ54н&c¶ó‘‹ÖCXªL yƒ7½½‡ÛöWI–¢ßá~ïè®}Mô:¨$(ŽìØrG¾?ó¯o2—2> ÉŽFÉ—JÜj?}^2HëåñîJãi`r°dxFÇì5mèÁq.ö ..IÿÏÁOú*7¢ßKs³âŽÆiå)ýÆÿE7¡ô—;>(ívÍÀþVcÿ†ÿè©Þ‰úY÷xŠ£unXSŸ„R«MÈ.,û¼E#Ø·¶>]¿$ÿáIþ­FáôÓ]ž#¨»¸oÃlÊ÷þ ŸêÔ7SHÛ’ìñ6¯D6ìü-Ó3Lgl‚U%R†ßÆ¢&<Ÿ›ÜJz†“nÝÉŽÇDSaD³HOF ÉãòŽ5I|èùC§PÿlJOdzyŒ¬cï~¦¦•pdãæµÍŸí­}D}ø¶-é×fK‹PîöHdFufÛ¦Lvê$|º…ÍNâ\‘ •‹›‹›>-"íÑI‰\&PŒDNtë‚XV•-`ü¼+4¼Õ9ãŒêUGÜÖ,ÌÁ"%˜¹7è§Œ’Nß•ÏÙʵgR=Ÿp›oFšX2 Té°ý`xqµE¥L/Â2¢’©mØpöóµaî‰.FvxF›h‘V"±:È"—S; à8UnÅlÀàê0P°â’H“‡èß!q§ÎLJ:Èñ™Aa"Tðò]™¿T ×<^'—ÓlEË|)‘ÜÎwœÜº<‘[Å‹¹´™Gœ0¬HYc"Ì<Ý!î5´îmMæ}E§½àÕ Xý?îXcÉ“m¹ ÐGŽøú¢XZúã^ž5ÆùñxQ787%“X” ß¶NLr—¥ U&hÝ£“"$Ö¸Ë zÂÂü9› éµsWóü;Ž.³TŠª®…ãgôë»·K»#oyÂË8üÒ-ÜØéÁöÕ÷W{QjQMb‹Ï¢¾žwvÓ¿dn9ø-ˆ¬%cœÚ[9,‘¦ê8ó©,Þ.>:Ç$™2•fc4­¢Mv:J-—å>_Óq #Þ±•,YÎ2L€ —' “rƒÄ0ü‚£qœîÂ?4’>hõ@cî¾ fäÁ—ÃÉp£:2"ð½ÁQæÓ{{+74a.U©§*”lí·N6;¶Ï:É,Lí/ ØêTà­nUTÉŠI, 3°6ibíàGlEd&ï’ÒG'››ÍÎÜ-RΨ,25?MçwÃhÛ!f™ÑqÀ!àáé³êöÞ©©¦…Ñ1rÿê<Œ¦f¿B‘¢t€Rú‰,%äÛ†’+¥™j<°¶Ÿ Z¹õ7Ð* »oüƒ'ãù«W·™YäP:ìnbKût¯ú+Shñá¿”~è©¡ p°ÅýÿÙ QQG ? ¥Â`© ¡íª ¡í½Õ‡…hJGxàýDcöª )ž¢HwîÍ…Lj[W=ÌÑòœÇÿ}þ˜”Ÿ8öæß•¸F±äï†Ç–tÅÒd“R†rš//‹Š^Ó×2ÜRq’ÜÄ2{‹3¸ó’Q¹¶ û|A H]ãK0Ib*nC:ù‹e¹^´Q¦Tã''WðädmÓ¶v&$)‘‘$‘)e‡5ŽŒ/Jh2òÔ/Ú*#*I¦gG‘SƒkÏlŒ™b“¦¨Ú£Pm†ÓäÇäGÿ¦>ÞgMË´éŒ×h»vÆñ¹ Á‰aÇ›)–y²¥H¡2jFe¹PO…FåTerqrJ¥»·v ×mÂúøvõ›þåG‘3eIt¨]VPŒnàéðç—¾S—Ÿ8ÊÔ¢šr•ñ=›êqwÉöÉcKÈÚrxu8¢9? ü.VÄøŠæuI£Í†ë\y'šÁø’jð>$nÇhlåÜ1ãhÇÔF뉂ÈXù]@ºûxÖs—•Åd_‹ÕíÙtQnv–¬ìé¢Å|™ðVÊŽ¨s#i Mbލ±Ùƒ+\q®wÃkSë'Ô-»”ÉV•)¼WnnX•aŽA)}MçÑÕéÞh•ÇÔ8ija`yW[»å¡ñwº“¹oe)N“7tŠ$†,̘àŒZ8’Y^öUÃg¹œ‘äM*)5ï=þa¼@Ü2Àö äÿZ£s-õ7?4¼YÎqîì#ŒdäDÑØäºdÌÍ+²¤j£hõ¬+å^ÌÞ5»¹TzO›;‘_†ù‘A7ªß6-½ïkîLïp†i1ß@Í•fV•dw!ºR#8O! ÍcTÙºL­§ê\”£/.µâω…,™™Y˜e“=¢1JÝI0YŠAæE¨K÷U©´è“¢Å ÇÊeØ^HV4WÇx´ ZU7Œ~«)A¤Ö To¼ñ£7o“(g¿ö¢ñ£ò,ªÞ^$ª£Æ¸©&»È­Ç2,Lþ¼°}KÆÝ¶ü‰‘bÈ&'’(¥Ë\y8d‘5\øÕ•¬U2#ûTe5Gä×Ú§ÕËŠ#¬)ШD.J ÈÆCÅ#—ƳÚÛ¡•ž<”œÅö‹)Þ aÆW ç­îMŠóù€»•NÇÝàl¸Ž•¬iìÊÂÜ1…æÅɳu,W6ü#Mz¸x0Oçµ6>âW ÷x ÐÊX«A’–±wÌ ª–[ðö4qiW÷ ñ\c\<b“n ²eÉ“§Q寲¥F‘M¸Ý´­ÏêÒ ¬¯Ô¨Õc¨ûcÜûrUÛv}Çz™Û.XâŠ>œ‚$²é¸Ç•oÓCØ…‹pt‚hi3mxøhv©w,|)UâÉ2tÌù|À•g5ê6v™Ýâ[sÜ“E/uXxZWq‘Ìh²渆ÄþôyTĽ;™¤ú}‹‹ü‹B`ï>ebcÉaf7çhåìóU™Ñ^ôÑ"|Î%pÒ¤EE§ÐV2ßQe@^ã‡>Jªš4}]Ã4}6]JZ_.‡,ÞËêºÛ« ËQſ߱ýÚçÔèÐê  !û»þÞÊøÅþjU¡™Yäd=ûÝÒö§m¶í*æKÖHV7%Pk¹ÔÄqð­âª`Ùžö¿ªõÞ}Ô6|LLhc–'Ÿ(˜ŽŸDy‹É¨þ>4“H”WdÈÝ›&Ü –M&ãRð6¢a¡Nçßrv²9±ã<ÒÕßäO[ôU´99œ‡jJ¬wõÿ“vxZNŒ 1…>gÐ/eøÕa*££'qGMÅ[Ó¿P÷^æÎÌÇÍÄŽŒ `hMô£üOÆœ¤Õw¨ô¸X·FUo1‡qz§¼íýãü›oŒaÃ:c¹’ýi‹Û̃Àq¸µ.Ýqi$_…ҭݲîJXÓö©¤ÄåãV"ċڵLñZ¦øÀýDvàu43Ôÿß›¾“ŸÅ+ ™£å9Ëþóý¥JÇ0^,UÀò¹.Ì«Ús QAÔŠ ºð#‘@R‚A¥N §ˆó\}•yE&uõwÊ÷`:ÂÉÃÅ’YgÛ`ܺ,ŸQ$Ñè4ŒÁzEoÔYJÜü¼êö®(éS^=Y‹‹U«ÊÜv\½“ùV×Ù§/ê–9g˜Æ“¡y‰¾˜Íî9ž~ʵé©R‡£Ï¼žÈÕWq‹cÛ  É>?Ĭžg5ïýˆ¨¸ñƒ"£ôQ£FRLª$é©i6µÏ²‘IæÎ;v!9\rt¥h3Þ%Ûp¦ÄÏˇë´O(—mk¢´b$Xß©b>rx{ªÐHôz|¢­*:ºâ‰lŒ¼©;wnܺS4ørôW5ÇP˜Í²K#$ŽÌÃî´nE#ÐúÛQt”²BîÌEõ…“‹6‰ë°k²³ñ³ æMn©æNô÷Ë×A–gÓcã»Je7ŠeŸ©ªÚÀ¶ž­û ñ­:!~0Šy &Nߟ;Çs±i2¥,ò=€t§9²í÷µ¦e®8En’tÄw.Xš!¡ÿC/nŠØ›30ó5/ÉÊ«+‰£ u tò§» 7܇ÌÁÝ22zrM41»;ôu €±UvAÊü½§NÞÆMêÇ=µ6Òø;n$‘æãä.4 ’Ø&8'šIªcëv_$¨­v½•´™ß˜÷rÆØ#†wéçǸEqË™=”F0Þ]^Auk|j©‹"õÈÅV…r—×I™¹H#‰¢§’2 ]"%¸¨ýSU‹2ª¯â4¯Oòñ[`1Þ¤Pœ\…Ñc~q·Qn…YP÷šÏ¦‹l9$AXÎD±e,|gQò¿è¬õ4кãäbM¿f¦<ÉÖÆÆH³`Õç F¸ÛM¹i'ë ËQÕ¿ ߣôV›huPX(nñ6íÌ£ï‹üä«C2³È ‚hŠOÍâc‘C)·M¶1 ñgì¸w 71£ÀÜgŠ3.D1évM¶€x(½þê– FæXqd}3”” ðnDq÷Š’*s¹nT]\MűÄBIDšYH$ØhIž##~…P¡™—™nÊ蛪¡æòU|Rºû f㸃¶Q)¯Ó¬D$Vfü5n­~:”MqX³Jô÷xvðÇøä¬jÀâIGŽÇ’~(º¯õjYÓ†f±é’ÿºK'EÊg#%dÔÓYcóHºßCVáTÔÓBýé²Ë—&$3Âw©! J)+{$µÒf…3ï9Y¸§o‘21f±Æ89U…ÿ‰©¢5ið©y°x²ÉÙ[›ÆÛ\ ø%÷,IÆl1 òce,hUõpnV5Œ3<¸]k¡L=úŒvýàc(Ê%³—§<¹¤h¡™¼¥URò Z‰ãÀ¯½ÇF‡R½±ÅÑ<ó+½>;3GrytÚtê˜5ôÜXÝyÑ3-ïOJŒ7êŸ"$Ç’ X¯"èA ±€V!¾S¨Þ®NÿYÍb°X“swã”­4xg¬îyÔõlÿ”pç5Éî}ýŽV=§˜ÈtÞ<¦[ć?eq~4{¿…˜æñ.KâíÉ‘¼¬Ó$8‡KFu5ãfjeöñ¯e5¡áF{±OOô÷{Ê›¶¾œwËŽã ¸å&„ƒòÜ0Z4tÁᙪzeX%ÀPÎgoªÖ]g²F5ÔÚ-ÊÕJbi¡!Û{„¹½ãºâÁ—ÔÄÚçÈúŒpËÑšQå#RÇ‘e%Šù—ˆ±®“ñ-ßáþç÷kŸS§CÚ‚B€ƒïoûc3ãùÉV†eg‘ŽwÆÿºì]±.ãµ@¹‰,h©pªÄÝŠŽ~ÊÞ(Á™×i÷Ǩ}×Ý˵åeÁ‰@Ó2˜Ì1ÃÒùXêüBtñçI:Š5½…÷‰ÕJ³ÈnD7V±µÇÆ‘uÅíÇX¨¨¡'Bõ÷ôГÎF ‘LsøñÿXT@÷¼OºïÐEYåÛ¡c)bmúǗƱžhùnSÜ’_–?i”¯oï;TÙ°n±Ã†Ù›YÇ"ªS2–ê”®ƒuà‡°^®¤žGÑÎÛ«®¤NÏÜ‘mÍÜÛ¾ƒ?SæÇ,c eµ6– ZÞÛU'othÕQ0»(4ÑrÞû—k™fÈ;¾ç¸cgÃ×¼Œ®«øi ¬šìMýר±iEQ*"×®ú½ƒ <ݶ-Æ|lø2r#U™!q èñ0N× ¥XËááUôÚö-{‚­JR”[µNÜ{†òFf€ÄglvrŠÙ†.ƒZêu T’æÀñª¬ñ<þÅ ±”²L7Î÷î˸sö͵·i6œYbLyáKE–»(úÝšÌ×øWKŠyb}•«ÞªÝ yYçyw"aN¸{\ù0íÙÙÐE6TF,™fкž@²2ôÒO[ˆnf®Ñx¶Ëòì»?fŸ8º»|˜X™ŸÊz'ÖÈ—]HŠI<Ës«ÝzçQ’•YÃÈ‚Ÿ"‡Ê•;J2M›&Ùƒõ­1ÌH3VP¡ã"CxôÿJýõ7´8z¢‡¼¬å_KeJ9í®­&v"rÕ2ßÙÀú+HžŒh£/Ò^0*¼/ì«£åošgmá&\›¾iÅè€1 Ô!YŸ¦Í£ê2)ò€™ðåZ$cÅãZ¸›¹*S-+ž¹ì­vm@Xù¬.@8îÌÔËågLirí½†Ü=¹³ì“àà ÍÊñ0‹:b0ãi%R-Ð!ßÊ8üÜkÅ—n­OÔßY‹{ ·TÎw,Ôúl4—3 côÀ*¢$N]¸Édó:ü}•Ö²9¢ÔRU4oN³rgíÅ-»mÙQD¬àƶL~n 'à›ÛÇ]xfjž˜áôáÉÉéÊžAÔ?ÁŸTiç‰HR¡tØUKJ’{cÇ/|OÕL|¹¡¦>kÄÐåcÃÇT*]OYÏÌo/Ëã]Z–ŸðÿsûµÏ©¾‡µ‚€‚ïƒnÖÍ?ìÏJ´3+<Œî"Äs­ŒE&ÇÛ²åÑ2£d(/¥yE¾4 ¨ö"EPŠå•ªÔ¥T*(Ií¨y5…A"˜ßÇÃÌ* )^¯wß¶gnÛte6Vå‡ ñÄÂ2X‚ó¥O\ê®5§´ðïtÉO—ëUm¢Tö38ím«pNáÈí¿¯8Òç`¶'^EL1È&.­Ê_W”‹XòozQÝJžå‹*r¥hE÷–.æÏ_˜rµRäS£®=‡Rä%bv’¬ä«îCÅöò¬h|mp:ÊÌÜe٥٠ɓ*f›<$²Zq¦5¨]>wãu]Nˆôìu%nÊ‚N©ºã™›̓$"i_9 ²§ã´I,ÃN¶üM!šÖ¹§¨Í£Õ§J¨º,ÅñS?* ˜·’LwDcgF [©ÖÚM‰µê7ëµÖ1ÚûD·L¼¼ Ï!‘q£h¡.ÅšÏ+ÊKãw·ÙIM¼Êò9~ªŠyÇâT2‡•¿h‘Õlˆ¿üOý°þÉ­bz1ù%úY ìxY3bI“ÄrÊ-ðRA?eiÙòœˆ·SMì8³äÙ÷a|x©*Ë:É9?N6Q3[ƒ`B“ãtkÀÞíI)Æ1×ÞO´CkÛ·\\œù!8ÿK6Zõ¼¬Uá‘#eSbu0áz—“<þú¹“[uDZáS;ÖN~>„Ç~£5‘Pš×( o —¼Õð/·íÙ8̲àÇ›¼E­#ÅE†±ºb`®A¾­bÜHéöá+s{wI'îÁèðøÔ’=7Ø·ÐãÉ}œÂDŒ¥™Û©Ô%Ç>/ì©eø^¬iG„V½½õÄËñ¶ŽÙÂÇØò5I4óMÂ2,O~-hø°txW-Ô¶ŸgËkcÝ]½Ä6ãŽ8^\\^ŒÐËôï–œž«|¯Ÿ0·jÆ%,É(GÙûTÑ=/ÃuíÖž\‰YîXR.§¿Ç_Ì|kFw[÷?¦½ ÙH­6µyKFàtEâ^0úÞú¦¨×AîÂû3z‹¹¬Jé¹FeY“c.;ÝnòäD ¤2üŠ8!n?5nd³.©ïÓÍXjth{PHPÏQ%vvàçÀÁùò#hfVy†4÷µoCSNCÀÔ¢±½H£ *€ê÷¨´$ò ‘L~3ÇáæjYõjíl¾ìšnåÜ÷ ,¡Çd\XZLn¢ÆN©XC2†P›ÛE00»D›m” —;lîM­!Þ'ÀÃÈ– w ¹¢Y›WFwº7üFÖŠž6>!ædäã5‹ÚÓ!aËþyÅ·çKõø£é±¡ÆÈ|†”*"hT×™fà µ g%´²vî×ê&ë&,¸’œí©³ß{Í.&ž)NV)~¬ª¬zgSŸuAX[j„lɺ nà¶áÑŠòÉ!–BŒ—X«{•­Æ¹äšt>_«ú‘»IËuP†\ÑãÀÙ. B "[Q.Á÷š.ÃͳkÔ’ŠÀmüÚæë„çÌ~,|Xòå´îþÝþ¯ÐÜåàF±`Šzâůbü§Ÿæ¨Ã´•Ó“ü_¾Fñ1Ž@¨ÈÅs⌠?0o]ËRÇqÓ¿†î×>¦úÔ ¥ê»hì ѽ‡ÿºŠ´µó¹‘‘홚•nkvŒS,³j€HÃ%ê@ê7`¡"Áü(ò¡’uª  Õ„Šâ›äF‹€T½Z—·fÜáÁÜ÷¹ö쉡›ý×&22ÇU5:ygU[~ʉdsÞ”)I.ÙÝ17v-Û{ÎËŸb›#Æhuǃ®í  ŠókÜžG¢H£Û¹K°½m~¢öækɳýâ%2éš…Š»=ÛS¸Ò 5[Ÿ mQ¹-JËŸf &ûŠÑÅíÞ÷Bûdrìó6lX9“džŠs›~ŠÜOk…]½èŽÏÖpœ Mq r.%R/n<ê‡/ ú¨†‚\˜¾•CÂdaüKU¾aÃP°òý•-#ß”*6›3&%‹„dÊdˆ]>r5q¸±%¿…JEãoˆä>S#Á!M# ™WQR±ñQæªtßY"ê b[2ñd–V"À~'ÃÝYëRpÛdhÊ‘cà|EJtf¶¯mu)‰ƒ‘>ý$Kªx$V“À-¹Üòñ®˜½O¤õ°ç£F­²öpÈ©ÑųX¨I“~Zxñ®˜[o³ÄøÛ÷fÿÙ/¸³ål]Ù™™ÖΉ.B±2D²š…@#£¶û¼Q…ØÝºêã'/Òëà4Þw#Ã>.Fé„’mðN¸Ø«“ ]®J„S¨¶¯ ‰A¤òñ7àqù½i8KleÙ–8”.Û—:9;¿VLÊ',E~©Vˆñ ßU¯\SUGé<˜©E¦›]Ç»ÎÄÝ…—Ìñ®N¶9æoÇ´ì ÀX7'—…dŽ0òGË÷šW¥{t˜›C4[>VÝ4½G91JdÄM¿Gêêö[…\õ-®ãFì;åeÆsZQŽì_U*ÑuaáÓbͬ5¸òµV†…èdÆ" Æámöڵ܊íýOÜþíbk¡íA` )ž±¾M÷†ö}7ÿuikæ)så0¯8 ×C9ËVp qøT%àËSn4¸òǶ„‹®Rûh×${j ;@øÔuõ<9ÜÔÆß.¬¸—ĸ·å¨$§wÄ7Ö,-¶w¤ÊÄrŒR4R¥”:Y€~àC"I#d;O<² ¼Ú؆ÑÄrçí¢ÈÚŠ¢Ý¡‡—¼¤sgî0ãácÆ—.cø¡›ñTèT‚~cqûÌMó¸Õhy]W„®Å7%Eæ+üÇ>M¾½å¾4±D°v-{^²gÉJôœ+äN¨EqæHñÆÎ®¹A!›]ˆä.mÆ¡Œ[­GSDÑL‚HŸæCÈØÜT¬ „Ü]bèИÛvÁ¤ 8LiK‚t`¹áJ¾Óo­½ù™Úàíã€Â€Û•ãS÷ŠcÚGÕÝüÏÄèbáŽì´h?EC3wîk'â*8“z–gP]@j` UçrÀüµ-m­ëwË\F}µ•¼äm‘¾äò+Fù2A†ÁDhf+ª] |Ï s? Òr‹#Ò꜔šµmÿ-vùÓb´ý< ™`1Þ "–XæIŽ¸ÙŽµ<‰ð½ôùmQTPår# Š|vö¥Žt©Ö6×¶glû†tm“ «fÁ¨•›K¢õ^E{]Û™ûjÌkÃåËq]¹Žø×ߨ!‘éÿr÷#Źcm„adådK†Ç>RÓH4ÂÊ©dEó!¨•kQ.óé¸÷®^†õ®'Púu™±n»~Vï<{bE”†VI›-ã1ƒ#Š(Á<‚ð¿‡*á¿Èô6Êå6¶!ÝZs³3rÿG4:T£”êÔÊomW½d°>_“Èß~RMíoNìOKv,]‡x²³’mƲr”N $A} WÊ=Õª>«[^Âõ²¾ dáY'—g¤×©ô€@]-¥µ¹QàtЙ‚H¦N¢ˆx½ù{ê-+akxZßšÕJ—¦+[¿~m›^å.ß>&\’´‘$l‡Z†&E>>ʺ¶Ú*î$è%©ºÆÒ.L?×ö5ÔúlUÏT;Ÿgß{sÚv©šmÇ$CÐ…¢š;ôò#‘¼ÎŠ¢Ê§Æ­4ñ+9¦ŒJ ›¸à 6pö2ŸÓ[U¸Ï»Â-&,ƒò¸ÔRBÓ-lÄQÚol9«°Ð‘e߀ãr>ÊUß—ú_e@;]õ?§QBj,›ìg…ê(MIMƒtIw|XÃqiß–„‰÷®ê6^ÿšàâgΘŠýj³0miba¦ÕW*;¨.5çU»tQ¹î[ÞCä.ß¶nÛösÍqÑ@qáIXcYK(y"–óy¿¥HΧ•ëAIfô®Dêw,AŸ´âàË“¹d<“㳕ÓÅ*lWÌtÜðñ0*LÁ…ÔÔÇ›†N.Nd“ââŒHP, nT Ç÷ˆ&†¼‹‘œëí]ƒ[T޲2áš(£ÅH#³/Í3-­¾…N†·.)F)E'‹íïeºŠ„`ÑìBÄß™©b([ùœßÊѤt>¤e5q,¨òÛŸšü¸Z­» ëÿ[Ѧ¹K"Úî{Ï ©Æ‘ÃKZì¬Wåã{߇³TÙ^’TRtöž‹9vUÔïó0RXßÚyšmµ‹l—’=ówpóâO“ ‰aBH|ˆºTyWÙF±7¹+×Zm94©–ˆÔ;qî;F^Äø¸xâ!›*Mü%>qbZ´yGÒy|›’P½°KæÅ–ÃÄid”ÁVRY(feAfäÃV¬ú-¨î(£‰tÆ4¯;qý5%#º™¿ù·œ€x¢ßûº-äa<Æ‹j¿UÊœLsÎ$?º(H“íØMÎ?º(( ûVÜo|uû/PM$ض¶ÿßjòvÖÖߨGÛJ“A´­·žE‡ä¨©4IÚx|tÈÃì¨ÚqÜÝ“…°`ÍŠìss…˜ÚÀ¥´úh™W›}Nj9FG֦̓b*⥋±òòŸºvä‘CL¼J媲Qyõ²û—wîÊÚööÈÆ0¢™‘'˜â:šÆáó_võÔá%Nâ'yô˺gÏhöݪDÚ£±Ä‹+'™ (2ÜÞõVÎ[ý&þÿåA¨iY/¼NH;ÎK$—žn?ûˆÕ\JeåË?Œ‡‰è¿vp¾VŸö“þ(h¿Ç¯öÇÅý±ú#¿1ümË1âQ$¿EIuþ9{YGâמAý‚•&±è4 SkoüŶVmR¶âÿòéð¬%™¼r¨$(€(€(€(€(€(€(€(€(€( vï$'~ËC"^Ÿ”°øjy^º!‘„ó£½öFϵíY[‚î„+Œ†BÅŇìÛQø^¯Ô¬ Rñ³7Y6ÙóVV;ãÙ›GÚ Ó}\µ)ù‡ç«Ô£G{V~û¸JÑcgjmi‘öø K¿½Óå|iG¼[ý4eïXþl$²ÖþõE Ü$ÙΟÄÙÉž‡ÿó¥áß3ã#«´d-¸›quC‹-¹9r ßh^Œ‘t8”ZÚÕE*Æùr`ÀtË*©<"¬@®ÔøÏŸÕ¬àðãFÉF™Ž-|+šY›G!J‚B€(€(€(€(€(€(€(€(€(€(€î]¶}ÏaÎÁÇ`™ÄD.y  ·ýáV‹£!¢Û=§º´YqnÛZãÃ>¶‘ ±´ŒíkxÝ—EÅùyŸeY²»Im«³âÛòä’,5Dm6³ƒÈ[ÄÔ¦»J¸²F\9Õ¡{V¾ëÖ›‘]¬nñL¤ë‰ÕyÜ«ÑSR("eˆp,÷ð fCâ ܹ2ãl¹’CEùK"£0öp5VZ(¥m˜y›÷lnpÃ&¨&Õˆovº1W*O›IÚ…*Ðó²{wxÛ÷Hþ©X,r Üø C•BFÏŠoŸÞk9æ^9 ÕKPPPPPPPPPPPPPº•O‰Šæï niAûÅNæEÏpíý£?\IñÐE2é}!·¸ŠìmD~ÇØû>Ëɉ&C¶AYeug r 8 ‡"XìvÎÞ³•åW$fHøŠýÄm$á‰bŒF¤/Ÿž&õVêJT;                                                                                                                       ?ÿÙntpsec-1.1.0+dfsg1/docs/pic/boom3a.gif0000644000175000017500000004357413252364117017214 0ustar rlaagerrlaagerGIF89a´÷ÿÿÿÿ!!!)))111999JJJRRRZZZccckkksss{{{„„„”””œœœ¥¥¥­­­µµµ½½½ÆÆÆÎÎÎÞÞÞïïï÷÷÷{ssskkcZZRJJŒ{{B99œ„„!B))R11{11BŒ11RZkRkŒœ!)JZck{„Œœ­½ÆÞç÷ÿJ9)B1!)!kR9R1)!„R¥„ZcBsJZ9„kJ{cBÖ­sÎ¥ksZ9{RÖŒ)­s!ŒZsJB)½œkµ”csR!ÎŒ)µ{!Æ„!cB”c”{RZJ1ŒsJRB)R9Ö”)1!œkJ1œ„ZÆ¥k½œcœs)µ„)­{!½„!kJkZ9œ„RcR19)1)RBœœ”„„{RRJRRBŒŒk„„ZRR!ss)ŒŒBB{{œœÎÎ))99BBRRZZkkss„„””œœ­­½½ÆÆÖÖÞÞçç÷÷ÿÿksJZ!)ZcZ)J)1R9)!!J1B)9!BRJ!cB)ŒZ)œc!„R1Æ{B))­k!”Z)R1{J1½{1΄)µs1{Z!kJ1­s)”csJ„RR1cBB)Z91!!kJZcc)11!))Rkk!{ïïÿJJR))19µRZ„)BRck„”¥µ½ÎÖç÷ÿµœ­1!)¥k„ÿ­ÎŒcs½{”电”{„R9BÆ„œÿ¥Æœ„ŒÎœ­Æ”¥Þ”­ÖŒ¥ŒZkŒs{諒”csJ19÷¥½­s„œcs­Œ”¥„Œ”s{cBJ½”œ{RZÀÀÀ!ùÿ,´@ÿÿ H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠôŸ?@Rª"Ãߎ”?~ñß !ø“ÖÍ›OŸÖL.I´¨Ñ£œ\ÉTe0LJ5é¬çOoݬQ*`]½¯`¿²w`Ò³F)hÊ•*-T´^¹‚¥ª–,X¨x E»ñÀÔ¿þ¢Qãv•Zµ«X«™€ï+>pë\ˆg®¹°_Í}@çÏ€¾ %RPê ¬Sþ@0—.ÝZªLÇ®…ª•É”6-°?hÕ°ýÄ6µl?©™œf­´P­xIW…ªCØË˜-%GnœàËÿ—JU*èð­[‡/A€½¼ã#”@ê(X¦RéjuZW]T®øÃ )©˜R *¯™ŠT $ÀH€ÌT€`€'¶ Xˆ±ÍfZ-°ìÛk ‚Ø¡*¬ö—=äôóÎ>õˆS:ï, x$€1O‚‰»=ÄÀ€Pä‘ äöž80À Àä¾ð@ò5T¶È[‰t©RŠ,§”",°y)[˜®Qg[Q`€0*FÍV>)ö×+º|ˆ¦*#FWW𾦦*¼³Ý¢–¡s:å¼##‡XI –dªé¦šR"I"R4ÁÐ@¨¢ Aª«ªšªDÿà@¤*ë\pc<Ú øS@ „jT}ö™( mšÈ¦³#ÖõšmþX“`Ï ×S7ѨèíTÑtcU6ߦ[+  I¨‰Ë†Ùaš¦tRŽ:î€UïŒäø#B9ô¤Ž=àP€%QrJɪ««&Áªë4pcxÀ€g `«Æø_ZŒò +ƒþ™¦¡hêâÏayvOÝLóíË ³ùn¡°¬2ŠTÒxƒIÕÅŸ´øJh¡Ì }žI%Ø£Ž:3Öc² B€sOgUÁÖP@@W¶ºz°«xˆ€? DP’EQPß\¨¨rŠ+"rH*¨åì 7ÊMÿõŒpÞèù²·¬”笉Ð"Þæi8ëìÏVA™Ä *_ÂRJ+§¸»x]4×â‰)§àŠVõ1ëÂbC€À:©2@ Ä®ÀÀšºè(i¿ª€P|¹]Éx¡˜ã€á)øàR•wÊ+úé"½.¦´ù®¡'ÞmüTKr)©´â `¬´Ò–)¨,ø:âÄÛ:˜¤šØ¨ @çã–À¦¶ÎÀð*»`ùƒmF©€Rú„=Ó0îx;K^ʘ÷­VÐÆ5gšY³÷ÀÁ Æ8 !4ã ZYzÀ |’4` ‹Ÿ |ƒ¨L¥Vëx…À@ ‡À`@“ÿð€È/K©€åWdnC›S/L’³k¨eܿՋP¼¢zÖÛ Ñà²EoÝ‚r!: ûøò„B¤x‰JjŒX¹G*(€I€ð ¯ð‡é"ö‹¹(n{HRÐØÐ1ˆ P„€@* 8€ë0ËEÜæ™"E¯XEâ¢EŠ©&r®ôIËø—ÖÜZ›»[€*H9»xhD ðÇ µ  ÈLæ%·f‘`¡XÅ*NQ—\´@&sŒ] ñø™DÀ: 0¡7"Žh´Ù>ã+ `Еx €Â#ø ÈF6ÿ³Ä¡ÂdþØOº8Ç.@qhYª_)H„&º˜âs‡4—iPÓ‹ØÈ©PW ·eÑæ[Eƒ Ä£ñè#\Üב•˜­"Äìñ¡ÀŽ¥RÓ!”Ô÷D B ˆ€Wàø%þðE„6€á[GCJ«ÐÖ” >:FbE I,‚™„ÿ l E¡wtµÏþö»ÿýð¿üçOÿúÛ"%¡¤J~ ƒV`ðU0̤%JT´~÷·€ è <€KÑØ;`C@ŠÇ,£>aEïÑ€"8‚ ¡þ0C)Á:P/£7ˆ'‰1!—Ž‘$È`^ÿ „å!BW‚ ­´Æàò!~q'Ù  ÈP…Wq Íá €ÁLóù ?àíØaƒø[9èÆÄ'ªÀ@tA-¡` É¥ ­@7„µñE´1|¡:àú>ê‚K òÀ(`ãp\x ÿ €eh† ð §  Rç=–Szo¤ ¡ ¼°&Bc(§dp‚£bϰ<ÞÒ Ê5(ï’&D˜9ˆt‘aaûàó€áàûÀ458#î°ð@&Q“)”°¥h uxp:D'lÐ àCP8±ˆÃëDµÖ R!uÆÖDòŒm3 !Ó,œ³9„‚Au€B-þp ÝÀ ŵÿÀ5öÔ5´'ϰ ˆÁ ôÝ3W²°.…RºÐŠó(-¦YøYhûf—‘@ˆp`ä÷®—yÁ~–P äg ‚Ð !n°Rdc\Ó*¾B5 ÿ?`m `m¹¿ó F1á„,¯H óŽNõT¦P(8á ÖòZÄ<_ô%P¥p”ÉC.«ØKOÅ9eW&’"ép‘äP3¢‘ð™þ@å€5 t§bKÖ*K$¥Rt À•òƒ*·<•Wâ%P$E<ƒ}3ΰ2{S•ûUJÙóKü„ˆÄ3Ùð {òÒ2‘ì~²!@¢S5ªé÷- B ÐI¸3 ã\Ž8ļF+%1cð;dHPSaA”A­ •{£Š#K”YF€EÍÂOÐ2"ˆ ÛÑÀ Ùÿ0B€ñ‚ ¥à ·àé™9§Ñ Ôá Ç'z á:¢À:©o0€?\x#p¸,#@à,xÈ1Ç@ÜiÑ€B¶Iƃmƒ,¼0"t>Ch P±ð .xAqì|׊}<ÀzM)aŒŠ€óÚƒ‚C#?¿¼Ø MJ¡M¡‚,¨"ØÒ°ÝP Ð^Á(6ÈÇÚ1Œ—° ¡ÐE»p3H(‚MAÑ Üp ×°¢É2ØÛù P—aÐ=9mÿ 1³l†»•Àëq—艭}ÉÉ[À{¼` œÀI*=MŽ ·0‡!3(y1ì8J:8ë‰NuuJ£ ²ˆ¥×ƒáö#ê 4ö€¼pa¡ûУà1R¡–@ ‰°ê A:dkð¾B…cRñ Wòù„S XÐ%² ¥p oD ­° wžN«;«yYÇ.ôaÐ`X¡ ÚK‘.Ï2‚N4¡X(²  j‰‘äPä@ÜAò#_éð5‰ ’@ žB›ÿ·)öl,u¤Ï@¦Ï5ºw™0 :‡L ÇËÆ @ç Ñ+G.ñXP$F‹ã°± Èà³.'Â%Vø-(#¯à •Å,b ªë¸F±! ðL£æ>ÎsOû hšâGP‚@ÏôœÔŠ~ŠÈB\âÕÂ4I?°Cc3déÇ H?_¹"늳zõ÷ؘÉñ a/Èpµß Ú€ÜX!ñù9Kåq"w “#²i" ¡ 4™ä@÷B%T®W€ÛÅw%pF-w²,n°®þx½[¥óK‡,¢?Ëÿ¬ƒ1Áð«JÑà#¡Ñ‘²‰32yu"Ôò”ÐJ’Ù Ü@­/s”RõáÙºŠa °j LE –ªƒUÕ‚eªS9uõ$Ö#7Ñ\ þÀ¹K D€þ$YÒäI”)GR8Ð B/c΄)3BÐ`AxY¦þ¨dÚ”)…¯J™¢Z aB„YÖBE*ã³jÞ¸q£–Ѭ³kÞÄJ3ÛÖm[SX·.¼ºê•[jØÞúã…J«À«ªZŒÐªÀTþØ«Ñ=wêÔ]ô'¢Ü;{þ¼»§Q¤SРY:p0@±£4UÝÀ_°uø1!©ÿ¿¡u«¤  ×BX j%®+cØnÕÜ:³¦ví^èmY™bx8«`¬ O5[íš¿PSQ™*ÕJWp­ÖfŽØSÛáDØ 9‚ŠÁ €àónKª ‚ÿFª@˜8 ¦š^j€Õ*£"8@±Ô‚Qü L!ôäRÅ+iœ+«-´ÔêfšèZôg:áØ».»ÃV9+šg¨ñÆ·Hñ 0ª².;öò„ºQ&ÐpI’¨pµÕ À ^Z°&bz`€™p‚„ j€€ž”`6`˜ °‚ÞjEa)ÅŸ½éæD³žIë9÷ZåǹäjÏ0Da±ÿ1£h¶Ùæ™Fw4KT`A•‚P! ½ö„뤕OT9e Üt“E‰Í&™¦|i4ØiV pP   €mÔ`à4 PÒÔ7åCàTiÅŸhÔú/µ¶‰&P·B1•S⊱ÐôdHQ·Jä‘ÚmÖu« ]XÉè”Jk å”StéÐ-t(`ÖMð‡VŒÀ4 fUxá…+( ü9 b€a¥"ÎíßÝàôÇ¿ 2¥ í¨õF/lÕRŽÛnU %£TZùpHâ¬ChQ·¢ÉÆm¶”RbAEh¢/Í÷=˜Uù$e9f| Á¬W@Pÿ (˜êÓiá Àµ·FI ³˜6à ¼Æ€þÑØéÐ*X €óÆ]´Z”S^™å¤Q¡å®#ĽnHšµÂù-hªÁÆ9粡fƽɦhÀ3åoàfrrˆHx@ù~ÅÖ€a2P€€u47ªŒÌΪiý$ ñ?÷æÛ,zk餔é:ŒæêÐýÝÅTjq%epK|ø\t(ªú2ƒ 2˜UƒV×(×Gé•ufµ #2”¾Y³B …•Qt9…S„îÄóä½èȾ[žYˆ-ìÐ DÒ*à»’ŠÌO7Õ.<÷¹0@ J@œÚÿñíDƒèÞÀ6( ÃhŠm9@I ‚ð4øÜç^Xà$>¹ÉÙJŤ @ÀϪS{Jææìé-aT (¤õœë*íaˆ+˜ŠS¨Â/ÆžA³€Phñ¯¸àç`  À6ð ¨Tº$ŒqBã} p‹qçp É`èÀ@@è –´I@Iìû‡ ´!$Êa$¸PTmð)#)â[œØ–D!Ï0°°"óJ.À$$1 x¤MR‘ÌÆ¤ˆ/nPC Ô@òG ŽàHfÀò3:ƒxR4”)F›T@–ÀÖfu5ÍÿÑÎ,ˆ‹a6u(U¬ð8jѤ?šã -ïÇ#—ˆSÊŒ„QS´ E±“@X|b±ƒe@€ÄɃþø¥[½ù*éa)x€Ë(ej̈K—‘ãùÑ Va¦wɈHR°í¨R×0…EìÒÄ çþùCY€Š“/lÔ‰ôG! ‘E-Œ3No”“e¯è¦ˆðIœþ'!°0Î'4e3ºLuZf9Â’à†* á n(C ˆ†*Tá h(AW3B€V6Å5 ¨S.lxnuT ]Õ €©ãÒBPῌ„Ä$Áj’º ÿuX@KYPœj( @Žp…B.…J´ÔCÚ„Ük ¹K>t'O\e;@­"x´‚ŠV¤ŸøDpj‘ŠóÈB hhZ«`„!L¡ `CJ€7 Aºn€CZÑp„dÖ)ðÇBƒã[Üâf$ÆðÇ^¡€Ô4K¦YÅTÕES4±­t€ÄX•Þ¢¨î¾2YG»Žaä#!i š‘!ÄáEâªS*C)#vÊëW1žˆÈE‚Y1Å-¼‹ž¸Œj£/ÞVh >RE/®°„¯¾á ]¨Bà`„ !Æu BZÁà7œaC(o\OR_ À²kÿ2ËÆÈ›ô²f¿ ×T¦ÂL„Ä·ØÒ|S⫌` ó:8,ÖöÁ„( ` 3êJÓÁ ^pƒûLÝ?wÁ \°Â¾€"èÖ½)ªP¡šž­šF°(%i‘,¼Á LBÀàV •X™/ji"è˜ LØ3’„2œ ^põ[‡*٠̵Â/`äÅà¾1AJntØd¬c6Q.XJS^íE)NZ4…?  € À~etø]Y–AÐ`Q  @»*Óü\®ÖÁX¨Ã€¼Ü*LAßm°ºu WÐÂ'¸ÿ¼ä„ƒ¿%g¨Â˜ð†2ÁÞo`õ§÷ŠS$ÔCk…WùŒ&0 p`‚Œ+\ /ü pû•dó‡°#°^®TÆ3À—Ø$(x]¹d0ƒÅÉ ’Î5±€ÛH,43Ë‚~`…l9\’ǦÐg8ÜÛGX‚ølö°ƒu L@ùŒP‡)p! öÇæPàâ·È¢¢>ŠCL–€Ä#ˆÐcÁìnX{ÞþxVƒµþ0Ä#,oyH(¢-M೦pO! g0KDq¢À %Ýà0uÞÓÀ`Rfé0EÁ€àÞ4Ao@lÊÿ“<ì¾ë0€Ѐ p®-ˆ€S&ÀK1¥•5ØôÚ±8¡ p»Ÿ?=@ƒáíqH»hÝ„&ðÛIƒ§W0p LXBý‰¬G :â7‚ÙùÌ„;`‚ãj‚*h9€LÀŒ‚”‚–óc WR‰ç³¶à+õ’ œ€˜=,ÑÀû è—`€øY€}: Y€Îb6Ǻ9(«Ë6P ª3‹:z  €¨ ì¯Ì  9íë>@ó+¨;?«&Ø.0ƒ*(ƒ% =³8ƒ;*03ƒ&ø"3 Dh„F`Fð·@¸„B.pƒ/0‚ÿ2(ƒà¼'ì‚x‚5€-·(ˆ‚5¸C%°ƒ* ¹/`‚B,Ä8X‚7ÐBFL‚/èBÆC¿X· 2€ :–b¬–h@€˜ L#˜£†° ’Ø ³ Y€¯°´‰¦#X3` X€ é@*¡,_\‡€ B´î ;‹s‹!90ØB#¸ƒ/ŒŽ é2H1dýs‹\c‚&@ƒ°²‚)Ø>ðš p`,P@w\?üCy¼CT9x)|ÔÇ|ÄÇvDÀ1ÐGŒðèê"X;H™# î²¶`)£h) Q ³À@ÿb›Å`‰*™‰Ó 0I ŦŒ€Œ â[¾±›ŸS€ 8E ˜x€¹Q¨.ÚìÛ>@ »&ø$³Èc-€‚;t‚ ¦$ƒ£üC(p‚ô#à‚)à*)ä­tƒ&(ƒ&À"(ƒ) 8#È2àƒ'$p $‚1xGzT@9ºÄ¯ 9>+,XÈ’hHARœ“L¬ûâÅŽT/™ðDcØ ^ÊŒà@›ôAH2]ÑW¼>¦p‚òɰ«‚ÉcXÔ0 `Ȉ'@J(‚½(‚,ˆG?dÀ0˜Ê·('ƒ1p'˜ƒÿ,ðM1Ȉ3X50+Qà²ÔQ€38ƒ-04=»³‚$øB»{»àÅ™Z’4*„G„HP„H¼GÀ¿ò|„CH„B0 ³¸Ì®ÉƒBLˆ´ÿ*€âkC2{ðË”@€M·I¸Å†u0ÐÝÐ)P3ÍÈ’u Ä#¨×„GxôÜÇyŒ Q ½ÍÕPy¼M(³À5³‹ƒ¶™@”X½Bˆ„HKxʃJxCx¹Æj“`A¨K@³x„ÁY¦…›ç‹VÐÙýœØ@œhÀX±Ø=±ÐxÏzp'9›umZ´å˜¤Y’•¡èˆ†‚U‘l Ú—õ˜¼~¯´íÛI 6YÐ$èÈZçà†lÈ$£­†‰¨¼•\~…ò[ š€Ì‘ÿüO ,VîɈ¨-Ü@ÁØ´ ‹¸ »ˆv\Öm‡v8ËÄ\ÍYªIyi…ïÒŽQ 3c•Ðÿ‘i˜nð†k˜†i0Ú¢U]kˉ ¾]Z€¼•sàÏŠ¥Ý§éK-]°ªá°Š®è€_ tc)  =€Êi j(ÞÈIª¸µkX™WpÞzà‡sXÀ€`‰p]‹¸KÔÞ IJ2,]H…A…êY¤Õl¦Ä‘†Å™ßȱ†ipY 6^û5(ý­‡°Ö ˆ‡Š à>`ÆS`üxZ…¬@…æ”à˜–-û²‘ŸW½€®½† Ɔi ®ý`ƒ­ß!€|À|Xpà‡È%‡q•–€¨s`Š`‰H€yá“ W¤”ÿRHÅ®'¹Ð”"ñ ñ"›~U× ÆIbjІơ'þãjx†úi8‡é•\yh‡0¦ˆ8áYÉE¶¢ˆÜz@‡26ã’x…º™¥…S(Wð-ßú‹R08Ü«¹ìmU!âjXbÆÁÊ©P™K¹”ƒ‡Cãé ->á °\c‰ÌÄ:FL Wí0‹TpoJÑÑš"v @cvÕ´)ÌwÑ…o¹DINcJæe×x€˜täšà(€B°Ñ$u„p•øo¤£½ ÔÀu¸‹â£(.AßY„`€H akºcpU …(±†’æÿ#Ð]òð¦Á™"D ¸\7If³ˆ†hÀc•٠ݲ™R†…ûõ0«sˆ¼Ur(‡t؇E's;ˆ„A'MÔ'­„E8 ”ð'6L„‰Ù€ˆ“T4 àAoó6 þ8Ì`‘˜‹1ÕÞÈ*"ÙÝÅÆŠÁÉŽ©J cÜüpg¸ó<‹ -Z1Rð„ý)fW…T‹@‡oŒz(‡Dw2hR„#HKÿvK`¾5 šk³¯‘2šP šh©™P>á à..€ U„†Ô ð8¥ Š¢DÉ*¥Î_ð]Xz Š2 hp”±È†x} É›ÿ¶ $*Á1”r€t@t‰xr˜ @‚B¯qè#OR="H"­ôDe„…&‰I½€ººt?:,17†gÝbæC$ $›¥¸S €#Èõ¿ˆGËð¡JqXh…Q8‚j‹‚å†m‘³9Ë¡l5€9[>Ö`‰ïŽlp ÝŲö®­Ða­(…x0ôEöbH‡2éž#(„B ‚»GIØûFȈº·{"`„@w<«oÔÀD‰¬®1‹Ff˜‰R À¦‡Q:’¿pî¤ôã¹ ]8‚Aޤz†‡ß$gÈ«A†Ö‡ù=™ÒbAΈ²§*K%XÈÜ? ÿŒ~ˆJ~‡Év‰HŒ €rpúXÂTæW$™EVE‰‰ŠŸœO|'¿ ÜN˜†} {7˜ab/jßOi]wñ´x©g¥Ê`=ñ™O‚[µû¶0W ”f¯Qf&…ˆPþÊ©«W\=uë™ó‡¤¸tJøógÀ¿Œ7rìèñã? D(‚É“&Qž\L˜ˆÌ€„ $¨”€ÀßB‡zPñ(R¾TÁRU‹)SXO™>…‹”¿WþªyëJ-©³k]»u“–ôlÖ¬ªÖª²¥m4oØÐVLU«©ÔªUQµreÕiÕ¦½\hР¹þÀÿ•£—Ž€;{(­l¹‚+U–dÉRÂ* ð€çÉë @X]ÒgÅ –gw¬`{B€W¦j=½;õ®Ó¦À›šÒU‘«×¤Öºzã¶Í,ݤ¥¬úÆ›·Ô¨¤Ñº]CkopÀÔaµ:<+ö’Sgœ;ÄK Sçnñc‹hóçX„<=0`gœqÖ@®¥Ô€b €L@PýY˜Q  TPåEUT­lÅ\5I=óŒXÜp]tg2\UQUUÊ[M“Í39“c*ÔÅØ¡TãÉè¡*é `Ï;fEg• a]ØŸ0 €¢ô%g`šÄÿ€ÆL ‰?L@E LÉ_DЉx²¬äZér‹?ÓxÖrɵ-à '¢«¼ 7Ö×]*¨àÅ(¨Äœž˜BåÉQØß;ꤣØ"¤c÷(æR¾ù& ô€˜_jf­?©Éf…¯ÎAQå UŒà7¢7_%…\s,zÖtR`T™âËYÔtWQ(¦¤âO+éE$uÖ" o©°ÂÊ[P@ ìg¥¼Tàê«°¶/¿ûÚF€ûUÀë°NJ,9BJ¯™Bš(P‘¾Î¶Î(áé^S¨¸r\WŽ‚%V³ÏÒÅÊn˜ž×ÇL’ÿ­7"Å‹¥Ö§1^À9µ S¡L`1m 8XE}´†,À™0€èÀ,½Ö4€LR$s 4QäF,,––b®Ë {³-RÈu3Ét™¹ã®ürEÒ¼½ŽG½BJ¹¨€B³ŒzK+­/Óùò3Ù–±™€Õ Ÿ4€f&u)çœ? a Ó'¸€ C±l‘ƒdvE´œr,(™6ÕŠ3Ε5ÖÊÒj*\±ÄFÅ·?~w×{6Ðp{wÇ©Ð|¢âwŠ*®¤bJ'¸>Ä XíÙI °´€$°¾ú (ð¾ t®/©?ÐØ*IÿÛ÷¯¯zTÆ„C•»¬"y]ÛQzוýî(¬(o„µ8ãM0HÈ3àòbv”PœÂ²ë‹uJ=—Ý)¬X ,²ã½þUFhþ`OÈÌE`À@Lr“ (ÆXß p’Œ$bÀ zÅBÿ …Ît‹Rø#.r9 ŠØÀŠô–â¡n†3 ]°OÚp£Îx–T¨¨0Ezür—^°¢§Eyh¡˜W¬p‰Da¾ dô]@‡†4d„˜Ä$Q(?ÄÖù‘#fó„S@d¾Å…ÊBŠØÀR âX…¤¬R®U²m•e„†6¶ŠDƒÿ­àT‹B¡‹»…"Ù9‹((SÉ¡ "0ÜÌI€¼äÎ@"%ÄÌ€ Áè$i‘>3#fÜ]P¡ Sô…ŠU¼¢‰°¡Å¶‚£Ð%.ýÁ!ÛQ°(c·¡ jÌÒ½PE,bqŠT¤¢M)Ý8»ôÈS+f7…²Äà(âC J RÈÀIöu’u<@ÈPÀÓDŒPÍ4 P4DÉnV`Y1¨.‚ƒŠžQ 7DÙÀUÀ©ÐE-$Ž F÷¤‹eé@]‚­¨êUyñKi8o: àfD=b}ô3¨ˆP‘c5ÿ`€d²8`PÐlêTM(¦?À€À4¬ù¦.ª÷ÔsîŽwël “ªÇ‚¶,ŒhcÙbÑTod£že4<+Újp£³Ð8E,>ÈÖ#x€Àƒã@Êp€¨µEpÀ€è˜P`¯xÀ?°°¶C6M,fƒÃÉ·iö± <ªVŽÚ!½ä¬•Ò4ª‘ æ·+Øð¬ó*‚ZV¬¶¹1† ʪœ&ò pº`@Hc‚¸l2{AR6½‚žmclu½!Õ-…”ªàE 9¦X=˜ÂÑac(Ñ'w9ˆ-ÿd Ôhh5€A—¤V’—d@ÿÕI0à€u¬c9´@E@@âÚðêTå*mÑ7t:6¨®)yáVpqMíð“‘ÂÆZ°bCÖÈON[3£MªH!s˜¼Ä|8"RÁ4ðŠUñ‰(¼ÜMØù#X°0(B€œâID(ò$Õéä,Wd„ªÞ¥„c 0Úl´J+"ð嬃½ b«td®$÷=Jša’ ØkX¦8‘Ô=‰)€ÀF °°`±åA „¡ç%òYp§pãT‚ÄÓÜqC¨y†:WT黵ÂÕúKw­œY_š)ºxrIÿüü¢8ulk0 Ô$`UÆ0JŸ9ª\`b@1Ÿ‚ñ"ª ÂœkÕHTЃÙúã;И{à‚ `„ÀX€ùl˜°¶?xÀQÌs.[Åœ¹ëÆ'mëf™ÊÓ:¹p<–åíYåÛ"·Ø‹Vˆ9¶10FNSƒPî¾(¤4€p@_ðËͶ}€Œ<€?Ë Ò= @ ›K[Ç~€f|¢xÙ¾`îd&r’9ÛÃ^|4‘Pþ±-¶¢`H¸œÐ ¼ ÁPüƒ C¯°Ápà.ù‡zÂÀá[d;0\zrèà…ÀÀ‚°€ÿŒ‚ÀÉ«b…vaÁHÊ:òìYÂ"¨,{‚t—e1»v“]:H°8VܬW^Ä‚QGqЦ>@1­ÈÏoK p€¨°So¸É£,}†¿nL40H`ÄÂŒn¬Â½Q`R“Ò2½V¯£9‹Â§(E²õreXÌ-¡X…YÙÆoå¾6¶ù¿mXÌ~<€0\‰¥µ/àŠ (À:œÚàE€0 EZ-ÀI]–U¶È5Q@]€„DN(Æ(ü’/èÉ_G- È ÚQìˆX„Üï„_& ¦Jý é},Èž_(€«ØP–üR`‰÷ñÿG}‡o B. àÁÔRL@§8€`A€0”€¹$ò†4À €ÀjHª™xYPÀD Ø‹Å)¡'üHê*ÐBܨŸêÍMéFÍÜ`Î4•Uø¡?Yo )ø Ë8SðŸFmIÅ$A ÁÜQ@V† ˆÈ*pº¥[$II Jœ—´†è€ ¼‚ŒH_TðÔ¿iD$ÑY€Î—øD€/BÑŠˆÎ^UœeD€¼š?Ô‘·Ü`&IqÇ[,‡ŠÐ?<4@ƒ3ÔyàIp϶M(ŒÇíuÛÊpÌ·À/UÄÌ£&‚TÿÀ A§0£(‚Ë †“.D œX (j¬„"_—Ãð€ Œ#yE”tÄ"µ¢j訄mÉ2kˆús͆(!+4ÞF­½‚38ƒVÐB+$–,|Ë+hOVhÅ[¼ÂtPПqÛPÅSÙÅ]ðI*ÈFÅ'´B*yÈ·Ðã&VAA &VAœA à£p%Á^…¢PfhŸvAl"XÁèPÌZ…T !…lPÀ@ Œú(KhHþ©ÒhÌËÀáՒþƒ¬ÊGšIq,W¬@üCœpN”†– ƒ …ÙAWΦž–@XÁ¥?<(YvK~ËxWÒ5…,˜.­Í*Ä#•™Â•ã˘©8(/LæRɈúïIl~‚?0l¨¬k,A ÞiLA öiˆÿª€AÁŒjG`ÃDœPHP,Ø–À%y‚$¼!PfÛ* ÂDêvâ G8®¢…BH0øƒ—M€ÔªŒ•„Q ®@&ö) l>ªºZEÌc'dØ¥`)ÎTO­%EðÔßüç*РAˆ\' $µ„‡#ÆŽ*|‹AùøA 6ÔPè¸AØAPè=Z(ô©€ÁÁðëFÔ Ø –¸‰M9ÀÄ\ª EÕ–f,€¼Â¾©)TMI<¼ÙbFP›ô"Î üÄ×TNþ €÷ ®7­JLxÊvæbnÊbî€ÿAt§j’&)ΑÕB­yBI ßôBR­]×VA”Á†^Áºˆ$S(gx‚+ÄVTÄp Á$Á,A„jm…žÁ¡~í½ÚcEÀ1€ä:åtIÃøDë`ÄÜž­–F©…‰bLÇÇ€öjMEdMÈ¡ø˜dåÌmû@~Ä®Vmtb 0A XÁÈ#„ž8ï0ËrK)œÂ)äŸ!Ù1°30p¤-,ðOâÂ)tbTA$´ÁÜXA˜®B)˜^̾Q)ˆá$­?LÁærÁ Á|pòb-…"¯DG˜åGÔTŸí¥ 笪7%ÿc¼Uy&£$@ÄÊJH)Zµ*€ü<`ûjZe!RÀ¨jp†¬FPlÙ¼XÊ®?ÔA¼ÁÇq…AEpø#…±B(ôñn0TEB" B"¸”Á0#kb»¶A $‚!$Â&$¤ì`­p0ò"×Pè ­E€À¨ò*_o®!ß×H€&D2&l €0°ªåH@hIfpÎfÄå¼Bê„T)QŒõElfl¦âÚ…ìêʺæ¢<Ö6·«xÁmfmñ2ò4Á>F‡QT*„BBÝÓ/`‚$%XÂ#(ÿ‚#<Â` –AŸ2Áôs`A»š°?L²#HB"$‚"Tr (FpÁ†¢4ò$Á)#EˆÂ*£ç¯T±¾ÀЭ‰`1€"@ mFK=€Çò2ù¢­1*€œê:CÀÛ…^A³ÖdN4OóªTAÊ®,'Zæ†emvó7óé×22pAÔ4 0TcÁ”G[³¥Á@{í¤Á@¬‡“nh¡WÆA¤÷QAX˜Á£ßx£èv"0ßó}#0B!$YV-üfŸz‡ŸueÜ(y@>Œ’ã:®CÁw‹œÿ|‹Aœ™?oÁr-»+3?Iì2O¸üØû:ˆ²Dƒ1ªí„t€g(€Çn{F€NEú¸÷kØT,»Vì$À@‰Å5À:ÈaZ‘}Ù+†iû{XºýQ0¤u«£r‹€À<@ A Á!,‚û_‚?Фì,´fP@Á 0Ä2d‚ˆ1Ä ”(kÖ,Œâp!‰)V\å¢D$þš¸ñÒeÈ7LJPøweÊ”øó‡ Ę 8À`Bƒ$00bˆ…P4 F‹BHàèºý® €Žp„CbWîáI\°–E ‚@ß"¡¹Í¹oV‰0D’û}ˆA ÿêB„, `H"ÌåÝÁˆDƒ˜Dñ…'CHâ¬P‚Šb±âßòHXBŒ˜„$Ô7 ôU¢‰B!Ö$’y| & @h. @¡:^G[øc!K@„!„Ç‘%Y@B èB> ”O™ä£  J,¢/…eØtÇLž•*a“WUDF``S›h‰ÉTÞR%#´#.yÙK_þ˜Áæ0‰YLc™ÉTæ2™ÙLg>—;ntpsec-1.1.0+dfsg1/docs/pic/pogocell.gif0000644000175000017500000002676413252364117017642 0ustar rlaagerrlaagerGIF89aÜÄJ©wœœ(p“>‰}XiV2dz;®sîÛðèëåTÖäàëÝ­SWZZ0ôì,€‡w’ŠÝÔ4¿ËÈÉ­éÍ(;N<éç~ö¤±°½¸Pá‹àääé)L6"=V!ù,Üÿ !ŠÍhžhª®lë¾°Û”qmßx<çãp|N¯Ûïø¼~ø‚ƒƒ„‡ˆ~~?"‘’“”•–—˜™š™žŸ ¡Ÿ›˜  ¨‘¨©«­®¯°±²³´µ¶©¤¹¹© e'ÇÈ¢ÊËÌÍÎÏТ‰Š{ÖרÙÚÛÜ"àáääâáåæçëìíWëñçOíòîíêÀ&îíH° Áƒ*<(`¡»t ÅAT‡n¢Eˆ3ž»¨®Àø8ŠI²¤É“(IÿŽKiråHv7Z¬È2"?Ša–#¨À„"BÖJ´¨Ñ£3Á¥“SgRš¡ž¸“©Mš_xð…P¤]–¶,Q±æ®Vµ:1ëU¶NÚŒÊs­RœwßîKK7€ ¾šL¸0J¬lŸÆµ‹ö-KªxW:ÝÛ8©0À‚ kÞ¼±Ô¶Ÿ†ý¬r¯^Óz‡ð ¸rM²"ëá#͹v饴q‹Í«›·ÝǨ™ÞÄ{öÁO®YZ KïQœÀñäÙž^myËì¼épµ§]®;ë·uÛ£áš@С½û÷4@hà¡Awêµ'¦»ûó~ð‡…æØiŒÝ÷RyÈNÿ\|, ¡„í]pA H‡ß†ž]'€¿eg ˆã—ZE€  ™¥ÛRM`Ð…ÖèžxPÁˆšuW‰ÞYÕ –Z‰ÛÍ…ÔVÐ"J/ 0À;hð Xº—€ÐñØ#z≉!Šàpã©$z—9é¥E/ð Ô Yºç`¾ùåYJ%i¦bkæ›Vq¥™äu‡µÉ¢ŸéDÙ„؈gžZ^A<|ý©àÒf•¨Œò¤¢pªš£ AJŽ5+}Ѐ•PÐÁ{.€i{ pÀ«Xð§šå$f¨aª*¦b‹B6æ’¬å*F³^é€ÿ®¼æªí¶Û á­ ìHì§ß™Jè³ü)hxNÛÖ õäaX™m„ÛâËm¾x»À¾> á¸`¡Ælv½ª® ¯«¼òºÛ̈k¯ûf¬í½&¸, wàVÁÉ(W0Û8¤¾Ynxd‚±›)À¥ tÀÆGöÏþöSH)ŸL99 0c:‡!þªíñŽ,‹ÖÒw/(sê\×TïæÙÎ>;Çc ¬î»·Žå î)<ö¶{0uc£Üü߀C/¸rÎy„ý2.!ÀÏ.{®S?ÁÀÝKÉ÷ ŠQïgùËYÝ@ã%fY¦JUÖLô ‚ìrÖÁÊRHU€=·J¤";G€-6’9°Ð$'i½ö8@‰—t¡Y°N¦¤ˆ¥(;@WF`^ÄàÍH¡6N…¼ŠÀ+‘‡¾@*QL¼ -Q‚KHêR—Zbä)Ë1% < RÛ›õ‰¯e.ñ>³¤å’¦IØÜPœ×¼b¿ÞóJSnó˜‘ý€ÏV–Ó’¾aŸ Õ‰všÄ èg<±èžnÓ³ª”¾&ÄO†êªž¤šCŒÍÇÿ´$ åBGi7mrñöš(E8Geþ1 ­ ûS–|”$ï)6ßó.Ækvce<íÆB¹˜ë‰“ÁäLxÓ‘ä”~:µè>xGMJgVDáý¹¥ÐIåou¡ AbS ¸ª_»UÕV.™ a¨É~ÚoƒƒÜÇÔ4Á‚p‡£U-ë ΚOl±µ­xE&U»‡9ö`ë~ ½ë8}åÀfµ kJÅäTš 'rPìAÕF ´9Ö¯_ûâ€jמì•3Í ‹×j7Òá©Ö[I¬ºœ×ÄQiv·”ãìD`S€ R¡£eëa¯˜Üånkµ(”d°ì¹“²p;ÿm±8åxMú©VW]5åbЦß.u*‚5« à‹-À¹Jî{ûØŸQàÀ_\#™ J¯Ü£Ðx¢V¯ÈۭÜuÞI¸AkãKZøÖölöuÝtÍ2¥èDÇ:×Eé蛫È%DYÓdƒ'¨šôŽ`ZÀÀµJ›+ùZX§p{ðk¡ýŒ,£7'R˜pK;&±„€º2‰5“ý©‹½aÙ“[6¾ñ5û]²å w?ÆÇù85“Ïò1ªÆL@X$¾ªoÅ †f•g‚Kâa¹Â¤*—UÛØ©WÀ‰a¹KÁÜÍÞcMÚÙ•hÿ²“#C²ñf6Îkt1 ‡ò \K¥ïmnsg'Ù乘·šOæ#7û G“‡¡'"+qâë»h¦hÖ¬Ÿ¯ÂÄ`On¢ÛÀ1ç™àÓÛõ¨Í†€»šºÙgs€k°ž†2M>³N¬Ñ!ëAiÃW~ÁMb&Ÿ‘¦Â6oKÃs;Ñœ»³²‡8÷´4º[î‹ß´ù»=‘¦îcrGÀË!m)°@„(`B²`Âs ^Zy-$Ò$Ì‚¢»ÍZ”dPÞ¢¦·½k\o[7GtêwÚ0°r DHƒà¹Læ†Lé ð3]µ:R@ZÒ$c·º:åå½åÝmÁåŒÿE«l-cY´£Ç<€pû;Ü,÷·ËÉ@Ö‚¹àܬÀ¬®}íµ —:­äú†ÔõÓm×ôB±¥“|Þs¶¿€ªGàê€÷7~#é(]PÈ ©¹0VÏ ñ7!bK tŒm1”f7åØô¶x€v?¬r—­Ã~±Õ„_´zàWŸ_ t}-±&@0€¦Aš‘‰çt,ÞêdèC·ÐÜW¸Ÿn”•ÀÖ8Àö¶s¿€×tžºW 6YÏ}‡"\€FUüÒ¨m¯ÛSèöПݩ›}Øã6û'FjÆ1oµJóŽâÜ_#%j Ùe¤3Ò÷pÿÐtÕ÷/õ3!ö¦TÇ}Êa ˆ·x €‡Fv{Zç5Æ”lÉ…Dñ5r’7!š"5€¥7êtõwL‘æA˜J¸73ZõC}rÿ"_ßòJ r¸zÚ$1B¶ À`ä1°˜6y’Õ-÷S6¦†[óA1W:q‡,íÒQÐd0˜ ga E}lõ5ö†Z:¸ƒ>øl¹Ò}§B8„€gGòR3%ÝtNHmF‚§ Ð{R6¸ö^jS_'CÕÁbš´IvAN1†ƒu–O¦V'¡6zn¸l¦–+À„Ûg‡«ÇàzE5òÿ´w{Ùsx(cêm5¦B&†-XiK…*¿5ya‰êU°!' P€Zyö‰óv}ËHmuhŠW,)|ã '`×xÜØ„€ç„FFo}øs<³±§{;ãr€×6‘Txk£ø1ç&%ê(Äu‘9-À›6rrCÕ¦§l§õlQjK PŠ]yu =xù*à5²ç ¿à †ð‘„Å3€{ø5p™r¹/­eFÞàË‘‚QƎ÷^͘ûgŒé:`ô|øÓƒM—˜õ#;%Ùà‘n€7E±y™À†Ð PÏɇôAm'xo qi6¯sŽár>J›ah, !Œ¼Èž•›+yè Ø %Úè5Ò6#×↧ՌígœÈ™œÿ§’Ëá  @ ™kv2ðôQõq{ýr_Ü—š«‰avcbæ†G”{©—w–32ö9–L)ü÷* {„¨LóÕ‰Ÿ8!£†#Ç) ·k@\Ô¹£ÊQ“™Ð à4jÕ×xªy=œSžÀ6ŸJùLóG‰˜w ð‰‰×ð* )³’0ꆂ8Â5i£+·”I`< ÿÑ)b‡{´'™¬Gxè—wàÅ€ÌÄ2Ys¥îyqQZ‘Sú’' %%š –Ù=¶oÖ¥;x?ÍzŽ9¦dúw!7·¦; ðŸšYGñŸÖƒ#ÈŽõY™—·Qÿš·0%â¤ð顪¢ÚP¢€P ‡“YÊ|þÂ~(Zöæs¨zÅj+æã¦Ëê "ÀÌ*{(­Gmp}‚1 àgzš£Ú®èª¢;K˜µ‹—µšI´:²îú Þú*¶*¯óš ^mu’Ïwwfƒ<9ý°ÿöAšÙ©Ñ)« ñ „Ô8™põv[àuk£õ4JÓ®Ø0ì®êŠÿßšT߃²*{ T‘ª¶Z²Ìùµ©º²Ü0+y–s94ö¥fo`Ä”°POP@Z­`œ†€|1°ÿ†¤Ú"mPu-ù´ójÛ@«Wµ2ÁP›®‹¸S› Ly²‰»µdk¶{-ϧ)èWoˆè‰I‹-2+Äj£Q˜2(À|“ ÛÚ ×Y´À€KÇ+«eo8Òx†Vó™ ઱ö¹±ëù¡Nä¤ eÃ[Ÿ”ÛqB¶Ð{ ³âW8±››¶ Ř£Zi8±ç·ÔhªJ“2 Ú ª°­Ô‰| pj‡0½µ¬w“¥ c±Ü”¸½P¶#šÇèµÿÆÛ;» • µ˜½Òëi¾š3ºKštò°Æ9«ÅVöÆ'Z¬ ©º)S§  å¦ðàè¾óq¬¶º««VòAЮ÷[«î¸± D~e¹ qŒ‡Z¢Á¼óZÀ“‹Àe›sø-`ƒœó17×Bœ¼Woô»q›œQ.|2½` §šÛÚÁ ;„ÃjÕƒË5¾ ÄË xy»¿ÙˆÆ\+µ,wÌ Ä@¬¨²¯3FZLÈKǽõvQK¶ÅP( |2Å*ÓÁ«À©Â` ÆeŠ`œãJ“Ùxö”;BÇ4ü=dËÆà`Àlœ¼c{µŽ»Æ*+ÇeŒÉЫ¨ÿ2º~í7Z^Q9÷ǽr8Ô{„ˆ{źL'Ù­k·«À¦\á³ë;«‡_AI'R'ò¡}õv+ævÀ‡Ã+‹”šœ®û+¶ˆ e<ü¼\ëè¼²ªÌ¶×bjãܯ&D°³ßµt€“ýxºÿè:â¬@e€  4~¡#ùg\\iÌØ?¯Ë3¦ŸJôÍì²ÉœŒÆp\«ãºh|®S¢Ž`²ßL¶Èü¥ûú¢?È'ÔÊô°I‹[7 Fð¾R\õ,{p` óìÀü ' …â9Ë|{£5`W^§ŒÉõ7ÍjÜ gÌÐ]kÆ ?}ÑÛ0Ž«Ì~2zW~Fšÿ€ÒзwµØ¾]ù‡'SÏd™ù|2ÆÉ°5mƒºÚœˆkú¢f—|ÑA=¯œ¼ÔõYÔÛÀ˜lSJÍÔ+kdËåƒÉu˜d„?û4E`%"]FµØl‘… š¾~AáPÖ˜]Öxp(Órp{}ö5@3`¦÷AeÀÔHyÀr¡×FÝd»Ã{Ô)à×Ý câ‡v…ͧ€Ü;ÕË\à9bBÞ¨Õ¬'„ø…v+œ ×™ÙÐÍDÛ/u¢91‘úÒáBÛ™Ìø»¿›,ÍD ѱ­Þ}± —@úJz½Z¨õ|²Ìè\u~Æ–µRÌHÅ­Û©ÿœ ÝÐ Ø“Lmµ‡{¾™ZøãK{HÛâ¨|Š×0,ÀåMÞtÌ׳ÞQë|ìÍÊî­-Œ#ß×x_°ÏiœPŠu˜â^¹Ü ™¾Ý_ˆŽeƒÒÖ3 '6íݵ;.Ôž×æÊë{á>lUQ¸­ÑÐÈ-â<ýsôýz{ý(œÅ # PcdýâšÍb'XjÉÖZ 2E^Ãý Ût=ÊÞÍÜý_E> 6¡Èáþ¥¹ëÇ¿iΡm݈ëqm´‡d`·j^¾˜ŸÀó°<_ÖçZqÞumµ>^ÍoÎé˜,ç(PçÖpl‰#’îwÿw©¹j‰(ß å¶×'`Ù†àÎb } Ù+wµÑüšh^ç êÚ›~Ç>L]Ïs.ê+ºt3r€Žjaƒ½dRǽϥZˆ] 9ÝšÂ¢2J§¶3¶Ÿ²œÇIÞ-ÏvíÝ™^¯mì­ ç=B{uríóõ«Ò>ÄííGQ2²7 È Îí tëí$ îÀ9à|« ‚¨µ-û)ÏëìÅ®—ÉΡmNᨬïÝý^/Ðþ¹ç+£#ÕFˆiºènÚºÐ9X% Â5trñ‡e­4Ø¡¸/7yÉàìßúkïøkòÿ˜ìÆi*_äÀòåò¢'ØL÷¨Ö«€wC°Þô r ¬À@TGP^OŸ3℃Ëew LÏãàJïõŽïQõ'ßVßï‡z>zlûÔÈO¼Òlçü¬M³-Þ AÞñ6D)3@1\Š€þ¢ZÙ}jK÷|Löñyßô 1Þ€¼NÇþWŸÞG»Ý//ðÔžïÅgà9 Ôù¿/ÂP‘öAñMPNV’?qŸ?vW`€÷§¯÷N?ò$ßéÈÎÔÅûXoµ¿õ]ÿŒÚþ;دK< p iü[þ$$ÀªûI³«"í\5>±E9@`8.BŒ ’Ÿl†vò€ª— ɵŸT$t%¸VqÉ^ýóÂH %€P-BAjQS "ž±9†Œˆ3hì¡C@j„¢ÿ(tP0Á½òÚû”NæŠMòHÁ³ô²„*Dxô$¿Y¶¼Ê²(‘Jš(„àB! <Ì d¬Gd䌀f(vôHÑCž ÒœA‘ˆ ;aÆŒñ]M¾äpÂÀB° ‚ F|‚†]¤õBQ,(Œ%xÐ Û!;`ÐãñN)9 ™†sV-k6eAÂeYÂ…º:ï¢Ê ‰_žØ•,簊É N\‚ „Z\:@G9)"êH AÈ®­IÏO/ü´Ã‡c=*¸ªޱլ[YÓül ÌH€|žáýü—  ºñœ šÔÐÿŽpEW\ƒV@‡t]O’uUW…TC uFd áLZoqŸ‹p€ÄÕ{ïtÆ ÕAiúñxœ?d¢Do(ؤà;é`“ÉÈ%UË $dÖAñÈQRˆ¡CŒ\¹‰ðâVg¨UQN©fAG+Î8# WÀÌôècr~þ ¸ ©˜)$©àX)7¥SÖãlV2¤¥d°$]PU(¤‰¬,@XZ¥&‹¬•fššäqZ¯²FÀ` } ød¡ŠíšëñZ(’:)e ‡ šÄ‹7h¯BÞqÁŒìCÿY‘8d`&`•5µL¸Ðu›"ŠtR”jCP$¬ïzäæEÇà³ý=Ꟗèôc°>ÔÛh‘4Ç,ƒ÷" À:@™ °ˆAýð†ëÃÿà#4˜ãQ~œÈ`ôš¥Äú¨ 8¸(v©`±‘ã"a‡/¾qsbŒ MvUJ°XñQ×€/ÂñQr*‡ªã…4Î † ûèÉ‘Ñ0ŒR‡!#ô ™R&LÕ#ÁÉ1§ZšÅ–r‘N~2—ÁOoÐØ ÑG‡$–^˜‡@º¬F€$3#‰Œ Dbþ ¥@"°]bU´ê¥nŠY(&³tÂäQ’ÆJ™éAæ+£¢(Í:‘±#–6ã®lâs¨d7ÅYÆ1ö ä,§+÷ù#u>’ì`5²4­(VçeúÂÐ…OX•+FÑÿðGºßä%˜Çæ)Gª?€z³ ÷R§šÙNU8Ô’žá‚gÂES,Êè¢ðRpù.öØÅ†4è§J‰R¼ Š}ì4©XêÒWàhÜð‚t" …nYT§–+ò𪊠¨*H dTRI© M&êZÊÁ—Öê…p &*MÝI«ïbCÜ3#VUBd\eFÚŽXÃà ¬y¡a I*Dæ)Vœ*}–SßúR 8%qœ§–$Š%ú¸J¯3:XàEZ÷\®I<,QaJ 9v~ÆÄàd{UÙ…>ÐÙJ\?´t…ybC$¢­Ó¼êS5­û£ñâYµ®5‚AkR ™ÿ·ÛÂuùˆ…¥¦1ŒÝÁ¯Ã…FHƒ†ã¦Œ®"KܪºÄ¶vn†•îQE:]ë¦ »/-Áva1OƒHDÈZëÆûž¯ ek0Õ æ ¾D•¯eCYßÙ¢·Mu¤~j¨á2JK±-œ³ dG¨zõZÞ $xl}F0Ük:ฬÏõ§Bç ÉP ‘µÖ5쇃п¶—¨‰#q€Z,DRŽ4 |`3À°ÆÍÍq|…©Äz9­C®m:9lÙ"çmÉ0„¶¨à )Yê@Z×b½î!ÆãÓ²ê‹ã ç/d~‡vda¾R·™Šp‘†ÿ‹¸³•õü;šÎî§}ý4h³o‡i¢sGÓ7[ÓÛ8H)]é¶ŒŸC´Í>­œôºf&u$   §L³s”]$è,ˆWëòÅ~–¦qM]˜ÙÚPÐ5ô®“ÜK©»V9 ¨ŒìdÃaÙÎ2—g½X¢T¤Gå1[Y)nüÍç(ÔÚöâ°€Eñ~›‹Áø@Ÿ[›ëZ«òx\ÁæF]ÀÅRMð@àºðæ¼æû“˜ÎàÀEAD¹A‚©uµ³{–«Õ&6»’¸ Òˆ{2µ?tY0ÅýndJ1R à³vçäåÓӌɭ¢ ¤€;9ÊùX^5éÓÿt˜°cn›ÛWP÷ÉͦN·<‡óá¡8¡ðEÑ1²‘¬î¨ÛÌ.¤tŽ›7jåÒºÓ3Žs9!ëZ×í.¦Ì0 …)¸HÙ?BÚ¾Ö)£l9‹ ‹1Ž>#sT?A¿΂xpÜÝzgCBÏxÌLûP4€vß¡«u(ðŒR/\ŒP"éõ{ U¬E@'º'@Qu­ù¬kÆ*«üPÐD4ßéÅ'`U_‡c tpUjy9{y$,²­n¡ïn ·ƒxW¬ŠM-œ ®H$àÇ6¬öà W,«ŸÆô³êú:@£©£ø ¦NHoÖÚŽ©ÿÛ¹=Õ®œÇy@¤ýÝùaïhÜÙý@.!ƒêÑŸHøÔbŽÙÉÞÐ\Þml‚ÚtŸr µýÞÁÅF€~ÈÊI´Å_Mì)ß µÁyL[¬–Å LŸù”— @L´EÛ}ƒ¨-É÷eÈUP€Š$ü„~H°_k°…ȸAòÝ :ŸôQB ’—ë}íÒÚ]"ÑLî½» ËJÛ^Wø¥ Õ€ CX…{1ØÙ{ܺœmHÀ=èàêMæ_WÅõ…›3ÅÈ !Æ%BÝÅ]÷É!eÙ!æ½|C$M™PáØX "ÿØŒ”ÅEC,bžšàRY|H¸ÇO¨!ÿåÇäEÊ%z` øÍ Æáab,Hz$#¤!0 †4úøˆy„Vz…ûa„¾E„dÀÌ_BbºŒ@1\„»LœÒI øÇ0Î0 Ër†&ªÎ2æÝô¨T44Ø9Á‡X€Kx3´Œ½ÁyŒc– ƒ(äáMßiáÛYhM%(€hTasµÝÓÅœ€8;>ÉÌE<žÔmbß(€ºtÚä 4¤‰™L…jH’¾Ç&‹ñh…B~£:Þäx„,"Gd@IfŽB ÄÈ;ÂcíùHR~$0Išdb€rÿ TžÍL’s`‹} •È$ª´݃M"ä(¤B*@08"ZêA'Y€¬¥(] C!Z$¾`¤ÿÙÌ0’åýeÚ”dιÓ&Pv æfœ‡þ]âG*‚ÇìÎ&õ"XŽ¥XºXÀü¡LB¦¥N"V,$ìÁgæ¤0Œ€EîS>;â^`Í`ÂC“Àm\ÀmREÇT…˜PÙÁ1@Ó¨DZBø&> Nâe¾lgÂËäägJ§NjÈâg®å´ŽjÒžä`n\ÞÈæÌ †ÑPC`иê­.Ô¸fW ED øÔOp’h#a©•jE,’eêqÄ%Âté€$RÏÀ¡úÄ€¾š)Vò^î]e½*ÀFÁ™é¶AY_ e²kT)€ºKFÚ…‡õ±ÜüÒü’Îñ å¹î¡Æ¦Ê¦˺¬ÞIÌBÿß_ÝÓ éaÎJ§î ¶ßb&cM¼ad]k’%R9£Æ9¨ÓF*ÔJm¡ºØ¼¨ÄhGl’”ö`j²ê± í `TÇnŸÆ±a ´mi¼íµìãF­IYÞÔlujÕÑ et¦%1 ÏðœÌ‹$lEKµì&¡=›älÊ,nA.äŠî㦙@ŽW%ÐeZÖ%/ª+å¨*¹N. úÛ ,Õé*!ùé¾cÕCì‚QóÀV0nmìì:¯ñF×ì"ÆЊjÃjÅZ "ã5ߺRì’ ³éFõXð*/RM®sÙõÊî"N¯þà/ýê/ö¶ïõÿÒ9©†˜*6©Œ1Äßž i°ú’ÛðîÐZÉîñRWüš[Ií/äÚ/Þ=ïï¯ãêïÿ",ù²,~ÄÅZZåìHt1ðt©o¨ÍW¿o“œ®ü†`ÿrp›Âsðä®0ë^pö* îiáí•ùÒ¯ð:p®ù/ê’Ò ŸÛ¯lþ²oÿÚ0ï0óÖÊj4' Âð ¿oH…qmÅpû21”ñ{ãý"Óùbq¯ð|Í©zññ#FSþF.qð>q·±S1Ëq"»¬éçß1Fñóï»p3S 7¯+2!LãB°+r(/ë#ψ²­ÿñ$ó±?e2Kp†“úv'»ñ'§²('²b´A!:'Ÿó:/€¬ò¿Y²þ1ûž±$Óo!ç0(;3.‹òëA`™òWåñ'F ›•Óñ1V2DZ2y2íBs9×PƒTs¬•2«ò0ï«$ïñ ¿òc¹³óгñ"²9Gsbà7"pÙa×sË {sA 糿0?Csb´Çèç:Ÿê6añ<ÃóW¢/% ³ZѲ!·2D“4€ƒ ½A@¿ÚãÅqóñAÓ. ³B?pH7óH“tNJ¨"VÜ)ßÁX/kôFËpGo3 #ïKñÓæ´Nco‘¨ÿÆù\#ʽ˜[ õÓôð5íRòX±0tAqS¿-?µ{YðÖÊyè¡ú©4ž±tVÿðWo3w9µpW³1çÞôCŸuFsó9ˉFGò½5óÑ^?s0µQ³+}õñâšbëoS[ßó_÷ó@%äŠl@Lj\mÊ…e6]o³ oõü4›³É0kK®ec¶f?µ¿hNÑpï ªÅáñç ¥L£õImtÒZ1bh31%Öd?ödm/¶m›s…5SÑÐCäVdDñ,/áò[©¶7{u?-wÛs3¯ôòK·NKn5N ù¼_FõÍ6·"#qq‡wÿSyËò϶›L1{·wI;s5òö6z’ÅÞrf‹qq³²%7 lÚüªu©w3xŸõkÍ—$Ûž<ž<ßr·öð>ö‰;ð,Ç4‘¥l-;4‡»÷'×äü”q¡LÆŽ¸2—¸ìªux‹w7Cx·8½¸HÓ¸Œß6w¶44_ÙAVmªÖÜ [qFƒrb ¹û˜yëwЇÒD¾®Y+ù’ŸîxTÙNñÔ¼:¢þí7™o°ƒ?/¹ï–÷^üNx—7$‰ùz'y™ø|ݺWíæ5¸f ]x’uAÝ9žk±Ro90CR=ü¹Ô¶ K·*¤¹¡3À§z0ÿ«ˆ(ð¢7÷ø£³:9˜í9­¡1ônzè0{º(“…¨:Ù½J-˜¡Sc‰[¯¤›¸‡¹,žç9U8žëµÀ­A®»²‡G÷®Ç4 }¯3Y 0AúzjÆxK·ºZÿ8³ˆ3» ¤¸¦«©µÿñ±{ú™EîU€·ŸúšŸòŒù:%¸:`ß7z/¬o²1·{Q‘ù´©µiûfÛy„º¾› Ú¹ Œ»Á’ë³`óç÷ϳC;SiúTzX`?|3o·ë;la¤Æ‹º š Í뱊sõZYú–Ã4=h<øõ¼oή+ÆÄ·| ˜Ö¿»DkñÆÿn»‡ŽÁsšt½% ½Æ÷Ê"ýþ }Ò£ ºXÀ¿WÀÀã÷ÿ~Ó£Sý´ÍúΓÚÕ×|¹ósÜZ¹*´¼·;¹5{{Ùë:QÛÐÚà¯6ЛuU~¨…R(bç|.Èâ3~ 9¦G,~ $>6s³§1ûq$|JÕƒæÍÖÏýqŒýÝ'="ŽþG€¾P·ñhµ%ôÈ»ôêCìŸÝë]¾ã~Òbþ~hýÍÿ°ÿ·ñëü0ËðÕ!YñJ~ð–—5Ó¶}ó3þñW¡'}ì#2ÚÇíå Ñ@µ~®-˯’§ÜO7Ý»ÿ’˜?0â(z•EÍI’^òL×¶äúÎ÷þ†Ä¢ñˆLê„ɦó9™FêJoÀnîÆ…i±±lçi©×¤†çÝ`‹,ïr€Ïë÷ü¾ßO`!8HXhh8„E”Gˆõ))ùP€ò¨eÇig6SöõeªS“ƒd!Ç:bùð0²!BË@à3±ËÛëû Ì+1,1A|,¡ ¼ÌÜìü -=M]½,Y·›ñ›áý .>Ž|Žžl½ÎÞî^<.ŸqYoI¯¿ÏÏoyY) À |@á£LWdP÷."´tÃæ‰ë0ÖÁS TøqR¤‡JqJa2¥Ê•,d SP ˜$­X©§NH6±X‰ 4¨…—„ˆB9Tæ¡@/g: „òŠL©Q£NÅrêÑ™„vzý:I)Š®RS‚UÑSçÍI.S¤= 7î×›C›.í*Èh¦­‡ôB2Éu«ÜÁ^þ¥ 7;ntpsec-1.1.0+dfsg1/docs/pic/oncore_evalbig.gif0000644000175000017500000001734013252364117021002 0ustar rlaagerrlaagerGIF89a¶|Õÿ$cl½½µNNLÎÎÅÿÿývv„ÆÆ¿ÞÞΆ†‰™™”™šœttw–”Šjij&%(ÖççMpuëïë„zxf†‰§¥œ¶µ±„{ŒÙØÖƽ¹½Æ¼Žªª¸ÊÊ¥œ”„zCD?÷÷ôŒŒ”ZZUbd_-|ùñæÓo솋 ïÝß/¿~üûôñM®C³óÎ TZ'œä'”à1TQ@Cv‚}áZúU˜ß…öaha†nèÿ!†/ÄG¡†$vXâ,°‚m–@^ 00Aƒs1¡„dU#U#!Wâ‡& ä?–è^{H •$|î)Ù$“íAéd’T°Á+2£T$MÀåÏu† `#ÍE&U3-Ùd”kN馔p¶§št>)çIÆ×¦ ¼ÐçŸyþé§Ÿ èž1(P]TŒ6Ø™^ L%—g”¢cæg ¶Ák z(’žÚ©¡¢‚Jê…zšª¡«~ÚiŸ|†Hª y†((­kÚ:(®HÚúÐã—9BˆRÊc™”fW¬ŽÀ%­Òækµ½^b­Úò*_·ÜÞš­´Ø~ ë¹´¦+ÿ-µï©»-»ï®Ë'­/À0@¡5Úè\Œ:ÇåH ø0L°—# ªèêšî¼ñ¾ ±»£qÅíR+±ÅO̱¼ÛÚ®¸€ŠL.¡&o{+¡1lP@–ý꣤TÁä¬óÎ9Cà‚‘Ðð¼kÑ#}mÒ'Ý4Ó*«›4Ò"³Ë. Rc¬uÖRcmõ,à`ÌÍéëU LÀóÎôì6xIƒÕD×M÷ÝvçêÖxïÍ5Ý~þ0Ñì-øáR#ήâó^àMÌǬöÚpSÞ6Û>Î5Éw¾¸çó2Îùç¤×-xé†÷=¸ßªómú ”@u‘Ç|ÿ3å¸_þ6Ü›n¾ú﮳®7ðxñcýÖ0,¿¼ ÍG½óÌS?½ôÑ?Ÿý ŠV0líQù€;ϳ½6 1`Ÿ¼öÖg_½úð³ÿûò³¯nõ÷ÛüÐ_ß?öþ{ßÿ´ç¿Â  @¾ÉL` lg“CŸþÈA6ïD HÂBO}'ô ªš·¼èíz/t¡ûbØÂÚÐy=(cö=ˆ/wLX™˜eæL1€¡ •ÈÄ%2/}ûûàþ¦w½6ÑŠNŒ¡oÅ.zñ‹` £½˜|#*;ì—rÇÀÎ(KYHÛå&Å1Êÿ0Œ5ŒAöÈÇìÑ1d íHÈAŠÑ„L¤"¿xƒ è`è£Òˆ;„ÉL.9š  ˜ ¨ 7¨ã"ÅÈÇDÀ”H%RÉÊTþqM ä(g¹?QÒò–0H_ Ѐô+ ÐÝÎÒX¶I½±Yž l‰K=òñ”§l¥*[I€jZ³š­|e,ÉÌ.v3ŠPÔe.Ç)Îr’óœæLç8sɽQG’’¤å$xIšŽjË@ÕÉOtæš®Œ¦4UyÍ‚´ X%*ÿ¸Í@B¯ŸE§D#šN]Þ` ðÏhœ#Ìœ©(r3“Ë=¤ D`¢(-g r0Pk²R ÿÓLèAgJS—¦27H)7wÊÓžúô§@Õ%7+ 0 ’DÀÚ Ò~ÍŒfÏÑ$HÔª®Ô”Ê™£hP D›¬\¥XeZÓ²4¹ôiZ­ÊÖ¶öôà*$;Ϭž1["噑¾qní)NAФÀˆM¬R 3~5¬d5«d¯é”ÀZö²>€ˆ€Ñ u©‘Ó‹B էƈ˜]© ëx °Ull }bS¬×Œ€fpƒÉžH­p-;€Ø1À³òäY‡¥T-€´gëëg(pÙ ü³h­FÛÃ&¶»¯-_¾jÐV`ÿy}»Ê{m ïUoЃ He@ȇƼîÌN©i3€†µ)M¹ ^›]ô†àÁ"k ‚„ 8ÁP™Þ X@ìЀ °—70p­š#x¥0~±‹cLã¯tÆ2¾± f " J²£-øe¿„)™éˆK³|qŒã\àx RÀÝl÷Êèeðƒ/¼Ç  äzE°„@!XÀ |›Hg5`òkÈXæøÎu®1Œ÷lg=Ó™:dÀƒÉ³—ų¿Qf£2I©€ ̤8àsža|D¹šy­aeKåæ µé½À ÿPÜ çE¯ƒU½ÞšêÖÍ€³¤÷çÔ:θ¶µ®s½ë^ߺſÎuœUÛLw@‚M#0YĶMR`Û™ bÀë_òØ2Юíw_ Û,à'è% ðÕ  ùÌèñ™Ðê™~ ÊÒ À"ðë]Ó¹ÿþ7µ.ð€üà§¶Á ®ð¸l£ù1ÏíA«߯Îf”»’¯røÂéÜb§XŘ· ‚ìz·»‹ÛbÀt`¦$ ¬j‡õžéÉóí‚„ä"OºÒ—¾t[ãúéPgzÒ/` p€³ÂôÚÆÿTÚé®Dæ€ v‡A€;Ýì7.y‹]å(g ÀÐ  aˆ—݈Œ0`ÊJg`­ Aà‚Ü6²¹u³›q¤;ýìR¼ä'/ù€3Jˆìlx\g pvÖøqбá_HZ ËÕ¯>7ˆ½ì={úW,Ð øíw °|Õv÷AoŠø Û¸å—/uÈGþñf~ . €, FfÜväŽBœmè¸ÜÁâ¶—¢'½í²ŸyÚÕþb'—–7È€¢ÐY À h€Š( „@ P»×R×toø¶xp2 }Ì×€y=°a À@ÿÉ&L[Ç3ÏÕ\=#ASq Á7°€'¿´`i7plWgï÷O¯´G®”wf—`––ƒ^%M¸UPàfˆÐx“·€$x„%˜„5€„K¨„Lø„I¸€€3 FõyÕl;#>#;ôy&od;@‚5 Æä =€àP€/€m0XJÔV¦´G;S5S9e(€MXˆGˆ†PhˆN¸ˆŠØˆH(ÐKÇ0ü¥pCzpÓ/=#&5 Q8÷ÕW1£ `Š Q=ßÀm+8‘{HƒÄGMÖƒÖ€¨xHˆÿŽ…Œ˜ˆÂŒÁ( A™Ç°6’„…*2 ¤I›´WFXÕA)Q¡2`„i˜ÒR¶s\ö9 ‚ á†rè¶{Y1…‹à€¸‹‹w;PŒM¸Kh„ü¸þ¨Ùùý¸ô•Š‘L_è\Ç"~q à p ám& v PTÔ!!§9Š’â/Ã@t,‘„°@ äYôTŽ˜ÇrØxíˆxÕ´K+øŠŠ·m1°Üø“>)BID9F)”9`!¶’8qÀÒZ  (p£(q3‘ÞÆ4``}Ç¢W5pÛ8@3ÒCÿB&h.IÀ"nù–p©Wé?ÃÞøÒx8ðd@¨“ø†/>¹C ”E9˜ƒ¹;@b)K¸9Ð tµŒ˜€^€'  "q;£ùq ċũxÿÔ¹œ™üÈœš¡>‰˜«„ÓÑrç‰pÉwgh"МEWkÓýçÖ™0! >& Ü6‡0>PXÐ6# ‹™b§¸c  j€…%£‘rePÁ‰n©’À¹T¾©œÈ@°‹–ƀȜ©¡Íy¦ ¸8ÐHàFnÙ5½Ä`fXòH‘ĉ"0£3:ç‰f`eI£-$ð õ¥y0 @bv5>@>yQùƒ+ø nf©,——–'_j™r©º à–¥ œùÇ3.J p%¯Xœœ2 ¦J«ÛˆgˆA‰«ÿ2 «½š«·š«‹õ]±5[±Õô”šUV*bÚ…|ʧ7%ç™§¤Z07c' Û‰^°]p†³e9e$ p·‹pº œ["ŠSAÐ6:pBXqtªÀÙ;£› 9¦Æiú«½š¡¼ê«fš¡;°ŠU±ˆå]ˆÀ:«?‡‘К§ÓÊ]TæþGa€J0Ý ‚âm›äZ{Ú‚yQ³S.0;t/,¡%ÒŠ Å#ß­¦— d$€À¢X<69SqëfbZœ#¸«»± {«^Ûµ¾ª«¾Jk±ÿf‹X `®>‰˜¶®ÐÚK:3À§Ÿ†f °ÖªT>@q9:­±)æàx¡j4 Šø t¯z@| u!,© pyòÊ'À >^²a@¡a«µbûµ`»º¬ ±ˆ9{¶[Tsšº2P@+bg6²ÜÕ``}­EhÎÚ‰X97Ÿ7 º?¨§õ¥Þ€² ¯©Œo€£Æ(€£ÂÐ9££ÙKÖ'°AbÓ—9x8ð¾^ ±§¿ôÛºÁº ¼t'»Ð:`„Ⱥ2%àKÐ*bI0è%·£5×™ÿ[9ZÇ©¨û%x)ÐîËœ•y=„WÜÊ!30Л@ ¯"–3`\u=ðv(, @¨:µžG¥›ƒpï ¿©k»§ Ľ Äb›«Ð9% ¯y©80·"`°" »*¶ºjE|ª‹ÚiUfevçZ! m£¥æZÛÅ]°¶ð›b<’hŠ}PjiSêfžp  3(0v À@øù×Y и;|>ìÃ?ܰFŒÅ”ÄEü«€^ú''„\Ÿ•¦€2p±7¬«ˆi®ºz„…ª Ú 3:·°eX)ð§xÿ1Ú§–¢Û[°º°$@ÁR%býÒ×'ŽHUpI ‚(0 @¡gzÀÂÐ38Úì;‡=Œ’LÄA¶?œÎ“Œ«7»¼e°¡¥ŒÖ¼‘Y–]')<ñ¬¦ C¨Ú "b|ºX$ëZs«Æö"†q`XrKj:ÉñLJ&bqp^iÆÌtu§º™9,Ô 0„ª”ÃÈêPºÜÃ0½Î—ŒÅê\Ó? ñì«t™ävžÿ—¢vf¯µX!Í=°r"¦¼Äª7›–]F]"ðÐw‹ÑþÇ]PýÃ70ÝK)ê¢ÿæÔ¦›Ç’¼y9+ ÀIÂp€ˆšL`8w%Œµ/=Î6Ý×~ý¾;ÀÅšX3@«8¿¯eeŸ£¶ªøz@Y`Ôd–%`}ô§%*W_;'·ç‰XÚ•f‚Gfu×r‡E<ÉÍ ½Äú'I™cfTöÇ’Vz¥#½‰œçµÁ‰‘3 x¨,{0Í×}Üï{ Ë0Ù°ÁÅ¥]‹M²ƒm¬0º'ྌD­ÆnúËÝ…¬Ô £f—%,7ÐÜ ¬™ð Ù©¢ÿ€—­h_QÊà™^éí¦YfÖifêFE&@—4w›K4PÁø%0 àtçl•né/]åUnåŽíÜ^Ü@/Óç>ìõmèä[Wfe nÙ*a3p°ñ\Úµ]e{4þð¦ \©üÑÌí%Η™^,Á€fŸva­Õ<@& $€sÆe]¦œo˜–IÔq§ LªfÀ„ß~ò(ò@Ópfé­ØUf²‰°mBHtw²é•Ü %"P·¿\T4 òTžòVN-÷`<`®;°ð¬UF¥ñ$04p¦´—-FtI3žÜ¦eÂðìÿt¬’>P¥x ‡½nôj¿öV~9žÆ&û"¨Ìà9ÀóÐu÷§€=|ßöÐb¬Ð Àö„ïyP)ðš-È~úã À€Ù}J7€>°-Œs6 0ã'eÔÇõ•4|„ò±gʪ/{¬É©ßÃ…u²)€ÙÞ÷Ê1|ÞjLàþmÖæ ðÒ®¿ú©ßú¬oÊÅoÊð bw¦Ð-×™02J€¥ßïúÎÅ"ÀËß÷Ü~ÊOQ«¯üÈŸþÅOüíÏþƯüÀKV–ÆxËöœØàÕî­˜Ãx8Ì@…ÅBĸ ¡Di”úœNÿAÉ“|žPœê6h>Í%†Ò˜²Ãàv9øBž†Uʯû¬ª DR!S'SF=FÀ¾†žŒp†0npLí<S¡x´6¿B$$<ºJ*=L&L*6PH*¬x<pT™›S¯ž›ûŒª).%%+#ÅBÆ@Pç*f(@@8(Œ¤ ë‰:ºÍB$éòö¨V&j¨@B‚PˆfbE{ÄÌ8€ K6Öª% …Ĉñ"£¤,¨ÀÀ ú9àÀà›ƒZ>÷Q)æÇ :’h‘)³O¡F•ÿJ' :]С2·K_3‰´ä¡IÍ :>bA3h0° B¤D¦æ}zƒ‚‘cÂtX`æË¿/A+ù ‚.ÅÐÑ;™re©7öJƼY3BTȰdÆ4Œ¨u©K$K€-¥£"Ѐ)hÀé\<ëÌ™¸d€Ö\C)ò°GHe’Vb jqëñ_¿nY*fС€ ˆca.5ò"b‚y.`¦ÃsÄ<ëÅ âKùx+;ËJà¡ä¸Á$ ç”Cl’$B>î(¬Ð§A2˜¡¦ @Ƶ@ÈÀ³ ÷b ÿfP?Ì0Qö@ <Ë*P‚hä J¸¥F¶p0Á´@‚°¦ ¡¤Ð€Þ ¨ÒÊ+§4 ƒ–0à@ HŒ+©ì­·~p« J °ì µ´pK  K,³|jnyœ0ü2O„l"³ážêÓQ5ÒI%}4ÍJ{c§Ø €­‘IÌ Rüª¼4U!l„W—ûb¥I¨€J0¨.…4U<(À"«ÅµZBàr€Ž­$BÌøŒVÚi©­ÖZ+)8]¢‡ADMÂÁÖj a‘&Ñ$ Tì ƒ$ [¥Í@ÿíD‡è€ÞŠ,!Yö@ `cýz‘h¯X≥e „¸ZeXµå&¹†|©…†ÐkÍ‘dÍ(ÀÖÞx° l¡ÉZye¾ƒ*8çßuš8Ð £Øè£­Ì¥3`šé* xµ¢ÛEÇ79\Zk¦·†sJ™,v6 4AÞ ,PAí.<ÐW®áæK àôW+—Æ9»+00¼o†»kÁ ÜðÂÇuoOo^|ñ ˜zT~®¡áç,éÎ`o¥¿Ùs¦i h±-° Uî¦Am„ÕÄCÍ7ÿÜsÇa‡ÝSOmgüíØ53Í]x­‡çœøãÿO^váoo¾ùÞg¨mTÂМnœÏ^ûæ+P×ZhaÖÐ<—KàAØ ×¾öìßÿýöøçß~Üñ—ßùøùß?ûx»™xŠ%°G^â¸.&X—ZŽØÌ¤í⢀Úo€ôTB½‰pá¹÷AŽÐƒ,d aˆ3 êí ˜AΈÃêP†=ÔaÎf’šÄ€¡PÀ 8ƒ~ˆ4ÜáeˆCŠ?¬"8E'2ч=Ôâ¯D1Š1‡c4ãÑ8F\|C"(€hÆ4ΑŽu´ãñ˜G=î‘h™7nSı…4ÿä!IÇ.òç#gÉG6’ޤä$g€ÉœŠD@À‘XÉL^’”‹Ôä(EIIU¢r•§\å+SKXÎ’•‹Œä-q™K]î’—½ä%ÖPÀ—Å4æ1‘™Le.3™çèå[| ÍhâRšpÑ¥°I·@³šÔô&/»iÍe†3’ädæ9§yËmZŽí„K;áOyΓž–+ç;ë™O}ýôç?P” 5èA G…Ò@¡ÐC!úP‰js¡e( "JÑ‹fT¢ ˜(F1:Ñ.T£åhI/jQ“Š¢)u©J_S˜ÎT¦!m)6=ŠS`s§8õéOÿ{šSžê”¨C5ªP‘T¥5©L]êQŸÚT¨:•ªSµªT±Õ þ”«]õêWÁV±Ž•¬e5ëYÑšVµ®­Â,«[ÙW¹Îµ«Â\Ç]A€W½æ•¯{õk_ûWÁ 6°yea[ØÄ.V±eìcûLL–²•µìe1›YÍn–³õìgAZÑŽ–´¥5íiQ›ZÕ®¶³píka[ÙΖ¶µµímq›[Ýî–·½õío\á—¸Å5nnK—\å.—¹Íuîs¡]éN—ºÕµîu±+ÝX Ûí.w½^ðŽ÷»å¯yÉ{^õ¦—½èuïzßÛ^øÎW¾õï}éëë^îî×»ü/Ü_˜Àî® œàð®WÁ 6pËëàó:XÂìõow1œa o˜Ãöð‡Ab˜Ä%6ñ‰Qœb¯˜Å-vñ‹I¼%ÌXÆ5¦ñmœcïXÇ=æñ}d YÈE&ò‘œä$#™ÉKvr“¡üd)G™Ê>nÁ®œe,oYË]æò—½f0YÌe&ó™Íœf4¯YÍmfó› >9Ï™Îu¶óñœg=ï™Ï}öóŸè>¿ÙÍ…&ô¡ hD/Zѳ !iIOšÒ•¶ô¥1iMošÓöô§Aj>;ntpsec-1.1.0+dfsg1/docs/pic/flt7.gif0000644000175000017500000001725013252364117016700 0ustar rlaagerrlaagerGIF89aËÖÕ?:jrU›§7b&‚ƒ„iÀÏ8=b´Â[§µG‚P5,/-QWDEFAvc±A4ÁÂÂ3]eNš&DJððð]¦=¢££"Wš9*KáááÑÑÑ)Dx.466J„1cde$?1V"=m*²²²SUUstt"/2 ’““#%  *24$&'+&!-"**1*(;?;BC'8#5 1=.:A:j½EpÍÝÿÿÿ!ù?,ËÖÿÀŸpH,ȤrÉl:ŸÐ¨tJe^جvËíz¿à°xLÎVÏè´zÍn»ßðx¼ÁØïø¼~Ïïûÿ€‚r†‡ˆ‰Š‹ŒQ >’“”•–—˜™š›œž>…Ž¢£¤¥¦§†Ÿ«¬­®¯ ¨²³´µ¶ª°º»¼Ÿ¡·ÀÁÂÃÄC¹½ÈÉÈ¿ÅÍÎÏÐs‘ÊÔÕ­ÌÑÙÚÛÜFÇÖàá—ØÝåæç·ßâ­ >’’Óë±èùúû¢ê÷Ÿø8@RŠHüÃǯ¡Ã‡küÁB`O^z”(¨`£&‘(œtàb%u‘ƒÈ²¥Ko_‰”@€À‚(Ô$àîÝ‚ÿš)‚b`“€x"AÕ4°³£Îš= %àÖÊ—X³ò“ÈJ$…0ð!a½ŠÝ˜€=EªP¤@({6­Àòºªµ°ám\W‰Ä˜ …uì  &X:ê#)¨#!ß™ü·çàèSkK pdg…6 È6 AälÙAn¦+I·çسÝ(Ø‹°êãÈe±ö´XR …ÊM˜£îJœƒ6(ãðeÉËWÓ•H`¸cp°sŠ‚ÌúÈéúÜ×½5‹ÜÈ>äû¿Ä6Þ€2²\'ç-…S ¾ƒÁN=Ù7Iv#ýÄÀ)@ `ÿÀFßWàˆ$JÃKR PII)I¢Ñ$íæCÓШ"‹¥„x%öèãrâY2P×B™÷ã’Lä&C.“‚ŠHb¢d“Xúød•\nre–`¸e—dZòe˜h†7f™l2”æ›#Ò1ÈœtÖiç„À©§˜à矀*è „j衈&ª¨gîéèK (Ðä”Vj饘fªé¦œvêé§=4úè¨E ꩨ¦ªêª¡’êêa¦²*무‚*꫸šk­¼öÊë­¹¾´AÄk,±¨)©¯Ì6›*°Áæ3¬ 4Ѐ @Ðç¡"d[C&  B¶«³è¦‹ÿ)´ÑnÃÁ'4Ðx À²Z €@ C'\lKç: A§>€p¥@0éÁ=(<©Äè²Û®3T+ƒ$,«®$h ƒ *pPªÇ£Ü)½–Z À¤XÐË3¿\ñÅûTp ƒ€¯ºšZà;„PB¹úœ° Sª¯Ì“àñÓ•>À4¥ؼiÖkmuÓ“Zö©ã\K*ÈKÔ@ƒš˜pÒºªìv 5u€ï"ì4BÔ’òí÷¤àM€ÞÔä0¥ xâd«xŒ*ޝ"à ‚ªe›ÊÎðsÛ«Z@BF×½ª"ô`Á ›ÐÌ ¬àÿA¨’ÊN»í@‚ë+hAÂWÚ8àà Ÿ| ´ ©ÚRN@ŒN¶çÙlp‚ 1“Þk#Ô0ÀÀ«Ùý©-Ðt ÛöÛºÔ=°ÿ'"°‚Óørm¼ã¸÷ ?×Ýúú(×Yu{(AD°9ï9ËAÊ·ªh &° U²•­¿Áïp„Àúu)ý1ŽðûßË °‚:Œ„¬: qh€mDW4ЀDCiŸ²@õ@¹øÝ.l€3¢Óz`?§Ý΄“:^ÿT83 Pªz”[ܪd8C9\ °^Õe ¼àÏ¢§‚w¹ô}’R@úúÇÈÿ1f¾‹X¶ü÷·ý%Q„û›òh fÑ€]¼ÅÃ8Æ1–±,†;Õ¼š´àv€\äð¿Lîø*6Üï„I,åýTÙƒRŽò1L$-"Ð ˆ±‘´€@¾`LÒS PÕ¢8ºaN,˜ÆC™ïÅe!3bW ÛØPÅÅ4TàXÅ2Y°*à—¸T@N@·t˜œ™z›qYM)D`&ø€løÀ `• dÐ@tú“‰3ð!0~ùχàˆlg6 ¨g-‚’(à‚Ô½i Á- ÚH¾™ œ³ (Gý©P%\ H@D[   ÿÚÌÒ>Ï9ÒF‚  ¶iMÙ‰ˆ €Xé.&@H6©ÝèNqÉ·€Ô:]jKJŸ¢@:Ö8ŒZ`“XOBfÐO©r”5ˆé)j¹¨¶ºõ­p+£¼ÈÌüãEíeC6¯ @*|ÍÂÉ `vf]jr€ÆÏ•ᱬd'k†7`TUYHP ‚†” . €Ê“‰L ,àj]b¥ÚTYŠ¢, í?öÒ€°jC{,¨Í. tÀJ]-:P[×.âj“&Àµjƒ–ÐŒ2ƒî w¤ø€^û† ˜À4ÊEÀ › Z^HÏaÿäu š8—»l¨À°ªÜ-€¼ÎØ€ Tk$¿[ï?Ûû^ø¢A¾ô­¯}ñK  @¶áØ‹7ìO˜ óUð% pßbx=e @ JáFZøÂhA€4\‰  À´à€ Ì¥œX±Ä¸<1Ч ‚#±øpmQQP©¾ÐŽMìÞKùq&``\à&?N€’—œÃ ð`ÈN>ÂŽ,åKP@ ´¸rf5¬åàrùs˜‡0ð–Ù`œ,ß¹Íov B0ç%T€k¾s%p‚YpÑŠvŽjM: t¶ÐH#= lWò%³¢ÿ 5J«ë(,¦‡PpZ ô)B`×Wû`45õ÷f°ç  Ö¶¶ zmˆÄ#ØïX›u]+TyÕCxA¢‘í0¸pA €R3ÛY"¸ôª90j[â(ÅVŒl |óÛ¾zÀ >½ãH×Ü•X±»ëj|û 7†·³@Ðh€LÓžv'¬= Mû[î8ºD€æ0WàØUJ€Š8D PÉÛÎDdí<Ü9}”8³,Pƒ}Ï0ì¾Çh%!€>Æl+Òíˆ Øß0P¹³0€BàÞw]€%Pîå6ý … Bnk”Uè¼’½¹{ÿ³bà~Nó˜‡Ta¶‡ (ýäõ8Öyå’ï¸ÈÀPg”K,éðÐ%þÎ ¨:% {„­Aƒ¹3kl—%Þ{á¤çã(O^R¢O¾]GR¤K“¾ó‚ïv<«êæÉó";ጙH‹{tÞŸ7«âÌÜŽyýÕU/+È;Ùõ»€cP „Ý ˜À4j‰äJ"¥…?<" ³(W‚ûÉ÷6ñWßè»'#¥ÌçðF þú ò @$€U·d¿ŸHÍËbýJL ÿÎ!j¯€“6~³Âkæ‡ àÀ ' ì÷~*R$ÿ“À€8 ðÐ ¹§ x«àqñO7 ^@# Zd€²"×f\! €ºÀ|Fápo5§  ƒ›À00· q}ÿ|•p{õ`z»°‚,X:<çk^× 48!àÀæ 7H· ¦u)ðW³±Ör%óA8ƒ#Á–€„’¼À„M¨*ðƒ]T1§ %a‘Àd&ƒ‚Á À Z1‚ Á( 7.§g 0%!øÿ6ZBᆕP¨‚q8+P~¶>V (T»p‚cǃšp°” ‚ò·wÕ& €q S8ÿªØ p¸‰ ó;fr¶¦UØ—|[¥}P‹°@ Ó@ƒ €~`d–‚¥Þø0MÂ*"@‡3ƒmBß(€{±QÈ ZÅKTpqçG LÇ€“0›ªx ˆ“±õDô"Žã˜)Ptæs]Â[¾Õ> °‹1~a ° )o× ùÇ|о˜ ¸"­(Z¿Hk1Oh`ý†$Öu).ÐàÚ%‡ XEƒÐ—™ƒ0²“" @Àõ’™Ò‹Ø.!°…ဠ ^›ò°)Ù Ð\Qïè Õ¸ÿ3Ç ¦Rñ•PŠ0@bP9)#Pqܵ™N0`•¼7^PpÀÆ&X8#;Iu­PŠ+à’Mžh`kÒ °e Ó€I ö‚H€—õå™™½prPù‡b—Ê€4°lšò4à–»µæH°{U"<Ù“ˆ5—+àd!°»}é)à}âp,yC …Ù´!z|™.›cTŽ;vhº)‹Ö)×I9wX “JPà‡pÑÈ%PÍâ A³65%v _%ŠywJœb:„ƒ7}”·³ŠÙ ™J@–eÂaÄaÆÉ,31ÿ?”¢Q#‰b´F —˜bAQô;r´0ûâãY ¶¹P¬ùÉH&íyœ¨RH–B¡“rWavwaòU–Ÿž¢Ã7a“2;1 Y©ÉUÙ%%^–¹) F,dšêm45ØI:GºcÀ´É 8 °Ó4¡—" •"# ]º ÀŒF°(ÐÔ ‰õå˜)àF F—t)puUÚ:R橚\j -ð;/ÄŸ”"¦U3H–)¦ p¢ÔpYªë†$×H§ AÃg)$à0}úFyÚ?•2¨PäO€iÖ¦˜ ’â0½360T)…pcÿú)p›Bð<Ê Ð UR0àf$Tª·J)ƒªªâN­êª±µ L1«I”¦ÁA¨´¢Ö09šæ©Bè­eÒm›"Gvô2#1 p59£**­Ð&_ѹ Œ1-€/Á´9Üz)OÊ8÷c®™À[(Œ±{,ð%БáP‹RnF¯}ŠjøbKð;}"¯3šz¸$Îi\Wf$了µã?Lô2ð7.Ë?”F/;:Ù»aåÑ·à§s—àƒO0ܘ ¤ùgÑzE$‘ôùFVD;“öOêlÓ0õ §¦fË…¢© `G§G´§5ÿÓo#W'’’³kˆÓp×q´—ÊP´½ ÆŠpÇ)¥zE@¦t)²t £9$XË]DK 9á~úŠ5–+ÐG/»)e4)·€þȆ."±“`’à•—pÏÆH%  ·»žÊp«`u€[j»C)¢“) ªÃµ¸®uÜÈ–çå °,+(3`Ì“)\ °¦]qw8‹'‘–X ÄÈ2Fz_A Lç_*Òb H® ¥8‰2}T€âø† NŠfZkˆRQ– `$¦= ¶øƒ)MCB!·Dè®{}1¼ ‹ ÀEFf @ºÉÿ]û 3‡îA{ñ Ž L¹Û¾«E²NÆs*$ p!W„“請šÒájx„ìFر¢ lR~–ë‰ ½É aá"$ˆq|ë ÀÁ7­(V/Ð¥©…X ¨Ç+¼Jö‡ÐñA+›Ñ«møƒñ‹g(Û ¬ `'vŠç|›±– K’Ž)pF|a'¯v0ˆ0ZÞ7Ê*€tðA|Z>P‹…µ1zÒ!Z˜°E±Ý9 `©oé¶ò°||Gc„w„)QiˆI‹uo _@É\w0ÂDÜ)€{ÑÀ @4vÁ};G†ýõ¦ÿG0®o 9l’L •g‹˜7{þW˜"rŸ,t‡j\cŒ¨Ò²õ‘ pZQ’†è5\Hܦ¥¸‡ÙÁ»iî#Ì—Pð Ê$`îæ&t %ë9ܾ˜ è€QÝ^½:b‚êù‚ø ½UmÞHÑÝÉdñéùèŽÞ%ØU£ vr @X.yÈ0…hƒ8r;è õ® ›‰Îø X,ñ>pKçèìøÆÇ®lœ‚jÖlÀ¼‹@q –ªm…œ…÷¶ð/ï 1/ó§NÀ6φC’²Z#²å¯»ÿ0ŠjÂmkS_Z>šÔ½‡Öge/„(À™B0ó¸èŽ¡ôÀ3lÜw _&Í)Q_fgo\Ð)ŠEYŠŽ˜!àæh÷o™‚ ~ó1Áóоð«N¬6ˆw ÛaÜ5âœÖŠ`ßÎ@NLðð¯@u°”!xk)ÏXE±.¢ð]“^5:ø‘¶ø²4Æ+ž˜¯ /pôB@º ÍÁtG&Òò‚PÏ©Ûd¯úd}ÀÚ.IÿjÀû$!.@÷H°ñ WñÐæ’°€˜PŠÓ÷ÿ€•N=¡(á3‘Iå’Ùt>¡Jkó³^±ÿYí–Ûõ~Áañ˜üU9¢iõ`,¶¯ 4.ekcž È è( Ž “œ (ûÔh2„(+)579>*ôBEGIKK78UÙê($&› >TªDCVz 9'0Š|‘ F,¡{R•«­BL·¹»½Ë¯Çj1P Ô×LJ@K/È• $&” &£YzCœ=ƒs ”ø¶aCS%2$wà…Ã,¨$®ê‡¬Ø£  (HF“j °g‘eK—V^<É @ž—dÎ\SÀ :F !†€#-¤“)“ &^F•ÿúíBĦš¸z檦a0 h€$è0Ò€häìªÓ@„©wñŠú k.6_VÀÐw“@P¬x„"Ý"PpWŠy1gþaie¡ î†Øç™È‘Q Œ”ôL €5ÇŽ}Më&Pè’º¡³í' ¤^IC2ß3 h•½Él ê3 âÿ ‡…ô‹¥ (€VÂìƒ^ ¤€„Só€À3B* Ql¨*»š‚Ùÿ6`EÒ€DÔD‹ÄkÐ-ŹáoFÀÁGÍJèÏ;:xæF· fG‰˜È+·á¯¹0¼ª®CàÀ'#ó ¶):4,Ù$…F»Š‘ôò ÁªÖÄ<ŠÌ‘rD3£2lSÐ1*0Á¦@¶:ï¬,Ï=#ëÎ6ÿ†´ÍtŒX Ï¾;ŽÔo*5)ô„4%ì€Úx’t3 àMq£‚  ÕžpÓµ$ÌT)<«« ˜€$Øò rÍŒ xÓ`]°’Í7ÏdŠ œT–OkûbÀÛ¦5DR‰ÐÖ^/¸E!eÿ&`àƒEÙÜÕ홆¶Ôu«“ð `‚0`—Žxˡؼ5ˆïíX‹ .0‰£ˆ–Bà8×\ T !Ý„G’ò ЀyxØ‹}`D‰.õØè/Á ˆe‘³òi`•ï…f«é烒¥y$ z3(…^9€vmù9‰´3š`€£áÖbƒ8¡nXGîà»âþ¡‚X€àGÜhÀ®f@ØëU)“ˆ²˜\ ¶+^;l{x;ðÏA×ä~zÙ‰W1p+*hÀ™Ç¡Q òƒ È™Ž €‚ZI lx×nu¢5C/Þø/ÿ6€t|-y 00á‚@¯PA†Ó`ïÁƒÙ `0A ¬ì ÍV °‡øãÙo‹ .°{¦™7Ë~³°©WÇ×õ™½&€æuÏú0Ž †r1¾ MëL§Šõ¹‚œÛA f° ^¬ç¸vIX€Žp€¡-!éS_aC†ì ]K˜é’APÈØ†¡“ ʈE E ª ‡3±NRQ€¥Ð?$3ȘE-~á5Éîéð-ŽƒÅ-®‘WèâU%;?ä'jãñøƒ ¼@‰{’ãkå¹<’H„ÿ€ãó€Írn#ä#Ù¸È@KŽÍIŽ¢A’“[ YH`I…eòfTëä)‰2€2U#Ñ€I©Šz¡’–YYd ˆ’ÌŠå8  ÆZ³‚P rIì !Âû¥1nELif‘ñk@Dà< Ï´††5Mq±PÁLðXz“ €ç;×xr±+v„ç=S‰IzöÁÃç?Ýg‚uîó ø @Z<ˆqöLèCç‚2Ô èD1ú¹Ì“¢O¸LFAú¹   £jøKHQ·¸°¤¿ÀOJaÚ± Ȩ¥N8iLqj¯Œ±¦F@¶ÿrÔlŤ§J¨‹P‘ª)ΕZÑLêSÛD¦žQ%Pµ*–*à‚²s½êW#Ä%ÕTNëY#¤gÒ@hu«„BÀSv"À}ë]3ó–fò,0%^?ÀêJ¯…â_ë3åô ~œ³W4AÑŸ‰íÆbPáñ𬠼*›Èz“²–µˆò>àY:åqx[¡ÊœÑÆ(­i/ëÚÌÚ ²k|Q9ï&¡B­pŽð+nOûÒĶ'°g_ÀXÙœàP~êÒf‘{Yâav°A ª`7b¡`«“¢@ìšÝm(ï Ë%ä^P…Ø@ºËáÀä ÅDõV½Ü¦ð7‹xÁgC`, `åu–€Ýþ^ö©%€YÀ=öJ÷uÙu&€qMØ!x‘fI ÂNù&@oŠažÁ]Í3¸bcœcÌÌ(]i±âtd«À pð*b ™Éy)Aàp8N´émò•ñ¢JÀb¢Nø .–Œe1g†n  ó&êC[| Óó›STÍÅ~@bÉßó—ðí*à0œ=. šÐÎc;ntpsec-1.1.0+dfsg1/docs/pic/flt9.gif0000644000175000017500000002136413252364117016703 0ustar rlaagerrlaagerGIF89axÂæ@‚ƒ„ÁÂÂDEF@@ ·²_,SP5Œ?y|y7b&4ðððááá2-¢££ÑÑÑ466cdeäݲ²²$&'’““sttSUU ÆÁ11 ^\  c±AÕÏ ™–¨¤ON Dx.mkWš9)­M•]¦=Ї*K"# u5fH#@—C‚S'J¢HŒ&$1V"€:oJ„1$?j1\=m*=7j½E¹RŸóìÿÿÿ!ù@,xÂÿ€@‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²Š¶··’³ÀÁÂò?ÇÈÈ› ?’?ÄÓÔÕÖ™ Ú?Ú»šÆÎ‘ÐÒ×çèéêƒ?„ïà@ ‚¶,,Â@Â1y‚H€ÁA! )”[G±¢ÅXíÞ ‚VáÁ@2:¶«]²Ù’Is¬»dÍ]œI³æ'‘Õ³ÒÝÈ ƒƒ X aGž<îbùƒ :mjÝÊ5NAÐ~ ô•©N D‰Ä¤  ?ÿ~”0¨™Ì®xóâýª/¡²@Dö@ãÚ¾-ý~¨·±cš|“õ)ˆdB ½;ÌqŸg‡‹ý>M]dÑ“$ÉÀO ÌüÆú`ýàºÂínéßÀg¾kð Ü€ŠÃ‡<.qfÆïX?¡Ýf ν{«á„Œj¶LJAMÌ![Âv½ËŸ_ B€~ƒ-ÄÀ¢ÚŒÕÚ>ùäŸU°Ýc~ƒ@€@ú¥Eß„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,Þã$î“É> ŽÅâ-pÌ62Â@= ©‡‰e@XPŽH®³Tÿ{àtØ%4>'d’T^ÃÍt…(H6»@À7)9@…H‚‚ا?ϰ€RL©Ùà}UÖù vT1‘k™D’8ƒýð~ $wÌy=5ãÒŸ*-…Á Ih礨ÌDf¯U˜”Î8Y=úzœGtõÁq½MöC§î TAávÌ@XU@é­¨xd•n„ð)¤“Cåvø1l;¶Âæê.Or³«s¸F+ UÚ,)¡¯ã;N_ÒHÖÓ;í¨¦)³S.EWf’J«î&ж$.¶“6åDÞŠÄ—eOû€PÉ®ëo&ÆFsË•?ƒU¶”=9mc9UÕ·½ö$oÑ@ûïÿÅ•pDHÀzô(É¥FÂ~9,Á›%{¯Ä³â·äéb,³#™iT—K@À…ŒIoiF²9QÁ”ÖÊ»è¨Ìj«Î¬ô# D σ¶EÄ€Œ°]ÀÐ}T?‡ SÛr÷è4‰îÒhã…]Ìi·MQ-1¹-÷L:±=÷ÝxçýJb|÷í÷߀.øàëâ* øâŒ7îøãG.ùä”Wþ¸ ÛNâøàùç ‡.ú褗nú騧>:™k."çªÇ.ûì´Óκë%Â^ûî¼÷ûí¸¨»ç8îûñÈÿÞzðï‰eBòÔ# õŸÏ<ˆÎCŸÁàgpLÿçØ÷>”ïƒöÛ7O¾çÐðùü0ÂüŠ0úàO€þ¢›Àÿ<'Àþ…NüÜÐ:ÅM`tü\ñðÀ.¯}êÞä÷¹øy®ÉØ@½L„ÈáúŽñ¡¯…ÇàÀç4Àd à DFç`xŒ(ð%4P?døtìÃà†4ÈAÐï2ô‚EϵäR¤¢ Y÷ƒ à|1äÀ1DàDñ 5”Y0ÆŠéóAY¸À L‘„H¼ /¤ÁïO‡>Ѐ èAã†ý$!7¸BØM ‡ž«_ú0ÇÁ ˆ¯ ŒãùâȺ xnÿøÀ>€Gˆ`yÜc‡4Ø1{_4£MØÀX2’‹Ÿß'¸À3"ˆ“# Å_–“UÜå!ÀìéQ•Òàg@ÏiÀ…?Hã,ßwMdh—ÛÜeú昌q³oËäüĘŒé9šK|ßó9ºqžÔ¥çìÙ?‚“˜äSçùŽ:uú@|¯,è0ç'`zòðÌI7F'ö2œ³$ã.úO}ª3Ù| ëThP1Þož2f?P?ò‰ ‹©Œ(†&:º âÛF…¹Aœþ@§ÕAë÷=Hú@Œßc¦øXPÄWô¨‡Ì!AW(Ó™Ê3ÿ˜*è&Æ!‚‡mœ_W5ðU²6|,ÁÜ9,5Ž$êô4Æ $T­î„_íhÁªòQžê ¬`©ê× 9o°ˆ¥^ +ŸÃ&ö±½[,c»ƒ8ËYö²˜Ílf17Ù î³  ­hAÛYúüÀvîj)\aÇ\8µï­Akå›Úúž€? AjWŒ¬¸ÆHþAxàä+ص¾±„ìåc¬ µù]q‡q`[ÿy+GæÁ1„ÜÚúXJ®oH`˜¿XæÁ{·lç ÃZ®s”ók_ÖRù7 A~»Ke%ó`Àâe-‘ß `:§vΪm jí,_þNZµPÖ—eBÿ9µ >¢ÁhU§ÖÇ`r†ï{ýò×?ÈñÝÌiB(  Ó5âÿ|Œó~:£öujoûƒÕÖ8Ѓ^õ‚y@íY[9Á@1µµ«/£ù)È0›Ûœˆ°{K†Ú‘ž¦g?Ÿ™µ t·U{mV :Ñú>†¢­ük(¯{ÜäÖî” ?gZÓÏäË›¡ñÀ[¶§c|Œß:ßþ¶s“¬mÕØÕPm‡¥ÌÚ@÷z·)ÈuŠ=Þë÷ûµ›Ž×»!öžÌ9èL†0“„"4§~pI)/z$â|iÁŒŽö¶ÿ_ Z¾ˆÞ0¢]ýƒx P¶´µýýo–¿×ë`Oô¹°nv#ÂInêÏžö y+}cþq€´m¤é>ñÀÏÏÑD¥ÿâ°ÉúHÃâ]*ɈDŠb@¡’Q*G0ݽ‰éÀ–K®Ú”ùâM£ |òUÓ`ôcûÔEŸè##õTþÁÃ!žœCìqŸ˜Æ¢<‚J ¼u{Áœ¤G8ÂÎ1*  ±¤âÍ)U3"€%íÂñ>‡ÀŒ*ŒÿàEŒ`ú´k ]îæ¼³÷@÷ëqg·±ÔMÝ[µègíŽ7Ÿò+@ÂFm÷7lµG;bqó| ?ÐÑ}áØa*Ë$³*Ý'(óð%uR -¡ÜàÁ€Üò›Á$obïአ~ [Vƒ©åÿgmçv‡`{?ðÓ×nb‚ ƒ9óBø0.8É2y$8%UÒíB‡r ŒrAØ×‚bC…‹@ƒ8¨hSÇ`5àe‡g€›² ÀÌG„:a !#qNÈ)"£) …›‚# ÆV3®Ò‡¸`b(,2ñxS"¸` @†e¸Z$€g“¨g›[9ÇsÃWì1(Žøˆ:“=ù7I¤Ð)¦.pŸ0p 2¥?0§·9¥xÊŸ(š p§Ö¹/™yÚu ¥=@Ÿ9@¦ijgJô\Ïö|ÏøœÏú¼ÏüÜÏþœÏþ ·€ƒQ*,Á޽<CNQ×'Î>A-îè,Qa ˆ² àGÿüÑ Ò"ÒpÀpY(Í; Þ§ƒœ‘$AÀáèe¬3ànÊ=a¢ñÅì ÐD)=Ô³3&MÔH-;+ ýa ~Vó"¶@^"(g"tQ æÐ â™ñ'þ0Q½%Œ" €{«b̪ÔIרcÔpÒr}× ©Ôxù"T< pׂ­@G=؃ý‡ñœÿ)(bÌ{Á؆×t}ÒfUÏ%ÐL‘ XÉ#1«Œ!Hy è< ýAö<yÙ€ÙÉ3Ùy=Õ$ØD$ÔÔã~bq²{Û¸Ûº½Û¨ë¼ýÛ¹-²MNÈR† =š}<¬íLœ£80$Ûw]Hå#ºÔ]ÝÖ}ÝØ=8à E‹FÉ×ú„=Ëí9ÿdJÇ@@*>#p=ž@0•Þ°ÞÄLX5;> µ>PßîD ÄïÝÞ#0ð>ð"pF¤V®ÙU”å‹6£­R #IäýßYe@X„tÅ"Åá%`@ þÿ£ôß">?ô}ÙðC+ÿá*PAÏâ0ý£>5^P…MÞçý9/õNTDF´KETO” Gt Hn?ÉðJ • QTNPÕN1Î9ÅEZž×»+Ý>þT> áUŽ ¡ÔHE4i~ k=\nCؤSGž Ð?-HØôS#ôScDÜ*DØuÝÚ‹£âs?"ÀTTBÊÄ%@‹®BŽNL"dI]DA t?˜~= >ÿ#FÓT<™î˜“¾LB>THbDPõCF%TAç“% 6æðDë‰aÜÙÃëÔßOì?%ä?@ºTC¯ôH?ð@ºDJï9/eçF¥‡„J¢Óí\TÏ?Nñ#Ðçâ:"òÉ€QåA%ÔòSHÄGÒÝM6ôÚ†TárCëóáóSQÞëCN@<å…TƒNåí{~ô­ŽçvÍñ?:EîKã¾PQÿKCîQäòÙdL¿dìôÔK\_àÒíDé­æû¾ÔxCëvý9õà^Þmÿö>ô?¯K2Îõú£K\öqÞþ]C¥ôIAÞH¤³ÿ@GTä#¿K‰õB•õŒôReê! ?…B'5 ïøK&øh7j¿:ÇPAqÿó\dúÏADæÁH~ôo@§d@úô!ÀÞ\EO„_èÌM:çcW! ø‹ïÁ/ÃïøùTCú]B–ŸM’.ñï}>,î?dWð]úüM£/:>ÀN>oäÖDBãóôÔú %="P?)EB"€â?…üâãNúÔ÷’^?S>†>??‡Œ>Š?Š>?…†‘“?•І—™?š‘!š‘§!‘?•>¥§†°?«Œ?@ÀÁÂÃÄÅÆÇÈÉÊËÌÍÿÎÏ϶‡£‡±#?#ˆŠŒØÚܵ†‰‹† †Ø?†Ú ¸°‡é†¯°íŒ‚„Œ¡¬ú A( >8JȈ Á… ºƒ8À,B P¡ðf9 1ÑÇ‹€ ѱ×/h0cÊœI³æLi¶´&bã„¡€6ê©â'@“(1-G äG"üi$i¨gHFj|؈_&`ÊK¶¬Ù²¾lª]˶­Ûa8ÏÊK·nY¯vóêÝ‹öåÛ¿€ —¯áÃzñ"^ÌØlÚÁ#K†Y¸±åËÔ±À¹³çÏ C‹Mº´éÓ¢O^ͺ50PËžM»¶ÿí ƒ$ØÍ»·ïßÀƒ N¼¸ñàª]+_þ€‹УKŸN½ºõëØ³kßnÝÅ <‹O¾¼ùóèÓ«_Ïþ|ræðãßlУ¾ýûøóëßÏ¿¿ÿÿî×Àwíhà&øž| 6¨ ô(á„VXက—à†vhà‚†(âkZhâ‰(Jˆ¡†èݰ›&xè¡@^6ÊȈ#ö(„Ú] (ÜÃ2ÔwÀE¦¨ß‘IÚ'Ã’Q:ÙÊ-v ‹)蘠ŠŒ·‚"$xÉ£h*d6¸ -Ø×ÀÔ§È'¢ðžùÍYg}0€ðà MZ‰¥y$(ÿBC à È ^"æâùC™fú•æ¦Ë­¹ßŠÁmˆûA ÷8ÈeÈ<&à¡÷~ Cõ…g_ÌJ”¼pØ>|…§b? – íH°ðÏ<뀣f‡1 à‘Œ¤$'IÉJZò’˜Ì¤&7ÉÉNz2“ˆDໜ ªÛÖß܆AA!/\”ž™—-à¥P ÓOχ½ªpŽ Á³L`©:Š)^ÏòÀ¾êh#eæpR>¼WÖ´vÌÅÏð'pŸí³g®­FŠ=N{­ÖXž™–¦ÐTb­Ä[Þ®¡Z}£l[Ù ¹Rmo{ÛÝöö¹jAÞçKá·@Å}íùâu¬$*ç¯x/ Xæ®Å¹ÐMo€òöN9 b8Н|çKßúÚ÷¾øÍo}]”ý.0'Ì Ì`IN°‚Ìà;øÁް‚[P€Hø3;7Ì៳¼ °@b Dà‰nއWÌb Å ð‹ƒEÐXÀÀA à à€8 °@xÿGLc8`à•‚ÑãðYÀ€˜ ã.{¹52~ð`8@x.0e (âÄ‘˜ñà‰޹hgÇ 6CºØñ—Mè·x”á…ó‹g !yþE£0çÌX~ô‚ èيس" QaÒ•~qð P?`…޵¬k`]T`ÆgŽ@0.ðƒ œú‰ˆ–"vœç'+Â/öõƒæ<ßZS€ö50N|äY3—ƒÈ± ­ Ày¬£ÈEá̰À¸ÍìÛÆ„Á)¹Í Œ0£;êB­u1cšÀÇW¶g¬ÿÓmCcÜ!‚@ža¡knÃ$Ì@Pø0 æ[Gò€ ¸éÍîz»»Æ?³¼ÇLo}»[à„¹,I‚Xc Øwr5—&(³ˆ èÔÔÄê´VçDw@Œ›ÏÀP„´ ˆ;Nt4ÉÛíô¦?ÝÆ¼¦8¥@„0”–jý‹š?ÚÉ2q{ˆ:@ß¼éh3¥Iœd¥@ïÀ¸!ðÈj~°€ÙÍ6t“WÖNÌ-Ÿ—žçÜ ” óåÉ ^?À›õ¼¹Î艓ó@`€"‚Ê Ð´Ô?ü‰}ívüÉ€‚,ðÛÿ7ýÉ”.úíäD.žÒtþAš­ˆ›.[à¡®;Ã×=ñ«ß®ñ Èó‹%>óˆ/ÓmV„¦ÉÜæŒÿ‰m޺Ƿÿ}ò›¹Ï€à˜Å Þ3?ä£÷>,ìüƒì; rŠpsòQto6dÁÐin?;^¿vdawndæ&¦|Q÷f'@ö øˆ§{Èt…‡pÇFxŠ7 `N†p¶§wÈ{&HvfÇ.|4ÕxÃàvêy?·iäfzE§tšw!BNÔNFuBHær¹k@pboâGiAG t×…ÝvrÆàƒòfX®÷g'7fHwbªÿo=BxpVf '^pv0jgv†o&^ìVu`è…ÿõ‡ÃlÇ@†æ¦†âåD‡(oëw"Bd‚{$6€ârçqJ—Ö•(Sö…‚øH(ˆÌ0f —€0`†HnÐ‰ÊÆˆ&׆øç èfNèÑ—zmv‰g¼çD€ÁФXŒÏ z  °'‹6Ggq¼§~±èˆ!Rzôm'v†"¶ àDÌ8 G¦¸t¡ÄqhŒè( ¼6q i¢·Š¥çh ÷ Òf ÇIØ~dvdÜ÷f°Æ‹@°Ž;6gð—/—Ž º»oêwo[˜Óè„nØ Ø&Q^Xà%+˜zD 7 Ifeö'Š y’Ë0‚Á& !ùv;öoÚÖd;–{/AZ&m(™“:¹“<Ù“>ù“@”B9”DY”Fy”H™”J);ntpsec-1.1.0+dfsg1/docs/pic/thunderbolt.jpg0000644000175000017500000011347613252364117020400 0ustar rlaagerrlaagerÿØÿàJFIFÿÛC   ")$+*($''-2@7-0=0''8L9=CEHIH+6OUNFT@GHEÿÛC !!E.'.EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEÿÀœ^"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?òÆc¸òzúÑ“Ó'ZCÔýh¬Ä.ãê:LŸSEÀ2}M.æÏçM¢€¿¼:pfþñÏÖ£ìúR±I’eºäþt›Èxþtä$á@É5r(Li¹íYÏ© ØÙ+•âžhùŽG\ú²5;´ó‰¥7Q/ j8ÞY°æÖ¢÷è^ÀºÍàãp4Óª\œüÔñ6žã扖’xlŒ[á›ÐÒÓª*=̯÷Ž}éžc€N>´œhëWd&ÄÜOñΤä.wŸÎ£Æiê:zžô؉­¦1È‚øê¼Ö¼’ZÎ ÉæEÆÉ¶–W¶Þdq#³wª÷^Îß½‹¶‹i”2X­ÀÄR»cD0DØJéëÍ]µŠ[Kµº·¹¦%óÜÌ#HP’x㥠¸ä[(NZyXŽ€©yqæ¶ÔgØ=M^Ô‘Á¶DITV:“FãK¨¬ÅFzr‚à Ì?a í✌²ŒãÚ˜ôûÁÁ$‚=zTB2[Ì8õÍM$›À,ç¦*-ç¦qBl›*žÌß/\ž´ð…ùÁëëP‰àƒFì¶sÏ ¥¨¬L¨þnåb«Ó©v“Éb~†ª<ŽŽ3J²>Ý«Ç~´Þ¡ÊÉåvèþuf#%‹LÒýî•"¬d`jn=ÐÌÃ’GãO¤PNK} ,úfœ"*2ÆzP™,F‘å<È÷§»€FzàÔ‰¹Fƒšd8«¾„Œx ‚ØúÓ<™‘v«•榎ààŠ°Š’“’{M(½‡©]Àf$ýiã=rßLÔ¶ÓŒô‡hS´’jÒ Çøç@Ê67Δ+)ÉäPÛˆÆÐ¿­;æcØœv9¨Ú\¸ŸÎ‘•‡9;GoZlŒ„€Ù&¥»ž`ÏúÃùÓüÒÄü{¬éUÏ=©„œ­È¨çhv4#¹9÷¤.FrÍùÕ%gÏÊ •dulõ§íÄža9õÍ#¡aÏ\Ô-†“9ÇãRœúñïB•Ä=&ÝòîËv ÓsÎI ŒmpTƒR³|ÿ†)«µ¨9 ãùÒã çŸzi#o§éÁ_n]±éNø¬p@ HëÁéH]†9=}i›|ãùÒƒ¶3Îüé.í™ÞîHíši''>ôÌ“’ÊXc­:6Ucž¢®$±c´†-»=3I¿‚2r=é¬9$gò¦Iç§cI¶€‘[§'ó¥‹gyÜÔ`ç¿>¢Œ•G5·08#–=ø4ö—+ŒÏ­W§j\ç9ÅRŸ`±1@•ÈoBzÔK¼® ßSH±å°~”õŒ¹<äjÂ`üýiÑ0.IrIëQ5Ÿ•²Ž3ŠC$cîî#ÐÒº[¥Ÿ5™±0Àõ 9FÎæaõª‘\*ç†õÏj{LÜÇ#±ëIOK±Ø¤`GùºM —ÌlÝ8ñP—Ü ùÛŠ\úŒ›{´±üóŠ|d³d¾N*À!8úŠb\"·$ƒŽõ\Én#—?xöæ’•¾ñúÒVÇ8RRÑ@ EPNÚšVq¼½ñIè4®Á¡pEiC¬Í…dVõšÆ?—Êg>¦«Æ‹4Ø"Ÿ^Õ“³Õ£¡v/ÿjÆÍ—¶SN•©'6ÆÌd4¹ÇzZYH>Y°}ÍGºPƽ´)l ÷ªSKæ¸`¡}…>êÝ`pÃéPíÇz¸¥º çØRhÚ (û¸íïVH½)A>ÔÁÉæœ"‘v-Nê •ªa®]‘ŒÊ³ÂœqNJ‹"¬Ëfòâå¿xç”¶·¦ÊbèëUC/éNeÜ=Í+ µw¨=ëeøöTçvzûÐ«Žªõ5*D7ðÚZ!ì1ç忤$<ÅN¶Û%ºõ§ª2gkŒبr%²R¹éI8WeÚ ÔÔïhù$Ÿ­AeU‹7N´&/1<Í…Æ*D‰9ÉùºœÔ³«‡ÞœC`ó×­1Ø“Éó3´‚½K¤cï?>‚ Œ²œL€‚F1S{ ¦XeLP¼I@Å9PääæœŽ}*\‰±ɵARAúѬ0Y 4öFvåS¤ ¼“Ú©”±äm4I.P€àb£‘Ó€gŠ|ÏbFÅÉÏ5pHй8öªíí4ä†F ‘D[[r1Œcžô ‡’*·–TÄ7F*„G–“Œtë@ÿÉ«"0HP¡QÌ*I8ɧˆîjÆÜô¾_AëK™…Š_fPr3š•`UÁïVB Øê}©V<œÍl5 X÷¶Éö§˜Ê6TñÄÃ;zö8©Í¾"ýéæ…bšùk÷²~”Æ Ï0>µ9µ'˜é »ÆrË×ò¤)å—2Ý…FË´nçž•0CÔqCE•>µ§AX¯´¶žõ¡®ídÇÝ‚ØHI'dÒ Xàci2p ûÕ§æcša‰”ò)l*—b0~Rh G\œô«>S2çÅ ­Œ‘Á¥p±XÆÛHÏ~´åFÏ­L®* æ‹ÜV ®á¸ô÷¥šËË]Ùià0#ð§î žç½iaØ WEE ö<÷}á@ ÈêsP4EOB*u[ŠÄ€§8ÍC2rH8#°«…9\Ó<±ŒŽ½ °(}ûS^0H݃žô­nË&äu¥) 唎Ɵ `‰V`Üuiñ¨R­ÕqÀ¨)éëŸJrÇûµ ß(ïÚ©2X’™2ÅHÚ?J½«@®O˜ÞõYrãp?Zr.2~ò}iÅ€õ/‡¢ŸÎ‘”îË0Ï­N›dª‘¡õ¨Ö݆Gš Ϊq{!\ŒFåK‘ÐÓ­çhƒ2jU!Tƒ„aÞ i ù@ú*Zå³C,©,¥J‘õ¦°Áçó¦ÂÏÊš“¸AéÍj¥tHÎÁàzÔ,7}ÓOû¼8'ÐÕv‘‹íPTÞ±c%°ß1Ûš~í¬v¶3H‘™<ÿZœ(Ü(HîÏr+ÈvŒsÆi>nnÑÚ‘£_”n9èkUn¤“Åóe³Üö¦1܇£zS×!ÞB稫,Â2Ÿ)ù†çàŠ¦ô ¼€ÊD|…ÈcŽ* I$6 ·+:Ç•é•ç™H ¤‘›$œÖVW,Xe¸lç±ô§JìTmNi›y#ø8ëHÀ«#œ`RhzMνÇÔÒ¢0©ºÔ{¼®F5,R|„íQf3Ÿo¼~´”­Ôýi+´ä (¤ ŽôQ@o0*.zÑE+ÝËqH®6I€=iÆÌ–ýÛ©Þªw➬{T8¾†ÊWܽÉ nxéUdf‘··9ïLÆ}éÃ8Á¤•µ-jsŠ]œf”):±Íi:bƒÏQH[žµ$kšq%}ŒôÅJ©ÜÔñF3Ó"¥çÓŠÍÌ»X¬ tÀ9©V"¬$ 9ïVoëY¹…ûDExj˜B8=jr˜$iÛxéÅK®D#ªUŒœRªdñßµN‹·!¸5 Üi¨üªtR}1î*H­Õ$ŸÂ¬"m¨℻Œ€CÇÍ8Cž0*Ï”GhXŠ1rNGjc û1RŽ9Í1â|ãžsVØ9*ËÛ¶)á7 :}(ŒlÛþ@ÍY3“ÃdœzPÖA[p89¥òî¹¢èCWc1ùHô"—fCÊ}êB”Ölá°Túõ¢à+ÚÂTã=G¬ŒûR¬PrIöÅHvn½é\ _b}ÇpéÁ4Ò ‚­hH¬¨T7ñÍ5ƒ"†ÇËŒsO˜,eùbmÎ9àSX´DãŽ;V„ ´±T’Gzl‘þøˆùäg¥;…ŒàëÐFOr=)¸IS ¹@ç‘ZîƒnÖxèU7#Èè;Ó½ÄQÄÌ@ÜüP`$(QŒTȘ„ «Ž~µqf¬¥7zü´ÂÆrÄN8(…Šä©ÅJd`IØ?»Šx›Ux÷=è¸X¦ £‚£™ÛoAWZUc€ 8üF±ï|¨»Ó¸$·Ø 1Ãzªñ•$Œf´¥…ŸIaØÓZÛr©+Ϧi5qXÊhwäA¥‘L¯«RB?•DÈÃŒŒw©»ŠÐƒóÿõêxؤ`°sC *Ny ÎýpZ¸îKÁQºðÝ*’d1?'qR2*›Štm¹?2c=3Z=]„#Qß"NL C‚sÐsQyðy¹*ŒR˜áŠ13 9úSO³hÕP"œò8æ£ivÇ€À烕éRÂÄG–eÞzTrNeŒùÈÃßmVÐA ÖöÆÂ\þf™,®ŽÔ~c[üñî‘sÆ*)d@í†cÉœQ«VBX»yõ4ý½I‡µDжI z t©"'ûKÛ=jRÖÀ?v;g©íJ¤1íLó œ¸ÛŸN”ôPI%³ŽxèjÕï ‰p6œçéž•».ziìíáXdóÅWFi˜3m_n”åØàÊ€µ ˰²³)•=Jþ3AR »ž¸Å6(c…‚Ë×±©z*«:êû[{´ŒË‘Á˜ÒbªYкjhömo-Hã£T—ÐBÂHƒÁAã©Àyœž隊6qT&Òyôíã 'ýŸJd’ÊÉ‚ƒ~Þ M> Lc±éP3•UÚO¶O&ŸØÿ2œ0È$`Ózê<ßxýi(<1úÒWAÎQE£éIN ojp“…b÷Zph³’µ,Ñy1âqŒ\{ –)`é"qê* `Ýʶ)ÞdCîÇùÔ5äj›îI71ä–÷ȨךgRH§g­-1Ÿã­DIϹÈ÷¦Œî¦‘2c¶ñš±u¨;â§ŒÕ2ب"ÂÔ·eJíU”(úÕ¤’%;G°‘LtdŒÔЏëÎ:PŒAR©^àþ“b{T¢ ÈýiÈøEÚ¸#©=êÌjŽwg½C!Kpy ãô:À¼sÏ¿zyܬ@#žÔðŽä`ò:@Äò±À© LPíSª˜À'©Äa“œþ”®tR‡Ž{xV~ \(mÙéÚ„bé\Ž•vB«»œVZ>ö@þ!Å xŸj\m#G'ëLŽÙ Ф¯FãšÒ´„£«c+œZ®½©g]’.ÓÕAþtî#š1‡8Çâ8¨Ö'w+ó;÷­æ·³…Uàsƒü©äDÐ(b©ÎáëNìz8yÜw¥{fY@”2ðž+uV'˜ýœg³É>¼Ôw!r®dPò`Ñp3ítò‹w7rqRGj#ñ…#‘Þ´E"™8ò{€vš©$²U1*íÆ1ÔQp)Ü”EÚ«óv9ÅS†5›²9 œÕ›©ã…YT(Îþ4Ò®e/Ó­R`A©¸ŸËEŸSçU."xØðéÉ«>cÄY¶ò}J©8~ äg±¤Ø2 I` qÜ3Hò2œ1܃ '4¾b#o'¸=iŠg#ÔzS¹Ðìf†8ëR?Ëy 7ƒó v¦ùIäHà1 ü`â¡\Û3R+®Te$50    õSÍ[w䌚­$9}ø;Gbx©IæcËåF'H×3GgŒlé¸zÑ¥¬) Ì8è ¢yÑQ•yr8ÀªUóÚ@¤Ðb¯‹„1y†"¬\`žAÜ­ .åÉAÏVè}ªÌ„*4¬êÞ›G Hgùãsß'¥D‘¾à®¬á#¯µ判",Þ½zÔˆùˆ``|ÕB2؃v©¡ÉBÌH*qŠ—°ÉU·8ÈÈížÔáŸ0„ÛŽ¹5\:³óéùSÔµ~~ë q‰dq½Ž}i ³I<}iX19>œÆêA ŒŽæžÝ,ŠHÇ4²Û¢õ`‡û¹æ­lÜv‚sœäµ ÌKæÜK qT„GKaI$`ãªëGª<À§æâ¨Éò|ѳœn¥I$2e9hÏ\ðM0‡Æ ÚsÈ5mæX€21G*:ÖzI#DÁ’1~bÇš`¼Žy”L6Ä£ ¦"ü“¤‘-ÆÑÇ=EK ¡‘‘\„屯+#u¤S+$¡Ð·ÝÎ%åÐSæ$¬Ü}ÑÚ„­ÍÕ¼V­n¿ fåÉý*”r@nåÆÁÀÏJÅ7.ÑÉÝÏz#˜«˜½Z{…Žá¡$ûžúSN#Œ*ô•»W1ö™O Ìê¼éR‹Ï,+=TœÊXÖeV,ÌüƒÎz7ÐT# ˆ+Ówñ}=*r΄£8àt Ts±”*ä9ï†Î(‡É4N†0[³*Œ–ÓÉædLg#,¾XE+:©ç~:Óc¹EÈd ¸Àfš·Q29íQBË &QØÓ’ÚYb’arƒËÈùV´ Ó㸈eW9'x*½ô¾b&S G]ÜþT¬‚å† Â²§Ì­Ô·jj—ðI*E2#þ„ ±$d(¨¸*¬ÄŽû)[°ùš2wä‘ÎMVE2~ï ÍŸ¥1äÃ9 xÈ©>М—SÈÀÁþuVŠc8Iö<‘@"W¤ ½‘Ò“to(!03ëQ¹Yð[=84ЙaÑíßp=~î:­G3¶@Ê•' Z|LòJ¥•¥\ sŠ ‰2åöPGýi zÈ|¸â‘œt'n8Ôá0W%]™[Œ6Ý‘d2ÇÊ@Py4Ý®#/Ý9*:àÒzŒ%8ž¹55¼xl© â«’®0zßµMjÛd àtÎ*Zv±WFAûÇëIJßxýi+¤å’Š(¢Š(¢Š(´¢’–¡ii)zR4D›C¯ô¥\ éLSƒÖœùëœÔùŽ/‘ȧÆFy¨TƒR`M 3N ƒä¯V­8Pü ©õ…lþ^HàŸZÒµ¸&M¬ž sJ6eXÒEe˜2±÷,qÈèwPÇ*¢»2à6j»@®ÃpŒÑŠ ¼ŽéPÊJg+€Oc‘RĬãpä³µ `J oUÿ ›VW„¢ˆÔ†#¿jtdíÇ8ô§˜ò„ Žœô¨‹0nç=ýEK)ÉŒ9íªQwxS·©Pð<6/tø·†Vã<®æx¡&Âé@*w‘×§ƒ¼n=1Oš7L¥HÇ*GPGcDhdeQÄÒÕ¾^Þ„£5:çççÓµ_kFÊ0õéQ›8Ž^]ʃ‚Þ´‚år }Â1ß#¥Xü ªŒ¸ëƒÖ¦š$J#+@pÄã>ÕiµÀ9 Žƒ¥0Wäc¹HÎzsJÒ†Ú¤á{’0iì6·îòO ¦:±äõé·½H, p§ úÓ‰y A€S`È;[}ÅK;8È–#ªœPs©Ú~éå…,;“4˜õ¨” 2z~59P,\¶ªÓIg‚*#Ž0F ¬ß: Œ% ǰÉÈ«þZÈsž§|’ÂÆHÀœci' Ф"“ß*8 Ëã'?úÕ ±™dó-åYr_ÂžÑ ù†Çtæƒp"A lA$‚0iŒd°ZÉ Á"IÝ™XïEààt$ã5 Ä,@–ÀäUr¤$Žà518Ü0G"¤óÔ±?)œw©<ñ1 e}*ª…hÈVQÎ:u¢àOçðväõ¨Ú@±…PW?7sN*C® ƒÛ=)²Ùƒîïœâî+Ý€ ž9ê~µ€2 ún×MªK²ŽIÈü)¦9| üÝ(qu'”P«dœ‚Ã8«é$˜“%rHû¹Ô$ †6–L>v‘ŽžõcKie‘š)(îTóøP¡$v‘#ÌÛ‡Vç¯õ¢çPÍÌ^Dq˜Çu䟭GsòZ,Œ!Þ­‚¸ÁÇ©¢òÞHâŠ/Ý£ã%ã qZ"X˹TËþ”8ÈUŸ¡ª‹ i — ŠŠÜn<~"¶b6‚(㘀Ur<Üý= WÕšG„ìmѺŒ)9ôÍ]‰)›½àÊ# ™ç>Õ[½Ä®©lÀd_½O–’µbÕÎþ»NÞ¸¡Ä®Qo¼~´ÚVûÇëI[áE´”Qõ¥ ¥Q@–’”}h”sM¥‹L\t¥‘KÆ}¨Úr0)`ñ‘ȧŽLnHRÎiâ& žÕ/Ì¥ä9d*}êÒÊJä·=±V-¼7©ÜªºYʱŸã“ä_Ìâ¶í<)Írˆ=#óøð?ZÎM'ÜÄ·‚cÖ¶-Ÿ e9­˜ü9¥Úå¦bÄõó$Àü‡øÕ•¼Ó¬Áè úF ~½ZÂI^ãI½‘XÛ\M±¶ÏVGæjÚiêü¶áÇðäÌàUY5Ç$ùmaüG“T'Ôn¥$<„sÛŠ‹Åª3~F¹´‚"LÒ…„ÿŸçPI§ÀÒò·b8þ_ãY–¶Âñˆy°Àt<š‚êÖKi60È?u‡z\Î×H¥J7³z—å×™3öh<ÿñýilµÉ ëæ¹Ys•`j›iæ$i{)&£¸Ód†?1X:ã>†©9î>JODvÉ<:²åŠÅx£†aòÈ=^¢ª‹42¼lÞTéËDç=AèG¸®cNÔÚ©+`Ž;WV’C«À\6É“ýTëÕÇéÞ©¨Í\ç”eMØG‰:6@¹;ªÕÀãÊdŒ *î4É&»°»×d¤¸Ê8?$£Õ}ýª'¹ýãß$Ö-; ;‘³»]TsÔuúTñ)>g x¨¡Úë,†dSµJç'Ò ;Bw `$7O†‡rggU9eÁ<:šis)` ê)þPf à ŒŒ p„— ÇÛ$äT€°¢0$ò£“íQ>âÿ.p9TÆa {#ÇU3,Ží’yÐ2M¹VŽ™¦ï(’&'¨j™eˆÂY³»n1Þ©HRI<´FR:óšh fdXbbNr3ÇãLGtêŠCÀÁëS­Â‚"e#‘Ž´Ÿi\ ¢¸9ߎ>˜¦"½ä ª²[ aÑÀÎ+1ݹBA¨#îý+ML°‚w³Ùà`Tx(ÄŽA鑜S¬LV'èPcæ<ì)’Üóœ¤dtÏ"Ÿ YPNð{ŒnúTq´ed Ì*œÓÙ'O'jÛîž´õ¶i#ÏîׂO?JˆÛùd\dƒÖ§ˆ„ÆòH~Tƒœ~À˜J)l+ÆT…idÜ6Ép =éÀ!^XFTàõ÷§ùd†‘°20vžð eVG‘0³*n)«võëþ5i`uгŸ€HïéM…¥Œ¼‹Ð€JhFqp%rqõâ®ÀÆ{€ÍlrED)¤ û³œ d{ÓˆŠ ÆÙ’~áÎ?:iÜÝÇ5Ç–-™¸ÚC~ØÝØ36ãq)–GÈïøR[YÏol×VÛ§‡œ# {Õè­’m9nR`‡oãÛ&´V!œÕ¼wFm÷[³rì:ûsÚ¬ßI`ÛîS·å5bXåš"®QŸmÇóÕ #ÜZ8°ANqÇÿ®¯a2¤—Ì\@£#q Á4Ÿèð9/ºWsÂãcV¬ª•·x”}öâ«oh%RÊÝnö5B,KªþXfæËàØS$…®‘!FبێÞ8¨NRbL¬ÆC¥rÇz?ÞÚªº0ÎA sõ5¢Ô–WщHS󑀌ÙãÚ™o4T±ƒž2ñ«KqD6©,¿ÆÔ†96Š‹› *¬"}Ñ*cylä€1ôÏj§$f¤anÑî“æd~5¡i ¾bÄ;ÁúÕ‰í¢¸xüÔ0éä)Úâ¹›h°´cËK)ËÉ9©dw3…„…“Ã?:·€¶•ÒÝS9=ÅSÕPÇP¦2Tãÿ];Y äS]¥»*L7J¼3*qJlw±²1UX±Ðu¬éNâY‰ç¦îµ\°'îâ¦÷ «¼EäÈI”çx#ýzÏräåÆ ç8ëMÀ¡‰-Ï_zd¶'9îjÌ7.±˜†0xñU±Å(>Ãñ¡‰;&šåT £Â’1Á¤ßn‰Ä„îä®Þ•› ˆ&z€E5YsºE-Ž:╊¸àªÿ2žsÒËñéIîS‚>”ø~g$zT²ítQo¼~´R·Þ?ZJÔÀ(¥ %´v¤QÞ—䜀ªI=ÑqØn(Ål[x[V¹PâÉãŒÿËIñþmŠÚ´ðƒux€v/úœ/ëRæ‘J,ã±NXÙˆdšôh<#¤Z`Ì)î%ÿÙWίÇ=…€ÛlŠ€v‚0Ÿ¯_Ö³u’ØÖ4¤ú§†õ[•–N±Ÿùi.6ÅlÙø"å°ÓÝÆ€õX¿êp¿­t2jÄ’b…CyÎ[óÿëÓLš…ØÈ,þ²uo±Ð¨IjÝŠöþÒ,þi÷Jzæi1ú.?^K:Áqm)óÅþ½Z€irŸžy@N95\Úy®~ÊZDèXñŠÍÎL¸Ò§Õ–dÖX±0À}¾fªsê7R–#P8©â†k(˳¢gø­Jöit‚t5úáºn*uf‹ÙÅí¡’ì]ÎâI?9m§f±¶O ‘ŠÚŠ8¥„ýV)WŽW•4È®L›­o$Æ9èÔ¹{Ú¾ˆ­mm=2Iõ¨úT³ÚÃp†â(ĬG8¢[‘§Ìc2 =0rWëP>¥2–¶Sµ¹en>¢â•‰å›wD–ñÛ]¶?&tþéÆ=êC*L^Úä1{ôÏ¡õ@ß]HìbP u**¤ «n¹“üm“ù »!¸kï;/¨ˆ Š\HGŒÿ:¤×vÊK’V?óÔäU.­cè%˜Žê6Π[ùfc´PÆq‘»æ'éžõj3{çN;BI+’‰’}´¬oÅ¡Ï*ôÝ’?*æÚêiK}¡åp8Æpôö©JE",3ÆÃÔ¯qMSåÔÎuù•¬zJM¥f-¯G™r®̧±±ÿ&².­¥ÓçXî[|.vÅpþËz5eh·Q‹É9<>„×a%Ì os¼n0Ctüǵ6¹•™ŠvÕBÌ¥QSv2ËÅ[ŽÞ î`,XrwUy"m%”Èí6›œ,¸ËÂ{ö÷«h[p# \Ò‹‹ÔÕ;¼˜úzÒ+¦Ý²=Ds—2 ÜUfrä°>üT”]fP¬<ÐAzàúU}›³‚>cI’'‚{ž;ÔrÍ*!vzH §ÌhG» øèj1:ÁyßwCM„<²g؇œæ“Èîaá…0 šlÁˆàæ¡ÚÞYÇÊ¡¨eŸd¥I+Ø€1S«G$g’úqL„ªåRIT÷wzÒÉ,jO—ÀN@ÎOçUÈÃüªCtŸZ‹Ê@K4„©é´ð?ƘÈe”å ç>•Û²*âe$sÈéW¢´’éÂÛîœú.sZxSQ’!çµAÎù˜þ5I_a6–ç<F”Žç$gùñJsv_˜9ê¿)ØG i¶Êâñ§aÉX¯âjÕ¸°¶l´ÄÎsºoš­S“!Í#™‡Jºº’6´Ëc’GÖ´àð–¡°‹é­¢Bs—ûߥnI{xË·p1ÑFÑUÀÜrÎ9äæµì‡S± ~Ó Mq<äcjƒU5/ ÐI§Í¹×‘  ú[Q"’ÜAҬƈz‚~µ·±‰Ÿ´‘åw),WL'Y~ò1ŽÕR¤–pÝ–½OXЭõx×ÏLH£ä‘~òÿˆö5ÃjVú¾Ä~U•T?ýcXN›‰ªš‘7{övŽæ‘Be’)Ç©Å6Þk+]8ª\8×#, Lõ$r]]éϸ7˜~ã$+êsPZÙ]@KO³j©Â2òM$K;‹¿1í *lòB¾@`=sŽióY Fp¬Jõù†ÓôÍE4r"›¨Ÿç^<—þU,Ww²0VH÷rì…h„C©Ü4ˆ‰ujaç’½ü*éÓã’Åœà#.IÝœLVZZN—'ˆJªq—n¿­hÜźƶ­´Œg!IíT"§œ¶÷LÅ@œ 1–«VÚ‰‚rÒ!Ç öÅCô;¥Äj„tu\ƒQ‹xîá'zÊùÈppÇÚ©i±,ÖhÚXãêªI'#­"£0dUFÚz6r ¡ ËÛÀ@âûË'$ý*Ý¥â̈ÊK?RkDɱ<€®#¤xÁR1B§ïBO${ýiÌïVÀãvOÑwt--VI>Ü•Çý{ÖšP >i¤•ø×1}©Í;˜÷Ÿ(⡞þâá˜4ŒTŸºMWï“À¨nàK#É)Ü9ã“MäÅdžœ~´ •÷¨† ¶äú hÆ~bpaÞŸ'ÌÙäžçj29úÕ"$.ÑÞœŠ­÷›ÃÏáH;:XxR ™‘_wQŒb£—”ñŒ‘Þ‘Žq–Î)²2e$‚¡éÅ>>¿7uõªë¸*x$ùŽãÎ*Z4Nè¨ßy¾´”7Þ?Z+S¢Š(§¨Í2œ &3¼ð†tíNÕ.'ò§;±.öoÜzeA×8®Áô´Ògh`E…GÝ0"Ǹzäsú×”èZÝΉ~—6Ä8toºëÝH÷¯dÓu+/éhð¾¢î9kwþáö=a$úšÅœíÕãÛÎBÂIæ'ñ¨ƒßÝò m?€­Kë#¹¡JºŸÊ³-îÎ_"s„ìÕÌÓ¾§tq÷V¤RX˜—}ĤƒÙFMMokfÊ%¿ß8ý*[“,Ò¦Á*Æ:5Ùdܰ‡ŒHszEs6µcnm"‘YíH;xe@Ï(P¾c: ñZam`mÓN¬Gð¯AøV}ô°I(xCß<­&iMßG©nÇPÇî§=Fž¿KeŒÑß‘øPÂPŠ•ïbõô–—póUdÇÏÐÖ}­ëÙ±+ó)ê§½BÓZ§ñ¼§ý‘ùš¯Œmû¨«üÇY½Hö”â¹w.½íÄÓy°Ç†èv þu ¢Gm×3"ŸöÛ'òZi'm†IØÆÿt ãòªä {‚IœCO—¹½¾Xiíýé&oöFÐ:F¸‘UŒVÈ…Fì¿Ì@õæ£x~Ñ!É#Œqµz}jõ¾‘©ÈŠ„”îcÈöªQFR«9nÌÏ´IvÆ7¸nGÊ3…>ÕRæ$L4nO8(ÝGåÚºËù®iSÜ ÅkZø^ÞÜŒ["·÷Ÿ“Z%c;œGqs(’ÒÞ`Ùä ù:·†/îæ2Ê#€³gôú^‰öâÀÎ~ƒÃÞãùÕ]!¬>BC]ÜÉ)êyÀ&¯Ã¦iö@í×#¾?©­VŒŒîç B3Ò•À¬×$ F€~¦(¸žE;Ï#8gb¯Zz|¤‘“Å;f6Ù‘€À¬§ÃÐJÍžÚM3|ö(e³¼%¡õ+꿨«¾h 6àçŽi¾{)Gò‘Ôæ¡¤Õ˜- y®¼èU×aVû¬§;ª¢¬NA=òkBóNóXÜØÇ‡'t¶££Ÿï'£{w¬¿–sº&ôçÐúÅÔÖ2¹kíX^ uö© ™?z&^ß(<Š‚Ìº6‚ŸžjÐx_!Áø9¬[¸Âm ‘ßš‚F⃠ÅGqÅAä¹7§¹õ©P¤nWy‘Ç‚ %ÁŽyTȬBûýê‹j1hãkð@†±•.Æ‘ŸsÌ®L© ɶ*r#ŸåX·(#!-UÖLòÃ¥kk:Uõ•á†æIÝ~9¢ªŠV)!i$îGAXꞦ¨Ræxã‰ËÌI?0ãú÷©™ftÛ…Þy(nÖ(‚0®Û÷Ïç¶+$Îc¸`n‰+Ü©úU­…byËŠø’3‡éMº…-JÇÄ‹•ʱ=áÒ9šx£Y†Ø$ºüj)¦öEÌxÎ÷ç>ÞÔqˆÆèÆy$2ÑKÕ»¡Ife}¼’»qíõª q!¸W‘ñ³qÀÇáW ‹Ì”Oæ(VÎUçêjâK.»%µ’LÒ¶þ c§½aÞÞÜ]LÞk°à ëVµkè®"FfÆ9ãò¬ü.ß”çJ¶ÉJã)Λž}{ÓÈàârhC~”™ùºÔƒ¡é’9¦7¥±s’G½ÃÉÏÒ›ŽàóJT® ϵRA¾R¶å¥°9«ñi·÷1"˜Jíé#œzSQD:³{²§ÚåšM³ÜQŒ}EBñìŽWw]á¸5¿†¤¸—}ÌŘó„^MlÙøAZî?Þ”ÿR‹fMœD 3‘«0ÇE]Ø5z=þíFè,t'‚?^‡‡•Üê˜þ֯ťÚDòËŸöÎj¹ æGžÚøYØl–áØg;#³iáÓZ÷¥?ã]’¨Œ|ŠzŠilôäiò¤+³#_À².*ذµ„ec þñÍ['=:ÔÙ\ÚTäòÅV•ÉäqëN‘Ï~*«¿AÊ‘b9Ãg úŠŠFîŽ=é$'ü WgÁäãŠB#úêsNgäâ¢sÁã4À\<ŸjPF9ÏTk»ž;Ó²:v vÏŸZsA¥Ü1Á£gÉÆFOQLôÆO5ZêÁ.Y¥ƒj]Ÿ¼„ŸÙ½ÐÔ„3“Nµ'PIà @c«ÈI¬…׸1ŸB?¯zž&g8çåë¸UùíÒëc«ˆ®p“c î°î¿Ê«Ó9âÍËÄN{©õ¬' jc;èÄ2;mʨ{N c-„SÒ´ô6ßSyòÇF3ëÍt–š&Ÿm‚Ñ àfO›ÿ­S9lšGÐ2•q§ï¹§”IPnùœ| g¯FÄDa(¥èÊj>HóG3­±ç7}+IRi]jJ¨žç;qj‘™Nì•ÎáœTP±dPxµÞÇRL-U# q%í¦p9ÂŒŸÀšÉÓÐ6Ö†óþ•”••ÙJWØÓO*úÓìzœæØô'ïÆ}T×-¯x:çIl°v»±¬©÷âöa]Ü &{âœ÷fÇ,a”Œ†„S§QÄRÎ+CñRJ°ß•…Uxdïÿë×]BÃt$ºµbêž²×£{d7c™- Àou®kOÔî´ å‚ïÍ]‡ýSƒÁï]š1hô í²äCÅY‹¶1þ‘¦jpjP,ñ0Qž•¦'Š,*ã¹ж3. üªÌkŽ3XøŸJ´á®C¿M± ƪ¿Œ.¦Ó´Æ+Ó̸m£ò¤çîÁE½ŽÅô扮`¶]×ÇŽîÀWs©ëYû^ª–ɹn1úÖT†ÉyI.¥çç‹VNºû*åªo©ÞÜx×I…¶Û´·’ÖèHüúVmÇŒuI”›k;{(ÏG¸}Ì?à"¸çÕ' VHÆ:(¬é¦–Rw¹?C©Qí¡|‘[N«4:Æ—<×úЖö˜¢E¨®:]Ò‰H¸äœ6)λTž8© …äF‘‘X åŠçô¬®ï©V¶ÆRI$m´€T›oSS·c|XàHãRLñC…Ž.§¯SL¸•-²’Ç"3ÁœÕjö {… ÑÌFÓÑéTäxØ6Âñ©è£‘Ks(vùw~3úT@°ÆqÀ㎕²‰ÜD.HŒzž5C9Àɰ©‰ÊfB~î EMØòÁɃëíUqr  žüuÔÞN}:âœp8üéBäòp1@ì5Ñ—pqÖ‡Øù`㎴¬sÆztæ“ œŽi’Ð…\rAõœ5KûéWnY•pI8áMŒ…À VÀd`óô¤Ëtç×µ9¹bsš]ß/ AÝœv¦+ '9È8§±çæ<‘LÁàS3hqà‚zÓG'ÓÞ”€@>´˜#½wê+Ž”øyý*1RÂ>sô¤ö È|Íõ¤¥#ç?Z*„'jZ( i(õéJ£5 BH^9õ¤Ùj7"`JANqƒMi‘%f[{”’—f%Lå€0ãõï[>ñº-ÎÖÜö’æÇÿ³zç)ÀàÔ¸¦¬4Þç»G,Ä ,2#Ê˘Ü%_CïU[Ãfîo2[<°ã/Åydž¼E&“'•#3Z¹€~áõìz>ªšŒ ŠÄ€UÁáÇ­amlÍo¦…[o¬@dŒzFµ¡“k% ÿ¼¥]Çÿ®—8úÕ¤‰»#XÖ!„EQè£ò 4¾œRLCOL´™ëϽñM& ¡7qÇJcsŸåéN,ãƒïÞ¢f-Œk±\œðÜsëOv?ÃÒ«39<Z’Ç#'ŸÂ«Èp8ÍI+€1Uø=)Œ›»f fÉíøÓå(bÀñÏš¬\ƒ¶Tc'pƽ!yíQ†;½¾´‰–ù·«+ò…Go¯z`MòntO”ü‡98þ”ÀVr£pV|sŒÒ1 …›;GãJ>gÎJ2páÞš_Ì“är6uP:Ó 2ï9ÉÀãÒš0ù‚îöô§9í*>ð<ý)$þ]›”õ9é@©-¹†°¡Ø£Çüâ¢4ÕËrÐÆ[9¥°kèPÉnœ;¨ûŸZ¤ÄŸzš+éÒ[˜D®‘±Ëå Ž˜¨H$qIh2#ךM¼úÒ°Á©/ñ0œ¬g’ò Fo Ž;UíGL,…ì))—‹w,¾©ýáQB¹€ùrìs ÚTóÀ5±q#;‹Ž}N:ö‡ãN0ö‰²'.WcÏݦʳ†0Æ)—¶¤¤ç“¸×wªèÑê´‡dãŸ;GûXëþðük¾Ó泺h§R˜äc¡Ž{ÒÖ.̸ÉIMŽWcåƒM+Æ•[dßÂ:õäTR¿on:VŠW-!’m8#q'®F)ª«nµ#aгÿ4Ò¾”Ó†ÈÍ"`sÈçœSü²NGçN²;pxÓ¸¬By9Š·i9Ç9©zzM¿/·×½%Äjïv9-ØqHIr8<•FÖ:Àn„dŒúŽi0@ÎÜ_JpRÜœûS[ñ¦&†–³Üô¦Ò÷äóIÛF Z:œgŠ;ô£½ñÅI ùûô¤Ez󞂤Š/ŸÔb“.̬ßxýi;S˜|çë@â¨I ŽiqK×­Ú‘iêtMê çSèjÄ`àqЉ3hD‚áqІ­Ý #rj¥T]ÑUï KIùÒŠ£4I`×MáŸ>—2Å+Ÿ³“GXϨ®XTŠØ¨”nZv>‡ÒõXõ—,7ã9zоXjñ xôéR\ùYù[ûŸýjõÍ7TŽþ ILdàðGb+µ£*×Õ[½) •GŸÀÓI9ëEÂÄŒÛGµF_ŸÎ›,«3ÈÁQFX±À•eE’6 ‡AàŠMŽÄÌÙ5^FŽôÍò3Éæ"¬`¬;½r;UYž}¬³ ˆ‚8V?ÔR)Kp1o—hÉÿª.XĈÿ# ƒÈ⣖q,û"œ+Dxç#Ò¢¸wf ž[ŒüáÏAô¤0ÞììÅ£1l*NOÖ aæÍóÄÀDr­»©úëK(òãXÒ"Tü¸BQëÿꨘ¤¨»€?(ÆXÿ_Ö€F.¤P‡$…Àolÿ…E#n¢H™æR2qOvò—“ÀgÇ&˜Šað_•¦!²*(e<žÔ¡"¡ ~Q·µœù…¹ëƒšT;¿yó¸Ý¿ `Â$PI;A<“HI lŸVãšrH,­”#ëMe,ÿ2)QÈ'“šhº‘’¹¤A€X©RÜ‘œÔ­`2O#œRÇë@¢–$î|7#=ªDVƒTtÇQO F½(Ž?1@TbáÐúTŠÄG·Ò£ TFy§ç9ìh7zÔ˜ŽŒ¢Hœa‘Ç = &21´zjre@¤žÀ œÓ‰­èq^Û|ìþDrbrÐ{?÷“ý®¢¹íàxKÄbmJÞF(2¢,6ðz=ÅzM®—#‰"„ ïæpáL½ð­«ÚÏo4¡ÑžLm…Ç< J¥Õ„Ú¹ÉxŸÅòxŠ‘i)oåéq+þñqÏú{ÖT:Σ-²‹© ‚Þlí•×ïcó#==*—ö\—ÒM;à‹’8÷éƒSG§Ém$ UM»ŠJÀ•«CRù_¨Ö‚_%œsC¿l£Šc Üõë6ËS’Ò]²äóž‡ëþ5®y4ûuWå*€\c8=»t5~ºtZÇ š>9ÉÞ?§åD@“êt–SÅv¡ánGÞ^â·­TlF sœÇnÕåñÍq§L“ÀøLü®½?ϵvºˆà½]“•ŠeÁ'¢žß…)A­F¥sµYÿÑÎ}+æ2ó»kINmúuªÜ[Á¼É"&?¼Ø­¥¤P–æ]ÄDœU$·2 Þ ã¸8«Rê°o%luqúš ×³ÎÅ¢‡‚N?xßÐV.Eh="HÎrYºdžiï´.•3üLp+=/ `ΕäÎ cõriÇQ‚Îì,ʧ’ê1“úÔÚMÙÑ$$Ixbòdf®>§ŸÒ¥‚kk;é‰&h‚›|§êß­f[Ïw$²´°3y,Í"³l-ŽÃ<ç§BhÚê2öÌK(ýìC çמ´{=lÅs¸Ó|Mb¤Ë<+o ù>Uó$~Ù,zcÐUýcÄ66H“2ÝÊ1è€?þ}kÏì®­ã„f%Mt­©Yji¶hå›z€ëœ~}*ÓpW ¯ÁÕºÚÄ'¶+ÎÏ•Ôûq€~•¼l#Õ4ÄšîÛË.ü‚„ÿ"xö>ÕŸ¤h>”‹1“a–8)+ŒðAÎsøt­.þ’Pa!@™ˆ?ˆ¯^­W½ñlg¶Ç¬øa¬!iÒ]è¤ !‡ô®l¯ÎKayôé^ÔmRâ È™B1µ×Ï·~s\f¹àÖ;®4ÐHÎLÇû¹þ_΢Tœ~HUé#‡ÀÛ‚_¥'–uçÚ­46+´‚!†h £Œ˘Þè®T>rNNN=é…9ëÀã5tÆwn"™/Ï#±IçåP¤+2’1ŒãÞ‡PÝxÇ¥Lc ›pçš6¶GEïƒÞÀª¥4 ^1Éëž1VYr~QŠn61 ¹ãÅRX¬P©99t eX£p9æ¬íÚ¼rrAéQ¹`¬Á#¿z¥"Z(·Þ<sJ£æà~b”§rzÓºco5­Îkj#'ÛùR``uÏz@ŒÜÔ¨»@ ÓÚ†•ÈFUºTñ7ÏÈìzÒ¸ä|Àõ¥C™;)=A«!ž_Ìs×4¾FHÉÀ5''“׊pví“õ£R×+è•ÎGk$–ÒλvDT7<óœqøTC+ЉF襡ïöë{<ýjÎòo x‘íÙ-®$ tŽCÛØûW¦Z_-Ô ñ¹~òƒúÖéÙ–[f9æ«\È_‹u’'I–húw© ˜9æ¡i8ÅC3$QGoH¨ÃbùCîsÚ –U‚4ˆÏ‡aµC’ÇúÓðÑ4Œó3äÆéP©vffxÙ1íãë@Ä-"ŵØ<€rqŒŸéUÕv«Hñ*ÊÃæ É>ÙïNtófýì+¶3”rsÏÓµFì%“feS HÈ ížô€jóLË"³UÛ út8“2¬…£a•ÇãëRL]¶ùnŸ›ŒäR1^sÎh —rIC>^ç4ÙÌ}²G” ç¿Òž Â€£Ð SÆ” ÅyÏÐäãê(àj0:v ,:ŠB}¼gâ“pqõÀfŸŽ)£“€ '°«ÖúMåÁ;vïÅ;¦Nôð¸Ær+Xé6Ö«»Q¿Ž/öTóUäÖ´K.-mžêNÌý?Z-mÙ¤)Î ¹Ò\7îcw=¶©5y4 Çï.+uîYª§öþ±¨–vëoãä_êi‡wtÛïï½@;5®ÈרrÿI~,¶Òhv_,·u'M©Ò£“Ä’"cKÓ‘èÄn?¥U”iú<»~Éö™T ÅÎBŸOLûUˆõŶy#·fsåäøŒÒ“qÝØkØ­£SãÄZ¬²æK©îŽåZzuì—vë$˜fÁí¸ãÿ¯XúåÌ3ɱŒ;ƒ‘ëRéwb7$¹ÇáJ”½ýάLa,:’Š_Ù×ï£IuqíÊ·î¿v6xÁqïÚ°gÓ!º¶Y¾Û!‘`2r~î;py5¾Þ3mRÞÎè,L™Ý"–h»mÏøÖ=à/¢4rÆÍ¾â bFõ t©W<¦sMgûá/¹›ˆàŽÜ~u¹‡lä³YQkÁ¿‡§÷zõÍRÔµ™õ"€ÂˆSœÆ0IîN+?2>8P8ÀàVÞóê :“¢}žs:¼}>c•?OJ¿öa´G Þ²Ǩ9ëøVX·{ç±ìkBÎs w Î%?úÕ}S+’Æ›F¶¶ùŽK•@7ãœí8÷5¤ñ}¢612«gq³Äõ©Ýš[tÚ¢BÙ*z¯çYÒ?Ê¡“Æáþ•KwV%nuâ.æÒJ®q#ü+â8t›•k˜–[@AIÑCoö…b[Ý^ØÜ4ƒ ­’Èy׊鴻Èõ |4ƒ|ƒ-Œ’½:wÈá*/š÷FÊQ©¦Ì’êçGÔl“íЭ¤ˆ2Ž£QŽ úV…Äuì£Ì)"“åÈ*Ãuõ­-WB’9d±V–^!ç1œòG¯Ò¹»K¡·ì×pù±1ÇO~•ÓJJ¤og5ÊìÍ‹»é×P+r’¤³cc>õn“ëVn4(Ø Ê^Ç1$ïŒ œuÇaŸzÇš£¶ñou%´@ {xì8®’ßÇÚ…¨Cw§Äv(P­&Þ¼/ζåêG1m¢\DÝO b@Ì Háˆ#Ö» o 5;[P%r¢%ù¦{ÕAãk=`¬÷J¶²$F-¥wl$õê+£Ñmu–W’nÀe6ЯL}9ªŒ"÷W%¶º™:O‰#kwŠbßåÜ>^œs]&—ök»q{D]‰Ìª$g5Ìßx-ŸSšXáTµ•÷6%áGSÆ+J SH°³Ž>xâE¿~ÙâŠtå6ö JêÅã®ÙRk â;¥ÆÂç唞­Cwtaßö‰P)#¦Ðs\n©u ö¤×bÜù™Kc:~ÛçIÙq¼üÍSž¹ª”fØÕ‘·5Ž›â¹'û3íš%ȸr}›ûß^)ÚWÃs Oq¨¡ùg²>§ü*-[µ…oEZHòƒA ¾õÊx{Æ—ÚÖQÌ1ùásòŸð>õ +y­FÝ¢zœ~Ð’ŒÛ»1óC¸{Žß¥pž#ðuæˆZU{NÓ(û¾Ì;}k¯“â-ŠÚC2ÚOûÅÜw`ü{ÓïõkígKfÑ爹÷–åFâè ïN¢…¶¹\òb›rÀ¨Ù3€9Çjµ,O ”•J2œ2·}E ™KŽBñõ®Us{ؤW®8Ï4„dÜÕ–Œïõ¨Ê0÷ÇSNå"¾9!:lçå/ä*ÁScUçWÉúUÅ݃*y|r8>”ª£wÍÐÕ6Ÿ˜55¢’BpÑÞµæ3劌`dŽ´™Î~¾ÿ…9­ä 02zqÚš©·;ÆuÍ+¡Ù¸ã¥> ä Ç)êU€ƒÆqÓñ©aPŽ8曕‘—)têy§*sÖ‘†Œ÷§ ÷«cŠ%jü'“õ§ŒïMÇÉëODeÉŽ= d͇ˆÕ"Üp~½©É)‹åéÒ£RÌo˜K#˜ò±®ŸZ›_@#–Mň9öªÒ&{S‹õ9ÓK`šÚ*Æs³Dl0i;SAMéVsØP)W­ /ZjiëfûÖóÎ J€c=yÏô¨¯c1lÌÈPn/Ô69LÕ½ I–iDűó:6cò<ý3ZÁu ¨XlåèÈÿ¦Š?öoüv‹]ö9ebv~ñ! –×-‰ˆäõö5Ëϧ][@²Í#|mèaúUE%FñÀsïYÊ7*ç¹ÛÝ­ÌDô=Ç¥ô5ÃxcÄ-+%¼Ïûà>RŒvI:Ì›”àŽ ö¬6ve‡VV—¡Ò ‘£1ô\c Çا3€xsŒ΀v¢S…QÎMFXt41=ê?oÈÐÍœQiÎGQLÉÿ '$â—Þ§ƒN»ºÇ•{žçW±c¶õØ`Ærj’l7Ñ%±Ö–(¤¶Â!ôQšÐmKA²ÿU·²ì0?Z`ñ©v6i–i °¹Àúô§¡¼põZ»V^zÃáëéWtÛ OW58Óô›1››¦€ÎØú~•Et­[PEåËnc÷wn?â´ßO³`r²pXŸ—#¯Jm5ª_x:t㼯èVÃlÆ=/MºnqÏä)ŒÞ!ÕÎæϾÁþ5¯ÙJù–þQÞˆŒSË4±Ä«÷ˆQWÝÛFÇߩÏ7‡Â6ë™ÞV?ÝýM_±·Ñ„¾\nª2ø]Øüj爖Y4û±Æfh™PD¤€íß'Ðf¼îêçS˜ý‰%[3œ_µÚ’Ñ7 ƒZ8F¢º|ºHÇñ­í­Ôi|ªX.TJ;7ÖŸ¦BfÓ 1°Éã­S‹Z²m3S‡zò?FCê3S>&‰Âʈ±3+tÜ0kžš‘×ö àG¿s‘YÞ&¾Ôâ[?¶[-´ð³*Íféí]1•´Fmw5ïì-®/ÚC$k ä/¡8Ÿ5´º¾¦"òáp«ëÜ×/¢ø– ݰßÇgÏÊÿàk¥=hºZ¤RW,XëóÆíÒ4±?ßIsô5çM}y¡j$‘“9!sòã=wS*rÒaWÔô¬Z÷N[vŠwÄÜmUÝϱìis_pqìKk£oæÂF žª}éþY-°=ûà…ɵº2XÉ" ?.î¤{â®\x†úq±d'¤cþ=jµ’:K•†Ý;¤g°úW?ªÝXÎû &^¤.ýiÖJÃÞç9<ֵͶ­­Î œ «:޳T¹w*ר·i¨[ëÚ¶pÂÞ{Tòá`0ê}y¨­gÔ<7v>ø J‚ÓFž[¨ì­Ö9%q• ãŽàš×´Ôs7YBñ¡)æ¿ÌVqœe¤‹qkc£ß¤øÂÛ÷Œ-õ ¼JSèÞ¢¸ý[H»Ñ®|›¸È”qʸõ¯^hW:1–y°’u®DÖàÕâ:n´ˆÈFIq÷=óÛëS:m2<ô’~¢£$óÀÇjÛñ›•©Émm'›Ã#ä©úV>0yí\Û;3e܉ãüj¬‹¾M½—žM]|lÕYTo^Íß=+H1Q¼–9àÅ\‰?v20Þ†«Fv'zç­Y'3ÉíJBbyAÿJ†E;ðTò“k;U`™8VÊc#œæˆ÷¡qÀù}3V#@¸õõ§ùb5ìÇ¿§ÀÁ=Hº’Òw¥S¸PjK/éžY–O6Ù§Y8_àä|ßÏó­WþÍ(xnáPüã9yÏéÄVFšÛg9¹û>Q†üuãîþ=+ ó¯LÙU·†È.?ÛSŸÌçð5Ka³<¦4J†úp0×ì?û7CUšKk9¥]§”ÀNWÐúúVÊ® Ë6¶sŒ¨#Ÿ¾£úþB«²»BLÚ:¶WïÇÛ1uý7~t¬+˜É'—(h˜ŒrÒ»Ï ë¿Ú vQvƒŽÂAþ5ÀÉHË*uÆAÅIo3A2Ët9v¬¤®h[$ƒ==Aê)ŽøéŠƒH¸MkFþÒ‘dóãaéqÇ sÒ´'>’]N÷ z*÷¨äv»S“´UÙ@¹g  ³vf­Á¤_N2±ykë!ÅV>+°‹JÓÒ2zew7ä)­g¯j˜7w űú •nš?V’Ö£Q/Ég¦Xs¨«8ÿ–qrj³x“Nµ;tí?{vy{Óíü)n™3»LÙè>QW¢Òí EÆ}Hþµi7ä/öxw—àŒiu]wQá¡CÙÁùÕxôY%m×S’}¹?™®¬ˆMÜÜæ«L6ð„1õ#ó¢§®âx¹%jiD©›gn‡oWæµ`•˜¬1®Y¸ 8¬±¹ŸgV?¥Mgp-oY)–­TRØçœå=dîYÔÚDØÆÌŠuÌë×ýÑ\³«_I~©a¾8SäŽÄõÍwú7ˆôýFâHòåL„¿;›°ÏëYþ.ðäd5å¢mprÇúÜÖu”t,¥i4#=Î…å¢Ã –BƒkW16½u¤Ü<’ÆÞdYXr '‡µ'Ñn¼ÝIä$Y±þ¬žÞ£úÓüUjbxÜMnÀ*ȧ${qï\ŠëÔÞQ³}™wIñÔqßZÃx¡-æSæ19ÃÿNµ«âMßT…æ³ÚÒ¹ƒÃíïÓß5æ×6-n«,™‘°T/qÔ\ö«–Ú܃ȆÙü¿ eX3|ÿ®áZÆn*Í©ÝÞ%½*âçÃíÕšN’+î÷‡Ú´µ[c­'Û r¬/ÀÜ1´ž»O~i$xµûrÊwè>eÎ<Ïþ½G¤h÷×QÝJo Z)cÏÍÆyʼnì\¥mÑRßÃ7q‘%¼ÊßïðHü?v´³Ó¯ŠH«•'p;qï\¯ªKªCHlV8ÎçGm’¼½Ò±ôÁ©XYR‰³ µùçÓ8ëß«‡ºô1“¾ŒôM[L²Y…¬f<.ð`)©ï/nît‹µø…Ê>1Æü~µÍéšÍ­Ý‰76òI $$±É·ð#ʶdº{æI8ª£¢Žx­ã+÷±œb¤Ö‡m<–³,±6ÖúçëW.ì­µ¸ B‹ÐÉ1{¯øUÐqŠr’„2ä2ô#‚ y©´î§IMY™ñ5ΛtþVØ‚§Ê§/ÏO¯µlhÁ6 ¿–EK9¸Ç=€ä“õâ¦y#ÔT ÈŠè}ÙzôÏ¿½cËg<¹uUxòÛÛ Ž:ärq[Æj[œ..›ÔßžÙ5Wx.â[]I;tI½Ôô±)¬eòfG 7ÌœÿUX›ÄwyPS*E‚¨Ã Þ§pèqŽk Ó´±¨íÔÆ¦×!F2ƒ³à?Òµi2£;˜-i%Û)µµz“Ò¦]"Þ]GuNãõ­-nù,b…î$7?6HˆÓÓŽ‡ñ5Ë[ßÛ‹çyƒPÝ“×ÿÕQfö)³Z(ìѱom-ÓŽíÂþ”·Í¹ûM‰i6Œ$l8_@_«O4Ö¡fŽáŒC€ÝF=ìj;Xõ]beá¢O˜J€*Äô>ÔãM‘ÝZ@,!–ÅÒGåà$¶ÓŽíùô«Za¹¶+pà”n݆1ÀükbÏÂç®%vör?ï¦Àü³ZÏ¥ÚY¿”Š»×üÛÏÓÓô­/¹Ÿ³»¹™WÈ×PÚˆdv18eÂ9Ç$÷ß¡¬OøsSÕnSÈdŠ1…‹vâ c¶k¨U`øû;KùT¶vƒž¸¥KªÛ­¼ÝZ——fD2Bñσ׵Ñ2½™çúï‚$Ñl’à_Á;mÜè2?/ò+2×ÄZ•µ¸‚ ýÖeÜÊ=tšÔi©Øn$ûFc#Æqƒý+‰·&é<Å#iùàù· %¡|ê73Kÿ d—<ǧá[¶*Lli,0È@ ŠÌ–(nQ‰úš­•ôpÇ4k2Û³•òw¦­9Di–5¬*×:{‰"/ a¾?ø®z½"Ö×JÔôè­ä1Ú]Å÷.ÔrÒP:{0à×1«øFÿIºòçhY\nG·+éN2²÷™޾é…`A9³¤jÂÒñ'»·K»e;^''ÿ_zÇx^¼¹AF§PVC—Úì0³{%f8¦vÿðYêó-î…z%iÛEê1ß·NµFu‰CóžAÆçà¨x}u/íkh4­Ð\4€nc”õüG^+STÕÕ®4ë>®„¾[Ï¥W#¯ÊxÁ¬*Eµ¡¬,ž¦Ç†üDlCuûÈä©ä~>¥esw5Üùi.0½‰îMrÛ~PAÀϯ4á,Š»c’ ñQ Cг4•+»¢MBdšãä9P;þµTð9äúRã×4cŽÜÖ2—3lÕ++Un@%pî¹ô«å}ùÅT™C¼â* õV+¨w*ùTôÆ?µ¿k çÛŠU]ŠÍüXô¦Ÿ“õ öªnä€Ì‡ 2;zÓ„\”ŽqÅ(äàp&=qPØÈJƒÏ§Z!EÝ’Ã¥9ÕHÁ#ŸÂ™œ–㎀Óè3#ø»õ8⎙ 9ô©KnÊ–džzÒ l8·ÖºîE…Pr¤òqÒ—qW 3š‡$Ó”˜Ùƒzc‘Ò¥”]ÃæÎzš2‡·­FzÓéÞ•rqŒä{Ña–ÌŠÄ=¸ÅQ»lÔѻؚŠyäôÏlQfKZ ÷Ó+¥2Vb«50åx¨)ÈûO=)48Ë£4t æý(Rg`ÀG&0r lÉTÝ.ŠTc9Œû)ÿëÿÀ« Í¡[¸ZงwÌc?0Õ²²Úy@Cª\Ävý×}Ο˜Å$hÅÛ§,»^+Ûr‘Ï9Ïä1øŠUëµcÕ¥ˆàq÷?¯F«+-Ùr`Õ­çPÙ`Á8u9üÎ:”Ç|]YZÜýÑŒõuùþB„`j{šì–¸D>gLü£Ã§áU“'ŸJ³©‚'BÖÂØ˜ìøá¿µjpGqP÷4[¿‚%"Öõ2pvçM¿¹{»¶s©ڸôßý¬u;Tÿ:‰Î'uÏF5•wî¤zYzW“êv$0ið!8ó\e›úWCoq ß.FH®vÖÊyâVE%p Iq+i–¾z|Χ,a[FšHóªÍÎm½Íëˈí›ËHËËŒàáT~&²§»¸“ JªIá-×s~gü*+MnÏ\²žG+ò áߦ3\¤þ?¹P°Ø@ŠÇ9eàu==+9'º!.çU¥Û’íA×}ÃdŠž;ÛKd’ µKuš@U7°ÙŸJò»ÏêzŒj÷o¶W(»Np\޽ÅgO2>BÌçw—Š~V©Ãr21ŠI;CÒ$œ\O%´në4@ùŠ9éÆr:І8æ[ù!ŸzB ¸ÁÆ*žŸ§Ò­^<¬Æ!»’285§6¥#EÔn"gˆmRÏ·Ü÷®†Õ¯ „$Ý¢®sÛ¶w HÄg'p>üW]¢ø³l‹ Ë´ñãk³ŽWž¾â±îukWP¿gY°x.8¬wËÝ ¡Qé±:~UÈêÆÝw;£„©5ï+Ÿˆt•·_·Ù*ÉnÃ$=·ò¬ë&Ô§³2 Em5é7(Wí“þÑëMÓ¼Cs¥Û´7P™-äq½^µfç^´ŽÖæßLIŒ·‡çi8Ø1Œô­´–§<ã8>Vd]jq–Ûc&àÎ$L2Ÿä~µV K–2¥¹%Xe•q‚~ŸZ¹Œv)öÉ9vSOïý*Ü—:ͱ Ž@X…Á!G\Ÿ¥sÉk«=,3ýÚi/S.hgÒ®£‘p$A¼9tÿõVŒž(¸Ô­ÞÚcƒÏÇœñç/Y^#IaÓÚYnY[¾O˃ëUí5{™´¤7ÙFà`]H1ðIü¾µtˌ·2eíJÚÙ¬…ŒLúáÄQÑsÔŸ ¤I.b-£]20få„K4;pÙ'Ôå¸Ï?Jçîµcß±ÞJóFÛ£x—b©ïîN;Õ_±j:ŒžmËHìŽg$þµ«inrFœê?u¶‘[ÁçÇg7n’°ŽOï é#oø§ç_úd­svvÂÒ‰I8ïï[ªOö%È'‹úÖw”Ÿ©êbbãB},s˜çs×8¥Û·'Öž:á€éÍ4òNkŒæx#€qíS¬‘Ï‚ðŒý×y?úÞÔ;Ÿ‰Í4ƒ‘z°§%fTÔmn4Ç $¬ð:â9ÕwdsÆsžõ^ËT¸Ó§Œi ¶Y±Ù ñÐóZØŠèÊD[¿ôü=+UÒź«"ÒÚžª~ôgßÓë]t¦¥£<ꔥMß¡­&®u)¹Üþ^3 ôFî@ÏJ£¨Û5ÌäµD8è?>õ†’ϳxY ˜$IÉÊ÷ϵnÚ]ßʯª#ÇvØešßoÝR:€?+wwÐmœënbß‘ÂêÒ~d9ê3ÔZõ=*A¤Û$†šÊ ~ÑÔžàqß5åÃ}-ºZ@ ‚)ïŸsØÉϧßYͺCw´H­„xfbÃŒc éJÖw4[Xèõ›dÕ yôc ·¿2çïLz×9¦_-ÍÓÂU¬î" ¼L¼:öÆ*9­ÍµËêZà·–/žKW|=½Gµdø—Y¤ñ™UÞ l 䜒{c®hn3Õ ³[;x‡EÓ ²·›/ñŽXýp2_j£uã«ÉЭ­¬qÛ´eƒM÷àsŽœ‘Ô×#ûÅxþXí7â!¼áÆ1œ60yã§ïPÜÚ<Ð’Þg˜[dÃa 0ù¸úcÞ“•·Ð9noÉ<:´[­Ö×»ü¸;Û<=ë/ÄÖ~,žÍíç•óv8ôÓ?ÅšTuQû˜£¸Ëó’:ÒfÉÉ99äõ¬½´c¢ÔÕR”–ºõÕΜ‡,ެ91œ…ö®»Áž,··Ó×JÕOdÄ‚¬9\÷D¢±*Ã+ß5Xé6¥ŽØöÝIãð¡bWT?aætZ熄CSÑ'{›Îc9hÇ¡õÆ£žçR³ðŒW·–Ö³ÙI0T‚BÛ÷e=@8éÎ+ÃVÔü;3˜äi u;“³ŽÿŽ)×:ÝÏŠe†‰k§Z ˆÊ¨õ'¹­T”•Ñ“‹NÄy·ÕcóÚÚ8јíqmœôÏZ­{¤+ǺØeê¹àÿõëfgµ‘£6pùíwÿõTxù¸Æ3é\nn÷^‡JÖ¥7Å3è‚­RKž|¹ŸþYçŽú ŸOX¡ŠGÔa3]Ý©±9+Îr}É«q\™ð‘' $ˆ2ŸÀƒŠˆ& ëϦkG_K$B¥­ÙÆÈðõ4˜ÏSÍJqÏ·Ö›´œçô®k›Xn)»HéR•ãÞ¨ÀÆ~´\uçZ¯9`Á”ŸÎ§˜|ïQ"…MÀœžMj¶2þªvqÞ E?xñš›Íp9#¨)ÖbÁ‰Èèi­c¶=E?'þ¦Ž'$cŠqÉ$÷ô©e"†îð{ŠW÷ÄNELÊn}i©÷ŽïN¢­10•³Ðž•!¸NsózSÔ#†árÇ$u¡` ~wÂ‘Ž§ð®–ÉòeÔ ÷{þu"¬sŽqPù $£`{sŠ nP78íÍwpq’HôéNiö4,jHÉ<=h\ØŠ9UŸ„Ï>”IF1»$Ô©#F1ÆAíL’U9v‘ÇÖ¦îàP˜sPVeªíÖºbrÕZ¢Š*ŽrHß t~¥wtc´Kx&*›T:ÀV~‡ôÍÕ›iжa»i=5F±wÑ4Ь×Z6 Ýàp§··þ…M#OY9–€0îxùÎ!úŠ«a:5£ùš”°J „d·Ï­E’í›0ê×#?Æ?ÛùœþtÖ¥lajY’/*á§Zƒ»øO?/ùõªñã9íWõ„dƒÏŠ(Ϋåó篾¥g¹›Üµ±Ùx1ñ-×=Qzýi—2î¥îCŸçQøAñy8õŒèB›yÅäßïŸçXbtŠg£—¿zHîôi¢ŠÑ‚¥ ô¨¯¯EõòÃnÏD“ò«vÉ=3Í`éú¤?fD™öŸ‰É®•'Ÿ˜Ô${6¦ó‡RG'åêzþbßH··d7§hÏ ÃjõÏ#©®¦ÁÑÑRÞ N6WnOÔdÔWZjíyæ“t’n#a{{œÔÉͫś…»IÓ_ÜÏ÷ä 0¼Ê«ç5§m£\ÊBuÀà~}juÜVvm³3P`‰dSíПƣ}Nåò7àÜtãzUq¾W ÷Ž©ªç¶ÌN„fÓšØÓ7ö±"€gFܯéìsïéU¥Õ®dÛµÊíçŽücœõéR øÄÎÑ„ H;Î1ŠÌÍ)J]K§ g[ ¼Cy ‰#’_ø½ë6- ægi1ÐiÒ€Xà ŸAP¥%±r¥ ;ÉÃo D«Žøæ¥ÍN–32î`}[ŠYaŽÖ0ò¬²‚B€ˆy&®4§.†SÄÑ¥¥þâº+HáTdš»4Û,gE9¤•ã·Rã·Ôœ±ü+.óV€@Ööê[y¤<z] œ]Þ§™_ë´¢´D{ÆpO=)wärzU§ Æ0:äš”I»¡â¸\,.bÉ“ÁÆ)Àç<úUS1È>”4‡;‡4r‡1+8ä“ǽ$w/m!d¿Îª¬¯ÎŒÊË‚5?Úí‰óÅ^~ìÌB~*;×J›jÌÁÒqwŠwï&Úl0uòïóÑ«¤[ë¨gó®–ÞÒ#“¶I~sø æäÕn˜Iÿv°~•E›sÜ“ÔÕ]v)S—Vuú‡ˆì33¤2Ê»–4ŒÆmÛ§Þx:çέºu—ìûdŒ…ŒÔóëYß6:õõ§u±ïI»îh ‘£ó Ù¤+ýØ—¯_Ö¥¸åŽO©æ³#ûÞ*Òoûۉ氜Gklh# SÀ8ëUb`Ýr>•*·<`úV ¤YB¤çæ‚ÀÎj¸mç‚) §¢‚8©åcæE‚랢Ÿ%ȺuÊ@6Œ~î0›©ÇSTüÆ-Ž{ j ×<Õ$ìC’¹qæ ƒŠF‘ ¸ñÏ\IÇÍN•4Nb+´Þ¾”ÔÈx碒E ·w–qÚ¬ŒضæÏ9ô ”SÙõcǧ³‰<ì­æÏçÕzR\ ƒüêy lņqïÔÓ[¯ 늟f»ö!Þ£®iƒ´äbžÊ1ˆãžÕJŒûšR‚[1© ™TõûæJ£ÊàdÛµM!Ë{Zo‘œ.(WB{‘à¨ÈêiXm;sŒúúÓ±ÜÔ›Aw~F›bHFF@Á÷Sˆõ튓o±Hq‘‘Ö¦æ– (s•åºT°1 žPã»Ab«ÿê¢0<ù¶œ}iÜ–f*—¸>jîLž¼Nž1m¿s íÀ+È]¤@–É9=sOš!æe_‚8ó]6×R¥%€ XÛÉæ«•À ·NÕ:*¦×f>Yè£ïŠI2q°Û×KÈ`[äíµzô+ ƒÍ1ˆUÆÐÙzbœÄ ûzUXd¥²>÷^ç½FqŒu4,ŠLŒsMwàÒH¤j;‘Ò¢lÖ&5))Æ’´9š”dtàÑŠ:YŠ@zõ­ø¯-/wìùT±òIéÏè:æ ç¥^´¼–^(åJ’=*v5O˜½ª}œë:`¾å—·ÍÆ?Ö¨''Æ´/§¸¼±¶žæå%Üïò¼§<çëÖ¨/ TËrã±ÒøMößH?é–ñáSêóÿ¾jŸ†˜-óäË&þ‡úUíWþB3öù³Xb~zŽ^…LÓ…6Ÿ$¿fҮ‹fÝã dã¥rB.R²=:•89¾„‘[Í;mŠ&ú(ÍYŠØü§hªS{+Óüræ*ÝkW÷ÈU#dcš¨¶®ÄnÂç «3jÚ‡ q%Ëáv®~µ7Ý2ºe”6ó7-OÙ§¬Ì^1¥jQ±­7–d’& I”V¾"òò¶°ˆý«*óY¿¿oô›©\wä*¹`W%¹ÏN´ôÂŒe:“øÝΖO …|‹4óñ†‘Û<ûV=潨]±\Sü)À¬æa´zžxôÕ|c'“A;†{Ò2äp~•H‡~…¸®w¥µ|qëÜö©’qé늦$ãÞ•Xõ5&—L¼Ò³(\àÔÒøÏ'_Ìà`S÷né“QË`%g w/J–åÀqUÔ1ëÖ¬EÜzŸ¥L¶Y^G<¥N21Q"óÈL‰‘É9õ®y #mç9ãò©AÚ1ŒqéQ¤gŽ:©çŽ1Y(pÌF=Iþ”ÍÌÄáNÑÆqNìÃqú`ö«àCêO¡§¢&亨‘$˜‘´vÍ0ÆÊ¹Ú¤cš°‘yÒª®'c€¾õ6ÐUÏÅ1Q˜ü€qÎÒ:R;ŽüƒŸÎ¦YGLRMù²¼jb d Çéìj2ÜåWëZ³#¬£/žIíÞ›¿Œap8Ó'•N2yþT›¾bY³ž ÷¥cÆ00=0¨ãhÏáÓÚ’É6ìùxà H¦ò>ö)¡]9éãv0àœõ¥ ‘ÄŽ®ª:õ¤ŽM‘²…W'?xgÖRÜZT‡{…@ÉåŽ1@ wÎ0ãÖ“v[ÆyÉíC&ÅÀüé<¼žz昇˜qaÛ±ïQ¬dž„c“žÕ)P§€px;jM¹;†Ç~9¥ÌRˆ‡*¼ðëQ Ç-Æx©Ù[w9À 0º¾à‚§5(²'Ù‚Iävê-§Ó¨ïRIµ›÷ÍFãž¹ÅR!ê708jž Žß2óŽŸw¿ëPíls“ÅKÚ¤*¸áyëõª$æ®$;mP¤œw¤ácµðqÎxâ£ùCT‘“R9ÈËn½«Ðµ´1ßP–FœÐV?“kä‘È=¿S¹T8íŸJk|ÛHÉÚ„1¥ŽÍ¹àJi#ŸÒŽüô¥#àcС œ€(9éIŸ¥“Žô àzõ¤Uù³éJF#šy|uÎhnÀ£v@E0ŒÔÎÈ~§ñ¦™2DxíE)¤ª2`i(4S$Z–|¶Áû§­EF) ]FÆ¥9w>¹S[Ú…œ“Ç Ñ.ì G^•Èxzùmµ;u‚Ä_ï1À^ÕÙM¬ØYD‚{¨÷Ô;ÏéQ8©G•4jºrçF|z}ÄŸǫ̀õn*ì0b7»?¨Aýk"÷Æ0²´v¶¦@F7Lp?!þ5‘uâ Rûå{†Dé²/”~•ŒhEntÏRZGC¬Õ/-4q$qC$ùFÒßZ殂¤ØàqþÕAÃ9<äô¥‘Ã’¢˜ŠIïéÁ«KA1¬ žMƒŠà,=ÀéQ´ETçzPGäàó@Ž˜©âôÏ$úÓ’,7«{ñŠW,+qÏZxŒ Œâ¥ÚXz矩§cåÀ#?Ê¥²’ 0‚A—ã#©8—qÓÞŸHõëÒ…UaŒOïJã+m+Œ{Ò¥0°G^´ &9fâ¥ÞŠÛY†XñšwŠŒ’ Üã9âŸA‡àãך±´SëõÐ@\wôõ£˜VòäªàößZaa±À=©¾ROÊz=)>îBSÚ‹ŽÄ{N0ŸåQ8bI'êO0~x8õß<ŒëM wäóêM3Ò§6îò:¨rqQÈrÆ}=*…¨Â¹ ÅvñÇ¥H8 Â>:÷5Èd'§''­IL¡H8¦2̱‘HpÜØ¥ÊH å“ÓœƒíDÈùbÇ8$õ¦¼†] ‚p¸ÀW£¹Î5P±Æ¶iMþ‡$½#p8lš¡\N8æ‚qÇ󤜶hÎzž)“qÄdŸÃ)ÏRsíFs@î‰c$sS¦KsŸ›ïU‘öŽ˜ç¨« ÊÅ[i'¹³‘¤^„l0~aÔôÛŠØä“ß¶*&Ç8Î3Öš`Ñ Ò1RÔŒÓJÑI ÍZ]§5$vîøÚ úSm"dȱ‘Í(´!Ò¤Œbµ-´x+3n$r+W„M£A½Î}awà)&¬C¦Í)û¤}k¥ŽÚ(À ƒž8…Bž?Zçx—Ñ*1[™PèØ½<öÛWcµ†W#<ç­Zè{:ÓŒExeÁ#±•IKvh¢–Å~ÈéšzÙUëœâ˜s¿Æ:ôä¼ãxëR"L Ãpú‘J'+œšPF¥ËaTòŒã¥!ˆy#œsÖšØ Œûfœ õô ‚Ù8äRÆBɨñÉíÒ¬#o©¤1ÿ0ëŒÓ@V;SŸ¥Fc ŸAÖ­lRÄ•àzE=`ÉÎJ¤Åb‡ÙÉP0Ç=)VÔ7 ’:ôÎ+M!@è)|½¨S+ì»ØŽËØw¦y8ÁÇ¡èkeÖ4S·œãŠ¢ñ±rUpÁõ4”›‘]`ã@ã&œc*øÆz°©»ƒ¯5!ŒŽrqÜPØ$P’-ž¿‰ !'æïWIPx#®)<­îëךi±4ŠËæµcÊM¹ûæäägœu§,cxÜØ^ärh`©Æ1Œr©Â¢{zž˜¨s”=z÷¤bzõþ•›E\•G@[ži ‡øW G\Ô`Àí=ièA9ãµ;!\O7œdéPo< ¨êGjs  Œôû óÏÖ¢ÜÑœÞ) ,Ä nËõêLm$ ݱP¬ÁóÇ>üf¹wüþ¼åªuÏ~¼‘ÐS'*"UÛµ2†ùH8è*,ëÂûÓK¸_°Ð9AÖ¤Ôv"¨ŒdŠŽG8äjw‹ 9o¯ZdË„éøRäŒÏsïC"1ç+>£äÝÅ(Óš~~îA#Þš3–$ü£Ó­+…€)Æv•çîi® #íÖ¥o ïša#p}Ç@{ñHc’Í–È=9éLPªX¨8Î9þ”0%›aƒ°íK“¸) |qëT‹Á'<žAQëSκ•Éb;ºc‘ýÞ”á„`yçœåV>Æ@ÛÀ$U˜lB€Xf³u"‘j%%Vm£®z`œŸ•ë[‹oƒ>½3R%¨ã (ì=ë/om‡Ê`'ç=jEÓ™€äÖ÷Ù—#ÉâmÿëRx‡Ð~Í‘éh¿{ÑMÎæ gž§«•M¥I8ãmG"¹zïPï Ás‚3S ›ù¹ŒóùÔGîä®îGJi2Z!\‡È'*F´ÊmC“üê¢Oå¶W†Ï§5ÜãqS¨h,YV9pAþ•*•”g•\ɽÎボ’8¥ßÕXçÒ¬DþFÖ6ëúTmÁ<ži©p¨2ò?­X,›j†Æ‘Xrzç¥8Í€2µ`Æ›²Ç 884ÂÑÆ˜'Ôf¦ã±Œ¨ÅÏCéG88'žFi@§ŒèG_zs?ÍŒƒš}D0àÖ”°ä¸íI“xäR€¤Žy=Çz`(EÙ“ÔÒà`cR9P09#¥3“Ž(¸Çª·P£µ9±ê@ôïNT#ï zŠxÉÈÀ9ô¨m"3`Iqô•!Äa\Ž¹Ï¡© œ¸ã=­#ëž6g=Í+±ØB@ŸNô R 3Ö‘À¯ÆqNå;ƒqÓHöæ˜d9eΠ¦¡V;X—'©‹±9sÓ4ÐÙ8`u8¦Épxµ4ò}0{S@HÀ[pǡʀÀ“õ¥0H|œõ4n=Ç­eÆÌœœ@É#¡ôöúT[K7ŸNÔíØ9¿ð¥`Éç 7s€Ùc¿Joq‚3޾´¹°pE)l`ÿõ©<Å>øíMàr3Ôäõ§¼u#Ö˜º‚C¼1šfv¨ƒš›*6“ÀéŠk•<Àý*“Ei2°9ý*<‘ƒŒzb¦ ƒ¡ëPç yúCVˆê8»ç†\›ùÓ Ü®ZWÁaÆÕ¦”wgŒ{QÙªnÀ ò(Ìr’Tydç‚~µkr‘èx«©*Å£ÝÎCg¥RsÓJœB:„`€g·<Ôœ “Ò³m—d1§cNî0A2iv¨,pTS‡$pžzõ©6^j éÔŠˆ~OzuÍC/'ëBF5m Ž´ð¼ p[ž¹¨Õ\ðÔŠ“y#®(½Æ:àRŽ0A!zã4á×'µB€I¿J Ç9¦O ÔðȆÈãŽzb£?7 u40õÁìpz OQ ’MÍ€W9•^Gn­ÉÇqW£`¨9MV¼ˆ  ‡¯Ò®.ä½ ÄæLžqH¥‘ºŒuçùP Ðc¨£$SVfY|”`ylñŒtþtÑ÷¸àvÅ$mòaÇCÇ=;ÑŸâۃס¤Z­€s韭%×Ð{6ŠTRÊq€qJÈw÷)òØŽN{P=àhXImŹTÉå(?¨'šV€ØÎ~éTÆ2àòE4¤ígŽiÄû‘Ž0G=ê¬ø Éàž£¾i1ÁUÏ¿4´6 ˜£-ÜçŠ,%€³`ŸsGB3Ïlt¥I6! §¡nÊàúÒµµ ܯ¸(ŽObG4 š„•à85Ê«€q€zçÖš“Èbòñó/ñÔÞ 6HÌŒV0BõNzŸ¥'@FsŸzMÙcœ`p)1…<“Ò€x·½Ž8¨Ã'Œ`sÒ•Ÿ¸è{Qa\qo›±âš[å œ{)¢EföõÍ5ˆ ž8ä SÇ ½AôéL9ÎW¥1›µDÌqßÞ©!\{|Ì[‚1Q³9¤/„<ò:ЬÎIÎx÷êkHÆæmêO¸d“Ž:œÒÇpªs¹ÇsФeÚÝG>´èæpÜ`ŒM_ ygs` œc¥;È H=;R7‘Æõ§m?tŠÏ™•b19ü}jB…”œSC(p29ô”¡ØžPm=xéL GÂã†4ܪÁë‚qŽià}ÒyÍ.í¼Œ@1S<È$ö§uS¹pcÖž¤zdúw¨€ §ï)ÇOZc¼3ck.{žõ9bBð}Ozz’®Àã ¨“‡'J, DÞµ›p‘KvÞlQ²·N1Òž\o*~ö3õ¥t&%'îƒÓ®j–äIhR#©8æ‚>”p;ÐèTì`Fy9£kg' ëV@¨GsÇ_­<2°AϧP~”Î@#nÈ`zóHd™F lw î'ŸZi$‘ž? š.Äœ‘éKa‘ÙÈ?Ê€åW§9«rB¿. ªñösÎi©‡Ÿ¾Pqîiw¶pFFsMa±éÒ˜ÒaÜôç¥RB’䲞sߌSÕ˜)ÝÐñŠŒ1‚NzûSÃ!ç¦2{SHHV=ý»ÐKey;OóQ¤ ¾sËñéMVo0’zr9ëI,Ì 7/#«“òáT€Ý=é%˜¹¿ZjË€NB{ vÐ/¨Öå9š•S{|DZ"¤HÔ£68#Žù4ÕÛ“±°}«'+–‘ÊûwÅ86\RùxaÔR2‘Çê”…a’8<ûÒ«18#éJ!cŒƒÏ­ WŸ\Ô¶˜Ò$àn錟Z3òŒu÷¦ò['{ÓÆ0^ƒ%Xa#ž¾Ô˜1îE8’8À¦3sŽKz†˜Xz®WØsژŸ*'ÐqMÃ\‚1G#Û#=(í¿(Éù»Œu§¹Û`*bzšbžÛJ9 ûP eÆXR…f\à㯥 8õ>ÔÇLOJiåxžßZfqÁ9ôÍHÌ?‡9÷¨Ë)##Êš9à‚áQ±#€Oj d䟥7€8éÐS$p$ç¡Ï¡¤Î9<Žþ‚˜OÌr)½{Ó°®L«g=ÍH·-œ2œîöª¥¶¶E (ÜrNzç4r…Ë‚QÓžh2ìàzU11 Q!Æ8úg4r˜´%§9Ò™sž‡žµPJ3Ó¿z<îx<ÑÊÅ–“wSÉìM0¹ ÅWiHöÊ‘œnÜ:žÃµ "¹>üŸ¼1ŽçŠipzàÔj†ÙLyŒç5J"¹;Ê0}=j»Ü¢ðAÍB[o~?:­+ Àà`qZÆše“+dÜSL%¤Éʆ­AæeG¸¨ÜíÀ^=kU=:çõ§Û:ù‡*Ž9ªîr¿¾qMˆç“Ò´åº3s±Ò?Ç’E?kÆÙHCÐãŒú ²"F-‘ëÒ›$ ÷rØôÍy×:¬@\:’ÀïÏQHΤ’3Њ“ÉòÍÅ %—s` b£œ67{R…ð9úP#åA8©š0;ž¦“é~P£$€9Áã50@žœÕ‚™*I<Žj&Œ<š‘æðOoæjuõ{æ˜Ñ “¹¸Æ9§[Ǿ5bÍŸZL ¨ÀçÖ¢v#Œsòý*  ü»SZPÝHDÙ ’y=vŠrdGóã9'Ó5"¯ NP“H"B³¼q@ UJ…$¸Ï•¤Ø€(Úx¥Ù¹XnaÏj’T iu.NsH7÷<çHO9-Œ ‘K³Ì O$søÓL ÈÀ³zŸj«Ç8%ð¸ÏQÏj0IÏãšt‘*F»G8ëøÑ·(rOJAqŒÉ¼¡0Î=J©çDè2T®Hî>”Al„÷ê‘§F»2ÊNz~”0Ü ¬SŽH#©>WÉ ö§ÜÆ<öÁ#>”ÅA¸uªó}vHB¸8èM][tmÄ–ùGD`L·^1M2YÌ%¿ –) =}êO%XóëŽ)c·BóøPDZc(Î6x\ÓÄ2dn`dÕ±n‰FrEF$|Ä}*oØkRlíÈïïM(у•üªÎÏ”üÇ¿áNŸæoº{úUs0±E—ÉàrqJÈIì9ö«‹‘â ÑȤ¹…b\©9/>•kRW ¹T88â£`¥—vAïØÔžJ„'&¤XTƒ8ÎÚb¹Q“ƒ»œ÷ÍM±ëÒ¬Éi‹ŒŒƉ©#8Ò³r¾Å(ØEÊà8Àê? É,TñÇJXS/ËP3(CÐÿE‹lŽCƒ×$u¦ÚØÛííR"n|< `òIàôíM!\h•”d¸=¸¤¸ŽÏB8Æ6)üi1ƒÉÀÏìÇ«`æò±çš‘£ OB?Z`Œ’M+ãÐo9à`zÔ2[¿ãSĸÈŒ$Œ4årFGjKpoBÇ#Œ Ž´æ`|Ò×$œÓÚÞ§''?Ö˜\„¹¦~”<…Ž[©êiÆ1ŒäõÅ3g'“Á¦!ìû†3Š@üŽ3NXWn2x\Ó^0£#4 ŠI06ãñ¨Äœã¦{š•†1ÏSŠc@¡”yªKB"ÝÅ#8#'QȘ,rx¥1†“×§)ËûÌÆiZNœŠ…ÐrO'Ü $^'‘UÊ€“Ígéš7x5 i–ÎãÁ­?ËžiòØÉ×$€* @l‚ÁZVŒduéý3L ƒ÷J¤€™žòûÑæàg޽éQìãvãë@ˆ9'µ>Tšq‚Tà}y£Í 2H9=Z‹ÊsdæÂ}æâŸ*O;<Ly>n§è 1œääñšãù‡ÌxªQBn´§1õ늉›8É4ñm¹cÍ4Æ3ÔÕ¤®E¸tΤݑÛëOhW®MP,¼š£'!‹÷±ž¾´"~ðí9bž"¦–(~0â™gÿÙntpsec-1.1.0+dfsg1/docs/pic/pd_om006.gif0000644000175000017500000004050013252364117017342 0ustar rlaagerrlaagerGIF89a¤dÕÿ‘wh×½­\J-ˆgVƨ—H2,µ”„¬Œkºœ‹—’¸±¬ÒÌËsmh2+*vVCPHB­”†àǸ‚m!l…±½½ÁëÚΛ„kodU·¥ŸÍµ¢žƒ,÷ïí/EöçÞ4'ÖµsÒÖëÆœ‹ÚëûªƒSak˜$URW“¹î71C1BnsW“½ÿÿÿ¥„r­Œ{B;>{g œœ²œrâÞ⌊Xƒ€„Z^BÇ¥ƒœZBÞçÞÄ™4àˆPÿÿÿ!ÿ ADOBE:IR1.0Þí!ù?,¤dÿ@—ÐȤrÉl:ŸP$Ê6ڊǪ ‰©âާZÁFÏè´ZI.¢Cöz¹‰ † q"†xw"0Œ’Ž0‘mœ |":y{¦˜‰sx/3 , ˜/•/rxsx0 m‘urv´{˜ Ï,½/ÑÔÓ0,,3 /ÚÜ3"º/ØÚ,3353,/Ùô3ÑÜÛ/Ì«G ßŒ*Ö(\ÈðH-Cd©rD! ÀŒèCà £äaCC¯L“PBÄi£:ÄæDˆ0#B†©2pŠo¦ÿw4èØD,€ ùêH’àˆQ„SŒÜH àÆUª6/dÈG A>7Ïh"HH—gTÂbqa³´s^Ô˜Ö ¿:3Ÿ9Þ`Ìà“÷¤¥ê¸Op?[³f`xGÀƒÈÔøÕ˜UE3!C‡vÀñ³-GpTq`DÆF!¬M;ôÙB+X„ w†¨ ±d(ªzöSi³nR50¡bTˆ•ÎʉJÇ×@¸CQ5 NO‚B»t¸ Ø*ƒzWx6“<Û«Àá´5@AÏŒáPÇ-/´1Ãý™§‰1‚9ƒ?,\Æ lÁcÇ<ïX³Y@ˆ#ÿgôDøt#˜-ý˜VÛŠ ÑPE1¸8DF 6€kC|a„Œ.ØÀÚ äÈâk$’J!¡ìbˆx‚¡Œe)xà@ L*:\Ù‰'"G"•¤BJ—T9å† üV€z’h6Á$PC$¢TC̰P‰LÐòˤ¤EŸ.çí¡Ws¬3 ô7ÌÌÀ“‡8@s 3”™¥¸³7}0&Ž‡Î¢Mã4p¶4¢€*ië¯)pD.Ðð£<ÒhÄE.èxl·&ëD\üÁP #TÑTÀäƒci0ås¹iQúù¦dý‘ÿˆ¡ÐTåL±™ˆK3ñ9ÌndEÇNº½Ëˆš¹y‡WÍÐщ:ì7 pÔQ ½ÔTà:4á‡GØâˆtL“@=Ý`€Ñ&¡- Í D 7ò#ëˆA?.!ÃÍ20±3=3á@ 9;±sÑLT¡kk Z Cpô±BÐPÑ («µ5@òl³žT¥S“:H`ˆ¶ T À¶9L ˆ†ÂæNÎòÜ•ÿ&’ …\¨#ì̤œMM0 3ÂÏ - ƒJÂÀå qÓIZ™43±#©>Å1Å\Í+³£Ma§r Ã\¡óBþM³2œv ÿk7Œ! ¼*P7ð4ƒ¬µ*CUp€ÃÔÄP 9}SDmìÏÃÆ0¯Äk.H”4EÙ»°ôĆo„jB °õûFrˆ Áœ¸ÉQÃ"[¹ÑÁ# B‚ ˜ À â%jà:’ó–‘ a”Ä­©oŒ¸_%D¡·y¢ fº‰'0QÌ'&)IèÎbBLh‚3 ¶95‘Ô0’ ’À®ã †60¡ ’ü¥0Y=¼q NMcx¡Eð0œ˜äh¤Pí!ñÈÀ¨1€GUÈÀdƒ6 © È«‚ –§„2VÿAX;ÒB0#øÁok"@ _D®$h€ °øŒ@G&8 ¶0pƒmh  ¼»·xâFºÒnþ §¸ a:±‰Þˆ—T<çNqêÅ D @DââÊÅ,…¡‹gìrm ƒW"À©8ƒ>xèT 8æ)†% Sr]:´(¾ cõÐÆla‹WõP °ƒ ¯ÉÀ^4äJ‘š;‡ x Aj@õÊ'ÆòõÈ.¨€ Æ_!A ésAÑæÉ/úñ}q˜Aq±@ð‡LOÂIÀ=bÁjp_Eò€ X[ !¸="ÿ ›qävÍâd &„ÊJšåª¤‡-¾¼'’è­H¥]zFèê„–ˆ€&ÚÌO""9mþ“èT%x9_°€–ÍèÔ9ž)Äl`‚€Ä9 ³;&F±!H…%È bĤá@bàW½Ö‚-B^éz£!ØÀå³AÏøú4&ÏŒ¥‚»Øämñ¡·" [¸Á(xjVV4Ð h œÙyÀDÚ d &È€NÀÄà€Ùº1E€¯Dì ¶8&ëÐ'¼ô‡à[&Ø¡‡u¼ƒcùÔ;H¾ˆ€1í<ФÓÿ& R«0_Ð"YR‰2&'ɨÕï4úD?J†w ¸Lf”­n¨*ð€AmÝzÎ$8àGåËû„€ÖLmÂŲ0(Ù!°†²ØëãÏn¢¥&³b̬Ôˆð "ðh‡T /½ÃÔ(['Ø”ŒÀ$ÐBðXÜÈ2ÀLP Àm¶&i‡ØÏ$Ñ"\Ö+‚•þ"&(®»ô/o9)aÁy“âÎC¨ç˜÷²L@ÂÜ®9ìD ÛT—CUbTƒ€pØI®#3×I‚»" mfˆ‰{èT85ºl ¤ÁR ?ÚN»ï°ZÈÀ§ÿÇH† Tà5‰°(KP/´X Ág¬:5 ¼|+žq²ˆ¡Oˆ«sVóh%ñ‚ÖŽà?àh"Ð< 9g `9 >ä 8@`¶3 X@ðÐ(ö•%e€„ƒY4ׄ’M€'.syÓy¦d¶PÈIÒqÃPˆqÓáˆ" Êô1Lö N1c½¼ML!Vÿ¸µ³œ…°‰Õv§µqL· dX_C„ᱦäùBùbdA åvÅ@Bݼ¸#Fhµ Ô(s- M×µ9käî°^=ª^àÈ'ZP‚Ú@W20Îpv£1Šo'ø~ÿ† |h@‚Ë7µÔ(¡È[*\¡8©sØ×¹«9¸)ÏxUò–Jõ"’p´QHÕƒ×5™*ªÛ†oô'¡Š†T„øÄ~ôÉQ‰†3 ZNcð¢C0§üßš)¡½Bˆó rÄMõ±N²Š‹5¯¼y0¯HEú…Pwz,Ž£ë[Ðͽ6NŠÀqC„ÅYtÈO€d€bt€ õ)²g2ˆNŒ é4ÍàN5ЀØÅ¤Ë]j3¨0›ž…¢­TEÀ,Ua\5v¤Ðe…à,çV-ص d".½6 õpÿ¢3î0áö º` ½àüQC;”*á@-„wMyA0À3WD°ÂÇr·‡a>çÓN°FOdÔªV{Üs#8„BX2 H×£OB¢A“>=rG€„v|C‚Þ• ð&Þ  ,ÐóÐ2`,€@AÓ àÜ74àÇ4r.°&8)à}"' 3I`z…’$}p …°Ý {w°kB ƒa$XxC ´ ’Bç•ÙÀÌ¥ 00<ñUd÷’ªðá óhŽ¡ ûAi°#"qyÑ)ïÐÿ}2˜æj>ˆ,í”RÄ÷ƒ`Œ7ÂW¤áa»’338ƒK€ñ45Pø‚6$UàU\¤YRÈ".Qˆ‹PtÄô90~àðð40u*Ð3€ `dX@„10( X30Ù &€m pR8°Ú¢-ÃAAp‡…H :ðÁ@Bœù0ÄÀˆpZ©¤ €à¼vB¶“b!ŸX á‘r194‘;jzt€ ³°Œ‘ŒÔpù€2©3¶P4 „BX#C`5ÑGÂ'F/Fë$P¹”ÃØ+Fðe{}ÄdĶ5T#G sÿV#|½Ò31>2Ž ás€7@!½!.mÑ6+¸€d& ( }P*ÐñEÀG0F uwø& i¾"ð&¶N4vÚ–wfLÒ@Íç:UI‰m ‰G ô€‡]< q ŠÑ‹Á;ÛÀK(i'ça2P´ßp£SœÂB9òP Ñp“`È›qxJ1{W©Wà#Pd䆟qÞ©EùtG\IŒ«·­§Ÿ–pk¿§b7—¡¡ý£700ÍR +X†¸0 Ð`‚)¬!~8ã à€!€ÿ®Á,P)ÐpùTΖO2P% v¤-7ÀIqcˆ †Ó HŽ  ’'HBqIyp'n_a@OE9"pgáJC÷.‚A æAK³ÝE¶0:²(Ò ÓeóÀå´×ÄMáà‚1žZð,gYVyF¥a5yÕrœæ¦,ç3é)çÞhä>c™¦ò¹"ÿr?ë5€>Á_ظÅ&`sŒñú`R×3išO€©JF‡`52ç1°1 2P˜(Ð °Ú+€† mp3‹pI—PBf£wõ" 6‘SÿýÑ upIs¶SâÒ${àù¤ØÐó x€(ªówÕp tgy<¸ ]0A &#©b2€pd¦Я„Qóze›{Že|ÕsO 05ÑØ•Màb¸f£¦”CðUÃ4à“©Ÿµ§ ñ%è §õ1,N°Qúˆõ´¡öªúŒŠY·¡_õ 9uE€p€((З¬ù1ª`G¹N@ \9°™rIfcˇvD™rõ"BµØ,úf¦ùLStÛqCØ¡Ò)Î`ã&Lz``¤7L¡ "ÿƒ§ãÔKV5€<ó?ós­q3r X&¶";„&?ó–‚P € ‚`0æP¥¡j¨dØó”J·p&àa–C`PŠ    *PP2 ,`V#À¡З à(P @P'€6Ðj#}ûCVæ@å&?* s%ùc ÀñIØ!.ƒQàuKS-‘ y‚± Ù19¿€güq½×¤$ 8d•q—MV‚qÛ¾îëGy`Ù«,¤Šêx®1°ð§u©}Dcù„²Îf²4À±bÀÐíD €ÿ}9³2p*€' Pa!êQÇ¡([yÔ¶6R†@ãä7‡ð,‡ 7FaK~V‚°RF‘C„ÂA®t…9‰æ! ¢€— ¡£ ¿ø¬ÿALÂޱqÑ!¸yÕ *ïÅRL$¡à-˜!'± Äp&0Yª1p€˜ŠË}Š{$¡ZbièÒ÷` à¸É@xì>¬ÑÀª#P€à0°p# ©¤±ÈÀ/€œ™7À8Þ2‘qÓ¡’{ók×! nà™RQAK¾€ `‘9"p|D, ^­“¾ø ÿScõÐ;ü€‡‹Äó` S|ÌÈ Ù Ä #'Ù»‚gù#`{Û9ÀVbâêl 0ut˜~Z©­±%@tD~iO%°ú˜p z ÀbU§¨w¨dšö°î×”q“üã œ &á î1 hñTðŸ< õü¯°L¯l7Ô²Lqñ’³Ã ý ÏD Ñ@£" œÌ,ÝÒg:Q\Fò0)ÐK§ih'°‘š–)€¹`WÀ&kâÊ ƒ&`ÎH/  ' : +s +è+p4Ss(`‡@ÿ`4ÀÇ P‘4v˜ì'—Ä¡`0Ä@f’ ³°‘c‚(%!Kj‘½ËD›¥ t½!" { r‘¡¢“Cnr5 ` .}Ù˜­]¡¼v7fÐ3Ñ(#`~l,²-f€HMXʲ)›ŒÿX,B!0¬ýÔpP©{è>–ûh’Ú¤Æ'p 'ˆ.Ï£‡ઓÔÒRÐY‹wõ³^Y¡v󬸤mV?…ÀwÈÀ¡†då ¢0 Ï) S ,Y ï8 qá ÕÙþÙœpe¶I/aUg( ‡–{#uÕNÿ7òÿXba§¸–™Œ"³˜„)@=Ò³6ð‹| 9`U@pepAÉ•KNà6p+°tX·‡ ,d¸5®š-ÂIÄ›Mòmeâ ‡H÷)¿@×$ÁÊ—RØPp¥2tI’ÄduM1§˜ÙðßfÎÒ¥óÒÝ%g ˆûØ—a `sÌ}¹¡ëÔ– ª[ɵ !ª“<%vðºRÇâ  [.Иç:¹sÐ* ™¹yb»¥‡€ 0Üg `²'[êØ¦8JU²^7 ‰ó˜ ø-ÿ¥†³Å¹¨¼” 0s¶ÚÐKâ1y!Æð1áû‹upæÎ>Å]n Þò  ‚04 Ž“;&þ2ð㧨§WÉ(7.‘^瀅u3°Ncy¡»5bùø© pn£„ÝùxàÀ+€<ŽŠz‡ps` y7p/øb’Su,±P * ÿ"S)pÚ€®uå"LGñsˆ'€³®…B º°È*€ Ya ð…ù zÕR àS )}%®t) rM£ðx¸!HuÍ\¢# ´igÊù80÷V« HQC :Ÿú{Zc ›K3Á)¯UW¬a=?rHŸ̓2õYTÁ’1õ´r4€ç`0ÜÑýº©‹¯´k 0eïpèú@)€Ï/ ï`èF³}©² ª ³Ð@ pIï¯ ®“{ïWIÙ¥¿‘DB“°ë™Õ¯‚)ˆ`ÿ0&€…@Da/ÖÌ{x]D/€sva±`‘×,Ì„ðyýžß÷ÿ  4`4„t44’."" ðN0TbdL *>0*2&ò*BdJ\jp">ðd0 Þ\^N: :>>b>lhn>pÆ6Q0bFFNB\\$@4Â?N &6&:Ú>p<<$$D<,<ÀåBƒ9rÀpáÅ ê‰t@‚ 8‚’,.Ì@€N– ¾h@€E‘“sÌxfF” ZdFˆãDQOŸ?:Ô'‘„d0R €/Pì’ÿ! jphðL L°ˆAÀÀçÌâ0‡‚<ÈpB€ 60h¡„ïÄ0A@8T6Ñn˜=ðàÂâ2BdžuÔM ËÃC XxОüƦ¦·à „7œŒ ICÞØt2Aî( D I`f†œš DšyAàÁƒ,§ÓÐñ™ˆ†?ž|ù¡ !³É slx×À ˆ 8±` pÀ…lpàµ:8áN†Ùㄦ¢Jà…øê€CÐÐ Àƒ Pp † Th0ÿc0Œƒptpd@A‚6 † BÃ@5 ,€ˆ)<€H h0¿Lh€†ÀÀ º¡ @ÒAF(Á‚t8©€jÒAŽ(Xxå(ÑB NŒõš@á™Ú¬Ì TÐA Ï#² B‘Ö ª`a‡DÁö0 À`hµY8€¾4‘†xƒN Ȇ x:† 0 WŠŒac@‚òøàe2˜Å‚ÐR˜a&€}ˆ Èš!ã¶ã HÚ—ÐA¦>nÝõ׉²È‘2Š  ÖÀ¸ô›JAûvfkç«ÿ h`dà‡< Ð08UmXJ¹ j0hë¬^ƒà‚dxG_\‘…20! P Á† n` † 7¹¡† €ï©Hpp \ø‡Û<  x@3ø› t0àöš  ®è"ôÀ%Êá tÀ"!š4P¦õPB=KÀ £CÒDÀ„9Ìp €À";!‘ˆ‚Ð ,â’í¨º¨OUˆÁ‚vÈ@1ˆCHNh™›‡õ†–38XX@Ë5 1‰jpº".“™D4 äP' †ÀÀñð0ƒ²PÅ*2°Ê ª3Ä‹*0»ƒM9€4 •\ô"À.veXÅŠùª@$}u@ŽÀk>€yÈ8A4€¾pHÀ3:°gzà™ƒj(€.©I‘Øü9A £êA )h  Á° 8"p  ‚`:Âa ÿö´œ!DÀr0øt˜ <‡Ð)-g†¥*Á Í´êU;6¦˜¡ °æ :á2b@ŠeÞìf`ŠW5˜³ZÜ¢7‹…H`‡Yådz¼CâDŒ R7 ô¬‘è¬vÎ×kðÃA­:3m©Š| P}x@]À@N—‘z9`> ((.Tñf ºy) ³jh­xˉE ÉÀÞt‹ˆ‘£ˆž4ÈÑËÜÚÿf@€4h@Ï.ÁN‡² U·àš À3ŠÍ^•‡ Ã1x] d lD8fáxà ŽH‚“œ,%ˆ#‚ €Ä;MPNG¤ 84,ª¸€å˜àÂJˆÁ¹E62Pnò‚àä@NHÐÝ·Ä—B'¸b‹rTä¼l…Ç_‰·V-&†½ÛEA ¢B\@]h%ÞËü2Nx¨ (ðO<—ƒ $¤1Pä0µ¬ ¡¹náöR%lÇãK;•I€sÑÙ\@èbF6pˆÂ! Pb""X2u3áÓ¹Ã`=“$ÿœÁ;pCCÀƒ™×½.Ä Ô”„¢²$¹WÔ6ãÂ]sV?}m dÀ  9Zë—³ˆß+Æ. ™ 6ø<xó.0KŒ9Íin-âÇÙð/2€l ÁJ# ppèu°H E §AÖ¸8fŠ*ð@*`@Šî• ÑÓO8 „I@¸±eD™2‚å÷ ¨ËÀã‚$¤Aaf`tžŒøšå-çƒf­q+4Ìpúi‡Cw± jØ Ú•§P à¼p@üÂùes«Ó€€.`@€þ†ŠH÷_NáeýŠT´ ÍŒ6X ^‡¾7 ¼x ÿ8 ˆ"—ï¶#1'ÈÁÜŽdào_sÕ"6 fžxÁÇ5‰'Ð’¦:µÈF0„35$Ê™ (aÂaZ‚aútùçYÞ”^¢vKyK“€Çè©Ä E4PÀ^¸m•Ä£9 Ðë©ñWÄXÑŠq9è^à-Ge¼Ä»òñ­øætWèÊX6 „Ðü™ßc—ÏfÎÙ¡j‰œêu‡LŠiwÀ± ˆ€N€ Ì@1PÀ\¨Í‚^ä@Eä ëDQ" t r1G  ¦ÀsÞà dôרÿÅ^P!ÙÞ›ìã>PÀp -Üê,Eú Ú^O8ÿ ¶ìõªè]RdBêƒ.à+¬Âêšïê¸Îø`¦BƒCà"Ô! þeX`ähjÀì& Tàü"<àD£/²«Ý<Àã5L€ï`yBMFൢaXâi.„Xâ ÿ4N×¢Àršc96'¹À` œ@ ¤`” ^`õй8¢tC&pàr ¶k»ˆ‹Í›C ç VAæ©ç -¨ T€J༬ˆB箩‚è]²N­ÎËœÎ銼¾+ëÃ,&À4 J`Öa‘8pаìn€ÚDÁCLë §ìEôFÒ`mÞRÿ  ã"À^ ‚ÜdlÆ :°ñ Òc¨2â=øD'`N|l&"T­t¾Är‚hÝѪ® 0$ ` €X¬b¦".´Ë¬¦h*Ž.ž'ŸGcŠÀ¿²«Š €* @8@ŽL ÀRš/»¬ËÞÌ/8ÄŠã" Ï ÊD‚ÐD ( €ÇNp   ZåVÃýbÒÂàÌ*àP UB ’â`|@.æ‹ «ƒ*H :¾À üÁ ˜ã˜¦À€J 4àöE× Ö„²À¨àÍr™8©¸Ðƒ@ À ÜŽ¬öñ,ÎÿŠîÞªê¨íõÚo$1À«­¬®*D eæA»TÑ˲è“/ÝŒïÍâ[dP,DH€< qð¡R ì¨Ã: UrÀ 8Äj4â'ÌÊŒøƒV\ý¸Çtð)“ëfè4§)h=t 7ìQòku jBÖøOׂƒβ9…È_%ÞØÒkžˆ.y$ì ïRÎÓ*Ú¡ù´Lr`ö>C’áæ#´òÇ,½8‘/´è"²&ãRàéAÊÁ* q¤€>îLÀ ‡ m@ D0@&ÿÆ,> tPÀàœÏFh:Š`Ö*ÿíÄèàêK–Š·¬ åq*tØ$¹Ä tä€ œ³F[G¬ Š‹–öƒ6ʳì Tqœ1­ðò¬1èhP ªh»@&d -©Á,²¯”ï=µmK9òŠp@ Ò/e|e2\Àlà’ü3ܧ¢’J``ƒrj& @ÞAæì,š:€%.$D@7«Ã8Ó 9\tÆE'p 昂ÎD \4#”#ÿâÀF9•c(A.áÕFÓXBŠ…4E¼é1¯íËÛ¸H ÕEæÃ´<ëvÜ‚rxÐMùÄéÍ’fTÑ€FãðT0ÿ)M/)¨YÃA6 €¤*ÀJâ®ö.¢„ž‡,L2À¾æTBÓMÚˆí FÇ–$áL˜¢ è€dM& '9ˆCæp” Jb:5`¥ òfàbø¦*@,  AP@.Ë"ÂjÊ<’¼t/I“tU;¼Käòú†Wç¬WQQÌVñ]p°IÈÂeÀ”ášÅFÔ”2  5A £‚}* NqrÀ Nñ(€¡d+Š_B¤bÚ„%ܤ&l‰$i'-™€) † 'ê–£µ^ˆtB¶ò ^H`Ѷ<¶rs¦j: Z@XÿökÀI?Ì¢MçnòœŠço3‹C?’gA,À/ÛËêÐ)­×ÜÊKXEáÂ2ã,^ ¡8€®jÄB@Mg„4‚ µ>>`4€<óÔV$Â\S¤Øtƒ|Æ/Wf`l °±:âÀöáC%•@µ–¨f€$¥’Çœà…œ@×ä`×Òöy‰‚.€!˜ª'b ?Eà5H!àB@m5çî–wv!»š/Ûäìe>ág]À´i/ÔIq{Õ­Ä ¿®MÛÖêÑ>€ÜgV ÞëL€8—T‰^(ƒ$6H2  J@\@.¦-Ti] ˆð%m ¦ÿÅÖ5ƒØòУ¸.¡!&UØÂ`8i^_m,Qtstãt<zmø'HS‘àbv¥{žÇß2€ õEk:lcoÒRT•î® ³H`ÐZà¾ÆgÌ‚~Å,1Êé¼ÞlK‰Ço^CªÄZ›ÁLÑ4MÅᔢ}gä ^ €l -€À\€Ñ Ô¦¬¤Ž3 Ü) Fiy'xS<x³ÀÍ@)nÈ€‹)^Í„#@Ûñ†=9Lb¨ØÒƒGc1b€-ÒTÚú&³ÚM>Y¦A”Aô xo­`ZJOÀ^d*ZuqóëÊÚ*œH¼ÿÄØ+ž¾ ÀL1sfgÄçŽY¸XàTnà0@F UN€ %ÚçB¢–:NG7á=€„!õãª@šB‡Þ`‰"æÕ=b’ëº>9  A'„#êHâ‘ †#ôvÅð¼Ô¥Ú ;3Á»,.*…£!g†I0€x2~ÛŠÇoÕ ¿Ô÷<€Î”V?ð§úx¦gÖFa¥çAŽ'¡¨ 4Á93¥™ýÞ"ŒÜ¤Ò°1ƒŠº @'–b+ ¸ÆewŸãH/ šrŽƒ cº«|°!¶òt<'ÂLà*¥@áÎAAZ–o6Dªÿ§R&ç"vÒðμéÜìR¿àA‹|µ~«¨Ap€` çxÎRœ!„f:Ü€Ìâ(tzÄÀ33¨‡à(Fƒ ¨c$ÈÐtâøK†—”BÖ^à(pvh'^@ –ã‡0`…Š+ ²À«ûú0 .ª´éwö–Õz]6(F£WHð:,ª†eœ(oã,>€bòŠx‹FV̆çŠ:ÒÀ,l1’Çc€NÄÔ C¸` >piHjG:WM3SfÓ§®Òø™Ç¨C)==ÂG-çã €·@Æ¥jb$RÂvj‹ÿõä’ ¸A|¢ ¦ÁíQ¶Øí’[Hà Œa4^£*¥úLÞx!–›AJ8àÑ\¦Š¼30‹”‹!—Õ·¯ŒxƒÌ½¿yC\¦Å±ë2àR€&è²&ä@8`Ø´YËZ@o\@m~!iÜ©•Fl%A–lŽ‚¨^ 3à¡ã¶gâµ+„è@B Ä}††X›©-G°€C JÆ×‹@] Êjˆœx¡Ýà2ާØÈ^N@^´´®ÉÛœäóoijâìBã,?Ô ï€n@Ô¥BBÞ€ Ä AXP²›Õ,@ÀE€‡ÿý£{]=Ñ™]Ép=H“ §rƒ`ø¯ ÖC$v7 €m^ÏQ4à=`ÐCÜvÇ hÊ–®ÀÅ!jÇÆÂÚx$ÄêV àÞˆ±œìôi(€z9¥¦!ù¸¨KôÚœ—ûÆrÀ4s¤J®w'€æâxx²"ñàEêƒØæiœ°t `nÀWþ[M‡}F¢}‡ Ö/g°‘jÀÍ!Bý0u&,yɤRãh¨)*F^Õ/ȱ9¬ÉsÄÄkaú#P¸`.ÕˆàjÅ`Ĭ¦­æÂ<$4ŸÎ ’Á”§AùC¡8ÁÆ«R±Ò-°KÖ‰—¦Xd€%ðDÿ46 K§-ì,âB>€¬i ^@kj€0€ÁÌYï¾<¤šÕ-Úw2Tþä;ÊÕ­ §$"pG%fË#ö¨LÍÖžO,¸b´†‹¾«ýï –ã˜Ez—W$Hý€÷òNgýØ ]­’ Þ²IðÖWš»úÂÉš˜­Hv­H1ˆÑ;ïCº & V€êœJAR aBÒ Êξ•æ~À9šÃ £‰ªcØ#@È÷ù>îˆF_±£1d$ —f®N c´×#–ªpÊÂ!±h<"“Ê%³é|B£ÒéôòšE ‘Ãá‰$`, ! `ˆ땈ÿš/€ðø ŸýIv”Åä@ää88$Ä|85É8ÄÄ@Ĩ\ª L\:h ài|ÈÈlèld à¸èLLä<Èx¸°Äœ|<<°ÌàHLÜt|@¸[ œ$ÌÐ(Øx$pL?34̰d\D,¼}h)hÀØÃdè‹$ôc°DxÆ*"L¨p!ÃãÁ˜N — ,`àF‹†ˆn2H ˜"^h| à}þ˜ DHχ`|ð`À#!ª8UÊ£ Ñ^ è™À,F¤EžÀ¡À ,PÀðìÖ à0õÁ„ÿP4À!D¹iDxÖ`€3}r„ži¨Ñ¥Ÿ—õ¾À p‘À—.¼»0ç ˜wÆ4̬y3çÎ x1æ…â$@Ý‹ ±K ðòŒ+0FJ˜“@Û*õœ@”óȇƒ0‹uçÃ}J‚”¨ŠAbPƒÆ‚htè±A< ZnhPõL€.^œÀAÀ® Á%äÄ¿P@ÆEÐ ”$Á8 \\„P ³ @¶Ð ,€mÚz„}ã™JÀ0€ä¨B`·y@{@Á¼“2!š{@/ö°•¸P•cƒ>b9Lj‘Ãë‡>^“È 2‘MÆ^ðÉ‹b´3—ÔÚ¼%¶Sl€LB¢èàun@Y½~N h1ÍÀkÓЊõcr"‘ˆJ~ÑÓcJ&€)…“ ÐÀßÔòäÿ˜ 1x€xÈâãÔ‚'²¨ 8pNT#˜pШ"^‘h€Azˆ€@£¾r…¢yjrª CÓ@uË^F1eL d4±ŠM`$30H@CÅœ¦ ·aÁòLªÔà®÷ÀG0Ù0¦©LD>µÊQÙ È f°°t%ã%€(I©*x÷x2è€ÿ1€ÌÀ=ã`è4(Ä @9ð©ö$ñp¯[z š!¤²± r "À) xJƒ$K^kHÙ…P9«D/€¬¶Øÿ¸ -M {·«1ÿy3zÀFeÓB+¼ +LIÑ0 ¦ô@’lH;7Уààkúß$ê:^b„¨Bx®N0 H #p<€âÈx† là•„€69€‚ÄÀ’è1S¼0ÄsÅ-là ŒXª†f£é¸€€(æ ,xéëô‘±Ã9ÎFM…o‘Ó€¶–µqè:š‘îŠg¡JƒŽ~ÁɶÈT†a(³PâJšJSXHcT6]¸}@3³  v-ª2i²$ü8¸@9رžö„rœ;nœh'À€{\€sºÀÿ¼4ËéÙtÑ5]vhر/°×”.$"øÂ‡4Eïa˜Dµ# ª¾pô×g´Q ivÕÚ_¦xA£$£Jã;5üæ\¨tÐÔgêaÁëFr ¸Îb”B‰@bê!÷Òú—] ÷…ñm%iï/Þ{pƒPâäèΠÁ4òzy†<÷lºªDI—Í?hÎÐX•xßiÀÀIdH¥Þ @:H@¢aØÒÄ”Á^šÐFIÍ»áÌe Ãe _ŒÄÈ®¬FëðCóØAó=D€HŒè€>Ôžò0R¯PÊ`ÿŒ>HÀkÑnhAe¨ ììâÝÁ|8Ç €ZT¡€L(@6¹¬Zd€, (Â9ýÍ@@àø`òhpfá_ ‘¾¸ÙÔ•ñL¯ü —u˜‰}ÈnØCÑIàªÎEXŒ`ŒÔ@Ì´»‰ˆÁ¼!@èϜٰ„–ç%ÀŒŽÈÏëüŠlñ •ÎbA–„à@#†DLÊKõ}0Ñþ™á)¤ÕÝ¥‚œ@TéZ…ÀSB× À|äÀ3¡#¸À-HG¢›±5ŠþÙàÖJl§ÙGºŒÒ"iƒª¤F8 5Â$¬HÞÌ€E¸G¡f‘a#ΈOš AÌH‰l]oL]tA܃ÀÀJÐÛ}A5c@xG,OnÔCW2¥ôƒDŒNöI€Q.€¸Ýyy¡¼X¸’ üÅ{ˆ tU¢Œ[][)bÄÁAÀ? Û)(É/ÚAÿ‰´Ž·QJh,O™ ôCLBf«ŒÌÊ ÔÀip`b„iTÖ¤ƒ‘Ûi ‰h ÃðΖ•L‰„娌YM¨ÐÛ"š™k( >„I@T<ÐT,ÀF)ÀDıÐÛ .@ãX€TL4ËyE1 G€Zq`@t@XDÞ< t$0~a až(жÜâ5J)Q&JCÍ飴aJD`FdÊ'ŠŒ›ÔØ]­˜(¥PÓ[òð’$ÆôJäžòTæ7yÔîÜdT½¡™ ŸkY8Ì=ÜÆdy"¥4Ka M]Æ CÍlÁäCCÅ8OO5ÑZºìÿÃÍ€Éy±ñWº$’¨XÂÀW\Qý"¥ËTíŸ0àNѤŠl_DÇèÃ|:ig𨌠&¯ð§)Íä7aVhµAhÙ^‰4ÀU¤Òn’0‘Æid]ÑXLM>ßa¼o¬’Æ0Í<–EÐCTÎí…€aT"_IÛ²äFntÒ!•^ì…°b€†it¡á/ú™,ѾØe&ÝÛ¹Ô@Ï Ã-õ!%iˆF„Óšjf€b €»Ü– ƒ(¶AÌdi$è—Î]= ÏŒÆÔ½ D:†Aª`JÚÓè VéhA(fI°AÍ=c蜩€¥@Í–Ø|”ÿ¢õ[ €0€zRÒÉmEY*ø‚/ Á°ÀY@»™ÆÌ¦b\`rZm` ehãâ$RjPÌtPAÑÈ©¬B,Q …0üÃÐüb I„ÐX˜h\€ŒôJ¯ÔÁÃ^¢®jHÍ ˜Ôˆ†)ñÍ´ŽUJÔ°HTΤ0@@p#}eG˜Ji§6üf,µª$åük?„D•%O­˜sîÓ‚¨ël ÃÃRظ¹ÛeÑÎì€X %:$”fåHóIIJ!KL– –ÞàŒDlÙÄb­¼jm˜£\ŒäŒkêKÍJ¼¢-]#¡Y]h(˜E¤ªˆ^A·‚lÿ=˜Î0Ô(Àe8ši1‘a0¥Ê P*ÐÑ[Åè ,RVrŠ2N”e<Ü)Î C³ü h”Î÷‘€bl#‰@$@ NZ¦ÔàäÔ tê±0Oi´Õ ™mñFÁd¾ ¼H£ì$hôL°0¤ªÃþÜIŒnÔ$-âÝnmM>FDbŒi~ˆè@ŒÆ¼QHÄ,Y(ãLÆÍuå°PŒrÂoØH²€H|ÖlMJ²†„ãÖoG˜†¢Pê±ÅÌÛ•˜@Ááµ1¢ñqaü+íEZ²ä†Ì@hc=J£ñŠ0ˆUÖ†!ÔAÏœiÜ!ÿ }@Û¦ŒôŒÏ\Æ0´ˆbDĺŒtAëà…É͈@ 8ãmŒÎüÆÔÆ`Šè@@ÔÌ/šÚæÄ,@J‘Œj„TŠHÌ üÒ7d΢†|”ÎÀ¼@°«ãpDC5ÎlaÁÄdJºЪÐToB›%ltp–ÁrŒ$f]ÅŽ"œ&}á¸9*øP •†DxÄEØÃU¤ÁjÄ$sYIáÌÕ=Æø^@oÈàaiƒñl ôJÑl- Í-É 'o~% ÈÁ‡Ä }A0À¯Ð@‹ Ëð<àTj>'IÀîÆ$Àl‘( ДdÿôQLeăiMŠ'êî"9h D!“s¬$\ ßÒ\a®ê€  Ãe±.Ô¦Yíe žÍ.…MR‹ ´aÑPŸD¬£QÒnF +š¥ÁÒµF3b:‡5@6Ž€JntD›òh€?†Ãmóè€ T«»ÃÄàX@Ì#Æ0O¦PŠH4À¤ä¶Q³cdŠ­¯šõ c´i9õÔ$ ÐÕöOc‘!Q5–XíÔçðá·@_‚£fÆ ŒðnÐĈkôÌO–θÍp–ÝðC çÊ úLò #IÆM‘ˆDéTN¿I+DmDÿôÆ› I C<À—µª¨cd€íFóQ©¯i€ãˆÝ0T<[LñÐüᎸ×K]A"ybèÀQ—3hô†E ˜|G‘Æ<Ý$é$ë¢×U؇¼kçÌÞŠµÄÚÆCðH®\VFà5‡Aže:D3Î-ÏñÜRA]P,P4çÒ‡DTDÔ‡€/½à²I@qVvtDV^L½!ÑHèã@ ÑFÀ¢áà €Ä¤ISšII;H’'’×Â!j“3ï©Ò ƒ6.Æ–Åv<›AÒáJq€ì¡J8j…‰ã«ŠF`Êž¥ÒÍ͸MÖ¯ i®ÌÈ’#b×ÒˆY7› ÿAd“Dp¦÷.EJ¬ Ÿ†c¼‘ã8Ú˜y"‰¢÷PLnPLЃ> ‰&Ɉ=š Š@Ú!Ôì†hpNJ„Ã=øó—’Øë¦b õš†56VNŽ’¸-Æi›c±yeϪøG±ÁXä°Ç‚y¦L[„àà äì(݉5 =¤ ˜}"ó´;܆ ç± TíDâZÌ…$vx°ãÊ 1ë §„ÅK™ ?`›@Ñ{i¡a%¥€_1”‡ Y—ò¸e„Š-ï·ÅÜþdföŽÃ²³jpaÎÐ×NlMjÈ'T˜‚;”ÈMzA…Ð’K²ªTm°ò×BԆʇÐ&…Š¿­ÁκF‡X°Ö´¾†êœ{QÎè1€,wŒµëôCq|µøåPÌQ=LªÚ0¸ÑA;ntpsec-1.1.0+dfsg1/docs/pic/pogo7.gif0000644000175000017500000003277113252364117017064 0ustar rlaagerrlaagerGIF89aË´÷ÿÿÿÿ!!!)))111999BBBJJJRRRZZZccckkksss{{{„„„ŒŒŒ”””œœœ¥¥¥­­­µµµ½½½ÆÆÆÞÞÞçççïïï÷÷÷ÿ÷÷½µµ­¥¥µ­­Æ½½œ””¥œœµ¥¥­œœ¥””µœœ­””µ””½””Æ””½ŒŒÆŒŒÖ””ÎŒŒÖŒŒÎ„„Ö„„Î{{Ö{{Þ{{ÞssçccçZZçRRïRRïJJïBB÷BB÷99ï11÷11÷))÷!!ÿ!!÷ÿÿÿÿï91÷91÷!çRJïB9ï9)çcRçJ9çkZçJ1½”ŒçZBÞsZÞkJÞsRÎ¥”Öœ„Ö„cïÞÖÆµ­Îœ„ÖŒk½­¥Ö„Z÷çÞΔsÖŒcΔkέ”ΜsƽµïÞÎÆµ¥÷ïçÿ÷ï½µ­µ­¥Þν½­œçεƭ”Î¥{ΜkïçÞÆœkÖµŒÎ­„Æ¥{Î¥sïÞÆÎ½¥çÎ­ÞÆ¥Îµ”Æ­ŒÎ¥kçֽƵœÖµ„έ{Æ¥sÞεֽ”εŒÆ­„ƽ­ÞƜέsÆ¥k½µ¥çֵνœÎµ„Æ­{÷ïÞÆµŒçÞÆÆ½¥ïçνµœ÷÷ïÿÿ÷½½µ­­¥µµ­ÆÆ½œœ”¥¥œµµ¥¥¥”µµ”ççZïïR÷÷)÷÷!ÿÿ!ÿÿÿÿÿÿÿÿµ½­cÿ)ÿ{Ös!ÿJïB1÷)ÿ½Æ½œ¥œœ­œ”¥”ŒÆŒ1÷1)÷)!ÿ!ÿÿÿÿÿÿZçk{ÖŒ­Æµ„ÆœœÆ­”¥œ”Æ­ŒÎµ¥Æ½kçÞ”œœ„ÎÎ{ÞÞkÞÞRççBïï9÷÷1÷÷)÷÷!÷÷!ÿÿÿÿÿÿÿÿÿÿ÷ÿZçïïÿsÖç­ÆÎcÎï9ÎÿŒ½ÞZ¥ÿ¥­Î„„ÿœ„ÿ¥œµ¥œ­œ”œ­”­µ”µ½Œ½çcççZçïRï÷9÷÷1÷÷!÷ÿ!ÿÿÿÿÿÿÿÿÿÿ÷÷1ïÆ¥½ÞcµÞkµÖ{œÀÀÀ!ùÿ,Ë´@ÿÿ H° Á*\ȰaÁ B4˜Ð¡Å‹3jÜȱ!Ĉ N,ÑBŃ&/\X@ å0 ¼ð¦?ÄÄ Ó“ÿ*žìH´¨ÑŒ?R4ɨ@•N%¦,)4)Ó‘-(вkN:gú0V€L˜daÒTK`‚AáK·®D "`µ‹Re®gÏÎ\K3'ÚÃj&6€v@¬Lî½8™¯e‡(@¬”Ä&ìùÐ$º&Öɵ€ D°Ð)Ð B‹º­»·ïAƒ¡áôõWµf¹æ40ñdÒ§WåJ†@‚Þ·4€z¹»Tè“ÎÿÎN @ð /@0LÖ_²p–ÿ&M4P`ž&`³‰½'SXÜ„SM:˜Øq6¡uÓaú3 ~Ve¥xag$¨ä]‘E'vD0àO"f¥È4€¼WÓ4#~ô†!¸V0B#H"ùã"¼1‚“p€¶\ º!IÈNFrH'(ªä¥‰.Zð! @ðP€[4@€ChTœO%çS €H§d€`$0ˆ‘xàá†r/p Pvüq¤“†,Pl™°`f]òã DJj¥‘‡ØÁdž‰6W‰%ÿ Ò4 fV`ëc&1€™#½ø×ÉyÙ¼%hz}ÈœPQ@“‡I$„àA(! ðÈ‘K^ Ü0B & VKhJN’b ?:9é¤xÄñc¡‘Ùê«¢YàÙ1ú¤Rzq‡DÀ;WJàA$g,p›¯>ð–TIí"‹Ü¡Û¾W2òÇ “X7Ȩvܦm½% GƒÝ¸Vr"èÖHy2–§vx­Çs‚Æô¬tuZ ñžÓñ¬I)pAɾe€Å˜·HÒ™( 42©ÉÙâ±Þ¬°C€ÂF*™ä‘W–lª•‹@`À ÿ²@«-UÀ”Þ°Á~°!¢UFÑàÅtÓv]àSK[0™cgJꮦ—dSàG$Ú–ür ,à€Ä®-ª 8°ò¾üAËÐGª6 h# ¼Ô–h G D€ˆnl<:T°éñ9=åuMe•öb&»ÒdW9àÚ?â›í‘Xšì¤½¾ÅMH úÚñÉ c#‰¥¤xxð!vÈï!p`€ B°á+BJà4¤!=VÙi°÷°¡å* hÖ’âX„`€tlB& °Co$…?ºÉG#“€è7Ö  Ø`«©XÄKzûJKÿ ЇÅ=àƒà0ˆFw’–³¨årÏ¡ ±L’š˜,à^‰‰M"´ ÌÐ7hYoâö²B âƒH$´…¥*eë…x¨–á<Èð!¼.¡  7SËÊ“¦W±'' #— †žDp€ÀpX @q7@"b{™½¡™t2p@ ÀŠlQ%ŠrÀXÒ€A\+‡`À£ìp­H " ØÂø±~pÁ$°BÐԚݨA_ɉ²P*@€ x€ƒ"qD°ÁMÕÜÛ4S¡ÉQ&%_Rʉ´8½„DG$¤ \¼ÿOÓà©P…)p€\øâ¿F1 m8Ô¡Ý8ÇìqŠÞcq.ÀŒ ƒ#lÔ£E(Š`„#˜t£(-‚ µ,>Å W¹Àöc€ãøç@à¨/ |Âh¥yпӼ&‹HÝìÐÅPx‚ ºP ˆC 03>&)@Œ‘   ô¡üñKÖRD ¹1þT´š°ä„J~,Ô"âN¡ndOP¡À( 8ŸNÀQtÄ'Ü3®ñ|—ÀìV}"ƒH&`ö c9Á ì@Íÿ•QQÕìúððšµ5QmCÀnc¯E¸ B‰Qêv†œ™¬KzQq¬DÅÜSWÁÝ©àéÓw­KpÀ"¼à®¯lºI¢¶Øð¾„Ìv¤’ðˆ^jG¹Ñ "ž·/'}â>¯Ы µ[F ¢¦f!Ú´ç%Én 8À×Òd4.ºÒ%ÊÔ«€0€³jIkn[€Üm ƒ€C-∟®à§ xÍó›ÆÁ&ØD"Ì`Mø)‡h1O@@œÙž3ÌäŽ\@æ)Àî ЀC¬ `Æ:T£`g€Z`µÆÔ’ää‰m²š×<"•ÐbÿÉlŽ3“]º¤T ŽŽÚŠ‹y¶˜Ì]O΀&êÆ‚èS*ŒÍ †K„að8`B-AK‚Ô˜±ÌGÒþá œíXç0å¼24„â@Ÿ6¥BK1Ó*Ä´:'Æ+Z ¡¤r:ÎÝMÊÖZD¢|Z@Šœ´«¬Vÿv,ñIÀ´é‡ôå¿’€™êÔå[#„EW霊^…A¢’ÐÅ3ªâƒhD#ÂÐÇU 2žMºb,@¶A/c\M,Õ–2¬‹TŸ"ËLGÞž­­MhRG0CØŠy|š• ”ç%òŠñ .ép ¸YKl7²HÇpáeÔvŠSlÂÈÿ€€n4<Ö‰‰×‚Ìb œìZ:pU‘¬SÄ„¨Ùj†³~‡àN aì©!gž|Â[YYnjª5¾Ì xäž½J6FØÁ5¨º‘>Ñ•ÀF„Yl…Wž‡'3ŸáŠÅš£žj:; ¢† æ©“õ‚ìà¨)fKRÙR•!¼@ˆ=h*µ @>9”`Áu*Χ.[“H@² C¦`'É?±@0w¹X%µ$øtO­¹”0PVn’ò=˜pN¢¡€ä™“.Q€Çï!v'60¢iÀD§6¥)êÿ 1&AC+¢ø›ˆ1ÿ¬e²µq©È ªJ&à€6]Nö‘‹I4Ó¹.;Uz’•ftƒ?ë¡ k àBŸw*nÔ€†±›P#`Ð[ À% ‰ðh[‘BAkÅ×"“WiFpÜñk¼Â~)’zêñVëÑ6Dw´à¦Böb €¨ãÚr~`>çÓBö7D¸$,áHŠâS ð Q²îV8;tðçd)¡fb*(Ó+Ñ!„q  t¤[ês?îC7÷¢*”x à—ªb$lvcêÂI ÀFÉÑ;t@ŒˆƒÈ~ànv7…¡=·{Yÿ¡!IÁ;ˆx•$ûS7dƒ%©ã>›¯õ „p×Â_ªs²t$à|K.1$ò4O!‘ ‘ r3|Bô*}ˆj%hmù$*2 pl F–CÈFËAõ’$läyëSC7$§AÖ5j1ìW0d) d <€¥u  pªò!6“‰1’*…µÙÈeðtOüÔ¦¶fvRSBÄRŽEÓà¤FhD2x°ƒƒ…;*$2‹plðr!ð l |iD›8ò¢&‰e À+öS†fu÷ñ/4“&ð*›p ÿPM_k ‡^~€}°„û…~ðàp0 ¨‰°cø‹( ¥P£pt­0 å µØ Ò Ö Õà uõ&PR'eE¹¤'bæÑnðGP.€ À=pqA±èÑRY PáFgø°q¨` ©¡»à ¾À ­´Õ e«Åð°Væ±2Gð6pDàQ`DÀÙ(,`C 71èkãç-çóB½‘Z4#i«f*éΡˆG‘"P>'’V¶h}ð!NvÀ^ÐÿPS ð#yiæjg¦Žp0;#s2f±Ž6óÓYƒ—hñø (¦Ÿ-ÑR’)gXÑ``´p+íÇOC—SËRYf¦jž8„0\jè‰îÉ}Ð-ɹW°†ƒ‚C¹á2Vò#6”XÎ5u-q€ttGx0 ŒÑ 1ÑA.ã/¼ÈId3!˜ˆÒ•W9.y8¾r_„dýôW1& 6l#°ƒà)ô }p ð#Ȩv˜‡p@ê¢-q}€Ðn}s>¯%WÈl¯Ò a±ñ3…r¢¤¡ pàDì%ˆàK‡P¿KÚá+ w5ÿd÷´G©e}À:p Bö  þW+‹©§s¦T™AY£Jö´p©`á Cª²Ú4ä_Òh³j‚˜±T)-™"QEò)uª1Â"‘«ô”h‰Æ1‰«ŽÉ5‡ &$‘"i²—|en¨ÙG“Èk/¥¬ŠÙ0uT½JTKµB3q6 ri­&?—T©*®¼ª!I&&Gõ¤1޹®."<ÖDq•6s2naÑ*2Œ©…"‘…Ç2pBÑ[{µj7qÃfîÁm•úJ9@9º®±'‹fÖäãÖS‡ ¶j3ÿ·n1Ȳ£Ö. 'Ъ§*¡išf9m*[g1ÅDfà( à auŠ\£$Ùj"[Ò«£aJm[‘…)ÑÅ2«sO²¢e*‚CBÙôHþA‘æb€ámW§ŠBP°S…s‘@c×bYŒ‘f&²ª)Q0§0´}ñV&2u¶z ‰F« ð0 ¾f9±&ietiì12k1û/4k[ƒaöMÜW(‡P!–¢°Ù7ÓV®ºT©Àzã1…³=㡤Àåajp•&3új¶l3A‰ y›Àœ* Læ‘­ºg4ÿÛŒt¸¿Õæ‹lî4Ñ!ò75¿«7Žò*3ÚªG+‹!=Q‘E²A2Á½Š ‡{ºñ˜ `Ûvtâ¹ ‡øeGç‰Ë—±ætÔ‰âTæ»Ŧ’©kG75°¥o࿲—®ñ§=l±†Öw¢O#ä+\¦lΛ`;x »ÓyI”“B„Œ…7ÆUΓ$ÚbkÀbrft¹‚&S¡Ê+Ø¡1êJ’¢1…æš"p•™>ꘗE.(f¸¬v ¥Dh/¼…²Q€&370ЍR+Ïs/€YŠÑ°8a#~&>%Ì8¦1t¢‘Yœ9ÿeù*]bbŒ+zRgDÕ XÆ;¸-‘à9±}(¶†àP-x s€ 5Ù ž¢)¹—P7w„¡› P1‡;³œÔaãñŒË¸.‰ P"¢ÄÆr¦zÛõ=I(Û¡T  &v¨7Ø6‡p^€xy¤-€€.4ÓEЙ²5%àŸ©s7X2£˜-›Äº dzêX¿¿Ú¾š4Ïž1BüÛ9¤“pý ’ØrGü#\»1¢’"Ÿ€nÀs°) C›ûÊ`f2¯’͈W„žN‹€|–—3-Õ&Ä,9…Ì4øTx´QlÞÿcaÐ*¿+#$-"s>ûrl€` ŠðŽpZ@ |Pu ¬B¯l1$Ä•-ÂSÆS77+18ù°”„¼³I‘rÊtí;´=µ¶ÂFKÓˆ*!>fáUŸe7ÊA3×¥€§²2lI¯¨¾ʨMÖQ~ž#Wœ´_;†c=k hÝ9 ´9ÈÒÌf]-jY!jyõ&DºÃWµÂ½´6)ì¼yœh$ ¬p»|IÞŒr p°_p@B”’ÜáNÚ¹:^hå·k"*QS`è@Øq­å¹y 9CÙ6åa¡E87º|šx*ìÿ³àH-¡„ yl-lÍ-Œ«¯^‰Q>ëAkM 49oÑ fP-w$)@̰$§  t>ªÃ>öâ‰,äu5Õˆ fð÷úç«JU²O¡‚¢Æ)P×9*a…q5hb1—Ì©u„†u$ÁHl-q° U{áÕª¯ñƒtCBÈ6›@e€½GBˆ„#ƒ¦]c &‘’&aã£0'bÌ–òD­e¢Cx†dƒFDH‡°6³“$XB*ãªXEŸPÇ@ À„’¨³L‚H„pL¾Í5Îv¿Ã½I‰Q W$«Óÿ0¶„k$˜z³¤‰.îY¾£_ õUÚÒˆ?uH“° v`à"©«ŒŽú˜Íöv·‚z¦C¯± ÝÆäto“ `DWˆð$?#ºV·ÎâÕ’ˆ·"Š0T†yä( àÅcÝ­10ˆiKáã1%›ð7ºD3ãˆ1‚ðÕl&Õê"/üÛ&ñh.¦” àqfVlø¡ép;Ш€ró¢jCøcîmÑe'{jœèYð¥À©²K*ÄR¹¡7¨4 °;c<Ûëbf¡šö³. h±.W2AÀ‘“Ã6³È6¼aujD2¦½àŒ^P €š0dý©ÿSŒ °zð‘`hðæ½Ã[?H‹Ð[˜ÆþQX¯Ö\`Ó[Øú™öÆ\½éT©®quŠYåaâ`Lpàò àqb9tŶ!ÕÒ?½Ý!‰±]|è2là»_aÚa¥ÆÔ2»‘^ºo½± ]”nՔ씧A‹9I¦ªÖª ŒXp´®,i²Õÿ–n-‘°b´@PÀ T-²1îÇ>J& ñ +œê¶. °>-têd- .(d˜ðÂ?ˆÿ=”XÑâʼn-@¤ˆÑãG >T˜Q"Ã…)-’lxáQªT¥J°€×/œ¿„%°¦Í§ÏlâÔ»Wôž<8ŒaÚÔÈÓ"Q£¢"OÑÁÀAB5¦D9ÁÁ…I. ÙÖíÛÄždû¯¤Øºí.t8Q¥VüÊN` 4û©mÛ¸âÙ+ú®@ ŠDÈ cé#CVùc’>ÃA ăîÂË0„. Àˆ3Qï°#Ä" Áƒ Ì*´íÀÝ P5ëdÄŽÈŒï¸DMˆ#¹°“ñPDÝRÿ‹!–Ä<àÀD+ ‘F@Ä#Ðã !$ Y„ByÀáDÄ>0“Ï»ð‡ÏãNÌ0‚8‚ÄAˆ•Bï@ !A$$PYýA.ä0]Ñ(HK.*°1QDr `.¾ìË!(d@쀀AAÓR5{<S4`“¤Ú 8äà äÌûq“øÔÓ; 8s×: 9ä;ä&`IŒ42Ð ËW?!!Îe‘ÎB7â–ÛkÍR@ Ò² ¸á hM.{¾ ‚†)|HèðÌ1Ít^4ñ8$¾¬€à€ÿà’Û=‚Hš’;<„€Ìáî&X„à?–~„ekmØÖ0³6kM9‘-Œ˜ç—¿ˆç d[ Z (ˆ ¯]Hq x`ö$΀¶þdƒ<¡sE˜@$¸cKâ``’Eì`À+ùT½c?àèZS#? Úò¸WdR<"YØ ×å­Ë‘ºm·¾"”xÖ>ûŸ%@ûÛß" °Ý44CH ¸¤6‘ €¨!™ Ðp@¼+À‚‰ª¢Ÿpò•±Œ­ì‡ÓÖhQ½ëEP‚Ø»À$ 9 hb%z: ÿ›8€A"À†FâLhŠ„10µIIxÀ`÷@gùÄ!:d©Ax—É™(°DÐ'øD(ê¥-´0C£zC$§-Àvˆ^r€0A ÷–„¤Å,Ý!¢à²á< …Šbí¨¨Á]€Dx£__w½´Üðs@åö$mÝQ‘‹|Î5ÂHŽ\`6€i@ çDHf²-ÕŽ&…IOJ1”£TTKt´‘ë8’”«de¢®eÅ!À=²£oÈÉVæR—çIZp>‘}o"#SxÈòÈ]&SF¥^tôG‡\ Þ‘…Nħ1ÿ©Le9;À•ù͈¨e“ ÂÈ‚È5•x 0ùJ…ju"2ÉŠY|‹[|P.p’Òœá\‹(™ù‘gf'%ä´‘\À¼=m9(+N²zM²QíE¢Ì§ùBÇpF3 c±‘3jNUNäðqgBÛ Ñø ¬šñá›­æƒÑŠf2G¯!hvN‚Ës¡Ó.9ÚèZ Hµ³Øߎƒ2•f ‘ÁQ3cj=ÀeD!¸¨#u4—éq$UÅ¥B ÀF2žD}§² ,e}çð”&@ÎèÅõ©,‹C"° ‹à6|1eKòz|åMVzJVKã©2–&4¡,Ûè\5©GtÁÿQå éB&P![¶°Lé5zÔ¸Ùªeuì"/0®‰Š\tMÉ/³y¢Y–VJekÜRÖP2QU«#)í e·zË©ŸTÈA&ÚÂ6Fýl­ˆJO¢ f*±Î'£ÚÛ•ŒD qÀEÑÁBOpKì`iÕ76÷¨eU‘µä#²$¸L®.Á’¸Ön´†Š+YHbÓìPÀByë,K3Ô&xž,©ƒ5/fšÑá2Ó ÐakPœú¶òg Ñ–B\¤G´ì·F6‹€pb€ë®á½–Ø;,¢C‘Ä£ì`‡><"hXÜøY›_Ñ 0Õë8ͶžžU Gÿ éS+¸_àžz¤Z"ÃbÂu§6zbÞ˜â(Þôæ`Ã&Pˆ>Àák¶éÀ¶EìîðάÚ(( ¦Ü^X¼DÕ´ ÙK’­(3Ÿ§=,$À²äˆ+øË|Õ*Ž µ•;mõe}6Eõì…Âÿ H:DbX€é]ôg`ºsy‚û×çy¸5t†WI–àFùÃNª'BA1O©Š­;c5V`QZ­’Ö,KP BM¼ú„ô0S òØ ¦ÐŠ$;ÓH _(ÏŠœL²€Ô%r‰ …z†XsÓAŽÕP×*ÖØf,e'¥PZMd!ˆ"VZ#ÿzÔcï ©ˆ%ÎÄN 8À{>ë®—êÖ²B¯Åf)yÞ0c=³%!jåÛPUô+¸©hðÑ´CòI¯PêÜMšu¨¾U*-‡‹E! ®eµ¬€0 DÐ-Fl-A¦ ´eQ¦°|Ž9–_\¶—άÈ:‡;¼¤< ØË# ôLû¯¹Üd\ Ù'Koh¥tÏùfa¡·dˆZâÓm bV Ý¿–\ˆ«þ>”а-Mê¬Ú¹9dXøÛ~„‡EÄAÆuZ„SLÊ{èÏSŠ^«Ôç"'ÑDN‰l0ÈsVAÏg]®nî À‡ædë1ÿÆÙ`Mº$`…($TÆlàÅœO0$9(íru½De*Ég‡éƒ€oŠ®Ê”4` €²KøvCú—кô¯¹>ªZ•°A‰6sk’-}p€  >ø!<„Ù(€°ƒ¥Ñ” z@@ø!5YÈ 5zñ4 €îS€¸·z7 Â2b² 2<!€ ‘j²Qª=Z‹E” Á™ú™iØ7è{“P¨“ä‘zÀ«ˆB€ZЕ!¹ ð˜#yW),c£è0ƒ€IJ&K‹í²ÚHA‘»Œs¬Ÿñ¦„¸Œ8·½â) ¸!ÿ„$ æŒB;±˜Ÿ*X¾á»y„Lñ˜A œ=™•w2¶ Auºœ#´‘€»‘¯†ˆ€F|ºLÒÝ*.¸§RºÊ9€‘I€Ä6ªuú¿¡B¨„`€6˜ƒ Ø€0*P!‘i>#±ƒlÎ*/2‘/—˜ ` ë C+¤Dã«©´¦s²çi—ÈõÞN0ƒ—h€>¨±|šM¨H°8 œX#!CÐÝ` ÅYœE@) 8HÀ^l!‰9AÑ9k€™Y’ ê®/± ÇYØÂzm™bœ«© ¿”60(ÿ¹¨‰FØ<áÇAèŸ@ÑGH—?8„4Ø€@ Û Ø 2€”TÔÇ@¤BèƒH½mr…˜€·úÀ @òc c·Ä® j 9J !”“p&X²  Qƒ–h!¤FXAÀ@€ 0€©œqÉ XžR€µéH›l›¶Á”"„M˜4ºi 'K‹¢è.½’‹q9'‰\7—تir8TÒ©jx@!B;БÍÓI@@JÀ  •y²Ô€£,@EÂÓìÁƒO¨Gˆ :ª)j)ºo«ìªÈ*´Kª8Ø»™ ¸,Ø®ÿÕJ£œÙ„n¼ƒ°B<Ø„ø„Y›8H€`ƒ?؃4€ÜkДűq¨}[€µYÍñ;Ø„ùàø7 Ì X=Ê’@¼ó&JÀ¬(-ªª€Dà tJ_AAD˜±;\„öð—ãù„HÀƒGx„«„L˜ÇyŒE²dƒ. ò<BX7ð^Ãcˆ¯‚ǹ( °ÏrºxF‹)uSˆ‘21ÄÁt“*t!“Ø-#a†èƒÈ—EðfX„Aм= X¬PÀ„4˜ƒ3Xž£:¾A€8¾!ü‹’ê¶Ì91¤ÌàžÉ±=ì™ÿm¡5ÀË¥’p™K%WyJé‰&´|OoTšù—t8уrבּµù‘À˜µº´f9x²ÉÔÝø=y“p‘ÆÚ‹÷<42¯ªr‘ ¸§¿¸Ú[é“嫾#Qͧ¹h€P€÷š„Ë?˜L Y)ˆŽX„IPHu¤”&Oü¸·;A‘.<-+dÊ5ŒÍhjÆÃ‘¤U¥ž´¸ÁØEè³”}§=„^ôMY#í@j26h€T °x¬™ˆDèÉ¡òSÁaA';#Xº«›±QÂë&/ìTêºÂ‰ B9´¼9ÿi€ÉÉ]T#XÉ×<á0ÀƒHˆNX¨9y6©8‹ JìZÁL @ Jd· SLÞÊžáA¦ûƒ>¼B°Ú@€(!I.ËT,Ϲ´#yZZ(L à¬Hø„ éšcYŸt«†pÈž™Y•PˆfÄÑ÷L‰ºiú¿†•žžÌð@删PǶá'\G4‰Z¨…€>ØÅ¬–‹rùXà ¬ªûlD€Y‘R ‡ôªš¡ÆÕ»Ù A‹+óË !œ„ƒˆ„8h„ȉŸ¥‰Õu ›°!OÃ]A¡Ú‹ZÈ A„°ñƒÿðT¼øÛÄ\Ž‹¯”H´ ? HC=”flPm€ÎÔQ4@;@„CðΜü!¥Yššl>£ãò²(¤ìs'rQéÅÜûÌÈ‹úW:”X…°¸ÜÝ’ 4~ÔÇÖ¼›Ë°úìÃK@ƒ·,\O<0ßÊ»KòÉ™#Ò8@^ #Ý–³m$Kd¥›m´ *ºD[€-ôOE['H´pÒ€x„‚¡Ú<tµ>Bh„š˜P`Mf胙yçä488`»µñ#H?Ø`ƒ;`: F¸ƒºÆû”ßœ"?ŽÞ¬ P¯"7¥+Ã0üÿ »ÇòÔáIÀ 1ÒÙЪ‘˜9ž@`‘|yÃjÖð+¾âA¦Bx€)«.§“^G a(<Þ¢dˆf½duˆ(K (la®”®d¶IÀ: Ü6Öåv9„O@°ƒƒØ„ÿãWé",¾e,&„B4óâ^'_*=RÑ÷LÂÁ)Uû¥øa;¹ÝÕ¤—›å5q€K ý>‚6ø“©< ˆFøfÚ9Ú!€ˆqQ`ƒå ޫ⯽ڭ}Õ±}¦­®›y+G‹CµQ“6³˜8Ñ+Ü‘ÑQ³‰ÂÔœ—F8€ƒKPY—Û„ÿiJX R­ù»2Ç©¦¡ÔKú¦¾à©>¦–0ƒjá>è"@¡X:ü!œ,‚®Bø„PxIºÄ”MhÂ;x€H@›H€éuz€N¨*Þ L5:8 ¼Y™Þ5ßÌS㤴€g¼Ñƒ Yy™ÃÅl®”ÁÑŠÁ’Îy³°ùépÖ?8jÑ„ÛÄ&^›i"Ê,µBA¹9¬ø¨WÜ¥ž2i‚Ó”˜¤ÇăI ÷‰€HÌØ„gž—uÌX9†—ƒÕKùéÉ™Wñ^‰h’ øØ söh96€@¸¦›Ÿ’ó¿Á|YE^éɨ÷Ó¸ÿù9 €C°c£ÊÆ>Ï4ÑìÓÂêCÛ_F€–Ç­Ä­¢(cር¶v‰Sk„£$ÌSÈx1@ÏÑÔÃ"È»‹ÁÁí·p¢¼x&ïÚ‹!:`ƒøf/ÀK ²• ’Ìå‘Äló…n<ˆp/½Î ¾M€ZõƒCè@sìZ$A€mÔŒ7‘Q!_1B³üÀêØUCë‘Ó¼A ÞMð€xgÛ¦m[9PÐ;$ÜÈ|n¥‰`<ð227s+1¨1K¸‹8_<„ˆ‹óàœ¸ ù„0‰Hd+ÚŠÞ8 `:ÞP1¡$AþhÿØ› tëR 0ƒDˆs30G~o…H3Ú(*ÒL*ù`Í.˜¯©—™ŽZ<€ÄF¸ƒHPtSôÝ9 H‰@˜U‘ñ 7W%îp{{˜Ë—1¡„@9•X(? yÓ&2¡O€å%z„Fh„Oж•!â †3è„ö†ª Sd<½ár2Ù úZ°Û]š®ÚÁU6Šš¶ÝòDk¹yAƒá`B˜Ÿ—ðöZà¹u²è#‚ÈIr¶H1À`]I»Œ²ëä›ÎbZ€@YjrÌØ 7ÀãÀCx‰h‹ëá‹hY6‡)˱}à~ôÿÁõ%…>@„>(±¨€O`ƒCÐ<ÚM“£ín§vó¿W±×».ˆ  `€€\9ãº9€;”Fmá¾¹Sð–@ [ t²A#»I€»ò­´PŸ1€Kèƒ@`ƒ$!“Þ v;1ß©½]žëçôg5&u!fà¾È€M„;ˆ³ ù—ü³qÀhÅÛ~S_—‹ "¹K©ã’Ž€l43ÐÁïu|38üÂ'ü2M7O£÷>x…04øT~E·ƒFû®¼HØÅ>h„85òf$ÿ8`€à@  èqø†‚«nžºð³R…T † »õ4êÔÉ®þü”©@ÑTdžŽ:S”*U¦$à%÷×°ÒðÞÝFnJ¿íìü²ðË"Ò]:72¤%̘ H½jA¬ÙÐ$ àsAìÚÝU³o¿õ;Ö¦cew•­°Â@³ $H¥ •ÄâK\ÀH`À3ÄiÎ ÔãW=  @Bà@Ýa/­P˜aÍEÇDc±5UT$PS€à¤ÓN5í4@,l¦¹w#j"†µPR²µfcRfM@€£¨RJMøK/  ]waSÎLë T4ð@"QD ÏÿGÈ0˜ \‚ŒJ‰•ÐhùM™‹ñô`@‡à±$}°±¬l¨ÃLĀà =4Á5 5ñRl 9 €cy8ÙËlN"Ót€Õ2BHµ ”lÿò@` q禹9³ÖÔ¤}ÆXrR@ÁT{ ƒxÁ'ƒ¬ŒmØÁ2,rI€4`rŒA#ƒ|‚Gƒ r@¡Ð fYð@Ö,êôÀŸ„,rˆŸ¸¼lÌ,€ÛAíÊ+/‚†ð³ÔB’5zcå<ù¢“#Ôɉ¨l-!Wýùµ¡W[1‘—ˆàï'v²e‹ú¤20‰=żâ²È"UÿAÈ+orâ².6p€MTÍóU{‚@Éþø£“ò„ôÁ,‘’“k¹{•3jã(È￯:è ³ìÉOÀÁÕ){`öÖð46!ûÙÔÿ1³:u+ýŠXµ€HP+x³[–J62ŸPÄwÌk³Ø­ ‚è[@Ú€Æ{Üûà±Ì’”À@Ä $`5j±x¢#[O¶ÔPÀ!u}˜Ä" À†MØÉs„°ŸÉ ðeû{ ¼F`z7±Éìá/à5/v/€%‡F [P\³bt Ml{Ä‘zÖu¬ŸE¥5Ùì0‰ÏmŽå{Ú"Re¯0 4#øC¤ê….OlX@C0 »m·½6Á¹ó1ò1{$Ù@¸•qª[W$®zFDBÏ8"¨¯!m !m”"KYJÿyjC€6!<°!eD߫Ɔ.Òädˆÿ§bp¬Öµü€§§mÂv·Ë¿ªÖ9æ±,]œÞôšeÄÁp `•È×*Ö}Êu¯+ì  “y@I9[¬ °ÍfèÄÿÛðª,óøÊ“ù„úàMa·xx@(rˆØ^†ßtÊjM`_ Ø„Ù.x;Ú\<‘Ñ*Æ g¸Ê\鬱0;^ë+†>€ÑˆF|‚t¢ÉßÊs€( fq¤*ÐÂEHÓny‚[¸^{D Â} Ç”…^Ÿ à²k\—•m®üP ©E3±2Ë’Ý‹HXhðìx­¤í¸xçAš½pˆC ®wÁ¼8†¢:– H„q‡HÀ¡m‰úJV5OEg8„$ÞE‰õµƒ`N-zL—À,oPè Tà˜R 6¡ÞI ‡ˆ0­Ö·-P/û Ѐ¦•]óºÝXÁB@Ð:D€k¼…ÃÙ`6T,F1úÛ‰EäG)…f>€+€ÁBÀ®l@HéµAwS\Ñ‚[ŒúÐL°!vˆ€ÂÌåÇ\Q¹Æ3xR;TU'Y+~*.óïµçý.ô!Phs÷z ;ÀìЋ2K"<‰•gNqv³'?N²¨º{­”Ì€h 4Œœ«ô­[.é®YOYÎxZ\€„Z"¢Îõµ³]•9ìµÛ.w Ͻîv§ÔÝó®÷­;ntpsec-1.1.0+dfsg1/docs/pic/clocktower128.png0000644000175000017500000000325113252364117020446 0ustar rlaagerrlaager‰PNG  IHDR€”l§Ñt÷PLTEþþþðððÔÔÔ¤¤¤¦¦¦NNNJJJJJJJJJIIIJJJ666JJJIIIJJJJJJJJJJJJJJJJJJJJJKKKJJJ¥¥¥¤¤¤¥¥¥­­­íííîîîòòòÿÿÿüüüýýýüüüõõõîîîµµµ¥¥¥¥¥¥¥¥¥]]]JJJKKKJJJJJJHHHJJJ'''>>>JJJIIIJJJJJJJJJJJJJJJJJJJJJJJJ–––¥¥¥¤¤¤¦¦¦¥¥¥ÌÌÌíííþþþííí®®®£££oooJJJKKKIII777JJJIIIJJJJJJJJJJJJJJJJJJKKKJJJ„„„¥¥¥£££¥¥¥¦¦¦¥¥¥»»»ïïïùùùÆÆÆLLLPPPZZZµµµÖÖÖàààddd DDDMMM§§§´´´ýýýwwwQQQkkkÐÐЬ¬¬222vvvÔÔÔüüüýýýåååTTT@@@‚‚‚ÞÞÞtttAAAóóóÚÚÚUUUççç ¿¿¿ØØØ“““NNNfffÇÇÇdddpppãããûûûúúúfff¶¶¶úúú:::eee ÿÿÿŽŽŽ‡‡‡%%%ûûûÐÐд´´%%%………ÑÑÑâââeeeëëë³³³úúúDDDŠŠŠššš"""©©©¬¬¬---]]]HHH(((]]]¹¹¹rrrþþþE3íÁtRNS@æØfbKGDˆH pHYs  šœtIMEß :JïH+IDATxÚíœ;rÛ0†÷pªÈ{D9.2“"M&¹HJ%Çðªtᡊ9€/± ßÁ¥,EÆ’ló ‹¥Lü ìXKH{éRJ]Q–eRJ¥54t¦¤ÌtÃ4[›ÖZUw•Ê…¡Ú©¶ªQc·–uë…Õð¼° €¶^7žì¦ßúLìv+@7ì®?@‡]ŽøGP&5í gÃÓ‹–>Ûê†gÂÂZ'd5–_ïàžà¡]´: ¯Ê ÛÆëÞ92Þ9æ\ ì  ¼ ¬µo!¢Mް×ÀÚ(‚úÀ¹Ö›õ«ºýo+Y·¬¯á%€:ŠÒ~Q—L£(Ðk)‘þs´'ÇmeUYùB©^§cCqG3ÿ?º6§.ƒñ§»Á}‡d¦¼ëbË¡ØTHÿ…Çø"x _ß:öÑk°èУYá%Uð¹Â"_Å ~v-û¿õ{n'¡ý?úŽœ¾î§X åñ¬Â@ ¶b5E-EFÅ6Eh»ë"`öe%`sd# PwSsi& Ðpb ´¦ÑÝÀÐr`ú,8 8 €8 €c#¨oXÄ{À²-ˆÌðnÏ@ЈAPïœÜô{Ân @ød h&ˆoˆŽf-!;œ ÜÙ”òxþê/v~|òD9"|††,K†ÍQQæ QYº”ªMÇ0âW6'ÜoÍ8_›Rø÷%`~uüvÛäKõ |`ã[]ð+¥_…/ÖOLK‚—´€*0Wxˆú'…­VáQûMì7\‚ûGvm”»dÝí/( ¬Í›Q¶¿JsÀýâ ²ËE¹O¼7J#ßj½áȸ´à±œ@¬<$€0H€Ÿßçóùà÷œV{7~1…àvÞ@j^q,¸–£zÿ{†D×`tHÃ`Œzˆ@q/FÌ €›`;”zþ£&™?ÿ·Ó¡c"3j:J“4üPyNêb&NÇIEND®B`‚ntpsec-1.1.0+dfsg1/docs/pic/alice11.gif0000644000175000017500000004312313252364117017241 0ustar rlaagerrlaagerGIF89aö´Õ¯¯¯óòdªbÌ®tÞÞÞhH#“ÆÆÆitss¢¡ öÕ¶íöKJGb_ ² ñÊÉ¥k'$×úw’zS:7% †²éŽ3þþœLdnâ{¥²9¨ÌwdA¸B¬_0r>¥c)ÿÿÿÿÿï÷ï:†FhW<ÿÿµ<)^^_ÎÎÎÖÖÖ!œ!½!¹j{JcÆ÷ÿÿÿÿ!ù?,ö´ÿÀÅ‘û¿œa™@:ŸÐ¨tJ­Z¯NniH49€&çr[Z™X@¶8älQ €[Ëïûù\8PJ0{‡ˆ‰ˆ[JM7[ i a…K9˜K8e—9MŠ«¬­®¯°ˆ g”z0gg]‚?8½j`\]Ž Ž±ÎÏÐÑÒS78B7JaDhÇ£? ¦ZZKåh0”8^ÓíîïðW™7”z™£^ë—¥JáhªDpÓDM¼ƒºó’cÐ0B` =‚¼P³LA… CŠ”¥¢ jØà¨õh‚p”~,±  ˜#sêŒ' ™!ÿ'™ÄX3ES1<¿ i†dN!¾TíœJu%/X›d ³¬HL¬ÁÔ,(°@IŠ¢Y`àìÀ'yUK÷ÐÖ]+W"¢AKÏJë–™B#‡¢ pÐËÖÆá'aÖ®K¹ò”®jÈÀè¦!Ç­ˆ¢ÜH¬ÔšÀsX(T?ZžM›¨ëÂ`+1T>å±| ¶‚—A]Ž­1Ï[)•¶õœ “ÖæÙÜ¥sÎTóôËó˜q hH¸ÀY<ܰjI¥¤×‚u×óìä²έ¤Íé\2P=‰­¶Àz«èž{Ä$ð R»]ÒËCp10úuøÿŽFâhÎ>qh –gE”‰®ÅQ“ 8ˆ@z0PŠcéåÞ%\ÒEY,U·JVDfu™Fá°ãaH• á ŠÝ– ˆsq@¥Íb”„£ ÄŘYb¢A_¾ùÃ$lÄä .à 0¸`§?Aщ Ò-ÉÊU€fåæW  +u²€…ìæ[Cô"Ñ^x¨Î0Š=á Ô1£ yг j€Ucä9dsΙC«r $¡2’„AJþP¨¹úùDG3jjÁËT§„(˜|ç™0½œuM§é]²Î¢!ÆjXàÞ 7j !¨5–!G©y”"PJ±0²ÿ[ *¨ð T#P94ÚT)âgÄO¢pèk8êz«©¦=1ÁR$õÒY¨–6æHQ°œž¥Â#¨À 7@ éhcÙX‡(7¡›mE¬‚L* |¼¼Áïj‹ 26u1)¨Ž‚,‰ÿŽˆ@'²r“D¦¤u±h±hÜzdaí†]ÖÑP©`ÒAÈîA|CÅqóÎ܉ê€àÀPàÀÝuS#TvÒiÓ Gt•Ê2ânaʵZ“²¯f=ˆ´ãH—ùÚìVÊXÚ¢U@ŸÕi6wzK§¥v ±zi6tÇ LÒk+uàÿ²3°ÁÜxãMÜ ö†9ZÆM”¤ 9âXStÄ @Ä0°tÀnÙÌèƒ{/B˜Þrgc½—{5zü 0àszK–ƒ êŒP)ÕE©³®â1P0óÖûx·ÝáíAi‰“VæE¡$ˆãÆò’6 ¡c“XÉFážT¯'èõNƒ˜ð#õ`'{Ä‹kXKµõ8©}2ÌáY&W¥Å¨ ¯šÓð¶÷o hW6pèn7(„2p¡ÃaŽ8¶˜$¦ÀCP£yá‚Üó‰$ÅH<’ KøA¦ð*TöàÞÔÿZËáÙ ¥CÒ%íL`ÒŽJ=,&Sœ(@7¸gvëÝÝVI»‘,ÈS£«« Î[@¤;,ñ›¾á b1*–ñœ5É„!ì€ QxB8ª0z'8 [fº>V¬]1ºä±õò$£!j°„±‰²ŠD@$³ºInC”dØ¢‹-LNÈšC®v•VêNwêÛf`Ä­`%*¹Ð€F=Ò„)œçµ×¥é™îSdS±ä­æAÖº#ú1ãççâˆTõÌ'(*ˆÁ`43¹Eòvî’äÝàæž¥!YI¨²…³¥3Nÿ·€™ÊÖ ¼„jˆÁìéA}¾‘z)4¡?ûi½³ü2‡*Ø‹ZCÖA[,ÊÖéB5!è忍'æó,È)€1 €;ø@ xW  ‚Höîwšƒ9%|Ì.$aˆQÒI¶@2Àc©XB*‘"NÉ2…°Ü©ÂÔ „Õ‹AQuxCoU•~:ħÔÇ1­&- TÃh‚™Câ á2D‹g`1f…™â&R8RS])‡Æ¼Wñí¥J™*WÛ5lF ×JÛSÄ¢Æ-ýÂê`±ÀLZòØGŒFjQ6[¶…Q€»gYÿz’ ­}0Êæïl+@ ¸ÕšzX$þÑ "T4!'©×KçÄ´¶¸‡8Š`uÁì4…@ a§nèàŠp¹¤^ JÌ3‰O ÀFg“©v-¨©ÓЛT°d H‡Àô9Ñì²€Ü$»Q·ð·Õïn¾q‡P¥T°ÄZœ)sR ¸ æª1„“­‘O­gá.¡X† †*u8ÆNÅKsÙ… ` `I{ÄV²‘c€Æî‰ÁúW£&Ñ€½»$|Ø“+þ÷‹MèQ’_š­ÕFbEò%ÒñÍõ€°*n>Ô:…ãuÿ£ƒ‚óqHC¯ M Ì €°Ž5œÅWR!…RI@ Þ='n ‰* fRIn@¡ FMjšþŒdÁ‹¾ÓŒÂºÚj¯–S®Œ#ËP—Оn jƒ*Bâx¨|L³º½{Ã>"FÅàAÀd`<¨ÀK0!L <@SÝV¼ë¾M|Y»Þ%¿`O¡ÂåUà’™½F{F`‚­½Ú ¤Cm€NC˜ÑXâ3ÜáÞiô²L·[Ý0' §Làû4È*@€d@çhÏÀè¼J†ÔNã:t—õÖŠ7 ¿‹¤0’,ô|Ûv lÆeÚ侨ÿh§s§±Æ7þWѵpË«—=8^×íÑÍ0«5•‚}9»ò¨{t>Ô»€çyÏ3€˜*ê=Âât€?H£$-€ËÀôVômô]’àÁß•D²ëN<42! xýN0è’µ#ðh¤€IÕƒ  ­7fσ7b«rôuúá1GhšUÌUCHç±üÎ•ßæÐñ``f(D a§d@S“|Ð. ¹í.¿ìJëËØõÚî;ò%i~3V¢IƒôÁIg:Š•<Í­L8 bö/Œ¶”¢Š¥S#ÐSõôFa–QV|„TÇ#AÇPZÿ<àj±k‚kò†5Ü¥ ]p (²W ð3š¢Gl.S»ók¶å?Á†;æ§c—äHæD0rx"Ñ ›Ð7mq4 e@gJáÎ1Õg268pi)gBup€Ò#K20H#=ÀÇ€Â$0 xA—&ЀÈ×f(x<Çõ–0ƒ]±ÞpZÓv³=,8l0#[vSMuIß3Á<|ˆ;ôkHó ~ލmî8±S27#ް>óQdž!° s1º4,¨K2P€Š5=ÒU]ó$KyV=^Æ…iö…7†c“`q;,1Yƒ!„;u&{ñ,3E F¸W=¢=¼—O1@mH—C`xÆL°sg2Às³I³HXcÊ÷sÿ²voe¹j¿  ’E§h±Y‚ŒÙÔ}°3~)RH3"‰7p7`=.pS€2†¯–>'!Sæé*ÚG`Œ˜ bÂÅ)O³ Æq4Áv°„Ÿk=¶DBþéxI :wiÆs´¨sÅWË×sîAgøsÊ·oB—CE¸&Ùw†ÿÞY¡2t UÒ b ©J‘XuÒ2d7Óé;)ˆ;Ù¤gzl$I@Ð7SçCú‚I£s *f ¦e³h¤Ð‰8Ó ‚‘"æ6m@H¸9Ub*pB1 -:ÅeVndBSšbVú¯X Ldzol¦P«ùfoî!–è<p^Ú/ÈÒQ£ß1‹G§¶e—’ˆÔY@C¤goI=Ð5¼ñ@ †ºsb¨uç& èeéÔ5ÂlÀ ¬£Ä U{%+Kh s  qŸ]B#äOmgBPmTê8wµV: }o;·o¯V«ˆz°]xÿ³³!6 r6N’"È"¢P'™Y[tk¢·“DÖŠ;#E7ó•V;R¨?p}6Ú «y‡@’¹³ÃC"”à)ðN&?-´(qSðJ¹7€™¶\×…níbmíö¯†çª\X j¨ *v›³ø‹4–a±×ç«9´(51 ¢R™!S3±?ш/XM„_Òh”§Sw@Ý·Q¿Ð+×—o™«Ë'o`s³¹oÑ‚N¨¯x/[””pcÇq°4¢P³P +ºæiˆ‡‚ªyd¥nÆ5’ë†0G»ö®Ï'‹ú…ø–‹„«>Z:dÀ `"S7`ÿ6P³“Ð7`@@Æt½s¿óZy“¼Åf¢Ã[#0’ËÐ_(³Þzø¥1Ô tò „*åK¾ a¥-¨ú^»d?»‘zš¶FZ˜g×ÅOšRmÂÒªÞbü«¿1W»dZˆ!x‡Q¶¯F½ñ6o³Y5ÛGBá„K¸!Æ(°îÁ40¼U[ì±2˜—x“¢×V»“D’UG@x8€W¸Y¸&ð•u %• s Ür&Kx&€` aWm­¥mrEÐm«xi¾7€˜¦)SËšò¯u— œ]bÉO¬-±¦-jH»*x@‹Âwwcr€6Æÿ!€a¼)'.€Qyi’eU·wéD„7‡ˆDõ}|IŽ'HÀf{¬ë1¹Â§]£B{!\P¾!¹wÆqç®2±'%ÄÉ÷”BÛ#G·‡n¼f#X{¥gr^[sof›dºb¶J<°³Ø ùj4W«/¿*sѼÆ{Æ(À(tz;Ê›§ÎjMn mÕ¬Ç&7Ø”DŒ‚ÇF0¦ÐÈkžŒPK+ëñ‡á¸âÀ͇±TGÎÇzx†=ØS.Qp…j—i2ðX;j]¹ª¦kº¹ÏØs°6o`ÐhMÌkMDxi[¼#¨æ—VÄ}10t9„"ý𙍮,¡béX|… 1¡Â"ä»ÇQH=vg¯¡a’]¯¤Æ.KWÇØ30æ]T¥ö¯JÌÙKÌ…@goaU >o¦|l&«ðsÜY°)¦_Ú’×vd­ËéQ§¼Û;lÿw“Q|êDxŠ;$«VIãEˆ©†{‘*††¶©°eƒqã¸áÍCHH]…ŠºœÞú™{þDÄšâA =¡&ÿÊïÍÙ¥ÜGkÈfæ}×ß5êsH®÷f#±*äé¦)6°c¼6Àc×Z¶u‡‚Ãkl ®Ì®Qìb—48D‘LŃ˜É—‹ñF|(Š)|Ü‚QË)±EG£“δäAo„ešÒrCüJ˜4EõAÖcºuߨ˅úÆÇÅçx*¦‘kæßZ†wÕ6 åR®Ë ŽSžË NÆ\n²"yD_.7@VþC[½3D03­š2'³`ÿ¿ˆ;§›P³úÖ)?7kqEFrŒäkj2r¶äçì¬=øKÚ3€'”¯ÒóxKmµœýÙÙåwX|® UùVø²Úëîñwp¶Ä¤êìÞî n]ÂV"¥ê ~·¿F3íÒåÕ}rmu¶þš¦¦ñVoòF¦<`ß â¡Ó)#l".0‚4©¸ÎÒŽBÌÅreB2Q=Õi'˜g1°Ôu§,ÀÙU ›‰º†cx†[é•e©æÊÇsš’åî^ó½Ü% ‹ˆll¢o¬‡{¦Á$¥¼l¥Q÷ ~3&{‡ò*¬Âú=†ÜÂ)4yp7b#µŠšÖiA¥OÑ.=¶OHó¹ÿš‚ŸL½ðýã9–}—|öo6Z|g‚ Ú¶h6@ó6_ó(`D¾}7öuÑ ˆ@¦743¶µPv*¨Ãö2F/+L¼°É†©Íæ±6†ódnpŒ#b¨‚j’ÅÉO÷„e²äF@maŸš“ÕÉJ ,@Ê=°Ú¾µS͵,ßsÜ®ëq†ú–Ï÷é{?üa ïd5Çz뵕VÏù—’—Q¸£c)[p"[7(hut$!tWk1ô²?÷sï1596B#\¬qÞ"Y'7€x{^ïìϾŸZÿ`8N=¡ÆÙ`òúø šÂX4 ƒ o@ ðœÎÁ@c9Ð4ÿ„eÅÉðx&!*T6ŸÑé2jEt¼A Ï›â˜¿ñ—è½ù@†T6àøT/ì@(.T>>,@, „ ,,2L2´ 4-LœÀÀ2 r n,f„\C‰b:xNN:NFFb|OxGxw‡cTŒƒ‡•‹T®/±E±¹-ªÀ‘ –,xH13  h œ4hÊ 45ÈÔòÕRŠ>TÿᩃçÎBAÔ¡Cs>lУ‚‡TXHÏB _´”ZWE” “ 4@ÐÀ7n¨L0dAÇdĈñK€¯e*„ýÑl×PD”é0âh W¸Øÿ•ÍpW3{¢¥BM4@râB€Ô•\0YÀ·F_>lªU ˆGàAºŒ(øy´ÐN ‡Åq°!°ƒŽx2¢I†®äY€€€2‚ ÁU`A n©…y¦VC|ýÕ¡§T¨è9Wì›À‚ 뷧צFÕ„+©®>u %$˜@V¢ÁF[ë!ØØ°±ÂB1Ñï Î1ø˜'‡îŸ±½yF‘ðL9À…‡†Cfa )›¥&K«e%2bå3"‚ †˜Ý‚Ê©€†ÚÅÂcù…'"lË„*©,Hª¬ò&â†pŒ‹ ÿR@.¹(Æz‚ >‹¿sª»Î-îÚHÁ*ôO<ó£(¯¼bð'  ±àB,økÞ^î2° &W ì†Y(ɸÁ2´‡†"D „Êm·¡ˆ)f§anëp˜tB†g¢!bDo Ñ.±Š¸µ˜bä*PBF-´R.°(`-×xk}O#ÉŒ 8È«ãD±ã¯!î8ä‚‹âJ%Â2"0ùì†^± –2 NX0:#|Š©<’6OØ–:ªƒ¡¶­ÓaÅ0+¢ÒÀ\ªª¹æ 0á¸w»Àqœ®¸+ .Ę¢€ÿ>õQ;"º'/TÅ»`úÀ`>€²!À.„ƒ÷2Á¡žÈŒè膌b!6@ÏÚ$3ÀÌ|e‰¥!†Km'kYë €¥® ¦ƒØ¶ÍDfñLê½"¨ÒDQ"È]—E ” ©^z™3zwø£ž}ûu‹ l`ƒŸÁ‘ƒà>!ŒBäØc ƒìÈdˆô„&º”'ƒ·[0 s²jI¼ <Ö%Îö.Ä—YÛé&ez!<7b9J§"òd O`\6´\©Ð%"sµd×èz¹b‚+zÇ¢!…&œø$¿íØ2ãS2V(C»€¯ÊhëVÉóÇ®E؈ŠxØ!š6‚à ÿðÛ»ä¿mI @þ.`–[ÎÖ–Œ  ¦§Û^«íå—ód\rØd Ð o⫈ *¤’Ý!6:Ò“&€†$FÇqÔ·È–©0v ‹îp;:4Œ.¿ÃØbà,½ç"1Ø€Mn°‘+0!ÑãLG&ã¨@dÉ:²€š¤‚dF 0¤!€¢(c/›Fž¦¡›îBL™ U4 ¤°€‹AÔü @#Ð-±]éD»–Ã…Ž”ˆ_l(CìøUE+° ÄIàÖ60‚ÍeT­(†2”bó’+bQ!«úLŠTrš7¾`\ üØ8”ñâO-Cÿ†l\ lK{LSD$ôê*Æa¢½’ƒ4lªhÈi£S Åðk;ܱA TÐE¹Ca Œ•xªÔ@>¸çëg~)plY°(€T8·½©pŽæbž…D®f'@¹ÁUâTî¹qÜ¢zf¢÷!9Žý¾ùM-‚+!Ìø1ªØag”mP%T€P²ab4RÇ#ñT„DøÃ!±ÎÀC.PB“WTMdÂ>c‹—œFW@[Ñ÷ÐçCÃY'¬AŸãÀ%Ã¥ÀÐ>ØIlp3ó(§ŽÞ5–Œ® X…<†àNîÀå*jZ€ ÿ`pQ”1`¯zŒT‘ÁÅF"Ì ~©‰Ë´"3 Þ&2˨LdGJŒð¸]Á8k|‘“}ë†á£!3±y "Žè, Ç'ð‡4ˉB3À#¤ ;é ‡< à‚d"a»S (2žß=þ¼ÐÖ(Ø"¸‚AŸ9SY‚’Ra¦ ·Ä o”r”žìÆLiÙ¶˜b·â6»¨Ïèš9»! YˆW OÔè]&qá¾lÊI!Š5( Ã<¶=ö¤ƒœ±¥û›J€ÌÔÂ{3 ¬ÝXä Ôfó¥¸pÿ”îFBqí¶dˆÛÜns·ãÕ¼Æbq¾t @'€URÙo P¯ؤ^äÀ?¦½ã3âCÝ­ýa‚ ÂH+f"ˆÆ„3 PA˜T0™º ^¯ ?ÎN¦=ž`sÒ£<£p¸¡0zb¾b\h‘­$Ϫp FqÃ}À)ð$5ЄúU!Rï.Qü´ëˆ± zÓ TÎh¸½5‚‹ÿ)âZ7µ<‚U4àÞ!¤iXY2eH‹„wL6F@€‰©Æ7)»™F^k'n!r[•Ûlœ±ÍÑ`Êt5ì•‹0 .uDÓŸ–?§¿Ä ú딚Ë^öÿ¶âÆ8N–£"{0U%5æ1ˆy`¢ .â0G@Än`€)nLN¡˜Û˜R²ôRô%5^ÝàÀ‘á8ë¥{‚%L#¨Ì^ÓøÎ¾Æ¸¢ËM(d Ç1‡ˆ»œü²û[Z~Þ<‹Z¯×ªµ®D܈°~Q< p‘oA)üc>~ÏÁ|…ƒ[¤£›…7TÐÞ™€0é]@Íéå`±§ýž1ªÕ‹gŒueÇX$‡›·ÛÝ6Ñ1¨Q·ÐiGŽÁœ`…§}DŸ†€ b]ë–ÔE™}þaݼl€Võ¤‹\©<¡~¨x‰)ˆ%Yü-ÿÆ¿´… '8y®×.@Òë œ€AËÙ“p­”«ô"Á·a8c~ä‚Ê­ j#Xª+DçÓ´ü¶åTá8/*ÂL“5±tÏÁ pÐ„Ó Ÿ1ú77WŽú™NÈL# ‘Æ …(ñÜsfææ ª©z¥_ofRcۀᎠ>ãB?~ O¢IŒ£X`¬*è/Ú½íøC•$Þó;€p!åŽ.@À8ô˜ÂV¼B¿'nÖÆ0X… sÔ¿³üY x#2…?à¯F¶bߪ€< ^lÑ®gnL€n†Ï\lÌ’EÚ` ¨â@r`ÑÀ¡)~ÿŒúŒÄÊê­G‡À¶,n°D„®¥s@íü€ޢyÆBÌ¢`¯ðm†€,ºÄàï2°·­.lÐ~éïªÁPÊ)~+ PÁ++at/Xª LøfnP"MBhà*ÆX®TnÐTÂÛÀhÆqV‹¿rÆBxƒûÀ/ >¥êÍ] 9²‚  è œf >Á»ÀÂÁö(&ˆ¥õOM4ì2< hnéᎆ¯ìx Kf–€G½$îô†Öä3,lô*áMŒ ï*ðo©ô«ÉdH'Zë|v†]°á\$IòŽRx…ÓZ$éLÀä`>"u`ädînÿ,Ó \áø,Ðr Æ~ª… €u„¦˜èã=èjQŽøÈäXj!LpÀR%˜§Û `c®¢'ZPš"^C)¬EûD*» %u‘¾@t–À\’cÔ Tâ ¨€é…“2‘ÓLÀ~êh‚2ÒL#-lVAp<ðo²±R~ é`ªœx èÞ,àº&Á¶J@C˜ð¦ãh gÚ‘ÚZÁºMp̦Àbmë¤\\F Òl"º!ª’·x`àðcF Àš?„€KÎ ‘h%\ €jà qîÎ$Y˜Ë%Îï8ð㥞ÿPG2Ã]–Æ?Ú0î.EÆLâŽLÀk½(CJ4"Å„0Ê8ŒaËJ7§5´7œ’“ºäx|ƒ]FÞô2>¡+Qͯ^òœæ P¡Þ⌅„,côÔ+&pÌXeu Ð.cªÔN²Ä ˆ x ʸª%È¡vÒ¼¢®½ªÎ€’M@œ’EãÂíBœXCJjñ®L È.ÊVÊ#¾j,P¥ðã8¦€¯¶@i²R @Àà;3Àï€ÍFo½žê¤½Ò+õç·Â)Šâ%7Ïó="ãFãÓãZ!õ D…Ú‘ôÒ ‹Ò>.ƒ„„J l‚ŒÁÿ ‘¦³Þ!+XBÏ#zå´ 1¿Š1JÉÔ|+ʬÀŒNÕ” Úˆ9X3šÑmÇî“ê«p~êï,U|´êŒÔË¢=Áb,–æ“‹Aí("4:ÕŸ‘-Q¯X»-L‹ÀņN@æÇ¯ª`߈À²2[jV‰Cè¤È~ÂàÌIÌø5g ®N'ïnP‹uî6¦ààïþ&¸¾¬>áFËɱ¢2hŶÊ2~нœÓ9»Õ€ôȉJ¬P§pVðqØ%9c2†ÓÈ5°tQð•aõG*jÿï 4Ag€"À´¬âjo¬ Ž8eLëM"» nÔ<³‘4 ÐQjUˆ%†7L˜,÷cAµï=´óJ%]»© £I3&ÊŒ ÄeD±R`päòˆb}æP¤ Ë!?h¯V`mL Ë$z>Ç`Ó%ž¢ X” *1? Åt‘`@òÆj=æé„UPOƒu°sk±¾^FBT°ñ¾óã„ €E¨Zß¶ –&=Ýa+U’E¡²#„à‰0¡½Ž¶ÖàÄQx²½„Õ%:â;§€S5é èò°b')ã&*c«7?s@3` ê0[X‹d 6¾—3¹¤ŒÔy—ÿ«*ãä-à47Rˆ« ®€1é *ùaî*Û0ë~x±DS!,>³Œ+3p*£ Ÿ¯ó³kEHsk§=·¶¯šŠaç®QÎaØTD@˜C7‹ƒ.4 ðmÚ7bPìX³k×òÆ”MÞˆqì—žb¯˜¨KžB9Î÷*ü&§‡«÷_õ“ó”’ÒIxöǤúŒ+h8£i¸p$>¥ÇüîGîïaszpâ:ÝùU@úØžEh2 ;-Z¦¯ú6+º$㔡X|L]è'úe¼Ï·À…Ñ•žà_`‰ü$ÀBJñªíê ªª†Ð"^â7*¡#p}|)ų“H98g¤GPAcXròGßÿqÀ–ç>önîùõh`ïN̶ÏÉ'ÀRõïùè–âG `ßšÀÀ¢ …f× š@¢SÁ±:&b¢äl_ê€Y{îõåè¹ @_?v:´„û´ç½çcg¥²ñkWʹܵ’xà3?êÿûýÛ¿‘šà1ÐZ†ì·z1ù›mŸÄ¤»*àô#^ Â« Ò'ÏhZ=µZz(\B˜ så3… âWH‘ƒ¡aDºÙ@ÊŽÒê‚¥p8hÒÿ±X‡ÄTêXa¾.¨ð©™À™XÄý¥©´ýæáI´pH”¼´\b^¾pÈPZZ^rdf¾¼t^ÊÈðE¶”¤Y!$¸<ÀPÅÆèº,@P!ð4'7¤¬i WT šðP ¦hðöàx,Ü à ÄjäP@hhe1hXŒ¥e€%*`î…Šð|ÐÏEkÉXCƒÐF]¤Ä¡&Ž£0q¹ñ#É/$è(U*¢ Ž^Q1 o\qð|ÅÊW¬ÆŒÁùYìÀ€b4Öd#  S¥¸±I Õ04èAgàÆ *n¡GKJ7ÿÂxÁ×…A.l5lAÂ4pÖÜ¡£BÀˆ<#øÎѳWП  Èpq±KIH•ª¤ÉÊ”;r¨T™Ë2ôXÈ…s4é[¾2üDdA¨P xðp*»iÈx¨,Ç‚t0 ¨I€Á¸ºé~È•î† Àès›-Ûõâ ŠðÕð,è b!::Œ8/`}‡‹qˆ*f,ÿŽŒ‰ Xøtyÿ ŽAÉÀ&-àÁ¤!˜ .¨ÃCEÅZ˜P€SÔL#Í4Ó(–2„d0À8À€J7ÈÔ l,`Ž@µX°€ üÀ]8Zz0`ÿxi C‡|·_'œ “ uÀä'ÔÑÈ*0¤|jL⇴Ê( ˆŠJ‘Ý!(¡ä!ƒg1H ÀX ’Vâ81Ø"4HØ!›6nÈT4$U 2¸ ŒÀ8äpƒŒk,p~âùZ†]ø£‹€'idyåz’G 0‚$¹¥®ÎyH@O¹)fH–•Ù }Ò§R %ˆ´'–Dä_;tÖ‰Xxƒ•²VA&XÓk†Ke85±`€åX+–΀ºÜPv·2AøMPV¤¨BŒ°µ*Š”øJ«ºÔÿÃÿ1G Xã«ÂÆéŸf™p°—%€¼‰qvòß %ä!ÊgsCíhF°“ . >³"1¬-sh4Ò\Ès¹²QƒÀVöª‘)‹ðv£b]‰ÀÅ…½wqðw›ªF ‹¬z㬖¡å2”2%@̰$krŒ("AëÙ Ä™"²ª™-©R€1„ùRn.ë¢.Ã倃JÈóSˆÈàLa†àa¡åbÈTåÈ àÜJ€½BÃL@[t¡Bi¤@~j «¾áµ*ÔMæ „±Qv@X6¦ ,Ð\K¤”R‚1,+÷­Š ÜX%\CH|ÿÿ¸2Í“ƒ÷õ.‘†P˰†5C¹†Jõ\.ótã9;JZï;4±˜ÿ€>kœJúRƒ ";@@ @©x•Ò@¨¤†ïƒàoXYõdSða=|²ä€ÿ¨âx-`Þ÷'Pë ¡kÃr ?  }"ŸŸÎ×€øD®r…bß –bsd*Fº‘ W¾3ŽÐG>]8Dðq«/üÁ G RŠ^DÂN„L™ á­:hŠ€¤‚Ó+ ™2³,ý|ÐÍÙ$ GJTÂ#eº^ò’’­3À,†€ÇÀÉ!#úÔoMˆB< Á3VEÿDž]Òg48ÙãxE^UÁ x## )|nñŸ¿¶E.bd ØA t2ZŽd%·ÛX ­TÁ ^ŽÉ Șe’e,$'™H’¼5…„zX€ eØ„Þày[rd„|5¡ å¦}@› %5@ @`€¼Q¤Òð›äP>`ƒbØ"$Ej1ß9dRÁ6ašä&!›©˜VhÂŽa«DØÔ´ 1zä˜jÒD?ò‰0ë¡ÄªÌ&6¡ jóv ŒdUGZ 6¬ i@DB½T)p)2p`€9gEl@À¤‰ÈDX$I˜þ¡9MXŒÁ&š÷Бÿ¯¢b8ÿp”?¸É™•) tP k$ì8ìíeV-TlIîÇ †UñœZ½U[EªIäAÉ€9’ŒñÀÚ¢nºÖ€s m ð'@œ€ŽVß1ÉJ¼ÄYÆè³DÄ›&žgFÔ°Ÿ³2ª’¨¢:k0j$é³&íâ™kÆÑÀ Üç>é°|ÚfÄ&ñÁ¥ž„½êî+d€Ú¢m2Älê@@ËÆFƒ"¸Î)ôá…bŠìïæN‚aͬl„(žîµ@l-Ä$íó6kÚ)jfò0óB/ˆ «øUXeJh6¾[Ž"’•ŒµfÂ^Ð'Àᑬ¥ÉYêÊ,ÿ…œ,Ë^U°l è 4Çy"ôm乂Ž/Í]s›”ÉÁÞ‰b ìÅ?ÌÃïÓ¦ æ`*iCíC, H¹ÁÑx>†'¨'Ä}€™-õ“õuZGEruZ³,eILPsÀ¼zc-Ö@ú¦î„(’Ú¢+3¤ˆÛ&@@ dÀóÙëˆdðA ‡ ÊDC1$€‡ðÀ>–pD|PÙ<¬>n³.p(m;1Ón–­r”š|!˱AÅ€Ì(SÛ͸!‹ƒMTØ@ØqiªËA„m%°Šù²pª’kºš« 40—ë6¤A Ï®ÀŽïÎgÄ@F3ø úz^ÿU…c‚©?l7g2 'ÈIñá!&? ߈XØ@^ÞDL‚6!˜ñAHÉWÛ]e’™ò¦’AýÞߊë+”+º3ÊB€@«ê ³ì0@|Øn\AAìíÎ:™ZÏÞ0€œ¹«Ÿü\Í(œ-ò>-hYc²\@[»õ8ìE ­N+ªÅîð›‚>Ïz,P´–DÿP§\àþr ¸TëÃk´ìb´ù.Å@2´œ|”¨A(’˜@$…@µëNc-éó†uȉv&+íÓ¢öí¡õ  u8@[ l@”´õX]Ÿ2uªH8ÆgÅ ^}©„¬jþoäÈ,l¬mf»Ëôéªê”¡*6CÙA ÉYvhÀf›œî]&àóï`6+8c² $í'jj'pa2¯¢ÆÀ[w|À|€c6¡ƒeÆå>ê—j¥Þ¸Y"]¹.€s§ëéî±@ ~s¹t…ȰYÀDäÈig¦”!dö·@e9lG±Ã6ìÃŽ6K¨j££6 |Àÿ[»µ| €8€صÆdê…Mìî|§ž–¨RžðÀÀø ´ëÞJû–«ÚÖñUi”Ùw@ˆ©Þ>,À"sË=ù–<îy—4k«vx3®i§öxc²J'é| ‹À‹gùbä8¦VN.嚦ç(wFƒÌ~KêÒpž̲¬–ðIù®o~–­u[v«À‹êÐ=y\ÂÊY³ô¢µI?ì 昹˜Ÿ5‹_À‹Û5^¬b³'d$Ðp›¢¤* Ö›ÀU5ÚFuûV ¼ù”hÍŠ*¯ê'®A Q"ˆ{⣳¦¶m¥>I UB‡4Í„ÚU€W½Žðr5ì¯^®zÙ²åkÖ¢]mú(&ÿ $$ýC@Œ.À¨‘Sñš™Jp õæ^l›ðà‰*uÔ*뜠‰ÎeŸ™êÍÑt3³æ‡Ïx}!áªj¿H¨Pû`ËGÞ²Ø[·­E¯HpÄÃÂÞ½ÉJZÀ$a+פKXœ]”._pÐ\·7™²u[OÜ|Œ$ÍÁSµS ±á™,‰õ§„%b ’¡€0¼å".Œ“ȬŠÄ-àfXðS@x,X¥.TLì°Ò.Ä3è› ; C¯*”ʉ5šÔ+m3Ÿ8”¡ $@ò|jƒ5O<£ÆSüC%±2bÐ "V„h8`†h¥Ûÿ „ˆˆßÇ&iäÊH¿4c«0 °¦7è@Î×” °¦#ðd:L&X0h¼L%Áð$¦Y%É!f‘kÜzC+-· tè,à,sC³©+ÃK0¿ðÂt‚ªîa“ :ûT½ù<ä€Î‚B5 üTº(!@ ¢NSY ;Ì×5JÈ“XŒp%I+òÅ edNÙ´ZS‘‰R:ø°ËN.v­QP5ˆ}ì;¨#,’<¾8÷Ü­¾‚iÅ0 ‘ƒ7¿óS6„Ú ­è„‰‹”ÒG‹¨“NT`X0a­…˜à¹Ìyªú"+jc]#špþ9@ЀUz¿$§““µ³÷”ýl ”v(a°"oîô;–‹—·Ë³¶f°`:bÛÅ9i¥—fºi§™.¡Vp0sC SaÏ"»eÎé$@I¦ÝAl­Î}ÚiìÔ Ø´“ÞáÈvà‚;ntpsec-1.1.0+dfsg1/docs/pic/oncore_remoteant.jpg0000644000175000017500000001133413252364117021377 0ustar rlaagerrlaagerÿØÿàJFIFddÿþAdobe ImageReadyÿìDucky<ÿîAdobedÀÿÛ„       ÿÀ¼²ÿÄ   !1AQ‘aq¡±Á"2ÑBRb’¢#3C$ár‚²ÂÒcDTðSƒ“³Ã%&61!AQ2ÿÚ ?•(1·Ú‡` »¿‚6p¹íâú#ÒVko –㙺R7pÇ,×'®žGkƒBé¿AŽþ‹”ÿ>ËÞ/íµþ–š•»0Ñ3ÏY¾;OÁÚ3V™}ÛC­.bœ_³{]ä+–rÖW* kY`1û ™Ì—dU¶4Ë)w¢ÝÞ­t·„µ©d¹™››‰˜Üs,ÛÑ=ãÁ}:ý˜ 7´®úø~ë7v¯–Î_Ôäs/‘§ðbyk6üØøBí¯ž³ðÏj´†ÓÍÕ=怶Êò6Ù|— }1‹?÷gµApÆÚÃñ¨Z¬Ø¬ÎøÏj}\ªÇe`ýüMcj™¢«°˜×¶­¸ÛÔZ~;ÕÂÙÚrðøf xÜöÕ®¯t_Z]ë y—½71œ‰Gk¨ÿÅÓJ²ÖrË]½Žåì$¶;½¼ É|·Öor¾ªÔݳÙÞÚÞ@'µ•³DíÎiéê+–rÚá@@@@@@@A©óQ̓ÁKs®Å*Èàv‚ÿ+3òrJæÍrOšH$F›;{<‹–ü²qã´×¾»°¼ˆ*‘wØ‚ê ‹1¡E]FJ®Ð ¸ˆP,Õ\° (ª€"*4QAéϳpâ4¡Ø¢²šßaoTàö°| Ÿ¿1­ZàØ€‚…õÛ--Ÿ;ýVJãξve˜û}-“ŸsåüÙ„ÇÂöPpñ5ÎØíÔî­i2_ŽYiÎnjÍΓTÞT”sÀкMc6¤×»î°“Rh(Ýy{%î^Êi"ÉI<žÖRç8º7ƒ„9”£W=£Nœ²9—=e ÓjV£‰iVã-Á5%¥Ý®+ÛåücŽÜ¶Xf±QT‹¸Â*ê ”‹˜‚ÍUÔapÀ* ܲµ]›”UV¢*(¡õOx Èè'ý®N>§ÆîÖ‘æ\ýÿ hÛ×±±ÌKÇÚé›§³aà*Uˆ=ª&tÙÈú’î*ý+¦œ&ÌN<ÄËyÄwÚx(ºF*M{žÀæéíG9p"[Ø@gÆo;kßâ\¶j$"ʹ¼ üïQ¨áجе·†..ÀiÝ^½6ÄŽ;FZ-SOÁúÁtîÏUÓ5x úÁ^é…Ìzʃî>²½×ªášÐÀúãàNçUÄzØðÿ\| ØÂášÜðãé3 +³\òÃéþÄÊas·®ûpïþÄøb²êÛsq ¡¼BªuU3®(hÛpïãý‰Ö 5ÔŸå[ôÿbuW¯ç©H?¦`þ#ð'Q´ò§+=ýÖX¾Æ4BCÅH©ãÙµp÷ü7£¢¯;b ?š5þUº§É*UˆI¨Øñ?´¯˜îýjõM˜ˆO¢à (<ël¥/ºFöÓMf33<þ[%rÈ­£è"Ù¤=ÿIü>•ßVUÆýá˜N¥F£ƒcÍYH, ½Zqvå’£©tŒ«26õ ÷•,½C±QY‘·¨vQZ8›°Ðv ®Ø›òGb ˆãoPìUã`ê H¹c@ ¢³ZƒÝ T”,ý.QÿÞÆÞÆ“ç^OúyŽÞn†¼Î‚ N¨±Žó s ‚ °ù«‹]Ú2Òîöî4Òµ¡³N†¥§RèÂqrÙ¶üŸÓMœvï”÷ä™îó®w– AÉùý.êQ¨Ž˜Ù¢dQq:”Ôk¹z|ïÇ-§Öb'µÂ­ ÷—XÂê5E³7*‹†t"Åv ¨•pѱi·b©U˜(ª*„:ÐRžîÒ }¤Ìg}À)•çòŽîw1ÍŽ{ŠÄâkXEzÞË]´ŸÚàØ€‚Ç6i‹¸?0ùb s2Cþ©tz=;*UÐÙ¦Á^µÑ„òäÔ>Ç•zU”§þ:ûÙÅç\ï-75¹­ô´z‡ûG6)V#¦¤ä¦¡ÆJùmžÊ“V×reZ]Ö;;ymͳ½äÓâØ·=+7WˆóG±Î|dt²½`û+f¸÷÷wÉRúS¢×ù¿3/£èÆêìcyX¾øjy®ì¬õŽaÁ°Ç1éuZKGಉí˜kÜ arÑu?%4ôÖ²Í8ÐH¢.Q£?ÿLÈ]Z´úŽ„ü+~|¦ÌlN¹y£\îÕÕ†BÞÂþOÄrÔ‰k'm§¯ŸJÊþÕ©ªe’‡HÜ8 ÌþÕ©¢vWþN“¦Gâ)ýge ô‹ÚÇ\h:Ó¡Ù!4o+´ô8\}ÌÐÏ-¼RHHøÎ`%x¶Ÿ]²Ýìð¸Û6†ÁZPC+àˆ‚ F«q+¢>Aò)V ÞºŽY¯.8\Y;žêt6…µí+Z5ÛlmõÀl[É4ó‘11¤¹ïvÆ´²º2ýÑ6sXèüœñº)í¬-¢–' 9®dMi`®uYµ¶E¼VS¶Ÿ"c¯àÿÙ¯›ÔIúÀ-yò»1xÛa°½9VÍcE6.‘–jÕ£bÐË@À¶Í\¶0ˆù,-,v΄TóƒbÏ“oì`_2òô/ZemYua4/õ\Ò R!§1ñQãõ ì,õ]]ŸÄ ºr»3ü˜Æ[d5ÆÞå‚H˜÷ÊFÊÞÞ.º8.ÛOŒKõ-ׄome4 ‡¼ÁŒ7X]´ün/̵çÊíÃc Ó«9jÁ@·—µbÔFZÞ”[erÝȃÁ#„ ¹ÛÉ;€EH&YÀÂ([GUù·—¡]@@@@@Aâ¹x ‡Üão©¹ïŸ*ºr·†ÁÈ8ÁרÊV — dy×}ÿ‹œå)±™Ç4ôŠ àœÉäæO%•}ý•Iq®Í†ª4çòò¿[Ú?Ðc7lªÔÞ§XÇd±:Ïšá…±Ž¶­O]“¤k—šïQÚÜ68Ÿ c]µ•5$2鯭f뮞‹˜yœUµý )ÚHxŒR¡Å»;¾ûef‘°YhNfÝŸ´¹|`ž†´SıýÛþפuN\ròç áw‘Ü]èÉ'¦æ÷‰Ü³v·š¸Ž¢;FM“[„mò¹wòŸö©É›!åžLOq'çJò¹ïÊÎÀŽ6îh•z@@@@@@@Aâoºx ˆ䣵MÙÚ]9[Ãn÷{‹ÿxŒüœ|ÄŽª½vôá‰ÊJ. 9Ç;ÜF–’EJ±l›úÀîžårôypçº_r‘áܸÁ)H ~ŒŽeË~Zœ6õ•S¸û‡þéJ!÷6Ϩ½ÿoŽÔÓ–¶oî껉·ˆñîþô¬eßÓ‡-yHÕÁ±<çL.“KLG@*Uˆ©bH»oH-àJïåÃ¥‡#î„ü¹Ç´›y'„÷8eqþ²Ç§+¯ ùaD®Í-¤?4 †¼Ò˪.é´q†Ÿ «ýU|ù][ݮԋìÄôÙ½¼Uî¹Îqò.¾¼1«¼®-4ÞjCít­Ðù¥J±à«oÚÒ(ÜÚ÷k_:íäÎÉ+îëzÙ4¶JʾµéSf¤Ö”õŸS^erh@@@@@@@Ae˜˜CŒç¡‡È¥X…úÊãóZ’áÀÕÆc³¬ ù×O(›¤»‹£Ó¹Kç ›¶Æ?àÆýů^YÕ×&„-kh.týÔt­Xï"•bdb6ÙyÙ·ìå­:(MŽ‹~wêlí>ï9Q§Ècɤwö¢V·ÀïìÈWOIó,ê+‹B o˜í³ÓwRO@©V!Ü®79™$;CKáy¯ßÊ|cz–˜Æ^ãxš[%ß´ºx?Þ¼ðý@=/Õ׆ð°¢ ð í%ˆŠ‡4„gãuLì#…²8ЧbkqZ«®^ê&áµ#.â[´Á·@Â}Y'c]â^›3¸Kæ=¯c^ÂÇZàj;ˆ+ÌÛÚEÏ­BÛ\?äÚú:O[oGJGÒ8{ŒÆRÚΓ>BfÄÞ°vŸájõO‘Êý©¥ag •½œ"ÛFÈ£5 Eæmp€€€€€€€€v à¾ðEÒ0d¡eiëiÅ1Sç5ÃcýaÔñý¥ßÏf6‰3É=jܾa/ÿ’Å46:²ÚîÃ÷=CàëYô×)­tÅÍ¡;™› ‘Æ¢¨"8u³:†KxÝÅ î ë^zæ›Vñîñ¤ ï'Ô3³ìmm¬ëÓ+€öŽºÝžÓÖþÖ;ÒâЀ€€€€€€€ƒª0åñ3ÚÈÐî&«* êì ÆžÏM -ˆ»a§waW[…±“Òyûì^^×)a ŠòÙÕÔµÍ#Òc©½Ž Ó1´r¿*Ri-_ŒÔØæÝZgpÀÍ›Èö‘8õõƒñ]¸®kejV}eD÷›:âÓ…–!'ê%,`ÚâOPI2¼#¦›Áå5& ŽuÝì•®ñ{ÜãóZœÔ×Öwñ]ÙÞ¾Òî!FJÃÐMxHÚ×0õn^‰fÑÏw]3ÍÛY¾£¶’87ƒ#¥jòwqR¦#ßÙÝ\öò³…›:$\BÙ­ädмU’1ÁÍpëTÅ¥T TšƒœëÞq`0Ëgc#rZPA ¬?ÞDj+G»òŽ%½ÂPbG*5«d§=ð¯zb2¸þJjË—·Û;َࢽêb7Ý5È{;w6\ƒÌ®¼'u{Ë+—PÄéÜV.&Çm[Na2Ê*‚ŽàøÔð §úZüJøÛìþ-ÿÙntpsec-1.1.0+dfsg1/docs/pic/alice23.gif0000644000175000017500000001711113252364117017242 0ustar rlaagerrlaagerGIF89ax´Õ±±±xvtþþþøþþ——ïïGFFX[Ç m¨Œ\™™™›œ÷fU4õbbËËË000bI<œ®ŒŒ‰ÎÎÊ»¦dad04”sJ6 §äHàvyÒw99©C”/Ö˰÷÷s±ò1J9¡[[\¬’ÿÿïïÄÄya@1½½½Æµ¥å㔽”kÎkŸŸn­Ó$ssÖÎÎÿÿÿÿ!ù?,x´ÿÀŸpH,ȤòÈ98Ÿ“Fãy˜–جvËíjåP¹œ„´1©yßðx<ନ3gÚ|îŸÇr‚ƒ„?N7fŠNx~$…”•ZMN 7$ˆ S –¤¥DUˆT ¬|±W¦·…5T4ª$ $ g } ’.¸Ë‚O.Og7ÒyÀÀgNÌÝ]5˜t«fc4Òzj¾g’¶ÞóJMm.¬SìÀøL!“¡Â˜ô"OŸÃ})pp%‰‰x‘Ià7… ‡Ô˜Àá¡I(¤`Z%eÌDÄI²F 'sJx ,ÿ_NFÑù @Μ(dI|¬‰Ð¡ +=šÓZ/‘@•¸|TOº¡•„†d]½r6¬Ã†èÈ1C#ei½1xé‚C[·úðrB‚ÆÜIy»z¦/̧̘‹Ê ‰™T•A˜'0€e€A“{Ô,ÆËŒk1œœub+YL9ÄêX/ëRå^ ØéDÂW“ÐÈLH𩣤§¿+íåìP•‹¿žhXD€Mò¢ ~À‚‡ÓÁçd:aÓ¡ÅW >îüdªL…ùºÑñ@ ù…|Æ€=Íð´ÓW"àVHVC n1ÐÉrZU”o ÊAÿÞLX t“°r‚ 0Ø!Ó=Sv9ÅxÀŠÂ ã @÷bÓ±ñ’3HU'I“‰ŽPü‡‰ˆ… € ‚eã„\E…“GããM¸ô4ê#Å6#Æk ‚* A€ ÄÿlD;š°Î§…‘¬n-ŽÃÎ:¦±øÌ}ÜHcÂÊ "ZýYÙɲ‡Äµ¹$JüYÅ4$aŒ>¸Ë#oˆ ëÆñ5!Ćt« !36 éù9B¸ ð蔉pmÚ£Ý ô‡¯@ìC#Ò€ @xÁ xà(ÂsÀÀo:ÔÚÆXôì)6LGËÒVÉ]µã èÖD2%ŒMÞË¡éˆ ·›Ò¨OhDØÌ8æ³Òñlc?øLÀ< “0H‘ZØ»I(2¼DŽ’U*ŒÁ%_ £ üx “ÐÎèõèNá™ K·¼.úF–5„I’9„<=ÁÓåµ:¡¨Y’E;ÿp\Õj ¿Rba$døH° `È[çLùñŸô™tBhÞõ¶™Ëÿ1í ’Ò,Ÿ€8ƒ‚fªÀÞ(:1Úð¦XA±P’‡@é ÓZ¨v½ÿqÓÍ+’8°€i=Ah³ùÆ8ÂÕ£@@"Â!t€®,4ä!°XÂRš¶”J­þã°ã5ÁEOHÚ„DYÃ"!„Ë4t¦S‹,àê$5Têõ ¯¦ZKm’EDNì!›œâe:šýë^I‚@¸ opi†Ðµ™d¶'¡fÿXæÚjé*mÊÚ”CÀ`šÓ@ªRäÝ“éÙ<Ræ ³ ™d«Zÿ2£‡Dn®MaªY!³eº`Okš¨FÂÖ¹2Þe@ 0°§þÆáUrqf)(ø ƒ>0ÁÿÀÔwåÄöéʳ§Àj z9b£7¿êm«XÈð_œ +4áyiª{]¶ªï>DOŸá 4Æ[<Òèí¿¬£!h?[š¼ÅaÇç­ñ­` =¦±ñ¹$ÜFdj€¿´AR Bô¦dnÑ<’²‡-tßðÆ¡±ëV¦˜àÍp~s|à`8}r}Q¬ÅûF¯:~+˜|9‰³4„`FÚ„æ­*<‰I,µž¸)r{÷«pvbˆª ëq kŠ0°u©=ÎêXm2ŸñÂ!øºa½µE"û„Oè9ªZ¤‘˜ 7‘§y thÈ®èZ¥K™«Ú'³*Ѐµ0k¤„`k¨ÿ×ѱÔJ{•ŸÙÚ]…?bôáµ` P™Èe÷§ 2ú§Æ*?y³„ FôbëT è?Nù¦)Iàý¹*6·O‹ÂùŒ}× FG¸¦8rW*³z;œg7„៱G;×2Ÿu*˜u7öü  Ÿ!+W¥ :áruu›u晘7™sÙ Kt­˜–㙵¬(‡ Uð)c×r—c 6¥µaal ‚’XÛš©ë3·ß8uzû“.ê}¦Û}e”!'º€š¬@¨Áʹ¤—ýìG”œjh-±t½VàY1rn:)2wF8¾™«Ç Š¥Þ'|ÿùq£‹vh—¾„ІÐZÅVMå)w™„п~åj˜5Ih“»½ˆ`‰‘!¾¿K®Þ÷“Ëž ÷““¹}<|A„‰È›Fg’dúXiƒ¿¸¤V»2`J÷3Mê HÂsy¢HX—ts[,  H³2+r wà šµ!—ˆåI„ÊŠàF¥0Ò‹¿ óC€TDsŽð+¡!ÅEáNðpu ãÉ²É ¨>hž$'ÁXÚŠ7zº 7IÕ’‚µäZŒ¼6óÆ\$ éÄæ¶7XR1d`©‰9)€4«‘7L¡¢;ºX‹†%9Áø®5~Oɬµ2oùưÿ¯òŸ­ùÄôåT—ˇ@ ) ªª8h¹›ûÅ£k®%‰†È¬wü€»²ªDl—‹³nIÉrkQ곯".³#T‡Þúp¡L€•I¥ rËtŒÃ_,üÌÔœT ¸×c”€ËÓT]Ër%ú ä¼ÄÉ”˜ ë€äZñê"þ.÷ö@²BŽ‹ ÀŒTM«pu+Êß—µÇ{¬¥Ñ¥j€÷|¡ùÌ\Œ0  ܬS ”Œ“«kSSKœ>²Y· 0F§Î4Ìž2ýÒ‚ÕKyˆF<‹'u2,Ã4ù8?gáÏ÷c*5‰^Öñÿ3‡F}p‰i§.JvÃ:t€ ÓR¸?¼4¬KcËÅ0P”° ÀÍ$+b 隈à´`Ös«HM®8+ˆ…¼›%g€{;×ëάèw×üXT`B€™UЀýjD2¢vÅÂ3Õ»Špeùw×jtÁêÒtr—%ù‘lÕOÐÆny  0FSCŠ®¹û†0h­ÚÓbªE®e‡yq9ˆ]ìƒÜçÚDhÛ2°µ|äÂÏ!雾m”,}à=Âämj¶,æ’ÜÌvÛéܪÀ!Ù×è˜ÈÆzë¨T,Þ/œ[ýùh S Üÿ¸«‹TÐ{A,^w_txi=-øýSTÀÿIníŠÙ®rÄ·*E™žG¹OÂ2 ² >¸:Þõ9/ Ñ-¢Á!Ø™Ð0gÒ*’÷m?ÅáÝYI À<À–L¡F<+1°"p¾×àxúw7))û'F` <®álIžß@þû´–ú­–å<°ä*.’,0"ÉÏ"©g±D„A4~@#àðÆÊÀWnWÐP,Œé¸bžáª%@ä?ÞäxÎ䊞*^£Ð ]€ÈÐ-q£ è¦î{Ê`Qèˆ^»Ç˜—Š¢©ÝãÙ„"Ž™t>éÿwÞÛ˜žél,P² K„ »q‚Q#肾€å×§ ¤Xä`¹˜–x ÒÎí ¤@/£% ù›:-äN®žj9çBã3 Ã^@ÛÑìÐ `ꦾ `\Ý̦›üw‰Kv¢èìÜ.ÁÍyLÎækéÙç®éç ì Ž á¤ÁN75°&4@ï@ç³>–\˜vw_{ÁǪ=Ðð (à Þ0_îæîn)ä,°îâ“/R,¤^@ÁÞ-¦DóÎì'0&?aŽ.)`i§G3Ð0'0U ÇàD3ñ9Nˆ²ÿñrÄ;Ó ¼Ã€îìw‚ïa õQÑ>-3gæZúë0 è …| > Pñ}`±DûAøÝ±„ É.è#‡¾Ç—5„És¯,œ¬~}QO_· Œ4@}ï_#€ aÛ!`°PøÚ®; › 1ú/ù×p±µ òÎsuX ¦ñ±þÒÊßX?ï¦>" À¢çzþú…´oý®/$pÊ®ñÀ±X@éÓ•ÿå<ªu!ñ·d{˜]ƒ''ðTOõôù¨_öZ„ÃðÀ!‘Ç›¦AJÐH›‘ƒZ¥v:ÿ›ÜÛõ~¹Imâ:bq=äg³Gål^( ÅP«1\~&h:¬6ª­Ú2n2š*76°NNÀ±Á\08ÀÕXW_A8øÐ@l÷ú|I'°h‹-Û³0¢QÓ 8$$̶Р€3ó¸çF3Pa˜õ`>ïñªçË…`üÄaß+þ9Ò4$@óü@€› ?µ‹[©‰ifµºun ‡xnññÿØî4€7% üƒ>«¶ €¡+['²¨'cÜq(NÕš¾C,.(Ãë@…¶øñ‡äÉz¼¸šÒéM#Y‡G1{æKLµð‰•1ìÜFvОcD‹ÇOh粩Xð@eˆ;&¸e6¬j˜î?@Ü"#.¼"„ ª„ûkš¶Já»N+N¼ŸêzŽpÖb qÊxC‹ÿð ð Ù¾ï³éFš`>ÓÑ-ž¼2ýUôòf+掛%ÿòm?²èÙ†È7 ÉÁ5îðnåŒÂà´šŠ0倕èÙÍú¤ÔpˆoÒðKÅÛÜ:ã>[¬I'múE£ª´SÓ Ö C•,Tl;1tœ³3Ää¶3Éšs"ß@ò¶X´"D%ÅîÃÄ0̸G•ûcÛÃ&$÷jŠƒš¤…—](%žq,b1`Yo @‚Níú7{¨rÛØ å!Õ t<tA"5 ¹N ¦Bªµž¤X‰®•„@Èe7?ˆõô£¯~ˆO/‡8MGãháè:4ÿZLWC1ƒÊd×<å›W©ºÉÛµü<@·yÎøÜø¾âÞvÂÏ'ã\TE¢¾@Ðw 9´|uN5(â:‰Æ,c;¶ÖõŒ_"T±é:$>7:v1.–Ä]\Ö¯E&¢‚ T *zÔà%œ3ü£ŽP"»Ê3¡4Äo–^sFƒÓžÝ%1NÕÀéJú[ivÀ-5_ÐK”.æyç¬FR®Å§9°‰Œ¾sžåk°3þy›ªxòëÒ¦¹=P.Åy²Z‹>Î42…CÅ~¹•)"8 ƒœk“ð°»ø£¨ø¤g† 4ä@ËJ¡ wRrgSÜâyÄx_£÷4jXÓÿO/ü¦“H©}§R´Sc‚£/¢Ã/=výežº]˪'ë£t•Åùzœ%¾-yåS²xÊ€QI7ZÌ)ð Ø‹Hz-^¼Ð+Íú0ˆ‹}B¾x‰PV¼ˆ•a}x¢›<|ч†Ä+m𘖄”†•­èX‹IÉ÷°Q@~] ¢)x;-Fw¸S 0~¯ ´;2 S¨.-òÅšèV/‰©(…úá¡OÈ77øBz¸ƒE "°c £)ÄÂ2 BªGn&ˆ5¨ˆÏñÂvu5\GKP¼ÅÔèD\èP‰{Ê œ#ò‹º+‰¤€ôE%ù1ÿã'j ¯!(p…Ðæ¸!*ƒã‚ºNaÅzEP«aÏpB Ô`XΡÓBR‚-…ì´{Š×Tv…GA’^ F"|ñ&kb¹!šÒ½Òz¨"%eÉlüáiã¹r8Ô½ÆS&á¢3À…|ãhqˆÃÑ`i¥SŠf‚ðÊrÆÅ5ÀKJ“X“G†\s! ±g2x‹Ýô,ƒ¯R`œÚ@¡ ÀK¡g»T„<°åSûl×Ìü¢ÉóIì. ]ßNNÇP‡v \~QÍQ±ãÕ1…B‘ Â"ø‡“fC}©Q§.£–è¼%!RÒe²Ò©$^®ÏKœ*Žfÿ¼¢˜#~Až‹œ#±rèŸ`¤ÂW ùC;ESskï4<áB¥ˆp×RP =PBY? ‹)î(Ä,\´–Ò|th‹„bYÛ@~i³%nF¡˜8 Å@Z±I‡,H³b¦"»ë )º¾»Mq!YŒÚJ!fÆfôŬFªÚ@?8¾Øø–(hr^5ÍhOT+,Z` S9Á€n’";p<à"$SG!¼;a7É`£x^jgÀš‹Ìá)(ñˆalHZ%î›x@àâïøNPQy<á ;ntpsec-1.1.0+dfsg1/docs/pic/oz2.gif0000644000175000017500000002004113252364117016526 0ustar rlaagerrlaagerGIF89a®´÷ÿÿÿÿÿÌÿÿ™ÿÿfÿÿ3ÿÿÿÌÿÿÌÌÿÌ™ÿÌfÿÌ3ÿÌÿ™ÿÿ™Ìÿ™™ÿ™fÿ™3ÿ™ÿfÿÿfÌÿf™ÿffÿf3ÿfÿ3ÿÿ3Ìÿ3™ÿ3fÿ33ÿ3ÿÿÿÌÿ™ÿfÿ3ÿÌÿÿÌÿÌÌÿ™ÌÿfÌÿ3ÌÿÌÌÿÌÌÌÌÌ™ÌÌfÌÌ3ÌÌÌ™ÿÌ™ÌÌ™™Ì™fÌ™3Ì™ÌfÿÌfÌÌf™ÌffÌf3ÌfÌ3ÿÌ3ÌÌ3™Ì3fÌ33Ì3ÌÿÌÌÌ™ÌfÌ3Ì™ÿÿ™ÿÌ™ÿ™™ÿf™ÿ3™ÿ™Ìÿ™Ì̙̙™Ìf™Ì3™Ì™™ÿ™™Ì™™™™™f™™3™™™fÿ™fÌ™f™™ff™f3™f™3ÿ™3Ì™3™™3f™33™3™ÿ™Ì™™™f™3™fÿÿfÿÌfÿ™fÿffÿ3fÿfÌÿfÌÌfÌ™fÌffÌ3fÌf™ÿf™Ìf™™f™ff™3f™ffÿffÌff™fffff3fff3ÿf3Ìf3™f3ff33f3fÿfÌf™fff3f3ÿÿ3ÿÌ3ÿ™3ÿf3ÿ33ÿ3Ìÿ3ÌÌ3Ì™3Ìf3Ì33Ì3™ÿ3™Ì3™™3™f3™33™3fÿ3fÌ3f™3ff3f33f33ÿ33Ì33™33f333333ÿ3Ì3™3f333ÿÿÿÌÿ™ÿfÿ3ÿÌÿÌÌÌ™ÌfÌ3Ì™ÿ™Ì™™™f™3™fÿfÌf™fff3f3ÿ3Ì3™3f333ÿÌ™f3ÿÿÿ!ùØ,®´ÿ± H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹)Êȱ£Ç nÄÆJ «’!/¢Lɲ%É‘ØFŽ\¹¦Ë…×ڼɓãʘ@ þ Úó ÊœE"-x æRlO}ê$ŠrçF˜;“N…ªp(É“Y+F5y¡W‹a7Ò:s LVcS*ûr Ü‚CYµ• ólL›5=V%Yp®ÛÃ;ã~,IW¨c¼‡b…LÙàÞÃ~f4md‚a3Þ%l•±À¨jíʼn´)䟡MnmVójÚ§?ãŽx2èQÂŽË:Y²øWÕ W^#~ÙîÎÀ2 ºNVïmÚײOO»TmoŒ–Öÿ´¹¼³ê²u*޼¡á˜qQ®Õ‰Û¯^›sµÿÌ>™~s‹ÊYgCÙÅ$ uЧàÄÖw²}v–WòéÅ_]ùè\åÒd4™ÆÙ€§!ešBÞ¨œvŽeW[.å¡eB­¥Ý^áÁõ[x2Ö!Y õÅ¢tPÁæ¢A# H£‚ñ¢~Îi7ÝA¡YÇct`µG%I®m$%Q ¢ØdaQ–G(YÁc^ ÍõÞVš÷âj\FÇå9î¶¡™pÚI”šp.ã‡Z˜ÓHV ´Ü{ŽÆå_"ædŸ”&œ†M6¥gIGÒ‰bB3 )š¢2ÂyâmüÉ”š¤ 5©–ýÍÿ†Ù«îégµ‡ qaºÕ§)ÙYoy‘wa{o~ÅÞ§%ê\°ƒ±§'™’™•™– ½øÞ‘~.4Y_¿êuŸAëá 'hFhg²Á§fì:èéEÝE8U´qº¨°·ÎëçS3RG麻%µó^»ÐOŒ) [d;¥Ûqýî÷X¬¹R•^µlzyp®ÿAÔ[lø!‰œ—ϘàfD=ÇTN {„¥¢î*×ѲW¤©ËL û`µdÎgîG?äW¯}ÇJ¢Ýn•™ŸÓNôð¼ÑÕôj°E-Qhck³]ŬTɈEz¢wümÒ|§–™Ö‹ èrUšJdß`^¥è)ÜöBÿŠ7¶·ä渫:V’³J’QtBçGjÈ&%˜¸vÌ–Œ€P‹qCÁ’•8£` 6;IjÏ”|嘠ÚÙQ9§‰{ŒÇdÓA•¹ˆ|Ïôƒà‹MÉ C%`°c(ª犖LE¹v—¦u{Œ—%:A„)išÓÇ!Šx~ 9»ô¦Fq1SŠØxéä‹á·‰\×KƒÜHq«&%jçW`€ÿ5¼©Š$a£é§¦E™‹‰Ö ˆ ’Y˜šz¦°™s©¸Wx 7z×$>§ï–jYP1•sâÒ D:L©™Éo‡š«®ztÚy \y²9ª¨§y-×yí6 ÚalŠvj…PQ˜uµ—„‰²4ñ¨â¢ ij€éªtªUE0Å8€Ja£‹ÇzçGpÙa§Ëz ³ l*%º‰Ýww—Š鎈jl_pðÚ ‰plšªgª‡*8|(¬îw‚ Ÿ‰Ð ³‰ àn»¤§Ó§x7Özw€ª®o·®ÙÁ/ð_Õõ³à‰ÉôQúç—•38‘'ÿØ}ýºv%6 %v¨V°Rªé±ïq¬â'ˆM©R’Að_÷50@›L¯i¯â"Â:Åh²íZÝõ´­»³ÚáxzçŠ~êxýWŒòÈ#¬:Nû´AøLu}{¸y&¢)x§êŠ ˜L%ö´kУ7žSùˆ£Çû‚ÉuqšLƒb{ÌúL0»x„9°x½y¤öÚ·É4·Þõ†«ŠºYúdqvÙrÜJ¹÷e¹§{{Ó'‘<™wñ¦®(¡Mٶ϶÷U¸†S4ȹ¨°`bvYš` ·¬šƒë]—{º+¤2YðNë¥àµíÿºMë]G° ®=*}K“‚§ ™ ‘™Mqy\d¡Ròöu_e€¾e˱3JŠY' ç]…›Aû˜!RRÿê]‰„S›LqY)¢pr¿ÃõôÛ® i_ÇÖÀÙq\zG®àç„ÁxÂ5 Œá&ÏT§p¿k0‡¹1 nJ£°É4Ó; ó¸x*%³Ãðk𥶫Ϥµ\ ¹÷ë]d½Éç¯0¦ñö²±*.™g—°éž ¡œ»T¨r[ÆfĈS¤w S(”˜‡÷5 –‘LÆ–,jóš¾Eè§YÚÛ; @P"¬ÚntÚ²½z#ÀT\@ªvÿwÑÄPûú¢…Jq³ vË–L1À®(†%±³À¬2pSÙÑ­øµí´%¹”ªí¨ÛdÉL:Ê©†¾ØÀC0¥¤šªÈK0 ÕuGPg®bʦFÌ—¬0«¼Õ—Åít²Ê¦|ÊÒ¡ÞÁ’Ƀ¶á”jÐÌ­ª¦©WqË#ÁxPÚƒÑøTÎÃLÊúkɹ[5¡ÒG˜Ý‰­j \ÏŠv‰UXˆ\l“ìÁ:XÅ:Ѹ¬É"û†,x ê,¦ÊV Éu¼I“Ùì¶ÍK;˧¡ÊYóšîè-e,ÐbìT•Ƚ¶à)3Ʀ¿¬ÍA`ÃX¤ÿx¾S¨)Ú¤E¬(šª·Ç á—fÆñæŠÒÒ8’È%WPj.MÔ¨T…$$ê§Ñb:hŠy%’:Ï q|1l¸É¦k1ÁT6‘(ÈŠ‚ËdhW 2MÓ Í q|-Ç}@=­¥[úxH¯Q’üy:SHMJÿX»'«|$ñP)ýÓZƒQõ\CðPªL^•ØûìJýÏÎdеTLuÙh€Ô Å*Ú±KýQ„­p•›I—s"cÚ)EUT…RBe,(±—…è¾qW\uÜ\5…R0ñvÆa8ÅURÅøa¤K,ºmܺÿ}ÐÁÚÃ-¾ÝÛ:•ÙÔ]‰Î’ºÝ)QÞè-T¦Í_°Â’X‡Û‹£ÛhRäuÜà­ ìyÃQÞLEÚS5Õ’2mˆÎ,AànEU9ÅUôÝe1Á’œ«9•ÙÏÕUnuÙØÊKÎÄ­åá×pDÑàPo•2Í2› Þàݲ0 2N…laá#Α•Xˆ%Y0w[lÑf·4±KÕð 7dERÂ0öʇ9ÞÑ  æY?þ2Y|}ê¦GµPÒðãKñˆ4»yON%ÙÑX& ÎÀI¹P O¥}ñ~Mž×ðMPåi5•p‘»°‰>À¶NÊ@ >PÕð%\}¬””.ÿ§\Ô æP4Pü1‚IHäIe– ®T ÐU× Ç$Ï`­Ö0WÓ gô Ä`YƒN{!hv).'ê×°GgEϰ\¨ qË8é>…䳞éšNpš'é±\Pµ Ê0ë¹@ G5èÖà„œËDUTwH³þ Å`ë"˘<ê»ÎèOäë´%Ž (ˆhQdÓp ¤ž ¿ jž ³Õå÷Ú}aTGö ä$ëV´ Œ^ ëttH•K­ïNQ¼ŽìNîÒ îtxÑ[#eÖ°GÄpCÐðçÞåHuÍòN?æîJÓpìa¤ï³~íæ˜îa6Vðð´¥ ©‹ãðO Éÿ^ ÄpHÏ€O÷d ˜°/_Z`¤ Òî)ÿD§@ѹÞ¬0ðÒ€òCŸ ™ÎNáÇËd?? Ô~FüžæÊP["+Ž1ðV¯îbåíÕ®ï¦`á.¡ôø¤ï›ÔôƒÖyæç¹°õÍà )ï Ó y¿N7ï>OIÔà Mÿ Îð ên ÐÀ {ð ¦àÚ¡ôÕ`FÅðöpHe, xô(«PZ² Òpõ³¾õÅ ÐOÒg__ìODZBoð¿@Z¡ä ÓpóÏH!AR6–î7´ùÖîôÒ0V¬Õða"³å²ž µ.óCôìŽHËEZ|¶0PEíÒÿDö©/ ŸEh> ÎЄôOòp¿ ÔF¾^cÌïÊO[‡ä Íæ‰¯ûœ´õ–Ø$Xpà*i ­åb˜KÙ4j¿NœXlš³b —%LhÊàG!"ä( ZÃ_×2Rd¹lÚÉ\ÐJN[%òc i #6L&-K–Ϩ­,Fc5›Yé\ÉÚ4b@)þʹò×3ŽÎô$åj'ÇiË>›Ö¥Ä†.aÆ,Y³ëÀkÒ†6ô•s­Ô\X«¡µ˜é[lKËR|j¯Ee(±J›¶ð[’ŒÅ6´6˜b±_hs¹Œ6ñ)ã@Î¥;-,Þ±ÒªU4Jí1½U³Œz3ÿã¦Ê6PNƉ=×NkvãΆº¥¹}l™´g¶3-œ¡5ÕYYÓÌ0šÏîxkþe”wo‘WržžxÜp®Ì„Ù3´HS´©©9I‡ïLÚB”¨q 0SÂÛl>é2NšfCO$ó ûŽ4Ûžš,9küx,Ã|iÆ'þ~ñï®\й(™.”n#g4sé¼A*…´DÍB–X­3Šük1ħ í­@ ´¨™÷ð’é™î¢1ÆxdMo„.< 4Ð?jøƒï)âà’«"YBÌK†D”ƺ“R$VZ2­J¼&|Ï£·èÌÅG¥*æC7{+I‹­Ð$Šð|ÿÓ+¢ÒK™Â„ï-){œÆ4©’¦Ò†täªO—ØäO¦5espQ‚𻬵$²ˆÆ4ÇÉ==œ¦O©4ÕsECÒ³¼èÐdH¹*AA• =Œ4ŠSÛ*K²×â «K3Í 9Ù<éWj‚öªR+:v . ,Q4±bÒL®ZíéVk%sÕ&Us‰¦©%…M“Zò<ö½Ù‚»ØÒ¤/©2ê)*a7²1M›*E+`o3]¬JQ¸šÿô•©à_.\†Þ>ÕdÍbpê›&û¥˜¦ªÁö[£ cyÜ÷­˜?ì¦.³•F©©øz÷d/‰¡¦=¡5¨³”ˆ®f]}áËÿ‰N¡5F)š_°ëXXRÄ ­¬AÊh™”4Ægæ/Ë÷xm¾ðXIXß®­‰{Ü ã[l5°ýj¯Ä¤’ ¾ƒrÛ!ep%F *—Ñ6$fsÁWÛn¦3ã7ï…¯®+{©½¢see•µ2›\¤@ ,Q£Œƒ&šk ¦ôaczu8Ya¥èh°IÖOÔ²tùe§ß”%䌊Ùê÷Š®Á\ÌÀ`.FÖ®Z‡¹ìOrþŠk¿Fáúd+QÿA‹±+°´Þú•Úí=Nòı¹»Äé9dù1Š¡ V¨`ê[V°ŒbÃXÈ`&›Ìd<Ö+Ÿÿà Ä lUEBèÁ°Â%h ¬ÄP*à—qCæ°8Äá5PÈ>V˜¢@ÛÝ j8À@´0†/ŒaHA6QŠRÄÞc+8P}03…ú²¸@+¦°‰XŒ¢Ã82Nq‰¨âɤD+°B‰xâYáD,ÆpY¬ãþÊh…J‘XDaøtÇ&1‘/tc`ðÅ4°ã‚$ 9Æ>Ìž"cøÇõ1c¬$™F:Nñ}(lcWøJ5&±•vÔb 9F#6e¼¤8K„50’jÌ"0Çe7:Þqq”£8Fß"ŽX¼†a·IÿÆÈ˜hôÊWPM:‚3‰Ï´¢Ø@gR˜Ù\”9Ï™ÊHFr›P Ä5&ÊzE˜Â¼†:?éÆ$:sqÔc$Ùù¦Aú‹ý& ú ðiËwáúÐO6¢Óœó\”ùèBs²žnÄh*éÅ,®ÒŠšˆ§{ رu#T_ñ]#Ê0µ,Ä—IlG¹ÔIs#Ì]íÕ†;Ê9ĸ‘*l¸ˆïˆÒÅw9· Vœ{€;ntpsec-1.1.0+dfsg1/docs/pic/sheepb.jpg0000644000175000017500000004750713252364117017315 0ustar rlaagerrlaagerÿØÿàJFIFHHÿí¢Photoshop 3.08BIMí ResolutionHH8BIM FX Global Lighting Angle8BIMFX Global Altitude8BIMó Print Flags 8BIM Copyright Flag8BIM'Japanese Print Flags 8BIMõColor Halftone SettingsH/fflff/ff¡™š2Z5-8BIMøColor Transfer Settingspÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè8BIMGuides@@8BIM URL overrides8BIMSlicesm´sheepb´8BIMICC Untagged Flag8BIMLayer ID Generator Base8BIM New Windows ThumbnailXps€åÿØÿàJFIFHHÿîAdobed€ÿÛ„            ÿÀpX"ÿÝÿÄ?   3!1AQa"q2‘¡±B#$RÁb34r‚ÑC%’Sðáñcs5¢²ƒ&D“TdE£t6ÒUâeò³„ÃÓuãóF'”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö7GWgw‡—§·Ç×ç÷5!1AQaq"2‘¡±B#ÁRÑð3$bár‚’CScs4ñ%¢²ƒ&5ÂÒD“T£dEU6teâò³„ÃÓuãóF”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö'7GWgw‡—§·ÇÿÚ ?ã¾Ù—m¥ïÊȵ ¹Û_e¼¹änvÖëô”mºÖ¼±Î©_µÁò.‚Ö–¸ýoê®}¸}dßS+sM2Êò7zf§¶æ=Ìý#7G¶í¶(õ\\ܬ×䛫ʺÇW[ݸ´ŸN¦0K²ïÑTÚ½k®þôIN{r-`¸¹¤ˆÊö»þ¹¹«®µáâë!¬IÛ¦víqÜç;èìBºœš×Y²dFÛãàØc¿ÓÓùÍ»q]q¿PƾæÚÃ[&7¼}ãoé\÷~÷èÿàÒS¥ö,À.Øúe›+{vý&qúK*wèÿFûWÐV±ë»`‘:ÛI“ßó7¤U1­ª×2Æï-{KÜßNÀÀ]¾Úæ›ëd~´×ûîgüZЦڪ´bœp}ƒÓöî´Û»p}Î?¥ÜæWúJÿš®¿ð))WcäÐãYöØ5–ÌK¿7ÜwmnßÜõ?ëj“î°´n Æ® П£ïüÅ­¶·>ø1 -s˜Ý#Ô±Îýßó?Â+ǥ͵¯¢¿mâÁ¡…Æ.m·3kwTë=6·ôv~Žïð›S„ÏVÇ8{šâ @&?÷5±üâµÚZàà\$w‘>ÖÿÔ.„²Šëc+.h¬m¦Ú‹C}G8~‘ï© *´<µ•ÖØs›êV×6ïs»wóþýžÿÑ×þ%<íuÙ[Þ,s¦È- ™çémÍå%¡’Ð^lØ+3éÍk½ÌÛǵŸ˜’JÿÐãñk³%¹•Š÷Öó·ÕnæÇéZ}¯ÝûŽF¿í¹W3Öö¸n†ÔÖÕ!Ǽձ®Û»e[¿›£ô5~hbc `Á§P'ñþoó½l~Çu…yì·$nÇ­ðl?BŸçÜïÜö{ÒSÅ_~Ãm¡Îloq—ØÑ®?Gù,Vqúu”†XÖ¶A—æ˜÷´mtnú?Û]•ý%Íʯ¦½ûe¬õ‹¹¯°µ»žç9¿˜ÿnïóHvt›Fxéà5ù̩׿·êŸkœELÙíßïþsôi)çö佯hc+õAZÊÚÛ÷î{Ó{}f7e¶„W¨ûcaâºßh``{«nâ×p=O£c¶û7¿ßéÿ„ZÕt›*Êks™ê}™¯´0~s€;[·÷›b²zifsz^æ;:ÚÍ£"°NãešTßJ=ߤúi)¤0.kç ·=F;ÀâÂïUÍ-ú.›*gøOúÒ5¸YVc9¯¨ ÎÖÖÆc¹²æ·{+¦Ý­mn÷Ûú?øÅ<Ü*ðFrmmnÏ3ŽdúZ ÓnS‡Ù™ìý'Òö'¿ ¼Vâúײ:ƒÅxeÞÛNÖ–ÛTÓ±û¿:Ï¡ü禒šžsCÝm5È+ÜÝÍ­s·†Ñž§þš¹¹Î®û(¨»a&ÜWtþvÑìmxìcýMžŠÐ·§z-̾ëZÆtæ‹2áæÒÀæîoèñ½gû«ñ£Uéf=µQ“ê³fXýY¾£7»óÿ™Ýê1ÎgúOÒ$§:Úíºûè/{œà@Ðë·wõ}©-šñ[Ï˺I)ÿÑ«ƒH–°;ÚñÓÒNã»ù2­}wêí?Z:EumÃÅÀÈÇa¼³EƆÙúVíuuU[='oýNÝL4NÖö#·}û“u¾—…Ô=œÚí²ÆÖÆt®«ªõ¨Þ¤Ë-ÅËönoó©)Ö¬üÿñ‹ƒÔkm8˜9Œ¡¡Ñ¶ÊÝgª×Ù-ÇÚÚÙþ‡Ø©õî§Xúû‡Ö©exø¹µb¹Žú¤\ç1öo÷l½¯÷ïöQX»ê÷LÏÍÁ®ÖY•eßg¥ÖµÅÓMFÜ~Ë+{­×ÿ¥ýä_¬êö'Ö/²õ’ì`ú=cÔo;Ûhm?dfÊ1w?ÔûEjØÿôðžšJjýfëoê__1qú•mÇÄÁÍ«ÝÆimîsì±ÞßÐZ×þú/ð¤U?Æ7XÍËúÑ›Pc0œê©dIu[Íž¯æý&—˜·~´ý^ém§ «u ßqê0dæKª¬o WO©»õšë¯oÚŸúð>‡£úT{þ®ôN­•Öju}R¼@ÑiƬUk7Xæ~Žª¾Ë~Ö>Û+²øD”áÿŒî³‘•žÌZì­Ý'ÐfGNu|=–2°çsù–UµžßßþZŸøÆê×:¬“ŒÖÜzoÂ{±ÓX«k3emüßå­Œ^™õ‘¸mõ±ŽEía³µ‹n­˜ÖW“[_S£CO¯c]o¥WÚê«ôŠŸUÆèÖäåtŽ¢ê°léõ¶ŒGfû‹™ê·&ŠéöU]UºŸøOµ¤§?¤uŒŒño”zig©nsèêD}6S}bª­í³÷)wúOúëˆ.³.ªèqmµ›^Ñ8½»íý×/Bà¿Šë­Ø¸Íµ¸ly`cý;¾ÛMžÆØËkû?ë?¦ý'ü*ª*J£/VÝäZk]FM™8–9¾­ÿ¡vÏJ¿ô5¤§¯}@=ð4“¯Í%_2ܺ=[j}/.>ˉýÔ’Sÿ҆ݭÜíÄît©ÿÉ-<\\p^âÜ}òd?{þ©s¸Y¢X; ‡}ë^ŒÖ±œ‚G=àsÛ褧g¢ãáãõj²ô·4¸~ó˜êë.Ÿ¥ûŒõ–ýaÌË»­[ûUþ¥ÔÝéÜù´µÛ«e~ï¢ÚŸùÎõüÚï™–ßL .>›ƒ¼#ée ¾‹Óz·W§"€Zû¬±÷42Ïé¿¥ÞíÌs>ÎüGdz·ï²»ò1ÿF’œoñÔòò:».tËkfF Úà7 ÑcÁýÇûõoª¦Î­“þ,ðhÛëäcõXÌõls˜ ¥¿›m.ú_ú1i}h§¦°þ™ÕØ0i¾†µBÑö‡×W•S*®šý_SÔmõ]²Ú±*¦Ë³p:uŸW~1ê=;ç_‡Šö¶ªý&\lßM6 s=÷²´3ùSþÔJy/ñwŸégõZ›hnnNƒ‡½§Õ{ý}»ÿGþþ)r™}C3#.Üœ›'"Ç9׸l.÷ýoÒ]ÏHËú±ÒºW©¥“xŒœ:«{¬Ò×Ý[>ÚïM·;–}žï³~Žæ)U‹‡öÊrpñ+u¬¬×ƒk[X6:Œ¿Rœ¬v4ì{2±.m>ê}Gÿ„ýéR¾¡`µÝ쌆’÷ä¸08Ahk[à ÷»ùÅÒŠXÑíl4Ñft¾ªÛ3²1¶Y7Üü‰q-õ^æ\ö½íõ}Mÿ£gÐ[Œn𤦷¦uððIZs$O’I)ÿÓå±rhy6¸M­¶¶ÖA2Ö7Ýôto¤èôÝüµbΡ]fÓìp°9š¢ñôdþoÒY8ìÙU•¹¤ú¶4µàC·Öæý/{¶ÓZtÓëÕuA­sžk°>^ ú^“?u›NíŸù”ÜÅÍqh‡£¯.Ÿú[?quSò[óQmOn%¬;è˜p/Ý'ù-ß¿üõÏôn—Ô/f-VWYÿ a-–6·Ù¹¯»{†Êÿ3zÕÇè™X¹L8yµeeã;_CÛ¶KëcÝEÎc^Ë=;«ÿFý‰)óιÔúP˶üýÍÊ2ƼÃÒg¢ÊìÝþ•Oø_çqõ³ªåc}Tú¿‰‡`·+ª¯sAúLm.¨k^ë(ÓþoÑ»þˆ]Gü_×wS¨ä2Ú†^^G«iÈ`&«,À°¿!Ïý'Úêvlõlû=ËK©}_ÅÆÀé]/«Šq1k¶Ø•ÞZïÐæ]-sl»ô¶Õ»þý#ÒSæ/É{ ˆt;qèuÕ¼{½ÞåëßSºKhú¯Ó›|›‹ó»óEsØÎÿàÜÅËt™Ñ:^ÛrÛSîµ¹X·ŒÝû=ÕZSk¥ÛZÜ‹qmþg~÷¾¯Ñ®Ïê÷PªÎ›‡Ž Am,i2ѳè7÷œßbJnŒ6´{xû’û+ãOŠºÒÞð¤^Ð'·ŠJi:˜Ž;$sÁvÁ$”ÿÿÔâq+k‹ío¹µ9Ÿ7z›?ªÿjéz}Xn nt·Ópú-“ô½¿OØíõ?ëk¦ÖãFP:’k ¶noõ~žÕÑá5à^ã©Þ×8éú>›’S«Ñþ•œÜƃsÓ[ÀvÒC÷S»ü%ok_îþwè.këµÖàu+ëÀ}¦Œ¶º÷2wKúÃÃ}mÞžçûV³õJ¿Dº\PøA ÇTÁoXèù· ²E”=VQµÙÿ’Öúoÿ„ôlIO’[——æ ®}»±‚Â] Ôí÷|UœlëÁâ\Ç0Ô ¸ú?c·7Úõo+ê§UÅétõ{oÄÈk OÜæzžêë¹±í{›ÿþqXéŸU:¦ê²rZÜvWÔ)À¶‹7 Ûm®n×vû[±ß½ïIO¥~Ëõ ß|¸Û6»ß²Ë\o½ôîXïUîüß¡±¥t pÜë(sÞfÇ=åî$ÿ_ó?u«a­üÝ¥AŽƒÔõ=BÔ­kJw늿ÿÐù6—®ƒŸ##ÜÆêXŸH»¢ªúöº1Û~…]ÿÎëZ%·åõƧù™¦[iŸ™^S½×tßô yGªéÚlzºésþø…–[c#Fဪ…±WçDïq`¦á“ÔGD˜2‚ÿåpjônÛb¨™bvž;‘ê[°OF{RÙ‡Úå£Os¹Û@®™k{;‹»‚íVD25"¹|$¤zS­1TÅtÉ"Ó!Ô§²ôƒ…{¹Éç/ÕÏ}3Ç£LUõ?•ã·Ó<¤Ÿ)ù~;ï=y‚Þ[gÕ·K«‹->I§‹êÖ°ÎÌŽdkf2²Æ -تxšî¯y;]´×ü½c¥êè,Æ›wŒ0Ýj–¶÷JÎcX’‘µºFQÙxòZÈŒUäÍ>]mY´ï.jäú$©=ÍÅ„RL¼åaïh\ÈHUì×âÅ^µå<ÚÝk/'—uƒsÇÔ…å½€»žÞU1»E/4hé" ŒÓì‚M*Ư|¯ªÝG}w‘µ}5Mâ46ËmpZÙ¥qèEÕã¶Á—á4ÅXÞ­ùUæ{›Á$~RÖ-F¨`›)Ùdn_jà”Tve”5=(*—ÉäËÚ-&÷Êw°¦¡+[h‰yi$S=ÌœZhm Ú…d  mLUå_˜´vŽÊçËú¥ý䋆ŸwlâCÊeñ_ß/J-:Ô U›_þ\jQ-Œz—®¬$¿ e§Lö¤I4ü©Ä )ZžE{Sbù5ç ê®þWÕ%Š%ýZ=:u‡œ¦†(èµv^ôREqV7æ+} »]bÕb[´¿ÖKz*’’:ü\‚ºž;Ð*qT¯ê3o­/§ÌS‹Ó‡¿·Æ›xâ¯ÿÑøñz/§žSNÉÎÖæÓ“gšn»„Ê|»â¯Ôó/æ×œ¿)¼‹ä«ÿÍ»];ÊšZØë~\Ô¯<—¨Mªi‹cg$WëögÐxÒÌ4d ™¢v],UðƵùSæK 'I¼Ò-în-µÓô]”‘Ec-íƒÈ!ŠáËFI%AF£®ü«ñU$oË?Ì}6òi/|§wou·é/« à.¶±À÷Hê² ϽŠ1V©YëZ&­s¥k6riº…¬‹õÛI„n#·!jµöÅYžñ½m%ˆ‚iBË,¢CÀ"ü,Ḡ,7@>cz-Œì4.é~nƒQ³·Òá|­¯Áhî,=vW o+ÄnÒ9‘ˆ‘Z¨õmñWªéÍsæ(¯m£ó%‡›,F\Íkyt®³é:„(g…#G boŒÆkÜÓdaÕn,4-/GÓUm|·o«Ì4m3×£Ih¶ðúަ6»³’Bwr¬)@1W±Zy·LóMþ©z•ˆ¤Z\wË}"<±JuHÊ#ʈYÖû¤«4 LUä½8Ü~˜*ÚÆ“woû¾NÅZôóXA>š²‘PǵqTÎîÈygEÒ<µ¦É§ê–žlÓlî®H–ÞÛP”Ç=ƒ? ÐD9óZÔ½()гo8ÚÛ7˜04¹#²³Ó໸Šü3qš«_3UŠ*5#~FÛ‚L.$]*ã^\­ëÝÙê+qª@Ä*+L‰(å1øÉ4úâ¯>M[NÓôí ÖÝå¸Õ¡–;«bSЭ×RÍSÏ”t<1RXÔPUNïtˆu[-VömU­[HÞŸ L©src’'Lf‰¨ÓA±¨UåZë_y†öóTÔ#H§Ô),óÁT€#;!‡UÚ˜«Ï¹]z¤R—¢ä Ÿˆ¡¯Êµ­C b¯ÿÒù!}¥I4·Vp̑ȼùA `V.v؆$ö¥w¦Ç¦*úçHógåݧ‘ÖÿÌ:µþ¯ù…§Ák™¦6…Ö—Ù?¯noš[¯Ý—ä9¼h¤p ð­Cªòo7y“G³óZåÊcËP$1i·ždÑ•o½iÌÓÝ,ª³8t¦)¨´h9KÏ`𦻝\YêúKèÚVœ·oq.£kdÐÜúˆßX1%ÄNLq„€„†€íмÕìõ YOi<$Н<î—Ò^#’Rµ4ßÛfš6‘<1H/§—ô|÷\.!YP†™žž£9 f¥ âiнâÑ|Ÿæo)ù[ËÚÆ¹«Xj~]²½zŒv *[K5ÔÒ›5„J$7Aã“Õ¯ øœU3žãÊ~_òö±¦yZ]G̺—™íb‡YÔ¥W²¶±¶Fg¤Qó‘ä3Ȉ9‚£÷d‚IU釖ü×§X^ßj÷ºN½§Þs¾_Ñž¦uê Id`šÙÕ€Tª±*>/Ù4Å^Š—¾EÓ4[= KÕî–×G[t·3X ¾³2ÞEys'#,Dqúi,À(Ýš¡ULt+o)é-ªÆÞk½¾´{û)EÄ:k¬)*•ÍÊð§#*8ï»в}&ßÊZf‰«ù³RÔ:%ìºB„±¶–åníàiYÞE–55;îqT?œõKy¼ËæO,ß^ËT¸–êîÆö/I±"KW Ï÷|kñ)ëCØb¨[mgD³Õ5î'º:.¤Ò®—¨¬ÝZ¤öS”zR:¬Š£íAbÅPr§–¬—Q±ƒU»¼¼Ö"k­Eôâ"ÓmÝã‘îR˜.$QÅx‘ÅkÔâ­j¿‘¦&ó®¹§\Å5Ô6›¦Dçê÷±…™nQî¤Õˆ&Ÿv*Ä|áq¥Ýj‹ƒ$ï¤Úév)£Ã‹yã…"©Vä_^U”>8« ú®•¸e_[™§3ÉzýqWÿÓùé§iÒ]ßNëÄ÷,Ž&$ôÖb9'ƒøî1TÊÃʉp'Ó¢´{;Èje½ü.døc‰¤%šŽXV•ÅU!ò…äw6è²Iojâu†ä'¨UÒN@@ÒJšŸˆoRF*IùT—úšÏ<²µ…¢O:Ë:²¡2•™þjì}¶ÅXF¥ù}>™ªéº_Õî†W“P0AœVµ…Da{S¦*õFü¾ŽÒÄó¶) ˆ‘Ǧ(ô`3C'3Ñzš øîqUÿán-õ[Y®íãëÃ*þês`ü} >è*ÜË1TöËÈóÖòêá–TRÛ‰ le]¡/Zñ¢ñ¯~˜«:°òuÜmc,,I$¢ê;–f~lÔ²Æ)PÕ튽ÉþO·±ÖnëZE˜›”ÛÌ!ÞÒ(îãh^hT2†tçÉjÊ;üÕzŸÔô >->}ÉÒçÒ¸×e½s>§Y™#‰W˜‰\)©dUÝqUš&‹§êGN”ßù6ÊöKÈ$ÝÍ$*!¡u$Šêñ2»)³'Ú"Œª¿šüºšÂɦ{ʺbiWr¯XN±Ùß4Š¥$‹díÆ”¥_cEj*ó8¿.’ûëiÑ좷œÚÜ,·±ú—2¦#–û3ˆùŸ‚ ý‘RI¯4+´šQ£ço·¶‘>$B˜˜u$èiß¾*Èü£ægÒl4})ü¡¤]Á¡*Íqõ¸Òg¿s)g’R#­a5ÛŽû’8ªÁu=-¯ï5mFXÃÝ\ýa (Í*j·!UR›WŠü±TÃü7(QuèYü@°¶§î~¬¤UyÒ•Þ¸«ÿÔò‹k5­æu(I½WtšîSTàŸ^KðÐò),Ué¶¾Z“W•m&)q¨"Èì”ÇnJÊ­ÍÔ³"5A£zÅ•í§iþ©»5̲"¨”ñJµO&]Cgkq¨é²i×â#çÊÆ9¤ŸÒÉoH¯dV˜Þœ* Å(Ÿ>‹m®ùzÞÞ]W\7FDŠT°N]¤¢P¬6ÜŒU!ÿœÕóçå?攵k~Ñü³6m©j~]Ò,í ¸6XešÞe2}Z9ó16Û 5 ªúßÍzf…üã6¥¬µÍ¯ä½®©äËÏ3]ië4Wrˆæ„4šâhä’C4`ŠV I5%WÅóï¿Ì­oó+ÍZ×ïü«¤ù¦ÛÊvâ¯6yïUyí¸ –Ÿcj@…©SÄ*ЂìKìbŸóžžp´ü»üçò¯¦yÊóÏ–çFÑõ½kòëW¸”X_D&s¬`§Õ¤4i)(Î FÜqWÝŸó¾·ù³ù:2<Ù ùkò¶oÌiõ5Ð/|·gG¤ØÅ$Uw¹ #$&EUS²TŠâ¯Í?ÉùÈ0é›Ö?–ñh–œ—ÿ™º•¦‡ä½[T¹ô¬lÑ®¾³ŠÑR6 y;¹ŒÈi@ÜE1WÝßóð)tÿ/þQÄÚOæ¡åŸ1è¾c³Ó´Ý'M•­b‰E¡bˆ¶ª>JDªf5&¥kJb¯3ÿœ"üùóç'™<«ùaƒåï+ùGò»EÍÍö”²þ‘š .6¶ÑÜOq#±iæ’²ò®Ý÷*òOùø/ž¼‹¦ù—ò÷ÌþF‘Ìú~˜5 ÝAÜ\ÛÍ}k¨˜þ«så•Þy³z©N44+Ç}‰ÿ8Qùù‹ùµùO­~m~qyâ;'Í^b˾]»‚+k9à†Öu‚GiጠÏ= m@}±W寘ÿç$õùÇŸùÈ/>kBšËÍvv÷š§—ô„ó@º¾H-­ïY¥H!yPFÁë3ŽTU#ö‹*ý ÿœ&üÊóOç'–8>25ø<¿äYo-´Ñìc[kx6Iµ Õ˜«Jœc‘Š~Ízb¯ÍÏÎùP_ó—˜~jü¦¾7zf±g§Gæ q©Ç] kߪ%Ô’ ‚Í_³P›U†§üç7æ¹çý _óŒöw=üñXyƒM´¶Û,È9M?$Ѥ?þZ€qWꆣÉN ]Õ‘dBhX«…øŽäv8ªqõ÷W¦zÔ§jõ ·ÿÖæ=½£E£dÈÐ +8jÄfJÖ4äØøÔ€;×~‡ÿÎ)zvo竉58l5Oðä’i“L=CÇ!’RbäT¨ $Õ¦*ü*üúüÆóŸ›<êÛyÖf“Tòä÷D±³<é4®¶ˆÒ–oMg’5;§jb¯Ñoùø?ž|¿–?’ï凛®¦òF¥å?ô”¶ª¿Õ/­c‚LÅUX§ôÚ9‘‰4 8«Ê¿çÖ¾lЭ<Õÿ9å{ýq´íÌÞHùjY O2ØK3ÎÖÏPÌÊfV|DN˜«óëó~çÎÒùŸWÒ¼é©Iqy¤JÊÑOŠC35Â$I'ÇFi}DZCÐÕWë'Ÿ¿3µ­/þ}Ëÿ8ׯþSù†[cCÓ­ìu[kÔÉgw¥#¥üÊ ;‹f¨5$4nO\UùÃÿ8™ç½#Éÿó”ÿ‘þnóv¦šw–l<ΫurM"´[¥kxŒÅGÞUåÐ ×®*ú[þ~7yçK_Îß0ýgTÔ®ü‹©ªE Ló/RÎŠêæ š39Tf H¤  Š¥óìïÍÿ'yóÏÍ^_ó»ýRËó[Ëw:'—ÛÓ†MDÜCp¶Ìÿh}dÅéÆÕ—Ï|¹ùû¤ëžXüÐó¶æ-EµènWQ[ùn’á„€BŒK–Yª F¤Šß~„þTkKæOùöŽ¡ä¿ËmRêÌO%ùU‚úÂØ*—óµú¬‘n„ÍnÀÄMBÈ„F*üó5ýýåÓ\ê}f¯,¯#–¸”½ É#kÌîÞõ;•_­¿ó‡¾q³ówüà§çäÿ•õKˆ4ôÏ1K­évVÒ*MõmJÞÒ8§…\ŽkÊŽBEœ6ý ¯È_4Oq%ÃÃ}'=BÓœr^S$nQ’Fm›Tli\U0ü·òÔþfó§”4Õ‚Iíµ-jÆÚN*x¡3!*OÙ-Jü5­8«úXŠÅ’1ÀˆQxÕ¥J·¾Øªï«|^Ÿ¨œzV»ññùâ¯ÿ׿>Tšâ8çÓ¬D×—0…î#UKºG]ù¹lUôg•õ­CF–ÇU³¼[K‹0 Ë4j9£#zmº¹øM)ðœUñ‡üäüãæ³®þk~bþœ¼›S´_/êpi$ÃiÚ©K+h£1GÒÞJ‚¡I~'rª}wù#æ{oÉ?;~Vj^j—\Ó¼¿«ØkÞQ¸·õb´Hn^Å臈-•½ Ûeµ·óê:ú]³j"æ@u9¢µ¼JÜ4 ‰KXÆ8ÀRjqWºyþqËVÐüù™ÿ8ùå»]nO<[&©å£8’+ÙžçO—)¦_J*ÈßWœQO @ä|Ëù‰ÿ8Mgåÿ>ëX—^›E·Ó¼­k«,2O ò Y€Y4é%eJò1“T«ÆqWÓŸ˜¿šOæ•çåd:Þ‹©Ãg&{äõúãÞýbÙM‚¾‘3ÂÔË,f>g¨à¬AÅXGå¯ü㎕ù ®yG_ó¿”}/Ì[‹ øµ=WžÖñtÛèÌFÚæÙ¡’HáVŒ²ƒ#WÔ ¸ûETßó—òkʾjµüÅ{(Eªy¿Î~ZÓ¼ãå›Û+ R’öÖóê׎¨ 3°‰ÄŒ±) iQܪõ7òz?É+^òF§¥ØyÊß™ú5”úÞ™ñ{¸îžÌZÝ$’3#DËpRj–ôÅ_TÎþ[Ùéù«ó*ó]½šËËžS°òÈŠ;û¹—PžBoî®™HYå8 ™HøH=U|ÿ9áçO!ùþrË^jüŸŽÖ WDÓ4½b+£mÕŒ÷Ʋ[_ÁÈȦUöäZÖ§wÅ_qÿÎ þvþj~}ysó[óWó[ó /|¥åˆ@°²°µM>XùÄ.®î&¸ˆTú1„Šš—5S|oÿ9¹ç¯#~[ÎFZy›òæâÞç^òd~®ÐjVïui%üÐG5ý°™Þ¢UYƒ0)V&•Å_bÎÿÎBþsÎMù‡Î¾tó¶¿`<¯ùYaõIâÓíE‘¼¼¼…Ê«°,…FÒÕTn"§|Uó'üüRò7—?5¼½ªySVo1ÞXZhúßš4[ùn.l¯Z0òXK?®^˜Û’3F «ÔV¼~_EÎ%ÎCþlÎT~fi¶ºÎ¡¤yIü»±ý5­Ýèvog4Ò3ú0Øêî’ò«’ VTš…^cÿ?Õÿ+£±ò,šSµç›5‹mJ}QO©-´ðYÎŵíÁpAIh†3¿Õ 1WÊ·óŸ_œ~eò…—”5m{J]ÚÚ$´´Óôû{y![0R(­ãO=>"œù )LUù;æx¼ç¥júÖ‹¢ÚÜyÇLÕL^fDŒfkû‰.ø-De¡_ܨ¡£- qW¾]þ^Üùš×ÌO©ùoL±Öõß4Øy–ò«¶©c9Ӵٴ袒;©G R±,‚K9b¬{^ÿœ{¹Ö´û‰må°ÑüÃr‚æ3=”3ß5çÚæx ŒÕ€$…ãM늽_òÇò¢ßòôÜ4Z¥Æ£,å^Iî‰XðUb"EUJ°?¯¶*÷>c•8üu¥)µkú±WÿÑ‚ù.ÚÆÚ;¶UãpÍ,ÖÒ¹xÇÄ RÍPý‘JøœUï¦xÜõe¨Œá½0y…£áZñ's^ƒz¦ZÁá¡ XDAHÇ:©eR¦´8ªc©i¶õ{¨ÔF¬®ñ ªJ† 銽ZÀZÛÎ7þjhšòéžkÒåšïD»…J›;Ù l'â*̪à+¬?˜QŠ¿™/6>¹oÕŽ»,ϯØÝ]Ç«ió”V‰•‰øYH‡v¨§þ„_²_ó‚ÞhÒîÿçÿ7´?,kMoçÏ+y“P¹†2}#ky¨ZÂ4é\¡$Ã'¤G`A¨Å_~l»×n¯¯ ó ¹¼Ô,šæÖCx¦ÚOÜÕi$A¸Æcf$(éøb¯Øùö§§Cÿ8áÿ9U—¯[éxµšÖh¡-m ŧ´VS0§,åÕòÎÍ$¤×öroŒ*)ñF'´jöÅ_Ÿ_ŸºÇ$üÅó­š.dº¹ƒP»žÖ[–)G4¬Ò%—œjª4ø‡í^“R$Ži9ÅI]ã *VV!O ò uß¶*ývÿŸzyBÔy3ÏÞi¹•æ¿©YZAv7^QxÖµ#Œ€ïÒ˜«ô"[RT¤)P€lH¨¦Ý·8ª‘¶‡§ÄŽ<è7<«°ð8ª ­$yJ”^,Êhi¿cÇoÐZqô·­8òÞ¾«ÿÒäÚôcN•㹉e2¢1ȈH‰9('‘?·{%†¯·õ£’Hë»ÐÔ™x)f#us@6$â¯\Òµ­cæX­Ò‚6ß’© “öª<1U驞A T…µžFZt­wéÓzb©ëë·šlêe½S¼µ˜‘Ô2)Yâf²ì9BkJtÅ_˜_œ?óŒ63hÿ˜^xƒSÔOœ4¿ÌØtmFëW¸CÖzÂú¶7,Ä*òDË3HÍ@»Ð⯰ÿç?çîÿ$l¿1õÍÎ}nËU’Ò<ç{,Z}­î{oÌm©HžH¤“’´AÈ*ÁȆUñ¿çüã+^ùÝ|ѪÞù•1¥òæ½.´‰ ˆµ)MÅÛ$h‰ ê9ÝB²Z×}…ÿ8­ù¦~Mê^|Ò4{;ß:þeßO…¶¥nòÛÚêš§kõ‘H™‚%jX³üJËÈ)оlÿœŒÿœBó5®ù‡ùú~ïWóu·œÔjk²Ã=ž Ê¶Ò«r@ÍLj¨n{â¯[ÿœ}ÿœyòçå?™|Á­j÷ÃÍ™^_]?RòV«§K-­„ÐÞÀÍdøV­"˜äJŽ˜«üîÿœuµüºüÞüÁÕu-NËÍ~[ò•‡™´ë7hÆhï­DígKycôc+½>×€Ugäwüâ¯åï—5-ß~cÁæDzÿ–%Õ<¿£}TÞÁr°ÝÚL‘ · ”qð$í°Å_^ÿÎ=éúG“4/9yRÞÎ×B}Î:Ì?¡mœÈñ[™yÀiZСڿEF*ú5•LŒ£â”Q½ßñ­qTrZ¤¦… $žý©\Ul–qª‰9;|*½þ.àt¯†*—ýW÷ÇcÈàq_)Ó­qWÿÓñæ“æY ›»iŪ\€mYb‹.ÑÆ¢”nmB~œUíZ/™"KoBòR¶ÿ¸š1Æj0uá!A·JPmôâ¯FÓ|ë¡´V’ ¡r}$pFTAQÒ•5ÜŽ˜ª>?5Mżëy Œ“óoA`%%Õ‹1­ÒµÅY˜´øåÕu‹«™ZÒi9ÛÙÆâÑöuDa;AèàÌßdV b«´è¼Áç/3ùÈ~Ykš¦¹qskåK} O‚;Kx‡˜´Û«i/ÄwS'må!Ñx% ÈH\Uú_ùËäˆõoʯ9Ùën»Ó¦ò¯•X­n"Ò­MâB%«"„T,êë½zS~þUÿÎQÉÿ8ïçÏ6_O¡i™Ú¾· hÞbó ÷7"ÊÂÝEo§ì‹„‘$¨ø˜b¯×¯ùÄß?ù÷ó×ÉÞeüÙ×mWÈþXóïèŸ(i6œ“EõWk{½J9&‰v5@XêhU~}ÎkÃaùWùͨ^[ù®ÿÌžY¶Ô4ý[\ò;kÍ̦>kc}s$¦'• •,¼kMÅ1U/;ÿÏÂ<Éç'ˤGä_.èvÖѬ:–Œ¢hmC©^ªjZ0ÀèÊF*Ç.üÿwùá¬ÚÁúR-»õ,XÝÚEµí©¶ŽI"V‹³$’¹v‚¯ ()FjywÎw’OúNîi]e‘ŠÚ0UJ"¿o‰È;ü\M1W§YêÖ—jšf©k¦³9‘f0ª¤DM?Œ0h+òÅ_bÎ+›[ÿÍ!YŸîìî'¼†Ù‚FQýå%kðÕWqÔûÕW“ÿÏÿç"|ñä>y«òÖÞ®-¼ÇåÖÒ<Ùñw5ù2Û’-Ä%ú‘ü , VŠ¿õRêçP·º‰}Xb·haN€· PW>#íü]úb¯èÛɺ¿˜¿+¿çÚ_–ZÖ›h·qÙùI_UµV rSP¿’hžÚQð‡C pkOf®*üFüÞüÜÕÿ6<ͬyÇÌâ µ¿9}]uso Ç%ÚÚÛˆ`‘Ô|0T£3Ò…‰$¯@x!Õ®-tûXYRêÞ5-9£ïD™xÓ‘jŽ*ý ÿŸmyGTó濞<Çs7§§[ùj8îmÏÂïuq8D'ˆ…X™À`Ûb¯Ùáä‹VQlŠ­1§¢PÔpi×—sLU<²°Ö‘<ÑÔ³®äT('üšƒ\UUôf2q“z›»S]÷ûûâ­6Ÿ$J# ä«Jµk°úiЦߣÙÐôŒ…xÛr 6§]ñUªAËÒ©å^­xô®*ÿÿÕùaù{­ê~wi´¹’Úùíõ^ªœ`ky–HË7óGÈm¹í¾*Ì´ãêy‡TfiRÖÑfKØ¡,x9€•ܬ„)~,ÁК¦ŽÀª©Í¯–uÇò4ÞmXë¢G«-ªj ¸BÊÄG!rBßâ Vªaå[[Ë«zRýZíGŒ\8 ʉÁ•XÕO*щÜb¯VÓîZ$õ®'Yf¼g¶˜]±4ʪvÛ‘(bM­1W·ùÏ·ÿ—^jò¯›tø¤›WÒÝØ… ÂXx0’3ÅMEä­;žØªQÿ?—ÊŸ™z•0´í7SÕüÍæ êh/kC|Cù‘¦èZßäwæ?–4‹ø<Û“<÷mx5X™;-XÔ¢åyjÊ…JÝ2¥ rHÞ˜«Óåó†¢ùÿDóNæ=/Ëú›ÿ/͘5(ÞÞ+A¨ùrè£Z4Šå ¬€8%hÞ"˜«#ü óy›ó& ?ÓÒt}KSµ×#šòxš`÷öª¾œE]ùŽPHƽjqWÝUó¾µiÿ^úÓæ TâýÔT_ U›Ç$aPTó¨1 êÄ ýتaô Aä ²ñU;w5ÜЍ½ò å"ªT»ïR+Ê·±T³P»‡Ç„jÀÙ¨@«WÃÛH}Túù‘åÈ/*Ÿm±Wÿ×ù;e âê6q%ËÃv·³Fära#†m•EXûu§\UìþBÓ!m>éR7‘ÒÒ[«x$RòÆ7`„¨¯êߦ*ú3É>MIô­SRšà驦Égs”Ÿ¤ ²ñj»ÔÆc_‰ @»’¨×Ñõ·’ù…¡>¹qÄ¥œ@ ‚ªñ,h¦ªw­6Å_£ßz£j“7¾YòÕ€ó]Ž™w¦ÝØêÁ ­ï®Eo%ªÕRº’ -i½1Wå—æ¿˜µ/É;ùûRú Ö5-cP·Ó­<×X¼Á­¬örÃj9HÑÛ‹äY®Ò«ÊDÜ‚ÔÅ_-Gÿ9wùÅ¡k/ªèÚóéÑ'×8Ù¿ïË;QÙÌœ™Hfv ]ˆ55Å^=qù³çM^üj:®±{q,¯47&p&ŠI&Iæ’júo+¦åißÇzͯüäæEí½‡—îÿ0üÅy¥†JÆöúâîHŠ«)'e;z{zŒUõüâö¸¾c‡ó[òå,¬—UüÉòÌ1Ûjúg·ŽM;T·¾¸…¸NEöFÍMêX•_sËK6ÇueæÏ.é·šn˜†/,ZYiðZÙ[¨Ë'5 $¥ªƒŸ±= ¬./ùÇÏ8]k°ÍkªéÞXòå›´ÓyJ±‚Ѧ¸wæ+"*´, NÝò6‰emnò¬ŸWS® oFÝwè{â¯X‚á_ÓVpŠеâO}ñUIæõ­ G(Ú›3PPûâ¨YîHICoÈ| ûx«8Ñ]'S¶Öü½vm/lœJäqôÝH^PÌâèãáeu ŽÇ|óÿ99ÿ8ËçÏÎk¯3~gi~d‚÷L¶–)¤Ðn„šSßLÏqèúް-¥H_ˆjÕwê~/ë6RY]ÜÄ®ÅIÇjV*·5'z‘×JíY <„Z3Fh R[mŽ*Œ³ýfYD!“0 I¢žKÄlIÅ_¨_óîÏ/¯˜0<Á«ÏDÖzS½ŒMV $Å,‰N‚ªF*ý¡o+Êž²Üj €8úЧ6>\x»¢³Eý–*,kµ(zb¯@]C£‘qõVã^Í"ÕíAñSCEa¤1D2FQ7'¹P9ÉcUãZÒ´â¨7ƒNiBAFe ,eW&¤tøŽÝ~㊪K¢é¢${yd– x‰åBpôÊ)ÈS¥qVp²Ä}E«VƒcC°Ûµ1U>KÀõåÈ|}«Jâ¯ÿÑùsùUb‹çß+´ñÉ0\ªBÞ“Fîþ¢(/qðŠòVPOCнÿËuÒßK ÊÜŽÚX¯.O3£GV@ÿñy4"”ÛlUïÞEIáò¤0Éwúmàagn%XÌ|fyLAª‘þñgâjÐ튽IWø5’Ùé·‹—ï §c@ÁkúñW«é«ÜG$ÈçÓ?¼Y£;PP³¿]']Ó([Õ´¸V]麼D‡R(AN*üNüÞÿœ3ü×Ó;<ÁùYä»üá%­§éÝTä-Ñô™Èô¤šy‚D¤×`O㊾yó¯äŸæåÜ:oŸ<µs£êM8P³0ýünÞšMn6o…\$÷ ÅW~T~Dþeþqù‚O.ùEKë›5K›Û«É㶆Ön1¼²1â*h@PM7¥*ýkÿŸsù Pò½×æuöªÐG©èZ¼^X¹Š 9†¹¶/$Ï)&¤±â‘¸Å_­K$á}2$º´Üšñ¡ðñÅSÈ´¶™TÅNT1òSCºñä{íßn//œË%¸’`ä¤l8Ô¨¨$Ò†§j}õ¡ÅRßðæ«c òðãráesÀ õ«Ëˆ¯D&½ºSJÚÞ)nŒ²Ù[0¯¨‘±NEÁ5FU#¢¶é8ª6é!m1; x%º‘Ëú R€3ô$(5­:تXtøÌolå!áıj°ëCǵzb©g艽S%çÔŸ¢Ÿ«ÿÒù¡ù{%¹ó·–ï.'7·T¶Ô!‘T¯§Y–Ee+Fõ!`X¨#•(»‘оªÓtèl|ÕªZ}j9§¹kwŠ#h¾1ŽÇÓRvÅGFPF*öŸ$ÙH¾XÔcHÕ^ãКh]VWõa`PPÈH,xž€b¯AòÞ,7ÁU˜ÌÒ"o@”¥ $îqW¥[Ég¡s§\O¿¦i¹¢“ýã*U•T¡$ÓGé:F-槬ß\G6Ÿª[%Æ‹u þ[]y—LÓ#¶Õ?/æƒX† x½C>™‘™­§äÕPè¥ØBÀíÔb­k—ú?“7ÿç9<«¤iòiŸ™¾W¸ò·˜´ûRÞÔà0ÝhÇÑ&œ¾®Ë NÔZ ©E^Áù) j¿—ÞbüÊK+O.é~oóUϘ¡œýn°Bк´®ÌG2Ý>Ï(«îï-O5䨰\D£kÈœ7¶àRŒHùb¯N´Š8ã Ä2¨,z³¹÷ÅRë}^êHlåtµ>¡m ªÈÌÑÃp9üBµWQâiß@ÞëgÔí…¬-Kˆ$™Ú‡Ó’8žj$Ø U޵ÒHg›ôeœ/­íË=]ú³ª*±Õ'ÄmŠ¥wi¦Ãw„LgwÒ%€h¢ŒJjËÓ‘mÉéÜb©ýÆš%^P«FqžUjSá"´ÛÛK¿Bßs¯/†•åCJÒ´ëü1WÿÓð'‘´94}{MÕ`³w0ê±MfáUdYVrð¬A¡^_Å_[ùU®çÖµ RöÄÞ®§‰;_­]¤4IÍ(ÔÔ(è}±W®ù_Ok=&{5/r'$I "yüâT¨âG@N*õÅ#´P¨IJ‰“p¥‡B{’+ÛNõ%“Q²ŽØIqmgxâ´sm0ôD³¨oHKöG‰4=qU; ô9â…‚èï$=Æ¡’=ä‘8 ^¯%vƒUåJÔÓy§æ?åfùåç0ùÛ_°Ô¯4(`¶³òFk¨¶ŸvK¶ŽÒÚæúE‰ƒM?<ãzÆ»*Ê<‰ùCåmÍúV«{åI4ûi.üÈš˜.µWÕ#²8c6rDÈVHù%UÇÚf­vÅTì,¼»¡FšW–¡®…¤jS·‘SÌ6÷o–n¤&cŽCƬ̞¡û+J®Õ*²ý7òïó+T×Pkúì°­VÚßLƒÒ‹Ñ”ÆŽ’¼¯UUãPÀVîF*ûsÉ^[“L·G¹fi_‰u,H(µ¨ëJøb¯WŠ”-¡¦Àí¸ï·\USôfšÊakH ;‰e_MJ»@Ì; †*ªÖ:|¾«HœÜK¥(>$å]qT4š>›!—•œTH’QUe5‘v©ñeòRÄ ðñ8«Bü«Dp+^Uýxªô‚zõ伩֢¸«ÿÕìzý£êVßVý;õ¯Ýýkê_¥©êQëéóøýuáÛìÓzf•ÿBGõ;o¨Šþ«ê~â¿^û}ëý¸«,ÓÿèRøÇõ/ñM}=ù}c—«Ë–Ý1VMmÿBÏVú·øE?¼ãJþÏ-úâ©í§ý ×ô_ñ9š{ö·­+Š§Ö¿ò£ùCôÿ?Úþò´ÅS«_ùSœ[êÿ¥ý>õåJÿ\U“ÚÿʵõÑý'Ïý×ýåz þÅYàŽ'ÓúÿS—ñüU§ÖiCZr­hzÓ¾*®?­t>ÕØâ©‘ÿ ú1Wë~M+εï튡$ÿ s^_[ÿ.œ©Oò±UTÿ ñÿ¾¢•劮ÿkâ§Öúü]zâ­§øsˆãõªoö¿×Zßáš/ûÓÇö:þ=ñTü꜇ûÛêTÿ7óŒUÿÙntpsec-1.1.0+dfsg1/docs/pic/time1.gif0000644000175000017500000000441513252364117017042 0ustar rlaagerrlaagerGIF89a?„DEF‚ƒ„ðððááᢣ£ÑÑÑ$&'cde466²²²stt’““SUUÁÂÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!þCreated with The GIMP!ù ,?þ $Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ p¸*¢’8@’¢pj>£GÀ.¿7A€0ˆ´xÍn»ßBbJÀï¯Càá)'z|Z xŒ%G†ˆ ˜™šxI —›˜ #"  ¡#¤¦q¥§¢o# jY…µÀÁÂ8´¾ÃZ±I¬%É%·Ç`  df" sÒÝÞßÅ¿àlO$Ì)ç±ÙÜåA‘{ u"çTðúûwâäüK䃠N…@wîÐnA¯lì(œHñGl ÚUäáJDA‚Í µSw+áF þÎÑé¤Ë—1 0 '˜8 ‚`€6&tRéiàÖOœ3HÒs—Ï8’Jš­@ToHàà€‚t!yrõ Áªƒ³Å²²ØÚõkœUK·®Ý»xóêÝË·¯ß¿À L¸°áÈ+^̸±ãÇ#KžL¹òÙ€3g"ó ³çÏ C‹Mº´éÓ¨S«þŒÁê×°cËžºæÛ˜¸àžñq÷‹1¾ƒ»Ñ-ÜEïâ*€#_>„8óÇŸ—P.½ºçÖÁf7A}»÷ؽGÏÞý»yá··^þ¼{h¼û6Eç}ã꓀Č™Dv ` à– lþQ²ÐÞ{ï"dý ÷é€ú0Füç5† 8 Ü$‡DôÃ8bá{êpñ@,Pó_ `1@5Xq–-˜«Ç†Kp÷\SϋҶcá-úA˜ˆÓÀÖLKƒMl¢»ØÈÞh†’Íìf;ûÙÐŽ¶´§Míj[ûÚØÎ¶¶·Íín{ûÛà–J;ntpsec-1.1.0+dfsg1/docs/pic/alice51.gif0000644000175000017500000003016313252364117017245 0ustar rlaagerrlaagerGIF89a’´Õunµµµ°9ÍÍÍw¬Í‚òŠÌ파ŒMMPKK sssPsŒ0c©ùÙÙÙk—¸Qe þþþ g%,.0”Öÿôó/1 1‹Ôo ’ 1L1EUQ„þa\d— N ³ Ect n D#±>We°°]]Rçç__R:ŒÎ÷Z„œ^Z^„½ç¡¡©?F>==v&b01J18xG”Bçç­ÿÿÿ!ù?,’´ÿ@Úáp‘4¤¤RöɧSƒà$ÉN¬#óÁ}H¸²o×+)KoSÆŠºYmÛAá–. ÇíÙR^„…1‡ˆŠ„]b4T4B’•1Š™š™‰ˆ†MVakMG§M mQf¯kO§sRR NWppwp·rYP¸¸¥G$h4gOƒ›1i2Ë'ÇÈ'ª  £gÈÑ1„‡Ñ㊞ž™…ë„â¯Û\£cc¯óò¤¯É^¼'´Â¸=šFرȼl‹D¤]ºC‘Œ|qR¦…U³Ž C#H‹+’ž¡ëôlS¢É ‘tÆ^™G3Úv‰Á†ÅS‰&L’ÿ™¹tn# ’$§s¥9¢í“4h^=QºMº£EMf²ÇåH€§èìú…kKW-X†F5ó _ÊâT¨dÉu‘Ü©›Ô ˃ ˘<CpÊ.1$ÔŠÕ.¹BQõ­3 å–•ÛÈPe§¬+}-XÈr U¬H|õw×]Ýq€üòìÝÊ D.ÑgOß—M8.ºbVqCà k3‡É7y^œ$;¢ìPÔe*UÌÐÒ!ê I’ê4‹ŒWié®;÷åï­È^~” ·œ:"ŸÕä)»r„, HÚY‚F«´‘…ÅÖ{p#wˆTÂZeÌ‚ÆiXpÃÿB G0W†=fqÒlWXÓÀ/}Éx•|ÇЋ4ص6ÖVW8øECÅ,ÌIÒ…t¬²M\jL1„lB%QI÷Ï O<Á‚kh%QÁfâ+£M%Øb€ÑU–•ñ_8%=Tœo↠¶€v7­¤ÁŠ56’U5JÓM[e2©Å5 3ˆ),X y2´Yd,Ñ£Ra é‡ÕCǵiUY_®Á£Ñ)—1ß(ƒ)f#Zh 2Á%UãAÌxñË UŠjœa`#FwãU8êU£!ÑÔø62È1“_Õn,(•™|ÆFÖ“l°ÍS˜4êTÿ¬™V]‡Û‰R5ÁÜ6ç!»R§Ç½ã¢ç wc;õ­WcbfKŒd}]ÈM•a¹¥…0N eFšfÄøÅˆe–Ù„õ®öD†ÃlT;îy"C ±‰}š´SKDœ’s?g˜¸’Ð ,Ô'Ž:Ïœ"‡|JûØ&)µdÊðn{!„¦m|ño(™bd7VÉ€)œÀ£%#HT$XAFYY(ˆK´T4„¯PTd9H%Âfc¼DsvµU-€§Qã2”ì6n< š¨ú…7• !”#@B!ôP„W*b1NJcy&ÊÛiC56R$¹´´RTDuN{nÿöæÙÜD‚¤¹0XÇ;Å Bñ”ƒðÏTð0‡f(É„1Þòî\VLMôöñk$„4@r±@¾KIí_œ¥Âräb…s0]VÆWŽ)¤`¼«ñ†%1¹CµüT މiBˆ „ƒ›5keÁ‰ [¤P.(€$\€ÍZÈð…]b2ЩWÁ¢‡&ÔO°0ƒœp 3‹|S†+ýAÔà1ªö¾5Ðç*@dL&'®Q†*牉« ÷Q¨„ƒcˤ2!ÊåÏx3H@ €$`VdÁô^×4%& þYrÖ…¤äcàé^²Ó¨ÿÌuŒÆ“Â)c<# "Æf"AA ÀþüРH@"ÊOÜ–¯8Æ@ÜØí,8FÁNÐqœ‹Y‹¹R^f4(Ñl<@ obÑ…óH£BÝšZ‹PÌ)¨A0Ìa&  Hdåà*ð d%ÓË}ˆ“ U‹•bÓϼ'9¶©­zÀ5È@•ÙÍ0¤˜“¾DÜ(æD)1‡éÈÌÀòd¡  `(ôS2ŠXÄ‘9}‘Á;TÖMr"•Z!-IAŒ‚4à›&!D{ã!tI~ g ™EyF¿¬ j0ERÿÂ$Þœ3¨—€*ï£6ûõ&ã(«1XqIoŒq!@Ô.kêVU. ÆÉs5¸ß é|‘5X¡p€€ûáàEûÀØFþ¡Êîí”Yeõ×OMBŽœqÀ;† ¸ÖNxBbåf@I<Ò¤ŽükjÐÏDÞo•cÁßbôL2²©®@{(Ix$Ç RS$ŽÈP‰ÔàÈú+£iåØÒš–˜Ž €ú (…®#ZBvÙ¯¬@d$ãÞÏêbÙˆ"m CëÓ¸yÙð€~ËDíi—Øà“Ð"òÄ5mÙc`ÄeVN3 îZ2 Ш¸›hó¨µÝÿ-ÀÜ!…Äu•9µbþøÊÜú:2«5¨bù‰€ëÉ3ÝØ©7à|àÀÁ|ÀÇÍ@&ü¸æ¼ªãC÷2Môf…(Ü"Jéiß厴‹.ˆê`ÀªÑ ÁŠËq‡L 8ð„o|EOxÂ]ƒgfwœTÑmBäéÊG¹à‹%fîIO:çBwùÓ(MÛŽáFdbA&@fÏÀÁ‚üýø9þ@þpœ4TæthF @9ÆùS¹Q.-$ë¾â7(€ÒÀɹæKÞª>0‚ãØxeV° 2‚”—»–3åÿ$¬`Êâh­ Z–q).&€Ä6mPÕŬ’tÅ”{lm@ÒÀÅ$eÓ óÈྩµpÇDsÒ¥H{ÿï°Âñ,r› ÒÉmÚ¦sË ¬ÉËtrù—? ÊÍ,²Q«¡Y¤_ÜÖš+%›]+O €žúxZ.§@M4°H[9@·Ñ٦ѩ£tKgÈì™ü†Q&¡?: ²à‰®U ʬ¾WC)LÑLZ-ËRÄžÝ,hÒE©)06µqº¬Ú¡tÛ³̦a܈ï|½æá´ylÏzÊÐ\ÊR{fƃJšHŒ‘ÈtbÆZÅQ†?Û©$ŠÑ{“¹É$nJ·t«­èl<<º£ ;µ"4W´¤©®Š˜Á@«"Kr PŠˆÒHŒ¬ IYUË‚ÆxË•¶Ú SÙbƒ­ÿGj‹þ ÓaÌÓ°¦<Ôˆì™ 0¡A/epEEÜ‹v|ÇîZÊÔw<<ÇîzeÂtLÅ:œ¾ëO`÷ÖD3p $Mª<=Ñ¿ÌA]^¯«s£©xb"‘pºfÒàZv!¦¤yÄŠ¨îàÏÍ\à¾îàr)«›IIùõK‡õKÉ€¢ÁUk)Ï÷„6±oâœÎ&pi•ãlmIë±CLCS‡ ¤Qª*ª¤J)p,±ÿm%…˜e¯ØŠC3æ¦TK Q 0 †B‘Фð8ËѸ‡ˆ‹/J6A -Ž,pʬ*JŽœÊÌ´°–b5Ô`¯¬f¸¸¼Hàf\âê,òR,@b.G‰¢§‰.P®òG)aa¬qü4‰Ìù.’õTßc'å‰j+ K,­©R””–šG@3ç…Û| U¾EÂNÌbÍê D Ë$ÊI÷h¦o´ºÀï Šê+Ò¸’Yr³t¥™Ý'x3@a¹€‚K¹åÖVÝ„èþøƒ€µSåmI8ö&'l.q šv X@`…:¨r¶Ö µ)½fs™^Pÿò)h¶1T$l„$‡…Ý1¦"*Òäóç ¡»ShA@ $×QHòL"A!vtÒYAþùW€ÀþKSœÇ(XbË2‹›èq,tŠ V–Q@!­PÀ)3 › €=@»Ø,²#€îoA{B¢.¥Ý •И"J–d ~ö“Ÿ í•}5ÎYˆë]Ò°‰fýde¸@CÈ2°&lålÕ›CR€6©Ìêe›ðƒßpœÀ*Ïø›vˆD‰tçßpß92 ¯MØô##øöõ(ÝT X4\BŽÇ)}„AL¸ƒob”€±Å ð”#€Oo øÿ^L,p;ÈÀ*É%ÂÁ |¬·òÀ9Þ„Àt„lBtÂß­pkG`^óv*Ðpœ¤T½¡<\ ¿ø€2KÊ[Ñ=P¶¿Ü ¤­¤ìr7ð< x 6€lÉ Øš­¸.ÜÞBÿ#`)j°rj@Mð47w,/dbºØãJ@ÑËÚL`tôØÍ›@sóM¬ZÏäŸý|ÞM”8®îó¬0€D{ÁÊ)ÙÞÞÀ«B ¸Tø‰‡ð;JWhÂÓÒ=³ Šg×@¸¦C)P]íÖÎ ž˜À ¡•ˆ›|Žò{MÃä&6·)šX < ÃŒTIÜ-”A "F@½ÛœrPßûÇ  ¤p- ¼“+ xJS áÎÚÊ `¯¶g—*M c9‡v‡Åm»)eœcj]®‚`بåÙÑÌØÅwʌ쪼}ì4>^=Û\Ü { ƒèdØÿÛNbÌ2\à%x8t),î˜åì-U›jú*hiEõ‘]šøãO/xÈmÎ ÎQ€ÁàÐnžäªg ”;9¬oÎp |^ÊËÐOb’ò&ÊmbG°xçR÷e”çï[`ã2ê“"5‹ûK‚ˆ—›uzä i ©ä‘„ã?3(Õ.5ÁÚкȈçpžßºÛ!@ÀÞá”›á]g†œã«Ÿì—8Ú.8+k$ZJ&   øˆGͤn”E˜9å!Ìk¤b$F >2H* ç0dRíÅ0n-²¯âÈý,ЯrÈOÜ@SàÊêÿÀèI "ˆ.oê΄'4j š%%n!œÊ@ƒ˜m’àöZáöžG%³ÚKòRp`8\fSö©µâT ÜhiÆÏ î@L,ŽóB«r,®ù)&(nU²oS4á›Îc©hb ª@f€M& Ôr ÐA£ùš«2@&ÒiÔj€Ð"&ô© É!:ð °Äêû|£)û®0ÑVãÎ\"̾ÈPüâ5v«£tà âãpÂ^Ì ù,ÁD ` ؤ”èM’ƒQ:!píM¾Ê;°WÅäšðÑo mÒÁD¢ÂM 1À¤öŽÿÅ ëfâ| ü¥5ÎÀ(Œ þ¡jC¤` •ªºª ì?T?B«8’ˆ£õc'ÐDB‡€!Läq BÏDÀ®, ™Ž s~¯îc ‚h¬ ô¦‡š&`«F`F`¬:-62cž¬ï)ä 88mŠáåÑ0BòML,2ú)n%ì.¾ *˾‰!A¡ìkœ`ELnÄÆ€/Hÿê(  Õk–.ˆ¨¥MJ¬ÏÊÐ)¾ %ݤa¢°÷ÃÊÆ"ÃÐÄ­Ü6Å4tä°&óg-Ù(dV¸ ‚S‚Mq€y êã:!*‘­8'2|€üôà-®?Hd2O2¾­QlÑ Ád.Aᓈ» í›È át-4[¡¾”*`£Ufÿ @’…&ಶæMÐJ¢1Æ/€!Ã$O‰vfõlQ?ô±Ð,²Ï1@Aþ”ºt¢‰t  <1ÒP ”'ÄjDåø4ÁBðãè ­/2®0âŽä¬ 3Ñú#‰ÜÄÔ@)ÿ ó¤“,ÝäE¶i¢ÖÒt@ 2&òÿCVÔwËÒV( e›2–ζžBBUH’0å2öC¯ì=0DCÚ #DéN·žæIzg-R2(&ˆÇR@jQ”j&,M  €4aÀ8Á- ­ÄH „ÃÄÀp?êà‹ãÐÜã«ÜdN|c!.´¾FLSÒR%J)ð l+64t@ :%>n‚Žm2FLãD,8GÜäéõC1ûCÎÈ¯ÄøíÈšˆ :µDc2-ÀÅ)À°sjÏ·*Å N&ØæAjcðÒ!Ô@"6®aC‰0ó5g/!R€ÔÖ×ÐoÔØ`NIÿ ÜÜCâ“H„C4)¤šÀcUÐj⋚àJ‡¸Dq_’À¿~ÀdCM•Š[@ "æî <8à&è„1ãFÚ Ù ICRÎB­<ôHäÐX§s°¸KÊÎKŠˆyNF VT fH'Uô‡,€@5±R¿¦ æR(`â¨ÔMò&b6Öö£ Kä¬1­òî°§‡aüÜdU’#^ÉòIé”0_êþ˜Ñaub†Ôƒ :E:Å Ðb”ñ-’ƒ6A¦]ƒ´La_QrÎØ =«òMÝ5¾ cQÖ âFHKRÕ”Bð1ôfrg¸ig¶€˜³9» è¯f–¤ÿBÚ”Ö7¬åGç„*@!¾pÀâ ô ¾òSѦB+¹ "úc º@CBÖ·1®R³4a Zp_dAm2 ~ UFç–k¬uãé-|à<Æì0I8æàeõqOïЂ3T³ðF-$¯ÌâªLù ~”7‡2æ@©‚ Õ(.ñ÷ìEUhÓ*«ÉC) {Z·fÒ¢?°‡8Í{çÎBÙÐÊ1Ž4?v2ÿUãä•»b¢‰—0…afï08WKêÁs; Rv¡•àð BÚàãia„2#WUÞ”üf–ÖŠÃH33‚°,‰B´?ÊNr‹ÿ ~b C2 ­—ükðS HngiPL¡E r JãÞBããFPEï, g†/f@Ütñ6LÁl‚8$°â1S×’vvÔÀ0HÕÊ”"f61p@xPffòQ,ê $ö…ÉVK\ÀS9\ aã“Ãr¨õ¥4ŽàtÑÊtŒæ¬ÌÃÄ6øMW‘N~p DÖâZqÊ )GÀ¤˜‘yd¬m™×ã‚Í ¶4<é QÛ$vݤTBÖõMœb8om,Müú(Žð0LcïÜàeô0Òxü€"iy–”…877¸x†²7¸Tâ2bBÉ “QþHõÿƒsL% ³I÷ äì—¡~ÐWy*ÀÎ{Ô>QrfÄyœÅù÷—8WpŠ¢5® ÑéZ**^êi#‘Js2]0K¡X `טü¨µf7Ž ÷¥Zj8ˆÃ<›Ø)Èœ+)ŸŠ`èh?eÒÁ&è¢ Rédê“¶&*žòÐ(Pû³†´ÏÁ'øÞ8—%sÑ?0ÔÄvÌ¡‘R)Ì[™·aM)£| t5  gžÐæMH C”[ŠaH2â¨ùâ­Ü<”EãeÛ $7‚­l«È9œËy·j¨(Újö‡ø¨I lÿØvY B*¾€X­¯+7l÷=úú!^j³2kÜ€GÛÀ!\8Ø´ ‰_;õÖØà¹Æù¹9) ¯j>åJ°‘0t !_cåclq¯wq_å 2-T6oãM†ÃN㮻΂*6¸!BôÊÞ ©IMýšÐ´õTFžË(+ÛœçáNÆþAo²„¿' Îô˜±Iz0Qer©)|À ,Nk<ù)ø• )ÜÀ=’Ø “±g’ŸŸ˜²))'€"w¬YˆÂj†GFf¬ðKßL)/ò_ž‚FÆÊêâÂå,C«"p-r­Ö“rÚ„ÔNY8î1¯Ì$Iõ †mÎÿZFjÙ¸kasÓ¨Yžf¬Z÷†R¬ü{–){–•²Å“ò¿7ÁÄq‘1K~ir6ëtüô±1@ÌŸÝÃ9G.š/Zß"n„Ic"‚ÐA—ä ¦fü¬¥=„SàÉe¤Ã1>Æñ`kª¼`më×ïÉË|ÍCl©ËuìÓó²ÉÜÒM)i{£Ù´ãB9LaC{0arT^ÝÅJÀa`4=ÊqÀå½Ì[œÅ”"ÇȨ̂þØ¥~Hž zë^¾³Þ–íÝÏš\ë;ÉÍ|Ô“rÔN¡Nÿò§‚~–å6šUL|/ÝØg9äûÓ=žÝ£¼–×}ì  ÷>SîУ®  ?òIIÞya…t¬N#Û)Ëê‡}æò9H‹è†¢’[¸æÐÏêTÅŸœÒÙé¹jd–Þ–W¾ãm¹ê)’5qà(7=‘ϾŒ ˜>ʺ³™Q>.ÿÀØi;rD£©Cõæàx© 1€cÎbØ~öÙiÇòPåǹ(™\åí}Ї»ÅõýÓ‘ÒÙs}ÌÉþŸ|;ÇêÂó2£Ñ"£ª’G²¢I- €©t’¹^ED*…ø"‰–šq¹8ƒ$\ÿ.ÜpAŠ“<  ¥Rù Õ¥NìIá 9:.tåii :âQN*ùdh8 1•:!dlN-ŒdØi]¡f|y¹Æâ)ÔŒU²þ>X¶ UâýõIý±ªF²L Gf¹je|Èâ,$wB?Oeà…||xÙª¤hè”èh|¥l37+_.ì5&¯úÉŽ’BCÂÙ³ô¯ŸFÌ=Ëu(ƒ4ƒÏ¦ù` Z4Ž÷„cæoP(y* PáऊyÜ0ÄÇp9`Òèl*VqA¶>!µ$j¶I2@ m܇ñOu®mô1–±Ÿ„>vAZ÷s•/P_Œ¡§¤ æNWã®,6èÏ¢/šE ã*²Ø¼µÉÑ` Z«JÁûãºýãiG¸ÝC#…Ì]?™3Y!¨žzBÌ¢ÑyŒÁ2~WÉÇX5Âì· yà’Ÿ„OùòÙPœ Õ'ÊŒ P®&\L3fBÙn‡ÖÞŽ)bñÿ Y 5f}®ãaTE]²blÛ¸‰shÅPwPZÒ®8ó’w¸eESMÆ!Tæí6•l*à4ã “ lòe”STtÛB­™¦žÀa‡ˆR™„ …²`1˜5fBPv^…Þl‰Rf%nÌÌédkW²hៜöZ 8äáÔXu¡”*Ž&Íâ"Xl}C˜Ñ g›—øXe•–ÎùZlG-€À©È)p‘­R‰ ‰¤?¿>\0Þ=wŽ/!ì3¥ìÕµÖ´vÊ$ ¹°q*Œ!Š!ˆ.‹+ÂË«q’5 _LR‡g4›ÐÊÑ@PÊÔç½ïºèP¨ÁZù(¨Õ1l©“Üt—#€}¡ën¶P ²WÆIs×Awšë+µL'Æ€íÑè%‡y"LÀ2O¤9¢Vͪçf«.öcíH˜œôzV4¨÷&xtÆ w³ôÄI]'É™.Í3ÓVjàÂk}µ°{r§©Îc“)Ù·X¶Øg³Í6ÉQ†ÚôÖsÓ]·Ýwã·Þ{ó­tÞA;ntpsec-1.1.0+dfsg1/docs/pic/alice47.gif0000644000175000017500000002502313252364117017251 0ustar rlaagerrlaagerGIF89a‡´Õùø®…RlP3ºººùžwØÔwsw÷ñ•uFFFGê骪ªwP!‰‰‰Y¹¶¥d ° þþþ [ ;2( q ¢ € Œ ðl—l‹[ )!°tSJ4Šqm 2+„'¥‚lc=I?ïïó¡ÆÆÆÎœœœÎÎÎ: FG¥œ¥ÞÞÞGT!B1ÿ³cgcUVU!Æ!Ö‰gJJ{´Yv/qÿÿÿ!ù?,‡´ÿÀŸpH,ÈckÙJ:ŸÐ¨tJ­J-lS˜m¼° Hc€XÏè´:Ú‚ `ËÁáЀٳvx~ñš[k‚ƒh8LypˆLˆsl f„–—N-‡ŠŒ‹0 ‡Oš  ’ iYžK˜¯?-/œœv-p •H-¹±9£Z­ž°˜m8yˆzy8 ŠXI9Ç¿ª£Êá®ÌÍ^³n?³xºY39H' ÇC¢GÉ⌠ öäV‘!ã…Ý1qÔ)‚C,‹›@úÖ‹ˆ'=ü@| ˆf j’BŠiàÄ@,pй S)˜/|GÐaä§+GEŽ~^¸ éÄ…ÿèÂ4J†Îvbp¼X"GDmrEDB̘‘ ´h.1N*Y^„<À!-ÂXÔê 8êjì@±7àx£•É…¯PZœD§¶!†ž2ŒŠÅ@^Ä“ŒxQ+®€2paÆÙ)Qƒ6i1#ׯ{‰áÈQö¬¶¿EÂT¶eÇP¾Ì!HúG¨ËŸ! XÙ"Ød:Íœ5?·¥Ï€yj„¾ê“X[p¾MD›Ý"-¸Ífâô0—Çä~£¾&ËèÚðåÕ6?ÇøwòŒ4pŒ³ aBÌÒÖ‘:Î Ë6i]C0gy1 /…D…¡b ƒ@ÿƒ„èÀ*Ç[PôÃ`jWϲY6ÆD¤/½¥0ÁŽ¥Ñ€ =féwúÌQÕ7ë@pÁ‹¾<'ãM`¼"‹$Ä‘iE"d9>Šà>QFÒ`™ L6iO 8ZbäTˆaÁ/Ä)çŽØÃnìµp”bI²À:&)ÁÎüÀ   ¸àg{’T)Ä .,ŠÝ@*+¤8C !Aù}Ú•"GxüÅ–•Vº ¦b¦†nªÆ¢‹å1Ì™B "¤íC ƒ…dìŸÅ\CI,º)›ÿK ý¨…˜ˆ®¡Í¸\`€€*À(›M–ÚŠðÀ$IÛ«iÚ °EÀn?£’Úi{<ÔÐê¶á´#³T~Y£\{5ô=M«¦²À¹ØØu1 ¥µ@¶±l>(@4ðwÅ‚'·ÑÍ|ê ž,€]%O Vˆ2/£™TAŠ{QÌ “Rk5×#ºÑ‘*–†Tí„9nð‘F€€ 4 ƒüÇ"9˜ÿ vX¶|îÀ±Áu Bpƒ¼œ¥ãÑò IP`­hå'Å ŠV³N‚ Škq@`B°¡‘ˆ)آƗ—ŒW2X7N ^¤ 8¦ˆwpM€@}øµ>‰^3 †ŽlÝ€()@¬eÍ[.iƒ†Æ¢ËBN R×"‰=âcŒ0d'%A Rq°ý ê'š¹§ˆñi¯I¿üD@μI5â¦x³Å­1hÓ†==$Z g­vDÎCÖ< Ì:r†—qI7ˆ%Bº6ÐwÊÁ Á ƒƒâ—ÆhV„à­>æÔÑ3ƒjК€+`ŽyÁ­NØf6§€–©}sÿüv¸øâ²­ÜrpÄDWèà°ƒø†àÖ˜5j€_ <`ÀtTRµ“Ö†€ŸœÝ@¡ô±§˜–„]Ç3\EÂ}àaåWW£`ø«Ä‰­‡õÌŸT ÄlìI)NðÛ¢‚šø5\šC„<à­Ü¥)%ÁÖÎ^Sµîœ<0©Àí«J8 BW 8É"&b4 S !—zaO°B2´[ Pvj¯Y7t¶³¦úogáUMSa~ÑŽUƒ¯EN9ª[HpCþsƒ§2 N |Pq­2… áYFc–ó;€HPÌÛ¹á­Ç6UHfVÿÁ:>àgÑæÙ£.‰j{`N¢uvÞð…c@gœ†ÎyFð@'ð #Hõ,@ʸu̯î,à(j=6Ì%qÐgÀŽ1?í:&ó‰à1:g{B‡ÌöE‚™š>ṙšø€c{¸¡añL?ÚÍÅ4¿Ú©±ÅéìZOx5o)}¼%¸Æ ÀšÏìÇ 8ãÆîN!€ þúø­†+ ã½…Ùs1nÏЭ+7Þ»Hľ{²ù¨ò†Œ#f0€éEíx+žÿ ;A7M ‡P ¨x€!g#a:±!U£ JÑ}ÿoe*_÷÷jVêg>Ö#‚)`"l&'ÌvF£‘f)`nÁÁd[@÷mã50i¡2/2X²`€z’`€P#¡HТ3.V Ó¤Vèç-Y£ØÀgBm¶f¨³#ð·#V4 1€9à(wb$ƒ ð³e%PPý³ »C ÿC3ðƒ€€!¡Z C˜)Ç&nÄG5¦~÷¶u$ñ=‚-bd«Ó35uv?B'd.HB=²E^&®2#@DˆŽq/à±Àƒ’P‡v!ÀCMi7DG1låð¶ßòV©¥~<#8 Â#ÿ©%=0Nx->Ò3_·1"j´t,’ [ÎxEsÀ >wЇ ÿ |ÓׇzPAðòq!`M`1²³g‡o õu"o³´~<"8Ɉ-hwŽâ/è±N=Ò-A–-=Ñ£3d茳.©¡ …¢ \€UÀ Á@=?}—€r8²e1H ökæ·xgr’-d³::8?B8’ :úh…"Ù-ÁØ-¾#;àŒ—a'Ð9‰í36Á  1 ©Òê˜~1Pc FBs4‹F*ÝbH¹„©’!!phoµ¢-t²?á¢#Ú"'ÒæÝw:)ÐÿûE5D^@èb›Bd;ä*Úa ñ–P2mÑVv’G+‡3uè‹"1q2)ƒ!ƒA7/-@qƒ¡%@f0Ä#ap‰_Ä j£ÀU`=8ðC’ ÿ}PAÝ¢V9•ž’°˜§U+#qì÷i+ |Ý’aö™ 1ÅŸg'}À!kb òtFÓ¹;B¿%it1#çAÃg*b €GøåDt„ÿ„Ût˜*m (6‹I6nVS6GNr¢©=q‰³U^æuE¼¡ ¬1 òä$5p)ò«7eÀ&àFJGë{ÿEKVI#Ú}“²:‘â›óu³’%ÌjãujHqdè¢ß–$r>‡Â[°0&3àÀ®¤CG·” sè‰iWøcƺ:øuê7=UyZ늯­6^@'[„†úY ]0¡WË­  >±< \ßÚ 0)Y–hè*c“‚#»’YR¬¨#—‡öx+1P"o$‘a5i^³u‰¯"±h‘3›  ­'@[ÿÆ &œAk¯‰¦pêh 7m`JK yƒˆ"²•E•‡0oþbWXÚž«Š¯²å¢æE†f^B†@š§«­{°èÔ³%qQg×d{]7PõÖbuPE{§ë ÷˜‡¦K8BÕ˜c! ™t7y²ˆ+8šÇsÃ%³ –XÍEK  u„oŒVCfnb¥YKm-9vÚR´›±læ-‡“W’d$[ûןKV5]Á¸µëp’0³%Á è ]zr³¤—ÏNDcU†c˜¦›Bõi¡[SUä‡S—_µ‡ Hixª6A3ðUн°p XB'惇›äÿ0¢¤aUA=Ŭˆ)Ú²¼#›Œ‘y´?¢#s);u=±d­Æ–ü7g '29 y'`5°8¥ FJ3£8 l¢¥usÚ`$©§ÁŒ“%"¹?@Â$¬Ö;[¹ª@7 0~ð©0À›W $`5 µÊzÜ×}Ùá¹7År—VSìê¡7yÁ* I¥±0àÄ$ÛwƯü‰¯T‹i §!CÁ¸DÆ%ñgÇ”Fù´¾¶J0ƒ˜”¢u•‡(8~{vžšžµB™qr8 ¢ŒÇ#,Ê @{¡P—Ò+0HøU;G"ñŪ_k¤¶@ÿ–ö®›E´1­Ü}>r’#Ã\·ÆÊ#§Çà©{ÊN¬xRwÔzüCûK°s’$ d{5P«¸öA©°ot£¨³/}t?j¿é#¶2‚`“XÇÄ,=tœù+Ê?4kÙŒÇR7_Õ,ʃá#Q w(0Ë@(e;OøòUWó¿J VxM¥lvyoqÀ'8fù®ËÏÞ2'?Ö-XØ-b¨82-†Ò<Ê÷Ä¡\ÐÔ,h¹ÜXl2ËÛº”k4&Ad ¦ù7háÎkf*§HrÚ»Úªâ`%©´ÆªÒèè#ñ*»”1]ÐÿLÁx<ÊN,4—’žck¶6œF;0Kƒ4CšµžVje?xÐìÄŒvÕˆÇm,mÄVDŠ´ÐM$Ì}MlÊM¬Í`Ê—RÆ!A ³<ñ[7€žÁ‘áB¤eJ+¶bhÌл-‚¿ÚB–E;=_C8¹má"Í•mf‘k PÍ•})y{¨8 @$€Í( ªædê°]Ô@Ð\5f¾4-ʈÍÓœÄR¨Ï‘âu;v"˜&½wìÄR—+yœk=Í&Æ—" °Ç5@"a¬šrí g4&Wl"B²Ó Ù ~ýĘÊ~Æ "Jû›ÿ‡¨ÒŠ=Ù«lô½ÞgMá ½}-AùCÇÝ᜔,Ì‘Š Ü•k”­(ÕP–Q¯ýB¢RKómNÂ2kbÍ¡lbï]Â?DÍ&VjAÉªŠ³ÐÜ¢,4‡' <!é­ÞAÃÛOÜâM<\¨K+°¼6pã7N]ÙcÜëMä—2ÍgÊU~)bÈn8kÜSŽÖ۬Ѫ¦¸ï ÍÑ,á:Þwìf©ŒÉh¾ÝáÁ:kŽ]Ù‰kná× m(NÍDÎæ1=k)¾Ê­ÁèÖ|ÖÑsÇœ®!°‡ŽÍ‰Îâ}G͘mº(mèéÜxl³Aþâ=QÐÿœ^Ù•=ÊM¾‡™®Þ;Å/ÐÈåâÂÖÉ~}ç× `Y¦ ëºÍ#<Í‘ÐÄ¥Ì}ôá•]ß»uÞ®ìOüë£ìzwK ÉçO Í:MÙ$ é×;,í£áË>;äã=1æÎîô­I’.î(>ð ¦>ÕC ANêÙ Íw¬ÓL!‘–ßô¾iîëyLŠ#ïáâµÎîÖ\ß®ëãžÐÕœèMœÊÞò_Å´ à9ð€³ÖnÑw4Mx*Íïg}Ö~^ßc¡äÉ®ç@?Ù’îÄ£>𩾇¥àŠ‚¢Ã牮ÇÓ,ày¬éØwô­Ö—}äO>¡\íÿÏðw¾¥ʹŽñÀ>ʾ^ë0éÛ÷– Ę1ÉÐAéöŽëµ^êGŽ€?(‘Ñ,Ê æ¦,ô;üƒoÁÛb¯Þ½«ôƒ/Ü©®æë>k9U… ‡Y÷‡ÁJ7kVžìo¯Í­8ò ø0‘€¿ú‰ú•ëíc¯ÐJÍAó×îþö¢¼Ö¥0qqQhaGU³ `M >1ænOò”ŸáÀÝ™«¯úÂübhé%LùDð^ß{¼üã.Ð×,»f£.kG¾ìð` áÓ:{˜ð£ûz¬ÍAƒ³h/Ü5­IÖ @!‘ E p>—ÅÉ` V/ÿÖÄ@‚ýZƒCxðĪ'Hë×CÖ æñjp«Ñå²\Œ œ–¢Ÿw‹ššˆ” --òN®ò2Z @ ÀZZrÖ̪ ÊÜ~r66, Ъ,bVóòŸš š†™•›™‰¡†Š‚ƒ/@j 2Z»Ø\C_~ðÂ!Âa· (,,\r1ªy äùžï1 õ—“‡‹ØÛ¡©«$ø 1 6k\X£Â  `ýÀ1 Ô¯€ÍËc¡Êyy€¨„å¾~Ìþåc´d' æ9E…Ä(6ØjÑ`À8Útxåæb‹ì `0ÿZ…NN«T­Ä¤$ #fQÀ¶d«“E @X5€Ê‰lÚ´X€1®+x[\€ëæ…D V¹ËS5^5Nó€T¯ÈS 0$6ZÌK–Š\ètõ…«æÜ˜ Ýâ•2\2ÆZ÷ÀèÓð˜,løD@h¶ÂI”ÚÚ¸^ž©dHMŦ-Ö­–y«½¤—þØr‚MÑcÍùe’†.>ž4ð€d5Å'@(V© 8ޢЃh«xH}®NêûÑ?-Ù?מšƒBbN­@1ƒ#X.r¨…‹RÀ;^Lòv6,yBÊb< ¤BÃ!4ó0>ôt{lJ ¡$¸€ÿ´( f¤@<­Z`¯Ðphᆠ –t0p!ò.4` 1œ¬¨"†×6P|jK+NÒÛ¬“+7°¥ø(I[xlÓ€·2ê½ jåHÊ£Š+Õ¡×pƒ´Ëâ vvär3-pFpÙÀ-Ô…=Ýt3•6\ ÎUCCБš4@É“½r*þKM@Hô·*ó°Í[0°…\ °…F3m³†­â¹>ÒH­©Ô¬BR^F &C4è ®8!%/ RlÛ2ðRÆÛÕÌ`3-AÝÈž;€•Ôv™çJ_z!É;`G€¤+ÿ•[+0@¶¾DÌMÂõÅ5xb¨x‚]«†-a,–₺ÔPÉá]­rxvÍÉ$°½† ÇN¸å÷ MO³Ey¤6¶§ÆËUÖõÀ‰'®f]b‹}ź Rg€´Àó®Ò+A˜ã4 ‹aç±Èš>¸-8h46ˆŠ@ÀeØJp $ð Í…¾¸ pB;N£ªôÞv¼ã.Ðò4ÏËk^í7 %¯2h+e &.¾ÀLDî’6Ï©b´‡]@ Pùú¼¨ír-@óN›H@p[äW…ÞÉ­ xÁp@ŠÔ`j G¢ò°‡-ipãÍø ñ• „‹«†üâ7\ xh!þ,g¼Øì!y"ð ó0ªÑbÁAજ ¦Xà ô•+)© àÉI!€¨„&òÿ–cŒèÅO~ùß ów9tûÅxˆŒ”°wUÈ6’ 7¤£ ?è ²‡¦©Ô(' 8D˜ÈXQJ€ü6™€|’k£ @<ŠA.jäaº©‡-X•\©ý‰jyI&–lý+h± I.ÁéÅËM€….œ@%iÍ1±$Å‚\ ¡g€–TÖ¹CB‘“¼æQU`eAb’BJCq3KC°¦Ã[.´„"øâ z¹6GÚÐh”Ø*ˆ'±b1ró-@…ƒBÇž½Ø“4›$£Í©i™ $z“¢ Èe š6žÑ…\ÛÿÖ&ý¥MÉ#–ÖÈÉ‹•` èÌrP$SÎQZSG™v‚2ä5FHFÀ.´tmcËTwˆµP5ªµ—…dÛ<àK¶±M<ðR϶֥†.#lAŠòY’æ#”ÊE©òð” \'OøÃnh…N«M {ÊMyËr~¨‘”«#%«õ›k%ZdúªN•T^¦z’T.¤£Kf&PÀ?Nkäa@þì\N@Ÿ[®u¸>Í_\'úY_:@s›;㺈»Ñòtk»ôy¥IÊFîpX<^£¯C ]ƒÔSøXY£d€ ˜[{i\¹ÎÕ‘(J ÿ¿`ü¬bÃmã°„È©ê‚!"ÕÔ¤r`IIªUyÍ%Âú*UáB•2JmÞv‚°÷7r#â·Zn•\sÞ[¬d$+æÆ†l¡-Å5,WO’–´€ã„k B–ú Ÿ(œÐ [e×øZ¹.YÄeÛE+À^Q~ȆT„e8KKxlbstãXUÊ5¸‰J³¡u”Ä•B@˜oNY%ËõŒK–ï\ûF`¸S‡C[E 4³PÍàÉŒj0ÀsÛF|¹4’‡GÐ" Ô@ ±›†´¦‚…'¸) ÁM9É\ÏÎunÛ’™ûÛ*Äï$¨M.°@¡ÿôKÛ^Š›!c0J`뎣„!³.*;òAÕòöÖÀÕ ­˜;‚s†‘m¢æ™"`Ì¡UÐ0`@6.À†ˆ24 l/bp %L¸™‰•j´!ÜFÁX³’¼*,¤G-Pþi5Za“Ædã}3½g—x ¶B ^ƒ Ô -pdÈ‚”•Oy¼Tܲ³ f¥{逊õ¡B¥Ðj#Ž ²ñø³ð>Ѓ LÏ@]BqìÍ€§Óp›×@=ˆ€ÞØŠdŽ!iHl\nñUÜtb«ó(™>3’¨Àö:]/pˆ ›*ÁððêWö ÿ?w;Ûápµg æ=02>ð€àóD…2h†¨è(2.‚,ÿV’°eú ZÇSldØÄ0›ÈHËf &›°M:ï¨BÝw€³€2ïA>@‚Ÿ¾~õû€è0‚‘‚"ð¡Pݵ4¸h@äáÇXk>‚¦8xWdw/ò  ÎÈØ…yÌC ó¼·Þõz?Úñ®w£ï½Ð‘Å|š¾þJ ˆB1¾Ò}$±„G´¥ {ÒÁ`à«Ï¥ž.iaD.È‘SˆúžOƒç°Å‡”iਠ&Dá£6®‹:eFf, Áˆl<¬Êì¨f@Ò«$öÿD¾j3Nf¤äl!^ /€f ä†â¶à( €h%f´¥z®'À@h¤eá2pc P® e(¼ ⡚ˆZÊäæ"`ï àÐxd.fàâ$âàpàº&pBüÊ-Âà5ˆpЬ¥^üiªag¢"I bÚLìaªøè’jlf€4Þ4‚…”)HpB 5ˆ²%)iĈTàU  v  é/œå˜Ð¤'Q3pÃîoŒ¢R.¥aº£Ð.nS@À #'@JÃÿfïMè %"Æ ÿkÆã/ F ¬ÑB®ŒÀCø8hŽ÷Tå)fÄt  ûÊ€(ÚàòAþÈ”–溲 7rPcÜh7j!¸®A΂ „M>L%Pºƒ0àÁÝÆããàæ)N Äñä%Á¬æFpä©’‰ ìfÃÞ¤V A0’}äã*„Ð#À$ð+ª^åËø1äãÜíiB½Ò|áj rÑcW¾GÏܬgV¨¤O!-ÐP~™NA¤¼P1bðK¶à¯âÄ©¯ ~¨$l’ÕÌêèe~Ì++ Â(¦&Â6ã;æAop$M†„ ìÿ¨¥ƒ*€êñ!à ƒè:¦ê/­#q°+J=~(²0øn0$…{²¦Æ2*êJ«ÐrIàáR–@ê`†°¬G®„éÚàˆd*`5­€# `5+`¢*ð©D ac ~ Òâêäå¤7ârG,h$¬SòÅF¢p¼êTÊ䦯ÑbNÄm Öàô’5•n`³p#¸ ™¸ qrâ äÓ!K¬"ÒH'xaÍLF^dD<¢ó  Ã%·k.Áã;.d h¯ÖNA5WÓ]á;à¯@* p@ó Ð^À0Ñ €5ÊNyHB€ŠÔÁJjÿ2-CÂ6 WÂüZrÆ;Pb@ (K-Î’$*e2³35 Û'WXMžâpàJ0âÂÔ@õ^s5É4 °k· cid †4#Š(däOlìpv ð9>FRVáÞÐ2¬f !„lMXô(‚‚4 Rc$ª`Nâ‚9RB ¾Áî&4ÿ&IF%Iä’È£6ÀPŽ3±àF¦Ø>¬ ®aÈÕV+P0b4X¬*®MŸr5iÀÌ`Kìæ0¹’(è†{³Òd.¦d  ºtV¯ +„j„$˜D9}ü%„°JQq$ ¬7ÃÿC4¤Ã]‹\/‚J¼¤ÖƑܮ§ÜMFeR{…Ê'±2 ò¤"IVá'á&¬°)v  æ2Ì,ð’Õ ÜT/­+_`5Õ¸Òj'q H­"{óºÃV—­²NP~ØFb*ôEPS¬F<Â*% Ay¨Ö(fdc–¦7s6_àƒÜ Ðà`6ÿ °+- ™6Ñ™ØË&CfšXiøvÅoÁ¹QôæaViGúÅ6CFÄue‹¤ä£76Ej)ŽâÐú.t1Þ‚#4àÈ øÉV/…dÀãN p]À¶èH0>Fr÷DF æa®dRX'lVôFxAr-‚m<¾1¦“  Ža™pZ¶à^ƒÞn‡ÂCö‚µhS¢åÜtˆ|DÔ™ÔÀSÙ¬åjD0‚óao @`gu$wi䃒) ÀC­ '6£LL€<ÈYÞö]„ªŠñ9Ú@tm%HÀ 5J#î@™ú 3Lóz àÇX#fa1²kAâæÿ­Â67ù p`¶†¯3œjiJ8_Ùâø÷GðõAò€–Í`ˆlЙ ”@a™Ö,Iâ­ñPCv¥oÔD‚<¶j#,·ÓbŸÓ@ˆ°b9Èc(ðéØJ)ªzSŸ){ãÌ ™Æà3L ”&U=€|k•j ·FåQ|²°t«¨c(>È:DÇ`@ Ò`3ÂaÆ @OJX¯ç~6#àè)•:äBN`Añé…¤V×áRŒ:CE*´sÄ\6€ÄÀMÝeÅ^'бâ®éßÀ±Uû‡µŽ$ðPœ!™ð˜¡Û³ÚÁŸJÈp@V›H* €Í«"t¸Ú@óž.|7Ú‚¦¬û<8fã@¦ +œ•À€ævÖ ÐeÇÝáÇx@àË(ÌC B<pÐrW ÃÃz=ÍöH©Í$K-3ÜLÇAØÈ;œå ,1š…ƒ ®4Q¡0K‚ÆÃ( 0 ±d¸Jÿ`WE¦"qtñ@ž"šÈƒ;¼g8"]HfɲÏ/ð&R1H™£J¡„y20èÒ pØ+Ìd`é0 P2žºZLž 0P£S Â9Jª2WÓÀ<|pÕƒ¥7²`ÉCE3=`d¨< & IiA ãak4§Ÿ8 )‹†%¹†øiæÑ}9ÈJ ™ä?ËŠfÈó «¡¥°Ñ„Ù©³UK—¬ötb0<ØŸ›‹ ð‚«­Š$B ÓeÀªµÄ ~¹òŠ,D3¬àÁZ¬Ä¢À%Æ "훲( .„v×e!„²0ÿµ!æ÷õ¨ÎTÁÄ‹0iߌŒúà 22€è:Ø‚I½oAÛ9˜`Â@ôzH¡²Rd=+œ¬,£ÉbÎψ\œÊ¥=qJ‹-<”h¢‹Šˆ:ò7…K“ªŽ` Œ<à8x&óÌ€€ ‚‡3ÒAr‡ŸÏt=¤ÐâŒ/Ýð>[i*"[tÜŒ˜²¨f#UZTê7‘iëÈ!§fº'­sR/8…ö4xÐwéùvÝ‚x€ËÈÞ4Î ë jΤ¢4*ðÀCíèM–3‰èÈR3ä^ ‚Ö‚¿ÎˆtGM>»*œuƒò›O#r‚Ä"zаÃë z ¯ÎýÿDys¿w÷‰ÌŽŠz3ñþMî3Å÷È…+Nø¨ýe²@ÙΗ$KóH™ŽªW ÌÃ_¾ÀŽ  ¸y"> Z›€@(Yt Ý)ßÝ€p>¸ùmZªÀÀŠÜ1Ï8 ¨ŒO°ÇåhÉ&Œaر­”5B_KÊ«F°™Å6$ 4™£v÷A_éèE4 là>FÖ®†¶ÁÆîƒ¬@Ms¾Á×8®8³©M#”s&’ ˆ$"Ðåzê Q¯XÆyÈ€ehæN„ðÀ2Š1‰h<Êà)À  Š‚†RØ xñ‰D’ŒÙj"C«G(G…ÅÁ ¹!€V'ÿf |£(#˜ì*%° ^“ɾüey¨Ñ'IR˜Hò–R¡J\Tó“€I/–™ÀÆ2 ´k–"™ÁqÉL©H`dOlâËQèÄS{ù 1I1êÌR™Al¦8ŸRØà”ÆG^©‰'º c3)aFlÙNÔ`fAZÂ9Î~¶$B\Â1ð“èPÇàY“xXHzð©.¼ þÌhTÔ–$`pÊ;kkÍ*Œ™Àü@ %`g) 0§|(¨9¦Ñšn””@€‹¤£9S,àfž«Ä%¦ÁŒµ-븲©RI£‘¢Â˜b¼F`¨vYõªXÿͪV·ÊU­†r©«ài*~jf„0 žZ×ÊÖ¶ºõ­p«\çêÖ|¬ªhêS±C©žõ…[ ¬`cÓ€»âbõéA©aÖ¿ö±Ka» ½’%ªÖð«c#ËÙÎzc²”eEbGAÖ‚4v³ý@«z£µ'ô,M@ÚUXVP-ˆfQ+ØÁÕÇ‘¹RM'[ö¶¶¥­þKÔãV°[„Á}öÜ׺¶¸ÆE.+nûˆÜöÕIœ`…! q1nq ž;J`ÂÛyƒ½ po`i«]IJðEÌ•ÆiU"Þ-–@³©ßp…[]oT»ü ðS²ÁÒ·¾§à.ÿ#¼;ÝòºÅY° ~p‹Û¸À³©ÀPp‹Ô„xÄÇ3ï| aM(w¥%È~/üØ[xjÁºûð⎧ÂÀ@&œRáq*à`·8N…ba < òþ˃…èX¸î±_b˜å9/FDŒ2c'ÖVÅæ•IÕß\Öyð—I1¿P°ž°/øšÅ zb¨ £ª™›3«þvÍî”u™ÓáÎÊyΛÉ@Q”¢™Ž9V Àivñà;e4£¥S ø‹GµÆQŸqløðì£!‰lTÀÒÔ „±Îs¤C:HŠfÀ”û®¤Íª9žî¼ÿ‘âS;».³8žíŒs H[¾Å™5­QΨ¡óàÀ6KñÄÜà3 `WOya>°xÀzJP‹÷Öû½hænW‚|ïû±ÚÞv"t"Mk¡yݶ9fuz˜_k »—¬‘^„$Ø2ã´#nÀO*r쎸ȳKqGd  }Œ  ÒD0&ØÈIa4.‡üæï›xÉ`:l?fÏqq,cN’8õ¬i¥«Ò—Îô¦¿Õ®;gÄ^½ÅÙÐ8¤è'§‡p‡wõë`»Øg¤óÒH€•dI`RN—üAwDí¼y–$ ãIQÁÑÃ[F¢ûýï€ Ê®˜ÿ9íªåÖPÀogRÍÂçàà„y õŒ+ÀûHÒtuj˜)ð ý,?3cò`OäÈ ²L2$•ÚT—WHsCd 3g)“ ÀO"e}ÈçEOüâ#…ôñòE‚žHø]ÔÎAþÆ‚Uìœà'b²2KY&ô}$*ßOþâ#?^óÕ.ÜËk§äâ¾/’é}DóI9ÿøýÞC’ƒ.ôW~‚×|¤:ªÇ~4Ñ8–!Œ0|fq²ð*png¡Â7€ºÑf´Cž! X~X+bg+8¡1ဋf1žÆ/€=0‚U‚žÁÿ#€º‚xˆ2na5ñ_¦ow2Ö‡<sž1lЄü…ƒƒ¡ƒÿçé& æ6>Ø7 S<ÐgÂi @&ö¤u„ná D(jp—8ã°lîð1œ”}IA&t·}gQ w .ˆL=D†Rércè¸!>xe²ž&²°+Pl¸Gt6˜ñ†*ÔçP&bÐÈö_G ÐgQ/aȲ„/°z7øw=tUÝG6"!S5âƒ'{…¡Š=À…‚ç1—‰À!$‚’S-A?=AŠ÷MÑ{§¦cIƒHQ×YµÈ2Èiöÿ*ög mô5>èP( ç& 0ǃüdžkQR÷!ªàB=¡k)Äv»Ó„‰px˜3Pœ†Zgöx 8z>´ƒ¯€3ˆŸwU!ñ…ðˆ‰ Ͱ‘›TV‹× Àr™0/€D×FµW ùIXÈF)&û Y‘“ÐŽÃw‘ǘ‘¦±õ©ä·—së“¥°’ ×YØ*"ð?"¡8Ð<>È.ü ‰9iŒ‚ˆ”õ…,pv£9K¡á5g…È”.I9c¨3P&ÞäƒÊô3 S8Œ[€]u7TaO@¡–“Ç?h ^û‡‰n ÿë‚Ñ3“"!²ð2’ù–¶è•6®‡J°§X.¡†hdÕJ©7pwhtF#¡úÐqj´yú…l”’FliȘ…€,ïvðˆ×²t—Oð ù©ùƒÐ™ƒšÙb¡oiEo¹Ré°;€›#!: Eι›Ñ™ž58--MeU~j¤ ”pT÷)%Ï©žü¹ž¾)wÅÅR,BÑd¥p[‘0 )Éc(Žú ¡:¡Z¡:¡+Àž_)’‹ Ÿ.flŠÁ&@$Z¢&z¢(š¢*º¢,Ú¢.Š¢ ¡ÈEBI*ÿ IF<à>У>ú£@¤B:¤DZ¤Fz¤AJ2Z[ÐRœ>“‘a¦£<ФTZ¥Vz¥Cª¤ÿ¹4ú7jÕX;Š¥dZ¦fš¤KJYMÊö¥Rz¦=j$@¢2§Cê`fª¥[ 奊yCa*¦Sj¦9pø1)`§@* …Š¥|Ú§Ï¡l: nú¦†* @4€¨Œê£Žº§i V]* _ª9jcZ¦! $ࣀ¨& «x ªAjxÚ¨­J¦‘*©ÇB©®a€—Š©dš<À@Чzê4€ð¬À*¢>`Õ* P§=:ªeÿ¬Âzªe¨X7¨¬ú¨WzÖJ¤ÀÀ©ˆJ=º­°ê,À#@& Þ ®ÀZªJµ¦¥à¡†p¬ÈŠ¥ÕJDš9°¨>ð®P¯ð$¯Ïú®#ð­¿ ©[SäŠPæZ«ú{¥ k¤@'€¨ëÕZ«=Ê®ô§²°¨ ²Â:¬NÚ¡:‡° {¥ýzÍ ±)0÷¡´1»²˰¢:¦;{¥âºm†4 ®ð§¦0U   Ð (´r²(«®Vš±ð¬=ª´µš¯'À£û´>j³²š³>0µVZµGÆ'V…ŸB¬¦Ýq'@$ œš¯ðÿD]W¶gš¯,À£!À®RËÞÊ®MËPëh»±úê±f[¥z[_» (Ð1 · µÉåº>€¨i—#){¥)€¨B³J»¯Ûš¹›kùº¯ýj¹ KªöL Wñò° -t#»?jìºÀq»Wj4 ´<@Þê¹+ #±< §N§”«¯á‹·¢²¥wÀ qp¥ pj{¤û‡ˆ¡½¡Ú£½J¥º ±<º¡t8ã艈¿4{¶Ë”½ÔÀ®ð»& ²À{1¡iÀ¬ÉZ²‰À|ÂT›Á9"$#ù©fJèÿ¢ŒÂ8l¤ŒW¦3˜`q\°¹dúªAÜc—ÄJ¼Ä¢Â’/¬ T·E]䯴¿>š€«â»ÅBjN| :Æd\Æf|ÆhœÆj¼ÆP<3©ó¼<Èo”&sµ›`´Bª¬# §)`·A*<ð³=ëOÊ’5ü `04#-ì €½†p±:¤ÕJ¯ùJĚLJ VäCAçc 0Å\"€f¬`Ak¯j­ŸK¤P£Ÿ,²¡€Dq¬ð“°5XC\ ñ’çH< Ã÷QÁ³\ËÚ(• ЙïãËßÀ)¬r¤ˆÊDzì¿ÊÜOépÿЋ8×ÁCÍ3aÍŒFJÈ[¤ÉÜÍ¡åDA°˳%ÍÞ`Îe œ«0û½îLËð¬QO$~¤ ŸÍRShí‡;0ü€—üÅ< ¿J;ÑA*½T’0ê3 ð‘§üZsH.à¿ÁÍÚ΄œ¶BJȆ¹Ñâ”w€” ¨l÷aJñ¢‡€. ¤ ´90·ýÅ-Ó55)ðÍ1ýÀÓ;”övLÂü³TÁÉÙ:ÀqÊÕ>jÄJ½ToÜœK3B„wŽ0AM¦'°ÈaÝL šES7_€Ë¥ ³Xš±žüÖÿi:Ò¨PF‹‹ *M¥¹ËÍ|-ÿpOô×{‘JˆMÆTØ,{ØÛÆ™–ἕ ÙÀ\L¤ð®›mÙ´œôûZhvÓÉ &ðÙÕK#p‚MÚ’Š$êàsâðáœÖ²À4@xÚ²”»aLÛ5•iFi¸pÄH¶0H ‚ €³ÛÆí›ã h÷Ô–êÓ„`°×ÞÍÍ*Ö-Þæ]Bû¤ç½Þã ôÌÞëÞƒÞð½Þd{©Zßç-ßß]ÜúMq÷ùý߯Íß…@ßnÙNÌåàmà†€à®Ô ^5ï=á ᇠáÞÍ9 Þá—ÝÚí£¡áˆÐXx¢ÝË"Ž\½ÐÛ0ÿãÛš žÎŸÖ#@〨Ä`È/VSÑ2€Å> Å«Ú !ËJ®-¾*ÛAþeŒAÿ,¤“{z¨€AÓk¤¡!@>åý4EM¥$@Ù¦ÙF>¤¹[Mb~XÈ‚ÑHzØ$+Ù^ÎnþæJrN¥„<Ûwv~¤!p­ç:Ú ©Î’ ðÇeJÈ!ŽèÕ£ÖO^¥CØqÃFÚÖ”nS p³fJu Âfúª“þét&Ì- Êñä…þ«ÐÎBíÝ•Üç=ú®‚×@Ê£Íêš`:L€ê@ª¬¨ž¯e¤'࿱æœËã0;¤9àÖÂ~ T! ÍÿXƒ.³<«Êêë@JI­бü ¯WæÙîèPÔ ÕY\±ˆÚîî¾A‹0ä.¤ã«ë8»êñ¾¦¶ÅNfö¹EZëiýïB¨ /ª_ð~*%TV¿¶±L¤ßþn¤n{¤Žñ&W7oZ±_¤ñEÚ¯, í_}8( ¦ζ}0,¡‹.¤ïÚò4Pv@M¤Ê*ÃëÌï9o oì ,qôE:ô$¯ä‚ úÈšÜ^ý£4pîMO ™Ö ÝAÜ b¼îúòCúª{}¡.¤µnUC:Vö\‘ðd&ñîNlØ~,kýÕ**¤ËÝv®÷ÝN ›žê¸nY}¦#쇆døédJâ€,úޤÐè“/D ôTÚ¯.>!lóDš±€úµBØDÛø^ó,ù®¯âÙHj½²¯sœ¾ëÍ|û·ôû úgδ㠭]¤2 ´­Oüh-`$`À!€äˆú+#•'…ª«Ý›!–/ýñ¢6ôXù¹ À63Pþæ_=WÛÆÖ  XÞ;ntpsec-1.1.0+dfsg1/docs/pic/alice35.gif0000644000175000017500000002141013252364117017242 0ustar rlaagerrlaagerGIF89a|´Õººº÷ö­9dd++-  ©©©˜˜˜_[s$Míwwwö¡¡øøÉÉÉEECþþþVUUbbιœ**‹¤^| ºªÿÿ“1¡”w-+­Cè|etlU1‰C‰œÆœ05ççº29|M. cc¡™aZE„„„ccck{kÊÊçççcZcsksÀ¿1­$nŒÖÿÿRÞ¥Þÿÿÿ!ù?,|´ÿÀŸpH,ȤrÉl:ŸÐ¨tJ­Z¯Âá`•™÷‡ $”# @ ã¯ü8ÜW€˜xzËYp42‰?fw 43HosQlqSb43[  q”33ž…o‚Cu}˜M ÀV‹œf7³ É¡Æ?» e·µ88 [2[ŽD7¾ÇL2Î_ßÐ4plÀšÞ4E ­Z"ÕñôsÞ¾°ÏO0T ?U\#H7êá¨5m£„¸Žð: ±¢Vï(P@%á@šh„”ÑìÊ"ÿwšŽCƒ ?ÖÒF$Ô.—Á0šñ­\­"Xnk…4œ=Uø;`UF;¯Ô8´[ÇÉáCfT£¶b™SŠ\j:ƒ…¼šbjªB¶8Éä”´P)WqÌÁŠ—Q¨48 ©3d>gÑÂsҒ︷ ÝàIm!Êå&÷ÁwÃOi9l$È`‰À¸[x’9Ã.4  f×ħ¤o!ŠMg&¬Ö—–P„¡&y'Cn©Õ­Ñj¹ÑªV»Ge½­ðM•z rÊ,£Ü:I ±[;R,±4Kq –YîÜr=ôЀq‡MB”3O"·àЗ x ÿB „XÀ 2øGÄ]34#A%ì6 7pÐu©õUÏšP]ÃxqM‚°QÏ$¶”sB‡H&éÁ1ú‡uDÕ;V!dÊaA Ø;ÓÜPÛCì9Bu–xÎ b±Vƒ’lzP%ÆSAåNrQpLõqÐ)ï|õ]Æ@Ó:–$âE‚pC}2®‰dtÈ!’PÀdw…pTxÐŒ…종1Br’#•ái¹Ì5%$ÆAµ’t)’øuX)¦àA Áá6ƒ-jQ²` J“YpǺJÂi‚Än´ÈúƒZÂàU§‚Ôlz»æ¯fºÿé’„éDæÅM=p¬Â_jµtŠÈ’•3f]tHvãE–Ö ˜'óÔC5 i‡dêºp0€ ›ªàmMVhá×,‰RÐA€YuNp>QG3L5pi x‹4uË¡$iðÅ9#iK sè­fU ’äi¼d½ñHAð~ ôž¢í0FZPÃyˆóš*äg@ˆEm¢·d{ˆ_ÃIŠ-´°ÃÚZÅ<ÀÈâFyÍ*ñÆ-c“ZCð‚ŠÀ=¶›jã‡ß-› «ÂØ@ÿzö®ìâ ±¤;ênåœ!CVÅÖÛPð‰­Í€q|ÑZp8ÿ¹a? ´ÌÀ͸›=_ÀÅnæG¼‡”Ž4d`#z#¯XZ9ëÈjóðç…X7è4(ÜÉ7ܳ¥ûÞ¶¤h+¹i-'¹¾ìUpg=û…ŽÇQ1nþ¥!˜š.6‰ÅLI!jÓÄà–>Él|Ã’Ìj±#b&þâÉ_À n„ƒNÉhІ#¤!àX`ŒAvU@ß9lXdæ&1‹•+I@ûg€!ƒ}éÆl€uxe6­8‘ž’;›0ÆJ›T~Ì·¤ákIxX;"vˆ‡H‚Åv‚%ˆåpâIT———x¥×ªà6¦u ÌÿÐ[ŒÙüe¾ˆ‘ËW^\’ÌÚ§$œñL †iÊ€¨…ZÀ(E, ŘzR…œ$?¶ð¢Æ4÷EâÝâbHÊOÏvÖ0ßÁlŠ:ƒˆ Ç#PF5†¸Æ( “ˆä”"!¶ ¡ExÔ*…ÈŽ7ì§@ÉÅÞéì“\Ä™ –ÆÇåÑÔ<ÅTÌZ#þ´¤?DˆËðÊjXìqìÚÌrfLõAPSá¸@Î VŒan}ÑäÕâ&E43Â)j\ÐË~"‰žå4f€QÌ8•<œlÀÑæ8±A,šš2ŸÆ291Þ…ÒŸ{ ]ât„0ø¡hÚŒ®,HèPfÿ·0ž<‡7#‡iŠ¢ðôŠ¥²ø!Ì5 Y0å¤ @'4Ru›1>Ì£‘ftÏ»9€ Ææ¸Å”S8ÛØ"FL%Á¬€:ý"ƺ <­Æ“siX† ‰¨Ð '´1%‡¢„¨ðÆ6¼±ÅsrÇ㢠Íj€Zꦇ%l=Éç&qNO‰§ŒFo 8èT;~i Áˆ­R¨\RoѰ.6?cžÝgVó À 5á‰Z·á¨‹à¡ i(YØ05ŒØb¯Â‹ÙC{·3ˆaî“ÁjpÄxS˜Qq‚ #Ìœ)”£´X¤¸dü<²UñcÃ/ÒqAÿNV…Õ\ÄŽvµ¡dkfK„ìQmoÁ™Èe±DÝ´AOnLGE÷-Tžßº—Ô-‰* m˜³5çpŽ‚¬†.8 F\£:ˆá[~ G ¹XÏ™þÒ+àãã’֬Ŷ-¦BpBŽQ~`c û2‚N`õÐ:šk¢HúD½R@oM Á1ûUÁ¦O[•¾t "¡–$@¿|ðÂ/P124C-«P¯_)¹±*·R ’'ˆ¬ˆG7,lKo£H:Êx%@0ƒGÌF zúevñ—úð$½¡rˆ…Ê+L¹QœÚÈK©ž}¨ž†´`,;…Ì<‘9H€ÿB jáH-É8ÍD¨—BÃt"žE , 0ëÎ)wÖ&Ž)¥€ç+yòÒVu”b‰pÀà©óŒÜB¨XÃ4¼àÖ2æ A-pE³(ç¬:šB ÚP5Mî[h‹ ™ÀÂá ä9ÎÌZâ 5 ï—Xò‡Öô0œõÕ+hö«¥òÖŠ†t¹¦yš.™˜¡>cs°Nƒ'àÀhˆªÂ*L©á™mÀJïŠÂé+„d’Rùù»ÃPvr·r ‡"Ê/ àÀx!~é÷ƒ~<4f£ÊÔåg'w /¦W‹ck…'nÇÜ€óÇñQAÿ{âåk°ZF`°Yí'iìr16Vubí÷~@9çfág+†Ç\9cE‹'h11 1PŒÁ^f§9ì'@Ó9&—p(·‚OЂ´Y€gyV…‰1¬s'@)Ýp=‘jQ¤udƒJb]KwdtÇ„:d0ÕÈñ€I@Ux‡y–¸ ¿e²pq@5×BòÄU{C€§ô~ÁÂNTs{I Tx‡{–9o/WBÅfþ°:Õ @±;hk™ƒr9Õ†äVbSDQ  ð³£&P ÿÐ6° 'Ôfàf-Û §  Šá@? ^151£3‚ƒú·yÒbÀiQ tæJ j{v sêèp}ƒ#-ƒ,2á$`3RÐŒ(fk÷-dãcµOšÂ$T0/KÐð†3ë(sŸØD@$ð'ÍHe=G÷¸\Öø5AESù§p‘¸)O‰Ä`½xD ©nŦaC@d˜÷xàôÐ €Š÷H =㈙&÷x9VeD½!G¥!РϘ’*i}?@ž)$à1y•Îv§u°§‚Y1Ã6–ÔŽEfžØlÿ_¡ 4væ”tt• ÒX)CxhWnÜØb.’RÐGàñÖ‰_1aÆ pN9 o“áPöØBøu.7}ÏØy÷wÀ—Q€àèFáPjzø6Pꆘ°ãvÖ˜ð3bM*˜pö§\¥tš mÙâlЋ V$§ nÕ` E &œÐ+¯In)#áUE™Y°¯Í&I™Ð)YNxå`10 “ÎøœšÖ™ÁV)›²—W€gð.hél¤æÝµŽþ£·@Ml Hˆ¬Q Î!fvRnf HÐsY4,ÿt˜ 3A4½}˜¥¦êh’a™ÊyD…“ ÀUùF ŽåY\4¡"IŒðµ€…န!êÈ,«1žÅ‰œ@†Ý % —'ª”J1  À·IuèYif‰R7…?`º‰ŽÊv søjº@Ú8é P¨™·Àa  &Pnºá)66£O¸"À÷©Ÿ·Ðl*2 n¨1j‚B@Цjj @ ÀÉ)°à¤ÏhGp§ U aN»ÐH•·àlWÈ j0s…¹i„Ñè9gê÷ØœDež ±ž·ö-ÿM²©_FÑRÁ¥ª o¤ŽíYî —WÙŒgа*­D•œ`s:#òDzz§‚1ŽU*4‡‹È¢›æŠŽ2Çcö ð¬µJm — !—Àœj!sZ©ád!QªiàJ‰oõ”V‘ª£&ƒ~¢À”¥štÆÏÚÇ"¢÷„Ί“͹` rú P$Nà­t°P€'Å,p ¡)jÎVj++f»ù `2™3`¤fà®1ùƒ«ð +ÊK@¢5ÇR0‰)6` 1ëVë&l9+p À,0³,À°qµÿ KœBpé ´l›“ Ф ¯P¬YúAùQ”Q E£:³› Seеˆ+¸‚û·£&jW+¸5ר©më®Î² °¹µ§G« ab•Ð|K‡š ¸»™3{ c›¸¶…Ћ‘ë¸ÁÎz¹îÊøŠ´`0¼Ã›¯P{¢{}P’§;RK³û·+{¶üyµ]jµ1³5¶Œû­kË»k´ `´šK´ pI¼+(IŸ*hë»¶èØlakµ`°3›½5‹¸ø{¶Žt&iº»µÊœÌ  ¯áð±бq+ÿùÚá]Zæ£';$[ ¼)³b‹¸à ‹¿ÌÁWëœð×¶»8*ÈÒGËÁ²p ÂË+š¾ i¢+‰jÁO°‹+¿7ʉ[ ,»DŒ½cûÁ%,³, –ç`Šák4i:·&б·`©µÅ&°¢—šÃØQܰҙ?l€½VÄ%¬½3ËÁ!<¸ ÀW³ P¤ÌI)š"°¹qz pkékÞ—¯rd7 qAR[R #6`l i÷‹¿‚Ûĉ+½[³dÙ×fU„l¾„²•*§@·r¼c,(@´=í’ @4š³$ܵ$ìÿìÀ,a4°¢^á¹Ä»¢H{ÊÀÁ¹µ@ÌÏX›wûrü'h¼±  ëö…š®·»G¹%¼Æ_» édXl FK¼X\¾ÇŒ´Å¼ÎÄ«ÅòeŒšœæÃJ²*¹Íêh™á »Flɯ[ êˆM„ͼ"0¼ò|©H;·€§á¹ ÍÎ | ª<÷Üp¤ÏIp ’:ºŽ z^ëÆo\Ðóœêvë`ð•ÚÀG ·Äk©ÄûÅØŠ´8ÍÎ3ÜHÀ¨ Mu™¡’ sÄ £éû›Ëf »@6 nªKÃ"@´LJÈì<·A­Óÿ7ÝÅ„§AM¼ÐµÙуrI­œP¨œp?A 2"Õ¯KÕ ¶2;˜ ®Å,`ÈàÐrZ©0̤6 ¼¶ ¼ ¼¹_ÜÕ:m&ÐÖ·0'qŽ"swŠá$\‡Q4›¸4K  b·|Lkzd¨Ì6lÈ–Š,Ù:Ö6<¼"k©1qU’õ̰22÷ÜøâL³àØŒ°²ÝË¡ª[Ñ Ð .ð½yʆ̤›;õð8,Ó¾thjVa·D0š.=}iñ{ Ú{)é¡õ1¿R«nÝ\E+ •»k híÛ¤3ܤµ€ÑÿÄÜêío%l*c 270^¦á0¶¬Í”3·Žÿ·Ák©h¹„üÅåk äé²’ÍÐì<§Ðá‡Q,,˜Y­n6ࡤઠ®àÏ Ú6=§ù¬*^¾E ·8ŒüÓçíÀÝ ~P+P†FÁ”×i)PÐ /${ØÑz\«o+Ñäm´3²¹ƒü¶ýž¨C ÞlYnt3ÍÉNš&P^3B/›Ô8¾rÃs[0<¼7üÝÆœ¾_ÂÑVž¶zž èÊ eÞ´ÕÊõaþå)@èbžpG[¼Ç,ÛÞPT©›ëз·ÿ“Né3Æ„ón .s€Á(DnÐÐ`êb½š†¾Ãû¶j½«·æÀÑn¾ëa¬Ã¡·TÃf˜3÷œÀ°S•¾) ¸vÓÃۤ벨¬iéKÞ9íínæŸ U­ÃÖ[âZš*ÈOîé«î"¼YìÀä=ÊØþäò²­îÛ2íY<¼ªÌ …ÃeT° ÜÀãÿ§i0îO `)Ð(5k©Â;ÃqÑrúÇ·Æà·°à3Žï ì±—zsmd™‡×Ë É p¶0ð(»ý«ÉÂkE ç­œðº^Êö®ëýÂMjRá™ÿAïÌ ¸å¶òÇÆ„Îô¶@³Êµó2îäœk©ïâÓŽí­Öi}Ì€M±5oQÜX  ƒ[Þ§\©^ Lßø îFÑvóåkÙŸÛ(_Ñoñ½ÓR³ ®pc!еðMoì·ÐöLoêºìì^´t>·`­õo.à[ïã;í}o µ©§€#’áV&ç@³,機w¿úGßø(_‰ ¶|ëò¼âFû¶›à\,õ×.È7ßî5}Ìêë7¨'£'ü0l@˺lê+/æ°ê¥ŽúL¿Écûô ¦çó&¹1GfSa "¡ÿGÇ@û}Á`à@!Ðf ¿á`BÆ qT ÊCÏI¥¦ øòöèꕎ>LLB0…–²šŽ?²ª ¢>ªL"âÀØ$PH]dX 4 öp¹˜8L¦róô*2ê}M>9ª0L@§”z6§€£z2 ZFßhd((nÚÆ^ã$4f32t öøô÷+*0yðgX.;÷4ø’Æ!@‚Àžah%¶N{À¡‘ æ ’ C2È0J¥›y`dp8ÆÂAŠB)8àÉµÇ ®€)ð(Xf e ‘#Ć Aÿ²… µKX˜p3ÀèÛ.a²3 aJPü1æåŒÀ·!>;Öò€o!Ÿ´ŒMj †L"AÓ¨Eœ•![;fÍÄñ0&2â°™±‡T$‘à¥í ÆŽeàð€Ã°‚&r¸…KX®Ù¹×©åiAŽDpñ k%_½(GÒM “¯bÜ@Rä hPÃì¼riÈÀ"P׸ ü©‹‹}ß<€³ÀÇâ†F€AKp刀iN§Jnªð¦yÀ@€‚wØl¤È*c;(ìŽ\`€ ¸@¼ ±‰/ Ä/¿Î H¤j‘¦@­ÿ a‡äúk̈*(«O#Âኋ7nm uf0eФà‡2Øš‡6Ø€C¤ÿšø@@鯹«²êˆiC3 ðl‡‘ ð$ Š€/,$åšBC Xñ¨Œzá »øF8AšÊšA o³†Óþ6”Lj4àNw$˜´“á†ÒÑî‡‘Ú é© !„ ¥rU$§ò¬p (Aè  +OjT@„p&ÓD©¾¡¨Šþ€q‹ ðD¥hà€¬ë63E‚VæAÀ€8ØÏ€g˜ú5X@Љùÿ˜°ÃZôÝWàeªñEªM–áÀ·±,¤£ ³?&ÜPiAX×ñl$²Î"@A2Ø}%èBàÕâ J 3ïß™i®y<¢„BS+°˜¥~cJ“p¬¨,âma4%ž>ɲb+¬3€Ö ™&F!§nk®™Ø`XkþW°)2ä£ú¤‘j‹0ðÍ×D·Újm0Y`*Êz!™ÏÌ=’X[E+X«~庇`ç0&\x&€¦Ytƒ 0¿€ÌÅì0£yyõFïôÆbh B4A¢È‚ô9ç­ë(œë®Öîïq%h£40`@_FXå•ÿ | `ï ¶ Áv!²1âõoÌEƒ Q~J”HBÓ‰æT@ t®öÌØuü$€êß¿¸šjvP†_²†tï]H Êæ©¥ Q má P‘ •¤\Â5BAG'~.Y ‘Î2\åêj¬ IŠWɦ:ˆ$0á…˜$,@`¸&ºª¸ ¬{Ì&F— Tö‘›Kt޹d€øUè\ MføøJç,I hGCõV ÒÔnó´`³•ŠC‰®4i²þ† Ï …3€„äñÅ$îœÛôÊ’BÃA©5ÃzÔhÿ,h* ºÈ‰µÂ])-Õ8Ë Úqµ‡þÎ îrŽJåŠ%¨c³3 ó®a0áXÔ0²[Òà°ò+Öyµ4@§@B3Ž ØyU6EÅÄß {R'ºï ë"a[˜À®1ŠB2è "0¨ÔOªò­ŸT»…tBƒ°[PÍ”Ú,øŒ‰±•-¤êùÁáj˜)ÃÆN¸ÁL€¼ÃKè×BKf– ÚØçF\TÝí?E«ŠlO ÿ[õ=Rœóø¦HBƒ ¯6uIP¥„é> wçî f°L1àžFƒ´†€&§ü"­Ë° ‹_‚z %4 >A…•¿ 5C«Ô•ȵ à}_0Å ì®4 `éÓÕŽF•mT¢uù}¯tE\Ý=¢" CW$7Tuƒ°2à ¬“N8 €Á‡š l<¹%w¿Æ@Y)ÒëZ£t é@uƒ®h@Gfn›ÞßÑ€¼0hl`$q2¡›h’ñû¢g¢TÉŠ¥ *VÍ N®ìïÐgU­$5µF3z±QäØ¿²yÓc¸9úb\ÖY .ë,ÿÚ€¼ªr‡#éøæùºĤêÑç+ .ðȺ°dødlÀгتS¾™>äÈ& B µH_PÕTÜ4*8 ©~ÎcyΕ¥yÛ§Ñî–-!AÀpÀYYÁœ+ D*@fŠ@(¿g}„j›8ˆM|b ü|Ìfin8È ÓÞÌâuÖAÀ‘ü¢‚›c¨á˜¼b­1 ýGJ;–Áudûî¿ñS ®äü,ä´o¹ª€"ö-çË/ã`Â+Bˆ'Ye‡‰2`ð«"Ñ«|`ØX(N˾DS\ú›µ dYªX “r3é¸ìïðBé¬ëj_6yóöÞ­ÿ¬ó qúÐ’â—TÚ%§ ‰uF⾬‹P7! lU§ÄšBˆ€œwlÐzÄ4Ë:_ÅÂ~46ÃI\å>·œ%ë_@äP˜`ÇÚ+½@€Ÿ£+ðIG-]#«–Êú‰Ò£EË@²dØ–.M­dK€WT7W°cl šñ.ÉÒws0 ³0 ó031s1ó‚;ntpsec-1.1.0+dfsg1/docs/pic/flt6.gif0000644000175000017500000003631313252364117016700 0ustar rlaagerrlaagerGIF89a½QÄ‚ƒ„ðððáááÑÑÑ¢££$&'cde466SUU’““stt²²²DEFÁÂÂÿÿÿ!ù,½Qÿ $Ždižhª®lë¾p,Ïtmßx®ï|†pH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËmpŽÇoL.›Ïè´zÍ®¾·p{N¯Ûïø¼~ô‹÷€‚ƒ„…i}q†‹ŒŽ€ˆ6r•–—˜™1’5”šŸ ¡¢ƒœ4žy©© e / x®( ¬€·2¼vŠ;µ'¾“‘p“Áx ͯd§+ x'ÐzÛ2áw¥9ØÚÜ8 Ç~€ë%Ö  Ö"ñóú@Sð•ƒg•ˆ•@ €ô‰P€ÁA dñ¯ y?z„ð ÁÁCÿ°Ö1ä+ ˆ`(†¢Å@~3‘M6Gê„ðRfyd~ÁÀä­¢%:@†Ò#?z@ï5Ô*@@{ HåLÞ?q™„ƒqê®=Bõ:5Ûƒ¦l$pK*œwzÊÉvðˆ€ Ö@R(X`€‚Är"¼ØÁ >“€€A‰!JÌiÀiv³ŒBÍ 2ß–œùÁ€Š¾æ LåË2 GÚ4jß ¼´;ó‰á“ã•>Ýl@ôé»s… ¡ÁÓéÊ“üZ:y>› x¬™8bÅŒ?h\ @kéÜ`7d%w@ @ÿiíA`À2=øÀ|@t÷r0ù€9±w€R'ŸL†á!X „ÙQPÖUÀ„%Ðâ‡ð"À:èÃ@qJq’ãL –H+7fs25N´A¯XÃ$P.N”Ùìhœë„•=Ðâ’4‰•;ry€—#€)æ8 Ø•Þ:Šð€ãŒ0§G’@&3¾ˆ€œÚ– 9’pƒn©Bü€>Ž®AhŠ%7Œ*ºÈ×M›wõ…vœCŠu”XçuÊY„pA°N‰”pX8B"ðŠE"l6Z¬ë@­u3l±žÌZ‚ò{)IÜÜÆ›ÆKÿ %J[g°Ç~Á¦œÔžÒªZ²›& «n˜Î8±_ } ‚+˜Êª’½;QJ-sE$†Ì%@®µêíaê ¨ÒQ"uYz"0<‚5»YÚèFsÖJÕb3 ,ä;ˆV±z”|6&˜€\,çXŒül8ÛÖµ%pò 5ÏYs7Ÿ›óÉßB{îÊ5±ƒ XÃIÏžâù0¸"(  ”=o*4oT2” ÌRFnk´Õç„Ì$aÓjص3$<‡®GçÒL³(™ÀçY2nó'N¡ªéÀFRà .8Q ú÷Oåp›'˜ø£®€ÿäYþÎçÌgvâÝd~ê¡£®:µ¬[cÁ Áël.ì†7úŠoTÁîå,¾íM5P¨­×9艱"/7Ÿÿ.À€‘_ÿÊèeïûçõbøv(G´,üz£²“ˆ;&Hì-ãâÄ_×€w~ÿ“ ?ý#¥»úo~ Èßüfa?Ë|dæÉÆÿø÷€&s7&h4‹5P6ð9h¶¶.3€Iào@Á³ø§E¶ ‚µ~Á}€ßºÒÀrÝ‚„8×6(¸ư “Ø=¡e°J>ŒÛ 0¨Ó4`†rËÃÁ\`·Qxñ‹` £ ¶Ø‚ÿ.ŠñŒhL£ÉÈ3ªñpŒã#ظ7ÊñŽxÌc©è6eèñ€ ä!øˆ"? RÍØH!è´“CŽ‚Ž*°£#!Äí ,š"m²€@߸ ß&ù H¦@’¤lÈ^qIXÀ“% I®6šô‡*ñKe&L‰TêÒï”BBFÁ à ôb§QþÒ¼ó]–ͬfó€ÙÍzö³jÀZAKÚÒþ`´¦M­jk€ÚÕºöµÓ¨ lgK[éˆöl£­n_ë™û5@R» îg 0„° ÷¸ž-¢M‘Ë\ÂÞ®ÍÅD(KÝêZ÷ºÿØÍ®v·ë…vD÷»¿ ,_ÁK^8Š·¼èäyÓËÞ<®·½ð5ïaëˆV(Ôm5 H,Çûµ•üÔÆ. šñØþåCŒè31·@¿'øÚ –|À:À° $l ¯ •… $uáñÎà½(àÓ†äCK@˜ó…@I%ùU7 €ÂâP† ‰ãN£D/FtOd”T ¢JIõj§syŒóýEZ¼Ìk-9Æ'Ær/§ê¸}è®§L% ˜õ¡5kx#b®‘—köAfz ÃQ¾‘¿cÌ@ÑG» `Íû °žó\çn$NÀ]I 6ÀÌ®0E]ôÿ3 ÒIŒzÍ[Î*}éÇ0ÒØê)ÜÎL×$:‰®ÉFW>­ˆBó›® œ7‰g¯Ôtb³fU+å4é2:ÌcI¬ßÜhNã"1{–S7ŽÂÌ>€ùœu*ÎŽò‡®8æÐ¸Ö‹®}ájJ›Ú,@± ®Gä &ðHle¼“@`p#4ò`rÚfº$à,3€®<³Þ±²5 Þ:®w€s3J´F9SIC$s‹f¤I€bÀ§÷À‹Š LÀL·ÇÏY8Xš@wG€ÄéWqú¨\z9=žyZ à- ¼=³!}Gœ5SÍZãÓq£èýfÆiÿt51‡›&ƒW[úŸí­üO•é÷º$Oj伋™¾ÝÉnÝé{ãØËS·€(Æ×ëOC§béy'|ŒÔ³r«Ó{6ZÏF*¾qÇr”¹}\y ±™ ecGáðÕ¤ÀR3ØT ¦n¡he6Òe‹%Þñ-“O‚V¶ .æó‚81yu¢¶õJ)§¼é›Ñ¦Ù@ò@U=SDûžËõUrÀægóã?4ž’ŸŠø>¥@ R ‡ˆ)ºW ^ŠGÍI:,ò|Ë#àñ æäú0ýÜÊ÷¬ø-ó ³’«Å¾<Ö• ¬8ëà{9ƒ*³¶/üÊGÿûUR#éð|ÿ(`xB&MŠ ,[%Ðø…+0æªð|##©`.“, + 9—Ó’€ê1=Ø/p@ ±W'úrTÒR‚Ct‚¥æ²"E†#‚?vTøêò{ÈÀ éÁ*Gw8S£1DHƒ_ЀøUtª`äG„Õ—OP(7¥!Zv!5ؼS)@¹ñ'’ˆ€„60Mè€2‘ª`sסe8 ›ô0 ZrC ¹ð;¨6òNÊ£QÕˆ× «±” ‡ö}"0,h8û ,&Øz)ø+ˆƒ‘¼ò~Fà Ä3'žxƒÙw. ø‡BèƒiR'l#}ÿTx ‹è‰Ù7=è þ@±pðäuý#Ìà ¯4è#ŠRôJT¤ Á:’àPp€†´xˆ¡Ò‡Gq&ÎÐV(è])Pp0Ü9{xÄÔŠÜ T,سWñcT˜Ž§°$ÄÄ=©ó'@!ûñ ¹Žà8мW€—x„_€=ÄE{øØ2æ³À'T¶¡ÙZÛ¤42vŽ=x„ ܃=¶3„†)šmÒ/Aƒb0 #ðõ4( äo„Ižƒ Ù ¢y•»³†ü”hŸlÇf'1yHg]¡À ¤Kpy¶Ï`ŠàN$@f Û ›DžÊæh¯† ¯`\îé5‘ænê‰.töïégþžØàžÿ¤¦žÞ©lS2kb_z±Ÿo@'ÿ‰ø¸î)£î‰.×¹ÚžMVžÉl°6z¦g­FÝiaú(þlʬb¢Év¡þ‰¡º¡;áat"`Ù¸>Ž4V.@eâ0dx8b'`LÖ^Í™G¹ Kº †(ñuI:¥V Uz¥ZŠ Yº¥eðM^š]JWTª”“Kš³W¤:Ðbiªã"Ѥr²†Aà`#ি™‚å? è ·G &@Gº@*>¼CL+€C˜Txfj„<ª0* Z7&Ö%²E‡*ƒ˜šhy=:š©[ç—Š·c'ÉÔ‘šÿ›úºSFÈS3-Ð¥XQ%³@ÄbÏp5é FdE9m2qä¦ Ø-VP¬À'¹ÚryªoI‘@ßgC[5¨Áº€PTIMºJñ˜±Â ¼ƒ¬»ƒD‘wÛQ€"8ä!¥Ì:fWô tDÂÇ;ÎT{*?ïS®éÑrmLW¯yZ²"|M:)¶Ñ,2¿1)'w™üšC¸0t¯:± À0kH>k°÷³§Ìê:Œê¯ k‹•Âò†-” Iç å²\öÃD ûœP#€4ö¨¥¨‰·‘ ®×‚#ÖÀ:؃³I®¹³ä!#;ñ,i5™F>M“ÿ";$"ÍÓ’ > ’C–U¢v³;ÝIšШ•i^+vÒF‘}@ À ŠŸ br%C!G›±­e}A$w½R¶3vhwK9»ãBrÂw{Sc‘œDÞÐ2EÇ9g¸0÷¸yãb(yOµÚ³qµ¬ ¹fk¸‡=D*|R%i«¸¨a§GA³¸„1Ö¥|²~@ ¥ŽÔ"qÍrƒ:*œ ,ÑC9x:±"éQA”µÇ.ThÓ+‘±Ã²;À'Î`Ž×ƒ›¢ª WÃt€âƒÓ+ÑC5ŸÑ ®GE¾Pc!+˺—%‘ÿÙÈ4}‰¡¨ÐǃfÃwÙeMa¾Í`Žã¿»¹â%1ó~!wü¿³$ºP2¢r±!wa¹ÁH)5..?¥³ÚÊ™c@|²†'´ê… õ{šéó uEx7½4t!ƒ@©L;ȽR†1“á%tb§© MA,­ÀÈà ,™fƒVè˱èÖ^ò"R|ëk‚_LKH-v‰‰…h8gü"K“.ã1;9ÎFÄ=·P$è‚Dùu Uˆ &+„ä¾ µPÃÂ9˦)0@¾Ò’¼"´z'Ë–$Ú—”áx®!ºêJñ.×ë ú`@ï ÄÿG•)‚Añw22Õà ¿“EbIC»Å±L(þB5TâNš,¶|®I¹ËUrÅœl‚¿ m<‰R´ÀJÆ\ ãbÌnsÇÜðzØ£ü[-R ,(5#¹…É:SˆÝAÅaP€= Üà­‘±¨­×ð`,ᦺM!Ú{ŒÚI”tMʬ@Ñ6 Ã×Î K*ð¤X‘°d ÉaŠo¦.UÖ¥Z^CÇsàQâ}Þ0ª£êm0éÝÞðÝcßô]Å]øßú½ßüÝßÿÚÏõÍàþEUàþ žà n ÞàÞáN^ážMó.ì™á~¾!8øñá$nÓUÄ%žâm°÷“Â*þâcã4>`²Zã:pâþ½ã<Þã>þã<þÞ8>䜩DNäó}äžäJŽáLÞäþäPáR>å ^åÆlì-VËEÏÙå2Ì70ÖyТ)@æ7  hn =Ðå/M€£s$ä2àGrr]þi9 #€™kñ”7—8Ъ?:Þï†t Å C§5ÆI*PÓÍëÎÂ`èÌ£®èÍÝ/ð½6Êp"æùg=)x*Áä–4ÿŠäN«A'm% '0ú2‡g'`®Ÿú¼ãö¡@ñ¡êI ¬në¹ŽëØ‚¢ÀUPËÅ¡ê1£ºÞªÚK¥0'ˆà B š&Ô¾”ž~æÊP'¿Î’Òí) ÀàVt£ƒ]‰ÕB@¢6G ~¬'‹b\C%ÂÙ—Øpœ‚:¢y±íR™9œRéqny€I–ÃGàËçVɲ}¹)B ŠAƒÃZå£DžÁŒ])˜XÉïß.Š0]\)±i’FgœZ-*¿Udé:yY¿ëŽÃÔÝè[ù²Oü–·°˜´Dò¥)•´qÇù¶Bœ¾©¢9ÿkHî‹6éþ ¤rÄÄÊ2€åƉDp£Üóì ÀJHòNú‚¯sEÃ`”VC”ö7#ÌÉË÷ÜÆ@±V¼"N-iŽqr•±Ü÷ ‰‘bp F™r/c‰v;ÈÜ#øÒ7('Ï£‘hi)çèNÙLL×­#† yÀ²d*ÞJ¼pâö%bù€q>–SvoÀP}Mápø-#Ìå9Èùh@úY ;HÛõå.¤·K³²ø’§'5Òí3:æ!ƒös…È BÕ8$x—D5ÒÂ(°2~4 “RwBIñSQOa™Š Èê¡-ÙvÊ`M'‘®þÿGÎ  ûÂÃó( k²$ùâìã¼t.ÈïaÐ9†,ÇÂäÁ „`€ýZ/æH’ Á zéL½ƒ"ÐDãò rn‡=ïwœÅ(£éhÁ$°¼<402Ö ô1ØÄ P±tù $åije95Ýš)õüȰ-$¬4$Ôž‘€Œ’jª­fóB ‡ö0!—f9ÿŒB0 …Â8€PõA­Òué”.5åIÁX±äbm+qÕøP90,Xà¤ÚŽ HÒ£‡›B9ðFñâ„Cä†Eˆ³X,P §€*ÿ0P%Á? >• µc”E?xá– ‚R!$(DÀAž¥Àè&Œ”/ö Ϋ_|Üì r´.Æ›jr…9RYjî‹ç­R¨M0tGÇèN‰ôàil:bO$ÚÒíÁn“BÚ»¦ÊMnw!¸Íe€ [y9‚('®f¹å –R ™G (ÐÊÂ’  X NR$˜qà&Ï$ëÈÆt×J/°"ON×YþÈ¡@ƒD)6”Á³‘A-™LŽ×GÈ@‘ yèÙtÕò=†ÀGð.û ™ì¬—fž j= ÿÙ%W¼ À°Éš¾åUÑ}7õeap{½ðrÊÁ€ÖC£°d^FúôöÅbðàiy0ÅgQpÖXŸaŽ‚( Ìj (˜ í4”Iô€÷#8ô1N&åBmD9Ãq;öhÉ”)xå%yÜ&f??>)À“/@fSZI„0Qºyæ—G gjVy¦U]æÀf%™  €N)Qk¶™IuùY/(@…šÄÙ¦šqü9ä%™Yfƒ>.šät–è#$=OÆE£Ý´³hLMå7cq¶âA$®»òÚ«¯¿ lÁB¤€@$ ±Ë2Û¬¯…ô:,D7:[ÿ­µ×b«™´Ù®¦Knq®¸½ Ѐk¸n«µã²Û®»ï¯¼óÒˆâk]®K¯¾ûòÛ¯¿ÿr›nY¾#œ°Â 3¬/ ̱ÄKL]Œ³ ”i›®6Æ1ig"ÿº@Æ×è¹/pr@ä œY#.‰!H£ˆê%”&Pö™âP=¦ÛŒžÃ~OÉá¹t{‰¥(`ŠžÍ(<)N Æ–5aU­¡hæË¡„Öþ<@ÍQÿRJ-ÀljéB^æš?À-££Óe<{UgVäf¶Y£ G7©dUBX84†ÄŽ 5Îõ‡H½>–â*‚dhB 9ò ­`†HÍt €Ô,:=s#2Eà%kÏ·88àÀ pâ´sô²@ M$@™0`6i‡ä |V6c•‚E”spZàa}Áiq:¬Å¸¬Ò'u+tÝÛÊ©ÔmÉuâ[J˜5º\Á a#Ô¶,¯€ _Y!*ÆñzÌUã¼6!},Ú¢F7‡00 8`€Ì˜ß]£(: ,FO0€o½6ºñ€Í+•ÿ @$}‰p1²èbž aî®ÑgŒfz"1’P‹ÛÓgˆlC¹´qäXRC†˜(ð³2ʵ6 N§Ï–& `iTûáÓyU´ (ÌV(¸)ŽÔ|í¸Ié(»!Òä† ÍE…!bÖnñ5««Ë‘W#ïÐe4›6ÙCÚö3—EF1 >³:ØnHd[šç]M×Vnؼæø,¨iüöUcvå´}|Wõð½q•ïY4áì‹!ò ‹œFá•øk´k5Wq¦F^0òc@ ]>#.€‰Ç-˸U0 ,¤­ÈRv†ÊC‚M¸ÀCRB~FF²ËCR¤"ì |pä¾ä®8ÐdÃC6ä¯8ÈŸ”OrDÜÈhƒ¯pÔBþ“uÆÃÅÊÉ8 ’ø$®´¤ØÙpƒøuWì Úœ$®ÜV=pÿR;pÆÐ@D;Ñ“\ÙU]BÖÈ 6ˆXzÚé À©<ŒYÊC;¸‘@L’´JÕ(äÎIƒ,é\ÆeYä×4Ï0䥕 Iö•CpMa…¥íÕ¥ÇÀYšc0ÐÏüaʃ©,àÐ%@ž¥ä}] èÁŒ,9‚m| Q8`1vÉGm“<„ÙS|ƒiÝK4Àrìz€S1L"ÁÀd^’᱈ôn.GD@æ‘~ðf  Íd^æaUw5'K€ØÞ4'GÄK8Ad'$Qd[¯qçM!x™Ëü™gbXLúÅG,yŠ„inÄ”ÑTéÌPæ‡%ÿ¥Žsº ýe!Ö èçû#dpæËxú@#¸gýQœu6Ì[¹„@àg"@sH<)œ?šJŽ‘ÍXB ¸„ pMuþÌ@’$ÕļL÷KB(Æ@l_ÌÄ^¸¨–9Bµ‡k˜Çˆ‘’Ñ$$K}%†]Wvy[”…H4À-ˆA‘NxnŸtŸ%Ù—Š^né9`i²Ô’y¬Àd©‹R™HÔˆðA¦IÚ8iP"P܈ÁŸvÌÌ)-Á$äEŒé÷õVšzjl"ªä ׌L%¾QÜ–DGl´|\«ÅA†éÂqÿƒÿ¨¯¥qìN æ @C1ðš “°H¥ èÀMC YÄ>Ì^©y[ÈÞ9$Õ7ýNv©'AÀE EÈ (*:(Á¢–„”©C®]¬‚øõ+7|ˆÁUÍC T@±9¡ Š>^ä|ª p©FèT5Â#D¢6x`B(üA'\E2ÌÅ(Ä*sÌj†äX‰®²Ãq¤†Üy[-è€!k4 G± ø‚“ÝÃ:zÕ9œ"tˆÄ`ÃMr†7x’tˆ•­RW ôŪ^¸Þ”lÒ¢*ë¹?ø@°l9BìÌÑ«ÿíŠBéEE\ÄáÁÿÀFtDCxVG¹ŠkœÄ]¬„æPšÂM¸¬z–Bs¼êN0Á]Œ¡TpÈ’²ÔD…¨R”Z$윙ç½È>Éþè[ĨÆOÔÀiC"TdPÅWå`q¨F°íZqBôÅ_ÀíÑF­X¥Öîß6†>›qþ=ÐtPö¬ó+²° ¼ÁS)ÇI õÿ­ëòÅ!…vÀÅ!é -’ÔMÑ(Q*ѳ)áU¨P(uÒ,-$¼Qêˆi‡@cJ#ñ~´Š%…“ |Ôd ¸µ¥Ì@uL±¹.át„Ø€n™ ¨lxÐ]‡WÀ5IƒW®Ñ¹n„~I!iCJ¨— ’gW-"µÒ]5ARð¸ UÒ%õÃâØÍø1IcÇP`ÏY ˜’YYã‡\§6RO°BȈ„iT9cÓRMÂ7Áf£À; G¬Çƒ%šW-ÕØf­Fé˜ÀG1@èê9)D÷«J”³’Ìz_ÁOÄ7-Ûu1XBA>#0|Ÿ–Vÿ”{›?™ªžÁ#d ÔÇu@!@­0”ŸžDù‰ÊèrÇÁ1e”sƒDqöˆwo«Iñ~[ð`³&?îLC|ÀÓRm+@Á“ò†ò!Ä­–DY1C`õ™·…T ƒ’ª”)€E$2‚@F!Í›ED›h«±´ÉIŸX5,±øeû—ZÙ†7LN_\µÃNSf=ÒCŸ9Îß2kWù•.–`éƒÕ.ê|Ù¨N¬b©c9h2”xR£¸`dg9Å4œÙj”mÙ€ãÆ%ˆ hi7gÉŒÂ$™‚}9¦Ìäôx}õÁ½ªy1:!¼Wè‰ÃW¶Z.‘ÿŒ Ü"¬ ƒ WHxºrKWã5³®äD­‹váwý%]a9÷T<¡KsÉiµ×pÁ—j.Èy ôÁ}Õy~Ý=VÃesGû9œÐ§ Û¦àøŠq,­~Bu(™¶™ƒ§2XI‚‹¢Û¸ãØ Ÿ6eºŠ!‹J‰ƒAXXëJ—¥ÅL‰Á0¸1A’1LzÍ€Y‚ø{„)Ù¢ñ$*Œo@Ùì¬&ØÅŸêÅ ®Àš±XÄ{õ…!‚À›Y…eÁmx2ŽB› »»ñ6œéy4ÓŸÛAŠ«Br¤€Rƒx"GŒÚ :`›?YÁÅ4ÿÁºýÎHÚ”Žè(Dœ–ve]ªÉð‡Ï<2ßñcòôO éÓ9õcÿ@Zefÿá»å|°¶v÷w†ô“¿éÌÏéÌë?½m¿É¶ÿ¶Iü  ÀiSæfd&¨ÿ0s ÁqÐʶî ÇòL×öçúŽ€óీ¢ñˆL*ŽÁbÑ @+Dba 6ŸÑ‡Á@, ‚Áa1`L!^GØAø–ôºýŽ—ùˆE~þ(øâ¶²°Æà°ò …qØ0pȈ°ö0°@å3(:JZ·Ô‡ZºÊÚ Ã ú¸ ð°¸äGEuË"ë,ìzʤ:ŒœlW»º¢…êã›{‹ú¹‰Û<«ÌÝTœê->ŽÃL7©½"AЩ°"ý áL®¿.äÇ/à8s+p€`>[_š¨ àÀRze£R`€ÙzìæÀ$…ÿ˜DFETz‡R@K\¶  j¥F+H9V2h«;F =*¬ ƒ À˜†4êG¢:ŒJ½:ŠA7(]ƒ5,9ª9¬Š=‹6­Ú?€Vu»6®Ü¹bÉ–ƒK7¯Þ½ûìÞ0Ë7°àÁ«üÚp àâÅŒ;~ 9²äÉ”+[¾Œ9³æÍœ;{þ :´èѤ'hkdƒÕ¬[»~ ;¶ìÙ´kÛ¾;·îݼw'ì <¸ðáÄ‹^0áåy1þºô?Χ[¡üºöÕ·oÏî=¼Œî⧃/ž•ôÒϳ÷®g/5ï »¯½Q‚hºâç{ßæ5@à6êÿàÏ5Ò€% ˆ×ƒß5` „r%èUinÈa‡n@K}Hb‰&žè™¶€Ðq.¾cŒ2ÎHc6Þˆ#n`¬8D„þd)†ÕÀcFiÄ4‰d“NÖ ä L>Ie•ì Ž•ZnyC”2LÉe˜z‰bžù$™¯˜‰æ ÎÀ_ ZFp´)Œš„°‰' Î4àßB1ú‚¨ ZŸÄ`ùŸŠâŠvîDÍ äXØ=ƨHŽ~j "ØÄ‚ƒ,ÿ tМƤj9ÖØG/ÍtÓN?mÀOØœÊj5`µÍHýpÖËRa­5_\‹åuT`‡­×Øa•Í(’ÀNL9ðj g£M—Ú+0 w—rEɱ+l+ÃÉĘrò6Þ·\zCP€S p¬.‚«–sÿsx2^ÜsÀ«¿îÖÔ9Z`»ˆÈ~@¼•³`€W`‚7¡#•BkPâDûmÛúYµØQœ:">@@B̪ÌïÃA±à~‹ôŸ'VÓ³ë€÷ €EFû#€Ÿ}Öüp¯,ï(\;È“/VÖú `y+œ€æßÉoÜÃGþ§®ŒgÉ‚[ð.0 PC`€âI8šhЀ˜p4á©É Cƒ…Ç8`b/¼ŒŠäǼ©ép‡±YÏv8ÁCeAjAÌÍŽp(Â'ùP;PIËö'—%ÑIKÌO­v%ÅzUÑ:MlV1°vÿÈ+¨ ÀVf¡= mq:]Ä"–r§ÁyŠ @ W‘l\k”NmŽ'@à#åœ2€Ôîc| Ͻ"8ï¹BnÃbbE%P’uBG Ð-éýrYˆÛ¥ÕGè<^‰|@»°G ,b•/¸$ à· ¢’Gð5J”ôNf|x ƒu O  ˜ ÆU]ÑRùs¦ ùI àÕkP…\ÊMS]³xÀd≀(Yg €ºÈš'²€b(œ8`Šô„.÷‡N—üóg{@<=á¶ô”hOØy‚ \줄*ª/Faáÿ(0Aè9)Ô#e5@'¯N:€ç“µg°&Ø‚ÂÅ€¥¹ØeA¹‘8ø(œÌ¡%宫Gø¢ŽH€&ˆjT|€!©•x‚%šÚޏNà„ã¬@¼Ó¤œSpC,…E`©@SºˆJhU HÅœ†ê„« o ?àŽÀ‚±²²€e“ûq"ÚÅŸ*npR”S€¤†%Kb/#˜;7]ð/³¶Ôb=¾P!eE³ í ©¤< õµàs>û”™RïørJÙ° B[º5¬†Œ3êØ'M7v¤†k9UBµ”X"•/·N°ÜB§ŠIÀh‰ *káΚß-ì/n½ ì^cÚÇû]‹À2ð0a|Z€mY˜Ä‹ð¡ üü/Æ ë+‰ì Nú]î™w1\õ!ÿz¡ÛÅÌDNpRQÎ.égA•˜Šh ¶Á0sÅ€Ar}£Ñ B‚Žé†°e6ñ%0Þ[a‘'fFl夷ƒÀ° Á8¾ ¢.8M ì<Ÿ. ƒ§»®!áå\;Œt )5¬ul3¹2io\©õX;eÓX@uëmhS»¡ðE´Ýìé~|SÔ0( ìmÈÓôÚÂ,è5(½‹ÛÓí yD0¤Æ÷H_´„°Û€- ‚=sWÙÛ ¤ [¡3Û‘.õ„Òpho!‹ˆÃDJ.‘ƒPNj*¬ÒžìòˆL¢Vx=÷ r­²Ü$•H–§òiÀ!óÀ¹CÞÿ‘òEd$1(?yMŽüi$†Ö=ÑÄÕñ:Í%$¦Ûœ› ¾›>Wú·¦Øºz¹ÊÌÎ2 ÿÂÞÔ F„i›U°¹b¶u’˶ãœn­¿xG)êù‹R5!˜-¡ ˜¶d¢<é ªà¥ä½&“(|:¯%xÉ?ž ”ò ã9ùÆGý€a€ ‚ärS„¯·æPÓ vdT»’S{u¡¢VS¬[ð°rL‚S¹èŠî¼âšºiœÈ ˆYÚ“Í;cZäx½Nïf5?0ÏGö©®íó¥ûgú>ÍÂoýs™ÿ/Sl’ø÷B~#ô[I01óÁ&q0ˆë?Lû‘ô~ÿzv0€0 ~Žæ(˜$ÓW—VCØ3 H(ïÀ…FUc°XwRh€Uìàûa'“PEi¥MœäC}j'Ä€7`4E$K&ƒÂ‘~Ð "²r©…9¿í¢TµÓ[Óu?möWÕ[J¸&Ï@‚ü÷‚9üC;i÷à ò°á G9EW;·€ö×.²#aÉS+à ,Õ‚<Ð|1…äS…:`X(VÁëôñb[‡c[†b'±•^!@ÀF-£ð†{‡É3‡9P‡Ôâ þ¦Hùàe2n˜`@Iæ)Vˆƒôm¸‹¨ã6p#7ÿ»3ˆ‘¸ Q ¶0V_GJ–ƒGE!ËD-að„<ë±y·sc M6<ÿ—Q×9‰Ã*ÀÂ8 à8,ãŠaCPóð7j”Àf4Cì&U,Âa—R—RÇð+‰D‰ÈwK`ŠrAGS@=¯2:Ôbod @&S%QEͤw¢qQ ûq<Œ c»@%z3%)P8Ñ#AÒt’”füÈh¡2S‡w+ÓQ úc‘ÔR?Ķ2ýØ$)9Y’Ãä‘õhh“;clGPB7™(¤B<©á!K5H”OƒhUAcE©”8BŠt!Êh$Mé”k‘Fÿ"•S™U© W‰•]•A•]¹6_ $a)–Wq¨æˆVIy&:fÂK™¤/l@–¼x–Ðq‚jl9Zy •¤KtH—y`–w‰y ¦GOERÜb…NÕâw×rQ·×˜”ÀnïT™õ*µÃ)0_)@Œ•Ùw¯BŸ y=QyˆùŠé à8rõt$…€œÐe°V”ÃI¸y]ªÂ“MUVðb……l`V qp@T&I†pUc D…€H j“f€´Ù|b'¬QÀ¥‹ðh=rÚå\«Åž«ò- `9@àZ÷àÓ• ÿÕ…‚/äå rÚ^—ž)#Ÿ6p˜æYyÉeTXã ïI r—”}4š¸+ôY°P! ¸à Î`vÁÖEÀpj ª ê Z›èyqNÀ7@p¡©Ÿ¸°h€Y  £z[:Úu{Æc, (êœô`Rþ’ ö\;£·à€3J£Ëa›«LðО¼IIv4 éö]ŠŒßõu@rÉ Öf@ú Î€WÆÅ-RP Hêf” b ¦@¦³™¥±¥¦4ÐA; Di¸`t[ÕUP¨7 iʉR3r€ Ëãh:‰‘ ó¹rMˆ¡0`1h°ÿºÕ¨äy†:ç—$ušApŸ÷¨2µä7¶•9y‚wÎP³³Œç1Z˜wO)±*·pĈ3i—®Š u™Kaf´¬5 D­É…7ð Öº~¹ Ë®ɪ7ð¨òg®‚Š­j¯R}õz­¬@Œ«‚¸ZÓzþº劯 Ä #ñ~¥8PG9*[;øR¬Ìú%¨°pk”yŒ@)q ”7x/‹G8qpz+±ñ“š:š:b­FRq©þBc›jr-çÃQÓ¬'JÓѳ¨Zhæ 8A #'K˜+û”I`mÿJè¦\(³@€©ub|JPyš§ë ônï†RY›@æ0ä§ï²x ²P; `ÒD&Šjª¦³pb2Vdîy Pzy] ù  ÑP¥|±v·q!·A·|–2©¶°Ô²·$õ’«–¬3¸«FÔ€¸N ‰‹K•"äŸX¦*׳¦”÷®Çy{ê%Hk – ^  ¹ˆÀº´#+Q¥Ì59+ºj-ÐY´;RVO 4$µcåºÔRGøÙ- €zöé8*ö¼hp *ê0bdU¥j…ª…ÉKHð¶Á{°Sš«rP•g/‹,ì¶«ðR7{ˆQ#  ncP÷{™«¢ Hì¦ ¦Çh³à–`s «Gi…æ‹èúå«À¤ÀÀøáÀ, \LÁ‚`ÁïÁ ? ”!Ì!ìÁyƒK‰Â5RÂÃ;ntpsec-1.1.0+dfsg1/docs/pic/flt1.gif0000644000175000017500000002152513252364117016672 0ustar rlaagerrlaagerGIF89a-ÝÄ¡¢¢ÐÐа±±‘’’ïïïßààrstSTUbdd$&&356ÐÑÑÁÁÁÐÐÑCEE‚ƒƒÀÁÁÿÿÿ!ù,-Ýÿ 3dižhª®lë¾p,Ïtmßx.å|ïÿÀ pH„íŠÈ¤rÉl6ΨtJ­*¡,ÄÊíz¿L¬JØ‚Ïè´Ú%FÚQ@B¯Ûïø¼~ϧÏû€‚xƒ†‡y…ˆ‹‡ŠŒ$m'–;“^™]›\VŸEP¡#f¤S¨RªQ¬N®?£/¥k·¹j¸©’3°kÁÂÃ<²2ÀÄÉÊË)Æ1ÈÌÑÒÂÎFÓרØÕ/ÐÙÞßO¾Çàäå`Ûlæêë«âÏìðñ¢îÖòö÷:ôÜøüýï¶ãü x]‹njÓ—N¡C~Y |H‘XÄ+jTsqÌÆå:6IÒ›HKÿªl…€µ&¤\IsÉÉ "0hY³çš›,àé³è9†)0ÀE€ŽÑ§V øAj€«¸fBÝjãfˆr†*F²hÉ=(1­Û|kÛž}K—ÆMŸ´Ö}z·DÞ½€÷Åë1j`}Í.{¢â‘„©èm,ñb™ž(㳌™0¯(4›(°Žó§°—‰ Zši«'¿XÍZvæÖxa?VÍz·kß¹WÄžB[´mPÀýêÆ¤¸æãéµU¨—õáuiAl,ŠÆEm8Š‚ŸKHgpâ°¤-›¢h)—ŠyNåç&¡¾Wä‡ðÉ$æ©ÊKb¥­ §؉!+¦’xš¢Y¹òºëšnÊMÿ&ÊŽ0,«v‰,³D%‹—³ÝQk¬;°Lkë—Öª€(¸çv¶-f­J—®¦ð.Ë©_ä~ê¾æžØç¬_¶—aܼ;´*ê·'D¯¸ü|›ÅæŠJI)·k,5Æ…0ÃìꙤLا¯«í®+ÆÑbLç£(ç±;´ÅÌò$;¬+¾Oœo¿š@ ð½îÕ|ÝÍ-å ðÎÚÞWò­'Cl‚ÊW-³d$4)ñÇ'mõªBÄ÷uÓñZ<²ÔZuÊ,SÜìÖN4ØcŠ='¿ÎÖQ3¤°°lQé˜cw·Ö„a“ÎiœµÒïæø£‰C±šTOÇËsá.¤)-ÿГë#7ÇjŠcà£öäÌéô˺e®8ÇO•îâ ‹§Ý©ÉÊï%÷¹œnmêð>^9Þ‰Nì%œ­º{ €jlm„fB$Î~ŽÒ¢›Ö—ýNßú¹‰×¢¬ù3ãÐw îß¾>ô¯þ%õöÇ?°)Û›[é>Ö‹^ÜÞõlÁ7õ] p°Â‹èšÅ<̨pä\—7€ & Jšˆ93àL€ZÚX7·†%ðp¹Ã åÒã[¤qüP‡«™dEDÒöõAÒl„Í+aKá½fm…Ã"_ jñ‘yi†6ìk¶(;ð«‚”÷¡¹ÿç¡ëpÛ æ¸¯Ü¨P›@T|ŒDIÔP…(_A¤h)5YÏïò”û¦Gž,®é[]ÜáòÄÀ9¦•àr* [ÊH° ê&` µ$>7Š…$À®R@åà5t¸ãÌs0cD}Ôcn²U)Êj‹ˆ”¡"½è‰%) û!%%É®É7PÀ&»ÖI:Þ]ïbà( HG>¦ònŸÛ¡,+æÂX’f[´dàônéª\î _$'å*Cîc5¨œ@‹Ù™»@à/ºZ3Cy¿/I຤"«É.TþÒ:­ì¢`y–˜œ&ÍÀ7NúÙpwvt§C™ÇK-FÿpItg:àÉ-‚ý‡h"*î 9î³%Ÿ¡CÅ¥¦hŽb޹ ‹AU)¯å)t|Œ¦r6ÚMN•%û›CýbCr™ÓŸUä—ðѶ“h#!)CŸšº• ü`Ži‹SÚñ—ÍXFKé –"vØö´µPI± {¨¹$êÁ¤¢l©vuIÔt^íÉšD0yg©Ûå5‚.ìª~<VmVEZ1±Ì<ât¨%£a\'°ºÒó€.œgÈòê”ø²žQûà_±8œyšYuPGµµ“u¢°z#Ùª¸Ìæò`ï®§ ÛýMÔÆéxñ;2Ö³ÿ2!¨[ÿz7ï•V²ô¬Q­ ê9¨FvY‡Û®R±0€¦Úí¶€6¥Z‰ªúe¸TNQ=2€˜ &—ýryÏ#€¥€­­?a±ZzLÛhNCkÈ©¡æ»®)î\˜3î°ö¼Y‹'y‹YQ-'g'5EGïÀØJ`"Y´Aê*šÓq¶ æb19ƒ¾’¶¹Hp[sŒ[K:غà…ªêðÙ!£²Â$-†y—MwŠŽÃì$Ÿ^ýÛ¾ÊÖ–EÍQ¬Ë{Ò!:”/eðbûŒ Â%`ÐvËé1ÙF@¹v£&1„0„`‘À7œé:T‡7ÆÌK²)ãewÂOæÿ)NåË×*Kà‚æôœeâ=-K€ª¶³—ëZâ¿ô½)6óm7*/6k6³‹Í ¶°iwLͤޟeXBÀJ9$Ž/l†b;”8qŽ d%»VÐ'£ÕÇVnEzÀožèÐÒÊ Úa™ÃÁ6íÀsB•ÖuîrO¿\jžBÇUñ59»ÑFºX³Ê2"„;nKÎ8¸Í¦N?˜ç^ïà À<ƒ¶]{–n9̳{ÎÚ‰f²À©½ÄËܵá¶i @ÊÒ¡`ß’î5eOÒŽV“¨×MjƒOïÝÔq·™Œéؼa®8^ôͺöfe'nýª$ÿŒ×”Ø|×G£Т¤5üá?.2i[›ã¯Œ“Õ™NO*7ŽSn÷ÓO×Êçølo‡fÑvpyXšrv]“w£`9˜YÙÂÏ±ÛæånXðL/y Ý–…½·ÐýœK žK%y‡ßöŽ“I`®®“Wpc€eÍfA¯FcYÞ­ø×ÙøÈ|ì5/;±’‡‰‹|£sÄ­·ÿÖh‡:z¦´—³-4÷—S›ëþì­ßk®¹› )œráU ö¯?4謮'àêW&E™?­oîPÿxAænÔ=È€\…,%})#®û¦${Á±Ë;˜}?Üïà%.rs/÷`ÿþÄqO÷M¸—]ŸVr$†xr7Nè†VPp—w؆|~4GbÅ ä§-©Tn¬¶sc÷b'ª’®6N_Ä÷eÎ%à‡néebÕlaq`¶w|.' $T%%ãp ˜uh)§i^7S çO?{)wss°M'É;sôt “€*vO»§Y_3h$ç"ç—zx7=´×M(fõwl”lÎU7—#hx¾W0Ö÷-5–}µâpaqì¢*5 4ÀJIØ^ç·ƒDÕƒÈ\I f„*†„òår4¤†fxG…wsÐxÿö¦ SªRcS;MqO àF¤8S3…3ݶ`[h¤õ%Æc|'ø%L"y° 'Š4…2¡‰‚‘·‡ÔpÖçh@(<µç‚J£Sí% ÂmжRÆ3I¨>â(ŽäG¾h AF–Š;FZ¥uOŽØmߘw”ˆ{MØ^K¨j# IȲd«xsW+ä·0Ñt:eqH_ñiƈ,Æs†€Ð g;°õXŒ‹¢˜OwæO¸‚IÅ‹,…’ÕènàzبqYd:ŒY'vtFÈ}äw‘¤ÅfXˆìŠðƒÃèŠ'ÙâVÒx, Iù¨„ÿ0‹¦ŒdêâÖ„_G{9‚(ZUø`ænn¤•\w‘w&†)†Î^ Ùƒ´¶T{¹QÀÈ‚LX®æ’›X†A™wÕ8=ú't$øOA–:)O¹Ø“<ô{©-ˆyORF—`‡‚Ÿ’šˆ’È"†Û*fp#øÅl–<í‚•`‰†  ˜ˆù%S¨˜v©{g6‚È…53” Ä…&¨˜Ù•x_ÒF9—¦°‘9N€—_ŒKh’+Ù—tð—D…xi–;j“à'–©YŠ T‚þ¤j4„ƒÇqÃxg—¶H–é\/‘™ÿäpƒc×l ú§i 9q’ÿˉšèçpT¹ÊTr#Ù†^g A”µ —Mxšõ1læ|iygC9œ÷é–¦AF ±(†ÓCyù%‘Ð!”[w’ØY–‡[<éj$¹”>7= ž1‰—¶”W詘ڙ~¦@‘¾—&”£X 7—^é‚{ùOcGT@©”5)†ØITPi ɶp&`fßÕ“y‡†´¡AZ‘Ós‹wx¡Ô¶ly YœU´“yŒ÷Ä\&*–)z—³I‰éç£Ãhy~) O* @U8Ú“!h ¡@JžÙhdw.¦ºÏI“—xiºøu]šwÚó¢Úèsÿ©ÔŸÅ¥¦é™¨)À 0›ðçPaê`1x`ñœ  p†ÉÓ¦;Ç‚yGUÜ9= ‚,ÑÇ‹'9”ù¦ÊX {*¡Ífˆyç•Iz—^É ~ÄÚž2:=͆‚‹j„­ÙxJ©°™q ù¸…èdz¡ù7JJCâÖ0Ö ¥Óó¯¸ù%ÎX~Pg¨¥[× {¼ò£…Å\A†—`ÁˆÓç.ت䙬¾—¦; §Ïªže8­yœø‹Ù°ÜŠ›ÝÖ„ˆ™ƒa¡ÀŸ?v®ŸÊ–º·Òy™k”K¯û ¢sè ýJC“9š½7SÿÌ•=‹¨È2£™¹“¨Zdýé‘#¹•¦©fÊ­è]¾ø"v¨¢Ù ¥Úhÿ)³§ù°w¦prÊ{vù¢,H­,Kœ.Û±öÊŠ1ëuÛJ³FÊ!¡a¦æÊ‚|š® d}ì_pwK ðœ}J¯¸ µ0y‘L¶©Ê”°/‚[»sKªí˜°Kp+¶)Ê«  ^¹QSˆ[ЕsöÁÀ¤af±#_–·„+·{·z·£·¹©ªŠ[à­qù·++–$‡­±Ã§Cy ¥wY¢ŠÙ^÷ôŸ“Z“qàg窄J{q슃çû"4d <*—ÍX¯ k©šz¡`{°ÿà“/ñ‹ª;@ø¤\[¯E©—;¡Ç«‹˜§{Œ¾p 0•4Œ;° ˆ`É\ÿª±„+–Yšw» "ënc„šk¤_K¹Øk— {¢zÈ’5{—š(¤Ð —“säˆ<—¸Ç¾Y¹TûsÓÃY±h–ú ¥ü;ºþ{Ààj'1I¢¹ˆ,³©”^·ÀǶ¹i™`‰\{ /gÀ¼+uQ¸š£Á[÷… м«±ýÉ…e ²íµ‘?†’ §šÙ³¡ ¹„~û¼5 ¬]«¹=üÃvO6 qüy’ p_’5û‘@{†%XŠ©& Å3Ë\L{—…ð¤ÿ}…ôFk÷D¬qÉY÷Ä´,È^§!þÚm`›¦Tj6¤ÅÂjq”¨šL¿€  ßYÇw·NëÀ“»Çw;N¸ ºVhK¦H“œ,š‡LÇ_×Ãè @º¬É§{M“jÆ»´Ðû­0ÙxL_F–û£ã:N,Ÿ9› ÊIŠqÚC¢†øe J!­ê“‹¸¹µÈrĪ˜•\3ÙZ‘Œ,Ä× K¬öAÖ6=¹(e°ìpÕ+ˆáK¾3  €†>Ë“Û÷ÅFª¥Ò:šSÓ É9†¬˜€¨*êuÊtÎ?ªbtgY„ g<{O—\žk:Ïο‰Ï7ÎÿÆÓÅ~*·ÖØ^ÛjóFš Û_WÎa iÊ©ËÀz‘†HÑÉ[ÄÏ+´zÉl–çÒSÊ¥ZÙý‡—W{›úçÒ…ü¦—š¼è‹Ûg‘ÚK/ÖÇJZÎwÙŠ(Mš2Ñl£HÔ†H“|ŠØ¥ ½wÊO¯Q=Êú'¢£iÀÞ ŒlV§ºì™Ö’{†ËõØ(¨ƒ‡Ü±Å_±™³³3Jµa /7usš¤x­¦9Ÿ9Šƒ>{©Æ£pgÉš]ŠŠ½ž´©“>­Öä[»I:[®‰2E=SªY_QÕèú‘*8SˆZ€£©±OÜ«tàŒ}ϳéŒHwP9É´ú­ÿ'|Ÿ»QéW³õ{ÛŒMœk ˆm-ª÷dyâàg 0…Ã-ŠîXŠ}úÓ)}°,»˜Ï;³[×Þ—ª“í¿×)¸my¬·‘›³É¦¢a8¹dYZÖŠJ¾á\Œ(Ošm¤û+­îý¹&*eKY“xg¶fì°Ké¾ ^h È­F8pÇ]Σ(¸?´ùÔ@q»T¢k5Gà~î7?f†aá‹–Ùây=¡Ø¦×`<®š¾`â¼h£IÕ,ú] šdþ-L_<Œ’ŠøÝîFasÞ–(¢2k¢š{Ö_Ù^mg™iÎL§ÙÂW£êcÑ 4A£Ë%ÛQ.ZS΂Vþ¢lŠS[N°ÿu`Bü„óÃkOÏǹœ(]{j.³å£È— y¥DE∪RÖÚQ¾?G‹™%úâ\þi¥åÇ‘†x¥¥|èýý˜Ú>>ã%j¢¢õ|§[ -μˆ2Rq|U0Ü ¾U·á&âÊŠVîˆ*ža¬å„kf±>€¶ˆ–àj ¥kƒW€œ}œ['äÞZz~›m¶é•ç'â1š³"~IåÛÚ]Œ±”V£ÖÜ­ØqÿTæ°Â‹æûh21›(Ik¤­Ý,{ž“«•îåˆ[{œTùíˆ$î”c ŽíVI.Z9¸³\.ê,³«øh– ôÞå!„VòC^˜WwzŸÒs¨-ÿ5J°Îw`´÷ªˆ<¤dðð•=’2‘ÒõWójìše“i´`w W›¦,9vƒ7‘.q·>=ÃÜ#©X_ñéï-Cóá-ƒèú•ìbq5VŒµÙîI ï£}ÚZÔç ø~ Þ.¼$_1*r1¸ºk8S€ óÊÓ“&¾îžÿ!ãyþñcë±½\ð„JØ0£Ç1IöÅ$Gí—ö-ZP÷ÒeŠ'Å0‚Ôöñ7]Šóè§W6V¢>êûlåBïÛÏdâš?׺ÖZÚüh0¼ƒîtÓt\Z¢Šˆ{¿Q+ E0€­·ø œuÍ$)¡Lx•œÕ˜è ,é A ؉)‰IdA¸¥¼Ü´É¥JNT²èLÜÝ!¼ Ê¸h‰ù¸-±<Ù@ž š–ÈAY^9ieu©…=ö”A£(äÀ‘?CM Ï€½HúÍÚ*–:¶•\²àPF@ÀH^¯š” w¤-d¨NMàÒP‰+.Gf„£&h[Œ*cˆåÐ씑8Æ–ÿ Q6$•:þL‰)4íPµ ìËæc›Oý‘‚0Ž\dGP8{$ϛ4ê6{¹ú¥rôcЍ”ÈØpWðã¶%ÏQ5&U±šº*iNcÛ `’õUr É,‹Ð9–ÒH –«ü±|[#ž‰K4U’‘·îÏ޽!rц(sG“UÉ÷ ÄÈV«¤}œ+PÖݺþƒje^ƒXò+ª¡–†©\”þ;´!IIæÒLVö¢Í‚ÿö’:Cõ™`‰gÔlé7 DpöØˆŠ®9gˆ,ÃÍ @9™€€÷!wˆníÇ|Ó€¥fUYsie:±M ÿ€ €§Dm>8S‡‹6Áp-ñwTQÄC áBgqtÌsÿíÑ×tç,¡!‹¦mUF0~†…\æa#Ÿ6‹ðÄ {ðq†‚5b CTì×–ƒ2hW•T:D.zuEÒ6Ö.I0a…p¨ÕÂ!]qØý@ä2ED£˜ ¡'CŠu4IÝ,JÕaz©§š5M¡\À쨓ñI”¼à Cú±wÛ7åÒ”jPj(@þÎ X‘ZXhYDw_Žv =dšÙ\in:’œÑÁø’’þ4D‡v{–óëŸ*•vž2KúÖ‰5$¯öö”J’? ®™*cª ¦:ž¦)"™¿¾I$DàZ'‹ˆn'ì9ÁúIüDZڸdàše2»è¿Ž²%m’½Aõ^SJæ§~ë®+ áК+«‹êŠ Q„ä¬0_g§x© ðÂ`bH­,œ«üæïE K&ºSÂ<.Ë,Dn4šV˜ÆG1X­†|T-¦‚\O¸ðN´™Ê+Ãa˜ªìJ5œh/ÌÂÈ¶Ô G ´LDçŠðð웟WK‘ÐlÉ¥,†šó,|w5[”£¥õä¬&p!E Õ!5#n}¥®ÉãhÉu+ŠÿŒ,l«]NšmÎΖŠmª¾xß@¶†•ºÅÀ~Ë:²y¸¡0€ ÂÈ4¿œE 1Ÿ^$ÆÛ$³Ið¡Ñ WqLˆB×à†º¹…^xîÒPp5ÉP KKÎÆ8¸h}º0ܦÖh輯ét‡‡èÏq\VÝlǪÀW¥päs†Bˆq—Å®?ŠÓH¤¾ºQ‡GQcö>¦=Q­Édé&1<–¥ëe§ÛÜò'nA0W Ù•âR—œ2IPLS ®gsE``wÈOòÞ Ã/„o(X¥e3B/>G‘šš´69͹pT~!|ÂáÁ"rFYð‰ '·QÕCVtÚÿ oÀÇ® òÍT4ûÓähBÁ€ dÚ#Ÿ•¸Å²I™Ù³Ä ²Š;ƒbºE2ª%êŠ "¿º:=ÂI­#ÉÉüfFˆ q’k— QBCŒC8¼Á {¨´ÔÁám<"“ˆÉʽL Nœ!·‚÷LÑc@Z‚Å€„ÊF…lÄ€d9Þ‡5ªÙ,^…ä ®-à’†/óÏ(½HÇ-2Ò‹«l`+‰‡ƒF-1–oÜÚ^²gI„3›¤¢á€dElšr}’"#—R¯göR™Gc"›ØF¯šTZ,wÙOn>Ï›E‰áã¹5aò~É$è@)ŠGGê²2Š2¦¹ÿɰñŽŸ•å?C¨X­ïƒ§lgFK÷gâ`eˆl¦…(F-‡Nnyt‹©Dñ¨´¤õ3H*•'ù0?…^s–"M"I'²Ôœ¥4ž‰š%9òé.ŸJ5›¯giZJU‰‰¢ÖëE‡:L›†MŒhuª/CÊLob4b ;ªÅ€ºÈ¤&* Xe©68Á®n-jîââ5™eWW†.žg-ªú ŠÈuÆÔBŠÛ«íN'W¢¼s+UEˆM«z–¡mèW ṵ́‰U—…m)0gªÑbv­;Mí$ û¬¨0.ž„eçjç×Ö¶®–Ž„a ÆÙ×Òî–¶^„¬BËד™%HëëPWšÿMbm KnliÜ©òu´eHq ÐvJ´·ˆMmYuk)ÆŽw¸ÊåhAH¶¨”w»¨ínj¿ ÜE6—¢Æía(Ý/Ù”²®õ¬~qàéÆW¨Ê%Aýš¦{6²L‡ý-‡…êß[—ª >®zLVx¦Í·°Üh„Û߬²VžEë†QlãÐ}X´¥ GÜž=Î÷­?…=CËÐ8LWÈ%¡z÷Š×Žò—³ÆfŽc Þ[ÁCV®‚]|äýFUÆÞMð”Kãêv˜36]•Ç»ã1gyÉ:¦h}™HÙRl˜r“˵‹ãmoó6Q 8À2kïz§;Qðv÷¿ îáy7àö&÷µkìøÛà‘íö³Å-ñcw6×zVx¼-®ðÃÊâHi¡å‹{\Û_ùB®ë€o;å¡ÃK@@Ú,'8ÆwîsÀš:áb¶·ÌÓíð@ ÿâ?ßuÏ—ÎòŒs’ -ú›i=Wn3ýé(wºÖ™½LŽÇœ×kn›µ¹^m^SÝìwuùÉŠºò´Ã‡!Ô†»£éNdµKêL¬®Ýyþj¬Ã|ç}ÇûÒQYŠ’â78F]­öÁÞçIûÛøËÿò‘üÉÁ¾ø‚7¾êß<é+Nù­ƒóg¯|é[Oêνã–Ï´çµîúÛ+ûôyŸýêc/yÜ¿¿ºç=ÑU¯y•?ù.=ñeO{ß ^ùÒßâðSoýÞgõÓß>«m}ýâ??û¨ç>÷a/þ¿_¿úN?>ù‰®~ðgÞø„gûcþ~çÇ?ü·}ý¥oþý£ïÝÿßïõŸÿ àçåöàø`ò1—ß`ú%J0ÀØü5`V‘öÁ_a€(@è­Ÿ’àJ`¢@ ÇÐ_¹•à î^¼‰‡ÒÁAÈ%A"¨þÁà&`¢`@†¡â_aÐÆ`2¡Õ `°q] 6a²Ÿ¶ÿMaâ[ já2ßB¡ ‚a^áëÉÞI¡N_¦áè±aÖêŸ" â¡ª[ò!üå¡úéaÆÛþá!Òá *""2¢ÿ½a#B¢Z¡ Fb%¶ß$^›%j¢òa"nâ'’^'*(’"ç=b)¢b(žb*²ÿ"*â¶b,¢Xo­Èí¢ÙÝ"×å¢Óí¢N æ"–,–Ý0Fa± €ã" £*Þ1"ÿ‘Ï-@À}qE6jã6rc7zã7‚c8Šã8’c9šã9¢c:ªã:n#¬¨Ù«üÔ™,ÒãÏõÖ2Öc>c´ý@/²\$]éÀ|Èø£Á d üÀ<þc€ô£Ù-ÙJ”\ä € âÏ)£|€ÃýÛF.däÊqD&@ @E A Äœl^,À¾ ä•@)à]\ ¿Éäæ%@Ì‚MÚ£XÀ%%á@@dë]IKÆQä]ÉÀ¸¤Ú‘\tIQFÒE ÀGé.(eDÄ$Cî\N:däøEW²›6ŒX.ݸ%@X©a{Ð%×UÏ8Ì¥+êc_>£_Yõ¼ÊÀ×Q¾`Î!b*Jñœd;\ 4E\¶"4Vâ,¿1¼B2*ÀP$ ,€ ªÁ(€b*¡iêÅ @ÀPêÀœËxXÄ,X j2£bþd ´¦ €†XàlÌŒ]_Rf$æfÍX‚(@! œ@9ÙfqÒ£,ÃgÚæ<@vþ[ƒwfg;ntpsec-1.1.0+dfsg1/docs/pic/flatheads.gif0000644000175000017500000003143513252364117017760 0ustar rlaagerrlaagerGIF89aß´÷ÿÿÿÿ!!!)))111999BBBJJJRRRZZZccckkksss{{{„„„ŒŒŒ”””œœœ¥¥¥­­­µµµ½½½ÆÆÆÎÎÎÞÞÞçççïïï÷÷÷½µµ{ssskk½­­RJJ­œœŒ{{scc1))½””!!1BJZksŒµ)9BRck{„”­µÆÖç÷ÿÆ¥œµœ”J1)ŒZJB)!kZRcRJB1)sRBZJBRB91!Ö½­„kZ¥”„Œ{k„scB91B1!ZRJRJB”„sscR1)!ŒkJ„cB)!Æœk¥„Zœ{R½”c”sJ­œ„Œ{c„kJ{cBÖ­sÎ¥ksZ9J9!cR9­ŒZ½­”µ¥Œ”{RZJ1ç½{ŒsJRB)„kB„{kÆ¥kçÖµÞÎ­ÖÆ¥Î½œÆµ”½­ŒZRB91!kZ9cR1skZ1))!{kB¥œ„œ”{”ŒsskRRJ­­¥ÆÆ½ŒŒ„kkc991))!!!1199BBJJRRZZcckk{{„„ŒŒ””œœµµ½½ÖÖçç÷÷ÿÿck{„)19J!!!1BÆÎÆÞçÞ”œ”)9BJZksŒœ¥µÆÎÞç÷ÿk{sBRJ¥­­ckk9BB)11Bss!RR11!!RR))99BBRRcckk{{„„ŒŒ””¥¥­­½½ÎÎÞÞ÷÷ÿÿRZkkscckBBJ99B!!)!¥œ¥çÖçZRZŒ{ŒB9B­”­Æ¥ÆRBRsRkR1J{JkB)9µŒ”œs{ÀÀÀ!ùÿ,ß´@ÿÿ H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jLø @€ 8pÉ“  ÀÒ£ËþbÊôGˆ””\ÎÜɳ§O™ DØH´¨Ñ£/ `0nÙ'’2Xˆ3_ùô‰#¢V¬TôY»ÖŠŸ; p€´®Ý»-L ?§UĶeÛ‡ûø!üJ¶‹ –ÂDfðj¶®g˜Q=ÓÓâ¹§K¹((’ÀË VhB ÇþYyëOëÙßg94óeO"K]óÖmÛ´kÖüp´¹úF # ”Ž …ŸD’Ô]ÿ`F fœD„ùø¹’àƒYÒsÙõ#,¼``:tæ¸ÙbA&ÅÖ˜ó߃Þ ŸuuA 0°S%Ùþh!€øÃ„“l!XdŠ Æ‡"2E"AôÀ Eä¨ãŽ>ÐàϰæÏ A aC7áSÎ9ç˜SN9äˆó6 4Afy4íÁ– (,áO"Þm!‰ M4a ¡Õ‰?UL’…VøsKLì³@fAàO;æà 31C/à°‚?àã 1è C <ð5ÄÄ‚E !ƒ ,Ä œšã0È„€FDà@ÿ #²à qDB‰LK¶˜àÅ„BcbAAßL&”€?Ç$cŒ?‘ô†ˆkÅáX`ÁUÐ$ØKTL2è&3Œ?84€ < | ÄÀ7þ7 }”óŸ9ÝxsÎ8„Óï ŸCMXjtvD @OŠx‡X=Óø³ØP,Æg50ÀR`øÖZ6ñÀ0pAP€B`ÃO‘ G{æÝ^š‘.ÀÄr?ôpƒ?4ÑÃ*$ÐîF„cÎ9 ‡Í°9á|CØb§ýt;×Õ³?·È–ph!«¸Rð!E¤ÿÅ”0GÞƒ 8Ö¶DoBøãƒ<í°\SóDƒþðıþäpõ ?UÑO‰“öÂéø#ðé§—šYà%¥ ’Š'ôÔÉ„qWðñGh=â%ÌV@ˆ?¥¤¢¼ò¢øC—B`CŽ<”ÆKºÉäòüVÁ´üF .ÑbË0ǃ öìcÿ‹?3„€7as?Ý M¬³®?¢ÓҞŠS”"D ZÌwa_Á þ0ÅòJ‘ ¨ 6E‰Þ-€Ñ _¬«í !ºŽ‘ à"á ѵ‹4$pë°þX\–àäè>ÌÑÿ|°9!ABÂP„¡>0Uµ# ì Sþ¨)0ἋXÀVÄXaáŠQ…¸0€’¸<`!óÇÆp‡7ØÁü²áÂÆ±­¶mFþ˜^hÀ@á ;¡¦†‰Ô ?ˆ$æ°Ø¹!ô€˜“ zPôà“9 2a»å™‚ˆ_Cø³ ö þÈ…1°wŒX®ïŒ"<.RÙ 8ÐÆ †bs yÈê0‰˜@rPÃñ†b&3j@ܱF»Hà)=ÀÖ’K|"—0(¶Õ‘DÀ  €€$@HE'êPÿÌ&¤³”Ë ¨@—GŠ¿0Äc¾À%2nq‹òá²}Á@7YeIhVš€F¿9‰.4*8@•&0Q-íÇ›ðÄ&F1Жºô¥0U**á 4¤q¼ð0€!ŒbøT;Ý)/üq®‡¦kZÏ3©R3€M°Â¡ˆ©ò.ñŠSHµ¥Ÿ¸DL À Àà@à‰&ÜA ÈÌCZÕ ˆ¡˜sHÀ ‰4¨!(D5ïº5¼\ª`/ò,LHõ¨X§%zUjÂ"ØÒLf À®kȬ1•©x<`%˜¬F(`Ó 9#‚R5LXÚØ>ƒ¢-C*ðÆ ÿ%$ øˆTY! HÀˆ),€Á dT$xXSŒÂD€meK]„X x@S(JÙùcyÀüˆ¿@è2Эz`ÓDAhV 2Ó¯bƒ5¯©7ôc@uŒ€· î!Hð€ ¼8'Lá [øÂΰ†Mz!,àYœûX"ª°@4!&Y˯ª“.¢ V°BÌàÒ„YŠÛ°Ž/BP@Xðü£õƒì€|þ0 2õÁ ‘ÑBÈ@ö”šxˆÇë  @ LwÇ`6 p…ɘ!*b ˜ ÿ‚Y"P"ïÀ¨WÄûn˜‘rOgÞX&Yˆƒž…+S71‰„ÖsãÈJ'°ýǨ!ÃtPb,’"NÜ“BKAKàƒàˆt0l(鞉bpÉàÜ û‡I…ˆI^ûA=(Rƒט =[( * j‚`àYÛ¸ßú°7¹­(Ó~è‚©TŽÿÍ^õ]àL"_"PB˜À™W Z"R€çµˆ2δ@ÄáwV †¼2ýÁVðRZ™.d¤±â@ (ìAÛ 7ú%là€>ˆ¤^€Åk˜®_昆5¾<:( ×2‹uL±@póðÐúÀóa ÝY‚36€• 0  ,êƒ$~B€<% `À*°…ø Ɇ?âH¦sžD˜°¢Å’–¸ˆ4Q(0 qp¬îGÆá‚ ŒÃÒÀ¤AkðOâðǪˆr X ‘‰$“þÚ;G» ŠÆ/I¢Ìqf ð·À…“ €@R6úÖ ÁvpȘààû9¨\ÿih@þçjQïA |Dµ H\‰ÞëõøpLƒþ Gñ‚ÀiDÉF ‹AŠcLW`W PÀL@V€\`5!&„à„€°Ïp¶b\gþ·WC¸` ½ÀÀñ À ÀÀþPßÇ~žÑ ” ¾0#¡ôƒå«“þ°tø·0c3òE@ Êà "ð2r£6æ`7†ãp UPÐÀ€/ q]Â0 r²0T@å ½° ¼  ¹À v¸rÉÀ » H€V€ ¸pKU ðÃ7“G3h“„‚ ÿ;VÓ“#D¯ ¤Yplz³’°{PP5 Wæ×ó £‚?è;hT´¸BÓ"` ¡¦Ç:Ü€GBxuô— PZÖáD’#WCE9"?Ó96PEpDRt5@ÄDBD§#D @ñ6±äP*Ä ¹P‹ì3-³ç`rM°ðVÉ´Vj ÃxÀ' +ÃB’ð ,QqEAu¦B<ÐÝçP¨<Ÿà<p^=fr@Щñ ©€ ¨` "9 þ° Ÿ  —À ÌõRÐEå¿€KÇð>½€Žé².þÿÀUqüÈ“%å`PeV÷xùhWt°¸TSØäÄ VS$É ž •¤0 UÕZË“ ð”q!Fbt ·ˆ6©Kð'nKe–_ –úÄ tP$ 1a@I vÅV) Ö¤X€ êÓsÇ@ ÂT½K3Xp©TÏ¢ U Z嘰’©ðXÉ–¦@UÍ#î’k Ð ª)†Ð¡1 rup` ütMH¹dÐb™ÖÁC¬…•þ0—w™ ¦@(`;ùFª¡ìÀ `/f(œ À[±ó¢S¦ 4ÿE™ÉÉE¸ˆÕQ€[qãô°2ÑaX_ä ð @3X`_w¥›j¥”~52b@làŸ‚à’èin4¦(ç“ÑI%ZñÝLAçŸ}ULÉ%… ` PŸªT¡^%`º¢4Z£6z£8š£*0:4Ç9~EóxÞ‘¨ÉÁ?zc—¡£v3þPbW&ðX¶°`z°]Ñf€‰P4V`(`e0“eǼä¤:æÐŽÒÑ @ŠÀ-à´2%#FJ&|° €QÚ D }°b"3Qÿú¨éœpJaªÉCve60³hTÑiÆ[@c{“p–|yç3á¨9©4ê à1— |°Å(àÕöi)ðp`g)@ÕÆ%Ð+ $Šç©ªNJg(°kêA€(@ »" Ç' `Ð Ø@êáY0¤lw4%¢¢ÆjäFr=aVðÀ3G#fb®°&¶oÿ Ô€6â°“pl`Áiõáw#V°‘(`-ÀøçPzð0®ñ¶"+ÐªÍÆÖ 0à+"7pÁ"‘ ‹áP8»“Y=‹É'f°Uàe4f5A·V€ Ø+0 36&ð,LñŠa+ð3àŸ492¿ºRà`Kà#p ` ÙÐ E˜ .”¶±[ ¶‚© x<+¹¢(Z“ Fà@UØ]@Z ©ÿ`”pŠ2,@t3PC0JIN„DC >À3Aï)º³¥V°7‘ñMÊÇWÿ€Zð{Þ!Š“VP»ß«w`Å ‘“¸#,ÀIE½3T¤9@t;P¼E09ÐHÚ(EBðÈâ¾Û[Þ2=¹ü8îA¨“€)PDs .P4Û/ÿ²{¼0ÿ#Ö?§£ ÇqBÿ¦¬±¤ÖPðÀã#4Ðfð ù^µ¤YS%'̸ÂqþðC Ùc T7~! ( xÞãØÃ*¶ˆ#&(>ø°‚(ác8.×ãö>vgÞñ èóøÄ+¨`/X>öüØãŒ8H.JÌÀû8øO{ЀŒ20€-ðÁ¶ÛÎ×$ D@kìkŸ¸ò&AØSÄ@ÿ±­ {x´À>bÉa],à8/wŒsÜð:3€Hà0¤`EF¤`~Œböl' ̯H„L ÀDØn À@f##-|˜›Dé”$€òPŒ y€ À8ØùC¶Ó"ø¡˜` |`Ânç? °\%…*°ñ޶‹Cãt?Q‘ ¹0€á-‘ RÀœ ³×B à‰‚äÃ@QZÔb À(Àa\d+ÿA¹ `/UÀ^)m—…*äóƒà ÀƒXÊ6Z¡Q8+]ùÌ‹¸Ã‹ d%ùIÿÀÁvP„6·„íÄÁˆ|PD )äq þ@×¹¯gÆó! ˜Ä1û@…I€²R%€E\’¡ì8Í=-ÐsR „?Š'ÏŽ\à@@êf·¼ n,É‚ÀØ:ED‚S@@Ñ€  ÜHYªÑdÙ è(´$¤ŠTä"`‹Ö?. #ðþ˜ä$á‹’Ll!%ÞÈâbµ )D¦P¹D€¡NíHBó@æ  VPÄ|WÖ-ˆ.` ^\Æ€kl#áÀ ¸‡n²3(àjÄ: •k¨œt$ç8‡60ÅÕŽD`ÿ [y·²nw~° ´`΢À²ZREÐ¥€#±ç°† €GÛ¢\¥kB › I,a Ö8‡b;œ²¹€Þ€H:æMB{@¡Là ’O¶PÞ¶ïiï$&%LP»-îÁ þ(‡oÑaŽ”l¸©€ÆÖäš”{ˆP†ÚñA&¸]çö€I\!ÖDD€O·¨4€xü1ŽÄ‚# š-åÿxyGC„þç¸]öû•÷ ণöP‡ „ €8E¹M ðG |»ço6ÿ°ˆD(q$Vgø±íRH‰ÌÑ’Ùsb}kŽk¤îŹ@P€G–î Íå½+T!Úôƒ* n$tÎ^Ng$)€ë“¡0sø# šµ]"ÄyâÜIþ=G6R5f¦±I¬š±?š@¾é]Gþ˜Ü8@€VÀVÚ3hs‹¢5¤Ž€¶Z"p“l( -D‚”ä“‚"!_X£åÇ7´a mCÖÂ4S0ð€àZ°qq ¤-³‡Â†Z…P D"º ˆñ}»’ëΞ2gÒ„-²,(B½í-„¸MBÄ1ý€‡£ÚÀÝ Çÿ²ëk_tpƒZÕVJ™{æ8DX² ­±•×Ù>ˆÖ{[6¥#µ: l`o”£|@ðA|!ØÛþ¸A eà|$6ŸÐS†;Ù=ñö¯¹„,P‚ { p$çIâÈÞ¶2 :#<@Þ¹  ¬#?Hy½‡°Ø€e¯wVÀö²A†tYÐL¦‘Äp‹RHD³g†S2j GæÃ) ÁOFóÁêMR Ö{0„µ×›>H9"ô€ÞpoŠ0ƒEÍîõž W€ºÛ"e9¼•!eêwq8¦ÎP…ŠvR7ÂäÿØ>ÐK±€bPöì ÞD˜Aó‡ „HÃópŸ?`°ö!ÄÀ/ˆ"Ïv ° qúU=Cö¤…óaKÐ%´èô4ƒnœßöð¢ Pèƒ$€qŒb†^àj 8¹²‚ `ð Øm@‡ò žðÈ+‚!˜çË@¶‚ m8Ž RŠ«±ˆ ›ÊŒŠ ¸›¼q¼™›"3€€8iA¸!œ¹‰€‹Éj©±XBèÁÇé‘(‰]ê»D}ò8ÓY.#mr c ˆ_`h‚ÑC9  †ICo`ÿÕBrAÊ‹; p‘ðß⇪ ` -¸‚%P„Xƒ­ìy°ÌÙ-Ð@t0)P¬óØù E¨¥µ*ïñƒ|ÚEð` „ ¸€¨3½ Ì(ó­l`nX1o°kè†5´/rðxj ùAz „+€‚%Ø8HóBx®ˆ®$c«hœ(™ €RL_ ß …ꤒ„wð‡.¸&<0Ø‚@\X¸ŽØ“Øp˜,J€€¥7\-l`ÞB¸6ô‡i‡]D8ÅêÅá«‹  ¡á0ÙY³Z"„ÿ£2¯S¦`º$3ˆ"Ûqº"Pø¹Ãaˆ'£ÄdŠ8X‚µ>¨À¿Ê€à¿Û‚Hð‡[@‚è…(A®#J†XÕ2‡p±Yd3‡hx„}ˆ¶HH…´¯tð[¸ 29€?„œIØ'.@ „.7QD‚ƒ+H)X¯Š:ÆGëƒ=h¨Òi‘@[(€ï‚ \øÂ]@>ˆ(¨¦KŠ+¨‚¹¼,‰a0ŠºÖp€m€ ˜p8j@‡n(äo°†ñºJ…®‡º BÁ"[€åi›b„‚=°‚˜åÈÚ!0ï9¨-ÿ IŽ 8Ìž¾+°€”¥>£üð‡c@†[ <ð‡ƒ¢$ˆ¢8HB˜„%5:òa a@* Y‡tX‡ò‡r‡o‡oðnH‡ýr8‡p€eën àÂCÕD8àjȼ€€Êd8Çì¡„k‡a§Br²ÛA䲯©±qŠƒFQ–ƒñˆ=yœÙØ%V;/ò }2‚d†\ À¸¸p¦»ˆ€á¡lІƒKPÅ:=l” cRûš6ˆÎ¥˜– ‰‘H#Oëƒ_ª‚È€ÀvÂ|ˆ„Nâÿ*P&À€) X‡u €I8(>0ÑI€ÈQ³–°ñˆ ð‡¸Òù£%°*ø¥DØ+èÉFÁÔÕˆÔ‘èh”Kà øG*ýTP•2_„'ãËGlœƒjT?Š.ØEhL3+DOPSQ²'ƵECNFáË&yå ¨"h¹F4Ó  A¼à rUûú« Uü²Ël XG„ôÓ#¢‚ŠÃ*ÄŬK¤øˆéÄ™ƒHýQÅ…\8€ P†eèµg8g ;µšû`ÙØÁ•†ôDE¦ €›E‡lÐÅ%ÅÛÏdP/©ÿ`0#¨KO#Ǽ”€  … "Èi"8T¾ðéí„L€‰ÖŒÙð‡HÛà™L^¸‡RŽTN¨ €…^¸F ØÈÙb(_Hˆ¥˜eS¬r°l [ÅʆÚÅÛÄÒ[Ù…’å KØ„W°‚ȼ+Ø%óñAyðÒ‘ Ðÿ¹MP¦¾` TÈ„­âˆ²x<æy Ȱ°lQ \J(`ðɑȅ^8Ï¢Æ3ÀZ´¯ph‡õÙªôßÄú õcy…Þ(€JX3k'm”O´²%ˆ„ ’•@Vx,ŽØ“9‚ˆ¡]`€¶­\Ž ð‡ÿ÷=`8O]èF¸xÇ\(•TL­«$‡kðm€C¸[ÿe­®@”=©T(&’„ƒ"£:ýá€u „G+1p‚•°ßåºù I`˜™š€›Z c(†_àf`z8€¤ÙÈ…[(e$ ƒ0€dá8aH¤BkÏ…5ÝbtÈÊM½l{…Ú …ðŸa³²*X‚e×u“eJ‘•8K€l­ÙÐJ:@Xá\Na¸^˜„5ƒà…Ÿ,ç8†[ ¿ˆH€NPÕ_iíÅ_ÄžðL8T¨@WDذ§uK5MøJ(wÅ . ÿ/PXãrî…\cHÏ÷…çÈ…~†ˆè_Õd±lfnh‘a€¸T…ç­²o«¥-âhèBΠØ}ó\ؘN^†aàwéd@aÀQ¥&]¸dì=4*͆ÄÛòJ’–ùéÀXRx(ðƒsK'ÌQh(°‚˜K øµA-˜a,æºÀ€È…Q~jÀþ xiˆ@l R=ŒVsÐ}NФg<Á:åó˜# T†Ý1ÿùƒñ”/™©}…¹ç ‚ YÓ«˜P0žöK¼ð0– lcøhÀ&†àúŠØãýLÿ}ØsÈÔÓ”½ »!P] @¹!ð˜ûâÆÁÛ–«Ôp¹ˆÔ—‰KXNàiÂ^Šé„c¥þrl_Ðf¥ €LÆ_j¸kˆoC n¨Ï‡-Ùf4‰ä.»Ø´ðé9´·å6Û”kÖ·7"8í€O˜v )á¥FŸìd0†QuŠÄ0˜A˜ƒëȹnXÊr0/r‡pèÝm˜†ix¶¬C” жµ9²³· Ø´+‚z¹×^€%ò"€ÔȶëðØ"]ŠcÉŒ† §a Ï… ‰ Ø–t „:ð–ò„ÿ.²#€gH)‰¤°IÈqGÐà­°Mn•‚‚mE!ø˜ûhE4T¾f%; °¹ ô3,‚Õ…ÂoÀ6` @+‡‹ÖòÀ¶a:þˆ€AX: Œ7¸˜ É5°y`ˆ(‹Bƒ<¸;ƒ/@«UѰŸnV&W]šÓÖeE” ‚B ´¥9¯T'×À˜E9A¿`•À ½˜ÌJ _Àåˆ_m×ðô¼^ŽØ‹FP xŽñÈŒ¡ƒ4€Vß9 4è‚4À 1Üïº.‚ï#‚A­Ÿþ 8îO°!ÈA+²9°ÿ‚ås{ãpÝ"øñL`“–0…W8€«M”l7Æ·ãô¼ò§6]èpÀ: ÐlÉ1@4Hƒ9h‚.@0@ƒ;,„9Žv¯ 7h$Èö‡¤Èct´½  î}ë„ÚøiHŽHÚ!k€u¨M¨„[P_TîQ3bª8…J°³ ]ˆcc À‘˜{ ÇíVŠ P€øùp‡x†ŠZˆOö ö6;€õw ÁŒCxˆ^‘½[³etº¶9(€J` ™î¨’pO€ h€ghP ‰SðBöI€ØòÆtÿ ˆ_ ýƋ鬀åˆ|èŽS· 5pâ˜y¼À€ Rô'ÏÓ”…X+N™¾³7-’½¢@ã„T0F„@`×6¹¦7™ž€ç–ÿB G1S È`'Lž0c5Ci@"ü«hñ"ÆŒ7f¤Ð Á $pPã’ä8Á¤Éß»vþÔE8`Ý‚gîÜIó·)U%9jÔŒñ‡)Tª¤J—2Mz*€ŽøÛ•ì*Ö¬X‘ùówL+ذXy!¸ õ,ÚöÖp7`@€¸þâHÀ 3³i÷òí{q?7CÑtõ‡â¦I-ÿyêdÍÉ”.Ÿ Ul9i&z9Zpë«Ø­·p….}õ× ~W³níÚ5…þÀ4Ù4Ê_§ËL=ùË4ªÔ¨J¢t_æäoÂÙ )…™¾šË²æa…ù{ðú:öìØ1¤|µ©R§SÄ™¢*EJüx˘üI@{Á@.é½ü“v×íú÷ó—Ú@Sz ¨”)s„¶ ƒ…ùb_VÀ ZZ¨ŸþŒ2 •˜Â¡R¨Tâ«YÐ `ÈA !Ô*èÒ2Å  /¹°Ù…=ú¸ü4`¥€¨T&þ$À× ÐU hÈ,Båq¥„ù“‹.]â ô°Cÿ€;qQð#šiî•’†–âÏp žò’?ð¸Q“þÆc¨A‡y¨±Æ•„ªQ B!q€j ÐÄt`áa¬1T!Q©¹)§VŠ)IbBŠn¤øãÉ‘¤\2¢$—@Ìh¡WJr y2@vÁ"·âzek _§Íþ?• ’*“ù£ ©L™€ž’I&—èy§F@Ì  ‘h4!À$…U‚Äy AIƒ"K¨ ‚PAjÎÜß V¶Ôœ]Y’m¬L+à)¬XbÊmþ€À$À%[Wqk€Hàˆ"( bÿ+À„^:Ç:ÀÀÁ9kGÕ+è)fÊ&¯dò¦?F™ +þ¸Cnv±Q 4Æ!ÇÆ03ìfÎ]·öné¡".(G'Å6ÀëÒÚDj„¯st²; 0íurøMK`À{[xAþdK\ÆÊ+þšrþ€Bh^À“pÀ]LÎwZLAXpë Dðr›f8$q¡øCƒÏ h'齟UÁèÍE¡š¤ íxDox´qšúþ|F Ä~Ö|î¬pŽ·­aGsOôáÿåÄoä€ÔÁíqJ¾?Žë¶ªGvââë¯ÿÜ `tAˆ—¡3QeÍ‚V:1 S0¤ð„%æ’‚Þé)þH›þÂw D XÀÅ£”qí`x€ T¬:ÈÁ b¨C´&6 ã0ƒÏÛà !À€÷8€$à F(P€39‹M]©ƒÜì@‰9ÄmPsð+BQŠS Âg§0Å/²¹(À6„Þcÿ@t €6‚¸OM0¢?±†:¤á CA²Ô@'Ø¡*`P ìP‡1¤áei „ÿ¾>À€?ð€ D¥%Êw¦ç# $¹À3üÄ\ýk(apCaà'5 @‚°Ä úŠ$.i‘ T ‘‘À’~´FXÀm€î/A  +…CJ€QæÂp€=·ì]jx‘ˆ³"XÀ p Mp€‚´EyÊf€Å"¡‚4$ëXÿJÈCÔ•´ê›|[Î0â]þÃ’ÿ¨€ èЃ]ÀU®ª`°®   É"å¥èÐul`)a¨AŸç_þÃíKÀ( €SˆóÇ2°ÌJ•ƒÒˆ&P¢+PÀ$SjC—þožø(À€  y Þa,°EM•*Ö±’µ¬f=+ZÏ;ntpsec-1.1.0+dfsg1/docs/pic/alice32.gif0000644000175000017500000004142013252364117017242 0ustar rlaagerrlaagerGIF89a´Õúúÿÿÿþªªªh™¶4MX ŠÌít¯ÏðšŒxooNN--'Îέ­#/8Ou‹ÇÇÇE5$’’rMTWwEfwYˆŸO+=FÚŽxN$)RE3gV5# ‚½ÞÁhwgC:Zl&'fãão¦¦9iiimm!ŒÎ÷  ”Ö÷ïïïÞÞá”Öï÷÷ö$$±”ÆÞFc]…”{:ÆWÀÿÿÿ!ù?,´ÿÀ#F,ÇÒõˆ5£ŠR‰¬&‘ÔçCjíz¿àkxL.›Ï`1ZÃvŒA¯Ûïøa}®‡óa{pyƒCo„Bq{y|„‹Šˆv1€Ž–—˜™—ntJtŒ—ssMBsŒD𫬠­ŸvR#""¥R£¹£!"& Á%%Ä% ÇÌÇÂ~"°ÓÔx¯z1’—.o|¯¨×™áÔãuBOÕªŸ[µï&&ËÍÌÁ"¼!!¼Q½üøFQÊÖ.„mÕ>ªÃiP*‡{<Å¢ÓäS9E Z<ÄèD=ô…Àw®%[x9II*D¼_"ó… ¹/W¾{¿Œ–ìÙ‡ÿ!@¤!……HeÃ$¢5BHmLJµS*Ú²ò¨jݧÜ”Œ2Á«Â»€Ñ 6Þ»€¹Úí2ûó§2î¬UªÊ÷éEK#BÜñÚ·0£-º*fs2JD2zÄ” «‡¬nZ¸3ˆÄçR„ÎböÒÆ3È Wµ ”Úë´ÕßNO¡^cTc¢Â°uugÄ=`û#ò}Ùóçc–MäMV_c‘oký„ ú—ˆ’p𣯠@ ÅhРR œ'¿ ]¥å$ü†NíA[¾­#¼ ÷'Ö-vJ Æ„fŒ&HQKYšõ¢KL4™eÜ];±Å“0ÊÅ‹\A !ÿ/ Àƒ ß ‰w4Q‘!Òj©‘‡ZUáˆgN6‚ÁÖ•x¯ æŸ#QD5Sm¨ÐòËK’E’M#)—ÏK1EÙ þ0¨‡ñÄSW &DE Ø!^S´GÈwáѱ4à¢DÐÎyDf_áA"Kµ3?âW&j¦Qí¼Ñ”„#½Õ(g攌eiMWL[•÷Ï9CýB¤ˆ×|×Dœ)6…G™¤ê& @°xí-`€ÖqŸ!¶Zë`=æá•[¤„Žn„@u“fXËVI W.€°ØfhýÌe¤DÞ‹•øb;dµj,ãÁÃðgÿ›Ay}Ü +‡±è$KMåš%½&õ‚m âµø0 V<‘UZ?ßNáam!(P› ¶@õŒR¢Ÿ0@@ÑÝ•9c·„ùzfÂéª £ij­ x3Ž®¡ìJï®ÕáT\€LHS«‚Ìrˆ€ÔiÁ[Ü9G¬++F™ƒ™æÍ1¯ay J•·?*&?s„€JŽü¦FGÇ œ G˜åضM¼Û6º%£Ëœµt#š'/ÌJ•³â*Q`­8_,{ÛêˆÀ ࢛@7NG§™ßªz'6 g%Þ€NNÍ…/¤Ô${ô»øì”8ÿ¢éÓ-+á`ºæ HÅæœ™¿Û*š‡ˆnèB]ùðŠÑá*íÖÌ,{FÔóÕãyUg î²yæM¯iº‹È‡I¹éŸS’Z h<¢"³ uGNëì4c}}öUÙ2·.ß1ÀUíBÓÖJ8¯míâ\ÞnÁ*;±‡<à±ÀÒˆ—ñﯠ å&Ô@ xG«Sç.óM/m»Þ<'ôX=©ŠìÖ€¨îRáÃDfÃ\ír•Pé±õ5“wwrÛ®ÖUtvP¢,Öú-8æá‰Ã†>qÓ)4Öƒ¢Há‡Ü0­®Êdn*—jö§½W˜J²üzb² Žà(M JiJA1ì „9‰k‰Fî®ó„@œRÓ.tÉvÂÿcmcxW6•Ì•ãåNËæö*Ä>LðÉk‹8ÉŠ£ŽŸ¦›³8‘!Q¦Làì,§øbfÍ6DVŒˆM…Ÿç¸OAV°rÛ@%úØ<õJñ»¨úÎJáÎ:¤P¼`TQ *É@õïÀØÀ(²"ÀawP@ LpVû0 Ñ L'`M­‰#"æBi¹›òˆwU$¬ŽbéE5ĉ@Þâ-–¬¼ìi“Œ(Ù¶mF©:;‚ÛXLôW!\ Cź–NmtCaf£‚-ÌVV|dž8.d1‘¨°k’ÕóZåM¸>Oªv^+Éã9Œr.UÀoþÿÎîÁMoV3*èÇl| $nš•ÔS6¢‘““-±s1"°<½æµ‘ãk˜øh+ƨNʃÏ#0‚oU½D-£œà>îïÞ;ç¹Vµ-@¨®¸Ö r#f¡¯tjB² Å8üäµ'˜­)Æ_#·v=Rsó|Þ¶(ÔXÎN'†6 å'9)ÒMè=©ÜJŸ†Û¶=h<Ïà[L@ÝÙG:¾bÄ­ B*Yh&eíà´`«“U¤€¹i¹^¬ eÝ:!ÓZ™C{®Œ3Z@Fé R‘Ó¾“ºN*#å0„°¸_3c—æ©øq2‹Ý Ù­fÕè|7ÿÏõ'½µ¬"Ú•ªú =<'êÎH'Ê‹nåË_ÞÂÓlM¯=rúÖÉ0ž¼SÅâ‰>z#ÊÂqr÷‡”qS‚~.tÉaÊÜã ¨Hü©œïÏSg›æÄ;xmMvCz‚–À£`zm“'þX%+9óÎr”vÂ*Q÷*ºÙ)é@ÒÏÏþ§ ©cÜRNb„ã}d†öªK—¸Ó[_»ùôZn€ñClE$À‚•*;Lí‚»`AõzŠïU¸«@{ X9e‡¬MSe³Ó¾#Èw7éu^„-YLB*;ÎZý¨ëFš¶¢ ]¥ cáSup"”°snÂK|ÄD•ÀEC lt°ÿFÃwß".'~ ƒ^2§H*÷. È9*§ùggñÇgê npN dÐÐÑx@EÈ-p°30@.ЉŸè¡¸€ð.‚ ‚]Lv@w5q¬Th"mÒyßÄs¦³-âÓƒÿxFwR_gä~OvŠ'BšdVJ!dQ‡ÐxÓØC ÕxG2P °3ÐP.€Dæ­R.+Xc­ÂŠt@4  °“4Ð)¶Ø@•°1­RaÒp,´1°G[%wâr‚.âw:/èXÕÃ?L1`NqÐð%ÀY‘-  @×uðÿ Ð-€‰Gщ`‰ha ’G5—³v…å& “zI3 —@´1‹/s2ó¨fñ³^ð£Op²6 .!âf‘CÏW:Æ+Ž@h“pŒ²k'1dÙPÚØ_Iš ‰-pšÖeés°–Cpn9 1¤A™Ãy"2+¢2@T‚yé—Âé— °wó *¦9m2E“v'¦R~‘Ä4ÜQt>C~'}bb‘¥{ igi¥8¡&°%°ÒH‘&9’-pu@›Pߨ‰ ކèàÈ€6y ^Y5™‚$# 0œª—6 ˜ ÈY&¥& d@ÿ”&¬QEMQ2.‚.ª1.£9Àt”ÀF½Ñ ø2r®k® µ!% ¤ù¢í©Ý('PwàpcI€–tÐ €d¸)bçC"MI40Ú¤€60¸6‡åUã´Þ‘GÁ/vÂEa8@Ree_DœI=—iË@0 `]À€x„˜8`£´„’È÷î)&ª±zß>JÅðऎš“ 0  Ї!òY‹ŒT…×9"$(';7@´&«¥ 3‡gtü’¦z8†{a n*-p `§.Žw@›ÿŸp'§„`’€ »³bš³˜@€ù¨Nê¬ ºÄÈãVPCc{tr$èGL•¬c†7‚€ª·ff¤n¿GD{°g»²IØ0Ê0«_)'`]@„»Q0§.à« -ýªˆt@“!ÑȺoÈ¥­ `£kç©cÇ&l‡³í2¹™"Bu{ª@±0ie"): ü™¬Ú:¥¡% ¥÷„ü0  §I'@ÀÐoIPúP;Drt£P ë¤+ +ÀNªÝç‘&²¬qW'F±mrs€‰Å_¤¡ÿ߆‡“€¢Ó°²J„’c,"0‘Ò(–0–vJ„v`‰õÙpu[´–X%`ûz )ЗM­’*µ¯æ€¡Ý‡9É#†5Ö^ð€wc7t rïQ‚éT'³ôQh„¶U‘±6ÃfQ#@«Ð(–Ô˜š@èžP’‰“H×5§ j+Šs [‚ʴ먪˜¯ŒÐ6¹5Nu4$+ÙKm¥s–bØW‚T9Éú ##u¶Ùq­|Ë _I‘@·1`’ ‰v „xsÐ~[·s0¸Æ`¢uêó£’z¼:È© E’«&˜f?ÝR'ÿ¡†pKC+9j +¨2¼ß#"sXP”S25Fʘcýòª†v Pq 0–a„=«ÄZ¾[8àוŸt:Žt` ã*ÀTKÀ{uÖ%±ÖŒ:E¤4jRp¾S'aâ.>p"/ì²s”óÄ¢’w³„4貄¡º­ Æ6ã/û¯°Û³ `§.*–v€÷ Ž„‰ƒ[›/KÄIDp#Ð!@Bì¤J‘fce‘w¹¼ã"âõ.}Õ&oU> V©±1÷"3I»“z±àHúbÂûâUv° ‘ËÂ}:§–q*ÃzJ-¤´¹´!~Àÿ+`l ÎJÄ.À„Œ€i)r½Rì±×ê½7÷E[v@Ñwl|$"íÔ8Ó¼?tI3#ì_…!ü`, ðŒI²©§AˆŸ@Ú’{–by–kˆÖ…:@Êá`P:€( `<#€“½ì—мÖH¼œ“6° Ý¹Ó be”>'’@á‘}²â=;àµvV4‡pãÝ®¶ ž†ÑtòÀ³*2@‰Ûº:`‘;ã$`’.ð ûLPÔ`à`A-`£À¸ÍÐR› “z¹¤Vͤ3@WMpZ`™ÿãdcFT#B>…z.aXBy#¾sHG4oCg3Ø5Û «4ˆ „¨ p$0Ø$ЈŸ$ –¯Œ6šÊ§iÃèMíJýEí3UÔH}Ô7TÏ:Õ:©«ìšÇÚùza(+%ƒãÁB³5"r"­wKµä3‚¯è cšxþ±²dRë©iˆ{*Ÿ§ ǹÊvp]4¬š¹Ú@]Ô—½ÏFÍ@ÝEÍJm:0E°Ð¢-œ4p©´ ÕOúzª"b:.}g±¨g:o“™KÊ”÷!+ãaÒÍuqå°oÄR1c1 ´© »6 Ó  ÿ¼ëž! „¸Ú³´iNÝšíE½Ï Ý—mL==0Þ:©ŠÀ€ÚÈY§'cwSoiR9ì7® øQäã.áT•c:íð.s+ܹ6©ði ÜU©g®ëà·{» ‘¾«£ PwÌ£<}5ͯ.ÔnÔ–Ù °åNÝõ\Áy¼4P“šÙ/Ðæ  Þz¹ŠtS'ãrâ5æE-¹B<˜°ÀÀâ‰=~2kw<–ƒ øStÿØ«^ø# '&£#'«²oÂKà)[+¤牟zéð–´)£;ƒˆ°¸š§œ#èq¥vÐDw@®’íÔÿÒ\6¿žÑd./cè–º¶+KÆË8«Ø·+›õg)‰'°àÖ5ñ’(£Ö%¨¦`öN­ÏÜ­Ô×}ö/ ^ê ‚€L)ž“<µÚ¼™ý¨6pâjb*„qÖîp€®ûz5£‚a: `èLŸ‘Ÿ"D`“`؊؈àšž‰@(ú' ÀCœ|p°ÙÝÍ—ýßöœÍ²?RBÃ.Efc1³À’IáÇÎJÁ´^—ÿ4P”ûà /ÃhœÑè‹#fc ˜ñØÓ°èJÿþÿF cV:6&'NN]Ab@4 @:2(h®0:Ð8 ±ñ€–WïãÂG\P†¾Qò8%¡ sTï d \‹f(&JÌ¢e5årE%¶TR‘°¹#^k5G'ÿ ÐÁ @ ~Å` 2€qÁ÷꯴`óшð]%Ø^ˆ 'r¸Ét§P¿ 9#*ÓêW+mf°’VX!’Ø"•Ùbrv¢ý|ôºü 0«9ÌZN-®€ÑWf@x •‚b¶¿øòBUæ„­G™ׯo°],§:ÀÙì?‰‚Èb@Iƒºx™‚hhy àXO‚B+âˆviƒÎ€W'²¿Xid+¶Z^3Z­“ ƒ¯€Åü`Ò „ 2À€ ¢Kid\¹ze£- Y¹wI2¢1â±³ÍF'mµàÅF(ƒ0¨€ªÞ– Oÿ `@â)pšËoÏw'ÊuÞöÃÔOZ—ü ¬¨!‚‰Û…òƒ#5fÓC±Õž|mÞÔ¿ÈúQ1g§Ý»¤æ Ä…d¦0@€t°„ø½4'~J.æä Y˜A?ø*ÌÏ.”`'³‚ b”Áé /1ÈҞĚi=gVÖ@Nó…<'V\xLÎá-ýíPd’+ˬÖP“Ø”ÅG³)"¤ô#§8å'î‰Ùf°›#¸è#Ípbà.Ð1`fÀ,Ü5±V eyÁl,5¸3vÁDÉáh6ˆ©{³*jóIE+r4zÅ h!!, ˆÿAÞø‚޼Ç™Ålf… ½ÊqNr $¹pƲԤRÂYsȇp•-Ž£J`ù W@2g…y röXÖéÎY ,b`Hà!ˆÀšwÍ ‡s¾‘Y±²T¬ ³«æhño,åwt˜¤BÈC‡3|E`Ö@#ìýP9"”d!??M–ÉeÒ•Oì&PK’èó‡˜.W­0„5ðD[¤â§hΩùk^3dÙÌaQ¬ Yj6бåòè.X÷‘Ó¬^xŒà ,{ „B´]„Ì(@2A‹>; M¢UHz:ÚUBÿÊP³Uà0 ŠúMý½{@ï"ÃB=Ê B*æjîõ(uÞ*†ŠH)Å) ÁÂHèLÒFmjÅZC)‰x5<……ŽVCˆ… 53¬És WPN^3&(DM† ¥5°®q“â*&W 3@R 0`…€ f [ñ d²÷ã‚»Ú¥èh’5® tÀY˜{}|]ôjñMÕÚU‚ÐÉçÙž3öÖ"hˆ …žã£ÇñG‹i€Î @"E¬ -CSô²F¶ëVe‹-нNJÌ{Íú!ýègC‡êïjE#¡i K¹–JQŠ•¿ÈZÿ¢2à‹>³m!úK‹=ÿiErCŠÌ ø†¡˜߆J(c †Ãl ) +‚‰5éÊ‹þʈÞÝÃz¿%›@ÆF"°I¥ö3KØ„´¾Íº†ItŠšñ°3üï"Zà[Ñi G³Fò––¦aØs8ÿüSÅ`a'¶ˆ—5Î@ßJ[B¸6}¡lœµOgÒ‡O ì1ötDP ‚¾VžI¬XcâÈ “ÌlmG Œ ›úÓqîZÓ¡ ”„2)Ì«\±†²TÄÍIE€úh  ŽjÛ2£`|³Â~xJBœ±U!i?7S)®56¨jq|¢Õ±¿Ú%Ä£´7GÿöÀù-`Åsñ2ª¹%,$¢8Kí¹¾çeá ך<цšµ…hêÆéÛ}rC#`éœOÍXÍ‚&tn›@/ÌsBJ¯ޱ&ßÁd 3ˆÑΈÀ¾E›{ lA.]ÂÖÓ50¶*bh&]}½Lú˜â…:mMMø àö:ÔVÅÜ“ú»œÅ›˜U5\4‡f€à@Áß:•’[EÐÀ 4ÐÎ܃&Ã2ió.‹¸†ªî^Ǽ7üª5ðô^Ø‹ xyH¨ à”ÉzœêœÄÓ©R´†¥~×™jsë9u}N³ÚÀôwpÛµ#jHÌldÏFÿ=.G@ j Ä]îu¯ÍI€L ÁfHp€ XÀƒS}{ûTKK«RÊUMŽ.b¶‚ 7 D¿ †0CHÀ2C•ëK´›†5~4Žƒ+µm­f¯j[­¾xÚN -Aíkƒ`"y@Ë  ¢ Qô “êè¯î¨˜g¢áß®±öDB‹qðÖ¡û€y„po#9R6`yüátÇ‹åŒR°ÖXL®¯€æk0ýŠEֆȼ Ç¢Åã R¹ëXE¸î£a(•*„ |M†˜$@´ë©%3|£ à!q yh/* €p`=üÏ7@ y¨<ÌAà2À7(¡Ü¢+l/k pï `‘+þÿæ5€—"1 š†ÐÐW['ZÃ'i 5ˆmu¢Ä5`‚hàªhñÞ®2Å«Ô@Ä£Lö\­XfE ïåM’Ð!]Àÿ $0*³Pb öŽq¶Àˆ„tô§âß$* .9R ât÷ æZ , ` à;A$ªp¬Ž`x©ïgúf!(_ÊIÏ”mzfÒ®Ô0°¤…ÇnÊ(ßQrNãÀhòÍŒòYÌQZäŒ2 à¢B+"üO–°.'@Z`(Od À$‡Of.8ƒSðHR$@À<òr.›ñM pŠ%åx­Uøÿk|ën kZquåéRgêH°†¦ÇYd Œ–©ÒîMÖþP…Äs®â-ò ;3’üCj‘ðhO] ‘ö–ê’Zx³)°âöºâ SN~Smïö .kO’3ö!LssBP=˜›:àZ€N༠®Xá MýjÌ]z†EgQäŒþ)4ÿàn›ö_¡ó ß®i7!õ*’óGwR6;å†ÐTÝõˆ3ï”ð8^kuMOkõBµ –r]<ò/R÷4%’@ žªÓZe–=£,~ß1NaŒhÔ «¢Ì NZxQ‹fk­zq&kgð8ô’ï ±Z y4áöö4@@!¸4ÖcÿN{íµöp3 /0-@Þ÷ú\1”Özw—G=h6nõ–ç0 ’*Î}I†~d½OÑö/Xcƒ¢OHÌÄ€j“\° xÇÖÎ3ËO“fJ¼•R–—$`÷6 ʲ7V5 ®A”Z4``á•T17@iÊ’o!_G5|ãoÏmµ4„TU“*4 Ú5‚/¡:`v v€òpÆñq,-Ì,³îp9$è39¹âP/ÙÖ^^(®è‹ád‹aé¥Xò˶â[qàªÐÿRXAkoyDB=&ÀªH“Ð5Bgxpe’ÿpíuô˜ù¶€öFU^3á!Ó•5=Kà@˜ ¼yÈ&V{Ô0P ¬&öôŒH(ûÚ,°ãúlŽŒ¸…Prö1â>±ÖJøU$‰t d3ؘR¢9'€MA§‘Ëín7šCøy›Ù¡9’–%  w@ßD†‚_K68‘ø6à¦"ø ¼9"OÀ®AxÊÑøez5"“°ykW¶«O 'h¦©çÃ"Xwu'ËÖöHE"{Sé7ÕT…epZ€ø:ࣹƒ–s¬W†7„BÝ5+ @@ðþa9 ¢àöޱ°—)Á<’¿C…q“ ¾YWëja‚œ+¬{ê|ºÑà…Q€±€­'ÝZÿ£~½vJœ©ñŒ;–“úV¡`=\;9ý!Å ±@¾@$òRÔx…1w´ÿáf SmÓTµQ²q6¼-»âjà÷ò•¼Rà‘ ŽÒîhPbè%·Tƒ~ݬv“äì së‚„lJ$hÑÜamIà™I€ÀßdJ¼ 6+Dm¶¨þF!Xý¶fq ôïȸÔtJ—g+àÛ.¡+Œ0hµ59‘¹Â,^»Bµ{{g> ‡Ï- \5VÇ®µ¼»q¦0,†Ö¹ Þ,°4¡Œ·âM ÅÁ3‚ÉðMh™£`sAðíÎÿX·:šO3: ø”*R=¹Øò-w*ô ×8ÙXðšs‘]n#q@¾a™™Ó/°ŒLO˜Æbh´Ü¹dÃìY7I¶³;^ + `yÚ|¼õÓøj˜ šS - ±ÍÃ"¥+Î5[nïDµãxw¯’©S•+"˜ïšp—Ü!çR޳0ù˜G…ãÕH‡ö68Ëò½²‡Àêê‡FHôðD¸ùEGMqí·²6¾ EP‚äi¥R†¯u€@¥Z˜e å˜¹žp¼²ŸÉ`ðúy/A£åeA¶‘ýîr3á†á»¬¿õ_ù5/Ë;ƒÿ*&Xyh‡¶.KXy&{ÄýŒ…Ù!•Ь½¿—!{› ïãÓ OæwuŠèôZY´Ê–(*“ŠîÞMh@< aѸ¢Ë͹µp@ÍQ„ÙÔ_§È)~Ýà0*`3ï†Ñ¾fI å’ÇBSÎdÙ˜ £¾éÅ<Ú8ÓTäo»-IÀA øDÕ÷¬âïΛ9½ hÄ&-Øåu‡õ“ÉyÝÀÉ2¬Gù¯ã*Müí.ò½édˆí05¡¿Í´)HV ¤€oQXBFLbH:„ÂUh‘$­CkZE :.§‹á,œba‚:!c-$È zwìUÁ ‰D=Ö³ r%W:b+--“l± øa*£¦MÕ:²ý‹T$-t£åÍÞ1DUCÁLDк¥ÿg€\ôG1ׯ¥ ÔFzÚšßB?àÐA\ hÆóB×knG\ÕßÞWm8Ç}GMjþÚ‰¢è% IxCB0à$RÒS¸b%¬À õÒÖ‹âù"US9.˜Ç†÷T*Ï‘‡wÂ!¿-ÈzyAÜsN0+iµaLò™: Ž YágœC@  bÙð|S(€cœ×É€;B¸Ò@¨¤%ÞJRûˆ¢^’Šq¨+ÉDOpÇ5!̲¨`¨± ú‚+Ø`tx÷±áÚQ 2Ò"ˆéÀX×àb ¦-$¦2à9K â„#ð#pÔ!Cåèp¹Ìî0÷‰ßÆ@ÿ‚$à l’vœ(ˆOLBxÀÑ@a±Qޏâ(Ž2“,A)#§Ž¡ àhôÅ BÀ€8I)¼{b œÔ=‰Òq@ÇUÄ‚žÁeRW{A Ò@ÃZ 1›€r™8С…Í \ch ùS˜TA,.ÈÖó0•DÄ+ (…ˆR9‰€™è8Ź”n °EPà²8ÊC[ h°K ÌÀc¶ŒÅ ¬‘Q PªŒwvåÇ#Ìã0r Ï“€E+V¹€MB|Ï&`ˆÀ (NXYgd ü m$à!"¶°éáÐäH)À™l-•8IÔí¶(Ô”0laÈhù‡]ÿ¦Š*ðETAC´ƒÍµ>A Á\¶Õ›6¡Eœ@8;ÂH±¼€u‰g¡N1 ¤ØÓ%bì]aáÏF¢‚D¡JI†T6ìrkD†¤h¡…V+/œN2˲˘a/¦ “õìˆ8²gsèéÖtL/lΤ-€Æú±˜¾ÎAÛË{°:2¼/“Êà¡ µµD%(E¡«r£ËæBSûôç'8Ðc@•KEÑ-&À—bÀn"ä(訖îyÃW6;‚BÏ@ÇpT.c}8'D‡(„T°bŸ£_K¼îQ×›«Î«@‰Ÿ2ƒ¥ŠñáïÖÝ1†ëÜ£…?†Þ{6JkRC^$€€ÞÝñ? Êu‡Ì¹¼2$%BV™cõ%ØÖç!¾:Vºæâ[üÂ0K¡ˆœziYÛÑhƒ“ã¬`øóˆ@Žé,QE,R!I›ÄCzõø%œZEZ6TžcP稃:3ˆÔØEH%„Þ¨ PG$ Ê"@ÜÖ)L—Õ“=eÛÿ=Ì%|—ß`€Û™Ê.1Ç È]ÅCÂ…C1$’€XSÊÐÊðÙŒÀ[™i!aƒ…ìÙ?0˜šX±u_¿É‰›\’x œ È ×3† “$Ü5ü_€€À[PA ð •E ب±‚LüÄQ¥^qè€Ü@LÀ^a@ €Aé 0DÛ1¨+tA{ÄWˆExŽEÄ‘2 áœýÊñN ¬žñÅl bAº¤Ã àØd(bFæ¬PÂa_ 0H^ä•!Ä@*±ü<;à>TÝêŒÀÜNeBÙ„Ž „`€,c`À ö43V@3v@HÀÿ5®@(ÀÔ^!âØ’q@1lÀûÀÚ3\A¼\äƒ%ʼýMÚ  i‡ @áu¡ m!öá„”‘à€äÁ=LF\dŽ Ib­@™Œð?VAºLç è@+¬ ¬NHÁ Ü èÀŠ\ÀêQ(BÍ@ŠcL ØIXŠB¹NýLóäÍW¤ŒV İã‹USÈ\O¬ÅÑÍÄE€Ô$ÜA:IÎQˆÐJŒÓa¤#Ib {l@pºÆ%ÅQ÷q†“¼Â$HÑ# ¬ÜÉä\Ò¥0ø’-EMÌÑž1ÈݘÀœõN!¨‰ÿ4cŒÑ ­1ÀŽ&‚ÀÍ`œ=²°ƒAp„p„ G0ƒ$ƒPÈ‹piºú¸ÏàTAè Ã(xÂê„LÖåmÞf &È„19ƒ;µAtåÕ`æC¨IYGE€–ÿq !†›‚æL&zðc^W0\Ѥ#²T%¯aÁfÔICPA!Q6…¥¬GÔ:‚ŸT…Z*(IO=ÌØ&nâ§r¬¼´q€ÆÄÖÝp¯¹Á1†»©ØÁO:‰Xì…\¼YˆðA¨gHQ’yA¤±ÃA&„ÐíVõå„=Á>ÌÞÐó=D ÀŠÿ2ÀfÄ2B%€À¬€Ã< !æg>Åñ€Ê¡´?ÖB­€ õ™‘WèÅÍA GÑ‘‰†QÊBà óµ[YF\ VÁ (›…XŽ¡ÃRD}`… ÐI÷]¥2€OÜ‚aU è¨âi!ºÝaÖÂI #&ÝOÝà;ð¥ðEÕ°ÜVÈ\†b[8Ñ!ôLpQ<‘¾µhý¨Ïgº \©å0ˆW„Æl•ƒ;È@lD¬8‚cÀ\æ©®€T@@ÉIÊÅ8 “JÁhµÁ`0ÒÁhMÔ}‰PA~Ò2¤ÏÈ@p=Þ¶H!Ì€IñeÁÿºìÙ`lC„KÏØÅ[ðÁ¦uQ ¸@/ØÀ Èêì€ h–AQ¬Í¬ê   €šaQ ‹²©Ëe !ßH( ΄†LÝ%¬bâ… K“CÑñAoÆ´ œ:øÃœ‡„€Ç^háãu*òÌF`‘¤à TPÀÔ+ÍgœQŒ@Ø«†Ç P@ò¨_'üçB¢ã8‰¡TðÔ‡ËÄÞsfD#:ûTÒÔd¬u´Ndø²Y±ùåÕúÀ ÊR{õB­Òk0 Y8:E­ A/à+ " ÐÀ PÞlÛä+Ë’cŸÊÒ5üÏ:ê&Ö[€Iÿ£N5©§ ®IMÃN¢¡IÚÑœÈZAGœ• |­ÖÔl°Á ÀËþ £ç:EÛ ÀÚúBéÁ¸€4&GÝBŒ@Ü Ë²Êõ¦)ƒø-œê]ìÉÜT†bÃ\|â4œ ÔÑÐÁ:hu8„[ü‡¶¤ƒy8ÃYiˢ݀çR ír0À¼« Œ@ F›Ä €}r¯½R@xîämtÈ’<¼’üeÕØ7„¥Éh•IÇ"€É˜èíCC<åš`Aø …¤AàG±ì…y1Yý°(ÄÛ¨§­$A>õºB&XV,ˆn÷Æ€Ž€  © à œUùŽ0<€£ º- ÌíK²ÝܺÄ TÀÂìjÿüLIH¥'}CZÔœ<"“T† ã\îßÐWÿ1Ø'‚Æg$QsEÎÒ-Q™èÜõ…S@J‡‘”+ D®kÒÀ€pÀm 3ì¢võ`@ìg4®½*c5j–4b@HÅ5fÀ}‰ˆø«Â™—E!°TËvl¢Z(”›iϺù  ·â }ùÁzÃ&‚F< K;$|"ÀÅL­ h€7샸Œ!`.ïlî/°¹ámSØÀ̬/^î"uÃç9Ág¹Mõƒ¾]„NÁ'ÚåÁF*u³”Ð><!±[ïµA“!ª3¸EÃý×:ȰXˆC´µ@T”„‰t„Àº¢îøòè8Œ è0$uÿ`×¼B"š‚03ž1'6 lËq)W Æ:,¡1<ÔQN"7I€ÿVöÝÌЄ:…‹³Y¤Fòæ'ã–Ý -À ìBQ£n–n6Ú€ TŠ?¥¬H¦„ZpŸ`ûaIÄge) ‡åĶbÆÈ\âåUÔ™PEXf ˜±5B^qP²€9DäJ,@0ݳª­ úI¦ !Ò”pÚŒMacZÜìöÀÀBüÕ[pxÅ B$ĪÔÊ8¸›CXڌбÇt8 7}– 1ó2\ tA:üŒH!›SütNSÕ„£˜H~ÃÿœAë@ÿB­5þ!ƒ? O÷)(üvN6Jf ‹|X¢ø}“~g®b9Š£Âÿ¸8Œ§…°Á¿Ètó¡áQw7V2W¬£þ±š‰AáÕÇw3Ò%yÃóšqX‡Ú´‚ŽÖ‚/ØÀ˜„L¨S, ¦©J&Цü¸Ÿÿy¸eZN¹ ÿø‰£ø‘§‚À’I",ƒ9dÁ ¶ß ‰kÁ—£z}YôžiB ΰd !.J=enð€€ L©áëdž&÷€WÖˆÔÄcEWµ)úmD—¢÷ú¯ÈÇýç4€@º@'W¨âNnvÿ\º ìÿÓš¼éŠtv”5å -!P" {¾P×§ÔZ ÁÌÒxoXØétý,¬‚ŠðŠTŠMÌB¥$LNtÁÜ‚-t.íÂT…Ç#ƒ÷1¯íCFc9W<<ÚàAFÔPƒ@¸!ßEœ"‡cYÍìÔX¨œîSÍl…Úci‚‡ K˜B½ø¹eíF¾̽ûŠÈËûNð:oФc!y¦Æïž8‡ÖQ2õ|Vÿ!¥ßŽŸÉXä:ðQ™Ü«à8ªº%Ô›Ž¨m |#2ìŽmÌu& 8PÐé¥è8ú• :ÿ6¡sÜŸÚÎßDÌ,ކr;—®ÿQveç S z­m½± `@íÄ·¬¹-UͦéN‰ø7©Lx°<‡Ì( ¤-¤^íˆÂ) ¸R,1ŒªþzcUä¡Qð É‚Ž_9 mÈTD») †Wü¦‹¡^ºŒåE \ «xQ)|˜é{B½··L8ÀÜÀ9o8×YVmŒÝòƒœ!ºÙy&œxû¹{£Í,ÜΨ EQÌÂ=àÁpè¯èã2—•ækû#<(ÉüÏÿ5˜ €\†+ftG$s™|6¡È&ècd ›âÄx…/ƒÀ,¸pÆŒõ‹í»åY¹Ã~wpôåG¹øÓIã¸8¼0-Hóð0PdPËr`HLTtt´T s3T³sËC½˜tÀ¤¼ #3Ó3°3€4{m ;ntpsec-1.1.0+dfsg1/docs/pic/panda.gif0000644000175000017500000000317413252364117017107 0ustar rlaagerrlaagerGIF89a~´‘ÿÿÿÿÿÿÿÿ!ÿ ADOBE:IR1.0Þí!ù,~´ÿœ©Ëí£œ´Ú‹³Þ¼û†âH–扦êʶî›2L—ò}×z‡ãûŸéõ€Ä‰ÐWL:ŽH¥Ál>ÑÜ”#Àä²±*÷ªBz€™÷¾D—7€û ŸÓ¯"Ïë«t£*£'˜ÇÔçÇg`6¸'f867Ã8Ùhõøp†C¹é&uÉ)À9Z6óÉ–I:jzºªJ:ðÕ xK:K›˜zËɪ»ëÕ» Ü5¼ú |ŒŒ[ìªÙLül,*=*K]{­š­-È›»l-ÞFI®~޾hYÌ~-”®-/ͼ§L{ÿnîäݳ~ƒ¢ÌWIÝ%w“ dBhß)†)ê Hh ¨ÿ-F$ÇÓDŽŒ 6Ùé7~ãè¥cÈQaŸ,!Oº,iÍœ<‰3IâÄX²Lʘð‘ô™å;9ì92ºç§ÃMN“òœBð¥Ç—!Ñ@š®W¨¢X‘Š4–쓬)Ï–Ò* Û¥bÓ 3•P£ºvŸRÙTŽÍ¾wך¤“)DÂj Ä%T1Æ"ó9·­äœ”ëxuÏçÈm;{-ÂXod¥«MW®Ñù-¯N®ý~®=V6nÏ.vûfLäwß7ˆ›þ!Ü®q–SÞ‘ÒÃ#Âè÷dZRR¹ƒHÒ·ÙU&LyØd²Ñf[‹dRÔ𤡙™¥ MNsr&¦ knô¦^:ɱ҈æe¹chm¤S"qFG¨AýÃ$Se@‡%Ÿp͹¦D­†x5¹iVWZ­—’y‘†hP€ ¹h*]²¦Jª`=´$—‘j¶ÚE|6tÒC2ééR8šuÔ§™&cUšÀÿ.&é±h!ªŸ}êTÈ …:[¦ó„ùÚ·®ª]T­’,oÜté±–JP´ØB»Æ è4e¨`¸æZ?Í&&X¼òÊVÀŸºû‘PÅþì0àùl¨»úZ- §|︱曉žÚöV±Íü¹T\"8EeÅù†Kñ^£Õ¨²Ðû(¬ÍÖ´°ÁбÜraçòËdV)ŒóÆ6ÓLòX;»›rмÚ;ÔЭ¶óÉ ¬27 ?sÄ/ûÔ½I ©ÊPoP´S—f¬É…µÖjÕÊB_Ü4‹Ì32þYÕÐc¥iâ’kð®óþ÷ÛÉUÒ§j©ÖwÛEŽ]W;ÿ]¸¨´Ù›ø!•»‰jÚLÓi©±ˆÚU &pÁžÉŸÜwÔvV€Ój˶å†;TÙª¾zë{Ó…§h¡s|¢Œv-º° C êhÏyÞzk›;éü7ï.ùilƒå›—Ë}fé×v¯óÁ‡¿w½ å*=úÎâP6Ípᓬ¿üþbºJ¡ªtÊ»NW%H¸{`j`lv™ß(.6¼0hÈåÕ‚‚M: ï ´: u=û`¤¤_mD_ÒÒj×Áî™Êw9«FhÃîFóÛßžf4P¤°„Â1Õ R"΄“IIÇÁúo4Cm¸®ßÿ)Ï #ôY’$téB­ƒlÜ‚wa”A‚r}»9„ ˆN29¶\‡+L©( p²\!nÉ h‡‘±„¤/{§Äj^¡‘Ù<å°VyÌ`Œd}‰8gúî N¹|A•ÅË «¾±E<Èð4ìè#¿‰M£œ’l®ÒÍ@n Ñ6‚O;KÔ(h>eù@·Yó™+&3ÇD"3–ИE ‚<" v(hDÝØ-”Ž,x2­©MoŠÓœêt§,(;ntpsec-1.1.0+dfsg1/docs/pic/dogsnake.gif0000644000175000017500000001250513252364117017615 0ustar rlaagerrlaagerGIF89a¹´÷ÿÿÿÿÿÌÿÿ™ÿÿfÿÿ3ÿÿÿÌÿÿÌÌÿÌ™ÿÌfÿÌ3ÿÌÿ™ÿÿ™Ìÿ™™ÿ™fÿ™3ÿ™ÿfÿÿfÌÿf™ÿffÿf3ÿfÿ3ÿÿ3Ìÿ3™ÿ3fÿ33ÿ3ÿÿÿÌÿ™ÿfÿ3ÿÌÿÿÌÿÌÌÿ™ÌÿfÌÿ3ÌÿÌÌÿÌÌÌÌÌ™ÌÌfÌÌ3ÌÌÌ™ÿÌ™ÌÌ™™Ì™fÌ™3Ì™ÌfÿÌfÌÌf™ÌffÌf3ÌfÌ3ÿÌ3ÌÌ3™Ì3fÌ33Ì3ÌÿÌÌÌ™ÌfÌ3Ì™ÿÿ™ÿÌ™ÿ™™ÿf™ÿ3™ÿ™Ìÿ™Ì̙̙™Ìf™Ì3™Ì™™ÿ™™Ì™™™™™f™™3™™™fÿ™fÌ™f™™ff™f3™f™3ÿ™3Ì™3™™3f™33™3™ÿ™Ì™™™f™3™fÿÿfÿÌfÿ™fÿffÿ3fÿfÌÿfÌÌfÌ™fÌffÌ3fÌf™ÿf™Ìf™™f™ff™3f™ffÿffÌff™fffff3fff3ÿf3Ìf3™f3ff33f3fÿfÌf™fff3f3ÿÿ3ÿÌ3ÿ™3ÿf3ÿ33ÿ3Ìÿ3ÌÌ3Ì™3Ìf3Ì33Ì3™ÿ3™Ì3™™3™f3™33™3fÿ3fÌ3f™3ff3f33f33ÿ33Ì33™33f333333ÿ3Ì3™3f333ÿÿÿÌÿ™ÿfÿ3ÿÌÿÌÌÌ™ÌfÌ3Ì™ÿ™Ì™™™f™3™fÿfÌf™fff3f3ÿ3Ì3™3f333ÿÌ™f3ÿÿÿ!ùØ,¹´ÿ± H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J´¨Q†,f´ºv´)Ék‰h@€@CS§X9¶ò"•êT/³²Š½uª ¯4и:¶íCAh©ªõ:ˆ­Û»¯µB«`jZ´³ìâ|íš©g©vM+µîàÇØ sÛWêⵂ!c- ÂÜÊRçþ…°4³f£œ[½Ü׫h^8>Zöb‰©&îºZ¶oÚ@}–kÙ+ñ¸¥Yþ"(gà:}{‘küì\¯¹i”Nd\*禡Ãÿôý·ßóÅWû= ¾â]±O³´Þâ®G3žj–¿ÕÂåµUŽÑf÷§Þ{•é _¤†Þk±…g`I€WÖmq×Ú~ga!Ûtz%Ÿ…†DÕR6xZ¢á×l{ 8Œ~-•âI–¶u¡½ÇW~Ieß5Búul³í8ÒyƒL‡X‰ç¹˜ä”uq6Èzçy&U ¥Eöœ“Ù_–Yw%ÒPaaQ}Ø— ·Ñå5^$b™¥dk¹eǦƒSúå›bš×•­pG•ÍɧCî)Ê_wK"Š~øÑ A„œÕà×uBy^nJBØ\"ÎMŠ\])ÿ6WbŸQw™ úõæ5XPg^e ®&šZ±^›«Ͳ˜ufš¤§DzY¤låiºf¢Å;jŽÈtÍ…fëëj4ˆÚå~Å¢g_"Ðz˜æS8çqÚu§˜~Úʾû^3Ë 5$¦Þn{eß–^ªY(¢*Ù! ‰Ø»ˆ çè›lQ‰Zì‡ùw "øž+'zTE°X BÒ-žén¬Ýž5ú£Bpç–œî6¬¬TÕ0H¿­(·TÈ^Sh â·+Ì"fêqˆp™î‘ä‰ìÄúÁX$ULû–duhEÃY/&Ê`‡]Ö±“–…-v=c}±Ÿœ‚z§}ÍÿJ6b5„­—Á“^+°kÂG`+]\Þ°…Þ騚ŽÊaÛa[1Ÿß–ˆ#­Ò Ã ¦\ºJÚFªyØ¡í) ¥Ã0“œså÷™ˆ’*š}•:¸©Í¢}œ—ï(8‰kqŽ:¹¾âÖº«OøÙ–á®kÕ•+xçï…夿Õw7/ƒÅ/H-ëž®Û_¶ìkš›ˆ‚Çùò÷Ü-øšqø£KWj¥Jý¹Vc]Œ  ƒíõ®U;º†rèv½ßi+V‰ÙU+LVûé:”³Ñ’B,\¬ H¢ Sd8þ´Ìk]ò[õT˜­@ oBØ ©Ä›m½çMéœ àv¡¦‰UðÍ„ÿÎã±Ô”NY¦rY~lȽ%¤,J ¯°Gõ¼æ3\ÚÅz•·/¨ÇR ’U… ©Œ±‡×`¬’–+SáÈnZb`–ö¯í BMJˆT@EB: ëkýC õ–/ˆ?`2eÚW%ãÀo!\Á 7D2 ¢WžltÀ¥÷ÀImÃֳ>…"°¢mhœNÁ¶:Ô¹Ë+yÚ×Ô2 ÒÁ¥?ÄeÞVJI/òÑ4fý ‘ RIfCÞÜæv¯S@löØKlp<ÁtËò§(ë™ð›ýb‰\‰Ì‡-<\ˆ›ò8Å&@Ö¢’§ˆU™Ýtås¬cÌÎTÿLiJe‰ ;i#L$‰«jÅ#ç7g©)]KFŒàC £#èœm_ü'´Ú”LiÍIçtå*WcO¿ "ýªfV8#Pù m£‰g—°G'¯ýežú“QóÎÙЦÿáJ 92‡î‹EÊ”9H±r.²5 ˆJ7£ƒ!ñ[—CÇÆ©¤¾òoÄá ê®G{D¤AëZ¸c=†Žr4«Œ§^cÅÜ‘ïGÂ2ë@&朅0| óL>¹*¨š…ïS'‡²À ½Ff8‚˜XZ…w¥gþ >j²Z_`èœY†k¤©s,V[CØl‰rb V<ï9V¡lzjáƒÚ„–³ÿTÔ^Xe¦'+Ê+er4ÚÒV>c›å ìV{!Ħ*ðÑí‹KŽþ@²Ö+‰,¤TúZCˆ:Ѭ…éñf(¢-±±»ãÃb>ÃGº8š°¯âëçwÁ[­±¥«3ÖÍ%î¦t;ñvJK™(»r'Ñr‡|›ªçTöUÃ;’ ´Òo¥7ºÁ½²2c¯AukÂå^/Œ¬ç°ô¶¡W20}a˜ô*âYÊLÞú¸ÔÍSÏ„´Í¯êSãlx3£J„ˆ "µµÌa~ñØ{Žˆ@°¢0D6ΊY|àÕ}QXÓ\-\Öj@i•M“ÿW(ø1—>!¤7OV¤Ñ0ÿm/žã:݆K¯ðQ´§=ffuA5㵎~r޲ÂÌ´+%“9ŽÅÆ„iK²£6—9¬~´ó^¬Êc…X|H#ûÓ˜‚üZ‰JZ9¿ÂìtDÊU+ôê­rˆ®„&¿0Kì>M¸ÛÙdÆûÍÕ¨hÈ“4®kµX=ègAju?½”4æ|AJšR0‡Ú{æÇ-_”±–zkë+°Àe³Ù‘#¾E¢ìÜ‹<̯¿‡³yá’\²æjSÅ«F…ùµåzâÜš/`_-! Ó\*ê>ú”!p„¾€ì y!BóÛ«¸ÕÀ‘Fnâ\•™‰p2tx_øãfa<ÞO¤ÿ+×"ëH¯lÏ_þ&“d˜§^Oï›û‹ÚRWž³±ÊføÓ¨ ¥ùò¢Q\hÓ7ñܼå—éÅ_,*î_×Gì+½prsžƒ•êLuÅŽi‰À¾—¹;}°p\Dñ7®B:ÉPa”à<[²™u·4Ê’Tᦞ馂XéM­´Ó/6è†Öù6^§¬ñ˚ꋚe½¸©+€7Mò5Ï‹%pÎɰJøØŒÕHÆ|>þФ`C\]}Ïoe¶3ÓqØê•óll ç敃2˜ìK¹Â.¸–‹Ûhö›l“•ÛYOu}T1W¡¿ø•ˆAÐz˜r-x ä¯!ƒ]¿£ÿŸûEƒ½Ç¸¥#r›7!™l^¢+ÒBCßèöVn>þÝ^NÍnþ ‘²õvöË!””yGVAÕÖ-n²9ÞÒ¢äqG…^ßäj–p1%$Kјt´N¸kA–bð"ð5CÁÇ ‹± xq2wf_S0]æq$Sµ–/ôdcÚÁëƒG±RÕ%yvÅ:VQýW{ãôÎá3ƒÁ[ä<S+EBzãr“O8(i¹R×€Á„0]2G–£Fj¤^ P’•XÊ@<˜<hN5SÂòn—eNu¡,“Dƒ²cVáó1PaX`ÿ3a‰:ܤvQ³a.‚IIè{aˆ'64@—èh]¨LBD`Îe-tgƒ#I¢5‡4dˆI˜U€f'>rIãõ"ו%¹–hž¨33VI©åP ö;¬Clw!$"9$s’exñ"{87]âàáiâ”.Àà’Vgn·ÕC±·e¼&oðÒ…aÅt’ ›×5·§9¦#d-$Ï&xˆ0pFÆfF6qæ¤Swe0§¨e–’+s‘%·T!þH÷Lõsak15ã„ë£vžR3«¡‰_ÒOˆ]'BŸ¤Ñ]&ÔCbç†öF{ÛÔJŠ%HÆÃ5F³1¸t+ì)‘š§5Íÿ5hA¢^z‘x†2m„Tj§Z˜Q)u±,H–„ÉáÎÑ+ F;¥~!ó2)§CIO™b~1=nÍ3Šýaq…æq”mÈÓ\QQ'¤Ç8‘Õ##“¾X¸'¤„±Q@B(5ÈX‹£SiC9ÆEÕ¸D™YªggZrXï8rÇ$X‚5V$5˜€Úb?E„i DkˆM q Ë`6f“a4¡¨¥Ö(%—¦œa«—äV_!‡³F34x6cžD"Ëù×ð × Õj­3ÁRù1ôø„¶@IH<J-”X˜3Ÿ4Ã5g#ñ®ËÕÊ«õ§Ÿ–9gþr4ÝT‡F#þ$† z>¾b1\±®ºêñʰÜs©‚U7Sÿ iGÒZ–Sƒ•6+Vû±aðmÿf€!y$Ròƒý—O}q$à’Yøâ1½££ñH'3Ëð {­P\Š#"©©%Ï9nŠAH¶Yneg—«4ˆKfªá¿ Å ¯ã†ÂHM•X:é@;3b3°%b݈àÁ{Âïš ¹à®eê ôŸÜT˜ØZ3ëG,@Ëk8hœiab-”x0ÃÑÀ·~{,¥¯<ë&°s6Hc±RD½ÃkyyËZ¡b%¹Š×P ”û·z 3­à¬ÄÄÒ ÉyB.KC{5$–-†_GMz1¡€yªËÀ·¹° ®«ºzáž-0NƒÇu¢tE›[§ì+w2|QRƒÍûufÿË®ÇÛº`‹p"Rx~±+=¿›³µpÒ¥ ã^‰z~Ëk¾ãË·MÛþ"vþ¾ÛY®·õ+°ðÒ F\Ú [zÀ÷k«‹¼ÉÁ§Új»r.: w©‘R,ÙöT™ÉYVg §—·,œ<Œ¼e[¾lP›7Ü?vÂ;äáƒárXÂZ§HÇ@¬V[‹Y­¬+Ã/\ªÜ±sï*ÐDZ¤V¶bŽ er¨ÊF枉€\ÛÄ‘¿L+ÆÑ:Ó*‰óE’³–ãÕ[dKƒ¾ÍÒ×Á(|`‰‘ãù[Á4ŒÊ¢‰mS·@¤,²H¡ÿª3+Ô@I…N£$‰;Õâ‘.ücË·3,'‡VK1½Y.!¹H^y)aô4€ä3øV*7‹˜Ì¢1kÆ l6óª@…qgºr±’È7ò˜›&õµUëù%±s`ŠÅ•a7˳Ð+_à1!±ÉdKËÑn’%ziz˜¡Jx$7þžø„¯%”`£z>»¢LsªÖ¬¼l¾O§ƒ`¢(É+jáp[ŨØs½Ü~7V'\¤:“Û·¿ Ï „~(±¶¨*SÒ‚æWâõ,…â£YC”çáÁ:)ŸØL -ϳ¡°ËP Å0­#]ÃÒW}ƒÓ°U­:ÿ¹s5ISŠU]±3ÅÝ<bÑá;c;Ã…¯KKÁœŒË+a¿>¢RÜAµˆŠ^/––Ѹ mj*Á Kü *MÁ^­Ò›\ÆóÜSEwCP¶\-’©+Æ©¡1úyX¥áE¨´ÔG¼^ÎÑÐÕ!˰åQ½Â"ãeO}zµK½gY²Ї¯ ¯ð6ãë·AÍ¿†ƒ ušt._1Œ·}ög Z0X‚3Öèì´³€µõç}ýç¨ò3[˜Ö'$ƒ2ãÜÒ)¡°xMÔ¸ÍT}àRX°¯}bÕBüòuCQÔHíÕ¿ÐÐe­LÍÑ=Òjͬ¼=Q³¸%­)"­Ò,½Ü8ÙVXÞæ}ÞèÞê½ÞìÝÞîýÞðßòí;ntpsec-1.1.0+dfsg1/docs/pic/beaver.gif0000644000175000017500000000541713252364117017272 0ustar rlaagerrlaagerGIF89aîÄÿýöfȤnǧx·šq¬‹]©ƒOŒŒŒgW>^N8[X%YW$WU#J FD?7+===<9/808/$2(1 -#"!   ÿÿÿ!ù,îÿà'Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ Ð¥)‡È¤rÉ<ͨtJ}iXÀUSíz¿IcvÌ›ÏèYqÌÖ–Óð¸üÚÎ>=ò¼ÞK¯»‹x{‚ƒHk~l„Œ8b‡XOF‹Ž–—*†‘’b”˜Ÿ #š›O•¡¨„£›œv‰Š©±{}¬v®Š§²¼^Š´µY¸®¯½Æ|£[kc·ˆ€»ÇÒaÉÖlÎdÐÓÜJšË«Ãm€ÝèBÌ’nœ[͇¯Ñéô4ßÏîÀ¸ãÛõÿ6î¹—Xbʨ˜Á‡ˆ`)œØâ[‚ø ’‘H±# †Íië§Ë£ÉKÿÕ‚a»y'º f¨ÜÁD1cÎ\IǦ9œ9;þºFlÒÅž‘Î¥8Ô¥QZ,Ùý„¹]Ó›I sè†jUnWî¬)õ™×¯ÒÂ>‹TËͳh{¥¼)Ïi[®PâÒ³èç©ÊväZ„«7_š‡o´±pºÄ¬ܪ‹¡ÂŽAA&µ e]P1gÆÔ™L.Œ”@7=mgQÉ!/«¶#šµ#®b]¿}±¶mF¸ƒÑ…fñVËßÇ‚ _K¼ˆñ7ÈcY^oogУë)™I9uw¸n­Á®]è¹›ðþ\ËðüÜßò]¾Iåû¿·?ä ¾9úõ-ß}FíÇ_xîý ÿV¦z¢g„Vhá…VØ@®µ  æ÷ „ÐtÐA„(¦¨â˜xá$~˜Dˆ4z`â8æ¨#Ž,¦ØAƒ2ÆQãDâ·£‰ç€_ié$‘Gþ˜Þ}L6ùä•WF¸d•V¹Á—`†)æ˜ á–\ž1ä´éæ›pÂ)¦™h¦©æ€_Æ©çžm–éä”êÙùEƒyòi¨›~š©de‚ʦ i›’J:©¥•öYæ™t2Úè`öy¨¡ln€"‘‹zú©…BÊæ¥™Fê*¥“"úåyX¦ãª9¼ºç`©¡²jŠ%¼J᫞À ;j±ÜzlÉ&Q(ÿ¦µF*k¥’²¹í›ÝJ;­ªÕ*±| Žh¹?´Ê-¬pr»øR@o­íºû.¼8<:*¥–¾J¯úº©î)Œ ð\¬Á¯‹hÃÓ>Üëµ´n©·ß«¯$Ü&¤¬ò`æ¤Æ7´ª'˜’®¬²¦r>pÀÎ;sà³Ï5 ó Ëfz©¦¤ÌóD«p´l*½4Ï?s0ôçü¥Ô)lq›*OMµÏ»^M´Ì‡n­3Óxý´›a‹}ÀÏe›Cžóæ é°a§,j­´ µÜsm7dý,Üø=/Áp¯MxÏ#˜xøº€|ôæ˜Jjr½ë>9ÿ7¢gùå;d>pà'㻯º‚?ÀÁèᦣ®ƒê««›ïëp¦<;áøÚ!ît {™{ëó [êºÅߪ-v¾š¡òËãÐ<Úq†Û&ÉïÍzð’€}òÚ'¿}÷7Ü&¶´Š·â€ vØd¯ˆû,:ülPµª¥Ì~ Ë“˜Ì8Ƚé 0ûq"ö¥g€4(àÏè+¼A j¡*ì ¶nïG62]ëv¹ªÉŽû ÒÅ1ó K8¡.N4fà‡?Ä  |6¹Ý jáêWâj½8åÐt•1@@E*Qˆ)øY§Ö´ú•І-[bÑò··ÿ <Ñ-B¡2PÅ6 X4·ÈÅ15o†÷B´ÀÔ>Üuˆnt#ã8‚9Ò‘g,›“" %¦ƒ-‹ ìcòÈ*2ކ„3a™JÄfþ°—o¼äáX¹E« ƒ ;ß1Iõ¥x ™½Tå4ƒ)LkŠ›_ƒ­ÔÉ®nªà›”¬¤8ÍFÍÉîšÔsà:ëÕ-^¬"ÏD¥3¥ /rVóg(yUý²©Ï}¾MNäB8™JŽðªž„»gBÙu¾…®ÿs„NÛœ¸Î¢ˆ€ Ô¢«2èASÀ±òt ÛÓœ\0Q 2à(ý”J‹¨Ñ´ô¡ ÝÜßâ4ÓÔô‡7Í@Ní´ÓŒ"ô#d ¶Z/çÑ«ƒK=/­ˆ (§,tLS_È¬Š qÛÔ&2¯Ê§.Ó¨U4唩cÙX€V=BÎ¥kµêã°JS“Rq¸h^—öT¾~Qs „¤CÙÙ1Daˆhf²0:µÆzó±dH§Ê¬Fë²?Ìì/u:¼Vöôå ©Y§¿ðQ•_¨Í¿µXÆšÓhí“øF¨N½ýŠ]am‚+Í Îvö·Àµ*³ô÷8DýÕ|ÿV}k#<ÐÆÝ‚Â¹cƒnt?hÜX¦5² ´•#tî¶—¹—ï0Å[‘/Ò‹¸0*¸Üº^E´—ŠÞÅDoÃ[ƒˆ)´µ•*Uc«ÝApÀÜ­Ì{S1àÂÑ‹ôþ‚:)Ê„ÉM‚ÝXÖOA¾tc^ë$Bï+|Ðû¢‰‡ÐÚž¹Â3Vî€SÌö~„„9BS|B¨HSx U˜…Û5 \HH!;ntpsec-1.1.0+dfsg1/docs/driver_zyfer.txt0000644000175000017500000000322013252364117020025 0ustar rlaagerrlaager= Zyfer GPStarplus Receiver = == Synopsis == ["verse",subs="normal"] Name: zyfer Reference ID: GPS Serial Port: /dev/zyfer__u__; 9600bps 8N1 == Description == This driver supports the http://www.zyfer.com/[Zyfer GPStarplus] receiver. The receiver has a DB15 port on the back which has input TxD and RxD lines for configuration and control, and a separate TxD line for the once-per-second timestamp. Additionally, there are BNC connectors on the back for things like PPS and IRIG output. == Additional Information == link:refclock.html[Reference Clock Drivers] == Driver Options == +unit+ 'number':: The driver unit number, defaulting to 0. Used as a distinguishing suffix in the driver device name. +time1+ 'time':: Not used by this driver. +time2+ 'time':: Not used by this driver. +stratum+ 'number':: Specifies the driver stratum, in decimal from 0 to 15, with default 0. +refid+ 'string':: Specifies the driver reference identifier, an ASCII string from one to four characters, with default +GPS+. +flag1 {0 | 1}+:: Not used by this driver. +flag2 {0 | 1}+:: Not used by this driver. +flag3 {0 | 1}+:: Not used by this driver. +flag4 {0 | 1}+:: Not used by this driver. +subtype+:: Not used by this driver. +mode+:: Not used by this driver. +path+ 'filename':: Overrides the default device path. +ppspath+ 'filename':: Not used by this driver. +baud+ 'number':: Overrides the default baud rate. == Configuration Example == ---------------------------------------------------------------------------- refclock zyfer ---------------------------------------------------------------------------- ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntpkeygen.txt0000644000175000017500000000275613252364117017334 0ustar rlaagerrlaager= ntpkeygen - generate public and private keys = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice23.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] Alice holds the key. |============================== == Related Links == include::includes/manual.txt[] == Table of Contents == * link:#synop[Synopsis] * link:#descrip[Description] * link:#conf[Initial configuration] * link:#cmd[Command Line Options] * link:#run[Running the program] * link:#access[Key file access and location] * link:#trust[Trusted Hosts and Groups] * link:#ident[Identity schemes] * link:#rand[Random Seed File] * link:#fmt[Cryptographic Data Files] ''''' include::includes/ntpkeygen-body.txt[] // The end of "Cryptographic Data Files" runs into this following text. image:pic/sx5.gif[] Figure 1. Typical Symmetric Key File Figure 1 shows a typical symmetric keys file used by the reference implementation. Each line of the file contains three fields, first keyno an integer between 1 and 65534, inclusive, representing the key identifier used in the `server` configuration commands. Next is the key type for the message digest algorithm, which can be any message digest algorithm supported by the OpenSSL library. For details, see {ntpkeysman}. +ntpkeygen+ just makes a sample file with good random keys. You can edit it by hand to change the keyno or keytype and/or copy lines to other key files. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/driver_truetime.txt0000644000175000017500000000776413252364117020545 0ustar rlaagerrlaager= TrueTime GPS/GOES Receivers = == Synopsis == ["verse",subs="normal"] Name: truetime Reference ID: TRUE Serial Port: +/dev/true+'u'; 9600bps 8N1 Features: +tty_clk+ == Deprecation warning == This refclock is deprecated and obsolete. The NTPsec maintainers plan to remove it in a future release. If you have a requirement for it, please make this known to us. This driver reports only two-digit years, and is thus reliant on the system clock to be near correct before samples will be processed properly. You will not be able to use it to run autonomously, nor will it reliably recover from a trashed or zeroed system clock. == Description == This driver supports several models models of Kinemetrics/TrueTime timing receivers, including GPS- DC MK III and GPS/TM-TMD GPS Synchronized Clock, XL-DC (a 151-602-210, reported by the driver as a GPS/TM-TMD), GPS-800 TCU (an 805-957 with the RS232 Talker/Listener module), and very likely others in the same model families that use the same timecode formats. Most of this code is originally from refclock_wwvb.c (now refclock_spectracom.c) with thanks. It has been so mangled that wwvb is not a recognizable ancestor. [literal] Timecode format: ADDD:HH:MM:SSQCL A - control A (this is stripped before we see it) Q - Quality indication (see below) C - Carriage return L - Line feed Quality codes indicate possible error of: -------------------------------------------------- GPS-TM/TMD Receiver ? +/- 500 milliseconds # +/- 50 milliseconds * +/- 5 milliseconds . +/- 1 millisecond space less than 1 millisecond -------------------------------------------------- In general, an alarm condition is indicated by ? at A, which occurs during initial synchronization and when received signal is lost for an extended period; unlock condition is indicated by other than in the quality field. == Notes on the TL-3 receiver: == The mini-DIN RS-232 port uses the Apple pinout. Send the clock ST1 to turn on continuous (1/sec) timecodes. You can also enable "mode C" via the front panel. ST0 turns off this mode. QV will return the firmware revision (and is useful in identifying this clock.) QW will return its weekly signal log, useful if you're testing antennas. You may wish to turn the loss interval down from 4h (04) to 1h (01), so the receiver declares itself unlocked sooner. When in holdover, drift can be on the order of 10 ms/hr since there is no high quality reference oscillator. == Monitor Data == When enabled by the +flag4+ option, every received timecode is written as-is to the +clockstats+ file. == Driver Options == +unit+ 'number':: The driver unit number, defaulting to 0. Used as a distinguishing suffix in the driver device name. +time1+ 'time':: Specifies the time offset calibration factor, in seconds and fraction, to be used for the West satellite, with default 0.0. +time2 time+:: Specifies the time offset calibration factor, in seconds and fraction, to be used for the East satellite, with default 0.0. +stratum+ 'number':: Specifies the driver stratum, in decimal from 0 to 15, with default 0. +refid+ 'string':: Specifies the driver reference identifier, an ASCII string from one to four characters, with default +TRUE+. +flag1 {0 | 1}+:: Silence the clock side of ntpd, just reading the clock without trying to write to it. +flag2 {0 | 1}+:: Generate a debug file /tmp/true%d. +flag3 {0 | 1}+:: Not used by this driver. +flag4 {0 | 1}+:: Enable verbose +clockstats+ recording if set. +subtype+:: Not used by this driver. +mode+:: Not used by this driver. +path+ 'filename':: Overrides the default device path. +ppspath+ 'filename':: Not used by this driver. +baud+ 'number':: Overrides the default baud rate. == Configuration Example == ---------------------------------------------------------------------------- refclock truetime ---------------------------------------------------------------------------- == Additional Information == link:refclock.html[Reference Clock Drivers] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/pps.txt0000644000175000017500000001064613252364117016127 0ustar rlaagerrlaager= Pulse-Per-Second (PPS) Signal Interfacing = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice32.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] Alice is trying to find the PPS signal connector. |============================== == Related Links == include::includes/misc.txt[] == Table of Contents == * link:#intro[Introduction] * link:#opsys[Operating System Support] * link:#use[Using the Pulse-per-Second (PPS) Signal] ''''' [[intro]] == Introduction == Most conventional time sources (radio clocks and GPSes) are connected using a serial or USB port operating at speeds of 9600 bps. The accuracy using typical clock-radio timecode formats, where the on-time epoch is indicated by a designated ASCII character such as carriage-return ++, is normally limited to 100 μs; NMEA streams from GPSes have a similar limit. Some are worse; the SiRF line of consumer-grade GPSes, for example, has a long-period wander of over 100 ms. Using carefully crafted averaging techniques, the NTP algorithms can whittle this down to a few tens of microseconds. However, some time sources produce a pulse-per-second (PPS) signal which can be used to improve the accuracy to a few microseconds. This page describes the hardware and software necessary for ntpd to use the PPS signal. The simplest way to collect a PPS signal is from a GPS over a serial handshake pin, typically DCD; Linux supports this. On FreeBSD systems (with the PPS_SYNC and pps kernel options) it can be connected directly to the ACK pin of a parallel port. Clock radios are more complicated; their PPS signal levels are usually incompatible with serial port interface signals. Note that NTPsec no longer supports connection via the RD pin of a serial port. [[opsys]] == Operating System Support == Both the serial and parallel port connection require operating system support, which is available in a few operating systems, including FreeBSD, Linux, and Solaris. The kernel interface described on the link:kernpps.html[PPSAPI Interface for Precision Time Signals] page is the only interface currently supported. Older PPS interfaces based on the +ppsclock+ and +tty_clk+ streams modules are no longer supported. The interface consists of the +timepps.h+ header file, which should be in the /usr/include/ or /usr/include/sys directory of your file system. == PPS Driver == PPS support is built into some drivers, in particular the NMEA driver, and may be added to other drivers in future. Alternatively, the PPS driver described on the link:driver_pps.html[PPS Clock Discipline] page can be used. It operates in conjunction with another source that provides seconds numbering. The selected source is designate a prefer peer, as using the +prefer+ option, as described on the link:prefer.html[Mitigation Rules and the +prefer+ Keyword] page. The prefer peer is ordinarily the radio clock that provides the PPS signal, but in principle another radio clock or even a remote Internet server could be designated preferred Note that the +pps+ configuration command has been obsoleted by this driver. [[use]] == Using the Pulse-per-Second (PPS) Signal == The PPS signal can be used in either of two ways, one using the NTP grooming and mitigation algorithms and the other using the kernel PPS signal support described in the link:kern.html[Kernel Model for Precision Timekeeping] page. The presence of kernel support is automatically detected during the NTP build process and supporting code automatically compiled. In either case, the PPS signal must be present and within nominal jitter and wander tolerances. In addition, the prefer peer must be a truechimer; that is, survive the sanity checks and intersection algorithm. Finally, the offset of the system clock relative to the prefer peer must be within ±0.5 s. The kernel maintains a watchdog timer for the PPS signal; if the signal has not been heard or is out of tolerance for more than some interval, currently two minutes, the kernel discipline is disabled and operation continues as if it were not present. An option flag in the driver determines whether the NTP algorithms or kernel support is enabled (if available). For historical reasons, the NTP algorithms are selected by default, since performance is generally better using older, slower systems. However, performance is generally better with kernel support using newer, faster systems. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/icons/0000775000175000017500000000000013252650651015673 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/docs/icons/mail2.gif0000644000175000017500000000043713252364117017367 0ustar rlaagerrlaagerGIF89a"ƒ„„ÿÿÿÿÅÞÖsœœsŒ”ÖïÖŒ¤¤)BZ”œ­­¤½µ½ÎÅ!ù,"ÌÈI«½7h{å’ €^&•H–S€¾”°’KÜ0Žsiã8€n·ú¼€A±÷:‘ÉÏø|"«œ…)ÉAQ@¬­ÕÂ/M0{F\…ÍÄs·Ù=ç»Nwn*ep}/l=Sz|rcˆb`c‘pqL¦¦¯-´-­•¶g¶‚®ºƒ£¬³¿¬2o¹Ä·2S¨É["#ш4Ï˪Ôa„¨ÔÙ<ÁÜÀÒeáÅß×ÜÚäå[hìï;ntpsec-1.1.0+dfsg1/docs/icons/sitemap.png0000644000175000017500000000541113252364117020041 0ustar rlaagerrlaager‰PNG  IHDR®9sRGB®ÎégAMA± üa pHYsÃÃÇo¨dtEXtSoftwarePaint.NET v3.5.87;€] yIDATXG•—iP[×ÇIâÉ$ÓtŸ&i›Õ±ãØ8Á,ñÆbvƒÄ.± ˆÕ ±H$!6- V ľ› bß± ¶ñJKëL'ù”f¦3í—f¦î$:ù÷¨î3~Õ¸ãr6jç³Ð²T]ç , DºŒPù*‹7â¸ê-ä4î`gׂ™û,<ay/[ ˜˜›Gniwçÿ ¿ºóàŽ±s^ÿ¤n6†™4è¦ÓP5“š¹,t-´£~p‰e+–.!´páEëp¼Êmlܾ¥Z,m/cc·Â pý±þ4‹îaËU½iì—?A‰_Ú¹w·ÆxMþuiw"|TŽ'£}CõÀ0wÆÅbôÌN#Q½ ŸøVxrj‘¤Ù@ùÕ-dÔÞBEÏÌã±þP…Ý}6Kps·ïkYeþ“[ÖÛ&š>¯ž "ž¼†\{Єþ--4“)ÐM1 ¤£i¡ ÷ÀÏ (w޶aÑ À(в°èÒ-L¬ÞÂÔDë,?ãÞÞV¶#>¯‹éŽ€ïÌ,mäÒªÿS:ÄCš:ªÁxô\/GÙh´ã©¨Ÿ–£ÅÒˆæ¡EðŠgpåX9°m¡²„Éב¢»‰éŒoÇ7™¡éß…°îPRW¸qµw àwOèáˆTÓþwØòžHˆJB¡ˆC÷f9*FD$s'T¦q̆oú(ü3Ç­bÈÛW „xê ˜ÇVqã~/ÖîÕâÞîøú{Ö ÀÀ:¤@ªÚ‚Îù.´®¡~! ˘Öò1tÝ„d©¹ŽI.O•uÁ#B‡PaZzæÑÖ¿ˆÁk4Ð;Õ´ç¹&\ˆn†kl+ÜyðH솧 ÞÉW÷¾£uBÞ<Ê:—Ð`Q@?Ež1›…ºùl4,Iл©…º¾ç&y·´|g|•°÷Tà´w >ô+Ãň*ð%H+îF}çØÂV„¦w":§¬ø¤—Œ"¡hžI½V_0º¿ÙS`Q+êÈëk®@3‘‘<Õ3™˜ßë@ûZ j:úŸØ9*fŠ5£8á*ÅIw9N€=à[ ‡€J¸q P¡oë${m  «‘«KGÃp-TÆIxð»l C´L L €òj–Ð1Û„ò1$ÕqPu a^•cäÑW¿±sf+dåƒ8~>'.à÷BœºTŒÓ^*+@pR#*Ú4¸REþ‰)¹ÍñÐô*aìYBey·v 1y#ðÚ¬­8`É4¦uXÖ6a¶è¡LµšUã|ÚÇaçH+/¤äÇΊqü\.ÞgÜÀ£È P¤ï€Ì”ŠÌ®Ô9ŸØ‡sÊ{dÐö)Q?\E< ®xx€ZÑÏÖŠŒpd³(6m ix<`]sÃhèø†ÙóÞ¸ Á{çàØ!€Ô4ÈŒôêŠpàZ²S,)ÀCž™‚ÖD*ÎF$S[R xQ'øˆ†È Hê„3 +˜GDá"JÛo £¸íŸÖjˆÍpö–á]§¬Cù¤€ Êj3ĵ<¤êCmŒ‘M @Ó>S©ÊQ¸˜‘­}Smfĸ¡Í 2tëðç×Þa’»ðôàVà‡ô}—?+Áûò)ªE™Ymð!€ˆïäSòúV„gôP+2fÔauCOÆŒ˜N €ýBd:aâªe¸r*«­G^ûi”À€£ŽéxûÌ·ÇÎIpÚƒV¯ëBVUô!€0k d‘Üä Tv£edçÉŒ.D“1^@nøÔ 3JcÌh œ‚d”Nþˉ¥`x{r_\ÅxëCÞfpÌÄQ—l*D ÒòÌ(j̰’EA¤@mÀ(c÷üšp6¢ç¸F\ˆ²™£Ùñ1v\P¿ – a×êí¶ƒåˆ'·â¯ÜR¼qJhxÇ!í)WP ][9’Ê.CXˆ èBiàÂ<Ô†˜ÌV8³«àZƒ³áõ8Ï9Àkß BŒÌ§C¥jÔaßžj É˾tõ—á { …HÃ;¤€G° -íà«ýl¤”µÉQR5§ÀJšP § oþWQ?²*fÈ9+~ç<·­þu:ÝîŠåpö¼yš>Jƒ½«C“Ä«|öJ/C¤ Erº˜ÜP…¾‚Z€.!«ç8´Mt(µ U5…‘‰y“ ùÁW)¦ò/†©?‹O£‰Üs¤¢DÛim:T~à—ø!CÍG¦Ü¿8-ì7ôQÃÁ¿g5pbëáL‡ÕÁ#¦ÙÚÿBYÿ— Ué¾ÅFFF>™pÅÞ=¼t'–Z좟¿·* w Tºfdë¯ôA^¹±:øDWà$Ù±½—ÒjÇÙ À-²iŠQDe¶àR¸\ÇåeýŒÉñ=ww÷çbcc_ 8 O²§-ø‹0ÛvT)ŽžáMûdœõ)@us;xJ_¨kàâWlõ‚dF'ÉŽ€Ó¤àÇkD†r— p (Rs¹ÜcbbŽðx¼ØlöóO‚ƒƒŸgþIñ2‘1t¿òw Ê© äiÿÆIª†W°Ξy‹¯DY©ù&: ˜ó@‚Sî28x+p>ˆ’ÆÔ šª_(í†7ýœñº’õ*ůiÞŸSŽŸÐõÅÐÐÐìèæyæ>ü) xâ-z>Jñ>Ŭˆ¤H¯°Ü&ï¨òÏÙñzp„êûð3MHÌjBR¶ü,3xYÍà¦ÁâþíÅ-Y JB8ñž4ÇIŠÇhî·)~K÷¿ xÉŽÏç3É_æp8¿¡ë»¶ÁÒÕâ …£-œ¼B2.,™Å)Pþ IùåÁ¯TºÿÂ9°ðÎ9v~m™w:ô=ff.fÎSïÑB_§xå¿G”…J\v"IEND®B`‚ntpsec-1.1.0+dfsg1/docs/icons/home.gif0000644000175000017500000000172713252364117017316 0ustar rlaagerrlaagerGIF89a$÷€€€€€€€€€ÀÀÀÀÜÀ¤ÈðUªÕ+U+€+ª+Õ+ÿ+UUU€UªUÕUÿUU€ª€Õ€ÿ€ªUª€ªªªÕªÿªÕUÕ€ÕªÕÕÕÿÕUÿ€ÿªÿÕÿUUU€UªUÕUÿU+UU+U€+Uª+UÕ+Uÿ+UUUUUU€UUªUUÕUUÿUU€UU€U€€Uª€UÕ€Uÿ€UªUUªU€ªUªªUÕªUÿªUÕUUÕU€ÕUªÕUÕÕUÿÕUÿUUÿU€ÿUªÿUÕÿUÿÿUU€ª€Õ€ÿ€+€U+€€+€ª+€Õ+€ÿ+€U€UU€€U€ªU€ÕU€ÿU€U€€ª€€Õ€€ÿ€€ª€Uª€€ª€ªª€Õª€ÿª€Õ€UÕ€€Õ€ªÕ€ÕÕ€ÿÕ€ÿ€Uÿ€€ÿ€ªÿ€Õÿ€ÿÿ€ªUª€ªªªÕªÿª+ªU+ª€+ªª+ªÕ+ªÿ+ªUªUUª€UªªUªÕUªÿUª€ªU€ª€€ªª€ªÕ€ªÿ€ªªªUªª€ªªªªªÕªªÿªªÕªUÕª€ÕªªÕªÕÕªÿ€ ÿªUÿª€ÿªªÿªÕÿªÿÿªÕUÕ€ÕªÕÕÕÿÕ+ÕU+Õ€+Õª+ÕÕ+Õÿ+ÕUÕUUÕ€UÕªUÕÕUÕÿUÕ€ÕU€Õ€€Õª€ÕÕ€Õÿ€ÕªÕUªÕ€ªÕªªÕÕªÕÿªÕÕÕUÕÕ€ÕÕªÕÕÕÕÕÿÕÕÿÕUÿÕ€ÿÕªÿÕÕÿÕÿÿÕUÿ€ÿªÿÕÿ+ÿU+ÿ€+ÿª+ÿÕ+ÿÿ+ÿUÿUUÿ€UÿªUÿÕUÿÿUÿ€ÿU€ÿ€€ÿª€ÿÕ€ÿÿ€ÿªÿUªÿ€ªÿªªÿÕªÿÿªÿÕÿUÕÿ€ÕÿªÕÿÕÕÿÿÕÿUÿÿ€ÿÿªÿÿÿûð  ¤€€€ÿÿÿÿÿÿÿÿÿÿÿÿ!ù,$´H° Áƒ*\˜ŸÃ‡#Jœø° ʼnþ.J,¨¢¿ Éïc?! †4ÙïdÆŽ;²lé²cA3iÖ¼XðbN;'¤øhЈ‰~\Ê´)Å‚DóIJuêK‰£îÛZuë¾|W#Ô à+Õ}eÁR,¨•ëY®a!ŒZµ®Ú‰éNÕÇ·j܇õJÕ@Ÿ_ŠçÓWøðÄ‚J›JF)± ÉËjÞ̹3Á€;ntpsec-1.1.0+dfsg1/docs/generic_howto.txt0000644000175000017500000002276713252364117020170 0ustar rlaagerrlaager= How to build new GENERIC clocks = Here is an attempt to sketch out what you need to do in order to add another clock to the generic driver: Currently the implementation is being cleaned up - so not all information in here is completely correct. Refer to the included code where in doubt. == Prerequisites == * Does the system you want the clock connect to have the include file termios.h? (You need that for the generic driver) What to do: Make a conversion module (libparse/clk_*.c) == What is the time code format? == Find year, month, day, hour, minute, second, status (synchronised or not), possibly time zone information (you need to give the offset to UTC) You will have to convert the data from a string into a struct clocktime: ------------------------------------------------------------------------------- struct clocktime /* clock time broken up from time code */ { long day; long month; long year; long hour; long minute; long second; long usecond; long utcoffset; /* in seconds */ time_t utcoffset; /* true utc time instead of date/time */ long flags; /* current clock status */ }; ------------------------------------------------------------------------------- Conversion is usually simple and straightforward. For the flags following values can be OR'ed together: |------------------------------------------------------------------------------- | PARSEB_ANNOUNCE | Switch time zone warning (informational only) | PARSEB_POWERUP | No synchronisation - clock confused (must set then) | PARSEB_NOSYNC | Timecode currently not confirmed (must set then), usually on reception error when there is still a chance the generated time is still ok. | PARSEB_DST | DST in effect (informational only) | PARSEB_UTC | Timecode contains UTC time (informational only) | PARSEB_LEAPADD | LEAP addition warning (prior to leap happening - must set when imminent) Also used for time code that do not encode the direction (as this is currently the default). | PARSEB_LEAPDEL | LEAP deletion warning (prior to leap happening - must set when imminent) | PARSEB_ALTERNATE | Backup transmitter (informational only) | PARSEB_POSITION | Geographic position available (informational only) | PARSEB_LEAPSECOND| Actual leap second (this time code is the leap second - informational only) |------------------------------------------------------------------------------- These are feature flags denoting items that are supported by the clock: |---------------------------------------------------------------------------- | PARSEB_S_LEAP | supports LEAP - might set PARSEB_LEAP | PARSEB_S_ANTENNA | supports ANTENNA - might set PARSEB_ALTERNATE | PARSEB_S_PPS | supports PPS time stamping | PARSEB_S_POSITION | supports position information (GPS) |---------------------------------------------------------------------------- If the utctime field is non-zero this value will be take as time code value. This allows for conversion routines that already have the utc time value. The utctime field gives the seconds since Jan 1st 1970, 0:00:00. The useconds field gives the respective usec value. The fields for date and time (down to second resolution) will be ignored. Conversion is done in the cvt_* routine in parse/clk_*.c files. Look in them for examples. The basic structure is: ----------------------------------------------------- struct clockformat _format = { lots of fields for you to fill out (see below) }; static cvt_() ... { if () { return CVT_NONE; } else { if () { ; return CVT_OK; } else { return CVT_FAIL|CVT_BADFMT; } } ----------------------------------------------------- The struct clockformat is the interface to the rest of the generic driver - it holds all information necessary for finding the clock message and doing the appropriate time stamping. -------------------------------------------------------------------------------- struct clockformat { unsigned long (*input)(); /* input routine - your routine - cvt_ */ unsigned long (*convert)(); /* conversion routine - your routine - cvt_ */ /* routine for handling RS232 sync events (time stamps) - usually sync_simple */ unsigned long (*syncpps)(); /* PPS input routine - usually pps_one */ void *data; /* local parameters - any parameters/data/configuration info your conversion routine might need */ char *name; /* clock format name - Name of the time code */ unsigned short length; /* maximum length of data packet for your clock format */ unsigned long flags; /* information for the parser what to look for */ }; -------------------------------------------------------------------------------- The above should have given you some hints on how to build a clk_*.c file with the time code conversion. See the examples and pick a clock closest to yours and tweak the code to match your clock. In order to make your clk_*.c file usable, a reference to the clockformat structure must be put into parse_conf.c. == Driver initialization == TTY setup and initialization/configuration will be done in ntpd/refclock_generic.c. * Find out the exact tty settings for your clock (baud rate, parity, stop bits, character size, ...) and note them in terms of termio*.h c_cflag macros. * in ntpd/refclock_generic.c fill out a new the struct clockinfo element (that allocates a new "IP" address - see comments) (see all the other clocks for example) -------------------------------------------------------------------------------- struct clockinfo { unsigned long cl_flags; /* operation flags (io modes) */ PARSE_F_PPSPPS use loopfilter PPS code PARSE_F_PPSONSECOND PPS pulses are on second usually flags stay 0 as they are used only for special setups void (*cl_poll)(); /* active poll routine */ The routine to call when the clock needs data sent to it in order to get a time code from the clock (e.g. Trimble clock) int (*cl_init)(); /* active poll init routine */ The routine to call for very special initializations. void (*cl_event)(); /* special event handling (e.g. reset clock) */ What to do, when an event happens - used to re-initialize clocks on timeout. void (*cl_end)(); /* active poll end routine */ The routine to call to undo any special initialization (free memory/timers) void *cl_data; /* local data area for "poll" mechanism */ local data for polling routines u_fp cl_rootdelay; /* rootdelay */ NTP rootdelay estimate (usually 0) unsigned long cl_basedelay; /* current offset - unsigned l_fp fractional part (fraction) by which the RS232 time code is delayed from the actual time. */ unsigned long cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional time (fraction) by which the PPS time stamp is delayed (usually 0) */ char *cl_id; /* ID code */ Refclock id - (max 4 chars) char *cl_description; /* device name */ Name of this device. char *cl_format; /* fixed format */ If the data format cann not ne detected automatically this is the name as in clk_*.c clockformat. unsigned char cl_type; /* clock type (ntp control) */ Type if clock as in clock status word (ntp control messages) - usually 0 unsigned long cl_maxunsync; /* time to trust oscillator after losing synch -- seconds a clock can be trusted after losing synchronisation. */ unsigned long cl_speed; /* terminal input & output baudrate */ unsigned long cl_cflag; /* terminal io flags */ unsigned long cl_iflag; /* terminal io flags */ unsigned long cl_oflag; /* terminal io flags */ unsigned long cl_lflag; /* terminal io flags */ unsigned long cl_samples; /* samples for median filter */ unsigned long cl_keep; /* samples for median filter to keep */ median filter parameters - smoothing and rejection of bad samples } clockinfo[] = { ...,,... { < your parameters> }, }; -------------------------------------------------------------------------------- Well, this is very sketchy, I know. But I hope it helps a little bit. The best way is to look which clock comes closest to yours and tweak that code. Two sorts of clocks are used with parse. Clocks that automatically send their time code (once a second) do not need entries in the poll routines because they send the data all the time. The second sort are the clocks that need a command sent to them in order to reply with a time code (like the Trimble clock). For questions: mailto:kardel@acm.org[kardel@acm.org]. Please include an exact description on how your clock works. (initialization, TTY modes, strings to be sent to it, responses received from the clock). ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntpdig.txt0000644000175000017500000000060513252364117016604 0ustar rlaagerrlaager= ntpdig - Simple Network Time Protocol (SNTP) Client = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/dogsnake.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] S is for snakeoil. |============================== ''''' include::includes/ntpdig-body.txt[] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/driver_shm.txt0000644000175000017500000002154313252364117017465 0ustar rlaagerrlaager= Shared Memory Driver = == Synopsis == ["verse",subs="normal"] Name: shm Reference ID: SHM == Description == This driver receives its reference clock info from a shared memory-segment. The shared memory-segment is created with owner-only access by default, unless otherwise requested by the mode word for units ≥2. Units 0 and 1 are always created with owner-only access for backward compatibility. == Structure of shared memory-segment == ------------------------------------------------------------------------------ struct shmTime { int mode; /* 0 - if valid is set: * use values, * clear valid * 1 - if valid is set: * if count before and after read of data is equal: * use values * clear valid */ volatile int count; time_t clockTimeStampSec; int clockTimeStampUSec; time_t receiveTimeStampSec; int receiveTimeStampUSec; int leap; int precision; int nsamples; volatile int valid; unsigned clockTimeStampNSec; /* Unsigned ns timestamps */ unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */ int dummy[8]; }; ------------------------------------------------------------------------------ == Operation mode=0 == Each second, the value of +valid+ of the shared memory-segment is checked: If set, the values in the record (clockTimeStampSec, clockTimeStampUSec, receiveTimeStampSec, receiveTimeStampUSec, leap, precision) are passed to +ntpd+, and +valid+ is cleared and +count+ is bumped. If not set, +count+ is bumped. == Operation mode=1 == Each second, +valid+ in the shared memory-segment is checked: If set, the +count+ field of the record is remembered, and the values in the record (clockTimeStampSec, clockTimeStampUSec, receiveTimeStampSec, receiveTimeStampUSec, leap, precision) are read. Then, the remembered +count+ is compared to current value of +count+ now in the record. If both are equal, the values read from the record are passed to _ntpd_. If they differ, another process has modified the record while it was read out (was not able to produce this case), and failure is reported to _ntpd_. The +valid+ flag is cleared and +count+ is bumped. If not set, +count+ is bumped == Mode-independent post-processing == After the time stamps have been successfully plucked from the SHM segment, some sanity checks take place: * The receive time stamp of the SHM data must be in the last 5 seconds before the time the data is processed. This helps in weeding out stale data. * If the absolute difference between remote and local clock exceeds the limit (either _time2_ or the default of 4hrs), then the sample is discarded. This check is disabled when _flag1_ is set to 1. == GPSD == http://www.catb.org/gpsd/[_GPSD_] knows how to talk to many GPS devices. It can work with _ntpd_ through the SHM driver. The _GPSD_ man page suggests setting minpoll and maxpoll to 4. That was an attempt to reduce jitter. The SHM driver was fixed (ntp-4.2.5p138) to collect data each second rather than once per polling interval so that suggestion is no longer reasonable. *Note:* The _GPSD_ client driver uses the _GPSD_ client protocol to connect and talk to _GPSD_, but using the SHM driver is the ancient way to have _GPSD_ talk to _ntpd_. There are some tricky points when using the SHM interface to interface with _GPSD_, because _GPSD_ will use two SHM clocks, one for the serial data stream and one for the PPS information when available. Receivers with a loose/sloppy timing between PPS and serial data can easily cause trouble here because _ntpd_ has no way to join the two data streams and correlate the serial data with the PPS events. == Clockstats == If flag4 is set when the driver is polled, a clockstats record is written. The first 3 fields are the normal date, time, and source designator common to all clockstats records. The 4th field is the number of second ticks since the last poll. The 5th field is the number of good data samples found. The last 64 will be used by _ntpd_. The 6th field is the number of sample that didn't have valid data ready. The 7th field is the number of bad samples. The 8th field is the number of times the mode 1 info was update while _ntpd_ was trying to grab a sample. Here is a sample showing the GPS reception fading out: ------------------------------------------------ 54364 84927.157 SHM(0) 66 65 1 0 0 54364 84990.161 SHM(0) 63 63 0 0 0 54364 85053.160 SHM(0) 63 63 0 0 0 54364 85116.159 SHM(0) 63 62 1 0 0 54364 85180.158 SHM(0) 64 63 1 0 0 54364 85246.161 SHM(0) 66 66 0 0 0 54364 85312.157 SHM(0) 66 50 16 0 0 54364 85375.160 SHM(0) 63 41 22 0 0 54364 85439.155 SHM(0) 64 64 0 0 0 54364 85505.158 SHM(0) 66 36 30 0 0 54364 85569.157 SHM(0) 64 0 64 0 0 54364 85635.157 SHM(0) 66 0 66 0 0 54364 85700.160 SHM(0) 65 0 65 0 0 ------------------------------------------------ The clock identification is normally the driver type and unit, but if your ntpd was built in strict Classic compatibility mode it will be a magic clock address expressing the same information in a more opaque way. == The \'mode' word == Some aspects of the driver behavior can be adjusted by setting bits of the mode option of the refclock declaration. ["literal",subs="normal"] .mode word bits and bit groups [cols="10%,10%,10%,70%",options="header"] |============================================================= | Bit | Dec | Hex | Meaning | 0 | 1 | 1 | The SHM segment is private (mode 0600). This is the fixed default for clock units 0 and 1; clock units >1 are mode 0666 unless this bit is set for the specific unit. |1-31 | - | - | _reserved -- do not use_ |============================================================= == Driver Options == +unit+ 'number':: The driver unit number, defaulting to 0. Used as a distinguishing suffix in the IPC unit name. +time1+ 'time':: Specifies the time offset calibration factor, in seconds and fraction, with default 0.0. +time2+ 'time':: Maximum allowed difference between remote and local clock, in seconds. Values <1.0 or >86400.0 are ignored, and the default value of 4hrs (14400s) is used instead. See also flag 1. +stratum+ 'number':: Specifies the driver stratum, in decimal from 0 to 15, with default 0. +refid+ 'string':: Specifies the driver reference identifier, an ASCII string from one to four characters, with default +SHM+. +flag1 {0 | 1}+:: _Check_ the difference limit check if set. _Skip_ the difference limit if not set (default). There is really no need for this flag, just let the upper level logic prune out false tickers. NOTE: this flag is the opposite of flag1 in NTP Classic. +flag2 {0 | 1}+:: Not used by this driver. +flag3 {0 | 1}+:: Not used by this driver. +flag4 {0 | 1}+:: If flag4 is set, clockstats records will be written when the driver is polled. +subtype+:: Not used by this driver. +mode+:: Can be used to set private mode +path+ 'filename':: Not used by this driver. +ppspath+ 'filename':: Not used by this driver. +baud+ 'number':: Not used by this driver. == Configuration Example == The most common use of this driver is to get samples from a gpsd instance: ---------------------------------------------------------------------------- refclock shm unit 0 refid GPS refclock shm unit 1 prefer refid PPS ---------------------------------------------------------------------------- == Public vs. Private SHM segments == The driver attempts to create a shared memory segment with an identifier depending on the unit number. Unix creates a shared memory segment with a key value of \0x4E545030+_u_, where _u_ is again the clock unit. (This value could be hex-decoded as \'NTP0', \'NTP1',..., with funny characters for units > 9.) Public access means a permission set of 0666, while private access creates the mapping with a permission set of 0600. ntpd is started as root on most POSIX-like operating systems and uses the setuid/setgid system API to run under reduced rights once the initial setup of the process is done. One consequence out of this is that the allocation of SHM segments must be done early during the clock setup. The actual polling of the clock is done as the run-time user; deferring the creation of the SHM segment to this point will create a SHM segment owned by the runtime-user account. The internal structure of ntpd does not permit the use of a refclock option if this is to be avoided; this is the reason why a mode bit is used for the configuration of a public segment. == Additional Information == link:refclock.html[Reference Clock Drivers] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/bugs.txt0000644000175000017500000000261713252364117016264 0ustar rlaagerrlaager= NTP Bug Reporting Procedures = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/hornraba.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] The rabbit toots to make sure you read this. |============================== ''''' == Security Bug Reporting Procedures == If you find or suspect a security related program bug in this distribution, please send a report to {project-security-list}. Please do not contact developers directly. == Non-Security Bug Reporting Procedures == If you find or suspect a non-security related program or documentation bug in this distribution, please enter a report on the project's {project-issuetracker}[issue tracker]. Bugs reported this way are immediately forwarded to the developers. Please do not contact the developers directly. If you wish to send a report via electronic mail, please remember that your report will be held until one of our volunteers enters it in the project's issue tracker. The email address for these reports is {project-bug-list}. You will need to join the bugs list to participate directly in any e-mail discussion regarding your report. If you don't register and we have questions for you we won't be able to make progress on fixing your problem. Please directly register on and use our bugtracker instance to report issues. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/discover.txt0000644000175000017500000001244713252364117017144 0ustar rlaagerrlaager= Automatic Server Discovery Schemes = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice51.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] Make sure who your friends are. |============================== == Related Links == include::includes/hand.txt[] == Table of Contents == * link:#assoc[Association Management] * link:#pool[Server Pool Scheme] ''''' [[modes]] == Introduction == This page describes the automatic server discovery schemes provided in NTPv4. There are three automatic server discovery schemes: broadcast, manycast, and server pool; which are described on this page. The broadcast scheme utilizes the ubiquitous broadcast or one-to-many paradigm native to IPv4 and IPv6. The manycast scheme is similar to specifying to broadcast, but the servers listen on a specific address known to the client. The server pool scheme uses DNS to resolve addresses of multiple volunteer servers scattered throughout the world. All three schemes work in much the same way and might be described as _grab-n'-prune._ Through one means or another they grab a number of associations either directly or indirectly from the configuration file, order them from best to worst according to the NTP mitigation algorithms, and prune the surplus associations. [[assoc]] == Association Management == All schemes use an iterated process to discover new preemptable client associations as long as the total number of client associations is less than the +maxclock+ option of the +tos+ command. The +maxclock+ default is 10, but it should be changed in typical configuration to some lower number, usually two greater than the +minclock+ option of the same command. All schemes use a stratum filter to select just those servers with stratum considered useful. This can avoid large numbers of clients ganging up on a small number of low-stratum servers and avoid servers below or above specified stratum levels. By default, servers of all strata are acceptable; however, the +tos+ command can be used to restrict the acceptable range from the +floor+ option, inclusive, to the +ceiling+ option, exclusive. Potential servers operating at the same stratum as the client will be avoided. Additional filters can be supplied using the methods described on the link:authentic.html[Authentication Support] page. The pruning process uses a set of unreach counters, one for each association created by the configuration or discovery processes. At each poll interval, the counter is increased by one. If an acceptable packet arrives for a persistent (configured) or ephemeral (broadcast) association, the counter is set to zero. If an acceptable packet arrives for a preemptable (manycast, pool) association and survives the selection and clustering algorithms, the counter is set to zero. If the the counter reaches an arbitrary threshold of 10, the association becomes a candidate for pruning. The pruning algorithm is very simple. If an ephemeral or preemptable association becomes a candidate for pruning, it is immediately demobilized. If a persistent association becomes a candidate for pruning, it is not demobilized, but its poll interval is set at the maximum. The pruning algorithm design avoids needless discovery/prune cycles for associations that wander in and out of the survivor list, but otherwise have similar characteristics. Following is a summary of each scheme. Note that reference to option applies to the commands described on the link:confopt.html[Configuration Options] page. See that page for applicability and defaults. [[pool]] == Server Pool Scheme == The idea of targeting servers on a random basis to distribute and balance the load is not a new one; however, the http://www.pool.ntp.org/en/use.html[NTP Pool Project] puts this on steroids. At present, several thousand operators around the globe have volunteered their servers for public access. In general, NTP is a lightweight service and servers used for other purposes don't mind an additional small load. The trick is to randomize over the population and minimize the load on any one server while retaining the advantages of multiple servers using the NTP mitigation algorithms. To support this service, custom DNS software is used by pool.ntp.org and its subdomains to discover a random selection of participating (in-country) servers in response to a DNS query. The client receiving this list mobilizes some or all of them, similar to the manycast discovery scheme, and prunes the excess. Cryptographic authentication is not required. The pool scheme is configured using one or more +pool+ commands with DNS names indicating the pool from which to draw. The +pool+ command can be used more than once; duplicate servers are detected and discarded. In principle, it is possible to use a configuration file containing a single line +pool pool.ntp.org+. The NTP Pool Project offers instructions on using the pool with the +server+ command, which is suboptimal but works with older versions of +ntpd+ predating the +pool+ command. Use of the +server+ command does a one-time DNS lookup, and uses the IP address returned thereafter. If the server becomes unavailable, the DNS will not be re-resolved. The +pool+ command will use multiple servers that the DNS resolves to, refreshing as required. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/driver_local.txt0000644000175000017500000000540713252364117017771 0ustar rlaagerrlaager= Undisciplined Local Clock = == Synopsis == ["verse",subs="normal"] Name: localclock Reference ID: LOCL == Description == Note: *We recommend against using this driver.* A much more flexible replacement is described on the link:orphan.html[Orphan Mode] page. This driver was intended for use in an isolated network where no external source of synchronization such as a radio clock or modem is available. It allows a designated time server to act as a primary server to provide synchronization to other clients on the network. Pick a machine that has a good clock oscillator and configure it with this driver. Set the clock using the best means available, like eyeball-and-wristwatch. Then, point all the other machines at this one or use broadcast mode to distribute time. Another application for this driver is if a particular server clock is to be used as the clock of last resort when all other normal synchronization sources have gone away. This is especially useful if that server has an ovenized oscillator. For this you would usually, but not necessarily, configure this driver at a stratum greater than any other likely sources of time, such as the default 5 for this driver, to prevent this driver taking over when legitimate sources elsewhere in the network are available. To further protect the Internet infrastructure from accidental or malicious exposure to this driver, the driver is disabled if another source is available and operating. == Monitor Data == No +filegen clockstats+ monitor data are produced by this driver. == Driver Options == +unit+ 'number':: Not used by this driver. +time1+ 'time':: Specifies the time offset calibration factor, in seconds and fraction, with default 0.0. +time2+ 'time':: Specifies the frequency offset calibration factor, in parts per million, with default 0.0. +stratum+ 'number':: Specifies the driver stratum, in decimal from 0 to 15, with default 5. +refid+ 'string':: Specifies the driver reference identifier, an ASCII string from one to four characters, with default +LOCL+. +flag1 {0 | 1}+:: Not used by this driver. +flag2 {0 | 1}+:: Not used by this driver. +flag3 {0 | 1}+:: Not used by this driver. +flag4 {0 | 1}+:: Not used by this driver. +subtype+:: Not used by this driver. +mode+:: Not used by this driver. +path+ 'filename':: Not used by this driver. +ppspath+ 'filename':: Not used by this driver. +baud+ 'number':: Not used by this driver. == Configuration Example == ---------------------------------------------------------------------------- refclock local ---------------------------------------------------------------------------- == Additional Information == link:refclock.html[Reference Clock Drivers] == Authors == David L. Mills ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/clock.txt0000644000175000017500000002002013252364117016403 0ustar rlaagerrlaager= Clock State Machine = == Table of Contents == * link:#intro[General Overview] * link:#panic[Panic Threshold] * link:#step[Step and Stepout Thresholds] * link:#hold[Hold Timer] * link:#inter[Operating Intervals] * link:#state[State Transition Function] ''''' [[intro]] == General Overview == In the NTPv4 specification and reference implementation a state machine is used to manage the system clock under exceptional conditions, as when the daemon is first started or when encountering severe network congestion. This page describes the design and operation of the state machine in detail. The state machine is activated upon receipt of an update by the clock discipline algorithm. Its primary purpose is to determine whether the clock is slewed or stepped and how the initial time and frequency are determined using three thresholds: _panic_, _step_ and _stepout_, and one timer: _hold_. [[panic]] == Panic Threshold == Most computers today incorporate a time-of-year (TOY) chip to maintain the time when the power is off. When the computer is restarted, the chip is used to initialize the operating system time. In case there is no TOY chip or the TOY time is different from NTP time by more than the panic threshold, the daemon assumes something must be terribly wrong, so exits with a message to the system operator to set the time manually. With the +-g+ option on the command line, the daemon sets the clock to NTP time at the first update, but exits if the offset exceeds the panic threshold at subsequent updates. The panic threshold default is 1000 s, but it can be changed with the +panic+ option of the link:miscopt.html#tinker[+tinker+] command. [[step]] == Step and Stepout Thresholds == Under ordinary conditions, the clock discipline gradually slews the clock to the correct time, so that the time is effectively continuous and never stepped forward or backward. If, due to extreme network congestion, an offset spike exceeds the step threshold, by default 128 ms, the spike is discarded. However, if offset spikes greater than the step threshold persist for an interval more than the stepout threshold, by default 300 s, the system clock is stepped to the correct time. In practice, the need for a step has been extremely rare and almost always the result of a hardware failure or operator error. The step threshold and stepout threshold can be changed using the +step+ and +stepout+ options of the link:miscopt.html#tinker[+tinker+] command, respectively. If the step threshold is set to zero, the step function is entirely disabled and the clock is always slewed. The daemon sets the step threshold to 600 s using the +-x+ option on the command line. If the +-g+ option is used or the step threshold is set greater than 0.5 s, the precision time kernel support is disabled. Historically, the most important application of the step function was when a leap second was inserted in the Coordinated Universal Time (UTC) timescale and the kernel precision time support was not available. This also happened with older reference clocks that indicated an impending leap second, but the radio itself did not respond until it resynchronized some minutes later. Further details are on the link:leap.html[Leap Second Processing] page. In some applications the clock can never be set backward, even it accidentally set forward a week by some evil means. The issues should be carefully considered before using these options. The slew rate is fixed at 500 parts-per-million (PPM) by the Unix kernel. As a result, the clock can take 33 minutes to amortize each second the clock is outside the acceptable range. During this interval the clock will not be consistent with any other network clock and the system cannot be used for distributed applications that require correctly synchronized network time. [[hold]] == Hold Timer == When the daemon is started after a considerable downtime, it could be the TOY chip clock has drifted significantly from NTP time. This can cause a transient at system startup. In the past, this has produced a phase transient and resulted in a frequency surge that could take some time, even hours, to subside. When the highest accuracy is required, some means is necessary to manage the startup process so that the clock is quickly set correctly and the frequency is undisturbed. The hold timer is used to suppress frequency adjustments during the training and startup intervals described below. At the beginning of the interval the hold timer is set to the stepout threshold and decrements at one second intervals until reaching zero. However, the hold timer is forced to zero if the residual clock offset is less than 0.5 ms. When nonzero, the discipline algorithm uses a small time constant (equivalent to a poll exponent of 2), but does not adjust the frequency. Assuming that the frequency has been set to within 1 PPM, either from the frequency file or by the training interval described later, the clock is set to within 0.5 ms in less than 300 s. [[inter]] == Operating Intervals == The state machine operates in one of four nonoverlapping intervals. Training interval:: This interval is used at startup when the frequency file is nor present at startup. It begins when the first update is received by the discipline algorithm and ends when an update is received following the stepout threshold. The clock phase is steered to the offset presented at the beginning of the interval, but without affecting the frequency. During the interval further updates are ignored. At the end of the interval the frequency is calculated as the phase change during the interval divided by the length of the interval. This generally results in a frequency error less than 0.5 PPM. Note that, if the intrinsic oscillator frequency error is large, the offset will in general have significant error. This is corrected during the subsequent startup interval. Startup interval:: This interval is used at startup to amortize the residual offset while not affecting the frequency. If the frequency file is present, it begins when the first update is received by the discipline. If not, it begins after the training interval. It ends when the hold timer decrements to zero or when the residual offset falls below 0.5 ms. Step interval:: This interval is used as a spike blanker during periods when the offsets exceed the step threshold. The interval continues as long as offsets are received that are greater than the step threshold, but ends when either an offset is received less than the step threshold or until the time since the last valid update exceeds the stepout threshold. Sync Interval:: This interval is implicit; that is, it is used when none of the above intervals are used. [[state]] == State Transition Function == The state machine consists of five states. An event is created when an update is received by the discipline algorithm. Depending on the state and the offset magnitude, the machine performs some actions and transitions to the same or another state. Following is a short description of the states. FSET - The frequency file is present:: Load the frequency file, initialize the hold timer and continue in SYNC state. NSET - The frequency file is not present:: Initialize the hold timer and continue in FREQ state. FREQ - Frequency training state:: Disable the clock discipline until the time since the last update exceeds the stepout threshold. When this happens, calculate the frequency, initialize the hold counter and transition to SYNC state. SPIK - Spike state:: A update greater than the step threshold has occurred. Ignore the update and continue in this state as long as updates greater than the step threshold occur. If a valid update is received, continue in SYNC state. When the time since the last valid update was received exceeds the stepout threshold, step the system clock and continue in SYNC state. SYNC - Ordinary clock discipline state:: Discipline the system clock time and frequency using the hybrid phase/frequency feedback loop. However, do not discipline the frequency if the hold timer is nonzero. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/rdebug.txt0000644000175000017500000001107313252364117016570 0ustar rlaagerrlaager= Debugging Reference Clock Drivers = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/oz2.gif[]| {millshome}pictures.html[from 'The Wizard of Oz', L. Frank Baum] Call the girls and they'll sweep your bugs. |============================== == Related Links == include::includes/refclock.txt[] include::includes/install.txt[] ''''' The link:ntpq.html[+ntpq+] utility program can be used to debug reference clocks, either on the server itself or from another machine elsewhere in the network. The server is compiled, installed and started using the configuration file described in the link:ntpd.html[+ntpd+] page and its dependencies. If the clock appears in the +ntpq+ utility and +pe+ command, no errors have occurred and the daemon has started, opened the devices specified and waiting for peers and radios to come up. If not, the first thing to look for are error messages on the system log. These are usually due to improper configuration, missing links or multiple instances of the daemon. It normally takes a minute or so for evidence to appear that the clock is running and the driver is operating correctly. The first indication is a nonzero value in the +reach+ column in the +pe+ billboard. If nothing appears after a few minutes, the next step is to be sure the RS232 messages, if used, are getting to and from the clock. The most reliable way to do this is with an RS232 tester and to look for data flashes as the driver polls the clock and/or as data arrive from the clock. Our experience is that the overwhelming fraction of problems occurring during installation are due to problems such as miswired connectors or improperly configured device links at this stage. If RS232 messages are getting to and from the clock, the variables of interest can be inspected using the +ntpq+ program and various commands described on the documentation page. First, use the +pe+ and +as+ commands to display billboards showing the peer configuration and association IDs for all peers, including the radio clock. The assigned clock address should appear in the +pe+ billboard and the association ID for it at the same relative line position in the +as+ billboard. Additional information is available with the +rv+ and +clockvar+ commands, which take as argument the association ID shown in the +as+ billboard. The +rv+ command with no argument shows the system variables, while the +rv+ command with association ID argument shows the peer variables for the clock, as well as other peers of interest. The +clockvar+ command with argument shows the peer variables specific to reference clock peers, including the clock status, device name, last received timecode (if relevant), and various event counters. In addition, a subset of the driver options is included. The poll and error counters in the +clockvar+ billboard are useful debugging aids. The +poll+ counts the poll messages sent to the clock, while the +noreply+, +badformat+ and +baddate+ count various errors. Check the timecode to be sure it matches what the driver expects. This may require consulting the clock hardware reference manual, which is probably pretty dusty at this stage. The +ntpq+ utility program can be used for detailed inspection of the clock driver status. The most useful are the +clockstat+ and +clkbug+ commands described in the document page. While these commands permit getting quite personal with the particular driver involved, their use is seldom necessary, unless an implementation bug shows up. If all else fails, turn on the debugging trace using two +-d+ flags in the +ntpd+ startup command line. Most drivers will dump status at every received message in this case. While the displayed trace can be intimidating, this provides the most detailed and revealing indicator of how the driver and clock are performing and where bugs might lurk. Most drivers write a message to the +clockstats+ file as each timecode or surrogate is received from the radio clock. By convention, this is the last ASCII timecode (or ASCII gloss of a binary-coded one) received from the radio clock. This file is managed by the +filegen+ facility described in the +ntpd+ page and requires specific commands in the configuration file. This forms a highly useful record to discover anomalies during regular operation of the clock. The scripts included in the +./scripts/stats+ directory can be run from a +cron+ job to collect and summarize these data on a daily or weekly basis. The summary files have proven inspirational to detect infrequent misbehavior due to clock implementation bugs in some radios. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/refclock.txt0000644000175000017500000002260513252364117017113 0ustar rlaagerrlaager= Reference Clock Support = image:pic/stack1a.jpg[gif]Master Time Facility at the {millshome}lab.html[UDel Internet Research Laboratory] == Related Links == include::includes/hand.txt[] include::includes/refclock.txt[] == Table of Contents == * link:#intro[Introduction] * link:#spec[Special Considerations] * link:#list[List of Reference Clock Drivers] ''''' [[intro]] == Introduction == NTP supports a large number of satellite, radio and telephone modem reference clocks, plus a special pseudo-clock used for backup or when no other clock source is available. A general description of the reference clock support is on this page. Additional information about each reference clock driver can be found via links from here. Additional information is on the link:rdebug.html[Debugging Hints for Reference Clock Drivers] and link:driver_howto.html[How To Write a Reference Clock Driver] pages. Information on how to support pulse-per-second (PPS) signals produced by some devices is on the link:pps.html[Pulse-per-second (PPS) Signal Interfacing] page. All reference clock drivers require that the reference clock use only Coordinated Universal Time (UTC). Timezone and standard/daylight adjustments are performed by the operating system kernel. Nowadays a reference clock will generally (though not always) be a GPS or GPSDO (GPS-disciplined oscillator). In former times it was often a radio timecode receiver synchronized to standard time as provided by NIST and USNO in the US, NRC in Canada and their counterparts elsewhere in the world. Precision time radios have been extinct in the U.S. since NIST changed their signal modulation at 2012-10-29T15:00:00Z; elsewhere they still exist but usage is declining under pressure from GPS technology. A device driver specific to each reference clock must be compiled in the distribution; however, most common GPS, radio, satellite and telephone modem clocks are included by default and are activated by configuration commands. Note that an attempt to configure a reference clock when the driver has not been compiled or the hardware port has not been appropriately configured results in a scalding remark to the system log file, but is otherwise non hazardous. Reference clocks are supported in the same way as ordinary NTP servers and use the same filter, select, cluster and combine algorithms. The connection to the computer is device dependent - usually a serial port. The particular device is specified by adding a soft link from the name used by the driver to the particular device name. The +refclock+ command is used to configure a reference clock. The options resemble those of the +server+ directives, but +mode+, +minpoll+, +maxpoll+, and +prefer+ options are supported for reference clocks, as described on the link:clockopt.html[Reference Clock Commands] page. The +prefer+ option can be useful to persuade the server to cherish a reference clock with somewhat more enthusiasm than other reference clocks or peers. It is further discussed on the link:prefer.html[Mitigation Rules and the +prefer+ Keyword] page. The +minpoll+ and +maxpoll+ options have meaning only for selected clock drivers. Additionally, the +refid+ and +stratum+ options can be used to override the defaults for the device. There are two optional device-dependent time offsets and four flags that can be included in the +refclock+ command as well. The stratum number of a reference clock is by default zero. Since the {ntpdman} daemon adds one to the stratum of each peer, a primary server ordinarily displays an external stratum of one. In order to provide engineered backups, it is often useful to specify the reference clock stratum as greater than zero. The stratum option is used for this purpose. Also, in cases involving both a reference clock and a pulse-per-second (PPS) discipline signal, it may be useful to specify the reference clock identifier as other than the default, depending on the driver. The refid option is used for this purpose. Except where noted, these options apply to all clock drivers. [[spec]] == Special Considerations == The link:driver_local.html[Undisciplined Local Clock] driver can simulate a reference clock when no external synchronization sources are available. If a server with this driver is connected directly or indirectly to the public Internet, there is some danger that it can destabilize other clients. It is not recommended that the local clock driver be used in this way, as the orphan mode described on the link:assoc.html[Association Management] page provides a generic backup capability. The local clock driver can also be used when an external synchronization source such as the IEEE 1588 Precision Time Protocol or NIST Lockclock directly synchronizes the computer time. Further information is on the link:extern.html[External Clock Discipline and the Local Clock Driver] page. Several drivers make use of the pulse-per-second (PPS) signal discipline, which is part of the generic driver interface, so require no specific configuration. For those drivers that do not use this interface, the link:driver_pps.html[PPS Clock Discipline] driver can provide this function. It normally works in conjunction with the reference clock that produces the timecode signal, but can work with another driver or remote server. When PPS kernel features are present, the driver can redirect the PPS signal to the kernel. Some drivers depending on longwave or shortwave radio services need to know the radio propagation time from the transmitter to the receiver. This must be calculated for each specific receiver location and requires the geographic coordinates of both the transmitter and receiver. The transmitter coordinates for various radio services are given in the {millshome}ntp/qth.html[Time and Frequency Standard Station Information] page. Receiver coordinates can be obtained locally or from Google Earth. The actual calculations are beyond the scope of this document. Depending on interface type, port speed, etc., a reference clock can have a small residual offset relative to another. To reduce the effects of jitter when switching from one driver to the another, it is useful to calibrate the drivers to a common ensemble offset. The +enable calibrate+ configuration command described on the link:miscopt.html[Miscellaneous Options] page activates a special feature which automatically calculates a correction factor for each driver relative to an association designated the prefer peer. [[list]] == List of Reference Clock Drivers == Following is a list showing the type name and title of each driver currently implemented. Click on a selected type for specific description and configuration documentation. If you have seen older versions of NTP, this list may have fewer entries than you expected. Support for some very ancient drivers (notably, those rendered obsolete by the WWVB modulation change at 2012-10-29T15:00:00Z) has been dropped in order to reduce our maintenance load. So have some other drivers (notably the Austron 2200A/2201A and Magnavox MX4200) after having been end-of-lifed with no sign of aftermarket activity for more than ten years. Several others have been removed for relying on obsolete buses or hardware classes that no longer exist. For security reasons, we will no longer support any refclock that requires a closed-source driver to run. This filtered out the Datum/Bancomm/Symmetricom bc600-series GPS/IRIG Receiver, the Hopf GPS/DCF77 6039 for PCI-Bus, and the Spectracom TSYNC PCI. The Hopf 6021 driver has also been removed because it duplicates support for the 6021 in the generic parse driver. [options="header"] [cols="15%,5%,80%",options="header"] |==================================================================== | Name | Flags | Driver |link:driver_local.html[local] | - | Undisciplined Local Clock |link:driver_spectracom.html[spectracom]| D | Generic Spectracom Receivers |link:driver_truetime.html[truetime] | D | TrueTime GPS/GOES Receivers |link:driver_generic.html[generic] | T | Generic Reference Driver (Parse) |link:driver_arbiter.html[arbiter] | D | Arbiter 1088A/B GPS Receiver |link:driver_modem.html[modem] | - | NIST/USNO/PTB Modem Time Services |link:driver_nmea.html[nmea] | T | Generic NMEA GPS Receiver |link:driver_pps.html[pps] | T | PPS Clock Discipline |link:driver_hpgps.html[hpgps] | T | Hewlett Packard GPS Receivers |link:driver_shm.html[shm] | T | Shared Memory Driver |link:driver_trimble.html[trimble] | D | Trimble Palisade/Thunderbolt/Acutime GPSes |link:driver_oncore.html[oncore] | D | Motorola UT Oncore GPS |link:driver_jjy.html[jjy] | T | JJY Receivers |link:driver_zyfer.html[zyfer] | - | Zyfer GPStarplus Receiver |link:driver_neoclock.html[neoclock] | D | NeoClock4X DCF77 / TDF Receiver |link:driver_gpsd.html[gpsd] | T | GPSD client protocol |==================================================================== The name in the left column is the driver type to be used in the refclock declaration. Matching to these names is case-insensitive. The flags field should be interpreted as follows: [cols="5%,95%",options="header"] |==================================================================== |Flag| Meaning | D | Deprecated. May be removed in a future release | T | Regularly tested by an active maintainer (some devices/modes) |==================================================================== ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/confopt.txt0000644000175000017500000000465613252364117017001 0ustar rlaagerrlaager= Server Commands and Options = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/boom3a.gif[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] The chicken is getting configuration advice. |============================== == Related Links == include::includes/confopt.txt[] == Table of Contents == * link:#address[Server and Peer Addresses] * link:#association[Association Commands] * link:#option[Server Command Options] ''''' [[address]] == Server and Peer Addresses == Following is a description of the server configuration commands in NTPv4. There are two classes of commands, configuration commands that configure an association with a remote server, peer or reference clock, and auxiliary commands that specify environment variables that control various related operations. The various modes described on the link:assoc.html[Association Management] page are determined by the command keyword and the DNS name or IP address. Addresses are classed by type as (s) a remote server or peer (IPv4 class A, B and C or IPv6), or (b) the IPv4 broadcast address of a local interface. If the Basic Socket Interface Extensions for IPv6 (RFC 2553) is detected, support for the IPv6 address family is generated in addition to the default IPv4 address family. IPv6 addresses can be identified by the presence of colons ":" in the address field. IPv6 addresses can be used almost everywhere where IPv4 addresses can be used, with the exception of reference clock addresses, which are always IPv4. Note that in contexts where a host name is expected, a +-4+ qualifier preceding the host name forces DNS resolution to the IPv4 namespace, while a +-6+ qualifier forces DNS resolution to the IPv6 namespace. [[association]] == Association Commands == Unless noted otherwise, further information about these commands is at link:discover.html#pool[Automatic Server Discovery]. The http://www.pool.ntp.org/[www.pool.ntp.org] page describes a compatible pool of public NTP servers, which are probably what you want to define associations with unless you specifically know otherwise. include::includes/assoc-commands.txt[] [[option]] == Server Command Options == include::includes/assoc-options.txt[] [[aux]] == Auxiliary Commands == Information on authentication for broadcast options can be found at link:authopt.html[Authentication Options]. include::includes/assoc-auxcommands.txt[] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntpspeak.txt0000644000175000017500000003413513252364117017151 0ustar rlaagerrlaager= A Glossary of NTP-speak = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/howland.jpg[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] |============================== [glossary] [[ACTS]] ACTS:: https://www.nist.gov/pml/time-and-frequency-division/services/automated-computer-time-service-acts[NIST Automated Computer Time Service]. NIST provides a civil time reference for computers equipped with analog modems that can dial to its phone lines. Due to variability of delays in the analog phone network accuracy is not very good, around 4 milliseconds compared to around a microsecond for <>. But the service is still occasionally used as a backup to more precise sources. [[association]] association:: An association is the relationship that an NTP client and server have when the server is computing and shipping time updates to the client and the client is expecting them. There are several different kinds of associations including unicast, manycast, and broadcast modes; another variable is whether the association is authenticated or unauthenticated. A significant amount of NTP's complexity is the means for discovering association partners, creating and maintaining associations, and preventing attackers from forming associations. [[association ID]] association ID:: A numeric identifier designating an association, normally internal to ntpd but exposed in <> packets. [[atom]] atom:: Old name for the PPS driver. Origin lost in the mists of history, but probably related in some way to cesium atomic clocks. [[cycle]] cycle:: An <>; code comments sometimes use this term to emphasize that modular arithmetic is going on. [[drift]] drift:: In an NTP context, drift refers to the frequency offset of a clock crystal in an NTP host that causes the system time to slowly drift. It is usually expressed in a parts-per-million (PPM) offset from its nominal frequency. It changes, slowly, in response to environmental factors (mainly ambient temperature). ntpd measures drift by sampling the clock and performing clock recovery against a phase-locked loop. The drift measurement is occasionally stored locally to a drift file so that when ntpd is stopped and restarted it doesn't have to go through the entire resampling and resynchronization process before providing reliable time. [[falseticker]] falseticker:: <> for a timeserver identified as not reliable by statistical filtering. Usually this does not imply any problem with the timeserver itself but rather with highly variable and asymmetric network delays between server and client, but firmware bugs in GPS receivers have produced falsetickers. [[epoch]] epoch:: 1. The zero date of an NTP era. The "prime epoch" (of era 0) was 1900-00-00T00:00:00 in proleptic UTC (leap second correction was not introduced until 1972). 2. Other calendar systems have other definitions; notably, the Unix epoch is 1970-00-00T00:00:00. [[era]] era:: One complete revolution of a 64-bit NTP 64-bit timestamp; approximately 136 years. Eras are numbered from 0, but this era number is not represented internally in NTP code because modular-arithmetic trickery is used to deduce the nearest time that could fit a given timestamp. [[fudge]] fudge:: Can have one of two senses. Either (1) an offset configured for a <> or server to correct its time, reversing a fixed or nearly-fixed propagation delay, or (2) in the old config syntax, a "fudge bit" was what is now called a driver option - set in a refclock declaration to change the refclock's behavior in some driver-dependent way. [[fuzzball]] fuzzball:: Historical. An https://www.eecis.udel.edu/~mills/database/papers/fuzz.pdf[operating system] running on PDP-11s used for early time-service and Internet routing experiments. Metonymously, a PDP-11 running the suite. While some of these remained in service as late as 1988 they are now long gone, but have left a few traces in the NTP codebase. [[GPS]] GPS:: Global Positioning System; also, "a GPS" is a radio receiver designed to get position and time fixes from the system. GPS fixes are derived from spherical trigonometry using the precisely known positions of satellites in a geocentric coordinate system. GPS also provides time service; those that emit <> are suitable as clock sources for Stratum 1 timeservers. In timekeeping, the term is used to refer not only to the US original GPS system, but newer constellations that work on the same principles, such as ГЛОÐÐСС (the Russian GLONASS), 北斗 (the Chinese BeiDou-2), and the EU's Galileo. [[GPSDO]] GPSDO:: GPS Disciplined Oscillator. A good crystal is synchronized to time from a GPS receiver. The combination gets the short term stability of the crystal and the long term stability of GPS. With appropriate firmware, the crystal can be used when the GPS receiver is temporarily non-operational; see <>. Most cell towers have GPSDOs. You can usually spot the conical GPS antenna. [[GPSD]] GPSD:: The http://www.catb.org/gpsd/[GPS Daemon], an open-source device manager for GPSes and other geodetic sensors. Frequently used as a clock source by Stratum 1 sites via the link:driver_shm.html[SHM] interface. [[GPS pivot date]] GPS pivot date:: The GPS pivot date is specific fixed arbitrary time. It is stored in the firmware of a GPS receiver, and is probably the date that firmware was released. For reasons, a GPS receiver will start reporting incorrect time 512 weeks (9.8 years) or 1024 weeks (19.6 years) after it's pivot date. There is generally no way to determine what a given GPS receiver's pivot date is, or to determine that it has failed in this manner. It is recommended that any critical Stratum 1 NTP server that uses a GPS receiver as a refclock not use one that is more than 9 years old, and to have a peer or nopeer relationship with other NTP servers, so as to detect when the GPS time is no longer sane. [[holdover]] holdover:: In connection with a <> or <

NTP Documentation

  • Latest - Latest documentation from master. Updated after every commit.
ntpsec-1.1.0+dfsg1/docs/driver_pps.txt0000644000175000017500000002000613252364117017471 0ustar rlaagerrlaager= PPS Clock Discipline = == Synopsis == ["verse",subs="normal"] Name: pps Reference ID: PPS Driver ID: PPS Serial or Parallel Port: /dev/pps__u__ Requires: PPSAPI signal interface for PPS signal processing. == Description == This driver furnishes an interface for the pulse-per-second (PPS) signal produced by a cesium clock, radio clock or related devices. It can be used to augment the serial timecode generated by a GPS receiver, for example. It can be used to remove accumulated jitter and re-time a secondary server when synchronized to a primary server over a congested, wide-area network and before redistributing the time to local clients. The driver includes extensive signal sanity checks and grooming algorithms. A range gate and frequency discriminator reject noise and signals with incorrect frequency. A multiple-stage median filter rejects jitter due to hardware interrupt and operating system latencies. A trimmed-mean algorithm determines the best time samples. With typical workstations and processing loads, the incidental jitter can be reduced to a few microseconds. While this driver can discipline the time and frequency relative to the PPS source, it cannot number the seconds. For this purpose an auxiliary source is required, ordinarily a radio clock operated as a primary reference (stratum 1) source; however, another NTP time server can be used as well. For this purpose, the auxiliary source should be specified as the prefer peer, as described in the link:prefer.html[Mitigation Rules and the +prefer+ Keyword] page. The driver requires the PPSAPI interface^1^, which is a proposed IETF standard. The interface consists of the +timepps.h+ header file and associated kernel support. Support for this interface is included in current versions of Solaris, FreeBSD and Linux and proprietary versions of Tru64 (Alpha) and SunOS. See the link:pps.html[Pulse-per-second (PPS) Signal Interfacing] page for further information. The PPS source can be connected via a serial or parallel port, depending on the hardware and operating system. A serial port can be dedicated to the PPS source or shared with another device; however, if dedicated the data leads should not be connected, as noise or unexpected signals can cause +ntpd+ to exit. A radio clock is usually connected via a serial port and the PPS source connected via a level converter to the data carrier detect (DCD) pin (DB-9 pin 1, DB-25 pin 8) of the same connector. In some systems where a parallel port and driver are available, the PPS signal can be connected directly to the ACK pin (DB25 pin 10) of the connector. Whether the PPS signal is connected via a dedicated port or shared with another device, the driver opens the device +/dev/pps%d+, where +%d+ is the unit number. As with other drivers, links can be used to redirect the logical name to the actual physical device. The driver normally operates like any other driver and uses the same mitigation algorithms and PLL/FLL clock discipline incorporated in the daemon. If kernel PLL/FLL support is available, the kernel PLL/FLL clock discipline can be used instead. The default behavior is not to use the kernel PPS clock discipline, even if present. This driver incorporates a good deal of signal processing to reduce jitter using the median filter algorithm in the driver. As the result, performance with +minpoll+ configured at 4 (16s) is generally better than the kernel PPS discipline. However, the +flag 3+ option can be used to enable the kernel PPS discipline if necessary. This driver is enabled only under one of two conditions (a) a prefer peer other than this driver is among the survivors of the mitigation algorithms or (b) there are no survivors and the +minsane+ option of the +tos+ command is 0. The prefer peer designates another source that can reliably number the seconds when available . However, if no sources are available, the system clock continues to be disciplined by the PPS driver on an indefinite basis. A scenario where the latter behavior can be most useful is a planetary orbiter fleet, for instance in the vicinity of Mars, where contact between orbiters and Earth only one or two times per Sol (Mars day). These orbiters have a precise timing reference based on an Ultra Stable Oscillator (USO) with accuracy in the order of a Cesium oscillator. A PPS signal is derived from the USO and can be disciplined from Earth on rare occasion or from another orbiter via NTP. In the above scenario the PPS signal disciplines the spacecraft clock between NTP updates. In a similar scenario a PPS signal can be used to discipline the clock between updates produced by the modem driver. This would provide precise synchronization without needing the Internet at all. == Driver Options == +unit+ 'number':: The driver unit number, defaulting to 0. Used as a distinguishing suffix in the driver device name. +time1+ 'time':: Specifies the time offset calibration factor, in seconds and fraction, with default 0.0. +time2+ 'time':: Not used by this driver. +stratum+ 'number':: Specifies the driver stratum, in decimal from 0 to 15, with default 0. +refid+ 'string':: Specifies the driver reference identifier, an ASCII string from one to four characters, with default +PPS+. +flag1 {0 | 1}+:: Not used by this driver. +flag2 {0 | 1}+:: Specifies PPS capture on the rising (assert) pulse edge if 0 (default) or falling (clear) pulse edge if 1. +flag3 {0 | 1}+:: Controls the kernel PPS discipline: 0 for disable (default), 1 for enable. +flag4 {0 | 1}+:: Record a timestamp once for each second if 1. Useful for constructing Allan deviation plots. +subtype+:: Not used by this driver. +mode+:: Not used by this driver. +path+:: Not used by this driver. +ppspath+:: Overrides the default PPS device path. +baud+ 'number':: Not used by this driver. == Configuration Example == ---------------------------------------------------------------------------- refclock pps unit 0 flag2 1 # Capture PPS on trailing edge of pps0 ---------------------------------------------------------------------------- This driver does not need to be configured explicitly in order to be active; thus no "refclock pps" is required unless you need to set a driver option, . It is initialized by whatever other driver is using it. That will be one of the following: generic, nmea, or spectracom. == Clockstats == If clockstats is enabled, the driver will log a few counters. Examples: ---------------------------------------------------------------------------- 57378 3313.351 PPS(0) 423681 64 0 0 0 57378 3377.352 PPS(0) 423745 64 0 0 0 57378 3441.352 PPS(0) 423809 64 0 0 0 57378 3505.351 PPS(0) 423873 64 0 0 0 ---------------------------------------------------------------------------- .Clockstats [cols="10%,20%,70%",options="header"] |============================================================================= |Column|Sample |Meaning |1 |57378 |MJD |2 |3505.351 |Time of day in seconds |3 |PPS(0) |Clock identification |4 |423873 |Total number of PPS pulses the kernel has seen |5 |64 |Number of PPS pulses the driver processed This should be the difference between col 4 and col 4 from the previous line |6 |0 |ntpd doesn't know the time yet |7 |0 |Error from Kernel |8 |0 |Number of times there was no pulse ready |============================================================================= The clock identification is normally the driver type and unit, but if your ntpd was built in strict Classic compatibility mode it will be a magic clock address expressing the same information in a more opaque way. == Additional Information == link:refclock.html[Reference Clock Drivers] == Reference == 1. Mogul, J., D. Mills, J. Brittenson, J. Stone and U. Windl. Pulse-per-second API for Unix-like operating systems, version 1. Request for Comments RFC 2783, Internet Engineering Task Force, March 2000, 31 pp. == Author == David L. Mills ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/assoc.txt0000644000175000017500000002634713252364117016442 0ustar rlaagerrlaager= Association Management = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/alice51.gif[]| {millshome}pictures.html[from 'Alice's Adventures in Wonderland', Lewis Carroll] Make sure who your friends are. |============================== == Related Links == include::includes/hand.txt[] == Table of Contents == * link:#modes[Association Modes] * link:#client[Client/Server Mode] * link:#symact[Symmetric Active/Passive Mode] * link:#broad[Broadcast/Multicast Modes] * link:#many[Manycast Mode] * link:#poll[Poll Interval Management] * link:#burst[Burst Options] ''''' [[modes]] == Association Modes == This page describes the various modes of operation provided in NTPv4. There are three types of associations in NTP: _persistent_, _preemptable_ and _ephemeral_. Persistent associations are mobilized by a configuration command and never demobilized. Preemptable associations are mobilized by a configuration command which includes the +preempt+ option or upon arrival of an automatic server discovery packet. They are are demobilized by timeout or when preempted by a "better" server, as described on the link:discover.html[Automatic Server Discovery Schemes] page. There are two principal modes of operation in NTP: client/server and broadcast. There are three automatic server discovery schemes in NTP: broadcast and pool described on the link:discover.html[Automatic Server Discovery Schemes] page. In addition, the link:#burst[burst options] and link:orphan.html[orphan mode] can be used in appropriate cases. Following is a summary of the operations in each mode. Note that reference to option applies to the commands described on the link:confopt.html[Server Commands and Options] page. See that page for applicability and defaults. [[client]] == Client/Server Mode == Client/server mode is the most common configuration in the Internet today. It operates in the classic remote-procedure-call (RPC) paradigm with stateless servers and stateful clients. In this mode a host sends a client (mode 3) request to the specified server and expects a server (mode 4) reply at some future time. In some contexts this would be described as a "pull" operation, in that the host pulls the time and related values from the server. A host is configured in client mode using the +server+ (sic) or +pool+ command and specifying the server DNS name or IPv4 or IPv6 address; the server requires no prior configuration (but see link:access.html[Access Control]). The +iburst+ option described later on this page is recommended for clients, as this speeds up initial synchronization from several minutes to several seconds. The +burst+ option described later on this page can be useful to reduce jitter on very noisy dial-up or ISDN network links. Ordinarily, the program automatically manages the poll interval between the default minimum and maximum values. The +minpoll+ and +maxpoll+ options can be used to bracket the range. Unless noted otherwise, these options should not be used with reference clock drivers. [[symact]] == Symmetric Active/Passive Mode == Symmetric active/passive mode is intended for configurations where a clique of low-stratum peers operate as mutual backups for each other. Each peer operates with one or more primary reference sources, such as a reference clock, or a set of secondary (stratum, 2) servers known to be reliable and authentic. Should one of the peers lose all reference sources or simply cease operation, the other peers will automatically reconfigure so that time and related values can flow from the surviving peers to all hosts in the subnet. In some contexts this would be described as a "push-pull" operation, in that the peer either pulls or pushes the time and related values depending on the particular configuration. A symmetric active peer sends a symmetric active (mode 1) message to a designated peer. If a matching configured symmetric active association is found, the designated peer returns a symmetric active message. If no matching association is found, the designated peer mobilizes a ephemeral symmetric passive association and returns a symmetric passive (mode 2) message. Since an intruder can impersonate a symmetric active peer and cause a spurious symmetric passive association to be mobilized, symmetric passive mode should always be cryptographically validated. Due to unresolvable security issues with symmetric mode, NTPsec includes only partial support for it. The deprecated +peer+ directive which formerly set up a symmetric active association is now a synonym for +server+. Servers which receive symmetric active messages will immediately reply with symmetric passive responses without setting up any new association; essentially they treat such messages exactly like client-mode messages aside from putting a different mode number into the response. [[broad]] == Broadcast/Multicast Modes == These modes cannot be effectively secured and are deprecated in NTPsec. Client-mode support has been removed; server-side support is retained for backward compatibility but may be removed in a future release. NTP broadcast modes are intended for configurations involving one or a few servers and a possibly very large client population. Broadcast mode can be used with Ethernet, FDDI and WiFi spans interconnected by hubs or switches. Ordinarily, broadcast packets do not extend beyond a level-3 router. A server is configured to send broadcast messages using the +broadcast+ command and specifying the subnet address for broadcast. [[many]] == Manycast and Pool Modes == Manycast and pool modes are automatic discovery and configuration paradigms. They are intended as a means for a client to troll the nearby network neighborhood to find cooperating willing servers, validate them using cryptographic means and evaluate their time values with respect to other servers that might be lurking in the vicinity. The intended result is that each client mobilizes ephemeral client associations with some number of the "best" of the nearby servers, yet automatically reconfigures to sustain this number of servers should one or another fail. Additional information is on the link:discover.html[Automatic Server Discovery Schemes] page. [[poll]] == Poll Interval Management == NTP uses an intricate heuristic algorithm to automatically control the poll interval for maximum accuracy consistent with minimum network overhead. The algorithm measures the incidental offset and jitter to determine the best poll interval. When +ntpd+ starts, the interval is the default minimum 64 sec. Under normal conditions when the clock discipline has stabilized, the interval increases in steps to the default maximum 1024 sec. In addition, should a server become unreachable after some time, the interval increases in steps to the maximum in order to reduce network overhead. Additional information about the algorithm is on the link:poll.html[Poll Program] page. The default poll interval range is suitable for most conditions, but can be changed using options on the link:confopt.html[Server Commands and Options] and link:miscopt.html[Miscellaneous Options] pages. However, when using maximum intervals much larger than the default, the residual clock frequency error must be small enough for the discipline loop to capture and correct. The capture range is 500 PPM with a 64-sec interval decreasing by a factor of two for each interval doubling. At a 36-hr interval, for example, the capture range is only 0.24 PPM. In the NTPv4 specification and reference implementation, the poll interval is expressed in log~2~ units, properly called the _poll exponent._ It is constrained by the lower limit +minpoll+ and upper limit +maxpoll+ options of the link:confopt.html[+server+] command. The limits default to 6 (64 sec) and 10 (1024 sec), respectively, which are appropriate for the vast majority of cases. As a rule of thumb, the expected errors increase by a factor of two as the poll interval increases by a factor of four. The poll interval algorithm slowly increases the poll interval when jitter dominates the error budget, but quickly reduces the interval when wander dominates it. More information about this algorithm is on the link:warp.html[How NTP Works] page. There is normally no need to change the poll limits, as the poll interval is managed automatically as a function of prevailing jitter and wander. The most common exceptions are the following. * With fast, lightly loaded LANs and modern processors, the nominal Allan intercept is about 500 sec. In these cases the expected errors can be further reduced using a poll exponent of 4 (16 sec). In the case of the pulse-per-second (PPS) driver, this is the recommended value. * With symmetric modes the most stable behavior results when both peers are configured in symmetric active mode with matching poll intervals of 6 (64 sec). * The poll interval should not be modified for reference clocks, with the single exception the ACTS telephone modem driver. In this case the recommended minimum and maximum intervals are 12 (1.1 hr) and 17 (36 hr), respectively. [[burst]] == Burst Options == Occasionally it is necessary to send packets temporarily at intervals less than the poll interval. For instance, with the +burst+ and +iburst+ options of the link:confopt.html[+server+] command, the poll program sends a burst of several packets at 2-sec intervals. In either case the poll program avoids sending needless packets if the server is not responding. The client begins a burst with a single packet. When the first packet is received from the server, the client continues with the remaining packets in the burst. If the first packet is not received within 64 s, it will be sent again for two additional retries before beginning backoff. The result is to minimize network load if the server is not responding. Additional details are on the link:poll.html[Poll Program] page. There are two burst options where a single poll event triggers a burst. They should be used only with the +server+ and +pool+ commands, but not with reference clock drivers nor symmetric mode peers. In both modes, received server packets update the clock filter, which selects the best (most accurate) time values. When the last packet in the burst is sent, the next received packet updates the system variables and adjusts the system clock as if only a single packet exchange had occurred. The +iburst+ option is useful where the system clock must be set quickly or when the network attachment requires an initial calling or training sequence, as in PPP or ISDN services. In general, this option is recommended for +server+ and +pool+ commands. A burst is sent only when the server is unreachable; in particular, when first starting up. Ordinarily, the clock is set within a few seconds after the first received packet. See the link:clock.html[Clock State Machine] page for further details about the startup behavior. The +burst+ option is useful in cases of severe network jitter or when the network attachment requires an initial calling or training sequence. This option is recommended when the minimum poll exponent is larger than 10 (1024 sec). A burst is sent only when the server is reachable. The number of packets in the burst is determined by the poll interval so that the average interval between packets (headway) is no less than the minimum poll interval for the association. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/clockopt.txt0000644000175000017500000000120713252364117017134 0ustar rlaagerrlaager= Reference Clock Commands and Options = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/stack1a.jpg[]| Master Time Facility at the {millshome}lab.html[UDel Internet Research Laboratory] |============================== == Related Links == include::includes/refclock.txt[] include::includes/clockopt.txt[] ''''' [[types]] == Reference Clock Types == Unless noted otherwise, further information about these types is on the link:refclock.html[Reference Clock Support] page. [[options]] == Commands and Options == include::includes/clock-options.txt[] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntptime.txt0000644000175000017500000000056613252364117017005 0ustar rlaagerrlaager= ntptime - read and set kernel time variables = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/pogo5.gif[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] The turtle has been swimming in the kernel. |============================== ''''' include::includes/ntptime-body.txt[] ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntpq.txt0000644000175000017500000000101013252364117016270 0ustar rlaagerrlaager= ntpq - standard NTP query program = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/bustardfly.gif[]| {millshome}pictures.html[from 'Pogo', Walt Kelly] A typical NTP monitoring packet |============================== == More Help == include::includes/manual.txt[] ''''' include::includes/ntpq-body.txt[] == Mode 6 Protocol == The Mode 6 protocol used by ntpq to communicate with ntpd is described link:mode6.html[here]. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/ntpsec.txt0000644000175000017500000003011213252364117016607 0ustar rlaagerrlaager= Differences from NTP Classic = [cols="10%,90%",frame="none",grid="none",style="verse"] |============================== |image:pic/clocktower128.png[alt="The NTPsec logo"]| Accept no imitations. |============================== == Related Links == * A list of all links is on the link:sitemap.html[Site Map] page. ''''' == Table of Contents == * link:#intro[Introduction] * link:#incompatible[Incompatible Changes] * link:#security[Security Improvements] * link:#timesync[Time Synchronization Improvements] * link:#configuration[Configuration Improvements] * link:#other[Other user-visible changes] [[intro]] == Introduction == The design objectives of this distribution, NTPsec, are in many ways a break with NTP's past. We have deliberately jettisoned support for ancient legacy hardware and operating systems in order to ship code that is security-hardened, simpler, drastically less bulky, easier to understand, and easier to maintain. We retain, however, almost full compatibility and interoperation with NTP Classic. The qualification "almost" is required mainly because we do not support the Autokey (RFC 5906) public-key encryption scheme. It had interoperability and exploitable vulnerability issues too severe to be patched. We are participating in an IETF effort to develop better security features. This project began as an effort to address serious security issues with NTP Classic, and we intend to keep a particularly strong focus on code security and code verifiability. Most of the changes are under the hood, internal to the codebase. A few will be user-visible. [[incompatible]] == Incompatible Changes == Normally NTPsec is a drop-in replacement for legacy versions. We have tried to hold incompatible changes to a minimum, but there are a few. Some can be reverted by building the software in strict compatibility mode with +--enable-classic-mode+ (note that this is a build-time switch, not a run-time one). * The +sntp+ program has been renamed +ntpdig+ in order to make NTP installables have a uniform name prefix and take up less namespace. Also, +ntp-keygen+ is now +ntpkeygen+, +ntp-wait+ is ntpwait, and +update-leap+ is now +ntpleapfetch+. * Log timestamps look a little different; they are now in ISO 8601 format. Reverted in the +--enable-classic-mode+ build. * Clock identifiers in log files are normally the driver shortname followed by the unit number in parentheses, rather than the magic IP addresses formerly used. This change affects the peerstats, rawstats, and clockstats files. Reverted in the +--enable-classic-mode+ build. * The -!m, -\>, and -< options of some Classic commands are not supported. (The argument-parsing framework code that implemented them in Classic was overcomplicated and buggy and had to be removed.) * The shortname of +--help+ options is now +-h+, not +-?+ * If you had a refclock on a path of the form /dev/palisadeNNN, that link needs to change to /dev/trimbleNNN. Reverted in the +--enable-classic-mode+ build. * If you had a refclock on a path of the form /dev/actsNNN, that link needs to change to /dev/modemNNN. Reverted in the +--enable-classic-mode+ build. * An instance of +ntpq+ built from the NTPsec code querying a legacy NTP daemon will not automatically display peers with 127.127.t.u addresses as refclocks; that assumption has been removed from the NTPsec code as part of getting it fully IPv6-ready. * ntpq no longer has the +-i+/+--interactive+ option, as there was no situation in which it was meaningful. * Interleave mode has been removed. It was buggy and the root cause of at least two CVEs. [[security]] == Security Improvements == We have spent more effort than anything else on reducing attack surface and hardening code. In toto, more than 70% of the NTP Classic codebase has been outright removed, with less than 5% new code added. * The deprecated ntpdc utility, long a chronic locus of security vulnerabilities, has been removed. Its function has been merged into +ntpq+. * Autokey is not supported; that code has been removed, as it was chronically prone to security vulnerabilities. * peer mode has been removed. The keyword peer in ntp.conf is now just an alias for keyword server. * Broadcast- and multicast modes, which are impossible to secure, have been removed. * The authentication requirement for remote configuration commands (e.g., via +ntpq+) can no longer be disabled. * The deprecated and vulnerability-prone ntpdate program has been replaced with a shell wrapper around ntpdig. Its -e and -p options are not implemented. It is no longer documented, but can be found in the attic/ directory of the source distribution. * A large number of obsolete refclocks have been removed in order to reduce attack surface, code bulk, and documentation complexity. * Various features related to runtime dumping of the configuration state have been removed for security reasons. These include the +saveconfig+ command in +ntpq+, the +--saveconfigquit+ option of ntpd, and the implementation of related config declarations in +ntp.conf+. * Likewise, the poorly-documented ntpdsim code has also been removed to gain a significant reduction in code complexity. * The ntpsnmpd daemon, incomplete and not conformant with RFC 5907, has been removed. * The 'trap' feature has been removed. It was broken by bit-rot in recent versions of NTP Classic, and if not broken would have been at high risk for bugs that would enable DoS vulnerabilities. * Interleave mode has been removed. It didn't work correctly (there was an implementation error in the timestamp handling), so no point in allowing it to increase attack surface. * The code has been systematically hardened, with unsafe string copy and formatting functions replaced by safe (bounded) ones. [[timesync]] == Time-synchronization improvements == * Internally, there is more consistent use of nanosecond precision. A visible effect of this is that time stepping with sufficiently high-precision time sources could be accurate down to nanoseconds rather than microseconds; this might actually matter for GPSDOs and high-quality radio clocks. * For a driver that ships 4-digit year stamps (notably including the NMEA driver) that data is no longer ignored in favor of deducing the year from the system clock. This means that (a) it is now possible for ntpd using a local clock source to recover from a trashed or zeroed system clock (e.g. at boot time on a system with missing or damaged battery back up) without requiring sync to a remote peer. * We've fixed bug inherited from Classic that could cause the jitter of a bad peer to be incorrectly zeroed, possibly causing that peer to be selected. This probably accounts for some flakiness within 8 polling intervals of startup on older versions. [[clients]] == Client Tool Improvements == * A new tool, +ntpmon+, performs real-time monitoring of your peer and MRU status with efficient (least-cost) querying. * Both +ntpq+ and +ntpmon+ can now optionally do display with time units shown and scaled, in the manner of "chrony sources". * There is a new data-visualization tool, link:ntpviz.html[+ntpviz+], which can produce various useful and interesting plots from the NTP statistics logs. These should assist in monitoring a time-server's performance, fixing configuration problems, and identifying noise sources in network weather and elsewhere. * Because +ntpviz+ exists, a number of ancient and poorly-documented scripts in awk, Perl, and S, formerly used for making statistical summaries, have been removed from the distribution in order to reduce overall maintenance burden and complexity. If you miss any of this cruft, the project team will (a) be quite surprised, and (b) work with you on better analytics using ntpviz and modern tools. * The ntpq utility resizes its display to take advantage of wide terminal windows, allowing more space for long peer addresses. * When running as root, the ntpq utility looks in /etc/ntp.conf and /usr/local/etc/ntp.keys to find credentials for control requests that require authentication. Thus it will often not be necessary to enter them by hand. Note that your installation's locations for config and key files may differ from these; in that case this convenience feature will fail, as we have elected not to chase it down a complexity rathole. * A new utility, +ntpfrob+, collects several small diagnostic functions for reading and tweaking the local clock hardware, including reading the clock tick rate, precision, and jitter. Part of it formerly traveled as +tickadj+. * Mode 6 now fully supports RFC 5907: "Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4)" by reporting a sys_rootdist variable that is the root distance of the selected peer. This can fill in the MIB's ntpEntTimeDistance entry. * ntpq displays the root distance (aka. syncronization distance) in the sysinfo command. == Configuration Improvements == * The notorious collision between pool and nopeer in older implementations has been fixed; the pool keyword is now fully usable. * There is a new, simpler syntax for declaring refclocks. The old syntax with the magic 127.127.t.u addresses and fudge command is still supported, but no longer documented. It may be removed in a future release. Relevant examples of the new syntax are included on each refclock page. One major feature of the new syntax is that refclock drivers are referred to by names, not numbers. * The unpeer command now takes a type-unit specification when unpeering a clock. * For the generic (parse) driver only: Using the new refclock syntax, the maximum number of units that can be set up changes from 4 (numbers 0-3) to unlimited. However, the old magic-address syntax will not work correctly - you _must_ use the new syntax to declare generic-driver refclocks. If the software was compiled with the +--enable-classic-mode+ switch, the foregoing is reversed. * In server entries, its now possible to specify a time offset to be applied to incoming timestamps (analogues to the fudge on certain refclocks). This may be useful for client systems communicating over ADSL lines, which have large but relatively fixed asymmetric delays. * The _restrict_ statement can now take an address range in CIDR notation rather than as an address/mask pair. * You can now turn off restriction flags with an _unrestrict_ statement that takes arguments exactly like a _restrict_. With no aruments it removes any rule matching the address mask entirely. This is expected to be useful mainly with the "ntpq :config" command. * The includefile directive now evaluates relative pathnames not with respect to the current working directory but with respect to the directory name of the last pushed file in the stack. This means that you can run ntpd from any directory with "includefile foo" in /etc/ntp.conf finding /etc/foo rather than looking for foo in your current directory. * If there is an /etc/ntp.d directory, its subfiles are scanned for more configuration declarations. Only files with the extension ".conf" are interpreted; others are ignored. This feature is intended to make assembling configuration easier for administration and package-configuration scripts. See {ntpdman} for details. * It is now possible to set the peer maximum dispersion with "tos maxdisp". See RFC 5905 for discussion of this synchronization parameter. * The default baudrate of the NMEA driver has been changed to 9600 to match the default speed of almost all modern GPSes. The code can be built in a strict NTP Classic compatibility mode that restores the old 4800bps default. * Most refclock drivers now support configuration options to override the default device path, the default PPS device path (if any) and the serial baud rate. [[other]] == Other user-visible changes == * The documentation has been extensively updated and revised. One important change is that manual pages are now generated from the same masters as this web documentation, so the two will no longer drift out of synchronization. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/docs/poll.txt0000644000175000017500000000664213252364117016274 0ustar rlaagerrlaager= Poll Process = The poll process sends NTP packets at intervals determined by the clock discipline algorithm. The process is designed to provide a sufficient update rate to maximize accuracy while minimizing network overhead. The process is designed to operate over a poll exponent range between 3 (8 s) and 17 (36 hr). The minimum and maximum poll exponent within this range can be set using the +minpoll+ and +maxpoll+ options of the link:confopt.html#option[+server+] command, with default 6 (64 s) and 10 (1024 s), respectively. The poll interval is managed by a heuristic algorithm developed over several years of experimentation. It depends on an exponentially weighted average of clock offset differences, called _clock jitter_, and a jiggle counter, which is initially set to zero. When a clock update is received and the offset exceeds the clock jitter by a factor of 4, the jiggle counter is increased by the poll exponent; otherwise, it is decreased by twice the poll exponent. If the jiggle counter is greater than an arbitrary threshold of 30, it is reset to 0 and the poll exponent is increased by 1. If the jiggle counter is less than -30, it is set to 0 and the poll exponent decreased by 1. In effect, the algorithm has a relatively slow reaction to good news, but a relatively fast reaction to bad news. As an option of the link:confopt.html#option[+server+] command, instead of a single packet, the poll process can send a burst of several packets at 2-s intervals. This is designed to reduce the time to synchronize the clock at initial startup (+iburst+) and/or to reduce the phase noise at the longer poll intervals (+burst+). The +iburst+ option is effective only when the server is unreachable, while the +burst+ option is effective only when the server is reachable. The two options are independent of each other and both can be enabled at the same time. For the +iburst+ option the number of packets in the burst is six, which is the number normally needed to synchronize the clock; for the +burst+ option, the number of packets in the burst is determined by the difference between the current poll exponent and the minimum poll exponent as a power of 2. For instance, with the default minimum poll exponent of 6 (64 s), only one packet is sent for every poll, while the full number of eight packets is sent at poll exponents of 9 (512 s) or more. This insures that the average headway will never exceed the minimum headway. The burst options can result in increased load on the network if not carefully designed. Both options are affected by the provisions described on the link:rate.html[Rate Management and the Kiss-o'-Death Packet] page. In addition, when +iburst+ or +burst+ are enabled, the first packet of the burst is sent, but the remaining packets sent only when the reply to the fist packet is received. If no reply has been received after a timeout set by the +minpoll+ option, the first packet is sent again. This means that, even if a server is unreachable, the network load is no more than at the minimum poll interval. To further reduce the network load when a server is unreachable, an unreach timer is incremented by 1 at each poll interval, but is set to 0 as each packet is received. If the timer exceeds the _unreach threshold_ set at 10, the poll exponent is incremented by 1 and the unreach timer set to 0. This continues until the poll exponent reaches the maximum set by the +maxpoll+ option. ''''' include::includes/footer.txt[] ntpsec-1.1.0+dfsg1/packaging/0000775000175000017500000000000013252650651015554 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/packaging/packaging.txt0000644000175000017500000000716313252364117020245 0ustar rlaagerrlaager= Guidance for package builders = == Package parts == Some useful components for package building can be found in the etc/ subdirectory, including init boot time scripts, systemd unit files, and boilerplate for default ntpd configuration. == ntp.conf installation == Installation from source (waf install) does not attempt to put an ntp.conf or ntp.d in place. Your installable package should do this. The reason this is so is that NTPsec does not yet have an authorized pool group of its own. This may change in the future. == Platforms without Python == Many tools (actually, almost everything except the core daemon itself) have been moved from C to Python. This is an important step for improving maintainability and reducing attack surface. However, we know that some platforms cannot support Python or choose not to include it in their core configuration. For these platforms, we recommend using cx_Freeze to render the NTPsec Python programs, and all their imported modules, into standalone files. These files can then be copied to a host that does not have Python installed and executed just as if Python was installed. cx_Freeze documentation lives http://cx-freeze.readthedocs.io/en/latest/index.html[here]. Your OS package manager may have a package for cx_Freeze. If not you can install it with pip like this: ``` pip install cx_Freeze ```` You may find that you also need to install the package 'patchelf'. Change to the root directory of the NTPsec source distribution and run the following command: ``` waf cxfreeze ``` Binary executables corresponding to every Python script will now be in the directory named dist along with the other files required for them to run. You can copy the dist directory, and its contents, to a host that does not have Python installed and execute the programs there. There appears to be no speed advantage, or disadvantage, to running the binaries created by cx_freeze. == Cross-era interoperability in modular calendar arithmetic == The protocol necessarily uses time/date stamps of finite length in order to fit into fixed-size fields; they happen to have a 136-year cycle, but the problems this produces aren't dependent on the specific cycle length. Thus, each instance of ntpd speaks time based on a specific epoch (cycle start date). The epoch of era 0 was at the beginning of 1900; the epoch of era 1 will be in 2036. Two instances talking to each other have no way to know that they're based in the same era. To mitigate this problem, each instance has a pivot date and resolves incoming timestamps to the era that minimizes distance between now and the timestamp. This procedure is part of the core protocol specification. An instance's pivot time is constructed from BUILD_EPOCH defined at configure time in config.h. If BUILD_EPOCH is set to a known time then the binaries are reproducible. By default the BUILD_EPOCH is the time when the last './waf configure' was run. You can override the BUILD_EPOCH with './waf configure --build-epoch' or using the SOURCE_DATE_EPOCH environment variable. More information on reproducible builds is at: https://reproducible-builds.org/[https://reproducible-builds.org/] == Disambiguation of NMEA dates == Due to bad design of NMEA0183, the reporting protocol used by many GPS sensors, the NMEA driver in NTPsec sometimes has to make an assumption about what century it is. Choice of a base-century hits the same issues; so here the year derived from the BUILD_EPOCH is also used. The alternative - trusting the system clock to report the right century - could produce very bad behavior near century boundaries, and also on cold-start of systems without an RTC. // end ntpsec-1.1.0+dfsg1/packaging/RPM/0000775000175000017500000000000013252650651016212 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/packaging/RPM/ntpsec.spec0000644000175000017500000000212713252364117020361 0ustar rlaagerrlaagerName: ntpsec Version: 0.9.7 Release: 1%{?dist} Summary: A secure, hardened, and improved implementation of Network Time Protocol derived from NTP Classic, Dave Mills’s original. License: BSD URL: https://www.ntpsec.org/ Source0: ftp://ftp.ntpsec.org/pub/releases/ntpsec-0.9.7.tar.gz %description NTPsec, as its name implies, is a more secure NTP. Our goal is to deliver code that can be used with confidence in deployments with the most stringent security, availability, and assurance requirements. Towards that end we apply best practices and state-of-the art technology in code auditing, verification, and testing. We begin with the most important best practice: true open-source code review. The NTPsec code is available in a public git repository. One of our goals is to support broader community participation. BuildRequires: bison openssl-devel libcap-devel libseccomp-devel pps-tools-devel libcap openssl-libs pps-tools avahi-compat-libdns_sd-devel %global debug_package %{nil} %prep %setup -q %build # %%configure ./waf configure %install # %%make_install ./waf install %files %doc ntpsec-1.1.0+dfsg1/packaging/RPM/README0000644000175000017500000000070613252364117017072 0ustar rlaagerrlaagerTo create the RPM directory structure, run rpmdev-setuptree. This creates all the required directories. Place the ntpsec.spec file in the SPECS directory and download the current tarball file to the SOURCES directory. You do not need to extract the files. RPM does this automatically. From the rpmbuild root directory, run "rpmbuild -ba SPECS/ntpsec.spec". This will extract the code and attempt to run the waf configure, build, and install steps. ntpsec-1.1.0+dfsg1/packaging/README.txt0000644000175000017500000000017413252364117017251 0ustar rlaagerrlaager== Packaging == Packaging scripts go in this directory. Some general guidance for packagers can be found in packaging.txt ntpsec-1.1.0+dfsg1/packaging/SUSE/0000775000175000017500000000000013252650651016333 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/packaging/SUSE/ntp.conf0000644000175000017500000000563213252364117020006 0ustar rlaagerrlaager################################################################################ ## /etc/ntp.conf ## ## Sample NTP configuration file. ## See package 'ntp-doc' for documentation, Mini-HOWTO and FAQ. ## Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. ## ## Author: Michael Andres, ## Michael Skibbe, ## ################################################################################ ## ## Radio and modem clocks by convention have addresses in the ## form 127.127.t.u, where t is the clock type and u is a unit ## number in the range 0-3. ## ## Most of these clocks require support in the form of a ## serial port or special bus peripheral. The particular ## device is normally specified by adding a soft link ## /dev/device-u to the particular hardware device involved, ## where u correspond to the unit number above. ## ## Generic DCF77 clock on serial port (Conrad DCF77) ## Address: 127.127.8.u ## Serial Port: /dev/refclock-u ## ## (create soft link /dev/refclock-0 to the particular ttyS?) ## # server 127.127.8.0 mode 5 prefer ## ## Undisciplined Local Clock. This is a fake driver intended for backup ## and when no outside source of synchronized time is available. ## # server 127.127.1.0 # local clock (LCL) # fudge 127.127.1.0 stratum 10 # LCL is unsynchronized ## ## Add external Servers using ## # rcntpd addserver ## The servers will only be added to the currently running instance, not ## to /etc/ntp.conf. ## # Access control configuration; see /usr/share/doc/packages/ntp/html/accopt.html for # details. The web page # might also be helpful. # # Note that "restrict" applies to both servers and clients, so a configuration # that might be intended to block requests from certain clients could also end # up blocking replies from your own upstream servers. # By default, exchange time with everybody, but don't allow configuration. restrict -4 default notrap nomodify nopeer noquery restrict -6 default notrap nomodify nopeer noquery # Local users may interrogate the ntp server more closely. restrict 127.0.0.1 restrict ::1 # Clients from this (example!) subnet have unlimited access, but only if # cryptographically authenticated. #restrict 192.168.123.0 mask 255.255.255.0 notrust ## ## Miscellaneous stuff ## driftfile /var/lib/ntp/drift/ntp.drift # path for drift file logfile /var/log/ntp # alternate log file # logconfig =syncstatus + sysevents # logconfig =all # statsdir /tmp/ # directory for statistics files # filegen peerstats file peerstats type day enable # filegen loopstats file loopstats type day enable # filegen clockstats file clockstats type day enable # # Authentication stuff # keys /etc/ntp.keys # path for keys file trustedkey 1 # define trusted keys requestkey 1 # key (7) for accessing server variables controlkey 1 # key (6) for accessing server variables ntpsec-1.1.0+dfsg1/packaging/SUSE/_service0000644000175000017500000000105313252364117020051 0ustar rlaagerrlaager https://gitlab.com/NTPsec/ntpsec.git git enable ntpsec yes 0.9.7+git%cd.%h *.tar xz ntpsec-1.1.0+dfsg1/packaging/SUSE/ntpsec.changes0000644000175000017500000005733613252364117021174 0ustar rlaagerrlaager------------------------------------------------------------------- Wed Jun 7 12:18:08 UTC 2017 - tchvatal@suse.com - Enable more options and refclocks by default ------------------------------------------------------------------- Wed Jun 7 12:01:11 UTC 2017 - tchvatal@suse.com - Add conflict even to utils package to make factory bot happy - Update to version 0.9.7+git20170607.7c0008c91: * Address GitLab issue #316: "ntpd" dies unexpected unpeering a refclock * Address GitLab issue #322: ntpwait should not terminate with a traceback at SIGINT * Fix for issue #334 - restrictions using ephemeral copy of address. * Another try at fixing issue #334 * Fix for Issue #335 (-Wsign-conversion) * Fix for Issue #339 (-Wsign-conversion) ------------------------------------------------------------------- Fri May 26 14:37:16 UTC 2017 - jengelh@inai.de - Fixup wording in description. ------------------------------------------------------------------- Fri May 26 12:00:19 UTC 2017 - tchvatal@suse.com - Add conflicts and provides to allow it to live in Tumbleweed ------------------------------------------------------------------- Thu May 25 08:21:41 UTC 2017 - tchvatal@suse.com - Update to version 0.9.7+git20170525.464804153: * Catch ntp.packet.ControlException in ntpwait * ntpdig: Use python's built-in UTC offset variables since they handle DST * Remove duplicate check for enabling of leap smear option * libntp: make ntpcal_ntp64_to_date() static * libntp: make ntpcal_days_in_months() static * libntp: remove unused function: ntpcal_daysplit_to_date() * libntp: remove unused function: ntpcal_daysec_to_tm() * libntp: make get_ostime() static. * libntp: add missing static to atexit_ssl_cleanup() * libntp: remove unused finction sockaddr_masktoprefixlen() * libntp: move convertRefIDToLFP() into tests/libntp, make static. * libntp: remove unused function ntp_be16enc() * libntp: remive unused function ntp_be32enc() * libntp: remove unused function ntp_be64enc() * waf: show loccount in .waf help * libntp: Move +ymd2yd() into ntp_calendar.c. * libntp: make errno_to_str() and addto_syslog() static. * libntp: make mvfprintf(0 static * libntp: remove unused mfprintf() * libntp: move msnprintf() to tests. It was only a test helper. * tests: rearrange to eliminate an #ifndef. * libntp: move mstolfp() to be a test local. * libntp: remove emalloc() which has been commented out for a while. * libntp: remove unused ntp_gettime(). * libntp: make auth_moremem() static. * libntp: move atolfp() to a static in tests. * libparse: remove unused get_mbg_svno() * libparse: make get_mbg_health() static * libparse: make get_mbg_cfg() static. * libparse: make get_mbg_tgps() static. * libparse: make get_mbg_tm(0 static * libparse: remopve unused get_mbg_ttm() * libparse: remove unused get_mbg_synth() * libparse: remove unused get_mbg_tzdl() * libparse: add missing prototype for get_mbg_comparam(). * libparse: remove unused get_mbg_tzname() * libparse: remove unused get_mbg_portparam() * libparse: remove unused get_mbg_comparam() * libparse; remove unused -get_mbg_eph() * libparse: remove unused get_mbg_alm() * libparse: remove unused get_mbg_iono() * libparse: make parse_restart() static. * libparse: remove unused parse_iopps() * combine two copis of days_per_year() into one. * libparse: remove unused syn_simple() * libparse: make pps_simple() static. * libparse: remove unused pps_zero() * libparse: make implicit cast into explicit one. * libparse: make 2 implicit conversions explcit. * libntp: maike implicit conversion explicit. * remove uslelss extern keytype_from_text() * remove unused typedef and extern: pset_tod_using set_tod_using() * ntpd: remove unused rereadkeys() and ntp_exit() * ntpd: remove redudnat (void) cast on function returning void. * remove two uneeded externs: octtoint() and ranp2() * remove unused extern: clockname() * libntp: make syslog_fname and syslog_abs_fname static. * remove two unused externs: ntp_tty_setup(), ntp_tty_ioctl() * remove TTY define from header, put in the one place it is used. * remove unused trimble_scmds[] * libparse: remove unused array, and wrong file comment. * remove unused extern: get_free_recv_buffer_alloc() * mbg_gps166: remove lots of unused cruft. * ascii.h: remove many unused defines. * mbg_gps166: remove unused SYNTH stuff. * mbg_gps166: remove unused header cruft. * mbg_gps166: remove unused macro * ntp_assert: remove Calypso support. * tests; remove duplicate is_leapyear() * ntpd.h: remove unused define. * ntp_control: make ctlsysstatus() static. * ntpd.h: remove unused define. * ntp_io: remove unused interface_enumerate() * ntp_io: make findbcastinter() static. * ntp_io: don't duplicate latoa(pif) & localaddrtoa(pif) * ntpd.h: remove unused sys_clocktime * ntp_peer: make peer_reset() static * ntpd.h: remove unused extern process_packet() * ntp_proto: make clock_select() and leapsec static. Remove leapdif. * ntp_proto: make sys_maxdist static, remove unused extern clear() * ntp_control: make auth_timereset static, move reset_auth_stats() * ntpd.h: remove unused extern record_crypto_stats() * ntp_config: make cfg_tree_history static. * ntp_control: make a bunch of counters static. * ntp_loopfilter: make clock_minstep, clock_panic, and pll_status static. * ntp_loopfilter: make state and ext_enable static. * ntpd.h: remove unused extern last_time * ntp_monitor: make mru_alloc static * ntp_peer: make several variables static. * ntp_peer: make several variables static. * ntp_proto: make several variables static, and remove an unused one. * ntp_io: make blockMask static. * ntp_scanner: make conf_file_sum static. * ntpd.h: remove 3 unused externs. * ntpd.c: make droproot, user, group and chrootdir static. * ntp_dns: remove unused define. * ntp_proto: make some definitions static. remove 2 unused. * ntp_peer: make ntohl_fp() static. * change isleap_4() to is_leapyear(). * ntp_control: make a define static. * ntp_time: make a define static. * ntp.h: remove unused INADDR_NTP * ntp_io: remove unused sau_from_netaddr() * ntp_refclock: make refclock_setup() local. * clk_meinberg: make MBG_EXTENDED static. * replace duplicate S_PER_DAY with SECSPERDAY. * replace duplicate S_PER_H with SECSPERHR. * tests: remove yet another different leap year test. * tests/calendar: fix coverity leak. * tests: remove unused check_y2k, add those tests into calendar test. * tests: don't test parse_to_unixtime() if no refclocks. * ntpfrob: fix incorrect coverity override of real problem. * keyword-gen: make some functions static. * keyword-gen: fix memory leak, remove coverity overrides. * ntpfrob: FreeBSD and NetBSD fail to define PPS_API_VERS publicly. ------------------------------------------------------------------- Sun May 21 17:56:52 UTC 2017 - opensuse-packaging@opensuse.org - Update to version 0.9.7+git20170521.fdb63ab2a: * Add droproot support type to the configure summary report * Check for libseccomp via pkg-config first, then fall back to a direct search ------------------------------------------------------------------- Sat May 20 15:49:26 UTC 2017 - tchvatal@suse.com - Update to version 0.9.7+git20170519.9b296f50d: * libparse: add get_msb_ushort(), with tests. * tests: use the right get_msb_*short() * change "(unsigned short)getshort()' to get_msb_ushort() * binio: move 2 private "static short getshort()" into one lib function. * tests: add test for getmsb_short() * remove pointless casts of (unsigned char*) to (unsigned char *). * Peers display with raw address no longer forces 80 chars on wide terminals * dolfptoa: fix a 32 bit warning that showed on RasPi. * leapsec_query: removea 32-bit warning. * process_control; silence a warning on 32-bit RasPi. * process_control: fix two 32-bit warnings about signed-ness. * onceore: remove pointless cast from long to ulong to long. * refclock_jjy: convert several implicit casts to explicit. * refclock_trimble: fix bad cast to (ulong). * ntp_control: change implicit cast to explicit. * refclock_oncore: change implicit cast to explicit. * refclock_generic: change implicit cast to explcit. * refclock_generic: change incorrect cast: sizeof not ulong. * refclock_neoclock: chjange implicit casts to explicit. * refclock_magnavox: change implicit acst top explicit. * sht: change implicit cast to explicit. * ntp_proto: Change several implicit casts to explicit. * ntp_loopfilter: change implicit cast to explicit. * refclock_shm: Change 4 implicit casts to explicit. * ntp_proto: Change two implicit acsts to explcit. * tests: fix type of free_recvbuffs() * tests: change some implicit casts to explicit. * If seccomp was requested but can't be found during configure, fail. * seccomp now crashes if it doesn't work * Added -a and -k options to ntpq. * Added missing docs for new ntpq options. * atolfp: fix a potential unintended sign conversion. * hextolfp: fix a ptotential unintended sign conversion. * refclock_gpsd: tv_nsec is long, not utin32_t. * refclock_jjy: fix two sign conversion warnings. * Typo in ntpq docs * tests: time_t is not uint32_t. * net_fp: remove two unused macros. * jitter: fix signed, and unsigned, mixup * refclock_generic: fix overagressive cast. * refclock_generic: make some implicit casts explicit. * tests: fix signed/unsigned confusion. * refclock_gpsd: stop some unsigned/signed shuffling. * refclock_generic: make an implicit cast explicit. * Remove package hints for libcap2 * ntpviz: add skewness and kurtosis stats. ------------------------------------------------------------------- Mon May 15 11:29:53 UTC 2017 - mpluskal@suse.com - Update to version 0.9.7+git20170515.aadc83545: * ntpmon: Add "-n"/"--numeric" option * Fixed division by zero error in ntpmon * refclock_nmea: fix 4800 baud * mrulist: Fix "kod" and "limited" error "Unknown parameter" * mrulist: Make "resall" and "resany" hex instead of decimal * ntptime: fix dumb macro name: NS_PER_MS_FLOAT -> NS_PER_US_FLOAT * Added encoding definitions to python files * mrulist: Fix "sort=addr"/"sort=-addr" in Python 3 * Remove now unused python import * pep8 fixes for ntpkeygen * Fixed ntpq ifstat bug #280 * Address GitLab issue #289: ntpfrob/tickadj broken (warnings)... * Fix a reversed conditionalization. * Address GitLab issue #274: functions fail to inline * Address GitLab issue #276: Tarball should include pre-built man pages * Partially revert: cfe5c7122cc6905365a9d1396be6abd5ba59ff69 * Tweak for getting started after DNS lookup works. * Fix dtolft() on NetBSD, issue 264 * Simplify FDF_RoundTrip * packet auth: remove undocumented DEFFAULT_NTPKEYS. * waf: pep8 fixes pythonize-header * waf: fix minor pep8 nits. * waf: minor pep8 fixes in wscript's. * waf: several minor pep8 fixes in wscript's. * ntpviz: minor pep8 tweaks * tests: minor pep8 tweaks. BTW, this test has existing failures... * Hack to dance around bug in older Bison. Issue 287 * Address GitLab issue #296: compiler warning (gcc 7.0.1): lfpfunc.c * Address GitLab issue #294: compiler warning (gcc 7.0.1): refclock_neoclock * Address GitLab issue 295: compiler warning (gcc 7.0.1): ntp_loopfilter.c * Fixes formatting error, and makes ifstats less brittle. * waf: move alignment warnings back to --enable-debug-warnings * Fix warning in refclock_oncore on FreeBSD * Remove unused ENABLE_DNS_RETRY * Address GitLab issue #263: Catchall for ntpq/ntpmon units problems * mrufail: explain how to get mu on old xterm. * waf: improve test for unsupported CC flags. * waf: Add conditional -Wimplicit-fallthrough to --enable-debug-warnings. * xterm/utf-8: explain need for LANG=en_US.utf8 * Fixed unicode errors in tests. * Fix crash if NMEA driver can't open /dev/gpsx, Issue #299 * waf: add a test for nanotime(), for old macOS * FALLTHRU: another test for issue #295 * Enable gcc:7 CI checks * Handle more than ten interfaces in __ordlist(). * FALLTHRU: yet another try at pacifying gcc 7.1 * Fixed type bug in packet.py * ifstats: fix dropping of valid interfaces. Fixes #298 * LIB_BUFLENGTH: gcc complained that 128 was too short, increased to 192. * Revert "LIB_BUFLENGTH: gcc complained that 128 was too short, increased to 192." * sLog: 2nd try at fixing #293, make log buffers bigger. * reflock_jjy: remove un-needed -1's on snprintf(). * FALLTHRU: llvm 8.0.0 does not understand __attribute__ ((fallthrough)) * FALLTHRU: try again, sadly gcc doc is inconsistent on the magic foo. * FALLTHRU: fix issue #295, warnings on implicit fallthrough * Changed ifstats en flag to be more robust * Adjusted ifstats header spacing * Patch from Takao abe adapted - prevents Coverity warnings. * Documentation corrections from Takao Abe. * FALLTHRU: only gcc > 6 understands __attribute__((fallthrough)) * Address Coverity CID 161765: Integer handling issues (SIGN_EXTENSION). * Fix resource leaks in directory-walking code. * Revert "Address Coverity CID 161765: Integer handling issues (SIGN_EXTENSION)." * Added guards to command line args that take ints. Added guard function. * Missed one! * waf: add check to help(). * tests: add tests for get_lsb_long(). * tests: add tests for get_lsb_short(). * tests: add tests for get_msb_short)( and get_msb_long(). * tests: remove incorrect comment * binio: remove 6 unused macros, 2 were broken. * tests/binio: fix 2 tests, and comment them out. * binio: change get_lsb_ulong() to get_lsb_int32() * binio: remove unused, and broken, get_msb_long(). * binio: remove unused put_lsb_long() * binio: remove unused put_msb_short(). * binio: remove unused put_msb_long() * binio: rename get_lsb_short() to get_lsb_int16() * tests: add tests for get_lsb_uint16() and get_lsb_uint32(). * binio: simplify get_lsb_int16(), all tests pass. * tests: add tests for put_lsb_short() * binio: rename put_lsb_short(,long() to put_lsb_uint16(,uint16_t) * refclock_generic: change getshort() to remove pointless castings. * Test results of test-options by running --version * Tweak to mru list allocation doc * Tweak warning messages when --enable-seccomp won't work * Address Coverity CID 161762: Out-of-bounds read (OVERRUN) * Address Issue #303: ntpq shell command unknown. * Remove the Jupiter/Zodiac driver. It cannot have worked since August 1999. * Add inline help in ntpq for "units" subcommand * Remove the shell command from ntpq. * Simplify ntpq's do_EOF and do_exit functions * Add peer depricated message * Tweak comment in ntp_monitor, Issue #281 * Implement ENABLE_DNS_LOOKUP * DNS bug fixing/cleanups * Typo * Add message when closing lots of files * Update seccomp to work on ARM * seccomp additions for Arch Linux, Issue #275 ------------------------------------------------------------------- Tue May 2 10:17:29 UTC 2017 - mpluskal@suse.com - Fix path to ntpd in provided unit file ------------------------------------------------------------------- Tue May 2 10:06:18 UTC 2017 - mpluskal@suse.com - Update to version 0.9.7+git20170429.dcab3ef9b: * Add a pivot-related bug warning. * Typo fix. * Add warnings about GPS wraparound. * Revision of GPS rollover warning. * ntpviz: change ppt to ‰ (millage symbol). * ntpviz: glossary tweak. * ntpviz: better define mu and some other items. * ntpviz: comment typo * Fix typos * add GPS pivot entry to docs/ntpspeak.txt * Fixed error in ntpq manpage * Lexically split waf's cross compiling flags * Cross-compiler also needs it's arguments split lexically... * Fix typos in comments * PEP8 fixes for calc_tickadj * PEP8 fixes for make-leap-seconds.py * Update a few more references ":config" -> "config" * Add ntpq's ":config" -> "config" to the list of incompatible changes * Stop using VCS_BASENAME since we really want the name of the project ------------------------------------------------------------------- Tue Apr 25 06:58:54 UTC 2017 - tchvatal@suse.com - Adjust the spec for the bugfixes included upstream - Update to version 0.9.7+git20170425.f7104e3b9: * systime: Fix comparing a double to zero. * ntp_loopfilter: fix 4 comparisons of double to zero. * ntp_conrol: fix comparing double to zero * ntp_loopfilter: fix comparing double to zero. * ntp_proto: fix comparing a double to zero. * ntp_scanner: fix a double compared to zero. Add fixme. * magnavox: fix last two compare of double to a number. * waf: -Wfloat-equal warning always on, no warnings now. * waf: comment out: -Wbad-function-cast * Rename EPOCH to BUILD_EPOCH to reduce ambiguity * Remove an incorrect assertion from devel/packagers.txt. * Eliminate wafhelpers.util * PEP8 fixes * Remove obsolete text. * libisc/error: add two attribute((format)) tags. * waf: remove -Wmissing-format-attribute * Load waf's gnu_dirs module and use it for installation target directories * Fixed warnings in ntpd/ntp_signd.c when ENABLE_MSSNTP is on * Move wafhelpers/configure.py into wscript * PEP8: indent to 4 spaces * Eliminate parse_version() function * We already import os, no need to import os.path.exists()... ------------------------------------------------------------------- Sat Apr 22 16:39:04 UTC 2017 - mpluskal@suse.com - Update to version 0.9.7+git20170422.4162ff742: * Typo * Fix typo in etc/ntplogtemp.service * waf: define EPOCH=[build-date], for reproducable builds. * EPOCH: remove REPRO_DATE and NTPD_IGNORE_BUILD_DATE. * ntpcal_get_build_date: now uses EPOCH. * ntp_calendar: add missing return. * magnavox: remove last __DATE__. * EPOCH: remove all mention of __DATE__ and __TIME__ * EPOCH: add URL to reproduceable build information. * Fix const warning from FreeBSD * ntpviz: When Peer Offsets has more than 6 peers, place key below graph. * libisc: mark two functions attribute const * lib_srtbuf: note where an attribute const fails * waf: remove annoying warning: -Wsuggest-attribute=const * ntp_filegen: mark one attribute pure. * ntp_leapsec: mark function attribute pure * refclock_trimble: mark one function static and attribute pure * libisc/assertions: fix possibly uninit variable. ------------------------------------------------------------------- Wed Apr 19 12:18:21 UTC 2017 - tchvatal@suse.com - Update to version 0.9.7+git20170419.bfac46769: * Mark alt text for the ntpsec logo image as "alt" * Address issue #268: Waf uses current date and time breaking repro builds * Added poll to clock variables * Add step for testing cx_Freeze to devel/pre-release.txt * Remove useless load of bison in waf * Tweak ntpq/peers printout to handle new DNS * Restore cross-era interoperability via pivoting timestamps on build data. * New systemd units to run NTPviz data gathering and graphing. * DNS reworking was not 10 years ago. * check_sanity() is now used in only 1 place. No need to keep it separate. * Add Debian Jessie to the list of GitLab Pipeline targets * Fixed lack of 4th digit in unitless display. * Nailed missing clockvar units bug to the wall. * Add configure-time warning about 32-bit time_t. ------------------------------------------------------------------- Sat Apr 15 19:38:07 UTC 2017 - tchvatal@suse.com - Update to version 0.9.7+git20170415.e3c051e3e: * Allow exit if no DNS ntp_seccomp * Attempt to address Gitlab issue #257: Warnings with clang 8... * Add sodium installation to buildprep where needed. * Improvements for systemd support from Tomáš Chvátal. * Add instructions for setting up boot-time startup to INSTALLL... * Address issue #239: pylib/packet.py should get key file location by reading ntp.conf * Fixed ntpmon crash when detail mode active and peers display hidden * Fix typos/grammar * Revert "Add sodium installation to buildprep where needed." ------------------------------------------------------------------- Thu Apr 13 20:01:01 UTC 2017 - tchvatal@suse.com - Update to version 0.9.7+git20170413.25a240c2e: * tests/decodenetnum: test for /etc/services and port names. ------------------------------------------------------------------- Thu Apr 13 19:21:57 UTC 2017 - tchvatal@suse.com - Add netcfg dependency to fix tests - Also require the netcfg as without it some operations won't work ------------------------------------------------------------------- Thu Apr 13 18:36:42 UTC 2017 - tchvatal@suse.com - Update to version 0.9.7+git20170413.c3e3769f9: * Unit bug fixes * waf: Prepend, anot append, computed CFLAGS. * decodenetnum(): return more error codes. * tests/leapsec: stop mising bools and ints ------------------------------------------------------------------- Thu Apr 13 08:50:04 UTC 2017 - tchvatal@suse.com - Switch to git snapshot for now as we debug issues with upstream - Update to version 0.9.7+git20170412.f0451c5a8: * libntp: fix format signs. * fix format signedness. * ntp_control: fix format signed-ness * test/lfpfunc: make sure printf works on 32-bit. * oncore: fix format signed-ness * refclock_mode: fix format signed-ness * jupiter: fix format signed-ness * Formatting function changeover and associated fixes. * refcklock_nmea: fix numerous format sin problems. * Fix two format sign warnings. ------------------------------------------------------------------- Thu Apr 13 08:39:18 UTC 2017 - tchvatal@suse.com - Do not use %license macro so we install on 42.1 ------------------------------------------------------------------- Wed Apr 12 21:08:13 UTC 2017 - tchvatal@suse.com - BuildRequire xsltproc ------------------------------------------------------------------- Wed Apr 12 20:51:38 UTC 2017 - tchvatal@suse.com - Use internal waf so we can build on older distributions ------------------------------------------------------------------- Wed Apr 12 20:38:22 UTC 2017 - tchvatal@suse.com - Enable gdb debugoutputs ------------------------------------------------------------------- Sun Apr 9 09:13:52 UTC 2017 - mpluskal@suse.com - Enable mdns ------------------------------------------------------------------- Fri Apr 7 14:03:04 UTC 2017 - tchvatal@suse.com - Switch to python2 as python3-gpsd does not exist (by upstream) and we need those binaries to work ------------------------------------------------------------------- Fri Apr 7 08:44:47 UTC 2017 - tchvatal@suse.com - Require python3-gps for ntploggps ------------------------------------------------------------------- Thu Apr 6 19:39:26 UTC 2017 - tchvatal@suse.com - Fix python shebangs - Try to make pps-tools-devel included for hi-precision ------------------------------------------------------------------- Thu Apr 6 19:17:56 UTC 2017 - mpluskal@suse.com - Update buildrequirements ------------------------------------------------------------------- Thu Apr 6 17:49:00 UTC 2017 - tchvatal@suse.com - Add config from the ntp package ntp.conf - Split the python module and utils using it to separate pkgs ------------------------------------------------------------------- Wed Apr 5 08:49:12 UTC 2017 - tchvatal@suse.com - Restrict version on the waf that is needed to build this ------------------------------------------------------------------- Wed Apr 5 07:29:06 UTC 2017 - tchvatal@suse.com - Inherit items from ntp.spec: * Add systemd services * Add logrotate file * Add firewall file - Enable testsuite (always returns 0 atm, 2 tests failing) - Create ntp user the same way as normal ntp does ------------------------------------------------------------------- Wed Apr 5 07:16:20 UTC 2017 - tchvatal@suse.com - Version update to 0.9.7 - Cleanup the deps a bit - Use system waf ------------------------------------------------------------------- Mon Feb 13 13:52:43 UTC 2017 - michael@stroeder.com - update to 0.9.6 ------------------------------------------------------------------- Wed Dec 7 22:29:05 UTC 2016 - malcolmlewis@opensuse.org - Initial build. ntpsec-1.1.0+dfsg1/packaging/SUSE/_servicedata0000644000175000017500000000035213252364117020704 0ustar rlaagerrlaager https://gitlab.com/NTPsec/ntpsec.git 7c0008c91645084fe46b246074bdf9506e7abafcntpsec-1.1.0+dfsg1/packaging/SUSE/README.txt0000644000175000017500000000020713252364117020025 0ustar rlaagerrlaagerSUSE packageing scripts go here Upstream project for them is located at: https://build.opensuse.org/package/show/network:time/ntpsec ntpsec-1.1.0+dfsg1/packaging/SUSE/ntp.firewall0000644000175000017500000000052713252364117020664 0ustar rlaagerrlaager## Name: xntp Server ## Description: Opens ports for xntp. # space separated list of allowed TCP ports TCP="" # space separated list of allowed UDP ports UDP="ntp" # space separated list of allowed RPC services RPC="" # space separated list of allowed IP protocols IP="" # space separated list of allowed UDP broadcast ports BROADCAST="" ntpsec-1.1.0+dfsg1/packaging/SUSE/logrotate.ntp0000644000175000017500000000027313252364117021055 0ustar rlaagerrlaager/var/log/ntp { compress dateext maxage 365 rotate 99 size=+2048k notifempty missingok copytruncate postrotate chmod 644 /var/log/ntp endscript } ntpsec-1.1.0+dfsg1/packaging/SUSE/ntpsec.spec0000644000175000017500000001367013252364117020507 0ustar rlaagerrlaager# # spec file for package ntpsec # # Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2016 Malcolm J Lewis # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ # %if 0%{?suse_version} >= 1320 || 0%{?is_opensuse} %bcond_without libbsd %else %bcond_with libbsd %endif Name: ntpsec Version: 0.9.7+git20170607.7c0008c91 Release: 0 Summary: Improved implementation of Network Time Protocol License: BSD-2-Clause and NTP and BSD-3-Clause and MIT Group: System/Base Url: https://www.ntpsec.org/ Source0: ntpsec-%{version}.tar.xz Source3: %{name}.changes Source4: logrotate.ntp Source7: ntp.firewall Source8: ntp.conf BuildRequires: asciidoc BuildRequires: avahi-compat-mDNSResponder-devel BuildRequires: bison BuildRequires: fdupes # Needed for waf init in the git snapshot BuildRequires: git-core BuildRequires: libcap-devel BuildRequires: libxslt-tools # Required for tests to pass BuildRequires: netcfg BuildRequires: openssl-devel BuildRequires: pkgconfig BuildRequires: pps-tools-devel BuildRequires: pkgconfig(libevent) BuildRequires: pkgconfig(libseccomp) BuildRequires: pkgconfig(python) Requires: netcfg Requires: ntpsec-utils Requires(pre): pwdutils Recommends: logrotate # For ntpleapfetch Recommends: wget Conflicts: otherproviders(ntp) Provides: ntp = 4.2.9 BuildRoot: %{_tmppath}/%{name}-%{version}-build %if %{with libbsd} BuildRequires: pkgconfig(libbsd) %endif %description A more secure implementation of NTP, derived from NTP Classic, Dave Mills’s original. %package -n python-ntp Summary: Python ntpsec bindings Group: Development/Languages/Python %description -n python-ntp The ntpsec python bindings used by various ntp utilities. %package utils Summary: Utilities and commands for ntp Group: System/Base Requires: %{name} = %{version} # For ntploggps Requires: python-gpsd Requires: python-ntp # For ntpviz Recommends: python-psutil Conflicts: ntp < 4.2.9 %description utils The ntpsec utilities relying on the python module of ntp %prep %setup -q # Fix python shebangs sed -i -e 's:#!%{_bindir}/env python:#!%{_bindir}/python2:' \ ntpclients/* %build # We use the date from the changes file epoch=`date --date "@\`stat --format %%Y %{SOURCE3}\`" +"%%s"` export CFLAGS="%{optflags}" export CCFLAGS="%{optflags}" ./waf configure \ --build-epoch="$epoch" \ --enable-debug \ --prefix=%{_prefix} \ --mandir="%{_mandir}" \ --python=%{_bindir}/python2 \ --pythonarchdir=%{python_sitearch} \ --pythondir=%{python_sitearch} \ --enable-seccomp \ --enable-debug-gdb \ --enable-early-droproot \ --enable-leap-smear \ --enable-mssntp \ --enable-lockclock \ --refclock=all ./waf build --verbose %{?_smp_mflags} %install ./waf install --destdir=%{buildroot} # Use correct path in unit file sed -i "s|bin|sbin|g" etc/ntpd.service # FIXME: As long as systemdenable switch for install is broken install -m 0644 -D etc/ntpd.service %{buildroot}/%{_unitdir}/ntpd.service install -m 0644 -D etc/ntp-wait.service %{buildroot}/%{_unitdir}/ntp-wait.service ln -s service %{buildroot}%{_sbindir}/rcntpd ln -s service %{buildroot}%{_sbindir}/rcntp-wait install -m 0644 -D %{SOURCE4} %{buildroot}%{_sysconfdir}/logrotate.d/ntp install -m 0644 -D %{SOURCE7} %{buildroot}%{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/ntp install -m 0644 -D %{SOURCE8} %{buildroot}%{_sysconfdir}/ntp.conf %fdupes -s %{buildroot} %check ./waf check --verbose %{?_smp_mflags} %pre getent group ntp >/dev/null || groupadd -r ntp getent passwd ntp >/dev/null || useradd -u 74 -r -g ntp -d %{_localstatedir}/lib/ntp -s /sbin/nologin -c "NTP daemon" ntp %service_add_pre ntp.service ntpd.service exit 0 %pre utils %service_add_pre ntp-wait.service %post %service_add_post ntpd.service %post utils %service_add_post ntp-wait.service %preun %service_del_preun ntpd.service %preun utils %service_del_preun ntp-wait.service %postun %service_del_postun ntpd.service %postun utils %service_del_postun ntp-wait.service %files -n python-ntp %defattr(-,root,root) %{python_sitearch}/ntp %files utils %defattr(-,root,root) %{_bindir}/ntploggps %{_bindir}/ntpdig %{_bindir}/ntpkeygen %{_bindir}/ntpmon %{_bindir}/ntpq %{_bindir}/ntpsweep %{_bindir}/ntptrace %{_bindir}/ntpviz %{_bindir}/ntpwait %{_bindir}/ntplogtemp %{_mandir}/man1/ntploggps.1%{?ext_man} %{_mandir}/man1/ntpdig.1%{?ext_man} %{_mandir}/man8/ntpkeygen.8%{?ext_man} %{_mandir}/man1/ntpmon.1%{?ext_man} %{_mandir}/man1/ntpq.1%{?ext_man} %{_mandir}/man1/ntpsweep.1%{?ext_man} %{_mandir}/man1/ntptrace.1%{?ext_man} %{_mandir}/man1/ntpviz.1%{?ext_man} %{_mandir}/man8/ntpwait.8%{?ext_man} %{_mandir}/man1/ntplogtemp.1%{?ext_man} %{_sbindir}/rcntp-wait %{_unitdir}/ntp-wait.service %files %defattr(-,root,root) %doc NEWS README LICENSE %config(noreplace) %{_sysconfdir}/ntp.conf %{_sbindir}/rcntpd %{_bindir}/ntpfrob %{_bindir}/ntpleapfetch %{_bindir}/ntptime %{_sbindir}/ntpd %{_mandir}/man5/ntp.conf.5%{?ext_man} %{_mandir}/man5/ntp.keys.5%{?ext_man} %{_mandir}/man8/ntpd.8%{?ext_man} %{_mandir}/man8/ntpfrob.8%{?ext_man} %{_mandir}/man8/ntpleapfetch.8%{?ext_man} %{_mandir}/man8/ntptime.8%{?ext_man} %config %{_sysconfdir}/logrotate.d/ntp %config %{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/ntp %{_unitdir}/ntpd.service %changelog ntpsec-1.1.0+dfsg1/waflib/0000775000175000017500000000000013252650652015075 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/waflib/Configure.py0000644000175000017500000002647213210576031017371 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,shlex,sys,time,re,shutil from waflib import ConfigSet,Utils,Options,Logs,Context,Build,Errors WAF_CONFIG_LOG='config.log' autoconfig=False conf_template='''# project %(app)s configured on %(now)s by # waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s) # using %(args)s #''' class ConfigurationContext(Context.Context): '''configures the project''' cmd='configure' error_handlers=[] def __init__(self,**kw): super(ConfigurationContext,self).__init__(**kw) self.environ=dict(os.environ) self.all_envs={} self.top_dir=None self.out_dir=None self.tools=[] self.hash=0 self.files=[] self.tool_cache=[] self.setenv('') def setenv(self,name,env=None): if name not in self.all_envs or env: if not env: env=ConfigSet.ConfigSet() self.prepare_env(env) else: env=env.derive() self.all_envs[name]=env self.variant=name def get_env(self): return self.all_envs[self.variant] def set_env(self,val): self.all_envs[self.variant]=val env=property(get_env,set_env) def init_dirs(self): top=self.top_dir if not top: top=Options.options.top if not top: top=getattr(Context.g_module,Context.TOP,None) if not top: top=self.path.abspath() top=os.path.abspath(top) self.srcnode=(os.path.isabs(top)and self.root or self.path).find_dir(top) assert(self.srcnode) out=self.out_dir if not out: out=Options.options.out if not out: out=getattr(Context.g_module,Context.OUT,None) if not out: out=Options.lockfile.replace('.lock-waf_%s_'%sys.platform,'').replace('.lock-waf','') out=os.path.realpath(out) self.bldnode=(os.path.isabs(out)and self.root or self.path).make_node(out) self.bldnode.mkdir() if not os.path.isdir(self.bldnode.abspath()): conf.fatal('Could not create the build directory %s'%self.bldnode.abspath()) def execute(self): self.init_dirs() self.cachedir=self.bldnode.make_node(Build.CACHE_DIR) self.cachedir.mkdir() path=os.path.join(self.bldnode.abspath(),WAF_CONFIG_LOG) self.logger=Logs.make_logger(path,'cfg') app=getattr(Context.g_module,'APPNAME','') if app: ver=getattr(Context.g_module,'VERSION','') if ver: app="%s (%s)"%(app,ver) params={'now':time.ctime(),'pyver':sys.hexversion,'systype':sys.platform,'args':" ".join(sys.argv),'wafver':Context.WAFVERSION,'abi':Context.ABI,'app':app} self.to_log(conf_template%params) self.msg('Setting top to',self.srcnode.abspath()) self.msg('Setting out to',self.bldnode.abspath()) if id(self.srcnode)==id(self.bldnode): Logs.warn('Setting top == out') elif id(self.path)!=id(self.srcnode): if self.srcnode.is_child_of(self.path): Logs.warn('Are you certain that you do not want to set top="." ?') super(ConfigurationContext,self).execute() self.store() Context.top_dir=self.srcnode.abspath() Context.out_dir=self.bldnode.abspath() env=ConfigSet.ConfigSet() env.argv=sys.argv env.options=Options.options.__dict__ env.config_cmd=self.cmd env.run_dir=Context.run_dir env.top_dir=Context.top_dir env.out_dir=Context.out_dir env.hash=self.hash env.files=self.files env.environ=dict(self.environ) if not(self.env.NO_LOCK_IN_RUN or env.environ.get('NO_LOCK_IN_RUN')or getattr(Options.options,'no_lock_in_run')): env.store(os.path.join(Context.run_dir,Options.lockfile)) if not(self.env.NO_LOCK_IN_TOP or env.environ.get('NO_LOCK_IN_TOP')or getattr(Options.options,'no_lock_in_top')): env.store(os.path.join(Context.top_dir,Options.lockfile)) if not(self.env.NO_LOCK_IN_OUT or env.environ.get('NO_LOCK_IN_OUT')or getattr(Options.options,'no_lock_in_out')): env.store(os.path.join(Context.out_dir,Options.lockfile)) def prepare_env(self,env): if not env.PREFIX: if Options.options.prefix or Utils.is_win32: env.PREFIX=Utils.sane_path(Options.options.prefix) else: env.PREFIX='' if not env.BINDIR: if Options.options.bindir: env.BINDIR=Utils.sane_path(Options.options.bindir) else: env.BINDIR=Utils.subst_vars('${PREFIX}/bin',env) if not env.LIBDIR: if Options.options.libdir: env.LIBDIR=Utils.sane_path(Options.options.libdir) else: env.LIBDIR=Utils.subst_vars('${PREFIX}/lib%s'%Utils.lib64(),env) def store(self): n=self.cachedir.make_node('build.config.py') n.write('version = 0x%x\ntools = %r\n'%(Context.HEXVERSION,self.tools)) if not self.all_envs: self.fatal('nothing to store in the configuration context!') for key in self.all_envs: tmpenv=self.all_envs[key] tmpenv.store(os.path.join(self.cachedir.abspath(),key+Build.CACHE_SUFFIX)) def load(self,input,tooldir=None,funs=None,with_sys_path=True,cache=False): tools=Utils.to_list(input) if tooldir: tooldir=Utils.to_list(tooldir) for tool in tools: if cache: mag=(tool,id(self.env),tooldir,funs) if mag in self.tool_cache: self.to_log('(tool %s is already loaded, skipping)'%tool) continue self.tool_cache.append(mag) module=None try: module=Context.load_tool(tool,tooldir,ctx=self,with_sys_path=with_sys_path) except ImportError as e: self.fatal('Could not load the Waf tool %r from %r\n%s'%(tool,sys.path,e)) except Exception as e: self.to_log('imp %r (%r & %r)'%(tool,tooldir,funs)) self.to_log(Utils.ex_stack()) raise if funs is not None: self.eval_rules(funs) else: func=getattr(module,'configure',None) if func: if type(func)is type(Utils.readf): func(self) else: self.eval_rules(func) self.tools.append({'tool':tool,'tooldir':tooldir,'funs':funs}) def post_recurse(self,node): super(ConfigurationContext,self).post_recurse(node) self.hash=Utils.h_list((self.hash,node.read('rb'))) self.files.append(node.abspath()) def eval_rules(self,rules): self.rules=Utils.to_list(rules) for x in self.rules: f=getattr(self,x) if not f: self.fatal('No such configuration function %r'%x) f() def conf(f): def fun(*k,**kw): mandatory=True if'mandatory'in kw: mandatory=kw['mandatory'] del kw['mandatory'] try: return f(*k,**kw) except Errors.ConfigurationError: if mandatory: raise fun.__name__=f.__name__ setattr(ConfigurationContext,f.__name__,fun) setattr(Build.BuildContext,f.__name__,fun) return f @conf def add_os_flags(self,var,dest=None,dup=False): try: flags=shlex.split(self.environ[var]) except KeyError: return if dup or''.join(flags)not in''.join(Utils.to_list(self.env[dest or var])): self.env.append_value(dest or var,flags) @conf def cmd_to_list(self,cmd): if isinstance(cmd,str): if os.path.isfile(cmd): return[cmd] if os.sep=='/': return shlex.split(cmd) else: try: return shlex.split(cmd,posix=False) except TypeError: return shlex.split(cmd) return cmd @conf def check_waf_version(self,mini='1.8.99',maxi='2.0.0',**kw): self.start_msg('Checking for waf version in %s-%s'%(str(mini),str(maxi)),**kw) ver=Context.HEXVERSION if Utils.num2ver(mini)>ver: self.fatal('waf version should be at least %r (%r found)'%(Utils.num2ver(mini),ver)) if Utils.num2ver(maxi) %r'%(filename,path_list,var,ret)) if not ret: self.fatal(kw.get('errmsg','')or'Could not find the program %r'%filename) interpreter=kw.get('interpreter') if interpreter is None: if not Utils.check_exe(ret[0],env=environ): self.fatal('Program %r is not executable'%ret) self.env[var]=ret else: self.env[var]=self.env[interpreter]+ret return ret @conf def find_binary(self,filenames,exts,paths): for f in filenames: for ext in exts: exe_name=f+ext if os.path.isabs(exe_name): if os.path.isfile(exe_name): return exe_name else: for path in paths: x=os.path.expanduser(os.path.join(path,exe_name)) if os.path.isfile(x): return x return None @conf def run_build(self,*k,**kw): lst=[str(v)for(p,v)in kw.items()if p!='env'] h=Utils.h_list(lst) dir=self.bldnode.abspath()+os.sep+(not Utils.is_win32 and'.'or'')+'conf_check_'+Utils.to_hex(h) try: os.makedirs(dir) except OSError: pass try: os.stat(dir) except OSError: self.fatal('cannot use the configuration test folder %r'%dir) cachemode=getattr(Options.options,'confcache',None) if cachemode==1: try: proj=ConfigSet.ConfigSet(os.path.join(dir,'cache_run_build')) except EnvironmentError: pass else: ret=proj['cache_run_build'] if isinstance(ret,str)and ret.startswith('Test does not build'): self.fatal(ret) return ret bdir=os.path.join(dir,'testbuild') if not os.path.exists(bdir): os.makedirs(bdir) cls_name=kw.get('run_build_cls')or getattr(self,'run_build_cls','build') self.test_bld=bld=Context.create_context(cls_name,top_dir=dir,out_dir=bdir) bld.init_dirs() bld.progress_bar=0 bld.targets='*' bld.logger=self.logger bld.all_envs.update(self.all_envs) bld.env=kw['env'] bld.kw=kw bld.conf=self kw['build_fun'](bld) ret=-1 try: try: bld.compile() except Errors.WafError: ret='Test does not build: %s'%Utils.ex_stack() self.fatal(ret) else: ret=getattr(bld,'retval',0) finally: if cachemode==1: proj=ConfigSet.ConfigSet() proj['cache_run_build']=ret proj.store(os.path.join(dir,'cache_run_build')) else: shutil.rmtree(dir) return ret @conf def ret_msg(self,msg,args): if isinstance(msg,str): return msg return msg(args) @conf def test(self,*k,**kw): if not'env'in kw: kw['env']=self.env.derive() if kw.get('validate'): kw['validate'](kw) self.start_msg(kw['msg'],**kw) ret=None try: ret=self.run_build(*k,**kw) except self.errors.ConfigurationError: self.end_msg(kw['errmsg'],'YELLOW',**kw) if Logs.verbose>1: raise else: self.fatal('The configuration failed') else: kw['success']=ret if kw.get('post_check'): ret=kw['post_check'](kw) if ret: self.end_msg(kw['errmsg'],'YELLOW',**kw) self.fatal('The configuration failed %r'%ret) else: self.end_msg(self.ret_msg(kw['okmsg'],kw),**kw) return ret ntpsec-1.1.0+dfsg1/waflib/Options.py0000644000175000017500000001427613210576031017102 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,tempfile,optparse,sys,re from waflib import Logs,Utils,Context,Errors options={} commands=[] envvars=[] lockfile=os.environ.get('WAFLOCK','.lock-waf_%s_build'%sys.platform) class opt_parser(optparse.OptionParser): def __init__(self,ctx): optparse.OptionParser.__init__(self,conflict_handler="resolve",version='waf %s (%s)'%(Context.WAFVERSION,Context.WAFREVISION)) self.formatter.width=Logs.get_term_cols() self.ctx=ctx def print_usage(self,file=None): return self.print_help(file) def get_usage(self): cmds_str={} for cls in Context.classes: if not cls.cmd or cls.cmd=='options'or cls.cmd.startswith('_'): continue s=cls.__doc__ or'' cmds_str[cls.cmd]=s if Context.g_module: for(k,v)in Context.g_module.__dict__.items(): if k in('options','init','shutdown'): continue if type(v)is type(Context.create_context): if v.__doc__ and not k.startswith('_'): cmds_str[k]=v.__doc__ just=0 for k in cmds_str: just=max(just,len(k)) lst=[' %s: %s'%(k.ljust(just),v)for(k,v)in cmds_str.items()] lst.sort() ret='\n'.join(lst) return'''waf [commands] [options] Main commands (example: ./waf build -j4) %s '''%ret class OptionsContext(Context.Context): cmd='options' fun='options' def __init__(self,**kw): super(OptionsContext,self).__init__(**kw) self.parser=opt_parser(self) self.option_groups={} jobs=self.jobs() p=self.add_option color=os.environ.get('NOCOLOR','')and'no'or'auto' if os.environ.get('CLICOLOR','')=='0': color='no' elif os.environ.get('CLICOLOR_FORCE','')=='1': color='yes' p('-c','--color',dest='colors',default=color,action='store',help='whether to use colors (yes/no/auto) [default: auto]',choices=('yes','no','auto')) p('-j','--jobs',dest='jobs',default=jobs,type='int',help='amount of parallel jobs (%r)'%jobs) p('-k','--keep',dest='keep',default=0,action='count',help='continue despite errors (-kk to try harder)') p('-v','--verbose',dest='verbose',default=0,action='count',help='verbosity level -v -vv or -vvv [default: 0]') p('--zones',dest='zones',default='',action='store',help='debugging zones (task_gen, deps, tasks, etc)') p('--profile',dest='profile',default='',action='store_true',help=optparse.SUPPRESS_HELP) gr=self.add_option_group('Configuration options') self.option_groups['configure options']=gr gr.add_option('-o','--out',action='store',default='',help='build dir for the project',dest='out') gr.add_option('-t','--top',action='store',default='',help='src dir for the project',dest='top') gr.add_option('--no-lock-in-run',action='store_true',default='',help=optparse.SUPPRESS_HELP,dest='no_lock_in_run') gr.add_option('--no-lock-in-out',action='store_true',default='',help=optparse.SUPPRESS_HELP,dest='no_lock_in_out') gr.add_option('--no-lock-in-top',action='store_true',default='',help=optparse.SUPPRESS_HELP,dest='no_lock_in_top') default_prefix=getattr(Context.g_module,'default_prefix',os.environ.get('PREFIX')) if not default_prefix: if Utils.unversioned_sys_platform()=='win32': d=tempfile.gettempdir() default_prefix=d[0].upper()+d[1:] else: default_prefix='/usr/local/' gr.add_option('--prefix',dest='prefix',default=default_prefix,help='installation prefix [default: %r]'%default_prefix) gr.add_option('--bindir',dest='bindir',help='bindir') gr.add_option('--libdir',dest='libdir',help='libdir') gr=self.add_option_group('Build and installation options') self.option_groups['build and install options']=gr gr.add_option('-p','--progress',dest='progress_bar',default=0,action='count',help='-p: progress bar; -pp: ide output') gr.add_option('--targets',dest='targets',default='',action='store',help='task generators, e.g. "target1,target2"') gr=self.add_option_group('Step options') self.option_groups['step options']=gr gr.add_option('--files',dest='files',default='',action='store',help='files to process, by regexp, e.g. "*/main.c,*/test/main.o"') default_destdir=os.environ.get('DESTDIR','') gr=self.add_option_group('Installation and uninstallation options') self.option_groups['install/uninstall options']=gr gr.add_option('--destdir',help='installation root [default: %r]'%default_destdir,default=default_destdir,dest='destdir') gr.add_option('-f','--force',dest='force',default=False,action='store_true',help='force file installation') gr.add_option('--distcheck-args',metavar='ARGS',help='arguments to pass to distcheck',default=None,action='store') def jobs(self): count=int(os.environ.get('JOBS',0)) if count<1: if'NUMBER_OF_PROCESSORS'in os.environ: count=int(os.environ.get('NUMBER_OF_PROCESSORS',1)) else: if hasattr(os,'sysconf_names'): if'SC_NPROCESSORS_ONLN'in os.sysconf_names: count=int(os.sysconf('SC_NPROCESSORS_ONLN')) elif'SC_NPROCESSORS_CONF'in os.sysconf_names: count=int(os.sysconf('SC_NPROCESSORS_CONF')) if not count and os.name not in('nt','java'): try: tmp=self.cmd_and_log(['sysctl','-n','hw.ncpu'],quiet=0) except Errors.WafError: pass else: if re.match('^[0-9]+$',tmp): count=int(tmp) if count<1: count=1 elif count>1024: count=1024 return count def add_option(self,*k,**kw): return self.parser.add_option(*k,**kw) def add_option_group(self,*k,**kw): try: gr=self.option_groups[k[0]] except KeyError: gr=self.parser.add_option_group(*k,**kw) self.option_groups[k[0]]=gr return gr def get_option_group(self,opt_str): try: return self.option_groups[opt_str] except KeyError: for group in self.parser.option_groups: if group.title==opt_str: return group return None def parse_args(self,_args=None): global options,commands,envvars (options,leftover_args)=self.parser.parse_args(args=_args) for arg in leftover_args: if'='in arg: envvars.append(arg) else: commands.append(arg) if options.destdir: options.destdir=Utils.sane_path(options.destdir) if options.verbose>=1: self.load('errcheck') colors={'yes':2,'auto':1,'no':0}[options.colors] Logs.enable_colors(colors) def execute(self): super(OptionsContext,self).execute() self.parse_args() Utils.alloc_process_pool(options.jobs) ntpsec-1.1.0+dfsg1/waflib/Runner.py0000644000175000017500000001045613210576031016714 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import random try: from queue import Queue except ImportError: from Queue import Queue from waflib import Utils,Task,Errors,Logs GAP=20 class Consumer(Utils.threading.Thread): def __init__(self,spawner,task): Utils.threading.Thread.__init__(self) self.task=task self.spawner=spawner self.setDaemon(1) self.start() def run(self): try: if not self.spawner.master.stop: self.task.process() finally: self.spawner.sem.release() self.spawner.master.out.put(self.task) self.task=None self.spawner=None class Spawner(Utils.threading.Thread): def __init__(self,master): Utils.threading.Thread.__init__(self) self.master=master self.sem=Utils.threading.Semaphore(master.numjobs) self.setDaemon(1) self.start() def run(self): try: self.loop() except Exception: pass def loop(self): master=self.master while 1: task=master.ready.get() self.sem.acquire() if not master.stop: task.log_display(task.generator.bld) Consumer(self,task) class Parallel(object): def __init__(self,bld,j=2): self.numjobs=j self.bld=bld self.outstanding=Utils.deque() self.frozen=Utils.deque() self.ready=Queue(0) self.out=Queue(0) self.count=0 self.processed=1 self.stop=False self.error=[] self.biter=None self.dirty=False self.spawner=Spawner(self) def get_next_task(self): if not self.outstanding: return None return self.outstanding.popleft() def postpone(self,tsk): if random.randint(0,1): self.frozen.appendleft(tsk) else: self.frozen.append(tsk) def refill_task_list(self): while self.count>self.numjobs*GAP: self.get_out() while not self.outstanding: if self.count: self.get_out() elif self.frozen: try: cond=self.deadlock==self.processed except AttributeError: pass else: if cond: lst=[] for tsk in self.frozen: deps=[id(x)for x in tsk.run_after if not x.hasrun] lst.append('%s\t-> %r'%(repr(tsk),deps)) if not deps: lst.append('\n task %r dependencies are done, check its *runnable_status*?'%id(tsk)) raise Errors.WafError('Deadlock detected: check the task build order%s'%''.join(lst)) self.deadlock=self.processed if self.frozen: self.outstanding.extend(self.frozen) self.frozen.clear() elif not self.count: self.outstanding.extend(next(self.biter)) self.total=self.bld.total() break def add_more_tasks(self,tsk): if getattr(tsk,'more_tasks',None): self.outstanding.extend(tsk.more_tasks) self.total+=len(tsk.more_tasks) def get_out(self): tsk=self.out.get() if not self.stop: self.add_more_tasks(tsk) self.count-=1 self.dirty=True return tsk def add_task(self,tsk): self.ready.put(tsk) def skip(self,tsk): tsk.hasrun=Task.SKIPPED def error_handler(self,tsk): if hasattr(tsk,'scan')and hasattr(tsk,'uid'): try: del self.bld.imp_sigs[tsk.uid()] except KeyError: pass if not self.bld.keep: self.stop=True self.error.append(tsk) def task_status(self,tsk): try: return tsk.runnable_status() except Exception: self.processed+=1 tsk.err_msg=Utils.ex_stack() if not self.stop and self.bld.keep: self.skip(tsk) if self.bld.keep==1: if Logs.verbose>1 or not self.error: self.error.append(tsk) self.stop=True else: if Logs.verbose>1: self.error.append(tsk) return Task.EXCEPTION tsk.hasrun=Task.EXCEPTION self.error_handler(tsk) return Task.EXCEPTION def start(self): self.total=self.bld.total() while not self.stop: self.refill_task_list() tsk=self.get_next_task() if not tsk: if self.count: continue else: break if tsk.hasrun: self.processed+=1 continue if self.stop: break st=self.task_status(tsk) if st==Task.RUN_ME: self.count+=1 self.processed+=1 if self.numjobs==1: tsk.log_display(tsk.generator.bld) try: tsk.process() finally: self.out.put(tsk) else: self.add_task(tsk) if st==Task.ASK_LATER: self.postpone(tsk) elif st==Task.SKIP_ME: self.processed+=1 self.skip(tsk) self.add_more_tasks(tsk) while self.error and self.count: self.get_out() self.ready.put(None) assert(self.count==0 or self.stop) ntpsec-1.1.0+dfsg1/waflib/Logs.py0000644000175000017500000001307313210576031016345 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re,traceback,sys from waflib import Utils,ansiterm if not os.environ.get('NOSYNC',False): if sys.stdout.isatty()and id(sys.stdout)==id(sys.__stdout__): sys.stdout=ansiterm.AnsiTerm(sys.stdout) if sys.stderr.isatty()and id(sys.stderr)==id(sys.__stderr__): sys.stderr=ansiterm.AnsiTerm(sys.stderr) import logging LOG_FORMAT=os.environ.get('WAF_LOG_FORMAT','%(asctime)s %(c1)s%(zone)s%(c2)s %(message)s') HOUR_FORMAT=os.environ.get('WAF_HOUR_FORMAT','%H:%M:%S') zones=[] verbose=0 colors_lst={'USE':True,'BOLD':'\x1b[01;1m','RED':'\x1b[01;31m','GREEN':'\x1b[32m','YELLOW':'\x1b[33m','PINK':'\x1b[35m','BLUE':'\x1b[01;34m','CYAN':'\x1b[36m','GREY':'\x1b[37m','NORMAL':'\x1b[0m','cursor_on':'\x1b[?25h','cursor_off':'\x1b[?25l',} indicator='\r\x1b[K%s%s%s' try: unicode except NameError: unicode=None def enable_colors(use): if use==1: if not(sys.stderr.isatty()or sys.stdout.isatty()): use=0 if Utils.is_win32 and os.name!='java': term=os.environ.get('TERM','') else: term=os.environ.get('TERM','dumb') if term in('dumb','emacs'): use=0 if use>=1: os.environ['TERM']='vt100' colors_lst['USE']=use try: get_term_cols=ansiterm.get_term_cols except AttributeError: def get_term_cols(): return 80 get_term_cols.__doc__=""" Returns the console width in characters. :return: the number of characters per line :rtype: int """ def get_color(cl): if colors_lst['USE']: return colors_lst.get(cl,'') return'' class color_dict(object): def __getattr__(self,a): return get_color(a) def __call__(self,a): return get_color(a) colors=color_dict() re_log=re.compile(r'(\w+): (.*)',re.M) class log_filter(logging.Filter): def __init__(self,name=''): logging.Filter.__init__(self,name) def filter(self,rec): global verbose rec.zone=rec.module if rec.levelno>=logging.INFO: return True m=re_log.match(rec.msg) if m: rec.zone=m.group(1) rec.msg=m.group(2) if zones: return getattr(rec,'zone','')in zones or'*'in zones elif not verbose>2: return False return True class log_handler(logging.StreamHandler): def emit(self,record): try: try: self.stream=record.stream except AttributeError: if record.levelno>=logging.WARNING: record.stream=self.stream=sys.stderr else: record.stream=self.stream=sys.stdout self.emit_override(record) self.flush() except(KeyboardInterrupt,SystemExit): raise except: self.handleError(record) def emit_override(self,record,**kw): self.terminator=getattr(record,'terminator','\n') stream=self.stream if unicode: msg=self.formatter.format(record) fs='%s'+self.terminator try: if(isinstance(msg,unicode)and getattr(stream,'encoding',None)): fs=fs.decode(stream.encoding) try: stream.write(fs%msg) except UnicodeEncodeError: stream.write((fs%msg).encode(stream.encoding)) else: stream.write(fs%msg) except UnicodeError: stream.write((fs%msg).encode('utf-8')) else: logging.StreamHandler.emit(self,record) class formatter(logging.Formatter): def __init__(self): logging.Formatter.__init__(self,LOG_FORMAT,HOUR_FORMAT) def format(self,rec): try: msg=rec.msg.decode('utf-8') except Exception: msg=rec.msg use=colors_lst['USE'] if(use==1 and rec.stream.isatty())or use==2: c1=getattr(rec,'c1',None) if c1 is None: c1='' if rec.levelno>=logging.ERROR: c1=colors.RED elif rec.levelno>=logging.WARNING: c1=colors.YELLOW elif rec.levelno>=logging.INFO: c1=colors.GREEN c2=getattr(rec,'c2',colors.NORMAL) msg='%s%s%s'%(c1,msg,c2) else: msg=re.sub(r'\r(?!\n)|\x1B\[(K|.*?(m|h|l))','',msg) if rec.levelno>=logging.INFO: if rec.args: return msg%rec.args return msg rec.msg=msg rec.c1=colors.PINK rec.c2=colors.NORMAL return logging.Formatter.format(self,rec) log=None def debug(*k,**kw): global verbose if verbose: k=list(k) k[0]=k[0].replace('\n',' ') global log log.debug(*k,**kw) def error(*k,**kw): global log,verbose log.error(*k,**kw) if verbose>2: st=traceback.extract_stack() if st: st=st[:-1] buf=[] for filename,lineno,name,line in st: buf.append(' File %r, line %d, in %s'%(filename,lineno,name)) if line: buf.append(' %s'%line.strip()) if buf: log.error('\n'.join(buf)) def warn(*k,**kw): global log log.warn(*k,**kw) def info(*k,**kw): global log log.info(*k,**kw) def init_log(): global log log=logging.getLogger('waflib') log.handlers=[] log.filters=[] hdlr=log_handler() hdlr.setFormatter(formatter()) log.addHandler(hdlr) log.addFilter(log_filter()) log.setLevel(logging.DEBUG) def make_logger(path,name): logger=logging.getLogger(name) if sys.hexversion>0x3000000: encoding=sys.stdout.encoding else: encoding=None hdlr=logging.FileHandler(path,'w',encoding=encoding) formatter=logging.Formatter('%(message)s') hdlr.setFormatter(formatter) logger.addHandler(hdlr) logger.setLevel(logging.DEBUG) return logger def make_mem_logger(name,to_log,size=8192): from logging.handlers import MemoryHandler logger=logging.getLogger(name) hdlr=MemoryHandler(size,target=to_log) formatter=logging.Formatter('%(message)s') hdlr.setFormatter(formatter) logger.addHandler(hdlr) logger.memhandler=hdlr logger.setLevel(logging.DEBUG) return logger def free_logger(logger): try: for x in logger.handlers: x.close() logger.removeHandler(x) except Exception: pass def pprint(col,msg,label='',sep='\n'): global info info('%s%s%s %s',colors(col),msg,colors.NORMAL,label,extra={'terminator':sep}) ntpsec-1.1.0+dfsg1/waflib/Task.py0000644000175000017500000004556313210576031016354 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re,sys,tempfile from waflib import Utils,Logs,Errors NOT_RUN=0 MISSING=1 CRASHED=2 EXCEPTION=3 SKIPPED=8 SUCCESS=9 ASK_LATER=-1 SKIP_ME=-2 RUN_ME=-3 COMPILE_TEMPLATE_SHELL=''' def f(tsk): env = tsk.env gen = tsk.generator bld = gen.bld cwdx = tsk.get_cwd() p = env.get_flat tsk.last_cmd = cmd = \'\'\' %s \'\'\' % s return tsk.exec_command(cmd, cwd=cwdx, env=env.env or None) ''' COMPILE_TEMPLATE_NOSHELL=''' def f(tsk): env = tsk.env gen = tsk.generator bld = gen.bld cwdx = tsk.get_cwd() def to_list(xx): if isinstance(xx, str): return [xx] return xx def merge(lst1, lst2): if lst1 and lst2: return lst1[:-1] + [lst1[-1] + lst2[0]] + lst2[1:] return lst1 + lst2 lst = [] %s if '' in lst: lst = [x for x in lst if x] tsk.last_cmd = lst return tsk.exec_command(lst, cwd=cwdx, env=env.env or None) ''' classes={} class store_task_type(type): def __init__(cls,name,bases,dict): super(store_task_type,cls).__init__(name,bases,dict) name=cls.__name__ if name!='evil'and name!='TaskBase': global classes if getattr(cls,'run_str',None): (f,dvars)=compile_fun(cls.run_str,cls.shell) cls.hcode=Utils.h_cmd(cls.run_str) cls.orig_run_str=cls.run_str cls.run_str=None cls.run=f cls.vars=list(set(cls.vars+dvars)) cls.vars.sort() elif getattr(cls,'run',None)and not'hcode'in cls.__dict__: cls.hcode=Utils.h_cmd(cls.run) getattr(cls,'register',classes)[name]=cls evil=store_task_type('evil',(object,),{}) class TaskBase(evil): color='GREEN' ext_in=[] ext_out=[] before=[] after=[] hcode='' keep_last_cmd=False __slots__=('hasrun','generator') def __init__(self,*k,**kw): self.hasrun=NOT_RUN try: self.generator=kw['generator'] except KeyError: self.generator=self def __repr__(self): return'\n\t{task %r: %s %s}'%(self.__class__.__name__,id(self),str(getattr(self,'fun',''))) def __str__(self): if hasattr(self,'fun'): return self.fun.__name__ return self.__class__.__name__ def keyword(self): if hasattr(self,'fun'): return'Function' return'Processing' def get_cwd(self): bld=self.generator.bld ret=getattr(self,'cwd',None)or getattr(bld,'cwd',bld.bldnode) if isinstance(ret,str): if os.path.isabs(ret): ret=bld.root.make_node(ret) else: ret=self.generator.path.make_node(ret) return ret def quote_flag(self,x): old=x if'\\'in x: x=x.replace('\\','\\\\') if'"'in x: x=x.replace('"','\\"') if old!=x or' 'in x or'\t'in x or"'"in x: x='"%s"'%x return x def split_argfile(self,cmd): return([cmd[0]],[self.quote_flag(x)for x in cmd[1:]]) def exec_command(self,cmd,**kw): if not'cwd'in kw: kw['cwd']=self.get_cwd() if hasattr(self,'timeout'): kw['timeout']=self.timeout if self.env.PATH: env=kw['env']=dict(kw.get('env')or self.env.env or os.environ) env['PATH']=self.env.PATH if isinstance(self.env.PATH,str)else os.pathsep.join(self.env.PATH) if not isinstance(cmd,str)and(len(repr(cmd))>=8192 if Utils.is_win32 else len(cmd)>200000): cmd,args=self.split_argfile(cmd) try: (fd,tmp)=tempfile.mkstemp() os.write(fd,'\r\n'.join(args).encode()) os.close(fd) if Logs.verbose: Logs.debug('argfile: @%r -> %r',tmp,args) return self.generator.bld.exec_command(cmd+['@'+tmp],**kw) finally: try: os.remove(tmp) except OSError: pass else: return self.generator.bld.exec_command(cmd,**kw) def runnable_status(self): return RUN_ME def uid(self): return Utils.SIG_NIL def process(self): m=self.generator.bld.producer try: del self.generator.bld.task_sigs[self.uid()] except KeyError: pass try: ret=self.run() except Exception: self.err_msg=Utils.ex_stack() self.hasrun=EXCEPTION m.error_handler(self) return if ret: self.err_code=ret self.hasrun=CRASHED else: try: self.post_run() except Errors.WafError: pass except Exception: self.err_msg=Utils.ex_stack() self.hasrun=EXCEPTION else: self.hasrun=SUCCESS if self.hasrun!=SUCCESS: m.error_handler(self) def run(self): if hasattr(self,'fun'): return self.fun(self) return 0 def post_run(self): pass def log_display(self,bld): if self.generator.bld.progress_bar==3: return s=self.display() if s: if bld.logger: logger=bld.logger else: logger=Logs if self.generator.bld.progress_bar==1: c1=Logs.colors.cursor_off c2=Logs.colors.cursor_on logger.info(s,extra={'stream':sys.stderr,'terminator':'','c1':c1,'c2':c2}) else: logger.info(s,extra={'terminator':'','c1':'','c2':''}) def display(self): col1=Logs.colors(self.color) col2=Logs.colors.NORMAL master=self.generator.bld.producer def cur(): tmp=-1 if hasattr(master,'ready'): tmp-=master.ready.qsize() return master.processed+tmp if self.generator.bld.progress_bar==1: return self.generator.bld.progress_line(cur(),master.total,col1,col2) if self.generator.bld.progress_bar==2: ela=str(self.generator.bld.timer) try: ins=','.join([n.name for n in self.inputs]) except AttributeError: ins='' try: outs=','.join([n.name for n in self.outputs]) except AttributeError: outs='' return'|Total %s|Current %s|Inputs %s|Outputs %s|Time %s|\n'%(master.total,cur(),ins,outs,ela) s=str(self) if not s: return None total=master.total n=len(str(total)) fs='[%%%dd/%%%dd] %%s%%s%%s%%s\n'%(n,n) kw=self.keyword() if kw: kw+=' ' return fs%(cur(),total,kw,col1,s,col2) def hash_constraints(self): cls=self.__class__ tup=(str(cls.before),str(cls.after),str(cls.ext_in),str(cls.ext_out),cls.__name__,cls.hcode) return hash(tup) def format_error(self): if Logs.verbose: msg=': %r\n%r'%(self,getattr(self,'last_cmd','')) else: msg=' (run with -v to display more information)' name=getattr(self.generator,'name','') if getattr(self,"err_msg",None): return self.err_msg elif not self.hasrun: return'task in %r was not executed for some reason: %r'%(name,self) elif self.hasrun==CRASHED: try: return' -> task in %r failed with exit status %r%s'%(name,self.err_code,msg) except AttributeError: return' -> task in %r failed%s'%(name,msg) elif self.hasrun==MISSING: return' -> missing files in %r%s'%(name,msg) else: return'invalid status for task in %r: %r'%(name,self.hasrun) def colon(self,var1,var2): tmp=self.env[var1] if not tmp: return[] if isinstance(var2,str): it=self.env[var2] else: it=var2 if isinstance(tmp,str): return[tmp%x for x in it] else: lst=[] for y in it: lst.extend(tmp) lst.append(y) return lst class Task(TaskBase): vars=[] always_run=False shell=False def __init__(self,*k,**kw): TaskBase.__init__(self,*k,**kw) self.env=kw['env'] self.inputs=[] self.outputs=[] self.dep_nodes=[] self.run_after=set() def __str__(self): name=self.__class__.__name__ if self.outputs: if name.endswith(('lib','program'))or not self.inputs: node=self.outputs[0] return node.path_from(node.ctx.launch_node()) if not(self.inputs or self.outputs): return self.__class__.__name__ if len(self.inputs)==1: node=self.inputs[0] return node.path_from(node.ctx.launch_node()) src_str=' '.join([a.path_from(a.ctx.launch_node())for a in self.inputs]) tgt_str=' '.join([a.path_from(a.ctx.launch_node())for a in self.outputs]) if self.outputs: sep=' -> ' else: sep='' return'%s: %s%s%s'%(self.__class__.__name__,src_str,sep,tgt_str) def keyword(self): name=self.__class__.__name__ if name.endswith(('lib','program')): return'Linking' if len(self.inputs)==1 and len(self.outputs)==1: return'Compiling' if not self.inputs: if self.outputs: return'Creating' else: return'Running' return'Processing' def __repr__(self): try: ins=",".join([x.name for x in self.inputs]) outs=",".join([x.name for x in self.outputs]) except AttributeError: ins=",".join([str(x)for x in self.inputs]) outs=",".join([str(x)for x in self.outputs]) return"".join(['\n\t{task %r: '%id(self),self.__class__.__name__," ",ins," -> ",outs,'}']) def uid(self): try: return self.uid_ except AttributeError: m=Utils.md5(self.__class__.__name__) up=m.update for x in self.inputs+self.outputs: up(x.abspath()) self.uid_=m.digest() return self.uid_ def set_inputs(self,inp): if isinstance(inp,list): self.inputs+=inp else: self.inputs.append(inp) def set_outputs(self,out): if isinstance(out,list): self.outputs+=out else: self.outputs.append(out) def set_run_after(self,task): assert isinstance(task,TaskBase) self.run_after.add(task) def signature(self): try: return self.cache_sig except AttributeError: pass self.m=Utils.md5(self.hcode) self.sig_explicit_deps() self.sig_vars() if self.scan: try: self.sig_implicit_deps() except Errors.TaskRescan: return self.signature() ret=self.cache_sig=self.m.digest() return ret def runnable_status(self): for t in self.run_after: if not t.hasrun: return ASK_LATER try: new_sig=self.signature() except Errors.TaskNotReady: return ASK_LATER bld=self.generator.bld key=self.uid() try: prev_sig=bld.task_sigs[key] except KeyError: Logs.debug('task: task %r must run: it was never run before or the task code changed',self) return RUN_ME if new_sig!=prev_sig: Logs.debug('task: task %r must run: the task signature changed',self) return RUN_ME for node in self.outputs: sig=bld.node_sigs.get(node) if not sig: Logs.debug('task: task %r must run: an output node has no signature',self) return RUN_ME if sig!=key: Logs.debug('task: task %r must run: an output node was produced by another task',self) return RUN_ME if not node.exists(): Logs.debug('task: task %r must run: an output node does not exist',self) return RUN_ME return(self.always_run and RUN_ME)or SKIP_ME def post_run(self): bld=self.generator.bld for node in self.outputs: if not node.exists(): self.hasrun=MISSING self.err_msg='-> missing file: %r'%node.abspath() raise Errors.WafError(self.err_msg) bld.node_sigs[node]=self.uid() bld.task_sigs[self.uid()]=self.signature() if not self.keep_last_cmd: try: del self.last_cmd except AttributeError: pass def sig_explicit_deps(self): bld=self.generator.bld upd=self.m.update for x in self.inputs+self.dep_nodes: upd(x.get_bld_sig()) if bld.deps_man: additional_deps=bld.deps_man for x in self.inputs+self.outputs: try: d=additional_deps[x] except KeyError: continue for v in d: try: v=v.get_bld_sig() except AttributeError: if hasattr(v,'__call__'): v=v() upd(v) def sig_vars(self): sig=self.generator.bld.hash_env_vars(self.env,self.vars) self.m.update(sig) scan=None def sig_implicit_deps(self): bld=self.generator.bld key=self.uid() prev=bld.imp_sigs.get(key,[]) if prev: try: if prev==self.compute_sig_implicit_deps(): return prev except Errors.TaskNotReady: raise except EnvironmentError: for x in bld.node_deps.get(self.uid(),[]): if not x.is_bld()and not x.exists(): try: del x.parent.children[x.name] except KeyError: pass del bld.imp_sigs[key] raise Errors.TaskRescan('rescan') (bld.node_deps[key],bld.raw_deps[key])=self.scan() if Logs.verbose: Logs.debug('deps: scanner for %s: %r; unresolved: %r',self,bld.node_deps[key],bld.raw_deps[key]) try: bld.imp_sigs[key]=self.compute_sig_implicit_deps() except EnvironmentError: for k in bld.node_deps.get(self.uid(),[]): if not k.exists(): Logs.warn('Dependency %r for %r is missing: check the task declaration and the build order!',k,self) raise def compute_sig_implicit_deps(self): upd=self.m.update self.are_implicit_nodes_ready() for k in self.generator.bld.node_deps.get(self.uid(),[]): upd(k.get_bld_sig()) return self.m.digest() def are_implicit_nodes_ready(self): bld=self.generator.bld try: cache=bld.dct_implicit_nodes except AttributeError: bld.dct_implicit_nodes=cache={} try: dct=cache[bld.current_group] except KeyError: dct=cache[bld.current_group]={} for tsk in bld.cur_tasks: for x in tsk.outputs: dct[x]=tsk modified=False for x in bld.node_deps.get(self.uid(),[]): if x in dct: self.run_after.add(dct[x]) modified=True if modified: for tsk in self.run_after: if not tsk.hasrun: raise Errors.TaskNotReady('not ready') if sys.hexversion>0x3000000: def uid(self): try: return self.uid_ except AttributeError: m=Utils.md5(self.__class__.__name__.encode('iso8859-1','xmlcharrefreplace')) up=m.update for x in self.inputs+self.outputs: up(x.abspath().encode('iso8859-1','xmlcharrefreplace')) self.uid_=m.digest() return self.uid_ uid.__doc__=Task.uid.__doc__ Task.uid=uid def is_before(t1,t2): to_list=Utils.to_list for k in to_list(t2.ext_in): if k in to_list(t1.ext_out): return 1 if t1.__class__.__name__ in to_list(t2.after): return 1 if t2.__class__.__name__ in to_list(t1.before): return 1 return 0 def set_file_constraints(tasks): ins=Utils.defaultdict(set) outs=Utils.defaultdict(set) for x in tasks: for a in getattr(x,'inputs',[])+getattr(x,'dep_nodes',[]): ins[id(a)].add(x) for a in getattr(x,'outputs',[]): outs[id(a)].add(x) links=set(ins.keys()).intersection(outs.keys()) for k in links: for a in ins[k]: a.run_after.update(outs[k]) def set_precedence_constraints(tasks): cstr_groups=Utils.defaultdict(list) for x in tasks: h=x.hash_constraints() cstr_groups[h].append(x) keys=list(cstr_groups.keys()) maxi=len(keys) for i in range(maxi): t1=cstr_groups[keys[i]][0] for j in range(i+1,maxi): t2=cstr_groups[keys[j]][0] if is_before(t1,t2): a=i b=j elif is_before(t2,t1): a=j b=i else: continue aval=set(cstr_groups[keys[a]]) for x in cstr_groups[keys[b]]: x.run_after.update(aval) def funex(c): dc={} exec(c,dc) return dc['f'] re_cond=re.compile('(?P\w+)|(?P\|)|(?P&)') re_novar=re.compile(r'^(SRC|TGT)\W+.*?$') reg_act=re.compile(r'(?P\\)|(?P\$\$)|(?P\$\{(?P\w+)(?P.*?)\})',re.M) def compile_fun_shell(line): extr=[] def repl(match): g=match.group if g('dollar'): return"$" elif g('backslash'): return'\\\\' elif g('subst'): extr.append((g('var'),g('code'))) return"%s" return None line=reg_act.sub(repl,line)or line dvars=[] def replc(m): if m.group('and'): return' and ' elif m.group('or'): return' or ' else: x=m.group('var') if x not in dvars: dvars.append(x) return'env[%r]'%x parm=[] app=parm.append for(var,meth)in extr: if var=='SRC': if meth: app('tsk.inputs%s'%meth) else: app('" ".join([a.path_from(cwdx) for a in tsk.inputs])') elif var=='TGT': if meth: app('tsk.outputs%s'%meth) else: app('" ".join([a.path_from(cwdx) for a in tsk.outputs])') elif meth: if meth.startswith(':'): if var not in dvars: dvars.append(var) m=meth[1:] if m=='SRC': m='[a.path_from(cwdx) for a in tsk.inputs]' elif m=='TGT': m='[a.path_from(cwdx) for a in tsk.outputs]' elif re_novar.match(m): m='[tsk.inputs%s]'%m[3:] elif re_novar.match(m): m='[tsk.outputs%s]'%m[3:] elif m[:3]not in('tsk','gen','bld'): dvars.append(meth[1:]) m='%r'%m app('" ".join(tsk.colon(%r, %s))'%(var,m)) elif meth.startswith('?'): expr=re_cond.sub(replc,meth[1:]) app('p(%r) if (%s) else ""'%(var,expr)) else: app('%s%s'%(var,meth)) else: if var not in dvars: dvars.append(var) app("p('%s')"%var) if parm: parm="%% (%s) "%(',\n\t\t'.join(parm)) else: parm='' c=COMPILE_TEMPLATE_SHELL%(line,parm) Logs.debug('action: %s',c.strip().splitlines()) return(funex(c),dvars) reg_act_noshell=re.compile(r"(?P\s+)|(?P\$\{(?P\w+)(?P.*?)\})|(?P([^$ \t\n\r\f\v]|\$\$)+)",re.M) def compile_fun_noshell(line): buf=[] dvars=[] merge=False app=buf.append def replc(m): if m.group('and'): return' and ' elif m.group('or'): return' or ' else: x=m.group('var') if x not in dvars: dvars.append(x) return'env[%r]'%x for m in reg_act_noshell.finditer(line): if m.group('space'): merge=False continue elif m.group('text'): app('[%r]'%m.group('text').replace('$$','$')) elif m.group('subst'): var=m.group('var') code=m.group('code') if var=='SRC': if code: app('[tsk.inputs%s]'%code) else: app('[a.path_from(cwdx) for a in tsk.inputs]') elif var=='TGT': if code: app('[tsk.outputs%s]'%code) else: app('[a.path_from(cwdx) for a in tsk.outputs]') elif code: if code.startswith(':'): if not var in dvars: dvars.append(var) m=code[1:] if m=='SRC': m='[a.path_from(cwdx) for a in tsk.inputs]' elif m=='TGT': m='[a.path_from(cwdx) for a in tsk.outputs]' elif re_novar.match(m): m='[tsk.inputs%s]'%m[3:] elif re_novar.match(m): m='[tsk.outputs%s]'%m[3:] elif m[:3]not in('tsk','gen','bld'): dvars.append(m) m='%r'%m app('tsk.colon(%r, %s)'%(var,m)) elif code.startswith('?'): expr=re_cond.sub(replc,code[1:]) app('to_list(env[%r] if (%s) else [])'%(var,expr)) else: app('gen.to_list(%s%s)'%(var,code)) else: app('to_list(env[%r])'%var) if not var in dvars: dvars.append(var) if merge: tmp='merge(%s, %s)'%(buf[-2],buf[-1]) del buf[-1] buf[-1]=tmp merge=True buf=['lst.extend(%s)'%x for x in buf] fun=COMPILE_TEMPLATE_NOSHELL%"\n\t".join(buf) Logs.debug('action: %s',fun.strip().splitlines()) return(funex(fun),dvars) def compile_fun(line,shell=False): if isinstance(line,str): if line.find('<')>0 or line.find('>')>0 or line.find('&&')>0: shell=True else: dvars_lst=[] funs_lst=[] for x in line: if isinstance(x,str): fun,dvars=compile_fun(x,shell) dvars_lst+=dvars funs_lst.append(fun) else: funs_lst.append(x) def composed_fun(task): for x in funs_lst: ret=x(task) if ret: return ret return None return composed_fun,dvars_lst if shell: return compile_fun_shell(line) else: return compile_fun_noshell(line) def task_factory(name,func=None,vars=None,color='GREEN',ext_in=[],ext_out=[],before=[],after=[],shell=False,scan=None): params={'vars':vars or[],'color':color,'name':name,'shell':shell,'scan':scan,} if isinstance(func,str)or isinstance(func,tuple): params['run_str']=func else: params['run']=func cls=type(Task)(name,(Task,),params) global classes classes[name]=cls if ext_in: cls.ext_in=Utils.to_list(ext_in) if ext_out: cls.ext_out=Utils.to_list(ext_out) if before: cls.before=Utils.to_list(before) if after: cls.after=Utils.to_list(after) return cls def always_run(cls): Logs.warn('This decorator is deprecated, set always_run on the task class instead!') cls.always_run=True return cls def update_outputs(cls): return cls ntpsec-1.1.0+dfsg1/waflib/fixpy2.py0000644000175000017500000000241613210576007016664 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os all_modifs={} def fixdir(dir): global all_modifs for k in all_modifs: for v in all_modifs[k]: modif(os.path.join(dir,'waflib'),k,v) def modif(dir,name,fun): if name=='*': lst=[] for y in'. Tools extras'.split(): for x in os.listdir(os.path.join(dir,y)): if x.endswith('.py'): lst.append(y+os.sep+x) for x in lst: modif(dir,x,fun) return filename=os.path.join(dir,name) f=open(filename,'r') try: txt=f.read() finally: f.close() txt=fun(txt) f=open(filename,'w') try: f.write(txt) finally: f.close() def subst(*k): def do_subst(fun): global all_modifs for x in k: try: all_modifs[x].append(fun) except KeyError: all_modifs[x]=[fun] return fun return do_subst @subst('*') def r1(code): code=code.replace('as e:',',e:') code=code.replace(".decode(sys.stdout.encoding or'iso8859-1',errors='replace')",'') return code.replace('.encode()','') @subst('Runner.py') def r4(code): return code.replace('next(self.biter)','self.biter.next()') @subst('Context.py') def r5(code): return code.replace("('Execution failure: %s'%str(e),ex=e)","('Execution failure: %s'%str(e),ex=e),None,sys.exc_info()[2]") ntpsec-1.1.0+dfsg1/waflib/__init__.py0000644000175000017500000000017113210576007017176 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file ntpsec-1.1.0+dfsg1/waflib/Errors.py0000644000175000017500000000177013210576031016716 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import traceback,sys class WafError(Exception): def __init__(self,msg='',ex=None): Exception.__init__(self) self.msg=msg assert not isinstance(msg,Exception) self.stack=[] if ex: if not msg: self.msg=str(ex) if isinstance(ex,WafError): self.stack=ex.stack else: self.stack=traceback.extract_tb(sys.exc_info()[2]) self.stack+=traceback.extract_stack()[:-1] self.verbose_msg=''.join(traceback.format_list(self.stack)) def __str__(self): return str(self.msg) class BuildError(WafError): def __init__(self,error_tasks=[]): self.tasks=error_tasks WafError.__init__(self,self.format_error()) def format_error(self): lst=['Build failed'] for tsk in self.tasks: txt=tsk.format_error() if txt: lst.append(txt) return'\n'.join(lst) class ConfigurationError(WafError): pass class TaskRescan(WafError): pass class TaskNotReady(WafError): pass ntpsec-1.1.0+dfsg1/waflib/Build.py0000644000175000017500000005427013210576031016504 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,sys,errno,re,shutil,stat try: import cPickle except ImportError: import pickle as cPickle from waflib import Node,Runner,TaskGen,Utils,ConfigSet,Task,Logs,Options,Context,Errors CACHE_DIR='c4che' CACHE_SUFFIX='_cache.py' INSTALL=1337 UNINSTALL=-1337 SAVED_ATTRS='root node_sigs task_sigs imp_sigs raw_deps node_deps'.split() CFG_FILES='cfg_files' POST_AT_ONCE=0 POST_LAZY=1 PROTOCOL=-1 if sys.platform=='cli': PROTOCOL=0 class BuildContext(Context.Context): '''executes the build''' cmd='build' variant='' def __init__(self,**kw): super(BuildContext,self).__init__(**kw) self.is_install=0 self.top_dir=kw.get('top_dir',Context.top_dir) self.out_dir=kw.get('out_dir',Context.out_dir) self.run_dir=kw.get('run_dir',Context.run_dir) self.launch_dir=Context.launch_dir self.post_mode=POST_LAZY self.cache_dir=kw.get('cache_dir') if not self.cache_dir: self.cache_dir=os.path.join(self.out_dir,CACHE_DIR) self.all_envs={} self.node_sigs={} self.task_sigs={} self.imp_sigs={} self.node_deps={} self.raw_deps={} self.task_gen_cache_names={} self.jobs=Options.options.jobs self.targets=Options.options.targets self.keep=Options.options.keep self.progress_bar=Options.options.progress_bar self.deps_man=Utils.defaultdict(list) self.current_group=0 self.groups=[] self.group_names={} for v in SAVED_ATTRS: if not hasattr(self,v): setattr(self,v,{}) def set_cur(self,cur): self.current_group=cur def get_cur(self): return self.current_group cur=property(get_cur,set_cur) def get_variant_dir(self): if not self.variant: return self.out_dir return os.path.join(self.out_dir,self.variant) variant_dir=property(get_variant_dir,None) def __call__(self,*k,**kw): kw['bld']=self ret=TaskGen.task_gen(*k,**kw) self.task_gen_cache_names={} self.add_to_group(ret,group=kw.get('group')) return ret def rule(self,*k,**kw): def f(rule): ret=self(*k,**kw) ret.rule=rule return ret return f def __copy__(self): raise Errors.WafError('build contexts cannot be copied') def load_envs(self): node=self.root.find_node(self.cache_dir) if not node: raise Errors.WafError('The project was not configured: run "waf configure" first!') lst=node.ant_glob('**/*%s'%CACHE_SUFFIX,quiet=True) if not lst: raise Errors.WafError('The cache directory is empty: reconfigure the project') for x in lst: name=x.path_from(node).replace(CACHE_SUFFIX,'').replace('\\','/') env=ConfigSet.ConfigSet(x.abspath()) self.all_envs[name]=env for f in env[CFG_FILES]: newnode=self.root.find_resource(f) if not newnode or not newnode.exists(): raise Errors.WafError('Missing configuration file %r, reconfigure the project!'%f) def init_dirs(self): if not(os.path.isabs(self.top_dir)and os.path.isabs(self.out_dir)): raise Errors.WafError('The project was not configured: run "waf configure" first!') self.path=self.srcnode=self.root.find_dir(self.top_dir) self.bldnode=self.root.make_node(self.variant_dir) self.bldnode.mkdir() def execute(self): self.restore() if not self.all_envs: self.load_envs() self.execute_build() def execute_build(self): Logs.info("Waf: Entering directory `%s'",self.variant_dir) self.recurse([self.run_dir]) self.pre_build() self.timer=Utils.Timer() try: self.compile() finally: if self.progress_bar==1 and sys.stderr.isatty(): c=self.producer.processed or 1 m=self.progress_line(c,c,Logs.colors.BLUE,Logs.colors.NORMAL) Logs.info(m,extra={'stream':sys.stderr,'c1':Logs.colors.cursor_off,'c2':Logs.colors.cursor_on}) Logs.info("Waf: Leaving directory `%s'",self.variant_dir) try: self.producer.bld=None del self.producer except AttributeError: pass self.post_build() def restore(self): try: env=ConfigSet.ConfigSet(os.path.join(self.cache_dir,'build.config.py')) except EnvironmentError: pass else: if env.version').ljust(cols) msg=Logs.indicator%(left,bar,right) return msg def declare_chain(self,*k,**kw): return TaskGen.declare_chain(*k,**kw) def pre_build(self): for m in getattr(self,'pre_funs',[]): m(self) def post_build(self): for m in getattr(self,'post_funs',[]): m(self) def add_pre_fun(self,meth): try: self.pre_funs.append(meth) except AttributeError: self.pre_funs=[meth] def add_post_fun(self,meth): try: self.post_funs.append(meth) except AttributeError: self.post_funs=[meth] def get_group(self,x): if not self.groups: self.add_group() if x is None: return self.groups[self.current_group] if x in self.group_names: return self.group_names[x] return self.groups[x] def add_to_group(self,tgen,group=None): assert(isinstance(tgen,TaskGen.task_gen)or isinstance(tgen,Task.TaskBase)) tgen.bld=self self.get_group(group).append(tgen) def get_group_name(self,g): if not isinstance(g,list): g=self.groups[g] for x in self.group_names: if id(self.group_names[x])==id(g): return x return'' def get_group_idx(self,tg): se=id(tg) for i,tmp in enumerate(self.groups): for t in tmp: if id(t)==se: return i return None def add_group(self,name=None,move=True): if name and name in self.group_names: raise Errors.WafError('add_group: name %s already present',name) g=[] self.group_names[name]=g self.groups.append(g) if move: self.current_group=len(self.groups)-1 def set_group(self,idx): if isinstance(idx,str): g=self.group_names[idx] for i,tmp in enumerate(self.groups): if id(g)==id(tmp): self.current_group=i break else: self.current_group=idx def total(self): total=0 for group in self.groups: for tg in group: try: total+=len(tg.tasks) except AttributeError: total+=1 return total def get_targets(self): to_post=[] min_grp=0 for name in self.targets.split(','): tg=self.get_tgen_by_name(name) m=self.get_group_idx(tg) if m>min_grp: min_grp=m to_post=[tg] elif m==min_grp: to_post.append(tg) return(min_grp,to_post) def get_all_task_gen(self): lst=[] for g in self.groups: lst.extend(g) return lst def post_group(self): if self.targets=='*': for tg in self.groups[self.current_group]: try: f=tg.post except AttributeError: pass else: f() elif self.targets: if self.current_group259 and not tgt.startswith('\\\\?\\'): tgt='\\\\?\\'+tgt shutil.copy2(src,tgt) self.fix_perms(tgt) def rm_empty_dirs(self,tgt): while tgt: tgt=os.path.dirname(tgt) try: os.rmdir(tgt) except OSError: break def run(self): is_install=self.generator.bld.is_install if not is_install: return for x in self.outputs: if is_install==INSTALL: x.parent.mkdir() if self.type=='symlink_as': fun=is_install==INSTALL and self.do_link or self.do_unlink fun(self.link,self.outputs[0].abspath()) else: fun=is_install==INSTALL and self.do_install or self.do_uninstall launch_node=self.generator.bld.launch_node() for x,y in zip(self.inputs,self.outputs): fun(x.abspath(),y.abspath(),x.path_from(launch_node)) def run_now(self): status=self.runnable_status() if status not in(Task.RUN_ME,Task.SKIP_ME): raise Errors.TaskNotReady('Could not process %r: status %r'%(self,status)) self.run() self.hasrun=Task.SUCCESS def do_install(self,src,tgt,lbl,**kw): if not Options.options.force: try: st1=os.stat(tgt) st2=os.stat(src) except OSError: pass else: if st1.st_mtime+2>=st2.st_mtime and st1.st_size==st2.st_size: if not self.generator.bld.progress_bar: Logs.info('- install %s (from %s)',tgt,lbl) return False if not self.generator.bld.progress_bar: Logs.info('+ install %s (from %s)',tgt,lbl) try: os.chmod(tgt,Utils.O644|stat.S_IMODE(os.stat(tgt).st_mode)) except EnvironmentError: pass try: os.remove(tgt) except OSError: pass try: self.copy_fun(src,tgt) except EnvironmentError as e: if not os.path.exists(src): Logs.error('File %r does not exist',src) elif not os.path.isfile(src): Logs.error('Input %r is not a file',src) raise Errors.WafError('Could not install the file %r'%tgt,e) def fix_perms(self,tgt): if not Utils.is_win32: user=getattr(self,'install_user',None)or getattr(self.generator,'install_user',None) group=getattr(self,'install_group',None)or getattr(self.generator,'install_group',None) if user or group: Utils.lchown(tgt,user or-1,group or-1) if not os.path.islink(tgt): os.chmod(tgt,self.chmod) def do_link(self,src,tgt,**kw): if os.path.islink(tgt)and os.readlink(tgt)==src: if not self.generator.bld.progress_bar: Logs.info('- symlink %s (to %s)',tgt,src) else: try: os.remove(tgt) except OSError: pass if not self.generator.bld.progress_bar: Logs.info('+ symlink %s (to %s)',tgt,src) os.symlink(src,tgt) self.fix_perms(tgt) def do_uninstall(self,src,tgt,lbl,**kw): if not self.generator.bld.progress_bar: Logs.info('- remove %s',tgt) try: os.remove(tgt) except OSError as e: if e.errno!=errno.ENOENT: if not getattr(self,'uninstall_error',None): self.uninstall_error=True Logs.warn('build: some files could not be uninstalled (retry with -vv to list them)') if Logs.verbose>1: Logs.warn('Could not remove %s (error code %r)',e.filename,e.errno) self.rm_empty_dirs(tgt) def do_unlink(self,src,tgt,**kw): try: if not self.generator.bld.progress_bar: Logs.info('- remove %s',tgt) os.remove(tgt) except OSError: pass self.rm_empty_dirs(tgt) class InstallContext(BuildContext): '''installs the targets on the system''' cmd='install' def __init__(self,**kw): super(InstallContext,self).__init__(**kw) self.is_install=INSTALL class UninstallContext(InstallContext): '''removes the targets installed''' cmd='uninstall' def __init__(self,**kw): super(UninstallContext,self).__init__(**kw) self.is_install=UNINSTALL def execute(self): try: def runnable_status(self): return Task.SKIP_ME setattr(Task.Task,'runnable_status_back',Task.Task.runnable_status) setattr(Task.Task,'runnable_status',runnable_status) super(UninstallContext,self).execute() finally: setattr(Task.Task,'runnable_status',Task.Task.runnable_status_back) class CleanContext(BuildContext): '''cleans the project''' cmd='clean' def execute(self): self.restore() if not self.all_envs: self.load_envs() self.recurse([self.run_dir]) try: self.clean() finally: self.store() def clean(self): Logs.debug('build: clean called') if self.bldnode!=self.srcnode: lst=[] for env in self.all_envs.values(): lst.extend(self.root.find_or_declare(f)for f in env[CFG_FILES]) for n in self.bldnode.ant_glob('**/*',excl='.lock* *conf_check_*/** config.log c4che/*',quiet=True): if n in lst: continue n.delete() self.root.children={} for v in SAVED_ATTRS: if v=='root': continue setattr(self,v,{}) class ListContext(BuildContext): '''lists the targets to execute''' cmd='list' def execute(self): self.restore() if not self.all_envs: self.load_envs() self.recurse([self.run_dir]) self.pre_build() self.timer=Utils.Timer() for g in self.groups: for tg in g: try: f=tg.post except AttributeError: pass else: f() try: self.get_tgen_by_name('') except Errors.WafError: pass targets=sorted(self.task_gen_cache_names) line_just=max(len(t)for t in targets)if targets else 0 for target in targets: tgen=self.task_gen_cache_names[target] descript=getattr(tgen,'description','') if descript: target=target.ljust(line_just) descript=': %s'%descript Logs.pprint('GREEN',target,label=descript) class StepContext(BuildContext): '''executes tasks in a step-by-step fashion, for debugging''' cmd='step' def __init__(self,**kw): super(StepContext,self).__init__(**kw) self.files=Options.options.files def compile(self): if not self.files: Logs.warn('Add a pattern for the debug build, for example "waf step --files=main.c,app"') BuildContext.compile(self) return targets=[] if self.targets and self.targets!='*': targets=self.targets.split(',') for g in self.groups: for tg in g: if targets and tg.name not in targets: continue try: f=tg.post except AttributeError: pass else: f() for pat in self.files.split(','): matcher=self.get_matcher(pat) for tg in g: if isinstance(tg,Task.TaskBase): lst=[tg] else: lst=tg.tasks for tsk in lst: do_exec=False for node in getattr(tsk,'inputs',[]): if matcher(node,output=False): do_exec=True break for node in getattr(tsk,'outputs',[]): if matcher(node,output=True): do_exec=True break if do_exec: ret=tsk.run() Logs.info('%s -> exit %r',tsk,ret) def get_matcher(self,pat): inn=True out=True if pat.startswith('in:'): out=False pat=pat.replace('in:','') elif pat.startswith('out:'): inn=False pat=pat.replace('out:','') anode=self.root.find_node(pat) pattern=None if not anode: if not pat.startswith('^'): pat='^.+?%s'%pat if not pat.endswith('$'): pat='%s$'%pat pattern=re.compile(pat) def match(node,output): if output==True and not out: return False if output==False and not inn: return False if anode: return anode==node else: return pattern.match(node.abspath()) return match class EnvContext(BuildContext): fun=cmd=None def execute(self): self.restore() if not self.all_envs: self.load_envs() self.recurse([self.run_dir]) ntpsec-1.1.0+dfsg1/waflib/Tools/0000755000175000017500000000000013252650652016173 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/waflib/Tools/qt5.py0000644000175000017500000003723613210576031017261 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file try: from xml.sax import make_parser from xml.sax.handler import ContentHandler except ImportError: has_xml=False ContentHandler=object else: has_xml=True import os,sys,re from waflib.Tools import cxx from waflib import Task,Utils,Options,Errors,Context from waflib.TaskGen import feature,after_method,extension,before_method from waflib.Configure import conf from waflib import Logs MOC_H=['.h','.hpp','.hxx','.hh'] EXT_RCC=['.qrc'] EXT_UI=['.ui'] EXT_QT5=['.cpp','.cc','.cxx','.C'] class qxx(Task.classes['cxx']): def __init__(self,*k,**kw): Task.Task.__init__(self,*k,**kw) self.moc_done=0 def runnable_status(self): if self.moc_done: return Task.Task.runnable_status(self) else: for t in self.run_after: if not t.hasrun: return Task.ASK_LATER self.add_moc_tasks() return Task.Task.runnable_status(self) def create_moc_task(self,h_node,m_node): try: moc_cache=self.generator.bld.moc_cache except AttributeError: moc_cache=self.generator.bld.moc_cache={} try: return moc_cache[h_node] except KeyError: tsk=moc_cache[h_node]=Task.classes['moc'](env=self.env,generator=self.generator) tsk.set_inputs(h_node) tsk.set_outputs(m_node) tsk.env.append_unique('MOC_FLAGS','-i') if self.generator: self.generator.tasks.append(tsk) gen=self.generator.bld.producer gen.outstanding.appendleft(tsk) gen.total+=1 return tsk else: delattr(self,'cache_sig') def add_moc_tasks(self): node=self.inputs[0] bld=self.generator.bld try: self.signature() except KeyError: pass else: delattr(self,'cache_sig') include_nodes=[node.parent]+self.generator.includes_nodes moctasks=[] mocfiles=set() for d in bld.raw_deps.get(self.uid(),[]): if not d.endswith('.moc'): continue if d in mocfiles: continue mocfiles.add(d) h_node=None base2=d[:-4] prefix=node.name[:node.name.rfind('.')] if base2==prefix: h_node=node else: for x in include_nodes: for e in MOC_H: h_node=x.find_node(base2+e) if h_node: break else: continue break if h_node: m_node=h_node.change_ext('.moc') else: raise Errors.WafError('No source found for %r which is a moc file'%d) task=self.create_moc_task(h_node,m_node) moctasks.append(task) self.run_after.update(set(moctasks)) self.moc_done=1 class trans_update(Task.Task): run_str='${QT_LUPDATE} ${SRC} -ts ${TGT}' color='BLUE' class XMLHandler(ContentHandler): def __init__(self): ContentHandler.__init__(self) self.buf=[] self.files=[] def startElement(self,name,attrs): if name=='file': self.buf=[] def endElement(self,name): if name=='file': self.files.append(str(''.join(self.buf))) def characters(self,cars): self.buf.append(cars) @extension(*EXT_RCC) def create_rcc_task(self,node): rcnode=node.change_ext('_rc.cpp') self.create_task('rcc',node,rcnode) cpptask=self.create_task('cxx',rcnode,rcnode.change_ext('.o')) try: self.compiled_tasks.append(cpptask) except AttributeError: self.compiled_tasks=[cpptask] return cpptask @extension(*EXT_UI) def create_uic_task(self,node): uictask=self.create_task('ui5',node) uictask.outputs=[node.parent.find_or_declare(self.env.ui_PATTERN%node.name[:-3])] @extension('.ts') def add_lang(self,node): self.lang=self.to_list(getattr(self,'lang',[]))+[node] @feature('qt5') @before_method('process_source') def process_mocs(self): lst=self.to_nodes(getattr(self,'moc',[])) self.source=self.to_list(getattr(self,'source',[])) for x in lst: prefix=x.name[:x.name.rfind('.')] moc_target='moc_%s.%d.cpp'%(prefix,self.idx) moc_node=x.parent.find_or_declare(moc_target) self.source.append(moc_node) self.create_task('moc',x,moc_node) @feature('qt5') @after_method('apply_link') def apply_qt5(self): if getattr(self,'lang',None): qmtasks=[] for x in self.to_list(self.lang): if isinstance(x,str): x=self.path.find_resource(x+'.ts') qmtasks.append(self.create_task('ts2qm',x,x.change_ext('.qm'))) if getattr(self,'update',None)and Options.options.trans_qt5: cxxnodes=[a.inputs[0]for a in self.compiled_tasks]+[a.inputs[0]for a in self.tasks if getattr(a,'inputs',None)and a.inputs[0].name.endswith('.ui')] for x in qmtasks: self.create_task('trans_update',cxxnodes,x.inputs) if getattr(self,'langname',None): qmnodes=[x.outputs[0]for x in qmtasks] rcnode=self.langname if isinstance(rcnode,str): rcnode=self.path.find_or_declare(rcnode+'.qrc') t=self.create_task('qm2rcc',qmnodes,rcnode) k=create_rcc_task(self,t.outputs[0]) self.link_task.inputs.append(k.outputs[0]) lst=[] for flag in self.to_list(self.env.CXXFLAGS): if len(flag)<2: continue f=flag[0:2] if f in('-D','-I','/D','/I'): if(f[0]=='/'): lst.append('-'+flag[1:]) else: lst.append(flag) self.env.append_value('MOC_FLAGS',lst) @extension(*EXT_QT5) def cxx_hook(self,node): return self.create_compiled_task('qxx',node) class rcc(Task.Task): color='BLUE' run_str='${QT_RCC} -name ${tsk.rcname()} ${SRC[0].abspath()} ${RCC_ST} -o ${TGT}' ext_out=['.h'] def rcname(self): return os.path.splitext(self.inputs[0].name)[0] def scan(self): if not has_xml: Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!') return([],[]) parser=make_parser() curHandler=XMLHandler() parser.setContentHandler(curHandler) fi=open(self.inputs[0].abspath(),'r') try: parser.parse(fi) finally: fi.close() nodes=[] names=[] root=self.inputs[0].parent for x in curHandler.files: nd=root.find_resource(x) if nd: nodes.append(nd) else: names.append(x) return(nodes,names) class moc(Task.Task): color='BLUE' run_str='${QT_MOC} ${MOC_FLAGS} ${MOCCPPPATH_ST:INCPATHS} ${MOCDEFINES_ST:DEFINES} ${SRC} ${MOC_ST} ${TGT}' class ui5(Task.Task): color='BLUE' run_str='${QT_UIC} ${SRC} -o ${TGT}' ext_out=['.h'] class ts2qm(Task.Task): color='BLUE' run_str='${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}' class qm2rcc(Task.Task): color='BLUE' after='ts2qm' def run(self): txt='\n'.join(['%s'%k.path_from(self.outputs[0].parent)for k in self.inputs]) code='\n\n%s\n\n'%txt self.outputs[0].write(code) def configure(self): self.find_qt5_binaries() self.set_qt5_libs_dir() self.set_qt5_libs_to_check() self.set_qt5_defines() self.find_qt5_libraries() self.add_qt5_rpath() self.simplify_qt5_libs() if not has_xml: Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!') if'COMPILER_CXX'not in self.env: self.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?') frag='#include \nint main(int argc, char **argv) {return 0;}\n' uses='QT5CORE QT5WIDGETS QT5GUI' for flag in[[],'-fPIE','-fPIC','-std=c++11',['-std=c++11','-fPIE'],['-std=c++11','-fPIC']]: msg='See if Qt files compile ' if flag: msg+='with %s'%flag try: self.check(features='qt5 cxx',use=uses,uselib_store='qt5',cxxflags=flag,fragment=frag,msg=msg) except self.errors.ConfigurationError: pass else: break else: self.fatal('Could not build a simple Qt application') if Utils.unversioned_sys_platform()=='freebsd': frag='#include \nint main(int argc, char **argv) { QApplication app(argc, argv); return NULL != (void*) (&app);}\n' try: self.check(features='qt5 cxx cxxprogram',use=uses,fragment=frag,msg='Can we link Qt programs on FreeBSD directly?') except self.errors.ConfigurationError: self.check(features='qt5 cxx cxxprogram',use=uses,uselib_store='qt5',libpath='/usr/local/lib',fragment=frag,msg='Is /usr/local/lib required?') @conf def find_qt5_binaries(self): env=self.env opt=Options.options qtdir=getattr(opt,'qtdir','') qtbin=getattr(opt,'qtbin','') paths=[] if qtdir: qtbin=os.path.join(qtdir,'bin') if not qtdir: qtdir=self.environ.get('QT5_ROOT','') qtbin=self.environ.get('QT5_BIN')or os.path.join(qtdir,'bin') if qtbin: paths=[qtbin] if not qtdir: paths=self.environ.get('PATH','').split(os.pathsep) paths.extend(['/usr/share/qt5/bin','/usr/local/lib/qt5/bin']) try: lst=Utils.listdir('/usr/local/Trolltech/') except OSError: pass else: if lst: lst.sort() lst.reverse() qtdir='/usr/local/Trolltech/%s/'%lst[0] qtbin=os.path.join(qtdir,'bin') paths.append(qtbin) cand=None prev_ver=['5','0','0'] for qmk in('qmake-qt5','qmake5','qmake'): try: qmake=self.find_program(qmk,path_list=paths) except self.errors.ConfigurationError: pass else: try: version=self.cmd_and_log(qmake+['-query','QT_VERSION']).strip() except self.errors.WafError: pass else: if version: new_ver=version.split('.') if new_ver>prev_ver: cand=qmake prev_ver=new_ver if not cand: try: self.find_program('qtchooser') except self.errors.ConfigurationError: pass else: cmd=self.env.QTCHOOSER+['-qt=5','-run-tool=qmake'] try: version=self.cmd_and_log(cmd+['-query','QT_VERSION']) except self.errors.WafError: pass else: cand=cmd if cand: self.env.QMAKE=cand else: self.fatal('Could not find qmake for qt5') self.env.QT_HOST_BINS=qtbin=self.cmd_and_log(self.env.QMAKE+['-query','QT_HOST_BINS']).strip() paths.insert(0,qtbin) def find_bin(lst,var): if var in env: return for f in lst: try: ret=self.find_program(f,path_list=paths) except self.errors.ConfigurationError: pass else: env[var]=ret break find_bin(['uic-qt5','uic'],'QT_UIC') if not env.QT_UIC: self.fatal('cannot find the uic compiler for qt5') self.start_msg('Checking for uic version') uicver=self.cmd_and_log(env.QT_UIC+['-version'],output=Context.BOTH) uicver=''.join(uicver).strip() uicver=uicver.replace('Qt User Interface Compiler ','').replace('User Interface Compiler for Qt','') self.end_msg(uicver) if uicver.find(' 3.')!=-1 or uicver.find(' 4.')!=-1: self.fatal('this uic compiler is for qt3 or qt4, add uic for qt5 to your path') find_bin(['moc-qt5','moc'],'QT_MOC') find_bin(['rcc-qt5','rcc'],'QT_RCC') find_bin(['lrelease-qt5','lrelease'],'QT_LRELEASE') find_bin(['lupdate-qt5','lupdate'],'QT_LUPDATE') env.UIC_ST='%s -o %s' env.MOC_ST='-o' env.ui_PATTERN='ui_%s.h' env.QT_LRELEASE_FLAGS=['-silent'] env.MOCCPPPATH_ST='-I%s' env.MOCDEFINES_ST='-D%s' @conf def set_qt5_libs_dir(self): env=self.env qtlibs=getattr(Options.options,'qtlibs',None)or self.environ.get('QT5_LIBDIR') if not qtlibs: try: qtlibs=self.cmd_and_log(env.QMAKE+['-query','QT_INSTALL_LIBS']).strip() except Errors.WafError: qtdir=self.cmd_and_log(env.QMAKE+['-query','QT_INSTALL_PREFIX']).strip() qtlibs=os.path.join(qtdir,'lib') self.msg('Found the Qt5 libraries in',qtlibs) env.QTLIBS=qtlibs @conf def find_single_qt5_lib(self,name,uselib,qtlibs,qtincludes,force_static): env=self.env if force_static: exts=('.a','.lib') prefix='STLIB' else: exts=('.so','.lib') prefix='LIB' def lib_names(): for x in exts: for k in('','5')if Utils.is_win32 else['']: for p in('lib',''): yield(p,name,k,x) raise StopIteration for tup in lib_names(): k=''.join(tup) path=os.path.join(qtlibs,k) if os.path.exists(path): if env.DEST_OS=='win32': libval=''.join(tup[:-1]) else: libval=name env.append_unique(prefix+'_'+uselib,libval) env.append_unique('%sPATH_%s'%(prefix,uselib),qtlibs) env.append_unique('INCLUDES_'+uselib,qtincludes) env.append_unique('INCLUDES_'+uselib,os.path.join(qtincludes,name.replace('Qt5','Qt'))) return k return False @conf def find_qt5_libraries(self): env=self.env qtincludes=self.environ.get('QT5_INCLUDES')or self.cmd_and_log(env.QMAKE+['-query','QT_INSTALL_HEADERS']).strip() force_static=self.environ.get('QT5_FORCE_STATIC') try: if self.environ.get('QT5_XCOMPILE'): self.fatal('QT5_XCOMPILE Disables pkg-config detection') self.check_cfg(atleast_pkgconfig_version='0.1') except self.errors.ConfigurationError: for i in self.qt5_vars: uselib=i.upper() if Utils.unversioned_sys_platform()=='darwin': fwk=i.replace('Qt5','Qt') frameworkName=fwk+'.framework' qtDynamicLib=os.path.join(env.QTLIBS,frameworkName,fwk) if os.path.exists(qtDynamicLib): env.append_unique('FRAMEWORK_'+uselib,fwk) env.append_unique('FRAMEWORKPATH_'+uselib,env.QTLIBS) self.msg('Checking for %s'%i,qtDynamicLib,'GREEN') else: self.msg('Checking for %s'%i,False,'YELLOW') env.append_unique('INCLUDES_'+uselib,os.path.join(env.QTLIBS,frameworkName,'Headers')) else: for j in('','d'): k='_DEBUG'if j=='d'else'' ret=self.find_single_qt5_lib(i+j,uselib+k,env.QTLIBS,qtincludes,force_static) if not force_static and not ret: ret=self.find_single_qt5_lib(i+j,uselib+k,env.QTLIBS,qtincludes,True) self.msg('Checking for %s'%(i+j),ret,'GREEN'if ret else'YELLOW') else: path='%s:%s:%s/pkgconfig:/usr/lib/qt5/lib/pkgconfig:/opt/qt5/lib/pkgconfig:/usr/lib/qt5/lib:/opt/qt5/lib'%(self.environ.get('PKG_CONFIG_PATH',''),env.QTLIBS,env.QTLIBS) for i in self.qt5_vars_debug+self.qt5_vars: self.check_cfg(package=i,args='--cflags --libs',mandatory=False,force_static=force_static,pkg_config_path=path) @conf def simplify_qt5_libs(self): env=self.env def process_lib(vars_,coreval): for d in vars_: var=d.upper() if var=='QTCORE': continue value=env['LIBPATH_'+var] if value: core=env[coreval] accu=[] for lib in value: if lib in core: continue accu.append(lib) env['LIBPATH_'+var]=accu process_lib(self.qt5_vars,'LIBPATH_QTCORE') process_lib(self.qt5_vars_debug,'LIBPATH_QTCORE_DEBUG') @conf def add_qt5_rpath(self): env=self.env if getattr(Options.options,'want_rpath',False): def process_rpath(vars_,coreval): for d in vars_: var=d.upper() value=env['LIBPATH_'+var] if value: core=env[coreval] accu=[] for lib in value: if var!='QTCORE': if lib in core: continue accu.append('-Wl,--rpath='+lib) env['RPATH_'+var]=accu process_rpath(self.qt5_vars,'LIBPATH_QTCORE') process_rpath(self.qt5_vars_debug,'LIBPATH_QTCORE_DEBUG') @conf def set_qt5_libs_to_check(self): self.qt5_vars=Utils.to_list(getattr(self,'qt5_vars',[])) if not self.qt5_vars: dirlst=Utils.listdir(self.env.QTLIBS) pat=self.env.cxxshlib_PATTERN if Utils.is_win32: pat=pat.replace('.dll','.lib') if self.environ.get('QT5_FORCE_STATIC'): pat=self.env.cxxstlib_PATTERN if Utils.unversioned_sys_platform()=='darwin': pat="%s\.framework" re_qt=re.compile(pat%'Qt5?(?P.*)'+'$') for x in dirlst: m=re_qt.match(x) if m: self.qt5_vars.append("Qt5%s"%m.group('name')) if not self.qt5_vars: self.fatal('cannot find any Qt5 library (%r)'%self.env.QTLIBS) qtextralibs=getattr(Options.options,'qtextralibs',None) if qtextralibs: self.qt5_vars.extend(qtextralibs.split(',')) if not hasattr(self,'qt5_vars_debug'): self.qt5_vars_debug=[a+'_DEBUG'for a in self.qt5_vars] self.qt5_vars_debug=Utils.to_list(self.qt5_vars_debug) @conf def set_qt5_defines(self): if sys.platform!='win32': return for x in self.qt5_vars: y=x.replace('Qt5','Qt')[2:].upper() self.env.append_unique('DEFINES_%s'%x.upper(),'QT_%s_LIB'%y) self.env.append_unique('DEFINES_%s_DEBUG'%x.upper(),'QT_%s_LIB'%y) def options(opt): opt.add_option('--want-rpath',action='store_true',default=False,dest='want_rpath',help='enable the rpath for qt libraries') for i in'qtdir qtbin qtlibs'.split(): opt.add_option('--'+i,type='string',default='',dest=i) opt.add_option('--translate',action='store_true',help='collect translation strings',dest='trans_qt5',default=False) opt.add_option('--qtextralibs',type='string',default='',dest='qtextralibs',help='additional qt libraries on the system to add to default ones, comma separated') ntpsec-1.1.0+dfsg1/waflib/Tools/ar.py0000644000175000017500000000051713210576007017145 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Configure import conf @conf def find_ar(conf): conf.load('ar') def configure(conf): conf.find_program('ar',var='AR') conf.add_os_flags('ARFLAGS') if not conf.env.ARFLAGS: conf.env.ARFLAGS=['rcs'] ntpsec-1.1.0+dfsg1/waflib/Tools/cxx.py0000644000175000017500000000230113210576007017336 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import TaskGen,Task from waflib.Tools import c_preproc from waflib.Tools.ccroot import link_task,stlink_task @TaskGen.extension('.cpp','.cc','.cxx','.C','.c++') def cxx_hook(self,node): return self.create_compiled_task('cxx',node) if not'.c'in TaskGen.task_gen.mappings: TaskGen.task_gen.mappings['.c']=TaskGen.task_gen.mappings['.cpp'] class cxx(Task.Task): run_str='${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT[0].abspath()} ${CPPFLAGS}' vars=['CXXDEPS'] ext_in=['.h'] scan=c_preproc.scan class cxxprogram(link_task): run_str='${LINK_CXX} ${LINKFLAGS} ${CXXLNK_SRC_F}${SRC} ${CXXLNK_TGT_F}${TGT[0].abspath()} ${RPATH_ST:RPATH} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${FRAMEWORK_ST:FRAMEWORK} ${ARCH_ST:ARCH} ${STLIB_MARKER} ${STLIBPATH_ST:STLIBPATH} ${STLIB_ST:STLIB} ${SHLIB_MARKER} ${LIBPATH_ST:LIBPATH} ${LIB_ST:LIB} ${LDFLAGS}' vars=['LINKDEPS'] ext_out=['.bin'] inst_to='${BINDIR}' class cxxshlib(cxxprogram): inst_to='${LIBDIR}' class cxxstlib(stlink_task): pass ntpsec-1.1.0+dfsg1/waflib/Tools/bison.py0000644000175000017500000000147213210576007017656 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Task from waflib.TaskGen import extension class bison(Task.Task): color='BLUE' run_str='${BISON} ${BISONFLAGS} ${SRC[0].abspath()} -o ${TGT[0].name}' ext_out=['.h'] @extension('.y','.yc','.yy') def big_bison(self,node): has_h='-d'in self.env.BISONFLAGS outs=[] if node.name.endswith('.yc'): outs.append(node.change_ext('.tab.cc')) if has_h: outs.append(node.change_ext('.tab.hh')) else: outs.append(node.change_ext('.tab.c')) if has_h: outs.append(node.change_ext('.tab.h')) tsk=self.create_task('bison',node,outs) tsk.cwd=node.parent.get_bld() self.source.append(outs[0]) def configure(conf): conf.find_program('bison',var='BISON') conf.env.BISONFLAGS=['-d'] ntpsec-1.1.0+dfsg1/waflib/Tools/dbus.py0000644000175000017500000000211413172024730017471 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Task,Errors from waflib.TaskGen import taskgen_method,before_method @taskgen_method def add_dbus_file(self,filename,prefix,mode): if not hasattr(self,'dbus_lst'): self.dbus_lst=[] if not'process_dbus'in self.meths: self.meths.append('process_dbus') self.dbus_lst.append([filename,prefix,mode]) @before_method('apply_core') def process_dbus(self): for filename,prefix,mode in getattr(self,'dbus_lst',[]): node=self.path.find_resource(filename) if not node: raise Errors.WafError('file not found '+filename) tsk=self.create_task('dbus_binding_tool',node,node.change_ext('.h')) tsk.env.DBUS_BINDING_TOOL_PREFIX=prefix tsk.env.DBUS_BINDING_TOOL_MODE=mode class dbus_binding_tool(Task.Task): color='BLUE' ext_out=['.h'] run_str='${DBUS_BINDING_TOOL} --prefix=${DBUS_BINDING_TOOL_PREFIX} --mode=${DBUS_BINDING_TOOL_MODE} --output=${TGT} ${SRC}' shell=True def configure(conf): conf.find_program('dbus-binding-tool',var='DBUS_BINDING_TOOL') ntpsec-1.1.0+dfsg1/waflib/Tools/gxx.py0000644000175000017500000000514313210576031017346 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar from waflib.Configure import conf @conf def find_gxx(conf): cxx=conf.find_program(['g++','c++'],var='CXX') conf.get_cc_version(cxx,gcc=True) conf.env.CXX_NAME='gcc' @conf def gxx_common_flags(conf): v=conf.env v.CXX_SRC_F=[] v.CXX_TGT_F=['-c','-o'] if not v.LINK_CXX: v.LINK_CXX=v.CXX v.CXXLNK_SRC_F=[] v.CXXLNK_TGT_F=['-o'] v.CPPPATH_ST='-I%s' v.DEFINES_ST='-D%s' v.LIB_ST='-l%s' v.LIBPATH_ST='-L%s' v.STLIB_ST='-l%s' v.STLIBPATH_ST='-L%s' v.RPATH_ST='-Wl,-rpath,%s' v.SONAME_ST='-Wl,-h,%s' v.SHLIB_MARKER='-Wl,-Bdynamic' v.STLIB_MARKER='-Wl,-Bstatic' v.cxxprogram_PATTERN='%s' v.CXXFLAGS_cxxshlib=['-fPIC'] v.LINKFLAGS_cxxshlib=['-shared'] v.cxxshlib_PATTERN='lib%s.so' v.LINKFLAGS_cxxstlib=['-Wl,-Bstatic'] v.cxxstlib_PATTERN='lib%s.a' v.LINKFLAGS_MACBUNDLE=['-bundle','-undefined','dynamic_lookup'] v.CXXFLAGS_MACBUNDLE=['-fPIC'] v.macbundle_PATTERN='%s.bundle' @conf def gxx_modifier_win32(conf): v=conf.env v.cxxprogram_PATTERN='%s.exe' v.cxxshlib_PATTERN='%s.dll' v.implib_PATTERN='lib%s.dll.a' v.IMPLIB_ST='-Wl,--out-implib,%s' v.CXXFLAGS_cxxshlib=[] v.append_value('LINKFLAGS',['-Wl,--enable-auto-import']) @conf def gxx_modifier_cygwin(conf): gxx_modifier_win32(conf) v=conf.env v.cxxshlib_PATTERN='cyg%s.dll' v.append_value('LINKFLAGS_cxxshlib',['-Wl,--enable-auto-image-base']) v.CXXFLAGS_cxxshlib=[] @conf def gxx_modifier_darwin(conf): v=conf.env v.CXXFLAGS_cxxshlib=['-fPIC'] v.LINKFLAGS_cxxshlib=['-dynamiclib'] v.cxxshlib_PATTERN='lib%s.dylib' v.FRAMEWORKPATH_ST='-F%s' v.FRAMEWORK_ST=['-framework'] v.ARCH_ST=['-arch'] v.LINKFLAGS_cxxstlib=[] v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.SONAME_ST=[] @conf def gxx_modifier_aix(conf): v=conf.env v.LINKFLAGS_cxxprogram=['-Wl,-brtl'] v.LINKFLAGS_cxxshlib=['-shared','-Wl,-brtl,-bexpfull'] v.SHLIB_MARKER=[] @conf def gxx_modifier_hpux(conf): v=conf.env v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.CFLAGS_cxxshlib=['-fPIC','-DPIC'] v.cxxshlib_PATTERN='lib%s.sl' @conf def gxx_modifier_openbsd(conf): conf.env.SONAME_ST=[] @conf def gcc_modifier_osf1V(conf): v=conf.env v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.SONAME_ST=[] @conf def gxx_modifier_platform(conf): gxx_modifier_func=getattr(conf,'gxx_modifier_'+conf.env.DEST_OS,None) if gxx_modifier_func: gxx_modifier_func() def configure(conf): conf.find_gxx() conf.find_ar() conf.gxx_common_flags() conf.gxx_modifier_platform() conf.cxx_load_tools() conf.cxx_add_flags() conf.link_add_flags() conf.check_gcc_o_space('cxx') ntpsec-1.1.0+dfsg1/waflib/Tools/xlc.py0000644000175000017500000000202113210576007017321 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar from waflib.Configure import conf @conf def find_xlc(conf): cc=conf.find_program(['xlc_r','xlc'],var='CC') conf.get_xlc_version(cc) conf.env.CC_NAME='xlc' @conf def xlc_common_flags(conf): v=conf.env v.CC_SRC_F=[] v.CC_TGT_F=['-c','-o'] if not v.LINK_CC: v.LINK_CC=v.CC v.CCLNK_SRC_F=[] v.CCLNK_TGT_F=['-o'] v.CPPPATH_ST='-I%s' v.DEFINES_ST='-D%s' v.LIB_ST='-l%s' v.LIBPATH_ST='-L%s' v.STLIB_ST='-l%s' v.STLIBPATH_ST='-L%s' v.RPATH_ST='-Wl,-rpath,%s' v.SONAME_ST=[] v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.LINKFLAGS_cprogram=['-Wl,-brtl'] v.cprogram_PATTERN='%s' v.CFLAGS_cshlib=['-fPIC'] v.LINKFLAGS_cshlib=['-G','-Wl,-brtl,-bexpfull'] v.cshlib_PATTERN='lib%s.so' v.LINKFLAGS_cstlib=[] v.cstlib_PATTERN='lib%s.a' def configure(conf): conf.find_xlc() conf.find_ar() conf.xlc_common_flags() conf.cc_load_tools() conf.cc_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/fc_scan.py0000644000175000017500000000323013210576007020132 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re INC_REGEX="""(?:^|['">]\s*;)\s*(?:|#\s*)INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" USE_REGEX="""(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)""" MOD_REGEX="""(?:^|;)\s*MODULE(?!\s*PROCEDURE)(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)""" re_inc=re.compile(INC_REGEX,re.I) re_use=re.compile(USE_REGEX,re.I) re_mod=re.compile(MOD_REGEX,re.I) class fortran_parser(object): def __init__(self,incpaths): self.seen=[] self.nodes=[] self.names=[] self.incpaths=incpaths def find_deps(self,node): txt=node.read() incs=[] uses=[] mods=[] for line in txt.splitlines(): m=re_inc.search(line) if m: incs.append(m.group(1)) m=re_use.search(line) if m: uses.append(m.group(1)) m=re_mod.search(line) if m: mods.append(m.group(1)) return(incs,uses,mods) def start(self,node): self.waiting=[node] while self.waiting: nd=self.waiting.pop(0) self.iter(nd) def iter(self,node): incs,uses,mods=self.find_deps(node) for x in incs: if x in self.seen: continue self.seen.append(x) self.tryfind_header(x) for x in uses: name="USE@%s"%x if not name in self.names: self.names.append(name) for x in mods: name="MOD@%s"%x if not name in self.names: self.names.append(name) def tryfind_header(self,filename): found=None for n in self.incpaths: found=n.find_resource(filename) if found: self.nodes.append(found) self.waiting.append(found) break if not found: if not filename in self.names: self.names.append(filename) ntpsec-1.1.0+dfsg1/waflib/Tools/ldc2.py0000644000175000017500000000165113172024730017365 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ar,d from waflib.Configure import conf @conf def find_ldc2(conf): conf.find_program(['ldc2'],var='D') out=conf.cmd_and_log(conf.env.D+['-version']) if out.find("based on DMD v2.")==-1: conf.fatal("detected compiler is not ldc2") @conf def common_flags_ldc2(conf): v=conf.env v.D_SRC_F=['-c'] v.D_TGT_F='-of%s' v.D_LINKER=v.D v.DLNK_SRC_F='' v.DLNK_TGT_F='-of%s' v.DINC_ST='-I%s' v.DSHLIB_MARKER=v.DSTLIB_MARKER='' v.DSTLIB_ST=v.DSHLIB_ST='-L-l%s' v.DSTLIBPATH_ST=v.DLIBPATH_ST='-L-L%s' v.LINKFLAGS_dshlib=['-L-shared'] v.DHEADER_ext='.di' v.DFLAGS_d_with_header=['-H','-Hf'] v.D_HDR_F='%s' v.LINKFLAGS=[] v.DFLAGS_dshlib=['-relocation-model=pic'] def configure(conf): conf.find_ldc2() conf.load('ar') conf.load('d') conf.common_flags_ldc2() conf.d_platform_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/fc_config.py0000644000175000017500000002224413210576031020456 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re,os,sys,shlex from waflib.Configure import conf from waflib.TaskGen import feature,before_method FC_FRAGMENT=' program main\n end program main\n' FC_FRAGMENT2=' PROGRAM MAIN\n END\n' @conf def fc_flags(conf): v=conf.env v.FC_SRC_F=[] v.FC_TGT_F=['-c','-o'] v.FCINCPATH_ST='-I%s' v.FCDEFINES_ST='-D%s' if not v.LINK_FC: v.LINK_FC=v.FC v.FCLNK_SRC_F=[] v.FCLNK_TGT_F=['-o'] v.FCFLAGS_fcshlib=['-fpic'] v.LINKFLAGS_fcshlib=['-shared'] v.fcshlib_PATTERN='lib%s.so' v.fcstlib_PATTERN='lib%s.a' v.FCLIB_ST='-l%s' v.FCLIBPATH_ST='-L%s' v.FCSTLIB_ST='-l%s' v.FCSTLIBPATH_ST='-L%s' v.FCSTLIB_MARKER='-Wl,-Bstatic' v.FCSHLIB_MARKER='-Wl,-Bdynamic' v.SONAME_ST='-Wl,-h,%s' @conf def fc_add_flags(conf): conf.add_os_flags('FCPPFLAGS',dup=False) conf.add_os_flags('FCFLAGS',dup=False) conf.add_os_flags('LINKFLAGS',dup=False) conf.add_os_flags('LDFLAGS',dup=False) @conf def check_fortran(self,*k,**kw): self.check_cc(fragment=FC_FRAGMENT,compile_filename='test.f',features='fc fcprogram',msg='Compiling a simple fortran app') @conf def check_fc(self,*k,**kw): kw['compiler']='fc' if not'compile_mode'in kw: kw['compile_mode']='fc' if not'type'in kw: kw['type']='fcprogram' if not'compile_filename'in kw: kw['compile_filename']='test.f90' if not'code'in kw: kw['code']=FC_FRAGMENT return self.check(*k,**kw) @conf def fortran_modifier_darwin(conf): v=conf.env v.FCFLAGS_fcshlib=['-fPIC'] v.LINKFLAGS_fcshlib=['-dynamiclib'] v.fcshlib_PATTERN='lib%s.dylib' v.FRAMEWORKPATH_ST='-F%s' v.FRAMEWORK_ST=['-framework'] v.LINKFLAGS_fcstlib=[] v.FCSHLIB_MARKER='' v.FCSTLIB_MARKER='' v.SONAME_ST='' @conf def fortran_modifier_win32(conf): v=conf.env v.fcprogram_PATTERN=v.fcprogram_test_PATTERN='%s.exe' v.fcshlib_PATTERN='%s.dll' v.implib_PATTERN='lib%s.dll.a' v.IMPLIB_ST='-Wl,--out-implib,%s' v.FCFLAGS_fcshlib=[] v.append_value('LINKFLAGS',['-Wl,--enable-auto-import']) @conf def fortran_modifier_cygwin(conf): fortran_modifier_win32(conf) v=conf.env v.fcshlib_PATTERN='cyg%s.dll' v.append_value('LINKFLAGS_fcshlib',['-Wl,--enable-auto-image-base']) v.FCFLAGS_fcshlib=[] @conf def check_fortran_dummy_main(self,*k,**kw): if not self.env.CC: self.fatal('A c compiler is required for check_fortran_dummy_main') lst=['MAIN__','__MAIN','_MAIN','MAIN_','MAIN'] lst.extend([m.lower()for m in lst]) lst.append('') self.start_msg('Detecting whether we need a dummy main') for main in lst: kw['fortran_main']=main try: self.check_cc(fragment='int %s() { return 0; }\n'%(main or'test'),features='c fcprogram',mandatory=True) if not main: self.env.FC_MAIN=-1 self.end_msg('no') else: self.env.FC_MAIN=main self.end_msg('yes %s'%main) break except self.errors.ConfigurationError: pass else: self.end_msg('not found') self.fatal('could not detect whether fortran requires a dummy main, see the config.log') GCC_DRIVER_LINE=re.compile('^Driving:') POSIX_STATIC_EXT=re.compile('\S+\.a') POSIX_LIB_FLAGS=re.compile('-l\S+') @conf def is_link_verbose(self,txt): assert isinstance(txt,str) for line in txt.splitlines(): if not GCC_DRIVER_LINE.search(line): if POSIX_STATIC_EXT.search(line)or POSIX_LIB_FLAGS.search(line): return True return False @conf def check_fortran_verbose_flag(self,*k,**kw): self.start_msg('fortran link verbose flag') for x in('-v','--verbose','-verbose','-V'): try: self.check_cc(features='fc fcprogram_test',fragment=FC_FRAGMENT2,compile_filename='test.f',linkflags=[x],mandatory=True) except self.errors.ConfigurationError: pass else: if self.is_link_verbose(self.test_bld.err)or self.is_link_verbose(self.test_bld.out): self.end_msg(x) break else: self.end_msg('failure') self.fatal('Could not obtain the fortran link verbose flag (see config.log)') self.env.FC_VERBOSE_FLAG=x return x LINKFLAGS_IGNORED=[r'-lang*',r'-lcrt[a-zA-Z0-9\.]*\.o',r'-lc$',r'-lSystem',r'-libmil',r'-LIST:*',r'-LNO:*'] if os.name=='nt': LINKFLAGS_IGNORED.extend([r'-lfrt*',r'-luser32',r'-lkernel32',r'-ladvapi32',r'-lmsvcrt',r'-lshell32',r'-lmingw',r'-lmoldname']) else: LINKFLAGS_IGNORED.append(r'-lgcc*') RLINKFLAGS_IGNORED=[re.compile(f)for f in LINKFLAGS_IGNORED] def _match_ignore(line): for i in RLINKFLAGS_IGNORED: if i.match(line): return True return False def parse_fortran_link(lines): final_flags=[] for line in lines: if not GCC_DRIVER_LINE.match(line): _parse_flink_line(line,final_flags) return final_flags SPACE_OPTS=re.compile('^-[LRuYz]$') NOSPACE_OPTS=re.compile('^-[RL]') def _parse_flink_token(lexer,token,tmp_flags): if _match_ignore(token): pass elif token.startswith('-lkernel32')and sys.platform=='cygwin': tmp_flags.append(token) elif SPACE_OPTS.match(token): t=lexer.get_token() if t.startswith('P,'): t=t[2:] for opt in t.split(os.pathsep): tmp_flags.append('-L%s'%opt) elif NOSPACE_OPTS.match(token): tmp_flags.append(token) elif POSIX_LIB_FLAGS.match(token): tmp_flags.append(token) else: pass t=lexer.get_token() return t def _parse_flink_line(line,final_flags): lexer=shlex.shlex(line,posix=True) lexer.whitespace_split=True t=lexer.get_token() tmp_flags=[] while t: t=_parse_flink_token(lexer,t,tmp_flags) final_flags.extend(tmp_flags) return final_flags @conf def check_fortran_clib(self,autoadd=True,*k,**kw): if not self.env.FC_VERBOSE_FLAG: self.fatal('env.FC_VERBOSE_FLAG is not set: execute check_fortran_verbose_flag?') self.start_msg('Getting fortran runtime link flags') try: self.check_cc(fragment=FC_FRAGMENT2,compile_filename='test.f',features='fc fcprogram_test',linkflags=[self.env.FC_VERBOSE_FLAG]) except Exception: self.end_msg(False) if kw.get('mandatory',True): conf.fatal('Could not find the c library flags') else: out=self.test_bld.err flags=parse_fortran_link(out.splitlines()) self.end_msg('ok (%s)'%' '.join(flags)) self.env.LINKFLAGS_CLIB=flags return flags return[] def getoutput(conf,cmd,stdin=False): from waflib import Errors if conf.env.env: env=conf.env.env else: env=dict(os.environ) env['LANG']='C' input=stdin and'\n'.encode()or None try: out,err=conf.cmd_and_log(cmd,env=env,output=0,input=input) except Errors.WafError as e: if not(hasattr(e,'stderr')and hasattr(e,'stdout')): raise e else: out=e.stdout err=e.stderr except Exception: conf.fatal('could not determine the compiler version %r'%cmd) return(out,err) ROUTINES_CODE="""\ subroutine foobar() return end subroutine foo_bar() return end """ MAIN_CODE=""" void %(dummy_func_nounder)s(void); void %(dummy_func_under)s(void); int %(main_func_name)s() { %(dummy_func_nounder)s(); %(dummy_func_under)s(); return 0; } """ @feature('link_main_routines_func') @before_method('process_source') def link_main_routines_tg_method(self): def write_test_file(task): task.outputs[0].write(task.generator.code) bld=self.bld bld(rule=write_test_file,target='main.c',code=MAIN_CODE%self.__dict__) bld(rule=write_test_file,target='test.f',code=ROUTINES_CODE) bld(features='fc fcstlib',source='test.f',target='test') bld(features='c fcprogram',source='main.c',target='app',use='test') def mangling_schemes(): for u in('_',''): for du in('','_'): for c in("lower","upper"): yield(u,du,c) def mangle_name(u,du,c,name): return getattr(name,c)()+u+(name.find('_')!=-1 and du or'') @conf def check_fortran_mangling(self,*k,**kw): if not self.env.CC: self.fatal('A c compiler is required for link_main_routines') if not self.env.FC: self.fatal('A fortran compiler is required for link_main_routines') if not self.env.FC_MAIN: self.fatal('Checking for mangling requires self.env.FC_MAIN (execute "check_fortran_dummy_main" first?)') self.start_msg('Getting fortran mangling scheme') for(u,du,c)in mangling_schemes(): try: self.check_cc(compile_filename=[],features='link_main_routines_func',msg='nomsg',errmsg='nomsg',dummy_func_nounder=mangle_name(u,du,c,'foobar'),dummy_func_under=mangle_name(u,du,c,'foo_bar'),main_func_name=self.env.FC_MAIN) except self.errors.ConfigurationError: pass else: self.end_msg("ok ('%s', '%s', '%s-case')"%(u,du,c)) self.env.FORTRAN_MANGLING=(u,du,c) break else: self.end_msg(False) self.fatal('mangler not found') return(u,du,c) @feature('pyext') @before_method('propagate_uselib_vars','apply_link') def set_lib_pat(self): self.env.fcshlib_PATTERN=self.env.pyext_PATTERN @conf def detect_openmp(self): for x in('-qopenmp','-fopenmp','-openmp','-mp','-xopenmp','-omp','-qsmp=omp'): try: self.check_fc(msg='Checking for OpenMP flag %s'%x,fragment='program main\n call omp_get_num_threads()\nend program main',fcflags=x,linkflags=x,uselib_store='OPENMP') except self.errors.ConfigurationError: pass else: break else: self.fatal('Could not find OpenMP') @conf def check_gfortran_o_space(self): if self.env.FC_NAME!='GFORTRAN'or int(self.env.FC_VERSION[0])>4: return self.env.stash() self.env.FCLNK_TGT_F=['-o',''] try: self.check_fc(msg='Checking if the -o link must be split from arguments',fragment=FC_FRAGMENT,features='fc fcshlib') except self.errors.ConfigurationError: self.env.revert() else: self.env.commit() ntpsec-1.1.0+dfsg1/waflib/Tools/waf_unit_test.py0000644000175000017500000001242713210576031021416 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,shlex,sys from waflib.TaskGen import feature,after_method,taskgen_method from waflib import Utils,Task,Logs,Options from waflib.Tools import ccroot testlock=Utils.threading.Lock() SCRIPT_TEMPLATE="""#! %(python)s import subprocess, sys cmd = %(cmd)r # if you want to debug with gdb: #cmd = ['gdb', '-args'] + cmd env = %(env)r status = subprocess.call(cmd, env=env, cwd=%(cwd)r, shell=isinstance(cmd, str)) sys.exit(status) """ @feature('test') @after_method('apply_link','process_use') def make_test(self): if not getattr(self,'link_task',None): return tsk=self.create_task('utest',self.link_task.outputs) if getattr(self,'ut_str',None): self.ut_run,lst=Task.compile_fun(self.ut_str,shell=getattr(self,'ut_shell',False)) tsk.vars=lst+tsk.vars if getattr(self,'ut_cwd',None): if isinstance(self.ut_cwd,str): if os.path.isabs(self.ut_cwd): self.ut_cwd=self.bld.root.make_node(self.ut_cwd) else: self.ut_cwd=self.path.make_node(self.ut_cwd) else: self.ut_cwd=tsk.inputs[0].parent if not hasattr(self,'ut_paths'): paths=[] for x in self.tmp_use_sorted: try: y=self.bld.get_tgen_by_name(x).link_task except AttributeError: pass else: if not isinstance(y,ccroot.stlink_task): paths.append(y.outputs[0].parent.abspath()) self.ut_paths=os.pathsep.join(paths)+os.pathsep if not hasattr(self,'ut_env'): self.ut_env=dct=dict(os.environ) def add_path(var): dct[var]=self.ut_paths+dct.get(var,'') if Utils.is_win32: add_path('PATH') elif Utils.unversioned_sys_platform()=='darwin': add_path('DYLD_LIBRARY_PATH') add_path('LD_LIBRARY_PATH') else: add_path('LD_LIBRARY_PATH') @taskgen_method def add_test_results(self,tup): Logs.debug("ut: %r",tup) self.utest_result=tup try: self.bld.utest_results.append(tup) except AttributeError: self.bld.utest_results=[tup] class utest(Task.Task): color='PINK' after=['vnum','inst'] vars=[] def runnable_status(self): if getattr(Options.options,'no_tests',False): return Task.SKIP_ME ret=super(utest,self).runnable_status() if ret==Task.SKIP_ME: if getattr(Options.options,'all_tests',False): return Task.RUN_ME return ret def get_test_env(self): return self.generator.ut_env def post_run(self): super(utest,self).post_run() if getattr(Options.options,'clear_failed_tests',False)and self.waf_unit_test_results[1]: self.generator.bld.task_sigs[self.uid()]=None def run(self): if hasattr(self.generator,'ut_run'): return self.generator.ut_run(self) self.ut_exec=getattr(self.generator,'ut_exec',[self.inputs[0].abspath()]) if getattr(self.generator,'ut_fun',None): self.generator.ut_fun(self) testcmd=getattr(self.generator,'ut_cmd',False)or getattr(Options.options,'testcmd',False) if testcmd: self.ut_exec=shlex.split(testcmd%' '.join(self.ut_exec)) return self.exec_command(self.ut_exec) def exec_command(self,cmd,**kw): Logs.debug('runner: %r',cmd) if getattr(Options.options,'dump_test_scripts',False): global SCRIPT_TEMPLATE script_code=SCRIPT_TEMPLATE%{'python':sys.executable,'env':self.get_test_env(),'cwd':self.get_cwd().abspath(),'cmd':cmd} script_file=self.inputs[0].abspath()+'_run.py' Utils.writef(script_file,script_code) os.chmod(script_file,Utils.O755) if Logs.verbose>1: Logs.info('Test debug file written as %r'%script_file) proc=Utils.subprocess.Popen(cmd,cwd=self.get_cwd().abspath(),env=self.get_test_env(),stderr=Utils.subprocess.PIPE,stdout=Utils.subprocess.PIPE,shell=isinstance(cmd,str)) (stdout,stderr)=proc.communicate() self.waf_unit_test_results=tup=(self.inputs[0].abspath(),proc.returncode,stdout,stderr) testlock.acquire() try: return self.generator.add_test_results(tup) finally: testlock.release() def get_cwd(self): return self.generator.ut_cwd def summary(bld): lst=getattr(bld,'utest_results',[]) if lst: Logs.pprint('CYAN','execution summary') total=len(lst) tfail=len([x for x in lst if x[1]]) Logs.pprint('CYAN',' tests that pass %d/%d'%(total-tfail,total)) for(f,code,out,err)in lst: if not code: Logs.pprint('CYAN',' %s'%f) Logs.pprint('CYAN',' tests that fail %d/%d'%(tfail,total)) for(f,code,out,err)in lst: if code: Logs.pprint('CYAN',' %s'%f) def set_exit_code(bld): lst=getattr(bld,'utest_results',[]) for(f,code,out,err)in lst: if code: msg=[] if out: msg.append('stdout:%s%s'%(os.linesep,out.decode('utf-8'))) if err: msg.append('stderr:%s%s'%(os.linesep,err.decode('utf-8'))) bld.fatal(os.linesep.join(msg)) def options(opt): opt.add_option('--notests',action='store_true',default=False,help='Exec no unit tests',dest='no_tests') opt.add_option('--alltests',action='store_true',default=False,help='Exec all unit tests',dest='all_tests') opt.add_option('--clear-failed',action='store_true',default=False,help='Force failed unit tests to run again next time',dest='clear_failed_tests') opt.add_option('--testcmd',action='store',default=False,dest='testcmd',help='Run the unit tests using the test-cmd string example "--testcmd="valgrind --error-exitcode=1 %s" to run under valgrind') opt.add_option('--dump-test-scripts',action='store_true',default=False,help='Create python scripts to help debug tests',dest='dump_test_scripts') ntpsec-1.1.0+dfsg1/waflib/Tools/glib2.py0000644000175000017500000002514613210576031017544 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os import functools from waflib import Context,Task,Utils,Options,Errors,Logs from waflib.TaskGen import taskgen_method,before_method,feature,extension from waflib.Configure import conf @taskgen_method def add_marshal_file(self,filename,prefix): if not hasattr(self,'marshal_list'): self.marshal_list=[] self.meths.append('process_marshal') self.marshal_list.append((filename,prefix)) @before_method('process_source') def process_marshal(self): for f,prefix in getattr(self,'marshal_list',[]): node=self.path.find_resource(f) if not node: raise Errors.WafError('file not found %r'%f) h_node=node.change_ext('.h') c_node=node.change_ext('.c') task=self.create_task('glib_genmarshal',node,[h_node,c_node]) task.env.GLIB_GENMARSHAL_PREFIX=prefix self.source=self.to_nodes(getattr(self,'source',[])) self.source.append(c_node) class glib_genmarshal(Task.Task): vars=['GLIB_GENMARSHAL_PREFIX','GLIB_GENMARSHAL'] color='BLUE' ext_out=['.h'] def run(self): bld=self.generator.bld get=self.env.get_flat cmd1="%s %s --prefix=%s --header > %s"%(get('GLIB_GENMARSHAL'),self.inputs[0].srcpath(),get('GLIB_GENMARSHAL_PREFIX'),self.outputs[0].abspath()) ret=bld.exec_command(cmd1) if ret: return ret c='''#include "%s"\n'''%self.outputs[0].name self.outputs[1].write(c) cmd2="%s %s --prefix=%s --body >> %s"%(get('GLIB_GENMARSHAL'),self.inputs[0].srcpath(),get('GLIB_GENMARSHAL_PREFIX'),self.outputs[1].abspath()) return bld.exec_command(cmd2) @taskgen_method def add_enums_from_template(self,source='',target='',template='',comments=''): if not hasattr(self,'enums_list'): self.enums_list=[] self.meths.append('process_enums') self.enums_list.append({'source':source,'target':target,'template':template,'file-head':'','file-prod':'','file-tail':'','enum-prod':'','value-head':'','value-prod':'','value-tail':'','comments':comments}) @taskgen_method def add_enums(self,source='',target='',file_head='',file_prod='',file_tail='',enum_prod='',value_head='',value_prod='',value_tail='',comments=''): if not hasattr(self,'enums_list'): self.enums_list=[] self.meths.append('process_enums') self.enums_list.append({'source':source,'template':'','target':target,'file-head':file_head,'file-prod':file_prod,'file-tail':file_tail,'enum-prod':enum_prod,'value-head':value_head,'value-prod':value_prod,'value-tail':value_tail,'comments':comments}) @before_method('process_source') def process_enums(self): for enum in getattr(self,'enums_list',[]): task=self.create_task('glib_mkenums') env=task.env inputs=[] source_list=self.to_list(enum['source']) if not source_list: raise Errors.WafError('missing source '+str(enum)) source_list=[self.path.find_resource(k)for k in source_list] inputs+=source_list env.GLIB_MKENUMS_SOURCE=[k.abspath()for k in source_list] if not enum['target']: raise Errors.WafError('missing target '+str(enum)) tgt_node=self.path.find_or_declare(enum['target']) if tgt_node.name.endswith('.c'): self.source.append(tgt_node) env.GLIB_MKENUMS_TARGET=tgt_node.abspath() options=[] if enum['template']: template_node=self.path.find_resource(enum['template']) options.append('--template %s'%(template_node.abspath())) inputs.append(template_node) params={'file-head':'--fhead','file-prod':'--fprod','file-tail':'--ftail','enum-prod':'--eprod','value-head':'--vhead','value-prod':'--vprod','value-tail':'--vtail','comments':'--comments'} for param,option in params.items(): if enum[param]: options.append('%s %r'%(option,enum[param])) env.GLIB_MKENUMS_OPTIONS=' '.join(options) task.set_inputs(inputs) task.set_outputs(tgt_node) class glib_mkenums(Task.Task): run_str='${GLIB_MKENUMS} ${GLIB_MKENUMS_OPTIONS} ${GLIB_MKENUMS_SOURCE} > ${GLIB_MKENUMS_TARGET}' color='PINK' ext_out=['.h'] @taskgen_method def add_settings_schemas(self,filename_list): if not hasattr(self,'settings_schema_files'): self.settings_schema_files=[] if not isinstance(filename_list,list): filename_list=[filename_list] self.settings_schema_files.extend(filename_list) @taskgen_method def add_settings_enums(self,namespace,filename_list): if hasattr(self,'settings_enum_namespace'): raise Errors.WafError("Tried to add gsettings enums to %r more than once"%self.name) self.settings_enum_namespace=namespace if not isinstance(filename_list,list): filename_list=[filename_list] self.settings_enum_files=filename_list @feature('glib2') def process_settings(self): enums_tgt_node=[] install_files=[] settings_schema_files=getattr(self,'settings_schema_files',[]) if settings_schema_files and not self.env.GLIB_COMPILE_SCHEMAS: raise Errors.WafError("Unable to process GSettings schemas - glib-compile-schemas was not found during configure") if hasattr(self,'settings_enum_files'): enums_task=self.create_task('glib_mkenums') source_list=self.settings_enum_files source_list=[self.path.find_resource(k)for k in source_list] enums_task.set_inputs(source_list) enums_task.env.GLIB_MKENUMS_SOURCE=[k.abspath()for k in source_list] target=self.settings_enum_namespace+'.enums.xml' tgt_node=self.path.find_or_declare(target) enums_task.set_outputs(tgt_node) enums_task.env.GLIB_MKENUMS_TARGET=tgt_node.abspath() enums_tgt_node=[tgt_node] install_files.append(tgt_node) options='--comments "" --fhead "" --vhead " <@type@ id=\\"%s.@EnumName@\\">" --vprod " " --vtail " " --ftail "" '%(self.settings_enum_namespace) enums_task.env.GLIB_MKENUMS_OPTIONS=options for schema in settings_schema_files: schema_task=self.create_task('glib_validate_schema') schema_node=self.path.find_resource(schema) if not schema_node: raise Errors.WafError("Cannot find the schema file %r"%schema) install_files.append(schema_node) source_list=enums_tgt_node+[schema_node] schema_task.set_inputs(source_list) schema_task.env.GLIB_COMPILE_SCHEMAS_OPTIONS=[("--schema-file="+k.abspath())for k in source_list] target_node=schema_node.change_ext('.xml.valid') schema_task.set_outputs(target_node) schema_task.env.GLIB_VALIDATE_SCHEMA_OUTPUT=target_node.abspath() def compile_schemas_callback(bld): if not bld.is_install: return compile_schemas=Utils.to_list(bld.env.GLIB_COMPILE_SCHEMAS) destdir=Options.options.destdir paths=bld._compile_schemas_registered if destdir: paths=(os.path.join(destdir,path.lstrip(os.sep))for path in paths) for path in paths: Logs.pprint('YELLOW','Updating GSettings schema cache %r'%path) if self.bld.exec_command(compile_schemas+[path]): Logs.warn('Could not update GSettings schema cache %r'%path) if self.bld.is_install: schemadir=self.env.GSETTINGSSCHEMADIR if not schemadir: raise Errors.WafError('GSETTINGSSCHEMADIR not defined (should have been set up automatically during configure)') if install_files: self.add_install_files(install_to=schemadir,install_from=install_files) registered_schemas=getattr(self.bld,'_compile_schemas_registered',None) if not registered_schemas: registered_schemas=self.bld._compile_schemas_registered=set() self.bld.add_post_fun(compile_schemas_callback) registered_schemas.add(schemadir) class glib_validate_schema(Task.Task): run_str='rm -f ${GLIB_VALIDATE_SCHEMA_OUTPUT} && ${GLIB_COMPILE_SCHEMAS} --dry-run ${GLIB_COMPILE_SCHEMAS_OPTIONS} && touch ${GLIB_VALIDATE_SCHEMA_OUTPUT}' color='PINK' @extension('.gresource.xml') def process_gresource_source(self,node): if not self.env.GLIB_COMPILE_RESOURCES: raise Errors.WafError("Unable to process GResource file - glib-compile-resources was not found during configure") if'gresource'in self.features: return h_node=node.change_ext('_xml.h') c_node=node.change_ext('_xml.c') self.create_task('glib_gresource_source',node,[h_node,c_node]) self.source.append(c_node) @feature('gresource') def process_gresource_bundle(self): for i in self.to_list(self.source): node=self.path.find_resource(i) task=self.create_task('glib_gresource_bundle',node,node.change_ext('')) inst_to=getattr(self,'install_path',None) if inst_to: self.add_install_files(install_to=inst_to,install_from=task.outputs) class glib_gresource_base(Task.Task): color='BLUE' base_cmd='${GLIB_COMPILE_RESOURCES} --sourcedir=${SRC[0].parent.srcpath()} --sourcedir=${SRC[0].bld_dir()}' def scan(self): bld=self.generator.bld kw={} kw['cwd']=self.get_cwd() kw['quiet']=Context.BOTH cmd=Utils.subst_vars('${GLIB_COMPILE_RESOURCES} --sourcedir=%s --sourcedir=%s --generate-dependencies %s'%(self.inputs[0].parent.srcpath(),self.inputs[0].bld_dir(),self.inputs[0].bldpath()),self.env) output=bld.cmd_and_log(cmd,**kw) nodes=[] names=[] for dep in output.splitlines(): if dep: node=bld.bldnode.find_node(dep) if node: nodes.append(node) else: names.append(dep) return(nodes,names) class glib_gresource_source(glib_gresource_base): vars=['GLIB_COMPILE_RESOURCES'] fun_h=Task.compile_fun_shell(glib_gresource_base.base_cmd+' --target=${TGT[0].abspath()} --generate-header ${SRC}') fun_c=Task.compile_fun_shell(glib_gresource_base.base_cmd+' --target=${TGT[1].abspath()} --generate-source ${SRC}') ext_out=['.h'] def run(self): return self.fun_h[0](self)or self.fun_c[0](self) class glib_gresource_bundle(glib_gresource_base): run_str=glib_gresource_base.base_cmd+' --target=${TGT} ${SRC}' shell=True @conf def find_glib_genmarshal(conf): conf.find_program('glib-genmarshal',var='GLIB_GENMARSHAL') @conf def find_glib_mkenums(conf): if not conf.env.PERL: conf.find_program('perl',var='PERL') conf.find_program('glib-mkenums',interpreter='PERL',var='GLIB_MKENUMS') @conf def find_glib_compile_schemas(conf): conf.find_program('glib-compile-schemas',var='GLIB_COMPILE_SCHEMAS') def getstr(varname): return getattr(Options.options,varname,getattr(conf.env,varname,'')) gsettingsschemadir=getstr('GSETTINGSSCHEMADIR') if not gsettingsschemadir: datadir=getstr('DATADIR') if not datadir: prefix=conf.env.PREFIX datadir=os.path.join(prefix,'share') gsettingsschemadir=os.path.join(datadir,'glib-2.0','schemas') conf.env.GSETTINGSSCHEMADIR=gsettingsschemadir @conf def find_glib_compile_resources(conf): conf.find_program('glib-compile-resources',var='GLIB_COMPILE_RESOURCES') def configure(conf): conf.find_glib_genmarshal() conf.find_glib_mkenums() conf.find_glib_compile_schemas(mandatory=False) conf.find_glib_compile_resources(mandatory=False) def options(opt): gr=opt.add_option_group('Installation directories') gr.add_option('--gsettingsschemadir',help='GSettings schema location [DATADIR/glib-2.0/schemas]',default='',dest='GSETTINGSSCHEMADIR') ntpsec-1.1.0+dfsg1/waflib/Tools/ccroot.py0000644000175000017500000003621113210576031020031 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re from waflib import Task,Utils,Node,Errors,Logs from waflib.TaskGen import after_method,before_method,feature,taskgen_method,extension from waflib.Tools import c_aliases,c_preproc,c_config,c_osx,c_tests from waflib.Configure import conf SYSTEM_LIB_PATHS=['/usr/lib64','/usr/lib','/usr/local/lib64','/usr/local/lib'] USELIB_VARS=Utils.defaultdict(set) USELIB_VARS['c']=set(['INCLUDES','FRAMEWORKPATH','DEFINES','CPPFLAGS','CCDEPS','CFLAGS','ARCH']) USELIB_VARS['cxx']=set(['INCLUDES','FRAMEWORKPATH','DEFINES','CPPFLAGS','CXXDEPS','CXXFLAGS','ARCH']) USELIB_VARS['d']=set(['INCLUDES','DFLAGS']) USELIB_VARS['includes']=set(['INCLUDES','FRAMEWORKPATH','ARCH']) USELIB_VARS['cprogram']=USELIB_VARS['cxxprogram']=set(['LIB','STLIB','LIBPATH','STLIBPATH','LINKFLAGS','RPATH','LINKDEPS','FRAMEWORK','FRAMEWORKPATH','ARCH','LDFLAGS']) USELIB_VARS['cshlib']=USELIB_VARS['cxxshlib']=set(['LIB','STLIB','LIBPATH','STLIBPATH','LINKFLAGS','RPATH','LINKDEPS','FRAMEWORK','FRAMEWORKPATH','ARCH','LDFLAGS']) USELIB_VARS['cstlib']=USELIB_VARS['cxxstlib']=set(['ARFLAGS','LINKDEPS']) USELIB_VARS['dprogram']=set(['LIB','STLIB','LIBPATH','STLIBPATH','LINKFLAGS','RPATH','LINKDEPS']) USELIB_VARS['dshlib']=set(['LIB','STLIB','LIBPATH','STLIBPATH','LINKFLAGS','RPATH','LINKDEPS']) USELIB_VARS['dstlib']=set(['ARFLAGS','LINKDEPS']) USELIB_VARS['asm']=set(['ASFLAGS']) @taskgen_method def create_compiled_task(self,name,node): out='%s.%d.o'%(node.name,self.idx) task=self.create_task(name,node,node.parent.find_or_declare(out)) try: self.compiled_tasks.append(task) except AttributeError: self.compiled_tasks=[task] return task @taskgen_method def to_incnodes(self,inlst): lst=[] seen=set() for x in self.to_list(inlst): if x in seen or not x: continue seen.add(x) if isinstance(x,Node.Node): lst.append(x) else: if os.path.isabs(x): lst.append(self.bld.root.make_node(x)or x) else: if x[0]=='#': p=self.bld.bldnode.make_node(x[1:]) v=self.bld.srcnode.make_node(x[1:]) else: p=self.path.get_bld().make_node(x) v=self.path.make_node(x) if p.is_child_of(self.bld.bldnode): p.mkdir() lst.append(p) lst.append(v) return lst @feature('c','cxx','d','asm','fc','includes') @after_method('propagate_uselib_vars','process_source') def apply_incpaths(self): lst=self.to_incnodes(self.to_list(getattr(self,'includes',[]))+self.env.INCLUDES) self.includes_nodes=lst cwd=self.get_cwd() self.env.INCPATHS=[x.path_from(cwd)for x in lst] class link_task(Task.Task): color='YELLOW' inst_to=None chmod=Utils.O755 def add_target(self,target): if isinstance(target,str): base=self.generator.path if target.startswith('#'): target=target[1:] base=self.generator.bld.bldnode pattern=self.env[self.__class__.__name__+'_PATTERN'] if not pattern: pattern='%s' folder,name=os.path.split(target) if self.__class__.__name__.find('shlib')>0 and getattr(self.generator,'vnum',None): nums=self.generator.vnum.split('.') if self.env.DEST_BINFMT=='pe': name=name+'-'+nums[0] elif self.env.DEST_OS=='openbsd': pattern='%s.%s'%(pattern,nums[0]) if len(nums)>=2: pattern+='.%s'%nums[1] if folder: tmp=folder+os.sep+pattern%name else: tmp=pattern%name target=base.find_or_declare(tmp) self.set_outputs(target) def exec_command(self,*k,**kw): ret=super(link_task,self).exec_command(*k,**kw) if not ret and self.env.DO_MANIFEST: ret=self.exec_mf() return ret def exec_mf(self): if not self.env.MT: return 0 manifest=None for out_node in self.outputs: if out_node.name.endswith('.manifest'): manifest=out_node.abspath() break else: return 0 mode='' for x in Utils.to_list(self.generator.features): if x in('cprogram','cxxprogram','fcprogram','fcprogram_test'): mode=1 elif x in('cshlib','cxxshlib','fcshlib'): mode=2 Logs.debug('msvc: embedding manifest in mode %r',mode) lst=[]+self.env.MT lst.extend(Utils.to_list(self.env.MTFLAGS)) lst.extend(['-manifest',manifest]) lst.append('-outputresource:%s;%s'%(self.outputs[0].abspath(),mode)) return super(link_task,self).exec_command(lst) class stlink_task(link_task): run_str='${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}' chmod=Utils.O644 def rm_tgt(cls): old=cls.run def wrap(self): try: os.remove(self.outputs[0].abspath()) except OSError: pass return old(self) setattr(cls,'run',wrap) rm_tgt(stlink_task) @feature('c','cxx','d','fc','asm') @after_method('process_source') def apply_link(self): for x in self.features: if x=='cprogram'and'cxx'in self.features: x='cxxprogram' elif x=='cshlib'and'cxx'in self.features: x='cxxshlib' if x in Task.classes: if issubclass(Task.classes[x],link_task): link=x break else: return objs=[t.outputs[0]for t in getattr(self,'compiled_tasks',[])] self.link_task=self.create_task(link,objs) self.link_task.add_target(self.target) try: inst_to=self.install_path except AttributeError: inst_to=self.link_task.__class__.inst_to if inst_to: self.install_task=self.add_install_files(install_to=inst_to,install_from=self.link_task.outputs[:],chmod=self.link_task.chmod,task=self.link_task) @taskgen_method def use_rec(self,name,**kw): if name in self.tmp_use_not or name in self.tmp_use_seen: return try: y=self.bld.get_tgen_by_name(name) except Errors.WafError: self.uselib.append(name) self.tmp_use_not.add(name) return self.tmp_use_seen.append(name) y.post() y.tmp_use_objects=objects=kw.get('objects',True) y.tmp_use_stlib=stlib=kw.get('stlib',True) try: link_task=y.link_task except AttributeError: y.tmp_use_var='' else: objects=False if not isinstance(link_task,stlink_task): stlib=False y.tmp_use_var='LIB' else: y.tmp_use_var='STLIB' p=self.tmp_use_prec for x in self.to_list(getattr(y,'use',[])): if self.env["STLIB_"+x]: continue try: p[x].append(name) except KeyError: p[x]=[name] self.use_rec(x,objects=objects,stlib=stlib) @feature('c','cxx','d','use','fc') @before_method('apply_incpaths','propagate_uselib_vars') @after_method('apply_link','process_source') def process_use(self): use_not=self.tmp_use_not=set() self.tmp_use_seen=[] use_prec=self.tmp_use_prec={} self.uselib=self.to_list(getattr(self,'uselib',[])) self.includes=self.to_list(getattr(self,'includes',[])) names=self.to_list(getattr(self,'use',[])) for x in names: self.use_rec(x) for x in use_not: if x in use_prec: del use_prec[x] out=self.tmp_use_sorted=[] tmp=[] for x in self.tmp_use_seen: for k in use_prec.values(): if x in k: break else: tmp.append(x) while tmp: e=tmp.pop() out.append(e) try: nlst=use_prec[e] except KeyError: pass else: del use_prec[e] for x in nlst: for y in use_prec: if x in use_prec[y]: break else: tmp.append(x) if use_prec: raise Errors.WafError('Cycle detected in the use processing %r'%use_prec) out.reverse() link_task=getattr(self,'link_task',None) for x in out: y=self.bld.get_tgen_by_name(x) var=y.tmp_use_var if var and link_task: if var=='LIB'or y.tmp_use_stlib or x in names: self.env.append_value(var,[y.target[y.target.rfind(os.sep)+1:]]) self.link_task.dep_nodes.extend(y.link_task.outputs) tmp_path=y.link_task.outputs[0].parent.path_from(self.get_cwd()) self.env.append_unique(var+'PATH',[tmp_path]) else: if y.tmp_use_objects: self.add_objects_from_tgen(y) if getattr(y,'export_includes',None): self.includes=self.includes+y.to_incnodes(y.export_includes) if getattr(y,'export_defines',None): self.env.append_value('DEFINES',self.to_list(y.export_defines)) for x in names: try: y=self.bld.get_tgen_by_name(x) except Errors.WafError: if not self.env['STLIB_'+x]and not x in self.uselib: self.uselib.append(x) else: for k in self.to_list(getattr(y,'use',[])): if not self.env['STLIB_'+k]and not k in self.uselib: self.uselib.append(k) @taskgen_method def accept_node_to_link(self,node): return not node.name.endswith('.pdb') @taskgen_method def add_objects_from_tgen(self,tg): try: link_task=self.link_task except AttributeError: pass else: for tsk in getattr(tg,'compiled_tasks',[]): for x in tsk.outputs: if self.accept_node_to_link(x): link_task.inputs.append(x) @taskgen_method def get_uselib_vars(self): _vars=set() for x in self.features: if x in USELIB_VARS: _vars|=USELIB_VARS[x] return _vars @feature('c','cxx','d','fc','javac','cs','uselib','asm') @after_method('process_use') def propagate_uselib_vars(self): _vars=self.get_uselib_vars() env=self.env app=env.append_value feature_uselib=self.features+self.to_list(getattr(self,'uselib',[])) for var in _vars: y=var.lower() val=getattr(self,y,[]) if val: app(var,self.to_list(val)) for x in feature_uselib: val=env['%s_%s'%(var,x)] if val: app(var,val) @feature('cshlib','cxxshlib','fcshlib') @after_method('apply_link') def apply_implib(self): if not self.env.DEST_BINFMT=='pe': return dll=self.link_task.outputs[0] if isinstance(self.target,Node.Node): name=self.target.name else: name=os.path.split(self.target)[1] implib=self.env.implib_PATTERN%name implib=dll.parent.find_or_declare(implib) self.env.append_value('LINKFLAGS',self.env.IMPLIB_ST%implib.bldpath()) self.link_task.outputs.append(implib) if getattr(self,'defs',None)and self.env.DEST_BINFMT=='pe': node=self.path.find_resource(self.defs) if not node: raise Errors.WafError('invalid def file %r'%self.defs) if'msvc'in(self.env.CC_NAME,self.env.CXX_NAME): self.env.append_value('LINKFLAGS','/def:%s'%node.path_from(self.get_cwd())) self.link_task.dep_nodes.append(node) else: self.link_task.inputs.append(node) if getattr(self,'install_task',None): try: inst_to=self.install_path_implib except AttributeError: try: inst_to=self.install_path except AttributeError: inst_to='${IMPLIBDIR}' self.install_task.install_to='${BINDIR}' if not self.env.IMPLIBDIR: self.env.IMPLIBDIR=self.env.LIBDIR self.implib_install_task=self.add_install_files(install_to=inst_to,install_from=implib,chmod=self.link_task.chmod,task=self.link_task) re_vnum=re.compile('^([1-9]\\d*|0)([.]([1-9]\\d*|0)){0,2}?$') @feature('cshlib','cxxshlib','dshlib','fcshlib','vnum') @after_method('apply_link','propagate_uselib_vars') def apply_vnum(self): if not getattr(self,'vnum','')or os.name!='posix'or self.env.DEST_BINFMT not in('elf','mac-o'): return link=self.link_task if not re_vnum.match(self.vnum): raise Errors.WafError('Invalid vnum %r for target %r'%(self.vnum,getattr(self,'name',self))) nums=self.vnum.split('.') node=link.outputs[0] cnum=getattr(self,'cnum',str(nums[0])) cnums=cnum.split('.') if len(cnums)>len(nums)or nums[0:len(cnums)]!=cnums: raise Errors.WafError('invalid compatibility version %s'%cnum) libname=node.name if libname.endswith('.dylib'): name3=libname.replace('.dylib','.%s.dylib'%self.vnum) name2=libname.replace('.dylib','.%s.dylib'%cnum) else: name3=libname+'.'+self.vnum name2=libname+'.'+cnum if self.env.SONAME_ST: v=self.env.SONAME_ST%name2 self.env.append_value('LINKFLAGS',v.split()) if self.env.DEST_OS!='openbsd': outs=[node.parent.make_node(name3)] if name2!=name3: outs.append(node.parent.make_node(name2)) self.create_task('vnum',node,outs) if getattr(self,'install_task',None): self.install_task.hasrun=Task.SKIPPED path=self.install_task.install_to if self.env.DEST_OS=='openbsd': libname=self.link_task.outputs[0].name t1=self.add_install_as(install_to='%s/%s'%(path,libname),install_from=node,chmod=self.link_task.chmod) self.vnum_install_task=(t1,) else: t1=self.add_install_as(install_to=path+os.sep+name3,install_from=node,chmod=self.link_task.chmod) t3=self.add_symlink_as(install_to=path+os.sep+libname,install_from=name3) if name2!=name3: t2=self.add_symlink_as(install_to=path+os.sep+name2,install_from=name3) self.vnum_install_task=(t1,t2,t3) else: self.vnum_install_task=(t1,t3) if'-dynamiclib'in self.env.LINKFLAGS: try: inst_to=self.install_path except AttributeError: inst_to=self.link_task.__class__.inst_to if inst_to: p=Utils.subst_vars(inst_to,self.env) path=os.path.join(p,name2) self.env.append_value('LINKFLAGS',['-install_name',path]) self.env.append_value('LINKFLAGS','-Wl,-compatibility_version,%s'%cnum) self.env.append_value('LINKFLAGS','-Wl,-current_version,%s'%self.vnum) class vnum(Task.Task): color='CYAN' ext_in=['.bin'] def keyword(self): return'Symlinking' def run(self): for x in self.outputs: path=x.abspath() try: os.remove(path) except OSError: pass try: os.symlink(self.inputs[0].name,path) except OSError: return 1 class fake_shlib(link_task): def runnable_status(self): for t in self.run_after: if not t.hasrun: return Task.ASK_LATER return Task.SKIP_ME class fake_stlib(stlink_task): def runnable_status(self): for t in self.run_after: if not t.hasrun: return Task.ASK_LATER return Task.SKIP_ME @conf def read_shlib(self,name,paths=[],export_includes=[],export_defines=[]): return self(name=name,features='fake_lib',lib_paths=paths,lib_type='shlib',export_includes=export_includes,export_defines=export_defines) @conf def read_stlib(self,name,paths=[],export_includes=[],export_defines=[]): return self(name=name,features='fake_lib',lib_paths=paths,lib_type='stlib',export_includes=export_includes,export_defines=export_defines) lib_patterns={'shlib':['lib%s.so','%s.so','lib%s.dylib','lib%s.dll','%s.dll'],'stlib':['lib%s.a','%s.a','lib%s.dll','%s.dll','lib%s.lib','%s.lib'],} @feature('fake_lib') def process_lib(self): node=None names=[x%self.name for x in lib_patterns[self.lib_type]] for x in self.lib_paths+[self.path]+SYSTEM_LIB_PATHS: if not isinstance(x,Node.Node): x=self.bld.root.find_node(x)or self.path.find_node(x) if not x: continue for y in names: node=x.find_node(y) if node: try: Utils.h_file(node.abspath()) except EnvironmentError: raise ValueError('Could not read %r'%y) break else: continue break else: raise Errors.WafError('could not find library %r'%self.name) self.link_task=self.create_task('fake_%s'%self.lib_type,[],[node]) self.target=self.name class fake_o(Task.Task): def runnable_status(self): return Task.SKIP_ME @extension('.o','.obj') def add_those_o_files(self,node): tsk=self.create_task('fake_o',[],node) try: self.compiled_tasks.append(tsk) except AttributeError: self.compiled_tasks=[tsk] @feature('fake_obj') @before_method('process_source') def process_objs(self): for node in self.to_nodes(self.source): self.add_those_o_files(node) self.source=[] @conf def read_object(self,obj): if not isinstance(obj,self.path.__class__): obj=self.path.find_resource(obj) return self(features='fake_obj',source=obj,name=obj.name) @feature('cxxprogram','cprogram') @after_method('apply_link','process_use') def set_full_paths_hpux(self): if self.env.DEST_OS!='hp-ux': return base=self.bld.bldnode.abspath() for var in['LIBPATH','STLIBPATH']: lst=[] for x in self.env[var]: if x.startswith('/'): lst.append(x) else: lst.append(os.path.normpath(os.path.join(base,x))) self.env[var]=lst ntpsec-1.1.0+dfsg1/waflib/Tools/msvc.py0000644000175000017500000006512013210576031017511 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,sys,re from waflib import Utils,Logs,Options,Errors from waflib.TaskGen import after_method,feature from waflib.Configure import conf from waflib.Tools import ccroot,c,cxx,ar g_msvc_systemlibs=''' aclui activeds ad1 adptif adsiid advapi32 asycfilt authz bhsupp bits bufferoverflowu cabinet cap certadm certidl ciuuid clusapi comctl32 comdlg32 comsupp comsuppd comsuppw comsuppwd comsvcs credui crypt32 cryptnet cryptui d3d8thk daouuid dbgeng dbghelp dciman32 ddao35 ddao35d ddao35u ddao35ud delayimp dhcpcsvc dhcpsapi dlcapi dnsapi dsprop dsuiext dtchelp faultrep fcachdll fci fdi framedyd framedyn gdi32 gdiplus glauxglu32 gpedit gpmuuid gtrts32w gtrtst32hlink htmlhelp httpapi icm32 icmui imagehlp imm32 iphlpapi iprop kernel32 ksguid ksproxy ksuser libcmt libcmtd libcpmt libcpmtd loadperf lz32 mapi mapi32 mgmtapi minidump mmc mobsync mpr mprapi mqoa mqrt msacm32 mscms mscoree msdasc msimg32 msrating mstask msvcmrt msvcurt msvcurtd mswsock msxml2 mtx mtxdm netapi32 nmapinmsupp npptools ntdsapi ntdsbcli ntmsapi ntquery odbc32 odbcbcp odbccp32 oldnames ole32 oleacc oleaut32 oledb oledlgolepro32 opends60 opengl32 osptk parser pdh penter pgobootrun pgort powrprof psapi ptrustm ptrustmd ptrustu ptrustud qosname rasapi32 rasdlg rassapi resutils riched20 rpcndr rpcns4 rpcrt4 rtm rtutils runtmchk scarddlg scrnsave scrnsavw secur32 sensapi setupapi sfc shell32 shfolder shlwapi sisbkup snmpapi sporder srclient sti strsafe svcguid tapi32 thunk32 traffic unicows url urlmon user32 userenv usp10 uuid uxtheme vcomp vcompd vdmdbg version vfw32 wbemuuid webpost wiaguid wininet winmm winscard winspool winstrm wintrust wldap32 wmiutils wow32 ws2_32 wsnmp32 wsock32 wst wtsapi32 xaswitch xolehlp '''.split() all_msvc_platforms=[('x64','amd64'),('x86','x86'),('ia64','ia64'),('x86_amd64','amd64'),('x86_ia64','ia64'),('x86_arm','arm'),('x86_arm64','arm64'),('amd64_x86','x86'),('amd64_arm','arm'),('amd64_arm64','arm64')] all_wince_platforms=[('armv4','arm'),('armv4i','arm'),('mipsii','mips'),('mipsii_fp','mips'),('mipsiv','mips'),('mipsiv_fp','mips'),('sh4','sh'),('x86','cex86')] all_icl_platforms=[('intel64','amd64'),('em64t','amd64'),('ia32','x86'),('Itanium','ia64')] def options(opt): opt.add_option('--msvc_version',type='string',help='msvc version, eg: "msvc 10.0,msvc 9.0"',default='') opt.add_option('--msvc_targets',type='string',help='msvc targets, eg: "x64,arm"',default='') opt.add_option('--no-msvc-lazy',action='store_false',help='lazily check msvc target environments',default=True,dest='msvc_lazy') @conf def setup_msvc(conf,versiondict): platforms=getattr(Options.options,'msvc_targets','').split(',') if platforms==['']: platforms=Utils.to_list(conf.env.MSVC_TARGETS)or[i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms] desired_versions=getattr(Options.options,'msvc_version','').split(',') if desired_versions==['']: desired_versions=conf.env.MSVC_VERSIONS or list(reversed(sorted(versiondict.keys()))) lazy_detect=getattr(Options.options,'msvc_lazy',True) if conf.env.MSVC_LAZY_AUTODETECT is False: lazy_detect=False if not lazy_detect: for val in versiondict.values(): for arch in list(val.keys()): cfg=val[arch] cfg.evaluate() if not cfg.is_valid: del val[arch] conf.env.MSVC_INSTALLED_VERSIONS=versiondict for version in desired_versions: Logs.debug('msvc: detecting %r - %r',version,desired_versions) try: targets=versiondict[version] except KeyError: continue seen=set() for arch in platforms: if arch in seen: continue else: seen.add(arch) try: cfg=targets[arch] except KeyError: continue cfg.evaluate() if cfg.is_valid: compiler,revision=version.rsplit(' ',1) return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu conf.fatal('msvc: Impossible to find a valid architecture for building %r - %r'%(desired_versions,list(versiondict.keys()))) @conf def get_msvc_version(conf,compiler,version,target,vcvars): Logs.debug('msvc: get_msvc_version: %r %r %r',compiler,version,target) try: conf.msvc_cnt+=1 except AttributeError: conf.msvc_cnt=1 batfile=conf.bldnode.make_node('waf-print-msvc-%d.bat'%conf.msvc_cnt) batfile.write("""@echo off set INCLUDE= set LIB= call "%s" %s echo PATH=%%PATH%% echo INCLUDE=%%INCLUDE%% echo LIB=%%LIB%%;%%LIBPATH%% """%(vcvars,target)) sout=conf.cmd_and_log(['cmd.exe','/E:on','/V:on','/C',batfile.abspath()]) lines=sout.splitlines() if not lines[0]: lines.pop(0) MSVC_PATH=MSVC_INCDIR=MSVC_LIBDIR=None for line in lines: if line.startswith('PATH='): path=line[5:] MSVC_PATH=path.split(';') elif line.startswith('INCLUDE='): MSVC_INCDIR=[i for i in line[8:].split(';')if i] elif line.startswith('LIB='): MSVC_LIBDIR=[i for i in line[4:].split(';')if i] if None in(MSVC_PATH,MSVC_INCDIR,MSVC_LIBDIR): conf.fatal('msvc: Could not find a valid architecture for building (get_msvc_version_3)') env=dict(os.environ) env.update(PATH=path) compiler_name,linker_name,lib_name=_get_prog_names(conf,compiler) cxx=conf.find_program(compiler_name,path_list=MSVC_PATH) if'CL'in env: del(env['CL']) try: conf.cmd_and_log(cxx+['/help'],env=env) except UnicodeError: st=Utils.ex_stack() if conf.logger: conf.logger.error(st) conf.fatal('msvc: Unicode error - check the code page?') except Exception as e: Logs.debug('msvc: get_msvc_version: %r %r %r -> failure %s',compiler,version,target,str(e)) conf.fatal('msvc: cannot run the compiler in get_msvc_version (run with -v to display errors)') else: Logs.debug('msvc: get_msvc_version: %r %r %r -> OK',compiler,version,target) finally: conf.env[compiler_name]='' return(MSVC_PATH,MSVC_INCDIR,MSVC_LIBDIR) @conf def gather_wsdk_versions(conf,versions): version_pattern=re.compile('^v..?.?\...?.?') try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Wow6432node\\Microsoft\\Microsoft SDKs\\Windows') except WindowsError: try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows') except WindowsError: return index=0 while 1: try: version=Utils.winreg.EnumKey(all_versions,index) except WindowsError: break index+=1 if not version_pattern.match(version): continue try: msvc_version=Utils.winreg.OpenKey(all_versions,version) path,type=Utils.winreg.QueryValueEx(msvc_version,'InstallationFolder') except WindowsError: continue if path and os.path.isfile(os.path.join(path,'bin','SetEnv.cmd')): targets={} for target,arch in all_msvc_platforms: targets[target]=target_compiler(conf,'wsdk',arch,version,'/'+target,os.path.join(path,'bin','SetEnv.cmd')) versions['wsdk '+version[1:]]=targets def gather_wince_supported_platforms(): supported_wince_platforms=[] try: ce_sdk=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Wow6432node\\Microsoft\\Windows CE Tools\\SDKs') except WindowsError: try: ce_sdk=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Microsoft\\Windows CE Tools\\SDKs') except WindowsError: ce_sdk='' if not ce_sdk: return supported_wince_platforms index=0 while 1: try: sdk_device=Utils.winreg.EnumKey(ce_sdk,index) sdk=Utils.winreg.OpenKey(ce_sdk,sdk_device) except WindowsError: break index+=1 try: path,type=Utils.winreg.QueryValueEx(sdk,'SDKRootDir') except WindowsError: try: path,type=Utils.winreg.QueryValueEx(sdk,'SDKInformation') except WindowsError: continue path,xml=os.path.split(path) path=str(path) path,device=os.path.split(path) if not device: path,device=os.path.split(path) platforms=[] for arch,compiler in all_wince_platforms: if os.path.isdir(os.path.join(path,device,'Lib',arch)): platforms.append((arch,compiler,os.path.join(path,device,'Include',arch),os.path.join(path,device,'Lib',arch))) if platforms: supported_wince_platforms.append((device,platforms)) return supported_wince_platforms def gather_msvc_detected_versions(): version_pattern=re.compile('^(\d\d?\.\d\d?)(Exp)?$') detected_versions=[] for vcver,vcvar in(('VCExpress','Exp'),('VisualStudio','')): prefix='SOFTWARE\\Wow6432node\\Microsoft\\'+vcver try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,prefix) except WindowsError: prefix='SOFTWARE\\Microsoft\\'+vcver try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,prefix) except WindowsError: continue index=0 while 1: try: version=Utils.winreg.EnumKey(all_versions,index) except WindowsError: break index+=1 match=version_pattern.match(version) if match: versionnumber=float(match.group(1)) else: continue detected_versions.append((versionnumber,version+vcvar,prefix+'\\'+version)) def fun(tup): return tup[0] detected_versions.sort(key=fun) return detected_versions class target_compiler(object): def __init__(self,ctx,compiler,cpu,version,bat_target,bat,callback=None): self.conf=ctx self.name=None self.is_valid=False self.is_done=False self.compiler=compiler self.cpu=cpu self.version=version self.bat_target=bat_target self.bat=bat self.callback=callback def evaluate(self): if self.is_done: return self.is_done=True try: vs=self.conf.get_msvc_version(self.compiler,self.version,self.bat_target,self.bat) except Errors.ConfigurationError: self.is_valid=False return if self.callback: vs=self.callback(self,vs) self.is_valid=True (self.bindirs,self.incdirs,self.libdirs)=vs def __str__(self): return str((self.compiler,self.cpu,self.version,self.bat_target,self.bat)) def __repr__(self): return repr((self.compiler,self.cpu,self.version,self.bat_target,self.bat)) @conf def gather_msvc_targets(conf,versions,version,vc_path): targets={} if os.path.isfile(os.path.join(vc_path,'VC','Auxiliary','Build','vcvarsall.bat')): for target,realtarget in all_msvc_platforms[::-1]: targets[target]=target_compiler(conf,'msvc',realtarget,version,target,os.path.join(vc_path,'VC','Auxiliary','Build','vcvarsall.bat')) elif os.path.isfile(os.path.join(vc_path,'vcvarsall.bat')): for target,realtarget in all_msvc_platforms[::-1]: targets[target]=target_compiler(conf,'msvc',realtarget,version,target,os.path.join(vc_path,'vcvarsall.bat')) elif os.path.isfile(os.path.join(vc_path,'Common7','Tools','vsvars32.bat')): targets['x86']=target_compiler(conf,'msvc','x86',version,'x86',os.path.join(vc_path,'Common7','Tools','vsvars32.bat')) elif os.path.isfile(os.path.join(vc_path,'Bin','vcvars32.bat')): targets['x86']=target_compiler(conf,'msvc','x86',version,'',os.path.join(vc_path,'Bin','vcvars32.bat')) if targets: versions['msvc %s'%version]=targets @conf def gather_wince_targets(conf,versions,version,vc_path,vsvars,supported_platforms): for device,platforms in supported_platforms: targets={} for platform,compiler,include,lib in platforms: winCEpath=os.path.join(vc_path,'ce') if not os.path.isdir(winCEpath): continue if os.path.isdir(os.path.join(winCEpath,'lib',platform)): bindirs=[os.path.join(winCEpath,'bin',compiler),os.path.join(winCEpath,'bin','x86_'+compiler)] incdirs=[os.path.join(winCEpath,'include'),os.path.join(winCEpath,'atlmfc','include'),include] libdirs=[os.path.join(winCEpath,'lib',platform),os.path.join(winCEpath,'atlmfc','lib',platform),lib] def combine_common(obj,compiler_env): (common_bindirs,_1,_2)=compiler_env return(bindirs+common_bindirs,incdirs,libdirs) targets[platform]=target_compiler(conf,'msvc',platform,version,'x86',vsvars,combine_common) if targets: versions[device+' '+version]=targets @conf def gather_winphone_targets(conf,versions,version,vc_path,vsvars): targets={} for target,realtarget in all_msvc_platforms[::-1]: targets[target]=target_compiler(conf,'winphone',realtarget,version,target,vsvars) if targets: versions['winphone '+version]=targets @conf def gather_vswhere_versions(conf,versions): try: import json except ImportError: Logs.error('Visual Studio 2017 detection requires Python 2.6') return prg_path=os.environ.get('ProgramFiles(x86)',os.environ.get('ProgramFiles','C:\\Program Files (x86)')) vswhere=os.path.join(prg_path,'Microsoft Visual Studio','Installer','vswhere.exe') args=[vswhere,'-products','*','-legacy','-format','json'] try: txt=conf.cmd_and_log(args) except Errors.WafError as e: Logs.debug('msvc: vswhere.exe failed %s',e) return if sys.version_info[0]<3: try: txt=txt.decode(sys.stdout.encoding or'cp1252') except UnicodeError: txt=txt.decode('utf-8','replace') arr=json.loads(txt) arr.sort(key=lambda x:x['installationVersion']) for entry in arr: ver=entry['installationVersion'] ver=str('.'.join(ver.split('.')[:2])) path=str(os.path.abspath(entry['installationPath'])) if os.path.exists(path)and('msvc %s'%ver)not in versions: conf.gather_msvc_targets(versions,ver,path) @conf def gather_msvc_versions(conf,versions): vc_paths=[] for(v,version,reg)in gather_msvc_detected_versions(): try: try: msvc_version=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,reg+"\\Setup\\VC") except WindowsError: msvc_version=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,reg+"\\Setup\\Microsoft Visual C++") path,type=Utils.winreg.QueryValueEx(msvc_version,'ProductDir') except WindowsError: try: msvc_version=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,"SOFTWARE\\Wow6432node\\Microsoft\\VisualStudio\\SxS\\VS7") path,type=Utils.winreg.QueryValueEx(msvc_version,version) except WindowsError: continue else: vc_paths.append((version,os.path.abspath(str(path)))) continue else: vc_paths.append((version,os.path.abspath(str(path)))) wince_supported_platforms=gather_wince_supported_platforms() for version,vc_path in vc_paths: vs_path=os.path.dirname(vc_path) vsvars=os.path.join(vs_path,'Common7','Tools','vsvars32.bat') if wince_supported_platforms and os.path.isfile(vsvars): conf.gather_wince_targets(versions,version,vc_path,vsvars,wince_supported_platforms) for version,vc_path in vc_paths: vs_path=os.path.dirname(vc_path) vsvars=os.path.join(vs_path,'VC','WPSDK','WP80','vcvarsphoneall.bat') if os.path.isfile(vsvars): conf.gather_winphone_targets(versions,'8.0',vc_path,vsvars) break for version,vc_path in vc_paths: vs_path=os.path.dirname(vc_path) conf.gather_msvc_targets(versions,version,vc_path) @conf def gather_icl_versions(conf,versions): version_pattern=re.compile('^...?.?\....?.?') try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Wow6432node\\Intel\\Compilers\\C++') except WindowsError: try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Intel\\Compilers\\C++') except WindowsError: return index=0 while 1: try: version=Utils.winreg.EnumKey(all_versions,index) except WindowsError: break index+=1 if not version_pattern.match(version): continue targets={} for target,arch in all_icl_platforms: if target=='intel64': targetDir='EM64T_NATIVE' else: targetDir=target try: Utils.winreg.OpenKey(all_versions,version+'\\'+targetDir) icl_version=Utils.winreg.OpenKey(all_versions,version) path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir') except WindowsError: pass else: batch_file=os.path.join(path,'bin','iclvars.bat') if os.path.isfile(batch_file): targets[target]=target_compiler(conf,'intel',arch,version,target,batch_file) for target,arch in all_icl_platforms: try: icl_version=Utils.winreg.OpenKey(all_versions,version+'\\'+target) path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir') except WindowsError: continue else: batch_file=os.path.join(path,'bin','iclvars.bat') if os.path.isfile(batch_file): targets[target]=target_compiler(conf,'intel',arch,version,target,batch_file) major=version[0:2] versions['intel '+major]=targets @conf def gather_intel_composer_versions(conf,versions): version_pattern=re.compile('^...?.?\...?.?.?') try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Wow6432node\\Intel\\Suites') except WindowsError: try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Intel\\Suites') except WindowsError: return index=0 while 1: try: version=Utils.winreg.EnumKey(all_versions,index) except WindowsError: break index+=1 if not version_pattern.match(version): continue targets={} for target,arch in all_icl_platforms: if target=='intel64': targetDir='EM64T_NATIVE' else: targetDir=target try: try: defaults=Utils.winreg.OpenKey(all_versions,version+'\\Defaults\\C++\\'+targetDir) except WindowsError: if targetDir=='EM64T_NATIVE': defaults=Utils.winreg.OpenKey(all_versions,version+'\\Defaults\\C++\\EM64T') else: raise uid,type=Utils.winreg.QueryValueEx(defaults,'SubKey') Utils.winreg.OpenKey(all_versions,version+'\\'+uid+'\\C++\\'+targetDir) icl_version=Utils.winreg.OpenKey(all_versions,version+'\\'+uid+'\\C++') path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir') except WindowsError: pass else: batch_file=os.path.join(path,'bin','iclvars.bat') if os.path.isfile(batch_file): targets[target]=target_compiler(conf,'intel',arch,version,target,batch_file) compilervars_warning_attr='_compilervars_warning_key' if version[0:2]=='13'and getattr(conf,compilervars_warning_attr,True): setattr(conf,compilervars_warning_attr,False) patch_url='http://software.intel.com/en-us/forums/topic/328487' compilervars_arch=os.path.join(path,'bin','compilervars_arch.bat') for vscomntool in('VS110COMNTOOLS','VS100COMNTOOLS'): if vscomntool in os.environ: vs_express_path=os.environ[vscomntool]+r'..\IDE\VSWinExpress.exe' dev_env_path=os.environ[vscomntool]+r'..\IDE\devenv.exe' if(r'if exist "%VS110COMNTOOLS%..\IDE\VSWinExpress.exe"'in Utils.readf(compilervars_arch)and not os.path.exists(vs_express_path)and not os.path.exists(dev_env_path)): Logs.warn(('The Intel compilervar_arch.bat only checks for one Visual Studio SKU ''(VSWinExpress.exe) but it does not seem to be installed at %r. ''The intel command line set up will fail to configure unless the file %r''is patched. See: %s')%(vs_express_path,compilervars_arch,patch_url)) major=version[0:2] versions['intel '+major]=targets @conf def detect_msvc(self): return self.setup_msvc(self.get_msvc_versions()) @conf def get_msvc_versions(self): dct=Utils.ordered_iter_dict() self.gather_icl_versions(dct) self.gather_intel_composer_versions(dct) self.gather_wsdk_versions(dct) self.gather_msvc_versions(dct) self.gather_vswhere_versions(dct) Logs.debug('msvc: detected versions %r',list(dct.keys())) return dct @conf def find_lt_names_msvc(self,libname,is_static=False): lt_names=['lib%s.la'%libname,'%s.la'%libname,] for path in self.env.LIBPATH: for la in lt_names: laf=os.path.join(path,la) dll=None if os.path.exists(laf): ltdict=Utils.read_la_file(laf) lt_libdir=None if ltdict.get('libdir',''): lt_libdir=ltdict['libdir'] if not is_static and ltdict.get('library_names',''): dllnames=ltdict['library_names'].split() dll=dllnames[0].lower() dll=re.sub('\.dll$','',dll) return(lt_libdir,dll,False) elif ltdict.get('old_library',''): olib=ltdict['old_library'] if os.path.exists(os.path.join(path,olib)): return(path,olib,True) elif lt_libdir!=''and os.path.exists(os.path.join(lt_libdir,olib)): return(lt_libdir,olib,True) else: return(None,olib,True) else: raise self.errors.WafError('invalid libtool object file: %s'%laf) return(None,None,None) @conf def libname_msvc(self,libname,is_static=False): lib=libname.lower() lib=re.sub('\.lib$','',lib) if lib in g_msvc_systemlibs: return lib lib=re.sub('^lib','',lib) if lib=='m': return None (lt_path,lt_libname,lt_static)=self.find_lt_names_msvc(lib,is_static) if lt_path!=None and lt_libname!=None: if lt_static==True: return os.path.join(lt_path,lt_libname) if lt_path!=None: _libpaths=[lt_path]+self.env.LIBPATH else: _libpaths=self.env.LIBPATH static_libs=['lib%ss.lib'%lib,'lib%s.lib'%lib,'%ss.lib'%lib,'%s.lib'%lib,] dynamic_libs=['lib%s.dll.lib'%lib,'lib%s.dll.a'%lib,'%s.dll.lib'%lib,'%s.dll.a'%lib,'lib%s_d.lib'%lib,'%s_d.lib'%lib,'%s.lib'%lib,] libnames=static_libs if not is_static: libnames=dynamic_libs+static_libs for path in _libpaths: for libn in libnames: if os.path.exists(os.path.join(path,libn)): Logs.debug('msvc: lib found: %s',os.path.join(path,libn)) return re.sub('\.lib$','',libn) self.fatal('The library %r could not be found'%libname) return re.sub('\.lib$','',libname) @conf def check_lib_msvc(self,libname,is_static=False,uselib_store=None): libn=self.libname_msvc(libname,is_static) if not uselib_store: uselib_store=libname.upper() if False and is_static: self.env['STLIB_'+uselib_store]=[libn] else: self.env['LIB_'+uselib_store]=[libn] @conf def check_libs_msvc(self,libnames,is_static=False): for libname in Utils.to_list(libnames): self.check_lib_msvc(libname,is_static) def configure(conf): conf.autodetect(True) conf.find_msvc() conf.msvc_common_flags() conf.cc_load_tools() conf.cxx_load_tools() conf.cc_add_flags() conf.cxx_add_flags() conf.link_add_flags() conf.visual_studio_add_flags() @conf def no_autodetect(conf): conf.env.NO_MSVC_DETECT=1 configure(conf) @conf def autodetect(conf,arch=False): v=conf.env if v.NO_MSVC_DETECT: return compiler,version,path,includes,libdirs,cpu=conf.detect_msvc() if arch: v.DEST_CPU=cpu v.PATH=path v.INCLUDES=includes v.LIBPATH=libdirs v.MSVC_COMPILER=compiler try: v.MSVC_VERSION=float(version) except ValueError: v.MSVC_VERSION=float(version[:-3]) def _get_prog_names(conf,compiler): if compiler=='intel': compiler_name='ICL' linker_name='XILINK' lib_name='XILIB' else: compiler_name='CL' linker_name='LINK' lib_name='LIB' return compiler_name,linker_name,lib_name @conf def find_msvc(conf): if sys.platform=='cygwin': conf.fatal('MSVC module does not work under cygwin Python!') v=conf.env path=v.PATH compiler=v.MSVC_COMPILER version=v.MSVC_VERSION compiler_name,linker_name,lib_name=_get_prog_names(conf,compiler) v.MSVC_MANIFEST=(compiler=='msvc'and version>=8)or(compiler=='wsdk'and version>=6)or(compiler=='intel'and version>=11) cxx=conf.find_program(compiler_name,var='CXX',path_list=path) env=dict(conf.environ) if path: env.update(PATH=';'.join(path)) if not conf.cmd_and_log(cxx+['/nologo','/help'],env=env): conf.fatal('the msvc compiler could not be identified') v.CC=v.CXX=cxx v.CC_NAME=v.CXX_NAME='msvc' if not v.LINK_CXX: v.LINK_CXX=conf.find_program(linker_name,path_list=path,errmsg='%s was not found (linker)'%linker_name) if not v.LINK_CC: v.LINK_CC=v.LINK_CXX if not v.AR: stliblink=conf.find_program(lib_name,path_list=path,var='AR') if not stliblink: return v.ARFLAGS=['/nologo'] if v.MSVC_MANIFEST: conf.find_program('MT',path_list=path,var='MT') v.MTFLAGS=['/nologo'] try: conf.load('winres') except Errors.ConfigurationError: Logs.warn('Resource compiler not found. Compiling resource file is disabled') @conf def visual_studio_add_flags(self): v=self.env if self.environ.get('INCLUDE'): v.prepend_value('INCLUDES',[x for x in self.environ['INCLUDE'].split(';')if x]) if self.environ.get('LIB'): v.prepend_value('LIBPATH',[x for x in self.environ['LIB'].split(';')if x]) @conf def msvc_common_flags(conf): v=conf.env v.DEST_BINFMT='pe' v.append_value('CFLAGS',['/nologo']) v.append_value('CXXFLAGS',['/nologo']) v.append_value('LINKFLAGS',['/nologo']) v.DEFINES_ST='/D%s' v.CC_SRC_F='' v.CC_TGT_F=['/c','/Fo'] v.CXX_SRC_F='' v.CXX_TGT_F=['/c','/Fo'] if(v.MSVC_COMPILER=='msvc'and v.MSVC_VERSION>=8)or(v.MSVC_COMPILER=='wsdk'and v.MSVC_VERSION>=6): v.CC_TGT_F=['/FC']+v.CC_TGT_F v.CXX_TGT_F=['/FC']+v.CXX_TGT_F v.CPPPATH_ST='/I%s' v.AR_TGT_F=v.CCLNK_TGT_F=v.CXXLNK_TGT_F='/OUT:' v.CFLAGS_CONSOLE=v.CXXFLAGS_CONSOLE=['/SUBSYSTEM:CONSOLE'] v.CFLAGS_NATIVE=v.CXXFLAGS_NATIVE=['/SUBSYSTEM:NATIVE'] v.CFLAGS_POSIX=v.CXXFLAGS_POSIX=['/SUBSYSTEM:POSIX'] v.CFLAGS_WINDOWS=v.CXXFLAGS_WINDOWS=['/SUBSYSTEM:WINDOWS'] v.CFLAGS_WINDOWSCE=v.CXXFLAGS_WINDOWSCE=['/SUBSYSTEM:WINDOWSCE'] v.CFLAGS_CRT_MULTITHREADED=v.CXXFLAGS_CRT_MULTITHREADED=['/MT'] v.CFLAGS_CRT_MULTITHREADED_DLL=v.CXXFLAGS_CRT_MULTITHREADED_DLL=['/MD'] v.CFLAGS_CRT_MULTITHREADED_DBG=v.CXXFLAGS_CRT_MULTITHREADED_DBG=['/MTd'] v.CFLAGS_CRT_MULTITHREADED_DLL_DBG=v.CXXFLAGS_CRT_MULTITHREADED_DLL_DBG=['/MDd'] v.LIB_ST='%s.lib' v.LIBPATH_ST='/LIBPATH:%s' v.STLIB_ST='%s.lib' v.STLIBPATH_ST='/LIBPATH:%s' if v.MSVC_MANIFEST: v.append_value('LINKFLAGS',['/MANIFEST']) v.CFLAGS_cshlib=[] v.CXXFLAGS_cxxshlib=[] v.LINKFLAGS_cshlib=v.LINKFLAGS_cxxshlib=['/DLL'] v.cshlib_PATTERN=v.cxxshlib_PATTERN='%s.dll' v.implib_PATTERN='%s.lib' v.IMPLIB_ST='/IMPLIB:%s' v.LINKFLAGS_cstlib=[] v.cstlib_PATTERN=v.cxxstlib_PATTERN='%s.lib' v.cprogram_PATTERN=v.cxxprogram_PATTERN='%s.exe' @after_method('apply_link') @feature('c','cxx') def apply_flags_msvc(self): if self.env.CC_NAME!='msvc'or not getattr(self,'link_task',None): return is_static=isinstance(self.link_task,ccroot.stlink_task) subsystem=getattr(self,'subsystem','') if subsystem: subsystem='/subsystem:%s'%subsystem flags=is_static and'ARFLAGS'or'LINKFLAGS' self.env.append_value(flags,subsystem) if not is_static: for f in self.env.LINKFLAGS: d=f.lower() if d[1:]=='debug': pdbnode=self.link_task.outputs[0].change_ext('.pdb') self.link_task.outputs.append(pdbnode) if getattr(self,'install_task',None): self.pdb_install_task=self.add_install_files(install_to=self.install_task.install_to,install_from=pdbnode) break @feature('cprogram','cshlib','cxxprogram','cxxshlib') @after_method('apply_link') def apply_manifest(self): if self.env.CC_NAME=='msvc'and self.env.MSVC_MANIFEST and getattr(self,'link_task',None): out_node=self.link_task.outputs[0] man_node=out_node.parent.find_or_declare(out_node.name+'.manifest') self.link_task.outputs.append(man_node) self.env.DO_MANIFEST=True def make_winapp(self,family): append=self.env.append_unique append('DEFINES','WINAPI_FAMILY=%s'%family) append('CXXFLAGS',['/ZW','/TP']) for lib_path in self.env.LIBPATH: append('CXXFLAGS','/AI%s'%lib_path) @feature('winphoneapp') @after_method('process_use') @after_method('propagate_uselib_vars') def make_winphone_app(self): make_winapp(self,'WINAPI_FAMILY_PHONE_APP') self.env.append_unique('LINKFLAGS',['/NODEFAULTLIB:ole32.lib','PhoneAppModelHost.lib']) @feature('winapp') @after_method('process_use') @after_method('propagate_uselib_vars') def make_windows_app(self): make_winapp(self,'WINAPI_FAMILY_DESKTOP_APP') ntpsec-1.1.0+dfsg1/waflib/Tools/xlcxx.py0000644000175000017500000000207413210576007017711 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar from waflib.Configure import conf @conf def find_xlcxx(conf): cxx=conf.find_program(['xlc++_r','xlc++'],var='CXX') conf.get_xlc_version(cxx) conf.env.CXX_NAME='xlc++' @conf def xlcxx_common_flags(conf): v=conf.env v.CXX_SRC_F=[] v.CXX_TGT_F=['-c','-o'] if not v.LINK_CXX: v.LINK_CXX=v.CXX v.CXXLNK_SRC_F=[] v.CXXLNK_TGT_F=['-o'] v.CPPPATH_ST='-I%s' v.DEFINES_ST='-D%s' v.LIB_ST='-l%s' v.LIBPATH_ST='-L%s' v.STLIB_ST='-l%s' v.STLIBPATH_ST='-L%s' v.RPATH_ST='-Wl,-rpath,%s' v.SONAME_ST=[] v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.LINKFLAGS_cxxprogram=['-Wl,-brtl'] v.cxxprogram_PATTERN='%s' v.CXXFLAGS_cxxshlib=['-fPIC'] v.LINKFLAGS_cxxshlib=['-G','-Wl,-brtl,-bexpfull'] v.cxxshlib_PATTERN='lib%s.so' v.LINKFLAGS_cxxstlib=[] v.cxxstlib_PATTERN='lib%s.a' def configure(conf): conf.find_xlcxx() conf.find_ar() conf.xlcxx_common_flags() conf.cxx_load_tools() conf.cxx_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/errcheck.py0000644000175000017500000001423213210576031020325 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file typos={'feature':'features','sources':'source','targets':'target','include':'includes','export_include':'export_includes','define':'defines','importpath':'includes','installpath':'install_path','iscopy':'is_copy',} meths_typos=['__call__','program','shlib','stlib','objects'] import sys from waflib import Logs,Build,Node,Task,TaskGen,ConfigSet,Errors,Utils from waflib.Tools import ccroot def check_same_targets(self): mp=Utils.defaultdict(list) uids={} def check_task(tsk): if not isinstance(tsk,Task.Task): return if hasattr(tsk,'no_errcheck_out'): return for node in tsk.outputs: mp[node].append(tsk) try: uids[tsk.uid()].append(tsk) except KeyError: uids[tsk.uid()]=[tsk] for g in self.groups: for tg in g: try: for tsk in tg.tasks: check_task(tsk) except AttributeError: check_task(tg) dupe=False for(k,v)in mp.items(): if len(v)>1: dupe=True msg='* Node %r is created more than once%s. The task generators are:'%(k,Logs.verbose==1 and" (full message on 'waf -v -v')"or"") Logs.error(msg) for x in v: if Logs.verbose>1: Logs.error(' %d. %r',1+v.index(x),x.generator) else: Logs.error(' %d. %r in %r',1+v.index(x),x.generator.name,getattr(x.generator,'path',None)) Logs.error('If you think that this is an error, set no_errcheck_out on the task instance') if not dupe: for(k,v)in uids.items(): if len(v)>1: Logs.error('* Several tasks use the same identifier. Please check the information on\n https://waf.io/apidocs/Task.html?highlight=uid#waflib.Task.Task.uid') tg_details=tsk.generator.name if Logs.verbose>2: tg_details=tsk.generator for tsk in v: Logs.error(' - object %r (%r) defined in %r',tsk.__class__.__name__,tsk,tg_details) def check_invalid_constraints(self): feat=set() for x in list(TaskGen.feats.values()): feat.union(set(x)) for(x,y)in TaskGen.task_gen.prec.items(): feat.add(x) feat.union(set(y)) ext=set() for x in TaskGen.task_gen.mappings.values(): ext.add(x.__name__) invalid=ext&feat if invalid: Logs.error('The methods %r have invalid annotations: @extension <-> @feature/@before_method/@after_method',list(invalid)) for cls in list(Task.classes.values()): if sys.hexversion>0x3000000 and issubclass(cls,Task.Task)and isinstance(cls.hcode,str): raise Errors.WafError('Class %r has hcode value %r of type , expecting (use Utils.h_cmd() ?)'%(cls,cls.hcode)) for x in('before','after'): for y in Utils.to_list(getattr(cls,x,[])): if not Task.classes.get(y): Logs.error('Erroneous order constraint %r=%r on task class %r',x,y,cls.__name__) if getattr(cls,'rule',None): Logs.error('Erroneous attribute "rule" on task class %r (rename to "run_str")',cls.__name__) def replace(m): oldcall=getattr(Build.BuildContext,m) def call(self,*k,**kw): ret=oldcall(self,*k,**kw) for x in typos: if x in kw: if x=='iscopy'and'subst'in getattr(self,'features',''): continue Logs.error('Fix the typo %r -> %r on %r',x,typos[x],ret) return ret setattr(Build.BuildContext,m,call) def enhance_lib(): for m in meths_typos: replace(m) def ant_glob(self,*k,**kw): if k: lst=Utils.to_list(k[0]) for pat in lst: sp=pat.split('/') if'..'in sp: Logs.error("In ant_glob pattern %r: '..' means 'two dots', not 'parent directory'",k[0]) if'.'in sp: Logs.error("In ant_glob pattern %r: '.' means 'one dot', not 'current directory'",k[0]) if kw.get('remove',True): try: if self.is_child_of(self.ctx.bldnode)and not kw.get('quiet',False): Logs.error('Using ant_glob on the build folder (%r) is dangerous (quiet=True to disable this warning)',self) except AttributeError: pass return self.old_ant_glob(*k,**kw) Node.Node.old_ant_glob=Node.Node.ant_glob Node.Node.ant_glob=ant_glob old=Task.is_before def is_before(t1,t2): ret=old(t1,t2) if ret and old(t2,t1): Logs.error('Contradictory order constraints in classes %r %r',t1,t2) return ret Task.is_before=is_before def check_err_features(self): lst=self.to_list(self.features) if'shlib'in lst: Logs.error('feature shlib -> cshlib, dshlib or cxxshlib') for x in('c','cxx','d','fc'): if not x in lst and lst and lst[0]in[x+y for y in('program','shlib','stlib')]: Logs.error('%r features is probably missing %r',self,x) TaskGen.feature('*')(check_err_features) def check_err_order(self): if not hasattr(self,'rule')and not'subst'in Utils.to_list(self.features): for x in('before','after','ext_in','ext_out'): if hasattr(self,x): Logs.warn('Erroneous order constraint %r on non-rule based task generator %r',x,self) else: for x in('before','after'): for y in self.to_list(getattr(self,x,[])): if not Task.classes.get(y,None): Logs.error('Erroneous order constraint %s=%r on %r (no such class)',x,y,self) TaskGen.feature('*')(check_err_order) def check_compile(self): check_invalid_constraints(self) try: ret=self.orig_compile() finally: check_same_targets(self) return ret Build.BuildContext.orig_compile=Build.BuildContext.compile Build.BuildContext.compile=check_compile def use_rec(self,name,**kw): try: y=self.bld.get_tgen_by_name(name) except Errors.WafError: pass else: idx=self.bld.get_group_idx(self) odx=self.bld.get_group_idx(y) if odx>idx: msg="Invalid 'use' across build groups:" if Logs.verbose>1: msg+='\n target %r\n uses:\n %r'%(self,y) else: msg+=" %r uses %r (try 'waf -v -v' for the full error)"%(self.name,name) raise Errors.WafError(msg) self.orig_use_rec(name,**kw) TaskGen.task_gen.orig_use_rec=TaskGen.task_gen.use_rec TaskGen.task_gen.use_rec=use_rec def _getattr(self,name,default=None): if name=='append'or name=='add': raise Errors.WafError('env.append and env.add do not exist: use env.append_value/env.append_unique') elif name=='prepend': raise Errors.WafError('env.prepend does not exist: use env.prepend_value') if name in self.__slots__: return object.__getattr__(self,name,default) else: return self[name] ConfigSet.ConfigSet.__getattr__=_getattr def options(opt): enhance_lib() ntpsec-1.1.0+dfsg1/waflib/Tools/nobuild.py0000644000175000017500000000041613155273014020175 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Task def build(bld): def run(self): for x in self.outputs: x.write('') for(name,cls)in Task.classes.items(): cls.run=run ntpsec-1.1.0+dfsg1/waflib/Tools/clang.py0000644000175000017500000000104313172024730017620 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar,gcc from waflib.Configure import conf @conf def find_clang(conf): cc=conf.find_program('clang',var='CC') conf.get_cc_version(cc,clang=True) conf.env.CC_NAME='clang' def configure(conf): conf.find_clang() conf.find_program(['llvm-ar','ar'],var='AR') conf.find_ar() conf.gcc_common_flags() conf.gcc_modifier_platform() conf.cc_load_tools() conf.cc_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/asm.py0000644000175000017500000000141513210576007017321 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Task from waflib.Tools.ccroot import link_task,stlink_task from waflib.TaskGen import extension class asm(Task.Task): color='BLUE' run_str='${AS} ${ASFLAGS} ${ASMPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${AS_SRC_F}${SRC} ${AS_TGT_F}${TGT}' @extension('.s','.S','.asm','.ASM','.spp','.SPP') def asm_hook(self,node): return self.create_compiled_task('asm',node) class asmprogram(link_task): run_str='${ASLINK} ${ASLINKFLAGS} ${ASLNK_TGT_F}${TGT} ${ASLNK_SRC_F}${SRC}' ext_out=['.bin'] inst_to='${BINDIR}' class asmshlib(asmprogram): inst_to='${LIBDIR}' class asmstlib(stlink_task): pass def configure(conf): conf.env.ASMPATH_ST='-I%s' ntpsec-1.1.0+dfsg1/waflib/Tools/fc.py0000644000175000017500000000756713210576031017144 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Utils,Task from waflib.Tools import ccroot,fc_config,fc_scan from waflib.TaskGen import extension from waflib.Configure import conf ccroot.USELIB_VARS['fc']=set(['FCFLAGS','DEFINES','INCLUDES','FCPPFLAGS']) ccroot.USELIB_VARS['fcprogram_test']=ccroot.USELIB_VARS['fcprogram']=set(['LIB','STLIB','LIBPATH','STLIBPATH','LINKFLAGS','RPATH','LINKDEPS']) ccroot.USELIB_VARS['fcshlib']=set(['LIB','STLIB','LIBPATH','STLIBPATH','LINKFLAGS','RPATH','LINKDEPS']) ccroot.USELIB_VARS['fcstlib']=set(['ARFLAGS','LINKDEPS']) @extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08') def fc_hook(self,node): return self.create_compiled_task('fc',node) @conf def modfile(conf,name): return{'lower':name.lower()+'.mod','lower.MOD':name.lower()+'.MOD','UPPER.mod':name.upper()+'.mod','UPPER':name.upper()+'.MOD'}[conf.env.FC_MOD_CAPITALIZATION or'lower'] def get_fortran_tasks(tsk): bld=tsk.generator.bld tasks=bld.get_tasks_group(bld.get_group_idx(tsk.generator)) return[x for x in tasks if isinstance(x,fc)and not getattr(x,'nomod',None)and not getattr(x,'mod_fortran_done',None)] class fc(Task.Task): color='GREEN' run_str='${FC} ${FCFLAGS} ${FCINCPATH_ST:INCPATHS} ${FCDEFINES_ST:DEFINES} ${_FCMODOUTFLAGS} ${FC_TGT_F}${TGT[0].abspath()} ${FC_SRC_F}${SRC[0].abspath()} ${FCPPFLAGS}' vars=["FORTRANMODPATHFLAG"] def scan(self): tmp=fc_scan.fortran_parser(self.generator.includes_nodes) tmp.task=self tmp.start(self.inputs[0]) return(tmp.nodes,tmp.names) def runnable_status(self): if getattr(self,'mod_fortran_done',None): return super(fc,self).runnable_status() bld=self.generator.bld lst=get_fortran_tasks(self) for tsk in lst: tsk.mod_fortran_done=True for tsk in lst: ret=tsk.runnable_status() if ret==Task.ASK_LATER: for x in lst: x.mod_fortran_done=None return Task.ASK_LATER ins=Utils.defaultdict(set) outs=Utils.defaultdict(set) for tsk in lst: key=tsk.uid() for x in bld.raw_deps[key]: if x.startswith('MOD@'): name=bld.modfile(x.replace('MOD@','')) node=bld.srcnode.find_or_declare(name) tsk.set_outputs(node) outs[id(node)].add(tsk) for tsk in lst: key=tsk.uid() for x in bld.raw_deps[key]: if x.startswith('USE@'): name=bld.modfile(x.replace('USE@','')) node=bld.srcnode.find_resource(name) if node and node not in tsk.outputs: if not node in bld.node_deps[key]: bld.node_deps[key].append(node) ins[id(node)].add(tsk) for k in ins.keys(): for a in ins[k]: a.run_after.update(outs[k]) tmp=[] for t in outs[k]: tmp.extend(t.outputs) a.dep_nodes.extend(tmp) a.dep_nodes.sort(key=lambda x:x.abspath()) for tsk in lst: try: delattr(tsk,'cache_sig') except AttributeError: pass return super(fc,self).runnable_status() class fcprogram(ccroot.link_task): color='YELLOW' run_str='${FC} ${LINKFLAGS} ${FCLNK_SRC_F}${SRC} ${FCLNK_TGT_F}${TGT[0].abspath()} ${RPATH_ST:RPATH} ${FCSTLIB_MARKER} ${FCSTLIBPATH_ST:STLIBPATH} ${FCSTLIB_ST:STLIB} ${FCSHLIB_MARKER} ${FCLIBPATH_ST:LIBPATH} ${FCLIB_ST:LIB} ${LDFLAGS}' inst_to='${BINDIR}' class fcshlib(fcprogram): inst_to='${LIBDIR}' class fcstlib(ccroot.stlink_task): pass class fcprogram_test(fcprogram): def runnable_status(self): ret=super(fcprogram_test,self).runnable_status() if ret==Task.SKIP_ME: ret=Task.RUN_ME return ret def exec_command(self,cmd,**kw): bld=self.generator.bld kw['shell']=isinstance(cmd,str) kw['stdout']=kw['stderr']=Utils.subprocess.PIPE kw['cwd']=self.get_cwd() bld.out=bld.err='' bld.to_log('command: %s\n'%cmd) kw['output']=0 try: (bld.out,bld.err)=bld.cmd_and_log(cmd,**kw) except Exception: return-1 if bld.out: bld.to_log('out: %s\n'%bld.out) if bld.err: bld.to_log('err: %s\n'%bld.err) ntpsec-1.1.0+dfsg1/waflib/Tools/c_osx.py0000644000175000017500000001060613210576031017653 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,shutil,platform from waflib import Task,Utils from waflib.TaskGen import taskgen_method,feature,after_method,before_method app_info=''' CFBundlePackageType APPL CFBundleGetInfoString Created by Waf CFBundleSignature ???? NOTE THIS IS A GENERATED FILE, DO NOT MODIFY CFBundleExecutable {app_name} ''' @feature('c','cxx') def set_macosx_deployment_target(self): if self.env.MACOSX_DEPLOYMENT_TARGET: os.environ['MACOSX_DEPLOYMENT_TARGET']=self.env.MACOSX_DEPLOYMENT_TARGET elif'MACOSX_DEPLOYMENT_TARGET'not in os.environ: if Utils.unversioned_sys_platform()=='darwin': os.environ['MACOSX_DEPLOYMENT_TARGET']='.'.join(platform.mac_ver()[0].split('.')[:2]) @taskgen_method def create_bundle_dirs(self,name,out): dir=out.parent.find_or_declare(name) dir.mkdir() macos=dir.find_or_declare(['Contents','MacOS']) macos.mkdir() return dir def bundle_name_for_output(out): name=out.name k=name.rfind('.') if k>=0: name=name[:k]+'.app' else: name=name+'.app' return name @feature('cprogram','cxxprogram') @after_method('apply_link') def create_task_macapp(self): if self.env.MACAPP or getattr(self,'mac_app',False): out=self.link_task.outputs[0] name=bundle_name_for_output(out) dir=self.create_bundle_dirs(name,out) n1=dir.find_or_declare(['Contents','MacOS',out.name]) self.apptask=self.create_task('macapp',self.link_task.outputs,n1) inst_to=getattr(self,'install_path','/Applications')+'/%s/Contents/MacOS/'%name self.add_install_files(install_to=inst_to,install_from=n1,chmod=Utils.O755) if getattr(self,'mac_files',None): mac_files_root=getattr(self,'mac_files_root',None) if isinstance(mac_files_root,str): mac_files_root=self.path.find_node(mac_files_root) if not mac_files_root: self.bld.fatal('Invalid mac_files_root %r'%self.mac_files_root) res_dir=n1.parent.parent.make_node('Resources') inst_to=getattr(self,'install_path','/Applications')+'/%s/Resources'%name for node in self.to_nodes(self.mac_files): relpath=node.path_from(mac_files_root or node.parent) self.create_task('macapp',node,res_dir.make_node(relpath)) self.add_install_as(install_to=os.path.join(inst_to,relpath),install_from=node) if getattr(self.bld,'is_install',None): self.install_task.hasrun=Task.SKIP_ME @feature('cprogram','cxxprogram') @after_method('apply_link') def create_task_macplist(self): if self.env.MACAPP or getattr(self,'mac_app',False): out=self.link_task.outputs[0] name=bundle_name_for_output(out) dir=self.create_bundle_dirs(name,out) n1=dir.find_or_declare(['Contents','Info.plist']) self.plisttask=plisttask=self.create_task('macplist',[],n1) plisttask.context={'app_name':self.link_task.outputs[0].name,'env':self.env} plist_ctx=getattr(self,'plist_context',None) if(plist_ctx): plisttask.context.update(plist_ctx) if getattr(self,'mac_plist',False): node=self.path.find_resource(self.mac_plist) if node: plisttask.inputs.append(node) else: plisttask.code=self.mac_plist else: plisttask.code=app_info inst_to=getattr(self,'install_path','/Applications')+'/%s/Contents/'%name self.add_install_files(install_to=inst_to,install_from=n1) @feature('cshlib','cxxshlib') @before_method('apply_link','propagate_uselib_vars') def apply_bundle(self): if self.env.MACBUNDLE or getattr(self,'mac_bundle',False): self.env.LINKFLAGS_cshlib=self.env.LINKFLAGS_cxxshlib=[] self.env.cshlib_PATTERN=self.env.cxxshlib_PATTERN=self.env.macbundle_PATTERN use=self.use=self.to_list(getattr(self,'use',[])) if not'MACBUNDLE'in use: use.append('MACBUNDLE') app_dirs=['Contents','Contents/MacOS','Contents/Resources'] class macapp(Task.Task): color='PINK' def run(self): self.outputs[0].parent.mkdir() shutil.copy2(self.inputs[0].srcpath(),self.outputs[0].abspath()) class macplist(Task.Task): color='PINK' ext_in=['.bin'] def run(self): if getattr(self,'code',None): txt=self.code else: txt=self.inputs[0].read() context=getattr(self,'context',{}) txt=txt.format(**context) self.outputs[0].write(txt) ntpsec-1.1.0+dfsg1/waflib/Tools/perl.py0000644000175000017500000000562013210576031017502 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os from waflib import Task,Options,Utils from waflib.Configure import conf from waflib.TaskGen import extension,feature,before_method @before_method('apply_incpaths','apply_link','propagate_uselib_vars') @feature('perlext') def init_perlext(self): self.uselib=self.to_list(getattr(self,'uselib',[])) if not'PERLEXT'in self.uselib: self.uselib.append('PERLEXT') self.env.cshlib_PATTERN=self.env.cxxshlib_PATTERN=self.env.perlext_PATTERN @extension('.xs') def xsubpp_file(self,node): outnode=node.change_ext('.c') self.create_task('xsubpp',node,outnode) self.source.append(outnode) class xsubpp(Task.Task): run_str='${PERL} ${XSUBPP} -noprototypes -typemap ${EXTUTILS_TYPEMAP} ${SRC} > ${TGT}' color='BLUE' ext_out=['.h'] @conf def check_perl_version(self,minver=None): res=True if minver: cver='.'.join(map(str,minver)) else: cver='' self.start_msg('Checking for minimum perl version %s'%cver) perl=self.find_program('perl',var='PERL',value=getattr(Options.options,'perlbinary',None)) version=self.cmd_and_log(perl+["-e",'printf \"%vd\", $^V']) if not version: res=False version="Unknown" elif not minver is None: ver=tuple(map(int,version.split("."))) if ver2: key=lst[1] val=lst[2] k[key]=val def isD(var): return var in k def isT(var): return var in k and k[var]!='0' conf.env.FC_VERSION=(k['__GNUC__'],k['__GNUC_MINOR__'],k['__GNUC_PATCHLEVEL__']) def configure(conf): conf.find_gfortran() conf.find_ar() conf.fc_flags() conf.fc_add_flags() conf.gfortran_flags() conf.gfortran_modifier_platform() conf.check_gfortran_o_space() ntpsec-1.1.0+dfsg1/waflib/Tools/__pycache__/0000775000175000017500000000000013252650652020405 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/waflib/Tools/g95.py0000644000175000017500000000263013210576007017145 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re from waflib import Utils from waflib.Tools import fc,fc_config,fc_scan,ar from waflib.Configure import conf @conf def find_g95(conf): fc=conf.find_program('g95',var='FC') conf.get_g95_version(fc) conf.env.FC_NAME='G95' @conf def g95_flags(conf): v=conf.env v.FCFLAGS_fcshlib=['-fPIC'] v.FORTRANMODFLAG=['-fmod=',''] v.FCFLAGS_DEBUG=['-Werror'] @conf def g95_modifier_win32(conf): fc_config.fortran_modifier_win32(conf) @conf def g95_modifier_cygwin(conf): fc_config.fortran_modifier_cygwin(conf) @conf def g95_modifier_darwin(conf): fc_config.fortran_modifier_darwin(conf) @conf def g95_modifier_platform(conf): dest_os=conf.env.DEST_OS or Utils.unversioned_sys_platform() g95_modifier_func=getattr(conf,'g95_modifier_'+dest_os,None) if g95_modifier_func: g95_modifier_func() @conf def get_g95_version(conf,fc): version_re=re.compile(r"g95\s*(?P\d*)\.(?P\d*)").search cmd=fc+['--version'] out,err=fc_config.getoutput(conf,cmd,stdin=False) if out: match=version_re(out) else: match=version_re(err) if not match: conf.fatal('cannot determine g95 version') k=match.groupdict() conf.env.FC_VERSION=(k['major'],k['minor']) def configure(conf): conf.find_g95() conf.find_ar() conf.fc_flags() conf.fc_add_flags() conf.g95_flags() conf.g95_modifier_platform() ntpsec-1.1.0+dfsg1/waflib/Tools/python.py0000644000175000017500000003777613210576252020107 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,sys from waflib import Utils,Options,Errors,Logs,Task,Node from waflib.TaskGen import extension,before_method,after_method,feature from waflib.Configure import conf FRAG=''' #include #ifdef __cplusplus extern "C" { #endif void Py_Initialize(void); void Py_Finalize(void); #ifdef __cplusplus } #endif int main(int argc, char **argv) { (void)argc; (void)argv; Py_Initialize(); Py_Finalize(); return 0; } ''' INST=''' import sys, py_compile py_compile.compile(sys.argv[1], sys.argv[2], sys.argv[3], True) ''' DISTUTILS_IMP=['from distutils.sysconfig import get_config_var, get_python_lib'] @before_method('process_source') @feature('py') def feature_py(self): self.install_path=getattr(self,'install_path','${PYTHONDIR}') install_from=getattr(self,'install_from',None) if install_from and not isinstance(install_from,Node.Node): install_from=self.path.find_dir(install_from) self.install_from=install_from ver=self.env.PYTHON_VERSION if not ver: self.bld.fatal('Installing python files requires PYTHON_VERSION, try conf.check_python_version') if int(ver.replace('.',''))>31: self.install_32=True @extension('.py') def process_py(self,node): assert(hasattr(self,'install_path')),'add features="py"' if self.install_path: if self.install_from: self.add_install_files(install_to=self.install_path,install_from=node,cwd=self.install_from,relative_trick=True) else: self.add_install_files(install_to=self.install_path,install_from=node,relative_trick=True) lst=[] if self.env.PYC: lst.append('pyc') if self.env.PYO: lst.append('pyo') if self.install_path: if self.install_from: pyd=Utils.subst_vars("%s/%s"%(self.install_path,node.path_from(self.install_from)),self.env) else: pyd=Utils.subst_vars("%s/%s"%(self.install_path,node.path_from(self.path)),self.env) else: pyd=node.abspath() for ext in lst: if self.env.PYTAG and not self.env.NOPYCACHE: name=node.name[:-3] pyobj=node.parent.get_bld().make_node('__pycache__').make_node("%s.%s.%s"%(name,self.env.PYTAG,ext)) pyobj.parent.mkdir() else: pyobj=node.change_ext(".%s"%ext) tsk=self.create_task(ext,node,pyobj) tsk.pyd=pyd if self.install_path: self.add_install_files(install_to=os.path.dirname(pyd),install_from=pyobj,cwd=node.parent.get_bld(),relative_trick=True) class pyc(Task.Task): color='PINK' def __str__(self): node=self.outputs[0] return node.path_from(node.ctx.launch_node()) def run(self): cmd=[Utils.subst_vars('${PYTHON}',self.env),'-c',INST,self.inputs[0].abspath(),self.outputs[0].abspath(),self.pyd] ret=self.generator.bld.exec_command(cmd) return ret class pyo(Task.Task): color='PINK' def __str__(self): node=self.outputs[0] return node.path_from(node.ctx.launch_node()) def run(self): cmd=[Utils.subst_vars('${PYTHON}',self.env),Utils.subst_vars('${PYFLAGS_OPT}',self.env),'-c',INST,self.inputs[0].abspath(),self.outputs[0].abspath(),self.pyd] ret=self.generator.bld.exec_command(cmd) return ret @feature('pyext') @before_method('propagate_uselib_vars','apply_link') @after_method('apply_bundle') def init_pyext(self): self.uselib=self.to_list(getattr(self,'uselib',[])) if not'PYEXT'in self.uselib: self.uselib.append('PYEXT') self.env.cshlib_PATTERN=self.env.cxxshlib_PATTERN=self.env.macbundle_PATTERN=self.env.pyext_PATTERN self.env.fcshlib_PATTERN=self.env.dshlib_PATTERN=self.env.pyext_PATTERN try: if not self.install_path: return except AttributeError: self.install_path='${PYTHONARCHDIR}' @feature('pyext') @before_method('apply_link','apply_bundle') def set_bundle(self): if Utils.unversioned_sys_platform()=='darwin': self.mac_bundle=True @before_method('propagate_uselib_vars') @feature('pyembed') def init_pyembed(self): self.uselib=self.to_list(getattr(self,'uselib',[])) if not'PYEMBED'in self.uselib: self.uselib.append('PYEMBED') @conf def get_python_variables(self,variables,imports=None): if not imports: try: imports=self.python_imports except AttributeError: imports=DISTUTILS_IMP program=list(imports) program.append('') for v in variables: program.append("print(repr(%s))"%v) os_env=dict(os.environ) try: del os_env['MACOSX_DEPLOYMENT_TARGET'] except KeyError: pass try: out=self.cmd_and_log(self.env.PYTHON+['-c','\n'.join(program)],env=os_env) except Errors.WafError: self.fatal('The distutils module is unusable: install "python-devel"?') self.to_log(out) return_values=[] for s in out.splitlines(): s=s.strip() if not s: continue if s=='None': return_values.append(None) elif(s[0]=="'"and s[-1]=="'")or(s[0]=='"'and s[-1]=='"'): return_values.append(eval(s)) elif s[0].isdigit(): return_values.append(int(s)) else:break return return_values @conf def test_pyembed(self,mode,msg='Testing pyembed configuration'): self.check(header_name='Python.h',define_name='HAVE_PYEMBED',msg=msg,fragment=FRAG,errmsg='Could not build a python embedded interpreter',features='%s %sprogram pyembed'%(mode,mode)) @conf def test_pyext(self,mode,msg='Testing pyext configuration'): self.check(header_name='Python.h',define_name='HAVE_PYEXT',msg=msg,fragment=FRAG,errmsg='Could not build python extensions',features='%s %sshlib pyext'%(mode,mode)) @conf def python_cross_compile(self,features='pyembed pyext'): features=Utils.to_list(features) if not('PYTHON_LDFLAGS'in self.environ or'PYTHON_PYEXT_LDFLAGS'in self.environ or'PYTHON_PYEMBED_LDFLAGS'in self.environ): return False for x in'PYTHON_VERSION PYTAG pyext_PATTERN'.split(): if not x in self.environ: self.fatal('Please set %s in the os environment'%x) else: self.env[x]=self.environ[x] xx=self.env.CXX_NAME and'cxx'or'c' if'pyext'in features: flags=self.environ.get('PYTHON_PYEXT_LDFLAGS',self.environ.get('PYTHON_LDFLAGS')) if flags is None: self.fatal('No flags provided through PYTHON_PYEXT_LDFLAGS as required') else: self.parse_flags(flags,'PYEXT') self.test_pyext(xx) if'pyembed'in features: flags=self.environ.get('PYTHON_PYEMBED_LDFLAGS',self.environ.get('PYTHON_LDFLAGS')) if flags is None: self.fatal('No flags provided through PYTHON_PYEMBED_LDFLAGS as required') else: self.parse_flags(flags,'PYEMBED') self.test_pyembed(xx) return True @conf def check_python_headers(conf,features='pyembed pyext'): features=Utils.to_list(features) assert('pyembed'in features)or('pyext'in features),"check_python_headers features must include 'pyembed' and/or 'pyext'" env=conf.env if not env.CC_NAME and not env.CXX_NAME: conf.fatal('load a compiler first (gcc, g++, ..)') if conf.python_cross_compile(features): return if not env.PYTHON_VERSION: conf.check_python_version() pybin=env.PYTHON if not pybin: conf.fatal('Could not find the python executable') v='prefix SO LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split() try: lst=conf.get_python_variables(["get_config_var('%s') or ''"%x for x in v]) except RuntimeError: conf.fatal("Python development headers not found (-v for details).") vals=['%s = %r'%(x,y)for(x,y)in zip(v,lst)] conf.to_log("Configuration returned from %r:\n%s\n"%(pybin,'\n'.join(vals))) dct=dict(zip(v,lst)) x='MACOSX_DEPLOYMENT_TARGET' if dct[x]: env[x]=conf.environ[x]=dct[x] env.pyext_PATTERN='%s'+dct['SO'] num='.'.join(env.PYTHON_VERSION.split('.')[:2]) conf.find_program([''.join(pybin)+'-config','python%s-config'%num,'python-config-%s'%num,'python%sm-config'%num],var='PYTHON_CONFIG',msg="python-config",mandatory=False) if env.PYTHON_CONFIG: all_flags=[['--cflags','--libs','--ldflags']] if sys.hexversion<0x2070000: all_flags=[[k]for k in all_flags[0]] xx=env.CXX_NAME and'cxx'or'c' if'pyembed'in features: for flags in all_flags: conf.check_cfg(msg='Asking python-config for pyembed %r flags'%' '.join(flags),path=env.PYTHON_CONFIG,package='',uselib_store='PYEMBED',args=flags) try: conf.test_pyembed(xx) except conf.errors.ConfigurationError: if dct['Py_ENABLE_SHARED']and dct['LIBDIR']: env.append_unique('LIBPATH_PYEMBED',[dct['LIBDIR']]) conf.test_pyembed(xx) else: raise if'pyext'in features: for flags in all_flags: conf.check_cfg(msg='Asking python-config for pyext %r flags'%' '.join(flags),path=env.PYTHON_CONFIG,package='',uselib_store='PYEXT',args=flags) try: conf.test_pyext(xx) except conf.errors.ConfigurationError: if dct['Py_ENABLE_SHARED']and dct['LIBDIR']: env.append_unique('LIBPATH_PYEXT',[dct['LIBDIR']]) conf.test_pyext(xx) else: raise conf.define('HAVE_PYTHON_H',1) return all_flags=dct['LDFLAGS']+' '+dct['CFLAGS'] conf.parse_flags(all_flags,'PYEMBED') all_flags=dct['LDFLAGS']+' '+dct['LDSHARED']+' '+dct['CFLAGS'] conf.parse_flags(all_flags,'PYEXT') result=None if not dct["LDVERSION"]: dct["LDVERSION"]=env.PYTHON_VERSION for name in('python'+dct['LDVERSION'],'python'+env.PYTHON_VERSION+'m','python'+env.PYTHON_VERSION.replace('.','')): if not result and env.LIBPATH_PYEMBED: path=env.LIBPATH_PYEMBED conf.to_log("\n\n# Trying default LIBPATH_PYEMBED: %r\n"%path) result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in LIBPATH_PYEMBED'%name) if not result and dct['LIBDIR']: path=[dct['LIBDIR']] conf.to_log("\n\n# try again with -L$python_LIBDIR: %r\n"%path) result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in LIBDIR'%name) if not result and dct['LIBPL']: path=[dct['LIBPL']] conf.to_log("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n") result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in python_LIBPL'%name) if not result: path=[os.path.join(dct['prefix'],"libs")] conf.to_log("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n") result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in $prefix/libs'%name) if result: break if result: env.LIBPATH_PYEMBED=path env.append_value('LIB_PYEMBED',[name]) else: conf.to_log("\n\n### LIB NOT FOUND\n") if Utils.is_win32 or dct['Py_ENABLE_SHARED']: env.LIBPATH_PYEXT=env.LIBPATH_PYEMBED env.LIB_PYEXT=env.LIB_PYEMBED conf.to_log("Include path for Python extensions (found via distutils module): %r\n"%(dct['INCLUDEPY'],)) env.INCLUDES_PYEXT=[dct['INCLUDEPY']] env.INCLUDES_PYEMBED=[dct['INCLUDEPY']] if env.CC_NAME=='gcc': env.append_value('CFLAGS_PYEMBED',['-fno-strict-aliasing']) env.append_value('CFLAGS_PYEXT',['-fno-strict-aliasing']) if env.CXX_NAME=='gcc': env.append_value('CXXFLAGS_PYEMBED',['-fno-strict-aliasing']) env.append_value('CXXFLAGS_PYEXT',['-fno-strict-aliasing']) if env.CC_NAME=="msvc": from distutils.msvccompiler import MSVCCompiler dist_compiler=MSVCCompiler() dist_compiler.initialize() env.append_value('CFLAGS_PYEXT',dist_compiler.compile_options) env.append_value('CXXFLAGS_PYEXT',dist_compiler.compile_options) env.append_value('LINKFLAGS_PYEXT',dist_compiler.ldflags_shared) conf.check(header_name='Python.h',define_name='HAVE_PYTHON_H',uselib='PYEMBED',fragment=FRAG,errmsg='Distutils not installed? Broken python installation? Get python-config now!') @conf def check_python_version(conf,minver=None): assert minver is None or isinstance(minver,tuple) pybin=conf.env.PYTHON if not pybin: conf.fatal('could not find the python executable') cmd=pybin+['-c','import sys\nfor x in sys.version_info: print(str(x))'] Logs.debug('python: Running python command %r',cmd) lines=conf.cmd_and_log(cmd).split() assert len(lines)==5,"found %r lines, expected 5: %r"%(len(lines),lines) pyver_tuple=(int(lines[0]),int(lines[1]),int(lines[2]),lines[3],int(lines[4])) result=(minver is None)or(pyver_tuple>=minver) if result: pyver='.'.join([str(x)for x in pyver_tuple[:2]]) conf.env.PYTHON_VERSION=pyver if'PYTHONDIR'in conf.env: pydir=conf.env.PYTHONDIR elif'PYTHONDIR'in conf.environ: pydir=conf.environ['PYTHONDIR'] else: if Utils.is_win32: (python_LIBDEST,pydir)=conf.get_python_variables(["get_config_var('LIBDEST') or ''","get_python_lib(standard_lib=0) or ''"]) else: python_LIBDEST=None (pydir,)=conf.get_python_variables(["get_python_lib(standard_lib=0, prefix=%r) or ''"%conf.env.PREFIX]) if python_LIBDEST is None: if conf.env.LIBDIR: python_LIBDEST=os.path.join(conf.env.LIBDIR,'python'+pyver) else: python_LIBDEST=os.path.join(conf.env.PREFIX,'lib','python'+pyver) if'PYTHONARCHDIR'in conf.env: pyarchdir=conf.env.PYTHONARCHDIR elif'PYTHONARCHDIR'in conf.environ: pyarchdir=conf.environ['PYTHONARCHDIR'] else: (pyarchdir,)=conf.get_python_variables(["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r) or ''"%conf.env.PREFIX]) if not pyarchdir: pyarchdir=pydir if hasattr(conf,'define'): conf.define('PYTHONDIR',pydir) conf.define('PYTHONARCHDIR',pyarchdir) conf.env.PYTHONDIR=pydir conf.env.PYTHONARCHDIR=pyarchdir pyver_full='.'.join(map(str,pyver_tuple[:3])) if minver is None: conf.msg('Checking for python version',pyver_full) else: minver_str='.'.join(map(str,minver)) conf.msg('Checking for python version >= %s'%(minver_str,),pyver_full,color=result and'GREEN'or'YELLOW') if not result: conf.fatal('The python version is too old, expecting %r'%(minver,)) PYTHON_MODULE_TEMPLATE=''' import %s as current_module version = getattr(current_module, '__version__', None) if version is not None: print(str(version)) else: print('unknown version') ''' @conf def check_python_module(conf,module_name,condition=''): msg="Checking for python module %r"%module_name if condition: msg='%s (%s)'%(msg,condition) conf.start_msg(msg) try: ret=conf.cmd_and_log(conf.env.PYTHON+['-c',PYTHON_MODULE_TEMPLATE%module_name]) except Exception: conf.end_msg(False) conf.fatal('Could not find the python module %r'%module_name) ret=ret.strip() if condition: conf.end_msg(ret) if ret=='unknown version': conf.fatal('Could not check the %s version'%module_name) from distutils.version import LooseVersion def num(*k): if isinstance(k[0],int): return LooseVersion('.'.join([str(x)for x in k])) else: return LooseVersion(k[0]) d={'num':num,'ver':LooseVersion(ret)} ev=eval(condition,{},d) if not ev: conf.fatal('The %s version does not satisfy the requirements'%module_name) else: if ret=='unknown version': conf.end_msg(True) else: conf.end_msg(ret) def configure(conf): v=conf.env if getattr(Options.options,'pythondir',None): v.PYTHONDIR=Options.options.pythondir if getattr(Options.options,'pythonarchdir',None): v.PYTHONARCHDIR=Options.options.pythonarchdir if getattr(Options.options,'nopycache',None): v.NOPYCACHE=Options.options.nopycache if not v.PYTHON: v.PYTHON=[getattr(Options.options,'python',None)or sys.executable] v.PYTHON=Utils.to_list(v.PYTHON) conf.find_program('python',var='PYTHON') v.PYFLAGS='' v.PYFLAGS_OPT='-O' v.PYC=getattr(Options.options,'pyc',1) v.PYO=getattr(Options.options,'pyo',1) try: v.PYTAG=conf.cmd_and_log(conf.env.PYTHON+['-c',"import imp;print(imp.get_tag())"]).strip() except Errors.WafError: pass def options(opt): pyopt=opt.add_option_group("Python Options") pyopt.add_option('--nopyc',dest='pyc',action='store_false',default=1,help='Do not install bytecode compiled .pyc files (configuration) [Default:install]') pyopt.add_option('--nopyo',dest='pyo',action='store_false',default=1,help='Do not install optimised compiled .pyo files (configuration) [Default:install]') pyopt.add_option('--nopycache',dest='nopycache',action='store_true',help='Do not use __pycache__ directory to install objects [Default:auto]') pyopt.add_option('--python',dest="python",help='python binary to be used [Default: %s]'%sys.executable) pyopt.add_option('--pythondir',dest='pythondir',help='Installation path for python modules (py, platform-independent .py and .pyc files)') pyopt.add_option('--pythonarchdir',dest='pythonarchdir',help='Installation path for python extension (pyext, platform-dependent .so or .dylib files)') ntpsec-1.1.0+dfsg1/waflib/Tools/icc.py0000644000175000017500000000077613210576007017310 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import sys from waflib.Tools import ccroot,ar,gcc from waflib.Configure import conf @conf def find_icc(conf): cc=conf.find_program(['icc','ICL'],var='CC') conf.get_cc_version(cc,icc=True) conf.env.CC_NAME='icc' def configure(conf): conf.find_icc() conf.find_ar() conf.gcc_common_flags() conf.gcc_modifier_platform() conf.cc_load_tools() conf.cc_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/suncxx.py0000644000175000017500000000217013210576007020070 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar from waflib.Configure import conf @conf def find_sxx(conf): v=conf.env cc=conf.find_program(['CC','c++'],var='CXX') try: conf.cmd_and_log(cc+['-flags']) except Exception: conf.fatal('%r is not a Sun compiler'%cc) v.CXX_NAME='sun' conf.get_suncc_version(cc) @conf def sxx_common_flags(conf): v=conf.env v.CXX_SRC_F=[] v.CXX_TGT_F=['-c','-o',''] if not v.LINK_CXX: v.LINK_CXX=v.CXX v.CXXLNK_SRC_F=[] v.CXXLNK_TGT_F=['-o',''] v.CPPPATH_ST='-I%s' v.DEFINES_ST='-D%s' v.LIB_ST='-l%s' v.LIBPATH_ST='-L%s' v.STLIB_ST='-l%s' v.STLIBPATH_ST='-L%s' v.SONAME_ST='-Wl,-h,%s' v.SHLIB_MARKER='-Bdynamic' v.STLIB_MARKER='-Bstatic' v.cxxprogram_PATTERN='%s' v.CXXFLAGS_cxxshlib=['-xcode=pic32','-DPIC'] v.LINKFLAGS_cxxshlib=['-G'] v.cxxshlib_PATTERN='lib%s.so' v.LINKFLAGS_cxxstlib=['-Bstatic'] v.cxxstlib_PATTERN='lib%s.a' def configure(conf): conf.find_sxx() conf.find_ar() conf.sxx_common_flags() conf.cxx_load_tools() conf.cxx_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/dmd.py0000644000175000017500000000262013210576007017304 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import sys from waflib.Tools import ar,d from waflib.Configure import conf @conf def find_dmd(conf): conf.find_program(['dmd','dmd2','ldc'],var='D') out=conf.cmd_and_log(conf.env.D+['--help']) if out.find("D Compiler v")==-1: out=conf.cmd_and_log(conf.env.D+['-version']) if out.find("based on DMD v1.")==-1: conf.fatal("detected compiler is not dmd/ldc") @conf def common_flags_ldc(conf): v=conf.env v.DFLAGS=['-d-version=Posix'] v.LINKFLAGS=[] v.DFLAGS_dshlib=['-relocation-model=pic'] @conf def common_flags_dmd(conf): v=conf.env v.D_SRC_F=['-c'] v.D_TGT_F='-of%s' v.D_LINKER=v.D v.DLNK_SRC_F='' v.DLNK_TGT_F='-of%s' v.DINC_ST='-I%s' v.DSHLIB_MARKER=v.DSTLIB_MARKER='' v.DSTLIB_ST=v.DSHLIB_ST='-L-l%s' v.DSTLIBPATH_ST=v.DLIBPATH_ST='-L-L%s' v.LINKFLAGS_dprogram=['-quiet'] v.DFLAGS_dshlib=['-fPIC'] v.LINKFLAGS_dshlib=['-L-shared'] v.DHEADER_ext='.di' v.DFLAGS_d_with_header=['-H','-Hf'] v.D_HDR_F='%s' def configure(conf): conf.find_dmd() if sys.platform=='win32': out=conf.cmd_and_log(conf.env.D+['--help']) if out.find('D Compiler v2.')>-1: conf.fatal('dmd2 on Windows is not supported, use gdc or ldc2 instead') conf.load('ar') conf.load('d') conf.common_flags_dmd() conf.d_platform_flags() if str(conf.env.D).find('ldc')>-1: conf.common_flags_ldc() ntpsec-1.1.0+dfsg1/waflib/Tools/gas.py0000644000175000017500000000051313210576007017311 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import waflib.Tools.asm from waflib.Tools import ar def configure(conf): conf.find_program(['gas','gcc'],var='AS') conf.env.AS_TGT_F=['-c','-o'] conf.env.ASLNK_TGT_F=['-o'] conf.find_ar() conf.load('asm') ntpsec-1.1.0+dfsg1/waflib/Tools/gcc.py0000644000175000017500000000503613210576031017275 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar from waflib.Configure import conf @conf def find_gcc(conf): cc=conf.find_program(['gcc','cc'],var='CC') conf.get_cc_version(cc,gcc=True) conf.env.CC_NAME='gcc' @conf def gcc_common_flags(conf): v=conf.env v.CC_SRC_F=[] v.CC_TGT_F=['-c','-o'] if not v.LINK_CC: v.LINK_CC=v.CC v.CCLNK_SRC_F=[] v.CCLNK_TGT_F=['-o'] v.CPPPATH_ST='-I%s' v.DEFINES_ST='-D%s' v.LIB_ST='-l%s' v.LIBPATH_ST='-L%s' v.STLIB_ST='-l%s' v.STLIBPATH_ST='-L%s' v.RPATH_ST='-Wl,-rpath,%s' v.SONAME_ST='-Wl,-h,%s' v.SHLIB_MARKER='-Wl,-Bdynamic' v.STLIB_MARKER='-Wl,-Bstatic' v.cprogram_PATTERN='%s' v.CFLAGS_cshlib=['-fPIC'] v.LINKFLAGS_cshlib=['-shared'] v.cshlib_PATTERN='lib%s.so' v.LINKFLAGS_cstlib=['-Wl,-Bstatic'] v.cstlib_PATTERN='lib%s.a' v.LINKFLAGS_MACBUNDLE=['-bundle','-undefined','dynamic_lookup'] v.CFLAGS_MACBUNDLE=['-fPIC'] v.macbundle_PATTERN='%s.bundle' @conf def gcc_modifier_win32(conf): v=conf.env v.cprogram_PATTERN='%s.exe' v.cshlib_PATTERN='%s.dll' v.implib_PATTERN='lib%s.dll.a' v.IMPLIB_ST='-Wl,--out-implib,%s' v.CFLAGS_cshlib=[] v.append_value('LINKFLAGS',['-Wl,--enable-auto-import']) @conf def gcc_modifier_cygwin(conf): gcc_modifier_win32(conf) v=conf.env v.cshlib_PATTERN='cyg%s.dll' v.append_value('LINKFLAGS_cshlib',['-Wl,--enable-auto-image-base']) v.CFLAGS_cshlib=[] @conf def gcc_modifier_darwin(conf): v=conf.env v.CFLAGS_cshlib=['-fPIC'] v.LINKFLAGS_cshlib=['-dynamiclib'] v.cshlib_PATTERN='lib%s.dylib' v.FRAMEWORKPATH_ST='-F%s' v.FRAMEWORK_ST=['-framework'] v.ARCH_ST=['-arch'] v.LINKFLAGS_cstlib=[] v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.SONAME_ST=[] @conf def gcc_modifier_aix(conf): v=conf.env v.LINKFLAGS_cprogram=['-Wl,-brtl'] v.LINKFLAGS_cshlib=['-shared','-Wl,-brtl,-bexpfull'] v.SHLIB_MARKER=[] @conf def gcc_modifier_hpux(conf): v=conf.env v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.CFLAGS_cshlib=['-fPIC','-DPIC'] v.cshlib_PATTERN='lib%s.sl' @conf def gcc_modifier_openbsd(conf): conf.env.SONAME_ST=[] @conf def gcc_modifier_osf1V(conf): v=conf.env v.SHLIB_MARKER=[] v.STLIB_MARKER=[] v.SONAME_ST=[] @conf def gcc_modifier_platform(conf): gcc_modifier_func=getattr(conf,'gcc_modifier_'+conf.env.DEST_OS,None) if gcc_modifier_func: gcc_modifier_func() def configure(conf): conf.find_gcc() conf.find_ar() conf.gcc_common_flags() conf.gcc_modifier_platform() conf.cc_load_tools() conf.cc_add_flags() conf.link_add_flags() conf.check_gcc_o_space() ntpsec-1.1.0+dfsg1/waflib/Tools/gnu_dirs.py0000644000175000017500000000533213172024730020353 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re from waflib import Utils,Options,Context gnuopts=''' bindir, user commands, ${EXEC_PREFIX}/bin sbindir, system binaries, ${EXEC_PREFIX}/sbin libexecdir, program-specific binaries, ${EXEC_PREFIX}/libexec sysconfdir, host-specific configuration, ${PREFIX}/etc sharedstatedir, architecture-independent variable data, ${PREFIX}/com localstatedir, variable data, ${PREFIX}/var libdir, object code libraries, ${EXEC_PREFIX}/lib%s includedir, header files, ${PREFIX}/include oldincludedir, header files for non-GCC compilers, /usr/include datarootdir, architecture-independent data root, ${PREFIX}/share datadir, architecture-independent data, ${DATAROOTDIR} infodir, GNU "info" documentation, ${DATAROOTDIR}/info localedir, locale-dependent data, ${DATAROOTDIR}/locale mandir, manual pages, ${DATAROOTDIR}/man docdir, documentation root, ${DATAROOTDIR}/doc/${PACKAGE} htmldir, HTML documentation, ${DOCDIR} dvidir, DVI documentation, ${DOCDIR} pdfdir, PDF documentation, ${DOCDIR} psdir, PostScript documentation, ${DOCDIR} '''%Utils.lib64() _options=[x.split(', ')for x in gnuopts.splitlines()if x] def configure(conf): def get_param(varname,default): return getattr(Options.options,varname,'')or default env=conf.env env.LIBDIR=env.BINDIR=[] env.EXEC_PREFIX=get_param('EXEC_PREFIX',env.PREFIX) env.PACKAGE=getattr(Context.g_module,'APPNAME',None)or env.PACKAGE complete=False iter=0 while not complete and iter0: package=local_packages.pop() if package in seen: continue seen.append(package) try: package_obj=self.bld.get_tgen_by_name(package) except Errors.WafError: continue package_obj.post() package_name=package_obj.target for task in package_obj.tasks: if isinstance(task,Build.inst): continue for output in task.outputs: if output.name==package_name+".vapi": valatask.set_run_after(task) if package_name not in packages: packages.append(package_name) if output.parent not in vapi_dirs: vapi_dirs.append(output.parent) if output.parent not in self.includes: self.includes.append(output.parent) if hasattr(package_obj,'use'): lst=self.to_list(package_obj.use) lst.reverse() local_packages=[pkg for pkg in lst if pkg not in seen]+local_packages addflags(['--pkg=%s'%p for p in packages]) for vapi_dir in vapi_dirs: if isinstance(vapi_dir,Node.Node): v_node=vapi_dir else: v_node=self.path.find_dir(vapi_dir) if not v_node: Logs.warn('Unable to locate Vala API directory: %r',vapi_dir) else: addflags('--vapidir=%s'%v_node.abspath()) self.dump_deps_node=None if self.is_lib and self.packages: self.dump_deps_node=valatask.vala_dir_node.find_or_declare('%s.deps'%self.target) valatask.outputs.append(self.dump_deps_node) if self.is_lib and valatask.install_binding: headers_list=[o for o in valatask.outputs if o.suffix()==".h"] if headers_list: self.install_vheader=self.add_install_files(install_to=valatask.header_path,install_from=headers_list) vapi_list=[o for o in valatask.outputs if(o.suffix()in(".vapi",".deps"))] if vapi_list: self.install_vapi=self.add_install_files(install_to=valatask.vapi_path,install_from=vapi_list) gir_list=[o for o in valatask.outputs if o.suffix()=='.gir'] if gir_list: self.install_gir=self.add_install_files(install_to=getattr(self,'gir_path','${DATAROOTDIR}/gir-1.0'),install_from=gir_list) if hasattr(self,'vala_resources'): nodes=self.to_nodes(self.vala_resources) valatask.vala_exclude=getattr(valatask,'vala_exclude',[])+nodes valatask.inputs.extend(nodes) for x in nodes: addflags(['--gresources',x.abspath()]) @extension('.vala','.gs') def vala_file(self,node): try: valatask=self.valatask except AttributeError: valatask=self.valatask=self.create_task('valac') self.init_vala_task() valatask.inputs.append(node) name=node.name[:node.name.rfind('.')]+'.c' c_node=valatask.vala_dir_node.find_or_declare(name) valatask.outputs.append(c_node) self.source.append(c_node) @extension('.vapi') def vapi_file(self,node): try: valatask=self.valatask except AttributeError: valatask=self.valatask=self.create_task('valac') self.init_vala_task() valatask.inputs.append(node) @conf def find_valac(self,valac_name,min_version): valac=self.find_program(valac_name,var='VALAC') try: output=self.cmd_and_log(valac+['--version']) except Exception: valac_version=None else: ver=re.search(r'\d+.\d+.\d+',output).group().split('.') valac_version=tuple([int(x)for x in ver]) self.msg('Checking for %s version >= %r'%(valac_name,min_version),valac_version,valac_version and valac_version>=min_version) if valac and valac_version= %r"%(valac_name,valac_version,min_version)) self.env.VALAC_VERSION=valac_version return valac @conf def check_vala(self,min_version=(0,8,0),branch=None): if self.env.VALA_MINVER: min_version=self.env.VALA_MINVER if self.env.VALA_MINVER_BRANCH: branch=self.env.VALA_MINVER_BRANCH if not branch: branch=min_version[:2] try: find_valac(self,'valac-%d.%d'%(branch[0],branch[1]),min_version) except self.errors.ConfigurationError: find_valac(self,'valac',min_version) @conf def check_vala_deps(self): if not self.env.HAVE_GOBJECT: pkg_args={'package':'gobject-2.0','uselib_store':'GOBJECT','args':'--cflags --libs'} if getattr(Options.options,'vala_target_glib',None): pkg_args['atleast_version']=Options.options.vala_target_glib self.check_cfg(**pkg_args) if not self.env.HAVE_GTHREAD: pkg_args={'package':'gthread-2.0','uselib_store':'GTHREAD','args':'--cflags --libs'} if getattr(Options.options,'vala_target_glib',None): pkg_args['atleast_version']=Options.options.vala_target_glib self.check_cfg(**pkg_args) def configure(self): self.load('gnu_dirs') self.check_vala_deps() self.check_vala() self.add_os_flags('VALAFLAGS') self.env.append_unique('VALAFLAGS',['-C']) def options(opt): opt.load('gnu_dirs') valaopts=opt.add_option_group('Vala Compiler Options') valaopts.add_option('--vala-target-glib',default=None,dest='vala_target_glib',metavar='MAJOR.MINOR',help='Target version of glib for Vala GObject code generation') ntpsec-1.1.0+dfsg1/waflib/Tools/cs.py0000644000175000017500000000737113210576031017152 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Utils,Task,Options,Errors from waflib.TaskGen import before_method,after_method,feature from waflib.Tools import ccroot from waflib.Configure import conf ccroot.USELIB_VARS['cs']=set(['CSFLAGS','ASSEMBLIES','RESOURCES']) ccroot.lib_patterns['csshlib']=['%s'] @feature('cs') @before_method('process_source') def apply_cs(self): cs_nodes=[] no_nodes=[] for x in self.to_nodes(self.source): if x.name.endswith('.cs'): cs_nodes.append(x) else: no_nodes.append(x) self.source=no_nodes bintype=getattr(self,'bintype',self.gen.endswith('.dll')and'library'or'exe') self.cs_task=tsk=self.create_task('mcs',cs_nodes,self.path.find_or_declare(self.gen)) tsk.env.CSTYPE='/target:%s'%bintype tsk.env.OUT='/out:%s'%tsk.outputs[0].abspath() self.env.append_value('CSFLAGS','/platform:%s'%getattr(self,'platform','anycpu')) inst_to=getattr(self,'install_path',bintype=='exe'and'${BINDIR}'or'${LIBDIR}') if inst_to: mod=getattr(self,'chmod',bintype=='exe'and Utils.O755 or Utils.O644) self.install_task=self.add_install_files(install_to=inst_to,install_from=self.cs_task.outputs[:],chmod=mod) @feature('cs') @after_method('apply_cs') def use_cs(self): names=self.to_list(getattr(self,'use',[])) get=self.bld.get_tgen_by_name for x in names: try: y=get(x) except Errors.WafError: self.env.append_value('CSFLAGS','/reference:%s'%x) continue y.post() tsk=getattr(y,'cs_task',None)or getattr(y,'link_task',None) if not tsk: self.bld.fatal('cs task has no link task for use %r'%self) self.cs_task.dep_nodes.extend(tsk.outputs) self.cs_task.set_run_after(tsk) self.env.append_value('CSFLAGS','/reference:%s'%tsk.outputs[0].abspath()) @feature('cs') @after_method('apply_cs','use_cs') def debug_cs(self): csdebug=getattr(self,'csdebug',self.env.CSDEBUG) if not csdebug: return node=self.cs_task.outputs[0] if self.env.CS_NAME=='mono': out=node.parent.find_or_declare(node.name+'.mdb') else: out=node.change_ext('.pdb') self.cs_task.outputs.append(out) if getattr(self,'install_task',None): self.pdb_install_task=self.add_install_files(install_to=self.install_task.install_to,install_from=out) if csdebug=='pdbonly': val=['/debug+','/debug:pdbonly'] elif csdebug=='full': val=['/debug+','/debug:full'] else: val=['/debug-'] self.env.append_value('CSFLAGS',val) @feature('cs') @after_method('debug_cs') def doc_cs(self): csdoc=getattr(self,'csdoc',self.env.CSDOC) if not csdoc: return node=self.cs_task.outputs[0] out=node.change_ext('.xml') self.cs_task.outputs.append(out) if getattr(self,'install_task',None): self.doc_install_task=self.add_install_files(install_to=self.install_task.install_to,install_from=out) self.env.append_value('CSFLAGS','/doc:%s'%out.abspath()) class mcs(Task.Task): color='YELLOW' run_str='${MCS} ${CSTYPE} ${CSFLAGS} ${ASS_ST:ASSEMBLIES} ${RES_ST:RESOURCES} ${OUT} ${SRC}' def split_argfile(self,cmd): inline=[cmd[0]] infile=[] for x in cmd[1:]: if x.lower()=='/noconfig': inline.append(x) else: infile.append(self.quote_flag(x)) return(inline,infile) def configure(conf): csc=getattr(Options.options,'cscbinary',None) if csc: conf.env.MCS=csc conf.find_program(['csc','mcs','gmcs'],var='MCS') conf.env.ASS_ST='/r:%s' conf.env.RES_ST='/resource:%s' conf.env.CS_NAME='csc' if str(conf.env.MCS).lower().find('mcs')>-1: conf.env.CS_NAME='mono' def options(opt): opt.add_option('--with-csc-binary',type='string',dest='cscbinary') class fake_csshlib(Task.Task): color='YELLOW' inst_to=None def runnable_status(self): return Task.SKIP_ME @conf def read_csshlib(self,name,paths=[]): return self(name=name,features='fake_lib',lib_paths=paths,lib_type='csshlib') ntpsec-1.1.0+dfsg1/waflib/Tools/tex.py0000644000175000017500000002501613210576031017341 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re from waflib import Utils,Task,Errors,Logs,Node from waflib.TaskGen import feature,before_method re_bibunit=re.compile(r'\\(?Pputbib)\[(?P[^\[\]]*)\]',re.M) def bibunitscan(self): node=self.inputs[0] nodes=[] if not node: return nodes code=node.read() for match in re_bibunit.finditer(code): path=match.group('file') if path: found=None for k in('','.bib'): Logs.debug('tex: trying %s%s',path,k) fi=node.parent.find_resource(path+k) if fi: found=True nodes.append(fi) if not found: Logs.debug('tex: could not find %s',path) Logs.debug('tex: found the following bibunit files: %s',nodes) return nodes exts_deps_tex=['','.ltx','.tex','.bib','.pdf','.png','.eps','.ps','.sty'] exts_tex=['.ltx','.tex'] re_tex=re.compile(r'\\(?Pusepackage|RequirePackage|include|bibliography([^\[\]{}]*)|putbib|includegraphics|input|import|bringin|lstinputlisting)(\[[^\[\]]*\])?{(?P[^{}]*)}',re.M) g_bibtex_re=re.compile('bibdata',re.M) g_glossaries_re=re.compile('\\@newglossary',re.M) class tex(Task.Task): bibtex_fun,_=Task.compile_fun('${BIBTEX} ${BIBTEXFLAGS} ${SRCFILE}',shell=False) bibtex_fun.__doc__=""" Execute the program **bibtex** """ makeindex_fun,_=Task.compile_fun('${MAKEINDEX} ${MAKEINDEXFLAGS} ${SRCFILE}',shell=False) makeindex_fun.__doc__=""" Execute the program **makeindex** """ makeglossaries_fun,_=Task.compile_fun('${MAKEGLOSSARIES} ${SRCFILE}',shell=False) makeglossaries_fun.__doc__=""" Execute the program **makeglossaries** """ def exec_command(self,cmd,**kw): if self.env.PROMPT_LATEX: kw['stdout']=kw['stderr']=None return super(tex,self).exec_command(cmd,**kw) def scan_aux(self,node): nodes=[node] re_aux=re.compile(r'\\@input{(?P[^{}]*)}',re.M) def parse_node(node): code=node.read() for match in re_aux.finditer(code): path=match.group('file') found=node.parent.find_or_declare(path) if found and found not in nodes: Logs.debug('tex: found aux node %r',found) nodes.append(found) parse_node(found) parse_node(node) return nodes def scan(self): node=self.inputs[0] nodes=[] names=[] seen=[] if not node: return(nodes,names) def parse_node(node): if node in seen: return seen.append(node) code=node.read() global re_tex for match in re_tex.finditer(code): multibib=match.group('type') if multibib and multibib.startswith('bibliography'): multibib=multibib[len('bibliography'):] if multibib.startswith('style'): continue else: multibib=None for path in match.group('file').split(','): if path: add_name=True found=None for k in exts_deps_tex: for up in self.texinputs_nodes: Logs.debug('tex: trying %s%s',path,k) found=up.find_resource(path+k) if found: break for tsk in self.generator.tasks: if not found or found in tsk.outputs: break else: nodes.append(found) add_name=False for ext in exts_tex: if found.name.endswith(ext): parse_node(found) break if found and multibib and found.name.endswith('.bib'): try: self.multibibs.append(found) except AttributeError: self.multibibs=[found] if add_name: names.append(path) parse_node(node) for x in nodes: x.parent.get_bld().mkdir() Logs.debug("tex: found the following : %s and names %s",nodes,names) return(nodes,names) def check_status(self,msg,retcode): if retcode!=0: raise Errors.WafError('%r command exit status %r'%(msg,retcode)) def info(self,*k,**kw): try: info=self.generator.bld.conf.logger.info except AttributeError: info=Logs.info info(*k,**kw) def bibfile(self): for aux_node in self.aux_nodes: try: ct=aux_node.read() except EnvironmentError: Logs.error('Error reading %s: %r',aux_node.abspath()) continue if g_bibtex_re.findall(ct): self.info('calling bibtex') self.env.env={} self.env.env.update(os.environ) self.env.env.update({'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()}) self.env.SRCFILE=aux_node.name[:-4] self.check_status('error when calling bibtex',self.bibtex_fun()) for node in getattr(self,'multibibs',[]): self.env.env={} self.env.env.update(os.environ) self.env.env.update({'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()}) self.env.SRCFILE=node.name[:-4] self.check_status('error when calling bibtex',self.bibtex_fun()) def bibunits(self): try: bibunits=bibunitscan(self) except OSError: Logs.error('error bibunitscan') else: if bibunits: fn=['bu'+str(i)for i in range(1,len(bibunits)+1)] if fn: self.info('calling bibtex on bibunits') for f in fn: self.env.env={'BIBINPUTS':self.texinputs(),'BSTINPUTS':self.texinputs()} self.env.SRCFILE=f self.check_status('error when calling bibtex',self.bibtex_fun()) def makeindex(self): self.idx_node=self.inputs[0].change_ext('.idx') try: idx_path=self.idx_node.abspath() os.stat(idx_path) except OSError: self.info('index file %s absent, not calling makeindex',idx_path) else: self.info('calling makeindex') self.env.SRCFILE=self.idx_node.name self.env.env={} self.check_status('error when calling makeindex %s'%idx_path,self.makeindex_fun()) def bibtopic(self): p=self.inputs[0].parent.get_bld() if os.path.exists(os.path.join(p.abspath(),'btaux.aux')): self.aux_nodes+=p.ant_glob('*[0-9].aux') def makeglossaries(self): src_file=self.inputs[0].abspath() base_file=os.path.basename(src_file) base,_=os.path.splitext(base_file) for aux_node in self.aux_nodes: try: ct=aux_node.read() except EnvironmentError: Logs.error('Error reading %s: %r',aux_node.abspath()) continue if g_glossaries_re.findall(ct): if not self.env.MAKEGLOSSARIES: raise Errors.WafError("The program 'makeglossaries' is missing!") Logs.warn('calling makeglossaries') self.env.SRCFILE=base self.check_status('error when calling makeglossaries %s'%base,self.makeglossaries_fun()) return def texinputs(self): return os.pathsep.join([k.abspath()for k in self.texinputs_nodes])+os.pathsep def run(self): env=self.env if not env.PROMPT_LATEX: env.append_value('LATEXFLAGS','-interaction=batchmode') env.append_value('PDFLATEXFLAGS','-interaction=batchmode') env.append_value('XELATEXFLAGS','-interaction=batchmode') self.cwd=self.inputs[0].parent.get_bld() self.info('first pass on %s',self.__class__.__name__) cur_hash=self.hash_aux_nodes() self.call_latex() self.hash_aux_nodes() self.bibtopic() self.bibfile() self.bibunits() self.makeindex() self.makeglossaries() for i in range(10): prev_hash=cur_hash cur_hash=self.hash_aux_nodes() if not cur_hash: Logs.error('No aux.h to process') if cur_hash and cur_hash==prev_hash: break self.info('calling %s',self.__class__.__name__) self.call_latex() def hash_aux_nodes(self): try: self.aux_nodes except AttributeError: try: self.aux_nodes=self.scan_aux(self.inputs[0].change_ext('.aux')) except IOError: return None return Utils.h_list([Utils.h_file(x.abspath())for x in self.aux_nodes]) def call_latex(self): self.env.env={} self.env.env.update(os.environ) self.env.env.update({'TEXINPUTS':self.texinputs()}) self.env.SRCFILE=self.inputs[0].abspath() self.check_status('error when calling latex',self.texfun()) class latex(tex): texfun,vars=Task.compile_fun('${LATEX} ${LATEXFLAGS} ${SRCFILE}',shell=False) class pdflatex(tex): texfun,vars=Task.compile_fun('${PDFLATEX} ${PDFLATEXFLAGS} ${SRCFILE}',shell=False) class xelatex(tex): texfun,vars=Task.compile_fun('${XELATEX} ${XELATEXFLAGS} ${SRCFILE}',shell=False) class dvips(Task.Task): run_str='${DVIPS} ${DVIPSFLAGS} ${SRC} -o ${TGT}' color='BLUE' after=['latex','pdflatex','xelatex'] class dvipdf(Task.Task): run_str='${DVIPDF} ${DVIPDFFLAGS} ${SRC} ${TGT}' color='BLUE' after=['latex','pdflatex','xelatex'] class pdf2ps(Task.Task): run_str='${PDF2PS} ${PDF2PSFLAGS} ${SRC} ${TGT}' color='BLUE' after=['latex','pdflatex','xelatex'] @feature('tex') @before_method('process_source') def apply_tex(self): if not getattr(self,'type',None)in('latex','pdflatex','xelatex'): self.type='pdflatex' outs=Utils.to_list(getattr(self,'outs',[])) try: self.generator.bld.conf except AttributeError: default_prompt=False else: default_prompt=True self.env.PROMPT_LATEX=getattr(self,'prompt',default_prompt) deps_lst=[] if getattr(self,'deps',None): deps=self.to_list(self.deps) for dep in deps: if isinstance(dep,str): n=self.path.find_resource(dep) if not n: self.bld.fatal('Could not find %r for %r'%(dep,self)) if not n in deps_lst: deps_lst.append(n) elif isinstance(dep,Node.Node): deps_lst.append(dep) for node in self.to_nodes(self.source): if self.type=='latex': task=self.create_task('latex',node,node.change_ext('.dvi')) elif self.type=='pdflatex': task=self.create_task('pdflatex',node,node.change_ext('.pdf')) elif self.type=='xelatex': task=self.create_task('xelatex',node,node.change_ext('.pdf')) task.env=self.env if deps_lst: for n in deps_lst: if not n in task.dep_nodes: task.dep_nodes.append(n) if hasattr(self,'texinputs_nodes'): task.texinputs_nodes=self.texinputs_nodes else: task.texinputs_nodes=[node.parent,node.parent.get_bld(),self.path,self.path.get_bld()] lst=os.environ.get('TEXINPUTS','') if self.env.TEXINPUTS: lst+=os.pathsep+self.env.TEXINPUTS if lst: lst=lst.split(os.pathsep) for x in lst: if x: if os.path.isabs(x): p=self.bld.root.find_node(x) if p: task.texinputs_nodes.append(p) else: Logs.error('Invalid TEXINPUTS folder %s',x) else: Logs.error('Cannot resolve relative paths in TEXINPUTS %s',x) if self.type=='latex': if'ps'in outs: tsk=self.create_task('dvips',task.outputs,node.change_ext('.ps')) tsk.env.env=dict(os.environ) if'pdf'in outs: tsk=self.create_task('dvipdf',task.outputs,node.change_ext('.pdf')) tsk.env.env=dict(os.environ) elif self.type=='pdflatex': if'ps'in outs: self.create_task('pdf2ps',task.outputs,node.change_ext('.ps')) self.source=[] def configure(self): v=self.env for p in'tex latex pdflatex xelatex bibtex dvips dvipdf ps2pdf makeindex pdf2ps makeglossaries'.split(): try: self.find_program(p,var=p.upper()) except self.errors.ConfigurationError: pass v.DVIPSFLAGS='-Ppdf' ntpsec-1.1.0+dfsg1/waflib/Tools/compiler_c.py0000644000175000017500000000340313172024730020652 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re from waflib.Tools import ccroot from waflib import Utils from waflib.Logs import debug c_compiler={'win32':['msvc','gcc','clang'],'cygwin':['gcc'],'darwin':['clang','gcc'],'aix':['xlc','gcc','clang'],'linux':['gcc','clang','icc'],'sunos':['suncc','gcc'],'irix':['gcc','irixcc'],'hpux':['gcc'],'osf1V':['gcc'],'gnu':['gcc','clang'],'java':['gcc','msvc','clang','icc'],'default':['clang','gcc'],} def default_compilers(): build_platform=Utils.unversioned_sys_platform() possible_compiler_list=c_compiler.get(build_platform,c_compiler['default']) return' '.join(possible_compiler_list) def configure(conf): try: test_for_compiler=conf.options.check_c_compiler or default_compilers() except AttributeError: conf.fatal("Add options(opt): opt.load('compiler_c')") for compiler in re.split('[ ,]+',test_for_compiler): conf.env.stash() conf.start_msg('Checking for %r (C compiler)'%compiler) try: conf.load(compiler) except conf.errors.ConfigurationError as e: conf.env.revert() conf.end_msg(False) debug('compiler_c: %r',e) else: if conf.env.CC: conf.end_msg(conf.env.get_flat('CC')) conf.env.COMPILER_CC=compiler conf.env.commit() break conf.env.revert() conf.end_msg(False) else: conf.fatal('could not configure a C compiler!') def options(opt): test_for_compiler=default_compilers() opt.load_special_tools('c_*.py',ban=['c_dumbpreproc.py']) cc_compiler_opts=opt.add_option_group('Configuration options') cc_compiler_opts.add_option('--check-c-compiler',default=None,help='list of C compilers to try [%s]'%test_for_compiler,dest="check_c_compiler") for x in test_for_compiler.split(): opt.load('%s'%x) ntpsec-1.1.0+dfsg1/waflib/Tools/irixcc.py0000644000175000017500000000207613210576007020026 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar from waflib.Configure import conf @conf def find_irixcc(conf): v=conf.env cc=None if v.CC: cc=v.CC elif'CC'in conf.environ: cc=conf.environ['CC'] if not cc: cc=conf.find_program('cc',var='CC') if not cc: conf.fatal('irixcc was not found') try: conf.cmd_and_log(cc+['-version']) except Exception: conf.fatal('%r -version could not be executed'%cc) v.CC=cc v.CC_NAME='irix' @conf def irixcc_common_flags(conf): v=conf.env v.CC_SRC_F='' v.CC_TGT_F=['-c','-o'] v.CPPPATH_ST='-I%s' v.DEFINES_ST='-D%s' if not v.LINK_CC: v.LINK_CC=v.CC v.CCLNK_SRC_F='' v.CCLNK_TGT_F=['-o'] v.LIB_ST='-l%s' v.LIBPATH_ST='-L%s' v.STLIB_ST='-l%s' v.STLIBPATH_ST='-L%s' v.cprogram_PATTERN='%s' v.cshlib_PATTERN='lib%s.so' v.cstlib_PATTERN='lib%s.a' def configure(conf): conf.find_irixcc() conf.find_cpp() conf.find_ar() conf.irixcc_common_flags() conf.cc_load_tools() conf.cc_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/compiler_fc.py0000644000175000017500000000323013172024730021016 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re from waflib import Utils,Logs from waflib.Tools import fc fc_compiler={'win32':['gfortran','ifort'],'darwin':['gfortran','g95','ifort'],'linux':['gfortran','g95','ifort'],'java':['gfortran','g95','ifort'],'default':['gfortran'],'aix':['gfortran']} def default_compilers(): build_platform=Utils.unversioned_sys_platform() possible_compiler_list=fc_compiler.get(build_platform,fc_compiler['default']) return' '.join(possible_compiler_list) def configure(conf): try: test_for_compiler=conf.options.check_fortran_compiler or default_compilers() except AttributeError: conf.fatal("Add options(opt): opt.load('compiler_fc')") for compiler in re.split('[ ,]+',test_for_compiler): conf.env.stash() conf.start_msg('Checking for %r (Fortran compiler)'%compiler) try: conf.load(compiler) except conf.errors.ConfigurationError as e: conf.env.revert() conf.end_msg(False) Logs.debug('compiler_fortran: %r',e) else: if conf.env.FC: conf.end_msg(conf.env.get_flat('FC')) conf.env.COMPILER_FORTRAN=compiler conf.env.commit() break conf.env.revert() conf.end_msg(False) else: conf.fatal('could not configure a Fortran compiler!') def options(opt): test_for_compiler=default_compilers() opt.load_special_tools('fc_*.py') fortran_compiler_opts=opt.add_option_group('Configuration options') fortran_compiler_opts.add_option('--check-fortran-compiler',default=None,help='list of Fortran compiler to try [%s]'%test_for_compiler,dest="check_fortran_compiler") for x in test_for_compiler.split(): opt.load('%s'%x) ntpsec-1.1.0+dfsg1/waflib/Tools/icpc.py0000644000175000017500000000077713210576007017471 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import sys from waflib.Tools import ccroot,ar,gxx from waflib.Configure import conf @conf def find_icpc(conf): cxx=conf.find_program('icpc',var='CXX') conf.get_cc_version(cxx,icc=True) conf.env.CXX_NAME='icc' def configure(conf): conf.find_icpc() conf.find_ar() conf.gxx_common_flags() conf.gxx_modifier_platform() conf.cxx_load_tools() conf.cxx_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/d_scan.py0000644000175000017500000000557013210576031017773 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re from waflib import Utils def filter_comments(filename): txt=Utils.readf(filename) i=0 buf=[] max=len(txt) begin=0 while i\d*)\.(?P\d*)",re.I).search if Utils.is_win32: cmd=fc else: cmd=fc+['-logo'] out,err=fc_config.getoutput(conf,cmd,stdin=False) match=version_re(out)or version_re(err) if not match: conf.fatal('cannot determine ifort version.') k=match.groupdict() conf.env.FC_VERSION=(k['major'],k['minor']) def configure(conf): if Utils.is_win32: compiler,version,path,includes,libdirs,arch=conf.detect_ifort() v=conf.env v.DEST_CPU=arch v.PATH=path v.INCLUDES=includes v.LIBPATH=libdirs v.MSVC_COMPILER=compiler try: v.MSVC_VERSION=float(version) except ValueError: v.MSVC_VERSION=float(version[:-3]) conf.find_ifort_win32() conf.ifort_modifier_win32() else: conf.find_ifort() conf.find_program('xiar',var='AR') conf.find_ar() conf.fc_flags() conf.fc_add_flags() conf.ifort_modifier_platform() all_ifort_platforms=[('intel64','amd64'),('em64t','amd64'),('ia32','x86'),('Itanium','ia64')] @conf def gather_ifort_versions(conf,versions): version_pattern=re.compile('^...?.?\....?.?') try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Wow6432node\\Intel\\Compilers\\Fortran') except WindowsError: try: all_versions=Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE,'SOFTWARE\\Intel\\Compilers\\Fortran') except WindowsError: return index=0 while 1: try: version=Utils.winreg.EnumKey(all_versions,index) except WindowsError: break index+=1 if not version_pattern.match(version): continue targets={} for target,arch in all_ifort_platforms: if target=='intel64': targetDir='EM64T_NATIVE' else: targetDir=target try: Utils.winreg.OpenKey(all_versions,version+'\\'+targetDir) icl_version=Utils.winreg.OpenKey(all_versions,version) path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir') except WindowsError: pass else: batch_file=os.path.join(path,'bin','ifortvars.bat') if os.path.isfile(batch_file): targets[target]=target_compiler(conf,'intel',arch,version,target,batch_file) for target,arch in all_ifort_platforms: try: icl_version=Utils.winreg.OpenKey(all_versions,version+'\\'+target) path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir') except WindowsError: continue else: batch_file=os.path.join(path,'bin','ifortvars.bat') if os.path.isfile(batch_file): targets[target]=target_compiler(conf,'intel',arch,version,target,batch_file) major=version[0:2] versions['intel '+major]=targets @conf def setup_ifort(conf,versiondict): platforms=Utils.to_list(conf.env.MSVC_TARGETS)or[i for i,j in all_ifort_platforms] desired_versions=conf.env.MSVC_VERSIONS or list(reversed(list(versiondict.keys()))) for version in desired_versions: try: targets=versiondict[version] except KeyError: continue for arch in platforms: try: cfg=targets[arch] except KeyError: continue cfg.evaluate() if cfg.is_valid: compiler,revision=version.rsplit(' ',1) return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu conf.fatal('ifort: Impossible to find a valid architecture for building %r - %r'%(desired_versions,list(versiondict.keys()))) @conf def get_ifort_version_win32(conf,compiler,version,target,vcvars): try: conf.msvc_cnt+=1 except AttributeError: conf.msvc_cnt=1 batfile=conf.bldnode.make_node('waf-print-msvc-%d.bat'%conf.msvc_cnt) batfile.write("""@echo off set INCLUDE= set LIB= call "%s" %s echo PATH=%%PATH%% echo INCLUDE=%%INCLUDE%% echo LIB=%%LIB%%;%%LIBPATH%% """%(vcvars,target)) sout=conf.cmd_and_log(['cmd.exe','/E:on','/V:on','/C',batfile.abspath()]) batfile.delete() lines=sout.splitlines() if not lines[0]: lines.pop(0) MSVC_PATH=MSVC_INCDIR=MSVC_LIBDIR=None for line in lines: if line.startswith('PATH='): path=line[5:] MSVC_PATH=path.split(';') elif line.startswith('INCLUDE='): MSVC_INCDIR=[i for i in line[8:].split(';')if i] elif line.startswith('LIB='): MSVC_LIBDIR=[i for i in line[4:].split(';')if i] if None in(MSVC_PATH,MSVC_INCDIR,MSVC_LIBDIR): conf.fatal('ifort: Could not find a valid architecture for building (get_ifort_version_win32)') env=dict(os.environ) env.update(PATH=path) compiler_name,linker_name,lib_name=_get_prog_names(conf,compiler) fc=conf.find_program(compiler_name,path_list=MSVC_PATH) if'CL'in env: del(env['CL']) try: conf.cmd_and_log(fc+['/help'],env=env) except UnicodeError: st=Utils.ex_stack() if conf.logger: conf.logger.error(st) conf.fatal('ifort: Unicode error - check the code page?') except Exception as e: Logs.debug('ifort: get_ifort_version: %r %r %r -> failure %s',compiler,version,target,str(e)) conf.fatal('ifort: cannot run the compiler in get_ifort_version (run with -v to display errors)') else: Logs.debug('ifort: get_ifort_version: %r %r %r -> OK',compiler,version,target) finally: conf.env[compiler_name]='' return(MSVC_PATH,MSVC_INCDIR,MSVC_LIBDIR) class target_compiler(object): def __init__(self,ctx,compiler,cpu,version,bat_target,bat,callback=None): self.conf=ctx self.name=None self.is_valid=False self.is_done=False self.compiler=compiler self.cpu=cpu self.version=version self.bat_target=bat_target self.bat=bat self.callback=callback def evaluate(self): if self.is_done: return self.is_done=True try: vs=self.conf.get_ifort_version_win32(self.compiler,self.version,self.bat_target,self.bat) except Errors.ConfigurationError: self.is_valid=False return if self.callback: vs=self.callback(self,vs) self.is_valid=True (self.bindirs,self.incdirs,self.libdirs)=vs def __str__(self): return str((self.bindirs,self.incdirs,self.libdirs)) def __repr__(self): return repr((self.bindirs,self.incdirs,self.libdirs)) @conf def detect_ifort(self): return self.setup_ifort(self.get_ifort_versions(False)) @conf def get_ifort_versions(self,eval_and_save=True): dct={} self.gather_ifort_versions(dct) return dct def _get_prog_names(self,compiler): if compiler=='intel': compiler_name='ifort' linker_name='XILINK' lib_name='XILIB' else: compiler_name='CL' linker_name='LINK' lib_name='LIB' return compiler_name,linker_name,lib_name @conf def find_ifort_win32(conf): v=conf.env path=v.PATH compiler=v.MSVC_COMPILER version=v.MSVC_VERSION compiler_name,linker_name,lib_name=_get_prog_names(conf,compiler) v.IFORT_MANIFEST=(compiler=='intel'and version>=11) fc=conf.find_program(compiler_name,var='FC',path_list=path) env=dict(conf.environ) if path: env.update(PATH=';'.join(path)) if not conf.cmd_and_log(fc+['/nologo','/help'],env=env): conf.fatal('not intel fortran compiler could not be identified') v.FC_NAME='IFORT' if not v.LINK_FC: conf.find_program(linker_name,var='LINK_FC',path_list=path,mandatory=True) if not v.AR: conf.find_program(lib_name,path_list=path,var='AR',mandatory=True) v.ARFLAGS=['/nologo'] if v.IFORT_MANIFEST: conf.find_program('MT',path_list=path,var='MT') v.MTFLAGS=['/nologo'] try: conf.load('winres') except Errors.WafError: Logs.warn('Resource compiler not found. Compiling resource file is disabled') @after_method('apply_link') @feature('fc') def apply_flags_ifort(self): if not self.env.IFORT_WIN32 or not getattr(self,'link_task',None): return is_static=isinstance(self.link_task,ccroot.stlink_task) subsystem=getattr(self,'subsystem','') if subsystem: subsystem='/subsystem:%s'%subsystem flags=is_static and'ARFLAGS'or'LINKFLAGS' self.env.append_value(flags,subsystem) if not is_static: for f in self.env.LINKFLAGS: d=f.lower() if d[1:]=='debug': pdbnode=self.link_task.outputs[0].change_ext('.pdb') self.link_task.outputs.append(pdbnode) if getattr(self,'install_task',None): self.pdb_install_task=self.add_install_files(install_to=self.install_task.install_to,install_from=pdbnode) break @feature('fcprogram','fcshlib','fcprogram_test') @after_method('apply_link') def apply_manifest_ifort(self): if self.env.IFORT_WIN32 and getattr(self,'link_task',None): self.link_task.env.FC=self.env.LINK_FC if self.env.IFORT_WIN32 and self.env.IFORT_MANIFEST and getattr(self,'link_task',None): out_node=self.link_task.outputs[0] man_node=out_node.parent.find_or_declare(out_node.name+'.manifest') self.link_task.outputs.append(man_node) self.env.DO_MANIFEST=True ntpsec-1.1.0+dfsg1/waflib/Tools/ruby.py0000644000175000017500000000741013210576007017523 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os from waflib import Options,Utils,Task from waflib.TaskGen import before_method,feature,extension from waflib.Configure import conf @feature('rubyext') @before_method('apply_incpaths','process_source','apply_bundle','apply_link') def init_rubyext(self): self.install_path='${ARCHDIR_RUBY}' self.uselib=self.to_list(getattr(self,'uselib','')) if not'RUBY'in self.uselib: self.uselib.append('RUBY') if not'RUBYEXT'in self.uselib: self.uselib.append('RUBYEXT') @feature('rubyext') @before_method('apply_link','propagate_uselib_vars') def apply_ruby_so_name(self): self.env.cshlib_PATTERN=self.env.cxxshlib_PATTERN=self.env.rubyext_PATTERN @conf def check_ruby_version(self,minver=()): ruby=self.find_program('ruby',var='RUBY',value=Options.options.rubybinary) try: version=self.cmd_and_log(ruby+['-e','puts defined?(VERSION) ? VERSION : RUBY_VERSION']).strip() except Exception: self.fatal('could not determine ruby version') self.env.RUBY_VERSION=version try: ver=tuple(map(int,version.split("."))) except Exception: self.fatal('unsupported ruby version %r'%version) cver='' if minver: cver='> '+'.'.join(str(x)for x in minver) if ver=(1,9,0): ruby_hdrdir=read_config('rubyhdrdir') cpppath+=ruby_hdrdir if version>=(2,0,0): cpppath+=read_config('rubyarchhdrdir') cpppath+=[os.path.join(ruby_hdrdir[0],read_config('arch')[0])] self.check(header_name='ruby.h',includes=cpppath,errmsg='could not find ruby header file',link_header_test=False) self.env.LIBPATH_RUBYEXT=read_config('libdir') self.env.LIBPATH_RUBYEXT+=archdir self.env.INCLUDES_RUBYEXT=cpppath self.env.CFLAGS_RUBYEXT=read_config('CCDLFLAGS') self.env.rubyext_PATTERN='%s.'+read_config('DLEXT')[0] flags=read_config('LDSHARED') while flags and flags[0][0]!='-': flags=flags[1:] if len(flags)>1 and flags[1]=="ppc": flags=flags[2:] self.env.LINKFLAGS_RUBYEXT=flags self.env.LINKFLAGS_RUBYEXT+=read_config('LIBS') self.env.LINKFLAGS_RUBYEXT+=read_config('LIBRUBYARG_SHARED') if Options.options.rubyarchdir: self.env.ARCHDIR_RUBY=Options.options.rubyarchdir else: self.env.ARCHDIR_RUBY=read_config('sitearchdir')[0] if Options.options.rubylibdir: self.env.LIBDIR_RUBY=Options.options.rubylibdir else: self.env.LIBDIR_RUBY=read_config('sitelibdir')[0] @conf def check_ruby_module(self,module_name): self.start_msg('Ruby module %s'%module_name) try: self.cmd_and_log(self.env.RUBY+['-e','require \'%s\';puts 1'%module_name]) except Exception: self.end_msg(False) self.fatal('Could not find the ruby module %r'%module_name) self.end_msg(True) @extension('.rb') def process(self,node): return self.create_task('run_ruby',node) class run_ruby(Task.Task): run_str='${RUBY} ${RBFLAGS} -I ${SRC[0].parent.abspath()} ${SRC}' def options(opt): opt.add_option('--with-ruby-archdir',type='string',dest='rubyarchdir',help='Specify directory where to install arch specific files') opt.add_option('--with-ruby-libdir',type='string',dest='rubylibdir',help='Specify alternate ruby library path') opt.add_option('--with-ruby-binary',type='string',dest='rubybinary',help='Specify alternate ruby binary') ntpsec-1.1.0+dfsg1/waflib/Tools/d.py0000644000175000017500000000400113210576007016756 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Utils,Task,Errors from waflib.TaskGen import taskgen_method,feature,extension from waflib.Tools import d_scan,d_config from waflib.Tools.ccroot import link_task,stlink_task class d(Task.Task): color='GREEN' run_str='${D} ${DFLAGS} ${DINC_ST:INCPATHS} ${D_SRC_F:SRC} ${D_TGT_F:TGT}' scan=d_scan.scan class d_with_header(d): run_str='${D} ${DFLAGS} ${DINC_ST:INCPATHS} ${D_HDR_F:tgt.outputs[1].bldpath()} ${D_SRC_F:SRC} ${D_TGT_F:tgt.outputs[0].bldpath()}' class d_header(Task.Task): color='BLUE' run_str='${D} ${D_HEADER} ${SRC}' class dprogram(link_task): run_str='${D_LINKER} ${LINKFLAGS} ${DLNK_SRC_F}${SRC} ${DLNK_TGT_F:TGT} ${RPATH_ST:RPATH} ${DSTLIB_MARKER} ${DSTLIBPATH_ST:STLIBPATH} ${DSTLIB_ST:STLIB} ${DSHLIB_MARKER} ${DLIBPATH_ST:LIBPATH} ${DSHLIB_ST:LIB}' inst_to='${BINDIR}' class dshlib(dprogram): inst_to='${LIBDIR}' class dstlib(stlink_task): pass @extension('.d','.di','.D') def d_hook(self,node): ext=Utils.destos_to_binfmt(self.env.DEST_OS)=='pe'and'obj'or'o' out='%s.%d.%s'%(node.name,self.idx,ext) def create_compiled_task(self,name,node): task=self.create_task(name,node,node.parent.find_or_declare(out)) try: self.compiled_tasks.append(task) except AttributeError: self.compiled_tasks=[task] return task if getattr(self,'generate_headers',None): tsk=create_compiled_task(self,'d_with_header',node) tsk.outputs.append(node.change_ext(self.env.DHEADER_ext)) else: tsk=create_compiled_task(self,'d',node) return tsk @taskgen_method def generate_header(self,filename): try: self.header_lst.append([filename,self.install_path]) except AttributeError: self.header_lst=[[filename,self.install_path]] @feature('d') def process_header(self): for i in getattr(self,'header_lst',[]): node=self.path.find_resource(i[0]) if not node: raise Errors.WafError('file %r not found on d obj'%i[0]) self.create_task('d_header',node,node.change_ext('.di')) ntpsec-1.1.0+dfsg1/waflib/Tools/compiler_cxx.py0000644000175000017500000000342013172024730021231 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re from waflib.Tools import ccroot from waflib import Utils from waflib.Logs import debug cxx_compiler={'win32':['msvc','g++','clang++'],'cygwin':['g++'],'darwin':['clang++','g++'],'aix':['xlc++','g++','clang++'],'linux':['g++','clang++','icpc'],'sunos':['sunc++','g++'],'irix':['g++'],'hpux':['g++'],'osf1V':['g++'],'gnu':['g++','clang++'],'java':['g++','msvc','clang++','icpc'],'default':['clang++','g++']} def default_compilers(): build_platform=Utils.unversioned_sys_platform() possible_compiler_list=cxx_compiler.get(build_platform,cxx_compiler['default']) return' '.join(possible_compiler_list) def configure(conf): try: test_for_compiler=conf.options.check_cxx_compiler or default_compilers() except AttributeError: conf.fatal("Add options(opt): opt.load('compiler_cxx')") for compiler in re.split('[ ,]+',test_for_compiler): conf.env.stash() conf.start_msg('Checking for %r (C++ compiler)'%compiler) try: conf.load(compiler) except conf.errors.ConfigurationError as e: conf.env.revert() conf.end_msg(False) debug('compiler_cxx: %r',e) else: if conf.env.CXX: conf.end_msg(conf.env.get_flat('CXX')) conf.env.COMPILER_CXX=compiler conf.env.commit() break conf.env.revert() conf.end_msg(False) else: conf.fatal('could not configure a C++ compiler!') def options(opt): test_for_compiler=default_compilers() opt.load_special_tools('cxx_*.py') cxx_compiler_opts=opt.add_option_group('Configuration options') cxx_compiler_opts.add_option('--check-cxx-compiler',default=None,help='list of C++ compilers to try [%s]'%test_for_compiler,dest="check_cxx_compiler") for x in test_for_compiler.split(): opt.load('%s'%x) ntpsec-1.1.0+dfsg1/waflib/Tools/lua.py0000644000175000017500000000111713210576007017321 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.TaskGen import extension from waflib import Task @extension('.lua') def add_lua(self,node): tsk=self.create_task('luac',node,node.change_ext('.luac')) inst_to=getattr(self,'install_path',self.env.LUADIR and'${LUADIR}'or None) if inst_to: self.add_install_files(install_to=inst_to,install_from=tsk.outputs) return tsk class luac(Task.Task): run_str='${LUAC} -s -o ${TGT} ${SRC}' color='PINK' def configure(conf): conf.find_program('luac',var='LUAC') ntpsec-1.1.0+dfsg1/waflib/Tools/flex.py0000644000175000017500000000227113210576031017475 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re from waflib import Task,TaskGen from waflib.Tools import ccroot def decide_ext(self,node): if'cxx'in self.features: return['.lex.cc'] return['.lex.c'] def flexfun(tsk): env=tsk.env bld=tsk.generator.bld wd=bld.variant_dir def to_list(xx): if isinstance(xx,str): return[xx] return xx tsk.last_cmd=lst=[] lst.extend(to_list(env.FLEX)) lst.extend(to_list(env.FLEXFLAGS)) inputs=[a.path_from(tsk.get_cwd())for a in tsk.inputs] if env.FLEX_MSYS: inputs=[x.replace(os.sep,'/')for x in inputs] lst.extend(inputs) lst=[x for x in lst if x] txt=bld.cmd_and_log(lst,cwd=wd,env=env.env or None,quiet=0) tsk.outputs[0].write(txt.replace('\r\n','\n').replace('\r','\n')) TaskGen.declare_chain(name='flex',rule=flexfun,ext_in='.l',decider=decide_ext,) Task.classes['flex'].vars=['FLEXFLAGS','FLEX'] ccroot.USELIB_VARS['c'].add('FLEXFLAGS') ccroot.USELIB_VARS['cxx'].add('FLEXFLAGS') def configure(conf): conf.find_program('flex',var='FLEX') conf.env.FLEXFLAGS=['-t'] if re.search(r"\\msys\\[0-9.]+\\bin\\flex.exe$",conf.env.FLEX[0]): conf.env.FLEX_MSYS=True ntpsec-1.1.0+dfsg1/waflib/Tools/md5_tstamp.py0000644000175000017500000000133413210576007020616 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,stat from waflib import Utils,Build,Node STRONGEST=True Build.SAVED_ATTRS.append('hashes_md5_tstamp') def h_file(self): filename=self.abspath() st=os.stat(filename) cache=self.ctx.hashes_md5_tstamp if filename in cache and cache[filename][0]==st.st_mtime: return cache[filename][1] global STRONGEST if STRONGEST: ret=Utils.h_file(filename) else: if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('Not a file') ret=Utils.md5(str((st.st_mtime,st.st_size)).encode()).digest() cache[filename]=(st.st_mtime,ret) return ret h_file.__doc__=Node.Node.h_file.__doc__ Node.Node.h_file=h_file ntpsec-1.1.0+dfsg1/waflib/Tools/c_config.py0000755000175000017500000006450113210576031020315 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from __future__ import with_statement import os,re,shlex from waflib import Build,Utils,Task,Options,Logs,Errors,Runner from waflib.TaskGen import after_method,feature from waflib.Configure import conf WAF_CONFIG_H='config.h' DEFKEYS='define_key' INCKEYS='include_key' cfg_ver={'atleast-version':'>=','exact-version':'==','max-version':'<=',} SNIP_FUNCTION=''' int main(int argc, char **argv) { void (*p)(); (void)argc; (void)argv; p=(void(*)())(%s); return !p; } ''' SNIP_TYPE=''' int main(int argc, char **argv) { (void)argc; (void)argv; if ((%(type_name)s *) 0) return 0; if (sizeof (%(type_name)s)) return 0; return 1; } ''' SNIP_EMPTY_PROGRAM=''' int main(int argc, char **argv) { (void)argc; (void)argv; return 0; } ''' SNIP_FIELD=''' int main(int argc, char **argv) { char *off; (void)argc; (void)argv; off = (char*) &((%(type_name)s*)0)->%(field_name)s; return (size_t) off < sizeof(%(type_name)s); } ''' MACRO_TO_DESTOS={'__linux__':'linux','__GNU__':'gnu','__FreeBSD__':'freebsd','__NetBSD__':'netbsd','__OpenBSD__':'openbsd','__sun':'sunos','__hpux':'hpux','__sgi':'irix','_AIX':'aix','__CYGWIN__':'cygwin','__MSYS__':'cygwin','_UWIN':'uwin','_WIN64':'win32','_WIN32':'win32','__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__':'darwin','__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__':'darwin','__QNX__':'qnx','__native_client__':'nacl'} MACRO_TO_DEST_CPU={'__x86_64__':'x86_64','__amd64__':'x86_64','__i386__':'x86','__ia64__':'ia','__mips__':'mips','__sparc__':'sparc','__alpha__':'alpha','__aarch64__':'aarch64','__thumb__':'thumb','__arm__':'arm','__hppa__':'hppa','__powerpc__':'powerpc','__ppc__':'powerpc','__convex__':'convex','__m68k__':'m68k','__s390x__':'s390x','__s390__':'s390','__sh__':'sh','__xtensa__':'xtensa',} @conf def parse_flags(self,line,uselib_store,env=None,force_static=False,posix=None): assert(isinstance(line,str)) env=env or self.env if posix is None: posix=True if'\\'in line: posix=('\\ 'in line)or('\\\\'in line) lex=shlex.shlex(line,posix=posix) lex.whitespace_split=True lex.commenters='' lst=list(lex) uselib=uselib_store def app(var,val): env.append_value('%s_%s'%(var,uselib),val) def appu(var,val): env.append_unique('%s_%s'%(var,uselib),val) static=False while lst: x=lst.pop(0) st=x[:2] ot=x[2:] if st=='-I'or st=='/I': if not ot: ot=lst.pop(0) appu('INCLUDES',ot) elif st=='-i': tmp=[x,lst.pop(0)] app('CFLAGS',tmp) app('CXXFLAGS',tmp) elif st=='-D'or(env.CXX_NAME=='msvc'and st=='/D'): if not ot: ot=lst.pop(0) app('DEFINES',ot) elif st=='-l': if not ot: ot=lst.pop(0) prefix='STLIB'if(force_static or static)else'LIB' app(prefix,ot) elif st=='-L': if not ot: ot=lst.pop(0) prefix='STLIBPATH'if(force_static or static)else'LIBPATH' appu(prefix,ot) elif x.startswith('/LIBPATH:'): prefix='STLIBPATH'if(force_static or static)else'LIBPATH' appu(prefix,x.replace('/LIBPATH:','')) elif x.startswith('-std='): prefix='CXXFLAGS'if'++'in x else'CFLAGS' app(prefix,x) elif x.startswith('+')or x in('-pthread','-fPIC','-fpic','-fPIE','-fpie'): app('CFLAGS',x) app('CXXFLAGS',x) app('LINKFLAGS',x) elif x=='-framework': appu('FRAMEWORK',lst.pop(0)) elif x.startswith('-F'): appu('FRAMEWORKPATH',x[2:]) elif x=='-Wl,-rpath'or x=='-Wl,-R': app('RPATH',lst.pop(0).lstrip('-Wl,')) elif x.startswith('-Wl,-R,'): app('RPATH',x[7:]) elif x.startswith('-Wl,-R'): app('RPATH',x[6:]) elif x.startswith('-Wl,-rpath,'): app('RPATH',x[11:]) elif x=='-Wl,-Bstatic'or x=='-Bstatic': static=True elif x=='-Wl,-Bdynamic'or x=='-Bdynamic': static=False elif x.startswith('-Wl')or x in('-rdynamic','-pie'): app('LINKFLAGS',x) elif x.startswith(('-m','-f','-dynamic','-O')): app('CFLAGS',x) app('CXXFLAGS',x) elif x.startswith('-bundle'): app('LINKFLAGS',x) elif x.startswith(('-undefined','-Xlinker')): arg=lst.pop(0) app('LINKFLAGS',[x,arg]) elif x.startswith(('-arch','-isysroot')): tmp=[x,lst.pop(0)] app('CFLAGS',tmp) app('CXXFLAGS',tmp) app('LINKFLAGS',tmp) elif x.endswith(('.a','.so','.dylib','.lib')): appu('LINKFLAGS',x) else: self.to_log('Unhandled flag %r'%x) @conf def validate_cfg(self,kw): if not'path'in kw: if not self.env.PKGCONFIG: self.find_program('pkg-config',var='PKGCONFIG') kw['path']=self.env.PKGCONFIG if'atleast_pkgconfig_version'in kw: if not'msg'in kw: kw['msg']='Checking for pkg-config version >= %r'%kw['atleast_pkgconfig_version'] return if not'okmsg'in kw: kw['okmsg']='yes' if not'errmsg'in kw: kw['errmsg']='not found' if'modversion'in kw: if not'msg'in kw: kw['msg']='Checking for %r version'%kw['modversion'] if not'uselib_store'in kw: kw['uselib_store']=kw['modversion'] if not'define_name'in kw: kw['define_name']='%s_VERSION'%Utils.quote_define_name(kw['uselib_store']) return if not'package'in kw: raise ValueError('a package name is required') if not'uselib_store'in kw: kw['uselib_store']=kw['package'].upper() if not'define_name'in kw: kw['define_name']=self.have_define(kw['uselib_store']) if not'msg'in kw: kw['msg']='Checking for %r'%(kw['package']or kw['path']) for x in cfg_ver: y=x.replace('-','_') if y in kw: package=kw['package'] if Logs.verbose: Logs.warn('Passing %r to conf.check_cfg() is obsolete, pass parameters directly, eg:',y) Logs.warn(" conf.check_cfg(package='%s', args=['--libs', '--cflags', '%s >= 1.6'])",package,package) if not'msg'in kw: kw['msg']='Checking for %r %s %s'%(package,cfg_ver[x],kw[y]) break @conf def exec_cfg(self,kw): path=Utils.to_list(kw['path']) env=self.env.env or None if kw.get('pkg_config_path'): if not env: env=dict(self.environ) env['PKG_CONFIG_PATH']=kw['pkg_config_path'] def define_it(): define_name=kw['define_name'] if kw.get('global_define',1): self.define(define_name,1,False) else: self.env.append_unique('DEFINES_%s'%kw['uselib_store'],"%s=1"%define_name) if kw.get('add_have_to_env',1): self.env[define_name]=1 if'atleast_pkgconfig_version'in kw: cmd=path+['--atleast-pkgconfig-version=%s'%kw['atleast_pkgconfig_version']] self.cmd_and_log(cmd,env=env) if not'okmsg'in kw: kw['okmsg']='yes' return for x in cfg_ver: y=x.replace('-','_') if y in kw: self.cmd_and_log(path+['--%s=%s'%(x,kw[y]),kw['package']],env=env) if not'okmsg'in kw: kw['okmsg']='yes' define_it() break if'modversion'in kw: version=self.cmd_and_log(path+['--modversion',kw['modversion']],env=env).strip() self.define(kw['define_name'],version) return version lst=[]+path defi=kw.get('define_variable') if not defi: defi=self.env.PKG_CONFIG_DEFINES or{} for key,val in defi.items(): lst.append('--define-variable=%s=%s'%(key,val)) static=kw.get('force_static',False) if'args'in kw: args=Utils.to_list(kw['args']) if'--static'in args or'--static-libs'in args: static=True lst+=args lst.extend(Utils.to_list(kw['package'])) if'variables'in kw: v_env=kw.get('env',self.env) vars=Utils.to_list(kw['variables']) for v in vars: val=self.cmd_and_log(lst+['--variable='+v],env=env).strip() var='%s_%s'%(kw['uselib_store'],v) v_env[var]=val if not'okmsg'in kw: kw['okmsg']='yes' return ret=self.cmd_and_log(lst,env=env) if not'okmsg'in kw: kw['okmsg']='yes' define_it() self.parse_flags(ret,kw['uselib_store'],kw.get('env',self.env),force_static=static,posix=kw.get('posix')) return ret @conf def check_cfg(self,*k,**kw): if k: lst=k[0].split() kw['package']=lst[0] kw['args']=' '.join(lst[1:]) self.validate_cfg(kw) if'msg'in kw: self.start_msg(kw['msg'],**kw) ret=None try: ret=self.exec_cfg(kw) except self.errors.WafError: if'errmsg'in kw: self.end_msg(kw['errmsg'],'YELLOW',**kw) if Logs.verbose>1: raise else: self.fatal('The configuration failed') else: if not ret: ret=True kw['success']=ret if'okmsg'in kw: self.end_msg(self.ret_msg(kw['okmsg'],kw),**kw) return ret def build_fun(bld): if bld.kw['compile_filename']: node=bld.srcnode.make_node(bld.kw['compile_filename']) node.write(bld.kw['code']) o=bld(features=bld.kw['features'],source=bld.kw['compile_filename'],target='testprog') for k,v in bld.kw.items(): setattr(o,k,v) if not bld.kw.get('quiet'): bld.conf.to_log("==>\n%s\n<=="%bld.kw['code']) @conf def validate_c(self,kw): if not'build_fun'in kw: kw['build_fun']=build_fun if not'env'in kw: kw['env']=self.env.derive() env=kw['env'] if not'compiler'in kw and not'features'in kw: kw['compiler']='c' if env.CXX_NAME and Task.classes.get('cxx'): kw['compiler']='cxx' if not self.env.CXX: self.fatal('a c++ compiler is required') else: if not self.env.CC: self.fatal('a c compiler is required') if not'compile_mode'in kw: kw['compile_mode']='c' if'cxx'in Utils.to_list(kw.get('features',[]))or kw.get('compiler','')=='cxx': kw['compile_mode']='cxx' if not'type'in kw: kw['type']='cprogram' if not'features'in kw: if not'header_name'in kw or kw.get('link_header_test',True): kw['features']=[kw['compile_mode'],kw['type']] else: kw['features']=[kw['compile_mode']] else: kw['features']=Utils.to_list(kw['features']) if not'compile_filename'in kw: kw['compile_filename']='test.c'+((kw['compile_mode']=='cxx')and'pp'or'') def to_header(dct): if'header_name'in dct: dct=Utils.to_list(dct['header_name']) return''.join(['#include <%s>\n'%x for x in dct]) return'' if'framework_name'in kw: fwkname=kw['framework_name'] if not'uselib_store'in kw: kw['uselib_store']=fwkname.upper() if not kw.get('no_header',False): if not'header_name'in kw: kw['header_name']=[] fwk='%s/%s.h'%(fwkname,fwkname) if kw.get('remove_dot_h'): fwk=fwk[:-2] kw['header_name']=Utils.to_list(kw['header_name'])+[fwk] kw['msg']='Checking for framework %s'%fwkname kw['framework']=fwkname if'function_name'in kw: fu=kw['function_name'] if not'msg'in kw: kw['msg']='Checking for function %s'%fu kw['code']=to_header(kw)+SNIP_FUNCTION%fu if not'uselib_store'in kw: kw['uselib_store']=fu.upper() if not'define_name'in kw: kw['define_name']=self.have_define(fu) elif'type_name'in kw: tu=kw['type_name'] if not'header_name'in kw: kw['header_name']='stdint.h' if'field_name'in kw: field=kw['field_name'] kw['code']=to_header(kw)+SNIP_FIELD%{'type_name':tu,'field_name':field} if not'msg'in kw: kw['msg']='Checking for field %s in %s'%(field,tu) if not'define_name'in kw: kw['define_name']=self.have_define((tu+'_'+field).upper()) else: kw['code']=to_header(kw)+SNIP_TYPE%{'type_name':tu} if not'msg'in kw: kw['msg']='Checking for type %s'%tu if not'define_name'in kw: kw['define_name']=self.have_define(tu.upper()) elif'header_name'in kw: if not'msg'in kw: kw['msg']='Checking for header %s'%kw['header_name'] l=Utils.to_list(kw['header_name']) assert len(l),'list of headers in header_name is empty' kw['code']=to_header(kw)+SNIP_EMPTY_PROGRAM if not'uselib_store'in kw: kw['uselib_store']=l[0].upper() if not'define_name'in kw: kw['define_name']=self.have_define(l[0]) if'lib'in kw: if not'msg'in kw: kw['msg']='Checking for library %s'%kw['lib'] if not'uselib_store'in kw: kw['uselib_store']=kw['lib'].upper() if'stlib'in kw: if not'msg'in kw: kw['msg']='Checking for static library %s'%kw['stlib'] if not'uselib_store'in kw: kw['uselib_store']=kw['stlib'].upper() if'fragment'in kw: kw['code']=kw['fragment'] if not'msg'in kw: kw['msg']='Checking for code snippet' if not'errmsg'in kw: kw['errmsg']='no' for(flagsname,flagstype)in(('cxxflags','compiler'),('cflags','compiler'),('linkflags','linker')): if flagsname in kw: if not'msg'in kw: kw['msg']='Checking for %s flags %s'%(flagstype,kw[flagsname]) if not'errmsg'in kw: kw['errmsg']='no' if not'execute'in kw: kw['execute']=False if kw['execute']: kw['features'].append('test_exec') kw['chmod']=Utils.O755 if not'errmsg'in kw: kw['errmsg']='not found' if not'okmsg'in kw: kw['okmsg']='yes' if not'code'in kw: kw['code']=SNIP_EMPTY_PROGRAM if self.env[INCKEYS]: kw['code']='\n'.join(['#include <%s>'%x for x in self.env[INCKEYS]])+'\n'+kw['code'] if kw.get('merge_config_header',False)or env.merge_config_header: kw['code']='%s\n\n%s'%(self.get_config_header(),kw['code']) env.DEFINES=[] if not kw.get('success'):kw['success']=None if'define_name'in kw: self.undefine(kw['define_name']) if not'msg'in kw: self.fatal('missing "msg" in conf.check(...)') @conf def post_check(self,*k,**kw): is_success=0 if kw['execute']: if kw['success']is not None: if kw.get('define_ret',False): is_success=kw['success'] else: is_success=(kw['success']==0) else: is_success=(kw['success']==0) if kw.get('define_name'): comment=kw.get('comment','') define_name=kw['define_name'] if kw['execute']and kw.get('define_ret')and isinstance(is_success,str): if kw.get('global_define',1): self.define(define_name,is_success,quote=kw.get('quote',1),comment=comment) else: if kw.get('quote',1): succ='"%s"'%is_success else: succ=int(is_success) val='%s=%s'%(define_name,succ) var='DEFINES_%s'%kw['uselib_store'] self.env.append_value(var,val) else: if kw.get('global_define',1): self.define_cond(define_name,is_success,comment=comment) else: var='DEFINES_%s'%kw['uselib_store'] self.env.append_value(var,'%s=%s'%(define_name,int(is_success))) if kw.get('add_have_to_env',1): if kw.get('uselib_store'): self.env[self.have_define(kw['uselib_store'])]=1 elif kw['execute']and kw.get('define_ret'): self.env[define_name]=is_success else: self.env[define_name]=int(is_success) if'header_name'in kw: if kw.get('auto_add_header_name',False): self.env.append_value(INCKEYS,Utils.to_list(kw['header_name'])) if is_success and'uselib_store'in kw: from waflib.Tools import ccroot _vars=set() for x in kw['features']: if x in ccroot.USELIB_VARS: _vars|=ccroot.USELIB_VARS[x] for k in _vars: x=k.lower() if x in kw: self.env.append_value(k+'_'+kw['uselib_store'],kw[x]) return is_success @conf def check(self,*k,**kw): self.validate_c(kw) self.start_msg(kw['msg'],**kw) ret=None try: ret=self.run_build(*k,**kw) except self.errors.ConfigurationError: self.end_msg(kw['errmsg'],'YELLOW',**kw) if Logs.verbose>1: raise else: self.fatal('The configuration failed') else: kw['success']=ret ret=self.post_check(*k,**kw) if not ret: self.end_msg(kw['errmsg'],'YELLOW',**kw) self.fatal('The configuration failed %r'%ret) else: self.end_msg(self.ret_msg(kw['okmsg'],kw),**kw) return ret class test_exec(Task.Task): color='PINK' def run(self): if getattr(self.generator,'rpath',None): if getattr(self.generator,'define_ret',False): self.generator.bld.retval=self.generator.bld.cmd_and_log([self.inputs[0].abspath()]) else: self.generator.bld.retval=self.generator.bld.exec_command([self.inputs[0].abspath()]) else: env=self.env.env or{} env.update(dict(os.environ)) for var in('LD_LIBRARY_PATH','DYLD_LIBRARY_PATH','PATH'): env[var]=self.inputs[0].parent.abspath()+os.path.pathsep+env.get(var,'') if getattr(self.generator,'define_ret',False): self.generator.bld.retval=self.generator.bld.cmd_and_log([self.inputs[0].abspath()],env=env) else: self.generator.bld.retval=self.generator.bld.exec_command([self.inputs[0].abspath()],env=env) @feature('test_exec') @after_method('apply_link') def test_exec_fun(self): self.create_task('test_exec',self.link_task.outputs[0]) @conf def check_cxx(self,*k,**kw): kw['compiler']='cxx' return self.check(*k,**kw) @conf def check_cc(self,*k,**kw): kw['compiler']='c' return self.check(*k,**kw) @conf def set_define_comment(self,key,comment): coms=self.env.DEFINE_COMMENTS if not coms: coms=self.env.DEFINE_COMMENTS={} coms[key]=comment or'' @conf def get_define_comment(self,key): coms=self.env.DEFINE_COMMENTS or{} return coms.get(key,'') @conf def define(self,key,val,quote=True,comment=''): assert isinstance(key,str) if not key: return if val is True: val=1 elif val in(False,None): val=0 if isinstance(val,int)or isinstance(val,float): s='%s=%s' else: s=quote and'%s="%s"'or'%s=%s' app=s%(key,str(val)) ban=key+'=' lst=self.env.DEFINES for x in lst: if x.startswith(ban): lst[lst.index(x)]=app break else: self.env.append_value('DEFINES',app) self.env.append_unique(DEFKEYS,key) self.set_define_comment(key,comment) @conf def undefine(self,key,comment=''): assert isinstance(key,str) if not key: return ban=key+'=' lst=[x for x in self.env.DEFINES if not x.startswith(ban)] self.env.DEFINES=lst self.env.append_unique(DEFKEYS,key) self.set_define_comment(key,comment) @conf def define_cond(self,key,val,comment=''): assert isinstance(key,str) if not key: return if val: self.define(key,1,comment=comment) else: self.undefine(key,comment=comment) @conf def is_defined(self,key): assert key and isinstance(key,str) ban=key+'=' for x in self.env.DEFINES: if x.startswith(ban): return True return False @conf def get_define(self,key): assert key and isinstance(key,str) ban=key+'=' for x in self.env.DEFINES: if x.startswith(ban): return x[len(ban):] return None @conf def have_define(self,key): return(self.env.HAVE_PAT or'HAVE_%s')%Utils.quote_define_name(key) @conf def write_config_header(self,configfile='',guard='',top=False,defines=True,headers=False,remove=True,define_prefix=''): if not configfile: configfile=WAF_CONFIG_H waf_guard=guard or'W_%s_WAF'%Utils.quote_define_name(configfile) node=top and self.bldnode or self.path.get_bld() node=node.make_node(configfile) node.parent.mkdir() lst=['/* WARNING! All changes made to this file will be lost! */\n'] lst.append('#ifndef %s\n#define %s\n'%(waf_guard,waf_guard)) lst.append(self.get_config_header(defines,headers,define_prefix=define_prefix)) lst.append('\n#endif /* %s */\n'%waf_guard) node.write('\n'.join(lst)) self.env.append_unique(Build.CFG_FILES,[node.abspath()]) if remove: for key in self.env[DEFKEYS]: self.undefine(key) self.env[DEFKEYS]=[] @conf def get_config_header(self,defines=True,headers=False,define_prefix=''): lst=[] if self.env.WAF_CONFIG_H_PRELUDE: lst.append(self.env.WAF_CONFIG_H_PRELUDE) if headers: for x in self.env[INCKEYS]: lst.append('#include <%s>'%x) if defines: tbl={} for k in self.env.DEFINES: a,_,b=k.partition('=') tbl[a]=b for k in self.env[DEFKEYS]: caption=self.get_define_comment(k) if caption: caption=' /* %s */'%caption try: txt='#define %s%s %s%s'%(define_prefix,k,tbl[k],caption) except KeyError: txt='/* #undef %s%s */%s'%(define_prefix,k,caption) lst.append(txt) return"\n".join(lst) @conf def cc_add_flags(conf): conf.add_os_flags('CPPFLAGS',dup=False) conf.add_os_flags('CFLAGS',dup=False) @conf def cxx_add_flags(conf): conf.add_os_flags('CPPFLAGS',dup=False) conf.add_os_flags('CXXFLAGS',dup=False) @conf def link_add_flags(conf): conf.add_os_flags('LINKFLAGS',dup=False) conf.add_os_flags('LDFLAGS',dup=False) @conf def cc_load_tools(conf): if not conf.env.DEST_OS: conf.env.DEST_OS=Utils.unversioned_sys_platform() conf.load('c') @conf def cxx_load_tools(conf): if not conf.env.DEST_OS: conf.env.DEST_OS=Utils.unversioned_sys_platform() conf.load('cxx') @conf def get_cc_version(conf,cc,gcc=False,icc=False,clang=False): cmd=cc+['-dM','-E','-'] env=conf.env.env or None try: out,err=conf.cmd_and_log(cmd,output=0,input='\n'.encode(),env=env) except Exception: conf.fatal('Could not determine the compiler version %r'%cmd) if gcc: if out.find('__INTEL_COMPILER')>=0: conf.fatal('The intel compiler pretends to be gcc') if out.find('__GNUC__')<0 and out.find('__clang__')<0: conf.fatal('Could not determine the compiler type') if icc and out.find('__INTEL_COMPILER')<0: conf.fatal('Not icc/icpc') if clang and out.find('__clang__')<0: conf.fatal('Not clang/clang++') if not clang and out.find('__clang__')>=0: conf.fatal('Could not find gcc/g++ (only Clang), if renamed try eg: CC=gcc48 CXX=g++48 waf configure') k={} if icc or gcc or clang: out=out.splitlines() for line in out: lst=shlex.split(line) if len(lst)>2: key=lst[1] val=lst[2] k[key]=val def isD(var): return var in k if not conf.env.DEST_OS: conf.env.DEST_OS='' for i in MACRO_TO_DESTOS: if isD(i): conf.env.DEST_OS=MACRO_TO_DESTOS[i] break else: if isD('__APPLE__')and isD('__MACH__'): conf.env.DEST_OS='darwin' elif isD('__unix__'): conf.env.DEST_OS='generic' if isD('__ELF__'): conf.env.DEST_BINFMT='elf' elif isD('__WINNT__')or isD('__CYGWIN__')or isD('_WIN32'): conf.env.DEST_BINFMT='pe' if not conf.env.IMPLIBDIR: conf.env.IMPLIBDIR=conf.env.LIBDIR conf.env.LIBDIR=conf.env.BINDIR elif isD('__APPLE__'): conf.env.DEST_BINFMT='mac-o' if not conf.env.DEST_BINFMT: conf.env.DEST_BINFMT=Utils.destos_to_binfmt(conf.env.DEST_OS) for i in MACRO_TO_DEST_CPU: if isD(i): conf.env.DEST_CPU=MACRO_TO_DEST_CPU[i] break Logs.debug('ccroot: dest platform: '+' '.join([conf.env[x]or'?'for x in('DEST_OS','DEST_BINFMT','DEST_CPU')])) if icc: ver=k['__INTEL_COMPILER'] conf.env.CC_VERSION=(ver[:-2],ver[-2],ver[-1]) else: if isD('__clang__')and isD('__clang_major__'): conf.env.CC_VERSION=(k['__clang_major__'],k['__clang_minor__'],k['__clang_patchlevel__']) else: conf.env.CC_VERSION=(k['__GNUC__'],k['__GNUC_MINOR__'],k.get('__GNUC_PATCHLEVEL__','0')) return k @conf def get_xlc_version(conf,cc): cmd=cc+['-qversion'] try: out,err=conf.cmd_and_log(cmd,output=0) except Errors.WafError: conf.fatal('Could not find xlc %r'%cmd) for v in(r"IBM XL C/C\+\+.* V(?P\d*)\.(?P\d*)",): version_re=re.compile(v,re.I).search match=version_re(out or err) if match: k=match.groupdict() conf.env.CC_VERSION=(k['major'],k['minor']) break else: conf.fatal('Could not determine the XLC version.') @conf def get_suncc_version(conf,cc): cmd=cc+['-V'] try: out,err=conf.cmd_and_log(cmd,output=0) except Errors.WafError as e: if not(hasattr(e,'returncode')and hasattr(e,'stdout')and hasattr(e,'stderr')): conf.fatal('Could not find suncc %r'%cmd) out=e.stdout err=e.stderr version=(out or err) version=version.splitlines()[0] version_re=re.compile(r'cc: (studio.*?|\s+)?(sun\s+(c\+\+|c)|(WorkShop\s+Compilers))?\s+(?P\d*)\.(?P\d*)',re.I).search match=version_re(version) if match: k=match.groupdict() conf.env.CC_VERSION=(k['major'],k['minor']) else: conf.fatal('Could not determine the suncc version.') @conf def add_as_needed(self): if self.env.DEST_BINFMT=='elf'and'gcc'in(self.env.CXX_NAME,self.env.CC_NAME): self.env.append_unique('LINKFLAGS','-Wl,--as-needed') class cfgtask(Task.TaskBase): def __init__(self,*k,**kw): Task.TaskBase.__init__(self,*k,**kw) self.run_after=set() def display(self): return'' def runnable_status(self): for x in self.run_after: if not x.hasrun: return Task.ASK_LATER return Task.RUN_ME def uid(self): return Utils.SIG_NIL def run(self): conf=self.conf bld=Build.BuildContext(top_dir=conf.srcnode.abspath(),out_dir=conf.bldnode.abspath()) bld.env=conf.env bld.init_dirs() bld.in_msg=1 bld.logger=self.logger bld.multicheck_task=self args=self.args try: if'func'in args: bld.test(build_fun=args['func'],msg=args.get('msg',''),okmsg=args.get('okmsg',''),errmsg=args.get('errmsg',''),) else: args['multicheck_mandatory']=args.get('mandatory',True) args['mandatory']=True try: bld.check(**args) finally: args['mandatory']=args['multicheck_mandatory'] except Exception: return 1 def process(self): Task.TaskBase.process(self) if'msg'in self.args: with self.generator.bld.multicheck_lock: self.conf.start_msg(self.args['msg']) if self.hasrun==Task.NOT_RUN: self.conf.end_msg('test cancelled','YELLOW') elif self.hasrun!=Task.SUCCESS: self.conf.end_msg(self.args.get('errmsg','no'),'YELLOW') else: self.conf.end_msg(self.args.get('okmsg','yes'),'GREEN') @conf def multicheck(self,*k,**kw): self.start_msg(kw.get('msg','Executing %d configuration tests'%len(k)),**kw) for var in('DEFINES',DEFKEYS): self.env.append_value(var,[]) self.env.DEFINE_COMMENTS=self.env.DEFINE_COMMENTS or{} class par(object): def __init__(self): self.keep=False self.task_sigs={} self.progress_bar=0 def total(self): return len(tasks) def to_log(self,*k,**kw): return bld=par() bld.keep=kw.get('run_all_tests',True) tasks=[] id_to_task={} for dct in k: x=Task.classes['cfgtask'](bld=bld) tasks.append(x) x.args=dct x.bld=bld x.conf=self x.args=dct x.logger=Logs.make_mem_logger(str(id(x)),self.logger) if'id'in dct: id_to_task[dct['id']]=x for x in tasks: for key in Utils.to_list(x.args.get('before_tests',[])): tsk=id_to_task[key] if not tsk: raise ValueError('No test named %r'%key) tsk.run_after.add(x) for key in Utils.to_list(x.args.get('after_tests',[])): tsk=id_to_task[key] if not tsk: raise ValueError('No test named %r'%key) x.run_after.add(tsk) def it(): yield tasks while 1: yield[] bld.producer=p=Runner.Parallel(bld,Options.options.jobs) bld.multicheck_lock=Utils.threading.Lock() p.biter=it() self.end_msg('started') p.start() for x in tasks: x.logger.memhandler.flush() self.start_msg('-> processing test results') if p.error: for x in p.error: if getattr(x,'err_msg',None): self.to_log(x.err_msg) self.end_msg('fail',color='RED') raise Errors.WafError('There is an error in the library, read config.log for more information') failure_count=0 for x in tasks: if x.hasrun not in(Task.SUCCESS,Task.NOT_RUN): failure_count+=1 if failure_count: self.end_msg(kw.get('errmsg','%s test failed'%failure_count),color='YELLOW',**kw) else: self.end_msg('all ok',**kw) for x in tasks: if x.hasrun!=Task.SUCCESS: if x.args.get('mandatory',True): self.fatal(kw.get('fatalmsg')or'One of the tests has failed, read config.log for more information') @conf def check_gcc_o_space(self,mode='c'): if int(self.env.CC_VERSION[0])>4: return self.env.stash() if mode=='c': self.env.CCLNK_TGT_F=['-o',''] elif mode=='cxx': self.env.CXXLNK_TGT_F=['-o',''] features='%s %sshlib'%(mode,mode) try: self.check(msg='Checking if the -o link must be split from arguments',fragment=SNIP_EMPTY_PROGRAM,features=features) except self.errors.ConfigurationError: self.env.revert() else: self.env.commit() ntpsec-1.1.0+dfsg1/waflib/Tools/nasm.py0000644000175000017500000000074513210576007017504 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os import waflib.Tools.asm from waflib.TaskGen import feature @feature('asm') def apply_nasm_vars(self): self.env.append_value('ASFLAGS',self.to_list(getattr(self,'nasm_flags',[]))) def configure(conf): conf.find_program(['nasm','yasm'],var='AS') conf.env.AS_TGT_F=['-o'] conf.env.ASLNK_TGT_F=['-o'] conf.load('asm') conf.env.ASMPATH_ST='-I%s'+os.sep ntpsec-1.1.0+dfsg1/waflib/Tools/javaw.py0000644000175000017500000002322013210576031017644 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,shutil from waflib import Task,Utils,Errors,Node from waflib.Configure import conf from waflib.TaskGen import feature,before_method,after_method from waflib.Tools import ccroot ccroot.USELIB_VARS['javac']=set(['CLASSPATH','JAVACFLAGS']) SOURCE_RE='**/*.java' JAR_RE='**/*' class_check_source=''' public class Test { public static void main(String[] argv) { Class lib; if (argv.length < 1) { System.err.println("Missing argument"); System.exit(77); } try { lib = Class.forName(argv[0]); } catch (ClassNotFoundException e) { System.err.println("ClassNotFoundException"); System.exit(1); } lib = null; System.exit(0); } } ''' @feature('javac') @before_method('process_source') def apply_java(self): Utils.def_attrs(self,jarname='',classpath='',sourcepath='.',srcdir='.',jar_mf_attributes={},jar_mf_classpath=[]) outdir=getattr(self,'outdir',None) if outdir: if not isinstance(outdir,Node.Node): outdir=self.path.get_bld().make_node(self.outdir) else: outdir=self.path.get_bld() outdir.mkdir() self.outdir=outdir self.env.OUTDIR=outdir.abspath() self.javac_task=tsk=self.create_task('javac') tmp=[] srcdir=getattr(self,'srcdir','') if isinstance(srcdir,Node.Node): srcdir=[srcdir] for x in Utils.to_list(srcdir): if isinstance(x,Node.Node): y=x else: y=self.path.find_dir(x) if not y: self.bld.fatal('Could not find the folder %s from %s'%(x,self.path)) tmp.append(y) tsk.srcdir=tmp if getattr(self,'compat',None): tsk.env.append_value('JAVACFLAGS',['-source',str(self.compat)]) if hasattr(self,'sourcepath'): fold=[isinstance(x,Node.Node)and x or self.path.find_dir(x)for x in self.to_list(self.sourcepath)] names=os.pathsep.join([x.srcpath()for x in fold]) else: names=[x.srcpath()for x in tsk.srcdir] if names: tsk.env.append_value('JAVACFLAGS',['-sourcepath',names]) @feature('javac') @before_method('propagate_uselib_vars') @after_method('apply_java') def use_javac_files(self): lst=[] self.uselib=self.to_list(getattr(self,'uselib',[])) names=self.to_list(getattr(self,'use',[])) get=self.bld.get_tgen_by_name for x in names: try: y=get(x) except Errors.WafError: self.uselib.append(x) else: y.post() if hasattr(y,'jar_task'): lst.append(y.jar_task.outputs[0].abspath()) self.javac_task.set_run_after(y.jar_task) else: for tsk in y.tasks: self.javac_task.set_run_after(tsk) self.env.append_value('CLASSPATH',lst) @feature('javac') @after_method('apply_java','propagate_uselib_vars','use_javac_files') def set_classpath(self): self.env.append_value('CLASSPATH',getattr(self,'classpath',[])) for x in self.tasks: x.env.CLASSPATH=os.pathsep.join(self.env.CLASSPATH)+os.pathsep @feature('jar') @after_method('apply_java','use_javac_files') @before_method('process_source') def jar_files(self): destfile=getattr(self,'destfile','test.jar') jaropts=getattr(self,'jaropts',[]) manifest=getattr(self,'manifest',None) basedir=getattr(self,'basedir',None) if basedir: if not isinstance(self.basedir,Node.Node): basedir=self.path.get_bld().make_node(basedir) else: basedir=self.path.get_bld() if not basedir: self.bld.fatal('Could not find the basedir %r for %r'%(self.basedir,self)) self.jar_task=tsk=self.create_task('jar_create') if manifest: jarcreate=getattr(self,'jarcreate','cfm') if not isinstance(manifest,Node.Node): node=self.path.find_or_declare(manifest) else: node=manifest tsk.dep_nodes.append(node) jaropts.insert(0,node.abspath()) else: jarcreate=getattr(self,'jarcreate','cf') if not isinstance(destfile,Node.Node): destfile=self.path.find_or_declare(destfile) if not destfile: self.bld.fatal('invalid destfile %r for %r'%(destfile,self)) tsk.set_outputs(destfile) tsk.basedir=basedir jaropts.append('-C') jaropts.append(basedir.bldpath()) jaropts.append('.') tsk.env.JAROPTS=jaropts tsk.env.JARCREATE=jarcreate if getattr(self,'javac_task',None): tsk.set_run_after(self.javac_task) @feature('jar') @after_method('jar_files') def use_jar_files(self): self.uselib=self.to_list(getattr(self,'uselib',[])) names=self.to_list(getattr(self,'use',[])) get=self.bld.get_tgen_by_name for x in names: try: y=get(x) except Errors.WafError: self.uselib.append(x) else: y.post() self.jar_task.run_after.update(y.tasks) class JTask(Task.Task): def split_argfile(self,cmd): inline=[cmd[0]] infile=[] for x in cmd[1:]: if x.startswith('-J'): inline.append(x) else: infile.append(self.quote_flag(x)) return(inline,infile) class jar_create(JTask): color='GREEN' run_str='${JAR} ${JARCREATE} ${TGT} ${JAROPTS}' def runnable_status(self): for t in self.run_after: if not t.hasrun: return Task.ASK_LATER if not self.inputs: global JAR_RE try: self.inputs=[x for x in self.basedir.ant_glob(JAR_RE,remove=False)if id(x)!=id(self.outputs[0])] except Exception: raise Errors.WafError('Could not find the basedir %r for %r'%(self.basedir,self)) return super(jar_create,self).runnable_status() class javac(JTask): color='BLUE' run_str='${JAVAC} -classpath ${CLASSPATH} -d ${OUTDIR} ${JAVACFLAGS} ${SRC}' vars=['CLASSPATH','JAVACFLAGS','JAVAC','OUTDIR'] def uid(self): lst=[self.__class__.__name__,self.generator.outdir.abspath()] for x in self.srcdir: lst.append(x.abspath()) return Utils.h_list(lst) def runnable_status(self): for t in self.run_after: if not t.hasrun: return Task.ASK_LATER if not self.inputs: global SOURCE_RE self.inputs=[] for x in self.srcdir: self.inputs.extend(x.ant_glob(SOURCE_RE,remove=False)) return super(javac,self).runnable_status() def post_run(self): for node in self.generator.outdir.ant_glob('**/*.class'): self.generator.bld.node_sigs[node]=self.uid() self.generator.bld.task_sigs[self.uid()]=self.cache_sig @feature('javadoc') @after_method('process_rule') def create_javadoc(self): tsk=self.create_task('javadoc') tsk.classpath=getattr(self,'classpath',[]) self.javadoc_package=Utils.to_list(self.javadoc_package) if not isinstance(self.javadoc_output,Node.Node): self.javadoc_output=self.bld.path.find_or_declare(self.javadoc_output) class javadoc(Task.Task): color='BLUE' def __str__(self): return'%s: %s -> %s\n'%(self.__class__.__name__,self.generator.srcdir,self.generator.javadoc_output) def run(self): env=self.env bld=self.generator.bld wd=bld.bldnode srcpath=self.generator.path.abspath()+os.sep+self.generator.srcdir srcpath+=os.pathsep srcpath+=self.generator.path.get_bld().abspath()+os.sep+self.generator.srcdir classpath=env.CLASSPATH classpath+=os.pathsep classpath+=os.pathsep.join(self.classpath) classpath="".join(classpath) self.last_cmd=lst=[] lst.extend(Utils.to_list(env.JAVADOC)) lst.extend(['-d',self.generator.javadoc_output.abspath()]) lst.extend(['-sourcepath',srcpath]) lst.extend(['-classpath',classpath]) lst.extend(['-subpackages']) lst.extend(self.generator.javadoc_package) lst=[x for x in lst if x] self.generator.bld.cmd_and_log(lst,cwd=wd,env=env.env or None,quiet=0) def post_run(self): nodes=self.generator.javadoc_output.ant_glob('**') for node in nodes: self.generator.bld.node_sigs[node]=self.uid() self.generator.bld.task_sigs[self.uid()]=self.cache_sig def configure(self): java_path=self.environ['PATH'].split(os.pathsep) v=self.env if'JAVA_HOME'in self.environ: java_path=[os.path.join(self.environ['JAVA_HOME'],'bin')]+java_path self.env.JAVA_HOME=[self.environ['JAVA_HOME']] for x in'javac java jar javadoc'.split(): self.find_program(x,var=x.upper(),path_list=java_path) if'CLASSPATH'in self.environ: v.CLASSPATH=self.environ['CLASSPATH'] if not v.JAR: self.fatal('jar is required for making java packages') if not v.JAVAC: self.fatal('javac is required for compiling java classes') v.JARCREATE='cf' v.JAVACFLAGS=[] @conf def check_java_class(self,classname,with_classpath=None): javatestdir='.waf-javatest' classpath=javatestdir if self.env.CLASSPATH: classpath+=os.pathsep+self.env.CLASSPATH if isinstance(with_classpath,str): classpath+=os.pathsep+with_classpath shutil.rmtree(javatestdir,True) os.mkdir(javatestdir) Utils.writef(os.path.join(javatestdir,'Test.java'),class_check_source) self.exec_command(self.env.JAVAC+[os.path.join(javatestdir,'Test.java')],shell=False) cmd=self.env.JAVA+['-cp',classpath,'Test',classname] self.to_log("%s\n"%str(cmd)) found=self.exec_command(cmd,shell=False) self.msg('Checking for java class %s'%classname,not found) shutil.rmtree(javatestdir,True) return found @conf def check_jni_headers(conf): if not conf.env.CC_NAME and not conf.env.CXX_NAME: conf.fatal('load a compiler first (gcc, g++, ..)') if not conf.env.JAVA_HOME: conf.fatal('set JAVA_HOME in the system environment') javaHome=conf.env.JAVA_HOME[0] dir=conf.root.find_dir(conf.env.JAVA_HOME[0]+'/include') if dir is None: dir=conf.root.find_dir(conf.env.JAVA_HOME[0]+'/../Headers') if dir is None: conf.fatal('JAVA_HOME does not seem to be set properly') f=dir.ant_glob('**/(jni|jni_md).h') incDirs=[x.parent.abspath()for x in f] dir=conf.root.find_dir(conf.env.JAVA_HOME[0]) f=dir.ant_glob('**/*jvm.(so|dll|dylib)') libDirs=[x.parent.abspath()for x in f]or[javaHome] f=dir.ant_glob('**/*jvm.(lib)') if f: libDirs=[[x,y.parent.abspath()]for x in libDirs for y in f] if conf.env.DEST_OS=='freebsd': conf.env.append_unique('LINKFLAGS_JAVA','-pthread') for d in libDirs: try: conf.check(header_name='jni.h',define_name='HAVE_JNI_H',lib='jvm',libpath=d,includes=incDirs,uselib_store='JAVA',uselib='JAVA') except Exception: pass else: break else: conf.fatal('could not find lib jvm in %r (see config.log)'%libDirs) ntpsec-1.1.0+dfsg1/waflib/Tools/suncc.py0000644000175000017500000000212713210576007017655 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib.Tools import ccroot,ar from waflib.Configure import conf @conf def find_scc(conf): v=conf.env cc=conf.find_program('cc',var='CC') try: conf.cmd_and_log(cc+['-flags']) except Exception: conf.fatal('%r is not a Sun compiler'%cc) v.CC_NAME='sun' conf.get_suncc_version(cc) @conf def scc_common_flags(conf): v=conf.env v.CC_SRC_F=[] v.CC_TGT_F=['-c','-o',''] if not v.LINK_CC: v.LINK_CC=v.CC v.CCLNK_SRC_F='' v.CCLNK_TGT_F=['-o',''] v.CPPPATH_ST='-I%s' v.DEFINES_ST='-D%s' v.LIB_ST='-l%s' v.LIBPATH_ST='-L%s' v.STLIB_ST='-l%s' v.STLIBPATH_ST='-L%s' v.SONAME_ST='-Wl,-h,%s' v.SHLIB_MARKER='-Bdynamic' v.STLIB_MARKER='-Bstatic' v.cprogram_PATTERN='%s' v.CFLAGS_cshlib=['-xcode=pic32','-DPIC'] v.LINKFLAGS_cshlib=['-G'] v.cshlib_PATTERN='lib%s.so' v.LINKFLAGS_cstlib=['-Bstatic'] v.cstlib_PATTERN='lib%s.a' def configure(conf): conf.find_scc() conf.find_ar() conf.scc_common_flags() conf.cc_load_tools() conf.cc_add_flags() conf.link_add_flags() ntpsec-1.1.0+dfsg1/waflib/Tools/d_config.py0000644000175000017500000000220513210576007020307 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Utils from waflib.Configure import conf @conf def d_platform_flags(self): v=self.env if not v.DEST_OS: v.DEST_OS=Utils.unversioned_sys_platform() binfmt=Utils.destos_to_binfmt(self.env.DEST_OS) if binfmt=='pe': v.dprogram_PATTERN='%s.exe' v.dshlib_PATTERN='lib%s.dll' v.dstlib_PATTERN='lib%s.a' elif binfmt=='mac-o': v.dprogram_PATTERN='%s' v.dshlib_PATTERN='lib%s.dylib' v.dstlib_PATTERN='lib%s.a' else: v.dprogram_PATTERN='%s' v.dshlib_PATTERN='lib%s.so' v.dstlib_PATTERN='lib%s.a' DLIB=''' version(D_Version2) { import std.stdio; int main() { writefln("phobos2"); return 0; } } else { version(Tango) { import tango.stdc.stdio; int main() { printf("tango"); return 0; } } else { import std.stdio; int main() { writefln("phobos1"); return 0; } } } ''' @conf def check_dlibrary(self,execute=True): ret=self.check_cc(features='d dprogram',fragment=DLIB,compile_filename='test.d',execute=execute,define_ret=True) if execute: self.env.DLIBRARY=ret.strip() ntpsec-1.1.0+dfsg1/waflib/Tools/__init__.py0000644000175000017500000000017113210576007020276 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file ntpsec-1.1.0+dfsg1/waflib/Tools/c_preproc.py0000644000175000017500000004156213210576031020521 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re,string,traceback from waflib import Logs,Utils,Errors class PreprocError(Errors.WafError): pass FILE_CACHE_SIZE=100000 LINE_CACHE_SIZE=100000 POPFILE='-' recursion_limit=150 go_absolute=False standard_includes=['/usr/local/include','/usr/include'] if Utils.is_win32: standard_includes=[] use_trigraphs=0 strict_quotes=0 g_optrans={'not':'!','not_eq':'!','and':'&&','and_eq':'&=','or':'||','or_eq':'|=','xor':'^','xor_eq':'^=','bitand':'&','bitor':'|','compl':'~',} re_lines=re.compile('^[ \t]*(?:#|%:)[ \t]*(ifdef|ifndef|if|else|elif|endif|include|import|define|undef|pragma)[ \t]*(.*)\r*$',re.IGNORECASE|re.MULTILINE) re_mac=re.compile("^[a-zA-Z_]\w*") re_fun=re.compile('^[a-zA-Z_][a-zA-Z0-9_]*[(]') re_pragma_once=re.compile('^\s*once\s*',re.IGNORECASE) re_nl=re.compile('\\\\\r*\n',re.MULTILINE) re_cpp=re.compile(r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',re.DOTALL|re.MULTILINE) trig_def=[('??'+a,b)for a,b in zip("=-/!'()<>",r'#~\|^[]{}')] chr_esc={'0':0,'a':7,'b':8,'t':9,'n':10,'f':11,'v':12,'r':13,'\\':92,"'":39} NUM='i' OP='O' IDENT='T' STR='s' CHAR='c' tok_types=[NUM,STR,IDENT,OP] exp_types=[r"""0[xX](?P[a-fA-F0-9]+)(?P[uUlL]*)|L*?'(?P(\\.|[^\\'])+)'|(?P\d+)[Ee](?P[+-]*?\d+)(?P[fFlL]*)|(?P\d*\.\d+)([Ee](?P[+-]*?\d+))?(?P[fFlL]*)|(?P\d+\.\d*)([Ee](?P[+-]*?\d+))?(?P[fFlL]*)|(?P0*)(?P\d+)(?P[uUlL]*)""",r'L?"([^"\\]|\\.)*"',r'[a-zA-Z_]\w*',r'%:%:|<<=|>>=|\.\.\.|<<|<%|<:|<=|>>|>=|\+\+|\+=|--|->|-=|\*=|/=|%:|%=|%>|==|&&|&=|\|\||\|=|\^=|:>|!=|##|[\(\)\{\}\[\]<>\?\|\^\*\+&=:!#;,%/\-\?\~\.]',] re_clexer=re.compile('|'.join(["(?P<%s>%s)"%(name,part)for name,part in zip(tok_types,exp_types)]),re.M) accepted='a' ignored='i' undefined='u' skipped='s' def repl(m): s=m.group() if s[0]=='/': return' ' return s prec={} ops=['* / %','+ -','<< >>','< <= >= >','== !=','& | ^','&& ||',','] for x,syms in enumerate(ops): for u in syms.split(): prec[u]=x def trimquotes(s): if not s:return'' s=s.rstrip() if s[0]=="'"and s[-1]=="'":return s[1:-1] return s def reduce_nums(val_1,val_2,val_op): try: a=0+val_1 except TypeError: a=int(val_1) try: b=0+val_2 except TypeError: b=int(val_2) d=val_op if d=='%': c=a%b elif d=='+': c=a+b elif d=='-': c=a-b elif d=='*': c=a*b elif d=='/': c=a/b elif d=='^': c=a^b elif d=='==': c=int(a==b) elif d=='|'or d=='bitor': c=a|b elif d=='||'or d=='or': c=int(a or b) elif d=='&'or d=='bitand': c=a&b elif d=='&&'or d=='and': c=int(a and b) elif d=='!='or d=='not_eq': c=int(a!=b) elif d=='^'or d=='xor': c=int(a^b) elif d=='<=': c=int(a<=b) elif d=='<': c=int(a': c=int(a>b) elif d=='>=': c=int(a>=b) elif d=='<<': c=a<>': c=a>>b else: c=0 return c def get_num(lst): if not lst: raise PreprocError('empty list for get_num') (p,v)=lst[0] if p==OP: if v=='(': count_par=1 i=1 while i=prec[v]: num2=reduce_nums(num,num2,v) return get_term([(NUM,num2)]+lst) else: num3,lst=get_num(lst[1:]) num3=reduce_nums(num2,num3,v2) return get_term([(NUM,num),(p,v),(NUM,num3)]+lst) raise PreprocError('cannot reduce %r'%lst) def reduce_eval(lst): num,lst=get_term(lst) return(NUM,num) def stringize(lst): lst=[str(v2)for(p2,v2)in lst] return"".join(lst) def paste_tokens(t1,t2): p1=None if t1[0]==OP and t2[0]==OP: p1=OP elif t1[0]==IDENT and(t2[0]==IDENT or t2[0]==NUM): p1=IDENT elif t1[0]==NUM and t2[0]==NUM: p1=NUM if not p1: raise PreprocError('tokens do not make a valid paste %r and %r'%(t1,t2)) return(p1,t1[1]+t2[1]) def reduce_tokens(lst,defs,ban=[]): i=0 while i=len(lst): raise PreprocError('expected ( after %r (got nothing)'%v) (p2,v2)=lst[i] if p2!=OP or v2!='(': raise PreprocError('expected ( after %r'%v) del lst[i] one_param=[] count_paren=0 while i1: (p3,v3)=accu[-1] (p4,v4)=accu[-2] if v3=='##': accu.pop() if v4==','and pt1: return(v,[[],t[1:]]) else: return(v,[[],[('T','')]]) re_include=re.compile('^\s*(<(?:.*)>|"(?:.*)")') def extract_include(txt,defs): m=re_include.search(txt) if m: txt=m.group(1) return txt[0],txt[1:-1] toks=tokenize(txt) reduce_tokens(toks,defs,['waf_include']) if not toks: raise PreprocError('could not parse include %r'%txt) if len(toks)==1: if toks[0][0]==STR: return'"',toks[0][1] else: if toks[0][1]=='<'and toks[-1][1]=='>': ret='<',stringize(toks).lstrip('<').rstrip('>') return ret raise PreprocError('could not parse include %r'%txt) def parse_char(txt): if not txt: raise PreprocError('attempted to parse a null char') if txt[0]!='\\': return ord(txt) c=txt[1] if c=='x': if len(txt)==4 and txt[3]in string.hexdigits: return int(txt[2:],16) return int(txt[2:],16) elif c.isdigit(): if c=='0'and len(txt)==2: return 0 for i in 3,2,1: if len(txt)>i and txt[1:1+i].isdigit(): return(1+i,int(txt[1:1+i],8)) else: try: return chr_esc[c] except KeyError: raise PreprocError('could not parse char literal %r'%txt) def tokenize(s): return tokenize_private(s)[:] def tokenize_private(s): ret=[] for match in re_clexer.finditer(s): m=match.group for name in tok_types: v=m(name) if v: if name==IDENT: try: g_optrans[v] name=OP except KeyError: if v.lower()=="true": v=1 name=NUM elif v.lower()=="false": v=0 name=NUM elif name==NUM: if m('oct'): v=int(v,8) elif m('hex'): v=int(m('hex'),16) elif m('n0'): v=m('n0') else: v=m('char') if v: v=parse_char(v) else: v=m('n2')or m('n4') elif name==OP: if v=='%:': v='#' elif v=='%:%:': v='##' elif name==STR: v=v[1:-1] ret.append((name,v)) break return ret def format_defines(lst): ret=[] for y in lst: if y: pos=y.find('=') if pos==-1: ret.append(y) elif pos>0: ret.append('%s %s'%(y[:pos],y[pos+1:])) else: raise ValueError('Invalid define expression %r'%y) return ret class c_parser(object): def __init__(self,nodepaths=None,defines=None): self.lines=[] if defines is None: self.defs={} else: self.defs=dict(defines) self.state=[] self.count_files=0 self.currentnode_stack=[] self.nodepaths=nodepaths or[] self.nodes=[] self.names=[] self.curfile='' self.ban_includes=set() def cached_find_resource(self,node,filename): try: cache=node.ctx.preproc_cache_node except AttributeError: global FILE_CACHE_SIZE cache=node.ctx.preproc_cache_node=Utils.lru_cache(FILE_CACHE_SIZE) key=(node,filename) try: return cache[key] except KeyError: ret=node.find_resource(filename) if ret: if getattr(ret,'children',None): ret=None elif ret.is_child_of(node.ctx.bldnode): tmp=node.ctx.srcnode.search_node(ret.path_from(node.ctx.bldnode)) if tmp and getattr(tmp,'children',None): ret=None cache[key]=ret return ret def tryfind(self,filename,kind='"',env=None): if filename.endswith('.moc'): self.names.append(filename) return None self.curfile=filename found=None if kind=='"': if env.MSVC_VERSION: for n in reversed(self.currentnode_stack): found=self.cached_find_resource(n,filename) if found: break else: found=self.cached_find_resource(self.currentnode_stack[-1],filename) if not found: for n in self.nodepaths: found=self.cached_find_resource(n,filename) if found: break if found and not found in self.ban_includes: self.nodes.append(found) self.addlines(found) else: if not filename in self.names: self.names.append(filename) return found def filter_comments(self,node): code=node.read() if use_trigraphs: for(a,b)in trig_def: code=code.split(a).join(b) code=re_nl.sub('',code) code=re_cpp.sub(repl,code) return re_lines.findall(code) def parse_lines(self,node): try: cache=node.ctx.preproc_cache_lines except AttributeError: global LINE_CACHE_SIZE cache=node.ctx.preproc_cache_lines=Utils.lru_cache(LINE_CACHE_SIZE) try: return cache[node] except KeyError: cache[node]=lines=self.filter_comments(node) lines.append((POPFILE,'')) lines.reverse() return lines def addlines(self,node): self.currentnode_stack.append(node.parent) self.count_files+=1 if self.count_files>recursion_limit: raise PreprocError('recursion limit exceeded') if Logs.verbose: Logs.debug('preproc: reading file %r',node) try: lines=self.parse_lines(node) except EnvironmentError: raise PreprocError('could not read the file %r'%node) except Exception: if Logs.verbose>0: Logs.error('parsing %r failed',node) traceback.print_exc() else: self.lines.extend(lines) def start(self,node,env): Logs.debug('preproc: scanning %s (in %s)',node.name,node.parent.name) self.current_file=node self.addlines(node) if env.DEFINES: lst=format_defines(env.DEFINES) lst.reverse() self.lines.extend([('define',x)for x in lst]) while self.lines: (token,line)=self.lines.pop() if token==POPFILE: self.count_files-=1 self.currentnode_stack.pop() continue try: state=self.state if token[:2]=='if': state.append(undefined) elif token=='endif': state.pop() if token[0]!='e': if skipped in self.state or ignored in self.state: continue if token=='if': ret=eval_macro(tokenize(line),self.defs) if ret: state[-1]=accepted else: state[-1]=ignored elif token=='ifdef': m=re_mac.match(line) if m and m.group()in self.defs: state[-1]=accepted else: state[-1]=ignored elif token=='ifndef': m=re_mac.match(line) if m and m.group()in self.defs: state[-1]=ignored else: state[-1]=accepted elif token=='include'or token=='import': (kind,inc)=extract_include(line,self.defs) self.current_file=self.tryfind(inc,kind,env) if token=='import': self.ban_includes.add(self.current_file) elif token=='elif': if state[-1]==accepted: state[-1]=skipped elif state[-1]==ignored: if eval_macro(tokenize(line),self.defs): state[-1]=accepted elif token=='else': if state[-1]==accepted: state[-1]=skipped elif state[-1]==ignored: state[-1]=accepted elif token=='define': try: self.defs[self.define_name(line)]=line except AttributeError: raise PreprocError('Invalid define line %r'%line) elif token=='undef': m=re_mac.match(line) if m and m.group()in self.defs: self.defs.__delitem__(m.group()) elif token=='pragma': if re_pragma_once.match(line.lower()): self.ban_includes.add(self.current_file) except Exception as e: if Logs.verbose: Logs.debug('preproc: line parsing failed (%s): %s %s',e,line,Utils.ex_stack()) def define_name(self,line): return re_mac.match(line).group() def scan(task): global go_absolute try: incn=task.generator.includes_nodes except AttributeError: raise Errors.WafError('%r is missing a feature such as "c", "cxx" or "includes": '%task.generator) if go_absolute: nodepaths=incn+[task.generator.bld.root.find_dir(x)for x in standard_includes] else: nodepaths=[x for x in incn if x.is_child_of(x.ctx.srcnode)or x.is_child_of(x.ctx.bldnode)] tmp=c_parser(nodepaths) tmp.start(task.inputs[0],task.env) return(tmp.nodes,tmp.names) ntpsec-1.1.0+dfsg1/waflib/Tools/intltool.py0000644000175000017500000000713113210576031020403 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re from waflib import Context,Task,Utils,Logs import waflib.Tools.ccroot from waflib.TaskGen import feature,before_method,taskgen_method from waflib.Logs import error from waflib.Configure import conf _style_flags={'ba':'-b','desktop':'-d','keys':'-k','quoted':'--quoted-style','quotedxml':'--quotedxml-style','rfc822deb':'-r','schemas':'-s','xml':'-x',} @taskgen_method def ensure_localedir(self): if not self.env.LOCALEDIR: if self.env.DATAROOTDIR: self.env.LOCALEDIR=os.path.join(self.env.DATAROOTDIR,'locale') else: self.env.LOCALEDIR=os.path.join(self.env.PREFIX,'share','locale') @before_method('process_source') @feature('intltool_in') def apply_intltool_in_f(self): try: self.meths.remove('process_source') except ValueError: pass self.ensure_localedir() podir=getattr(self,'podir','.') podirnode=self.path.find_dir(podir) if not podirnode: error("could not find the podir %r"%podir) return cache=getattr(self,'intlcache','.intlcache') self.env.INTLCACHE=[os.path.join(str(self.path.get_bld()),podir,cache)] self.env.INTLPODIR=podirnode.bldpath() self.env.append_value('INTLFLAGS',getattr(self,'flags',self.env.INTLFLAGS_DEFAULT)) if'-c'in self.env.INTLFLAGS: self.bld.fatal('Redundant -c flag in intltool task %r'%self) style=getattr(self,'style',None) if style: try: style_flag=_style_flags[style] except KeyError: self.bld.fatal('intltool_in style "%s" is not valid'%style) self.env.append_unique('INTLFLAGS',[style_flag]) for i in self.to_list(self.source): node=self.path.find_resource(i) task=self.create_task('intltool',node,node.change_ext('')) inst=getattr(self,'install_path',None) if inst: self.add_install_files(install_to=inst,install_from=task.outputs) @feature('intltool_po') def apply_intltool_po(self): try: self.meths.remove('process_source') except ValueError: pass self.ensure_localedir() appname=getattr(self,'appname',getattr(Context.g_module,Context.APPNAME,'set_your_app_name')) podir=getattr(self,'podir','.') inst=getattr(self,'install_path','${LOCALEDIR}') linguas=self.path.find_node(os.path.join(podir,'LINGUAS')) if linguas: file=open(linguas.abspath()) langs=[] for line in file.readlines(): if not line.startswith('#'): langs+=line.split() file.close() re_linguas=re.compile('[-a-zA-Z_@.]+') for lang in langs: if re_linguas.match(lang): node=self.path.find_resource(os.path.join(podir,re_linguas.match(lang).group()+'.po')) task=self.create_task('po',node,node.change_ext('.mo')) if inst: filename=task.outputs[0].name (langname,ext)=os.path.splitext(filename) inst_file=inst+os.sep+langname+os.sep+'LC_MESSAGES'+os.sep+appname+'.mo' self.add_install_as(install_to=inst_file,install_from=task.outputs[0],chmod=getattr(self,'chmod',Utils.O644)) else: Logs.pprint('RED',"Error no LINGUAS file found in po directory") class po(Task.Task): run_str='${MSGFMT} -o ${TGT} ${SRC}' color='BLUE' class intltool(Task.Task): run_str='${INTLTOOL} ${INTLFLAGS} ${INTLCACHE_ST:INTLCACHE} ${INTLPODIR} ${SRC} ${TGT}' color='BLUE' @conf def find_msgfmt(conf): conf.find_program('msgfmt',var='MSGFMT') @conf def find_intltool_merge(conf): if not conf.env.PERL: conf.find_program('perl',var='PERL') conf.env.INTLCACHE_ST='--cache=%s' conf.env.INTLFLAGS_DEFAULT=['-q','-u'] conf.find_program('intltool-merge',interpreter='PERL',var='INTLTOOL') def configure(conf): conf.find_msgfmt() conf.find_intltool_merge() if conf.env.CC or conf.env.CXX: conf.check(header_name='locale.h') ntpsec-1.1.0+dfsg1/waflib/Tools/c_tests.py0000644000175000017500000001022413210576007020203 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file from waflib import Task from waflib.Configure import conf from waflib.TaskGen import feature,before_method,after_method LIB_CODE=''' #ifdef _MSC_VER #define testEXPORT __declspec(dllexport) #else #define testEXPORT #endif testEXPORT int lib_func(void) { return 9; } ''' MAIN_CODE=''' #ifdef _MSC_VER #define testEXPORT __declspec(dllimport) #else #define testEXPORT #endif testEXPORT int lib_func(void); int main(int argc, char **argv) { (void)argc; (void)argv; return !(lib_func() == 9); } ''' @feature('link_lib_test') @before_method('process_source') def link_lib_test_fun(self): def write_test_file(task): task.outputs[0].write(task.generator.code) rpath=[] if getattr(self,'add_rpath',False): rpath=[self.bld.path.get_bld().abspath()] mode=self.mode m='%s %s'%(mode,mode) ex=self.test_exec and'test_exec'or'' bld=self.bld bld(rule=write_test_file,target='test.'+mode,code=LIB_CODE) bld(rule=write_test_file,target='main.'+mode,code=MAIN_CODE) bld(features='%sshlib'%m,source='test.'+mode,target='test') bld(features='%sprogram %s'%(m,ex),source='main.'+mode,target='app',use='test',rpath=rpath) @conf def check_library(self,mode=None,test_exec=True): if not mode: mode='c' if self.env.CXX: mode='cxx' self.check(compile_filename=[],features='link_lib_test',msg='Checking for libraries',mode=mode,test_exec=test_exec) INLINE_CODE=''' typedef int foo_t; static %s foo_t static_foo () {return 0; } %s foo_t foo () { return 0; } ''' INLINE_VALUES=['inline','__inline__','__inline'] @conf def check_inline(self,**kw): self.start_msg('Checking for inline') if not'define_name'in kw: kw['define_name']='INLINE_MACRO' if not'features'in kw: if self.env.CXX: kw['features']=['cxx'] else: kw['features']=['c'] for x in INLINE_VALUES: kw['fragment']=INLINE_CODE%(x,x) try: self.check(**kw) except self.errors.ConfigurationError: continue else: self.end_msg(x) if x!='inline': self.define('inline',x,quote=False) return x self.fatal('could not use inline functions') LARGE_FRAGMENT='''#include int main(int argc, char **argv) { (void)argc; (void)argv; return !(sizeof(off_t) >= 8); } ''' @conf def check_large_file(self,**kw): if not'define_name'in kw: kw['define_name']='HAVE_LARGEFILE' if not'execute'in kw: kw['execute']=True if not'features'in kw: if self.env.CXX: kw['features']=['cxx','cxxprogram'] else: kw['features']=['c','cprogram'] kw['fragment']=LARGE_FRAGMENT kw['msg']='Checking for large file support' ret=True try: if self.env.DEST_BINFMT!='pe': ret=self.check(**kw) except self.errors.ConfigurationError: pass else: if ret: return True kw['msg']='Checking for -D_FILE_OFFSET_BITS=64' kw['defines']=['_FILE_OFFSET_BITS=64'] try: ret=self.check(**kw) except self.errors.ConfigurationError: pass else: self.define('_FILE_OFFSET_BITS',64) return ret self.fatal('There is no support for large files') ENDIAN_FRAGMENT=''' short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; ''' class grep_for_endianness(Task.Task): color='PINK' def run(self): txt=self.inputs[0].read(flags='rb').decode('iso8859-1') if txt.find('LiTTleEnDian')>-1: self.generator.tmp.append('little') elif txt.find('BIGenDianSyS')>-1: self.generator.tmp.append('big') else: return-1 @feature('grep_for_endianness') @after_method('process_source') def grep_for_endianness_fun(self): self.create_task('grep_for_endianness',self.compiled_tasks[0].outputs[0]) @conf def check_endianness(self): tmp=[] def check_msg(self): return tmp[0] self.check(fragment=ENDIAN_FRAGMENT,features='c grep_for_endianness',msg='Checking for endianness',define='ENDIANNESS',tmp=tmp,okmsg=check_msg) return tmp[0] ntpsec-1.1.0+dfsg1/waflib/Tools/compiler_d.py0000644000175000017500000000260713210576007020662 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import re from waflib import Utils,Logs d_compiler={'default':['gdc','dmd','ldc2']} def default_compilers(): build_platform=Utils.unversioned_sys_platform() possible_compiler_list=d_compiler.get(build_platform,d_compiler['default']) return' '.join(possible_compiler_list) def configure(conf): try: test_for_compiler=conf.options.check_d_compiler or default_compilers() except AttributeError: conf.fatal("Add options(opt): opt.load('compiler_d')") for compiler in re.split('[ ,]+',test_for_compiler): conf.env.stash() conf.start_msg('Checking for %r (D compiler)'%compiler) try: conf.load(compiler) except conf.errors.ConfigurationError as e: conf.env.revert() conf.end_msg(False) Logs.debug('compiler_d: %r',e) else: if conf.env.D: conf.end_msg(conf.env.get_flat('D')) conf.env.COMPILER_D=compiler conf.env.commit() break conf.env.revert() conf.end_msg(False) else: conf.fatal('could not configure a D compiler!') def options(opt): test_for_compiler=default_compilers() d_compiler_opts=opt.add_option_group('Configuration options') d_compiler_opts.add_option('--check-d-compiler',default=None,help='list of D compilers to try [%s]'%test_for_compiler,dest='check_d_compiler') for x in test_for_compiler.split(): opt.load('%s'%x) ntpsec-1.1.0+dfsg1/waflib/Utils.py0000644000175000017500000003366113210576031016546 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import atexit,os,sys,errno,traceback,inspect,re,datetime,platform,base64,signal,functools try: import cPickle except ImportError: import pickle as cPickle if os.name=='posix'and sys.version_info[0]<3: try: import subprocess32 as subprocess except ImportError: import subprocess else: import subprocess try: TimeoutExpired=subprocess.TimeoutExpired except AttributeError: class TimeoutExpired(Exception): pass from collections import deque,defaultdict try: import _winreg as winreg except ImportError: try: import winreg except ImportError: winreg=None from waflib import Errors try: from hashlib import md5 except ImportError: try: from md5 import md5 except ImportError: pass try: import threading except ImportError: if not'JOBS'in os.environ: os.environ['JOBS']='1' class threading(object): pass class Lock(object): def acquire(self): pass def release(self): pass threading.Lock=threading.Thread=Lock SIG_NIL='SIG_NIL_SIG_NIL_'.encode() O644=420 O755=493 rot_chr=['\\','|','/','-'] rot_idx=0 class ordered_iter_dict(dict): def __init__(self,*k,**kw): self.lst=deque() dict.__init__(self,*k,**kw) def clear(self): dict.clear(self) self.lst=deque() def __setitem__(self,key,value): if key in dict.keys(self): self.lst.remove(key) dict.__setitem__(self,key,value) self.lst.append(key) def __delitem__(self,key): dict.__delitem__(self,key) try: self.lst.remove(key) except ValueError: pass def __iter__(self): return reversed(self.lst) def keys(self): return reversed(self.lst) class lru_node(object): __slots__=('next','prev','key','val') def __init__(self): self.next=self self.prev=self self.key=None self.val=None class lru_cache(object): __slots__=('maxlen','table','head') def __init__(self,maxlen=100): self.maxlen=maxlen self.table={} self.head=lru_node() self.head.next=self.head self.head.prev=self.head def __getitem__(self,key): node=self.table[key] if node is self.head: return node.val node.prev.next=node.next node.next.prev=node.prev node.next=self.head.next node.prev=self.head self.head=node.next.prev=node.prev.next=node return node.val def __setitem__(self,key,val): if key in self.table: node=self.table[key] node.val=val self.__getitem__(key) else: if len(self.table)0x3000000 and not'b'in m: m+='b' f=open(fname,m) try: txt=f.read() finally: f.close() if encoding: txt=txt.decode(encoding) else: txt=txt.decode() else: f=open(fname,m) try: txt=f.read() finally: f.close() return txt def writef(fname,data,m='w',encoding='ISO8859-1'): if sys.hexversion>0x3000000 and not'b'in m: data=data.encode(encoding) m+='b' f=open(fname,m) try: f.write(data) finally: f.close() def h_file(fname): f=open(fname,'rb') m=md5() try: while fname: fname=f.read(200000) m.update(fname) finally: f.close() return m.digest() def readf_win32(f,m='r',encoding='ISO8859-1'): flags=os.O_NOINHERIT|os.O_RDONLY if'b'in m: flags|=os.O_BINARY if'+'in m: flags|=os.O_RDWR try: fd=os.open(f,flags) except OSError: raise IOError('Cannot read from %r'%f) if sys.hexversion>0x3000000 and not'b'in m: m+='b' f=os.fdopen(fd,m) try: txt=f.read() finally: f.close() if encoding: txt=txt.decode(encoding) else: txt=txt.decode() else: f=os.fdopen(fd,m) try: txt=f.read() finally: f.close() return txt def writef_win32(f,data,m='w',encoding='ISO8859-1'): if sys.hexversion>0x3000000 and not'b'in m: data=data.encode(encoding) m+='b' flags=os.O_CREAT|os.O_TRUNC|os.O_WRONLY|os.O_NOINHERIT if'b'in m: flags|=os.O_BINARY if'+'in m: flags|=os.O_RDWR try: fd=os.open(f,flags) except OSError: raise OSError('Cannot write to %r'%f) f=os.fdopen(fd,m) try: f.write(data) finally: f.close() def h_file_win32(fname): try: fd=os.open(fname,os.O_BINARY|os.O_RDONLY|os.O_NOINHERIT) except OSError: raise OSError('Cannot read from %r'%fname) f=os.fdopen(fd,'rb') m=md5() try: while fname: fname=f.read(200000) m.update(fname) finally: f.close() return m.digest() readf_unix=readf writef_unix=writef h_file_unix=h_file if hasattr(os,'O_NOINHERIT')and sys.hexversion<0x3040000: readf=readf_win32 writef=writef_win32 h_file=h_file_win32 try: x=''.encode('hex') except LookupError: import binascii def to_hex(s): ret=binascii.hexlify(s) if not isinstance(ret,str): ret=ret.decode('utf-8') return ret else: def to_hex(s): return s.encode('hex') to_hex.__doc__=""" Return the hexadecimal representation of a string :param s: string to convert :type s: string """ def listdir_win32(s): if not s: try: import ctypes except ImportError: return[x+':\\'for x in'ABCDEFGHIJKLMNOPQRSTUVWXYZ'] else: dlen=4 maxdrives=26 buf=ctypes.create_string_buffer(maxdrives*dlen) ndrives=ctypes.windll.kernel32.GetLogicalDriveStringsA(maxdrives*dlen,ctypes.byref(buf)) return[str(buf.raw[4*i:4*i+2].decode('ascii'))for i in range(int(ndrives/dlen))] if len(s)==2 and s[1]==":": s+=os.sep if not os.path.isdir(s): e=OSError('%s is not a directory'%s) e.errno=errno.ENOENT raise e return os.listdir(s) listdir=os.listdir if is_win32: listdir=listdir_win32 def num2ver(ver): if isinstance(ver,str): ver=tuple(ver.split('.')) if isinstance(ver,tuple): ret=0 for i in range(4): if i0x3000000: ret=ret.encode('iso8859-1','xmlcharrefreplace') return ret reg_subst=re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}") def subst_vars(expr,params): def repl_var(m): if m.group(1): return'\\' if m.group(2): return'$' try: return params.get_flat(m.group(3)) except AttributeError: return params[m.group(3)] return reg_subst.sub(repl_var,expr) def destos_to_binfmt(key): if key=='darwin': return'mac-o' elif key in('win32','cygwin','uwin','msys'): return'pe' return'elf' def unversioned_sys_platform(): s=sys.platform if s.startswith('java'): from java.lang import System s=System.getProperty('os.name') if s=='Mac OS X': return'darwin' elif s.startswith('Windows '): return'win32' elif s=='OS/2': return'os2' elif s=='HP-UX': return'hp-ux' elif s in('SunOS','Solaris'): return'sunos' else:s=s.lower() if s=='powerpc': return'darwin' if s=='win32'or s=='os2': return s if s=='cli'and os.name=='nt': return'win32' return re.split('\d+$',s)[0] def nada(*k,**kw): pass class Timer(object): def __init__(self): self.start_time=datetime.datetime.utcnow() def __str__(self): delta=datetime.datetime.utcnow()-self.start_time days=delta.days hours,rem=divmod(delta.seconds,3600) minutes,seconds=divmod(rem,60) seconds+=delta.microseconds*1e-6 result='' if days: result+='%dd'%days if days or hours: result+='%dh'%hours if days or hours or minutes: result+='%dm'%minutes return'%s%.3fs'%(result,seconds) def read_la_file(path): sp=re.compile(r'^([^=]+)=\'(.*)\'$') dc={} for line in readf(path).splitlines(): try: _,left,right,_=sp.split(line.strip()) dc[left]=right except ValueError: pass return dc def run_once(fun): cache={} def wrap(*k): try: return cache[k] except KeyError: ret=fun(*k) cache[k]=ret return ret wrap.__cache__=cache wrap.__name__=fun.__name__ return wrap def get_registry_app_path(key,filename): if not winreg: return None try: result=winreg.QueryValue(key,"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s.exe"%filename[0]) except WindowsError: pass else: if os.path.isfile(result): return result def lib64(): if os.sep=='/': if platform.architecture()[0]=='64bit': if os.path.exists('/usr/lib64')and not os.path.exists('/usr/lib32'): return'64' return'' def sane_path(p): return os.path.abspath(os.path.expanduser(p)) process_pool=[] def get_process(): try: return process_pool.pop() except IndexError: filepath=os.path.dirname(os.path.abspath(__file__))+os.sep+'processor.py' cmd=[sys.executable,'-c',readf(filepath)] return subprocess.Popen(cmd,stdout=subprocess.PIPE,stdin=subprocess.PIPE,bufsize=0) def run_prefork_process(cmd,kwargs,cargs): if not'env'in kwargs: kwargs['env']=dict(os.environ) try: obj=base64.b64encode(cPickle.dumps([cmd,kwargs,cargs])) except TypeError: return run_regular_process(cmd,kwargs,cargs) proc=get_process() if not proc: return run_regular_process(cmd,kwargs,cargs) proc.stdin.write(obj) proc.stdin.write('\n'.encode()) proc.stdin.flush() obj=proc.stdout.readline() if not obj: raise OSError('Preforked sub-process %r died'%proc.pid) process_pool.append(proc) ret,out,err,ex,trace=cPickle.loads(base64.b64decode(obj)) if ex: if ex=='OSError': raise OSError(trace) elif ex=='ValueError': raise ValueError(trace) elif ex=='TimeoutExpired': exc=TimeoutExpired(cmd,timeout=cargs['timeout'],output=out) exc.stderr=err raise exc else: raise Exception(trace) return ret,out,err def lchown(path,user=-1,group=-1): if isinstance(user,str): import pwd entry=pwd.getpwnam(user) if not entry: raise OSError('Unknown user %r'%user) user=entry[2] if isinstance(group,str): import grp entry=grp.getgrnam(group) if not entry: raise OSError('Unknown group %r'%group) group=entry[2] return os.lchown(path,user,group) def run_regular_process(cmd,kwargs,cargs={}): proc=subprocess.Popen(cmd,**kwargs) if kwargs.get('stdout')or kwargs.get('stderr'): try: out,err=proc.communicate(**cargs) except TimeoutExpired: if kwargs.get('start_new_session')and hasattr(os,'killpg'): os.killpg(proc.pid,signal.SIGKILL) else: proc.kill() out,err=proc.communicate() exc=TimeoutExpired(proc.args,timeout=cargs['timeout'],output=out) exc.stderr=err raise exc status=proc.returncode else: out,err=(None,None) try: status=proc.wait(**cargs) except TimeoutExpired as e: if kwargs.get('start_new_session')and hasattr(os,'killpg'): os.killpg(proc.pid,signal.SIGKILL) else: proc.kill() proc.wait() raise e return status,out,err def run_process(cmd,kwargs,cargs={}): if kwargs.get('stdout')and kwargs.get('stderr'): return run_prefork_process(cmd,kwargs,cargs) else: return run_regular_process(cmd,kwargs,cargs) def alloc_process_pool(n,force=False): global run_process,get_process,alloc_process_pool if not force: n=max(n-len(process_pool),0) try: lst=[get_process()for x in range(n)] except OSError: run_process=run_regular_process get_process=alloc_process_pool=nada else: for x in lst: process_pool.append(x) def atexit_pool(): for k in process_pool: try: os.kill(k.pid,9) except OSError: pass else: k.wait() if(sys.hexversion<0x207000f and not is_win32)or sys.hexversion>=0x306000f: atexit.register(atexit_pool) if sys.platform=='cli'or not sys.executable: run_process=run_regular_process get_process=alloc_process_pool=nada ntpsec-1.1.0+dfsg1/waflib/Node.py0000644000175000017500000002557413210576031016337 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re,sys,shutil from waflib import Utils,Errors exclude_regs=''' **/*~ **/#*# **/.#* **/%*% **/._* **/*.swp **/CVS **/CVS/** **/.cvsignore **/SCCS **/SCCS/** **/vssver.scc **/.svn **/.svn/** **/BitKeeper **/.git **/.git/** **/.gitignore **/.bzr **/.bzrignore **/.bzr/** **/.hg **/.hg/** **/_MTN **/_MTN/** **/.arch-ids **/{arch} **/_darcs **/_darcs/** **/.intlcache **/.DS_Store''' class Node(object): dict_class=dict __slots__=('name','parent','children','cache_abspath','cache_isdir') def __init__(self,name,parent): self.name=name self.parent=parent if parent: if name in parent.children: raise Errors.WafError('node %s exists in the parent files %r already'%(name,parent)) parent.children[name]=self def __setstate__(self,data): self.name=data[0] self.parent=data[1] if data[2]is not None: self.children=self.dict_class(data[2]) def __getstate__(self): return(self.name,self.parent,getattr(self,'children',None)) def __str__(self): return self.abspath() def __repr__(self): return self.abspath() def __copy__(self): raise Errors.WafError('nodes are not supposed to be copied') def read(self,flags='r',encoding='ISO8859-1'): return Utils.readf(self.abspath(),flags,encoding) def write(self,data,flags='w',encoding='ISO8859-1'): Utils.writef(self.abspath(),data,flags,encoding) def read_json(self,convert=True,encoding='utf-8'): import json object_pairs_hook=None if convert and sys.hexversion<0x3000000: try: _type=unicode except NameError: _type=str def convert(value): if isinstance(value,list): return[convert(element)for element in value] elif isinstance(value,_type): return str(value) else: return value def object_pairs(pairs): return dict((str(pair[0]),convert(pair[1]))for pair in pairs) object_pairs_hook=object_pairs return json.loads(self.read(encoding=encoding),object_pairs_hook=object_pairs_hook) def write_json(self,data,pretty=True): import json indent=2 separators=(',',': ') sort_keys=pretty newline=os.linesep if not pretty: indent=None separators=(',',':') newline='' output=json.dumps(data,indent=indent,separators=separators,sort_keys=sort_keys)+newline self.write(output,encoding='utf-8') def exists(self): return os.path.exists(self.abspath()) def isdir(self): return os.path.isdir(self.abspath()) def chmod(self,val): os.chmod(self.abspath(),val) def delete(self,evict=True): try: try: if os.path.isdir(self.abspath()): shutil.rmtree(self.abspath()) else: os.remove(self.abspath()) except OSError: if os.path.exists(self.abspath()): raise finally: if evict: self.evict() def evict(self): del self.parent.children[self.name] def suffix(self): k=max(0,self.name.rfind('.')) return self.name[k:] def height(self): d=self val=-1 while d: d=d.parent val+=1 return val def listdir(self): lst=Utils.listdir(self.abspath()) lst.sort() return lst def mkdir(self): if self.isdir(): return try: self.parent.mkdir() except OSError: pass if self.name: try: os.makedirs(self.abspath()) except OSError: pass if not self.isdir(): raise Errors.WafError('Could not create the directory %r'%self) try: self.children except AttributeError: self.children=self.dict_class() def find_node(self,lst): if isinstance(lst,str): lst=[x for x in Utils.split_path(lst)if x and x!='.'] cur=self for x in lst: if x=='..': cur=cur.parent or cur continue try: ch=cur.children except AttributeError: cur.children=self.dict_class() else: try: cur=ch[x] continue except KeyError: pass cur=self.__class__(x,cur) if not cur.exists(): cur.evict() return None if not cur.exists(): cur.evict() return None return cur def make_node(self,lst): if isinstance(lst,str): lst=[x for x in Utils.split_path(lst)if x and x!='.'] cur=self for x in lst: if x=='..': cur=cur.parent or cur continue try: cur=cur.children[x] except AttributeError: cur.children=self.dict_class() except KeyError: pass else: continue cur=self.__class__(x,cur) return cur def search_node(self,lst): if isinstance(lst,str): lst=[x for x in Utils.split_path(lst)if x and x!='.'] cur=self for x in lst: if x=='..': cur=cur.parent or cur else: try: cur=cur.children[x] except(AttributeError,KeyError): return None return cur def path_from(self,node): c1=self c2=node c1h=c1.height() c2h=c2.height() lst=[] up=0 while c1h>c2h: lst.append(c1.name) c1=c1.parent c1h-=1 while c2h>c1h: up+=1 c2=c2.parent c2h-=1 while not c1 is c2: lst.append(c1.name) up+=1 c1=c1.parent c2=c2.parent if c1.parent: lst.extend(['..']*up) lst.reverse() return os.sep.join(lst)or'.' else: return self.abspath() def abspath(self): try: return self.cache_abspath except AttributeError: pass if not self.parent: val=os.sep elif not self.parent.name: val=os.sep+self.name else: val=self.parent.abspath()+os.sep+self.name self.cache_abspath=val return val if Utils.is_win32: def abspath(self): try: return self.cache_abspath except AttributeError: pass if not self.parent: val='' elif not self.parent.name: val=self.name+os.sep else: val=self.parent.abspath().rstrip(os.sep)+os.sep+self.name self.cache_abspath=val return val def is_child_of(self,node): p=self diff=self.height()-node.height() while diff>0: diff-=1 p=p.parent return p is node def ant_iter(self,accept=None,maxdepth=25,pats=[],dir=False,src=True,remove=True): dircont=self.listdir() dircont.sort() try: lst=set(self.children.keys()) except AttributeError: self.children=self.dict_class() else: if remove: for x in lst-set(dircont): self.children[x].evict() for name in dircont: npats=accept(name,pats) if npats and npats[0]: accepted=[]in npats[0] node=self.make_node([name]) isdir=node.isdir() if accepted: if isdir: if dir: yield node else: if src: yield node if isdir: node.cache_isdir=True if maxdepth: for k in node.ant_iter(accept=accept,maxdepth=maxdepth-1,pats=npats,dir=dir,src=src,remove=remove): yield k raise StopIteration def ant_glob(self,*k,**kw): src=kw.get('src',True) dir=kw.get('dir',False) excl=kw.get('excl',exclude_regs) incl=k and k[0]or kw.get('incl','**') reflags=kw.get('ignorecase',0)and re.I def to_pat(s): lst=Utils.to_list(s) ret=[] for x in lst: x=x.replace('\\','/').replace('//','/') if x.endswith('/'): x+='**' lst2=x.split('/') accu=[] for k in lst2: if k=='**': accu.append(k) else: k=k.replace('.','[.]').replace('*','.*').replace('?','.').replace('+','\\+') k='^%s$'%k try: accu.append(re.compile(k,flags=reflags)) except Exception as e: raise Errors.WafError('Invalid pattern: %s'%k,e) ret.append(accu) return ret def filtre(name,nn): ret=[] for lst in nn: if not lst: pass elif lst[0]=='**': ret.append(lst) if len(lst)>1: if lst[1].match(name): ret.append(lst[2:]) else: ret.append([]) elif lst[0].match(name): ret.append(lst[1:]) return ret def accept(name,pats): nacc=filtre(name,pats[0]) nrej=filtre(name,pats[1]) if[]in nrej: nacc=[] return[nacc,nrej] ret=[x for x in self.ant_iter(accept=accept,pats=[to_pat(incl),to_pat(excl)],maxdepth=kw.get('maxdepth',25),dir=dir,src=src,remove=kw.get('remove',True))] if kw.get('flat',False): return' '.join([x.path_from(self)for x in ret]) return ret def is_src(self): cur=self x=self.ctx.srcnode y=self.ctx.bldnode while cur.parent: if cur is y: return False if cur is x: return True cur=cur.parent return False def is_bld(self): cur=self y=self.ctx.bldnode while cur.parent: if cur is y: return True cur=cur.parent return False def get_src(self): cur=self x=self.ctx.srcnode y=self.ctx.bldnode lst=[] while cur.parent: if cur is y: lst.reverse() return x.make_node(lst) if cur is x: return self lst.append(cur.name) cur=cur.parent return self def get_bld(self): cur=self x=self.ctx.srcnode y=self.ctx.bldnode lst=[] while cur.parent: if cur is y: return self if cur is x: lst.reverse() return self.ctx.bldnode.make_node(lst) lst.append(cur.name) cur=cur.parent lst.reverse() if lst and Utils.is_win32 and len(lst[0])==2 and lst[0].endswith(':'): lst[0]=lst[0][0] return self.ctx.bldnode.make_node(['__root__']+lst) def find_resource(self,lst): if isinstance(lst,str): lst=[x for x in Utils.split_path(lst)if x and x!='.'] node=self.get_bld().search_node(lst) if not node: node=self.get_src().find_node(lst) if node and node.isdir(): return None return node def find_or_declare(self,lst): if isinstance(lst,str): lst=[x for x in Utils.split_path(lst)if x and x!='.'] node=self.get_bld().search_node(lst) if node: if not os.path.isfile(node.abspath()): node.parent.mkdir() return node self=self.get_src() node=self.find_node(lst) if node: return node node=self.get_bld().make_node(lst) node.parent.mkdir() return node def find_dir(self,lst): if isinstance(lst,str): lst=[x for x in Utils.split_path(lst)if x and x!='.'] node=self.find_node(lst) if node and not node.isdir(): return None return node def change_ext(self,ext,ext_in=None): name=self.name if ext_in is None: k=name.rfind('.') if k>=0: name=name[:k]+ext else: name=name+ext else: name=name[:-len(ext_in)]+ext return self.parent.find_or_declare([name]) def bldpath(self): return self.path_from(self.ctx.bldnode) def srcpath(self): return self.path_from(self.ctx.srcnode) def relpath(self): cur=self x=self.ctx.bldnode while cur.parent: if cur is x: return self.bldpath() cur=cur.parent return self.srcpath() def bld_dir(self): return self.parent.bldpath() def h_file(self): return Utils.h_file(self.abspath()) def get_bld_sig(self): try: cache=self.ctx.cache_sig except AttributeError: cache=self.ctx.cache_sig={} try: ret=cache[self] except KeyError: p=self.abspath() try: ret=cache[self]=self.h_file() except EnvironmentError: if self.isdir(): st=os.stat(p) ret=cache[self]=Utils.h_list([p,st.st_ino,st.st_mode]) return ret raise return ret def get_sig(self): return self.h_file() def set_sig(self,val): try: del self.get_bld_sig.__cache__[(self,)] except(AttributeError,KeyError): pass sig=property(get_sig,set_sig) cache_sig=property(get_sig,set_sig) pickle_lock=Utils.threading.Lock() class Nod3(Node): pass ntpsec-1.1.0+dfsg1/waflib/ConfigSet.py0000755000175000017500000000746013210576031017330 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import copy,re,os from waflib import Logs,Utils re_imp=re.compile('^(#)*?([^#=]*?)\ =\ (.*?)$',re.M) class ConfigSet(object): __slots__=('table','parent') def __init__(self,filename=None): self.table={} if filename: self.load(filename) def __contains__(self,key): if key in self.table: return True try: return self.parent.__contains__(key) except AttributeError: return False def keys(self): keys=set() cur=self while cur: keys.update(cur.table.keys()) cur=getattr(cur,'parent',None) keys=list(keys) keys.sort() return keys def __iter__(self): return iter(self.keys()) def __str__(self): return"\n".join(["%r %r"%(x,self.__getitem__(x))for x in self.keys()]) def __getitem__(self,key): try: while 1: x=self.table.get(key) if not x is None: return x self=self.parent except AttributeError: return[] def __setitem__(self,key,value): self.table[key]=value def __delitem__(self,key): self[key]=[] def __getattr__(self,name): if name in self.__slots__: return object.__getattr__(self,name) else: return self[name] def __setattr__(self,name,value): if name in self.__slots__: object.__setattr__(self,name,value) else: self[name]=value def __delattr__(self,name): if name in self.__slots__: object.__delattr__(self,name) else: del self[name] def derive(self): newenv=ConfigSet() newenv.parent=self return newenv def detach(self): tbl=self.get_merged_dict() try: delattr(self,'parent') except AttributeError: pass else: keys=tbl.keys() for x in keys: tbl[x]=copy.deepcopy(tbl[x]) self.table=tbl return self def get_flat(self,key): s=self[key] if isinstance(s,str): return s return' '.join(s) def _get_list_value_for_modification(self,key): try: value=self.table[key] except KeyError: try: value=self.parent[key] except AttributeError: value=[] else: if isinstance(value,list): value=value[:] else: value=[value] self.table[key]=value else: if not isinstance(value,list): self.table[key]=value=[value] return value def append_value(self,var,val): if isinstance(val,str): val=[val] current_value=self._get_list_value_for_modification(var) current_value.extend(val) def prepend_value(self,var,val): if isinstance(val,str): val=[val] self.table[var]=val+self._get_list_value_for_modification(var) def append_unique(self,var,val): if isinstance(val,str): val=[val] current_value=self._get_list_value_for_modification(var) for x in val: if x not in current_value: current_value.append(x) def get_merged_dict(self): table_list=[] env=self while 1: table_list.insert(0,env.table) try: env=env.parent except AttributeError: break merged_table={} for table in table_list: merged_table.update(table) return merged_table def store(self,filename): try: os.makedirs(os.path.split(filename)[0]) except OSError: pass buf=[] merged_table=self.get_merged_dict() keys=list(merged_table.keys()) keys.sort() try: fun=ascii except NameError: fun=repr for k in keys: if k!='undo_stack': buf.append('%s = %s\n'%(k,fun(merged_table[k]))) Utils.writef(filename,''.join(buf)) def load(self,filename): tbl=self.table code=Utils.readf(filename,m='rU') for m in re_imp.finditer(code): g=m.group tbl[g(2)]=eval(g(3)) Logs.debug('env: %s',self.table) def update(self,d): self.table.update(d) def stash(self): orig=self.table tbl=self.table=self.table.copy() for x in tbl.keys(): tbl[x]=copy.deepcopy(tbl[x]) self.undo_stack=self.undo_stack+[orig] def commit(self): self.undo_stack.pop(-1) def revert(self): self.table=self.undo_stack.pop(-1) ntpsec-1.1.0+dfsg1/waflib/processor.py0000755000175000017500000000256613210576031017470 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,sys,traceback,base64,signal try: import cPickle except ImportError: import pickle as cPickle try: import subprocess32 as subprocess except ImportError: import subprocess try: TimeoutExpired=subprocess.TimeoutExpired except AttributeError: class TimeoutExpired(Exception): pass def run(): txt=sys.stdin.readline().strip() if not txt: sys.exit(1) [cmd,kwargs,cargs]=cPickle.loads(base64.b64decode(txt)) cargs=cargs or{} ret=1 out,err,ex,trace=(None,None,None,None) try: proc=subprocess.Popen(cmd,**kwargs) try: out,err=proc.communicate(**cargs) except TimeoutExpired: if kwargs.get('start_new_session')and hasattr(os,'killpg'): os.killpg(proc.pid,signal.SIGKILL) else: proc.kill() out,err=proc.communicate() exc=TimeoutExpired(proc.args,timeout=cargs['timeout'],output=out) exc.stderr=err raise exc ret=proc.returncode except Exception as e: exc_type,exc_value,tb=sys.exc_info() exc_lines=traceback.format_exception(exc_type,exc_value,tb) trace=str(cmd)+'\n'+''.join(exc_lines) ex=e.__class__.__name__ tmp=[ret,out,err,ex,trace] obj=base64.b64encode(cPickle.dumps(tmp)) sys.stdout.write(obj.decode()) sys.stdout.write('\n') sys.stdout.flush() while 1: try: run() except KeyboardInterrupt: break ntpsec-1.1.0+dfsg1/waflib/Context.py0000644000175000017500000002471313210576031017070 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re,imp,sys from waflib import Utils,Errors,Logs import waflib.Node HEXVERSION=0x1090e00 WAFVERSION="1.9.14" WAFREVISION="1343b6f466c2357df01378f8fa0a28c3808d7325" ABI=99 DBFILE='.wafpickle-%s-%d-%d'%(sys.platform,sys.hexversion,ABI) APPNAME='APPNAME' VERSION='VERSION' TOP='top' OUT='out' WSCRIPT_FILE='wscript' launch_dir='' run_dir='' top_dir='' out_dir='' waf_dir='' g_module=None STDOUT=1 STDERR=-1 BOTH=0 classes=[] def create_context(cmd_name,*k,**kw): global classes for x in classes: if x.cmd==cmd_name: return x(*k,**kw) ctx=Context(*k,**kw) ctx.fun=cmd_name return ctx class store_context(type): def __init__(cls,name,bases,dct): super(store_context,cls).__init__(name,bases,dct) name=cls.__name__ if name in('ctx','Context'): return try: cls.cmd except AttributeError: raise Errors.WafError('Missing command for the context class %r (cmd)'%name) if not getattr(cls,'fun',None): cls.fun=cls.cmd global classes classes.insert(0,cls) ctx=store_context('ctx',(object,),{}) class Context(ctx): errors=Errors tools={} def __init__(self,**kw): try: rd=kw['run_dir'] except KeyError: global run_dir rd=run_dir self.node_class=type('Nod3',(waflib.Node.Node,),{}) self.node_class.__module__='waflib.Node' self.node_class.ctx=self self.root=self.node_class('',None) self.cur_script=None self.path=self.root.find_dir(rd) self.stack_path=[] self.exec_dict={'ctx':self,'conf':self,'bld':self,'opt':self} self.logger=None def finalize(self): try: logger=self.logger except AttributeError: pass else: Logs.free_logger(logger) delattr(self,'logger') def load(self,tool_list,*k,**kw): tools=Utils.to_list(tool_list) path=Utils.to_list(kw.get('tooldir','')) with_sys_path=kw.get('with_sys_path',True) for t in tools: module=load_tool(t,path,with_sys_path=with_sys_path) fun=getattr(module,kw.get('name',self.fun),None) if fun: fun(self) def execute(self): global g_module self.recurse([os.path.dirname(g_module.root_path)]) def pre_recurse(self,node): self.stack_path.append(self.cur_script) self.cur_script=node self.path=node.parent def post_recurse(self,node): self.cur_script=self.stack_path.pop() if self.cur_script: self.path=self.cur_script.parent def recurse(self,dirs,name=None,mandatory=True,once=True,encoding=None): try: cache=self.recurse_cache except AttributeError: cache=self.recurse_cache={} for d in Utils.to_list(dirs): if not os.path.isabs(d): d=os.path.join(self.path.abspath(),d) WSCRIPT=os.path.join(d,WSCRIPT_FILE) WSCRIPT_FUN=WSCRIPT+'_'+(name or self.fun) node=self.root.find_node(WSCRIPT_FUN) if node and(not once or node not in cache): cache[node]=True self.pre_recurse(node) try: function_code=node.read('rU',encoding) exec(compile(function_code,node.abspath(),'exec'),self.exec_dict) finally: self.post_recurse(node) elif not node: node=self.root.find_node(WSCRIPT) tup=(node,name or self.fun) if node and(not once or tup not in cache): cache[tup]=True self.pre_recurse(node) try: wscript_module=load_module(node.abspath(),encoding=encoding) user_function=getattr(wscript_module,(name or self.fun),None) if not user_function: if not mandatory: continue raise Errors.WafError('No function %r defined in %s'%(name or self.fun,node.abspath())) user_function(self) finally: self.post_recurse(node) elif not node: if not mandatory: continue try: os.listdir(d) except OSError: raise Errors.WafError('Cannot read the folder %r'%d) raise Errors.WafError('No wscript file in directory %s'%d) def exec_command(self,cmd,**kw): subprocess=Utils.subprocess kw['shell']=isinstance(cmd,str) Logs.debug('runner: %r',cmd) Logs.debug('runner_env: kw=%s',kw) if self.logger: self.logger.info(cmd) if'stdout'not in kw: kw['stdout']=subprocess.PIPE if'stderr'not in kw: kw['stderr']=subprocess.PIPE if Logs.verbose and not kw['shell']and not Utils.check_exe(cmd[0]): raise Errors.WafError('Program %s not found!'%cmd[0]) cargs={} if'timeout'in kw: if sys.hexversion>=0x3030000: cargs['timeout']=kw['timeout'] if not'start_new_session'in kw: kw['start_new_session']=True del kw['timeout'] if'input'in kw: if kw['input']: cargs['input']=kw['input'] kw['stdin']=subprocess.PIPE del kw['input'] if'cwd'in kw: if not isinstance(kw['cwd'],str): kw['cwd']=kw['cwd'].abspath() try: ret,out,err=Utils.run_process(cmd,kw,cargs) except Exception as e: raise Errors.WafError('Execution failure: %s'%str(e),ex=e) if out: if not isinstance(out,str): out=out.decode(sys.stdout.encoding or'iso8859-1',errors='replace') if self.logger: self.logger.debug('out: %s',out) else: Logs.info(out,extra={'stream':sys.stdout,'c1':''}) if err: if not isinstance(err,str): err=err.decode(sys.stdout.encoding or'iso8859-1',errors='replace') if self.logger: self.logger.error('err: %s'%err) else: Logs.info(err,extra={'stream':sys.stderr,'c1':''}) return ret def cmd_and_log(self,cmd,**kw): subprocess=Utils.subprocess kw['shell']=isinstance(cmd,str) Logs.debug('runner: %r',cmd) if'quiet'in kw: quiet=kw['quiet'] del kw['quiet'] else: quiet=None if'output'in kw: to_ret=kw['output'] del kw['output'] else: to_ret=STDOUT if Logs.verbose and not kw['shell']and not Utils.check_exe(cmd[0]): raise Errors.WafError('Program %r not found!'%cmd[0]) kw['stdout']=kw['stderr']=subprocess.PIPE if quiet is None: self.to_log(cmd) cargs={} if'timeout'in kw: if sys.hexversion>=0x3030000: cargs['timeout']=kw['timeout'] if not'start_new_session'in kw: kw['start_new_session']=True del kw['timeout'] if'input'in kw: if kw['input']: cargs['input']=kw['input'] kw['stdin']=subprocess.PIPE del kw['input'] if'cwd'in kw: if not isinstance(kw['cwd'],str): kw['cwd']=kw['cwd'].abspath() try: ret,out,err=Utils.run_process(cmd,kw,cargs) except Exception as e: raise Errors.WafError('Execution failure: %s'%str(e),ex=e) if not isinstance(out,str): out=out.decode(sys.stdout.encoding or'iso8859-1',errors='replace') if not isinstance(err,str): err=err.decode(sys.stdout.encoding or'iso8859-1',errors='replace') if out and quiet!=STDOUT and quiet!=BOTH: self.to_log('out: %s'%out) if err and quiet!=STDERR and quiet!=BOTH: self.to_log('err: %s'%err) if ret: e=Errors.WafError('Command %r returned %r'%(cmd,ret)) e.returncode=ret e.stderr=err e.stdout=out raise e if to_ret==BOTH: return(out,err) elif to_ret==STDERR: return err return out def fatal(self,msg,ex=None): if self.logger: self.logger.info('from %s: %s'%(self.path.abspath(),msg)) try: logfile=self.logger.handlers[0].baseFilename except AttributeError: pass else: if os.environ.get('WAF_PRINT_FAILURE_LOG'): msg='Log from (%s):\n%s\n'%(logfile,Utils.readf(logfile)) else: msg='%s\n(complete log in %s)'%(msg,logfile) raise self.errors.ConfigurationError(msg,ex=ex) def to_log(self,msg): if not msg: return if self.logger: self.logger.info(msg) else: sys.stderr.write(str(msg)) sys.stderr.flush() def msg(self,*k,**kw): try: msg=kw['msg'] except KeyError: msg=k[0] self.start_msg(msg,**kw) try: result=kw['result'] except KeyError: result=k[1] color=kw.get('color') if not isinstance(color,str): color=result and'GREEN'or'YELLOW' self.end_msg(result,color,**kw) def start_msg(self,*k,**kw): if kw.get('quiet'): return msg=kw.get('msg')or k[0] try: if self.in_msg: self.in_msg+=1 return except AttributeError: self.in_msg=0 self.in_msg+=1 try: self.line_just=max(self.line_just,len(msg)) except AttributeError: self.line_just=max(40,len(msg)) for x in(self.line_just*'-',msg): self.to_log(x) Logs.pprint('NORMAL',"%s :"%msg.ljust(self.line_just),sep='') def end_msg(self,*k,**kw): if kw.get('quiet'): return self.in_msg-=1 if self.in_msg: return result=kw.get('result')or k[0] defcolor='GREEN' if result==True: msg='ok' elif result==False: msg='not found' defcolor='YELLOW' else: msg=str(result) self.to_log(msg) try: color=kw['color'] except KeyError: if len(k)>1 and k[1]in Logs.colors_lst: color=k[1] else: color=defcolor Logs.pprint(color,msg) def load_special_tools(self,var,ban=[]): global waf_dir if os.path.isdir(waf_dir): lst=self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var) for x in lst: if not x.name in ban: load_tool(x.name.replace('.py','')) else: from zipfile import PyZipFile waflibs=PyZipFile(waf_dir) lst=waflibs.namelist() for x in lst: if not re.match('waflib/extras/%s'%var.replace('*','.*'),var): continue f=os.path.basename(x) doban=False for b in ban: r=b.replace('*','.*') if re.match(r,f): doban=True if not doban: f=f.replace('.py','') load_tool(f) cache_modules={} def load_module(path,encoding=None): try: return cache_modules[path] except KeyError: pass module=imp.new_module(WSCRIPT_FILE) try: code=Utils.readf(path,m='rU',encoding=encoding) except EnvironmentError: raise Errors.WafError('Could not read the file %r'%path) module_dir=os.path.dirname(path) sys.path.insert(0,module_dir) try: exec(compile(code,path,'exec'),module.__dict__) finally: sys.path.remove(module_dir) cache_modules[path]=module return module def load_tool(tool,tooldir=None,ctx=None,with_sys_path=True): if tool=='java': tool='javaw' else: tool=tool.replace('++','xx') if not with_sys_path: back_path=sys.path sys.path=[] try: if tooldir: assert isinstance(tooldir,list) sys.path=tooldir+sys.path try: __import__(tool) finally: for d in tooldir: sys.path.remove(d) ret=sys.modules[tool] Context.tools[tool]=ret return ret else: if not with_sys_path: sys.path.insert(0,waf_dir) try: for x in('waflib.Tools.%s','waflib.extras.%s','waflib.%s','%s'): try: __import__(x%tool) break except ImportError: x=None else: __import__(tool) finally: if not with_sys_path: sys.path.remove(waf_dir) ret=sys.modules[x%tool] Context.tools[tool]=ret return ret finally: if not with_sys_path: sys.path+=back_path ntpsec-1.1.0+dfsg1/waflib/__pycache__/0000775000175000017500000000000013252650652017305 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/waflib/Scripting.py0000644000175000017500000002702113210576031017401 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,shlex,shutil,traceback,errno,sys,stat from waflib import Utils,Configure,Logs,Options,ConfigSet,Context,Errors,Build,Node build_dir_override=None no_climb_commands=['configure'] default_cmd="build" def waf_entry_point(current_directory,version,wafdir): Logs.init_log() if Context.WAFVERSION!=version: Logs.error('Waf script %r and library %r do not match (directory %r)',version,Context.WAFVERSION,wafdir) sys.exit(1) if'--version'in sys.argv: Context.run_dir=current_directory ctx=Context.create_context('options') ctx.curdir=current_directory ctx.parse_args() sys.exit(0) Context.waf_dir=wafdir Context.launch_dir=current_directory if len(sys.argv)>1: potential_wscript=os.path.join(current_directory,sys.argv[1]) if os.path.basename(potential_wscript)=='wscript'and os.path.isfile(potential_wscript): current_directory=os.path.normpath(os.path.dirname(potential_wscript)) sys.argv.pop(1) no_climb=os.environ.get('NOCLIMB') if not no_climb: for k in no_climb_commands: for y in sys.argv: if y.startswith(k): no_climb=True break for i,x in enumerate(sys.argv): if x.startswith('--top='): Context.run_dir=Context.top_dir=Utils.sane_path(x[6:]) sys.argv[i]='--top='+Context.run_dir if x.startswith('--out='): Context.out_dir=Utils.sane_path(x[6:]) sys.argv[i]='--out='+Context.out_dir cur=current_directory while cur and not Context.top_dir: try: lst=os.listdir(cur) except OSError: lst=[] Logs.error('Directory %r is unreadable!',cur) if Options.lockfile in lst: env=ConfigSet.ConfigSet() try: env.load(os.path.join(cur,Options.lockfile)) ino=os.stat(cur)[stat.ST_INO] except EnvironmentError: pass else: for x in(env.run_dir,env.top_dir,env.out_dir): if not x: continue if Utils.is_win32: if cur==x: load=True break else: try: ino2=os.stat(x)[stat.ST_INO] except OSError: pass else: if ino==ino2: load=True break else: Logs.warn('invalid lock file in %s',cur) load=False if load: Context.run_dir=env.run_dir Context.top_dir=env.top_dir Context.out_dir=env.out_dir break if not Context.run_dir: if Context.WSCRIPT_FILE in lst: Context.run_dir=cur next=os.path.dirname(cur) if next==cur: break cur=next if no_climb: break if not Context.run_dir: if'-h'in sys.argv or'--help'in sys.argv: Logs.warn('No wscript file found: the help message may be incomplete') Context.run_dir=current_directory ctx=Context.create_context('options') ctx.curdir=current_directory ctx.parse_args() sys.exit(0) Logs.error('Waf: Run from a directory containing a file named %r',Context.WSCRIPT_FILE) sys.exit(1) try: os.chdir(Context.run_dir) except OSError: Logs.error('Waf: The folder %r is unreadable',Context.run_dir) sys.exit(1) try: set_main_module(os.path.normpath(os.path.join(Context.run_dir,Context.WSCRIPT_FILE))) except Errors.WafError as e: Logs.pprint('RED',e.verbose_msg) Logs.error(str(e)) sys.exit(1) except Exception as e: Logs.error('Waf: The wscript in %r is unreadable',Context.run_dir) traceback.print_exc(file=sys.stdout) sys.exit(2) if'--profile'in sys.argv: import cProfile,pstats cProfile.runctx('from waflib import Scripting; Scripting.run_commands()',{},{},'profi.txt') p=pstats.Stats('profi.txt') p.sort_stats('time').print_stats(75) else: try: run_commands() except Errors.WafError as e: if Logs.verbose>1: Logs.pprint('RED',e.verbose_msg) Logs.error(e.msg) sys.exit(1) except SystemExit: raise except Exception as e: traceback.print_exc(file=sys.stdout) sys.exit(2) except KeyboardInterrupt: Logs.pprint('RED','Interrupted') sys.exit(68) def set_main_module(file_path): Context.g_module=Context.load_module(file_path) Context.g_module.root_path=file_path def set_def(obj): name=obj.__name__ if not name in Context.g_module.__dict__: setattr(Context.g_module,name,obj) for k in(dist,distclean,distcheck): set_def(k) if not'init'in Context.g_module.__dict__: Context.g_module.init=Utils.nada if not'shutdown'in Context.g_module.__dict__: Context.g_module.shutdown=Utils.nada if not'options'in Context.g_module.__dict__: Context.g_module.options=Utils.nada def parse_options(): Context.create_context('options').execute() for var in Options.envvars: (name,value)=var.split('=',1) os.environ[name.strip()]=value if not Options.commands: Options.commands=[default_cmd] Options.commands=[x for x in Options.commands if x!='options'] Logs.verbose=Options.options.verbose if Options.options.zones: Logs.zones=Options.options.zones.split(',') if not Logs.verbose: Logs.verbose=1 elif Logs.verbose>0: Logs.zones=['runner'] if Logs.verbose>2: Logs.zones=['*'] def run_command(cmd_name): ctx=Context.create_context(cmd_name) ctx.log_timer=Utils.Timer() ctx.options=Options.options ctx.cmd=cmd_name try: ctx.execute() finally: ctx.finalize() return ctx def run_commands(): parse_options() run_command('init') while Options.commands: cmd_name=Options.commands.pop(0) ctx=run_command(cmd_name) Logs.info('%r finished successfully (%s)',cmd_name,ctx.log_timer) run_command('shutdown') def distclean_dir(dirname): for(root,dirs,files)in os.walk(dirname): for f in files: if f.endswith(('.o','.moc','.exe')): fname=os.path.join(root,f) try: os.remove(fname) except OSError: Logs.warn('Could not remove %r',fname) for x in(Context.DBFILE,'config.log'): try: os.remove(x) except OSError: pass try: shutil.rmtree('c4che') except OSError: pass def distclean(ctx): '''removes the build directory''' def remove_and_log(k,fun): try: fun(k) except EnvironmentError as e: if e.errno!=errno.ENOENT: Logs.warn('Could not remove %r',k) if not Options.commands: for k in os.listdir('.'): for x in'.waf-1. waf-1. .waf3-1. waf3-1.'.split(): if k.startswith(x): remove_and_log(k,shutil.rmtree) cur='.' if ctx.options.no_lock_in_top: cur=ctx.options.out try: lst=os.listdir(cur) except OSError: Logs.warn('Could not read %r',cur) return if Options.lockfile in lst: f=os.path.join(cur,Options.lockfile) try: env=ConfigSet.ConfigSet(f) except EnvironmentError: Logs.warn('Could not read %r',f) return if not env.out_dir or not env.top_dir: Logs.warn('Invalid lock file %r',f) return if env.out_dir==env.top_dir: distclean_dir(env.out_dir) else: remove_and_log(env.out_dir,shutil.rmtree) for k in(env.out_dir,env.top_dir,env.run_dir): p=os.path.join(k,Options.lockfile) remove_and_log(p,os.remove) class Dist(Context.Context): '''creates an archive containing the project source code''' cmd='dist' fun='dist' algo='tar.bz2' ext_algo={} def execute(self): self.recurse([os.path.dirname(Context.g_module.root_path)]) self.archive() def archive(self): import tarfile arch_name=self.get_arch_name() try: self.base_path except AttributeError: self.base_path=self.path node=self.base_path.make_node(arch_name) try: node.delete() except OSError: pass files=self.get_files() if self.algo.startswith('tar.'): tar=tarfile.open(node.abspath(),'w:'+self.algo.replace('tar.','')) for x in files: self.add_tar_file(x,tar) tar.close() elif self.algo=='zip': import zipfile zip=zipfile.ZipFile(node.abspath(),'w',compression=zipfile.ZIP_DEFLATED) for x in files: archive_name=self.get_base_name()+'/'+x.path_from(self.base_path) zip.write(x.abspath(),archive_name,zipfile.ZIP_DEFLATED) zip.close() else: self.fatal('Valid algo types are tar.bz2, tar.gz, tar.xz or zip') try: from hashlib import sha1 except ImportError: digest='' else: digest=' (sha=%r)'%sha1(node.read(flags='rb')).hexdigest() Logs.info('New archive created: %s%s',self.arch_name,digest) def get_tar_path(self,node): return node.abspath() def add_tar_file(self,x,tar): p=self.get_tar_path(x) tinfo=tar.gettarinfo(name=p,arcname=self.get_tar_prefix()+'/'+x.path_from(self.base_path)) tinfo.uid=0 tinfo.gid=0 tinfo.uname='root' tinfo.gname='root' if os.path.isfile(p): fu=open(p,'rb') try: tar.addfile(tinfo,fileobj=fu) finally: fu.close() else: tar.addfile(tinfo) def get_tar_prefix(self): try: return self.tar_prefix except AttributeError: return self.get_base_name() def get_arch_name(self): try: self.arch_name except AttributeError: self.arch_name=self.get_base_name()+'.'+self.ext_algo.get(self.algo,self.algo) return self.arch_name def get_base_name(self): try: self.base_name except AttributeError: appname=getattr(Context.g_module,Context.APPNAME,'noname') version=getattr(Context.g_module,Context.VERSION,'1.0') self.base_name=appname+'-'+version return self.base_name def get_excl(self): try: return self.excl except AttributeError: self.excl=Node.exclude_regs+' **/waf-1.* **/.waf-1.* **/waf3-1.* **/.waf3-1.* **/*~ **/*.rej **/*.orig **/*.pyc **/*.pyo **/*.bak **/*.swp **/.lock-w*' if Context.out_dir: nd=self.root.find_node(Context.out_dir) if nd: self.excl+=' '+nd.path_from(self.base_path) return self.excl def get_files(self): try: files=self.files except AttributeError: files=self.base_path.ant_glob('**/*',excl=self.get_excl()) return files def dist(ctx): '''makes a tarball for redistributing the sources''' pass class DistCheck(Dist): fun='distcheck' cmd='distcheck' def execute(self): self.recurse([os.path.dirname(Context.g_module.root_path)]) self.archive() self.check() def make_distcheck_cmd(self,tmpdir): cfg=[] if Options.options.distcheck_args: cfg=shlex.split(Options.options.distcheck_args) else: cfg=[x for x in sys.argv if x.startswith('-')] cmd=[sys.executable,sys.argv[0],'configure','build','install','uninstall','--destdir='+tmpdir]+cfg return cmd def check(self): import tempfile,tarfile try: t=tarfile.open(self.get_arch_name()) for x in t: t.extract(x) finally: t.close() instdir=tempfile.mkdtemp('.inst',self.get_base_name()) cmd=self.make_distcheck_cmd(instdir) ret=Utils.subprocess.Popen(cmd,cwd=self.get_base_name()).wait() if ret: raise Errors.WafError('distcheck failed with code %r'%ret) if os.path.exists(instdir): raise Errors.WafError('distcheck succeeded, but files were left in %s'%instdir) shutil.rmtree(self.get_base_name()) def distcheck(ctx): '''checks if the project compiles (tarball from 'dist')''' pass def autoconfigure(execute_method): def execute(self): if not Configure.autoconfig: return execute_method(self) env=ConfigSet.ConfigSet() do_config=False try: env.load(os.path.join(Context.top_dir,Options.lockfile)) except EnvironmentError: Logs.warn('Configuring the project') do_config=True else: if env.run_dir!=Context.run_dir: do_config=True else: h=0 for f in env.files: try: h=Utils.h_list((h,Utils.readf(f,'rb'))) except EnvironmentError: do_config=True break else: do_config=h!=env.hash if do_config: cmd=env.config_cmd or'configure' if Configure.autoconfig=='clobber': tmp=Options.options.__dict__ if env.options: Options.options.__dict__=env.options try: run_command(cmd) finally: Options.options.__dict__=tmp else: run_command(cmd) run_command(self.cmd) else: return execute_method(self) return execute Build.BuildContext.execute=autoconfigure(Build.BuildContext.execute) ntpsec-1.1.0+dfsg1/waflib/ansiterm.py0000644000175000017500000002170113210576031017260 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,re,sys from waflib import Utils wlock=Utils.threading.Lock() try: from ctypes import Structure,windll,c_short,c_ushort,c_ulong,c_int,byref,c_wchar,POINTER,c_long except ImportError: class AnsiTerm(object): def __init__(self,stream): self.stream=stream try: self.errors=self.stream.errors except AttributeError: pass self.encoding=self.stream.encoding def write(self,txt): try: wlock.acquire() self.stream.write(txt) self.stream.flush() finally: wlock.release() def fileno(self): return self.stream.fileno() def flush(self): self.stream.flush() def isatty(self): return self.stream.isatty() else: class COORD(Structure): _fields_=[("X",c_short),("Y",c_short)] class SMALL_RECT(Structure): _fields_=[("Left",c_short),("Top",c_short),("Right",c_short),("Bottom",c_short)] class CONSOLE_SCREEN_BUFFER_INFO(Structure): _fields_=[("Size",COORD),("CursorPosition",COORD),("Attributes",c_ushort),("Window",SMALL_RECT),("MaximumWindowSize",COORD)] class CONSOLE_CURSOR_INFO(Structure): _fields_=[('dwSize',c_ulong),('bVisible',c_int)] try: _type=unicode except NameError: _type=str to_int=lambda number,default:number and int(number)or default STD_OUTPUT_HANDLE=-11 STD_ERROR_HANDLE=-12 windll.kernel32.GetStdHandle.argtypes=[c_ulong] windll.kernel32.GetStdHandle.restype=c_ulong windll.kernel32.GetConsoleScreenBufferInfo.argtypes=[c_ulong,POINTER(CONSOLE_SCREEN_BUFFER_INFO)] windll.kernel32.GetConsoleScreenBufferInfo.restype=c_long windll.kernel32.SetConsoleTextAttribute.argtypes=[c_ulong,c_ushort] windll.kernel32.SetConsoleTextAttribute.restype=c_long windll.kernel32.FillConsoleOutputCharacterW.argtypes=[c_ulong,c_wchar,c_ulong,POINTER(COORD),POINTER(c_ulong)] windll.kernel32.FillConsoleOutputCharacterW.restype=c_long windll.kernel32.FillConsoleOutputAttribute.argtypes=[c_ulong,c_ushort,c_ulong,POINTER(COORD),POINTER(c_ulong)] windll.kernel32.FillConsoleOutputAttribute.restype=c_long windll.kernel32.SetConsoleCursorPosition.argtypes=[c_ulong,POINTER(COORD)] windll.kernel32.SetConsoleCursorPosition.restype=c_long windll.kernel32.SetConsoleCursorInfo.argtypes=[c_ulong,POINTER(CONSOLE_CURSOR_INFO)] windll.kernel32.SetConsoleCursorInfo.restype=c_long class AnsiTerm(object): def __init__(self,s): self.stream=s try: self.errors=s.errors except AttributeError: pass self.encoding=s.encoding self.cursor_history=[] handle=(s.fileno()==2)and STD_ERROR_HANDLE or STD_OUTPUT_HANDLE self.hconsole=windll.kernel32.GetStdHandle(handle) self._sbinfo=CONSOLE_SCREEN_BUFFER_INFO() self._csinfo=CONSOLE_CURSOR_INFO() windll.kernel32.GetConsoleCursorInfo(self.hconsole,byref(self._csinfo)) self._orig_sbinfo=CONSOLE_SCREEN_BUFFER_INFO() r=windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole,byref(self._orig_sbinfo)) self._isatty=r==1 def screen_buffer_info(self): windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole,byref(self._sbinfo)) return self._sbinfo def clear_line(self,param): mode=param and int(param)or 0 sbinfo=self.screen_buffer_info() if mode==1: line_start=COORD(0,sbinfo.CursorPosition.Y) line_length=sbinfo.Size.X elif mode==2: line_start=COORD(sbinfo.CursorPosition.X,sbinfo.CursorPosition.Y) line_length=sbinfo.Size.X-sbinfo.CursorPosition.X else: line_start=sbinfo.CursorPosition line_length=sbinfo.Size.X-sbinfo.CursorPosition.X chars_written=c_ulong() windll.kernel32.FillConsoleOutputCharacterW(self.hconsole,c_wchar(' '),line_length,line_start,byref(chars_written)) windll.kernel32.FillConsoleOutputAttribute(self.hconsole,sbinfo.Attributes,line_length,line_start,byref(chars_written)) def clear_screen(self,param): mode=to_int(param,0) sbinfo=self.screen_buffer_info() if mode==1: clear_start=COORD(0,0) clear_length=sbinfo.CursorPosition.X*sbinfo.CursorPosition.Y elif mode==2: clear_start=COORD(0,0) clear_length=sbinfo.Size.X*sbinfo.Size.Y windll.kernel32.SetConsoleCursorPosition(self.hconsole,clear_start) else: clear_start=sbinfo.CursorPosition clear_length=((sbinfo.Size.X-sbinfo.CursorPosition.X)+sbinfo.Size.X*(sbinfo.Size.Y-sbinfo.CursorPosition.Y)) chars_written=c_ulong() windll.kernel32.FillConsoleOutputCharacterW(self.hconsole,c_wchar(' '),clear_length,clear_start,byref(chars_written)) windll.kernel32.FillConsoleOutputAttribute(self.hconsole,sbinfo.Attributes,clear_length,clear_start,byref(chars_written)) def push_cursor(self,param): sbinfo=self.screen_buffer_info() self.cursor_history.append(sbinfo.CursorPosition) def pop_cursor(self,param): if self.cursor_history: old_pos=self.cursor_history.pop() windll.kernel32.SetConsoleCursorPosition(self.hconsole,old_pos) def set_cursor(self,param): y,sep,x=param.partition(';') x=to_int(x,1)-1 y=to_int(y,1)-1 sbinfo=self.screen_buffer_info() new_pos=COORD(min(max(0,x),sbinfo.Size.X),min(max(0,y),sbinfo.Size.Y)) windll.kernel32.SetConsoleCursorPosition(self.hconsole,new_pos) def set_column(self,param): x=to_int(param,1)-1 sbinfo=self.screen_buffer_info() new_pos=COORD(min(max(0,x),sbinfo.Size.X),sbinfo.CursorPosition.Y) windll.kernel32.SetConsoleCursorPosition(self.hconsole,new_pos) def move_cursor(self,x_offset=0,y_offset=0): sbinfo=self.screen_buffer_info() new_pos=COORD(min(max(0,sbinfo.CursorPosition.X+x_offset),sbinfo.Size.X),min(max(0,sbinfo.CursorPosition.Y+y_offset),sbinfo.Size.Y)) windll.kernel32.SetConsoleCursorPosition(self.hconsole,new_pos) def move_up(self,param): self.move_cursor(y_offset=-to_int(param,1)) def move_down(self,param): self.move_cursor(y_offset=to_int(param,1)) def move_left(self,param): self.move_cursor(x_offset=-to_int(param,1)) def move_right(self,param): self.move_cursor(x_offset=to_int(param,1)) def next_line(self,param): sbinfo=self.screen_buffer_info() self.move_cursor(x_offset=-sbinfo.CursorPosition.X,y_offset=to_int(param,1)) def prev_line(self,param): sbinfo=self.screen_buffer_info() self.move_cursor(x_offset=-sbinfo.CursorPosition.X,y_offset=-to_int(param,1)) def rgb2bgr(self,c): return((c&1)<<2)|(c&2)|((c&4)>>2) def set_color(self,param): cols=param.split(';') sbinfo=self.screen_buffer_info() attr=sbinfo.Attributes for c in cols: c=to_int(c,0) if 29>4)|((attr&0x07)<<4) windll.kernel32.SetConsoleTextAttribute(self.hconsole,attr) def show_cursor(self,param): self._csinfo.bVisible=1 windll.kernel32.SetConsoleCursorInfo(self.hconsole,byref(self._csinfo)) def hide_cursor(self,param): self._csinfo.bVisible=0 windll.kernel32.SetConsoleCursorInfo(self.hconsole,byref(self._csinfo)) ansi_command_table={'A':move_up,'B':move_down,'C':move_right,'D':move_left,'E':next_line,'F':prev_line,'G':set_column,'H':set_cursor,'f':set_cursor,'J':clear_screen,'K':clear_line,'h':show_cursor,'l':hide_cursor,'m':set_color,'s':push_cursor,'u':pop_cursor,} ansi_tokens=re.compile('(?:\x1b\[([0-9?;]*)([a-zA-Z])|([^\x1b]+))') def write(self,text): try: wlock.acquire() if self._isatty: for param,cmd,txt in self.ansi_tokens.findall(text): if cmd: cmd_func=self.ansi_command_table.get(cmd) if cmd_func: cmd_func(self,param) else: self.writeconsole(txt) else: self.stream.write(text) finally: wlock.release() def writeconsole(self,txt): chars_written=c_ulong() writeconsole=windll.kernel32.WriteConsoleA if isinstance(txt,_type): writeconsole=windll.kernel32.WriteConsoleW done=0 todo=len(txt) chunk=32<<10 while todo!=0: doing=min(chunk,todo) buf=txt[done:done+doing] r=writeconsole(self.hconsole,buf,doing,byref(chars_written),None) if r==0: chunk>>=1 continue done+=doing todo-=doing def fileno(self): return self.stream.fileno() def flush(self): pass def isatty(self): return self._isatty if sys.stdout.isatty()or sys.stderr.isatty(): handle=sys.stdout.isatty()and STD_OUTPUT_HANDLE or STD_ERROR_HANDLE console=windll.kernel32.GetStdHandle(handle) sbinfo=CONSOLE_SCREEN_BUFFER_INFO() def get_term_cols(): windll.kernel32.GetConsoleScreenBufferInfo(console,byref(sbinfo)) return sbinfo.Size.X-1 try: import struct,fcntl,termios except ImportError: pass else: if(sys.stdout.isatty()or sys.stderr.isatty())and os.environ.get('TERM','')not in('dumb','emacs'): FD=sys.stdout.isatty()and sys.stdout.fileno()or sys.stderr.fileno() def fun(): return struct.unpack("HHHH",fcntl.ioctl(FD,termios.TIOCGWINSZ,struct.pack("HHHH",0,0,0,0)))[1] try: fun() except Exception as e: pass else: get_term_cols=fun ntpsec-1.1.0+dfsg1/waflib/TaskGen.py0000644000175000017500000003154113210576031016775 0ustar rlaagerrlaager#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import copy,re,os,functools from waflib import Task,Utils,Logs,Errors,ConfigSet,Node feats=Utils.defaultdict(set) HEADER_EXTS=['.h','.hpp','.hxx','.hh'] class task_gen(object): mappings=Utils.ordered_iter_dict() prec=Utils.defaultdict(list) def __init__(self,*k,**kw): self.source='' self.target='' self.meths=[] self.features=[] self.tasks=[] if not'bld'in kw: self.env=ConfigSet.ConfigSet() self.idx=0 self.path=None else: self.bld=kw['bld'] self.env=self.bld.env.derive() self.path=self.bld.path try: self.idx=self.bld.idx[self.path]=self.bld.idx.get(self.path,0)+1 except AttributeError: self.bld.idx={} self.idx=self.bld.idx[self.path]=1 for key,val in kw.items(): setattr(self,key,val) def __str__(self): return""%(self.name,self.path.abspath()) def __repr__(self): lst=[] for x in self.__dict__: if x not in('env','bld','compiled_tasks','tasks'): lst.append("%s=%s"%(x,repr(getattr(self,x)))) return"bld(%s) in %s"%(", ".join(lst),self.path.abspath()) def get_cwd(self): return self.bld.bldnode def get_name(self): try: return self._name except AttributeError: if isinstance(self.target,list): lst=[str(x)for x in self.target] name=self._name=','.join(lst) else: name=self._name=str(self.target) return name def set_name(self,name): self._name=name name=property(get_name,set_name) def to_list(self,val): if isinstance(val,str): return val.split() else: return val def post(self): if getattr(self,'posted',None): return False self.posted=True keys=set(self.meths) keys.update(feats['*']) self.features=Utils.to_list(self.features) for x in self.features: st=feats[x] if st: keys.update(st) elif not x in Task.classes: Logs.warn('feature %r does not exist - bind at least one method to it?',x) prec={} prec_tbl=self.prec for x in prec_tbl: if x in keys: prec[x]=prec_tbl[x] tmp=[] for a in keys: for x in prec.values(): if a in x: break else: tmp.append(a) tmp.sort() out=[] while tmp: e=tmp.pop() if e in keys: out.append(e) try: nlst=prec[e] except KeyError: pass else: del prec[e] for x in nlst: for y in prec: if x in prec[y]: break else: tmp.append(x) if prec: buf=['Cycle detected in the method execution:'] for k,v in prec.items(): buf.append('- %s after %s'%(k,[x for x in v if x in prec])) raise Errors.WafError('\n'.join(buf)) out.reverse() self.meths=out Logs.debug('task_gen: posting %s %d',self,id(self)) for x in out: try: v=getattr(self,x) except AttributeError: raise Errors.WafError('%r is not a valid task generator method'%x) Logs.debug('task_gen: -> %s (%d)',x,id(self)) v() Logs.debug('task_gen: posted %s',self.name) return True def get_hook(self,node): name=node.name for k in self.mappings: try: if name.endswith(k): return self.mappings[k] except TypeError: if k.match(name): return self.mappings[k] keys=list(self.mappings.keys()) raise Errors.WafError("File %r has no mapping in %r (load a waf tool?)"%(node,keys)) def create_task(self,name,src=None,tgt=None,**kw): task=Task.classes[name](env=self.env.derive(),generator=self) if src: task.set_inputs(src) if tgt: task.set_outputs(tgt) task.__dict__.update(kw) self.tasks.append(task) return task def clone(self,env): newobj=self.bld() for x in self.__dict__: if x in('env','bld'): continue elif x in('path','features'): setattr(newobj,x,getattr(self,x)) else: setattr(newobj,x,copy.copy(getattr(self,x))) newobj.posted=False if isinstance(env,str): newobj.env=self.bld.all_envs[env].derive() else: newobj.env=env.derive() return newobj def declare_chain(name='',rule=None,reentrant=None,color='BLUE',ext_in=[],ext_out=[],before=[],after=[],decider=None,scan=None,install_path=None,shell=False): ext_in=Utils.to_list(ext_in) ext_out=Utils.to_list(ext_out) if not name: name=rule cls=Task.task_factory(name,rule,color=color,ext_in=ext_in,ext_out=ext_out,before=before,after=after,scan=scan,shell=shell) def x_file(self,node): if ext_in: _ext_in=ext_in[0] tsk=self.create_task(name,node) cnt=0 ext=decider(self,node)if decider else cls.ext_out for x in ext: k=node.change_ext(x,ext_in=_ext_in) tsk.outputs.append(k) if reentrant!=None: if cnt /dev/null;then # Give ntp 15 seconds to exit for i in `seq 1 15`; do if [ -n "`ps -p $PID|grep -v PID`" ]; then echo -n . sleep 1 else echo " OK!" rm $PIDFILE return 0 fi done fi echo " FAILED! ntpd is still running"; return 1 } ntpd_status() { if [ -r $PIDFILE ]; then echo "NTP daemon is running as `cat $PIDFILE`" else echo "NTP daemon is not running" fi } case "$1" in 'start') ntpd_start ;; 'stop') ntpd_stop ;; 'restart') ntpd_stop && ntpd_start ;; 'status') ntpd_status ;; *) echo "Usage: $0 (start|stop|restart|status)" esac ntpsec-1.1.0+dfsg1/etc/rc/rc.d/0000775000175000017500000000000013252650651015635 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/etc/rc/rc.d/ntpd0000644000175000017500000000107213252364117016522 0ustar rlaagerrlaager#!/bin/sh # PROVIDE: ntpd # REQUIRE: syslogd cleanvar devfs # BEFORE: SERVERS . /etc/rc.subr name="ntpd" rcvar="ntpd_enable" command="/usr/sbin/${name}" pidfile="/var/run/${name}.pid" start_precmd="ntpd_precmd" load_rc_config $name ntpd_precmd() { rc_flags="-c ${ntpd_config} ${ntpd_flags}" if checkyesno ntpd_sync_on_start; then rc_flags="-g $rc_flags" fi if [ -z "$ntpd_chrootdir" ]; then return 0; fi rc_flags="-u ntpd:ntpd -i ${ntpd_chrootdir} $rc_flags" } run_rc_command "$1" ntpsec-1.1.0+dfsg1/etc/rc/rc.d/ntpwait0000644000175000017500000000053013252364117017241 0ustar rlaagerrlaager#!/bin/sh # This script, when run, runs ntpwait if ntpd is enabled. # PROVIDE: ntpwait . /etc/rc.subr name="ntpwait" rcvar="ntpwait_enable" start_cmd="ntpwait_start" ntp_wait="/usr/sbin/ntpwait" load_rc_config "$name" ntpwait_start() { if checkyesno ntpd_enable; then $ntp_wait -v fi } run_rc_command "$1" ntpsec-1.1.0+dfsg1/etc/rc/rc.d/TIMESYNC0000644000175000017500000000040413252364117017006 0ustar rlaagerrlaager#!/bin/sh # PROVIDE: TIMESYNC # REQUIRE: LOGIN ntpwait # This depedency ensures that all services which require stable system clock # are run after ntpd is synchronized. It's run as late as possible, if you need # stable clock before login use BEFORE: LOGIN ntpsec-1.1.0+dfsg1/etc/rc/ntpwait0000644000175000017500000000026313252364117016416 0ustar rlaagerrlaager#!/bin/sh NTPWAIT=/usr/sbin/ntpwait ntpwait_start() { $NTPWAIT -v } case "$1" in 'start') ntpwait_start ;; *) echo "Usage: $0 (start)" esac ntpsec-1.1.0+dfsg1/etc/rc/README0000644000175000017500000000123213252364117015662 0ustar rlaagerrlaagerThis directory contains some example rc scripts for ntpd. In general, ntpd should be started as soon as possible in the boot process. If any services require stable system clock, the ntpwait script should be run before them as late as possible. The rc.d contains scripts for systems using rc.d init system (originated in NetBSD). If a service requires stable system time, indicate it with TIMESYNC dependency and set ntpwait_enable to YES. For SysV init systems, you'll have to create links as /etc/rc2.d/S20ntpd and /etc/rc2.d/S80ntpwait yourself. (The numbers are just examples, try to give ntpd as much time as possible to synchronize before running ntpwait). ntpsec-1.1.0+dfsg1/etc/ntplogtemp.service0000644000175000017500000000026013252364117020151 0ustar rlaagerrlaager[Unit] Documentation=man:ntplogtemp(1) Description=Temperature information logger for ntpviz [Service] Type=simple ExecStart=/usr/bin/ntplogtemp -o -l /var/log/ntpstats/temps ntpsec-1.1.0+dfsg1/etc/ntpviz-daily.timer0000644000175000017500000000022013252364117020066 0ustar rlaagerrlaager[Unit] Description=Run ntpviz for daily data graphing once an hour [Timer] OnCalendar=*:53:00 Persistent=true [Install] WantedBy=ntpd.service ntpsec-1.1.0+dfsg1/etc/ntploggps.service0000644000175000017500000000031713252364117020000 0ustar rlaagerrlaager[Unit] Documentation=man:ntploggps(1) Description=GPS information logger for ntpviz Requisite=gpsd.service After=gpsd.service [Service] Type=simple ExecStart=/usr/bin/ntploggps -o -l /var/log/ntpstats/gpsd ntpsec-1.1.0+dfsg1/etc/ntploggps.timer0000644000175000017500000000025113252364117017455 0ustar rlaagerrlaager[Unit] Description=Run ntploggps every five minutes Requisite=gpsd.service [Timer] OnBootSec=5min OnUnitActiveSec=5min Persistent=true [Install] WantedBy=gpsd.service ntpsec-1.1.0+dfsg1/etc/ntpd.service0000644000175000017500000000116413252364117016731 0ustar rlaagerrlaager[Unit] Description=Network Time Service Documentation=man:ntpd(8) Wants=network.target ConditionCapability=CAP_SYS_TIME After=network.target nss-lookup.target Conflicts=systemd-timesyncd.service [Service] Type=forking PrivateTmp=true ExecStart=/usr/sbin/ntpd -g -N -u ntp:ntp # Specifying -g on the command line allows ntpd to make large adjustments to # the clock on boot. However, if Restart=yes is set, a malicious (or broken) # server could send the incorrect time, trip the panic threshold, and when # ntpd restarts, serve it the incorrect time (which would be accepted). Restart=no [Install] WantedBy=multi-user.target ntpsec-1.1.0+dfsg1/etc/ntpviz-daily.service0000644000175000017500000000033113252364117020411 0ustar rlaagerrlaager[Unit] Documentation=man:ntpviz(1) Description=Graph daily information for NTP Requisite=ntpd.service [Service] Type=simple IOSchedulingClass=idle ExecStart=/usr/bin/ntpviz -w l -p 1 -o /var/www/localhost/htdocs/day ntpsec-1.1.0+dfsg1/etc/logrotate-config.ntpd0000644000175000017500000000043113252364117020530 0ustar rlaagerrlaager# On Linux systems with logrotate, # drop this into /etc/logrotate.d/ntpd # That will rotate ntpd.log monthly and then # kick ntpd to switch to the new log file. /var/log/ntp/ntpd.log { monthly postrotate /usr/bin/killall -HUP ntpd endscript rotate 9999 } ntpsec-1.1.0+dfsg1/etc/ntplogtemp.timer0000644000175000017500000000022313252364117017630 0ustar rlaagerrlaager[Unit] Description=Run ntplogtemp every five minutes [Timer] OnBootSec=5min OnUnitActiveSec=5min Persistent=true [Install] WantedBy=ntpd.service ntpsec-1.1.0+dfsg1/etc/README0000644000175000017500000000165413252364117015266 0ustar rlaagerrlaager= README file for directory etc = This directory contains example run-time configuration files for the NTP daemon, and some example startup scripts for launching it. Under ntp.d, the "use-" files are modular pieces of ntp.conf files that you can string together in any order for effect. The file titles are clues to what they're useful for. Each has an explanatory header comment. The file "default.conf" shows what a file made out of use snippets is like. This directory can be copied to /etc to give a sane default configuration. The rc files are start/stop scripts for NTP under System-V-style init. They are intended as models for distribution packagers. The ntpd.service file is intended for use with systemd. Note that it assumes the waf default installation location /usr/sbin for ntpd; you may need to modify this for some distrbutions. Additional information can be found in the ./docs subdirectory of the base directory. ntpsec-1.1.0+dfsg1/etc/ntpviz-weekly.timer0000644000175000017500000000022513252364117020271 0ustar rlaagerrlaager[Unit] Description=Run ntpviz for daily weekly graphing twice a day [Timer] OnCalendar=11,23:45:00 Persistent=true [Install] WantedBy=ntpd.service ntpsec-1.1.0+dfsg1/etc/ntp.d/0000775000175000017500000000000013252650651015426 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/etc/ntp.d/use-no-remote-configuration0000644000175000017500000000064013252364117022712 0ustar rlaagerrlaager# Exchange time with everybody, but don't allow configuration. # This is the right security setup for 99% of deployments. restrict default kod limited nomodify nopeer noquery restrict -6 default kod limited nomodify nopeer noquery # Local users may interrogate the NTP server more closely. restrict 127.0.0.1 restrict -6 ::1 # The following sets edit modes for GNU EMACS # Local Variables: # mode:conf # End: ntpsec-1.1.0+dfsg1/etc/ntp.d/default.conf0000644000175000017500000000061613252364117017721 0ustar rlaagerrlaager# This is a complete, usable ntp.conf file assembled from use snippets. # To configure as a Stratum 1 using GPSD, uncomment the first line. #includefile use-gpsd-shm # GPSD via SHM as a local clock source includefile use-pool # Get servers from the NTP pool includefile use-no-remote-configuration # Normal security includefile use-minimal-logging # Declare a drift file and that's it # end ntpsec-1.1.0+dfsg1/etc/ntp.d/use-performance-logging0000644000175000017500000000072113252364117022065 0ustar rlaagerrlaager# Collect NTP performance statistics for analysis driftfile /var/lib/ntp/ntp.drift statsdir /var/log/ntpstats/ statistics loopstats peerstats clockstats filegen loopstats file loopstats type day enable filegen peerstats file peerstats type day enable filegen clockstats file clockstats type day enable logfile /var/log/ntpd.log logconfig =syncall +clockall +peerall +sysall # The following sets edit modes for GNU EMACS # Local Variables: # mode:conf # End: ntpsec-1.1.0+dfsg1/etc/ntp.d/use-minimal-logging0000644000175000017500000000026213252364117021212 0ustar rlaagerrlaager# Minimal logging - we declare a drift file and that's it. driftfile /var/lib/ntp/ntp.drift # The following sets edit modes for GNU EMACS # Local Variables: # mode:conf # End: ntpsec-1.1.0+dfsg1/etc/ntp.d/use-gpsd-json0000644000175000017500000000057313252364117020051 0ustar rlaagerrlaager# Use gpsd with the JSON refclock. # # Not recommended for production use: the JSON driver is buggy and # needs fixing. This snippet is intended to give us a reproducible # configuration for testing. refclock gpsd unit 128 minpoll 4 maxpoll 4 refclock gpsd unit 0 minpoll 4 maxpoll 4 flag1 1 # The following sets edit modes for GNU EMACS # Local Variables: # mode:conf # End: ntpsec-1.1.0+dfsg1/etc/ntp.d/use-gpsd-shm0000644000175000017500000000113513252364117017662 0ustar rlaagerrlaager# Simplest possible refclock configuration for sites with a GPS primary source. # # Uses the shared-memory driver, accepting fixes from a running gpsd # instance watching one PPS-capable GPS. Accepts in-band GPS time (not # very good, likely to have jitter in the 100s of milliseconds) on one # unit, and PPS time (almost certainly good to 1 ms or less) on # another. Prefers the latter. # GPS Serial data reference (NTP0) refclock shm unit 0 refid GPS # GPS PPS reference (NTP1) refclock shm unit 1 prefer refid PPS # The following sets edit modes for GNU EMACS # Local Variables: # mode:conf # End: ntpsec-1.1.0+dfsg1/etc/ntp.d/use-pool0000644000175000017500000000233613252364117017115 0ustar rlaagerrlaager# If you have no other local chimers to help NTP perform sanity checks # then you can use some public chimers from the NTP public pool: # http://www.pool.ntp.org/en/ # # iburst tells it to send the first few requests at 2 second intervals rather # than wait for the poll interval, which defaults to 64 seconds. That greatly # speeds up the time for ntpd to set the system time and start responding to # requests. # # You can speed up initialization, and spread the load better, by # using a country-specific potion of the pool, e.g. something like # # us.pool.ntp.org # # If you are not in the USA, then it will probably work to # change the 'us' to your two letter country code. # # Major Internet-using countries with pools include: # us gb de fr ru au at ca cn jp de fi it be br cz hk # # If you don't know your country code, find it at # # https://en.wikipedia.org/wiki/ISO_3166-1 # # and then try prepending it to ".pool.ntp.org" and pinging that. # hostname. If you get a response, you can use it. # # Alternatively, if you are running Linux your distribution may have # a designated pool disparcher, e.g. ubuntu.pool.ntp.org # pool pool.ntp.org iburst # The following sets edit modes for GNU EMACS # Local Variables: # mode:conf # End: ntpsec-1.1.0+dfsg1/README0000644000175000017500000000612113252364117014505 0ustar rlaagerrlaager= The NTPsec distribution base directory = This directory and its subdirectories contain NTPSec, a security-hardened implementation of Network Time Protocol Version 4. You can browse a summary of differences from legacy NTP here: https://docs.ntpsec.org/latest/ntpsec.html The contents of the base directory are given in this file. The contents of subdirectories are usually given in the README files in each subdirectory. The base directory ./ contains the configuration files, source directories and related stuff: INSTALL:: Generic installation instructions. NEWS:: What's new in this release. README:: This file. VERSION:: The version stamp, to be used by scripts and programs. attic/:: Directory containing source code that is *not* part of a normal installation. Things can disappear from here at any time. buildprep:: Executable script for fetching installation prerequisites. contrib/:: Directory containing contributed scripts, dragons dwell here. Some of this might eventually move to being supported code. devel/:: Documentation and small tools aimed at developers. Includes a hacking guide and a tour of the internals. docs/:: Directory containing a complete set of documentation on building and configuring a NTP server or client. The files are in asciidoc markup. This replaces the 'html' directory of previous versions, but html can be generated from it. etc/:: Directory containing a motley collection of configuration files and launch scripts for various systems. For example only. include/:: Directory containing include header files used by most programs in the distribution. libjsmn/:: A minimal JSON library used by the GPSD-JSON driver. libntp/:: Directory containing library source code used by most programs in the distribution. libparse/:: This directory contains the files making up the parser for the parse refclock driver. For reasonably sane clocks this refclock drivers allows a refclock implementation by just providing a conversion routine and the appropriate NTP parameters ntpclients/:: Directory containing sources for clients - utility programs to query local and remote NTP installations for log status, state variables, and other timekeeping information. The term "clients" is used quite loosely here; any tool that is not a multi-file C program probably lives in this directory. ntpd/:: Sources for the main time-synchronization daemon. ntpfrob/:: The ntpfrob utility collects several small diagnostic functions for reading and tweaking the local clock hardware, including reading the clock tick rate, precision, and jitter. ntptime/:: Directory containing a utility for reading and modifying kernel parameters related to the local clock. packaging/:: Parts and guidance for distribution packagers. pylib/:: Installable Python helper modules for scripts. tests/:: Self-test code. waf:: A copy of the waf builder. This is the engine used to configure and build the codebase. wafhelpers/:: A library of Python procedures used by the waf build system. wscript:: NTP-specific waf rules. www/:: Sample ntpviz files // end ntpsec-1.1.0+dfsg1/attic/0000775000175000017500000000000013252650651014734 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/attic/ntpver0000755000175000017500000000034313252364117016175 0ustar rlaagerrlaager#!/bin/sh # print version string of NTP daemon # Copyright (c) 1997 by Ulrich Windl # Modified 970318: Harlan Stenn: rewritten... # usage: ntpver hostname ntpq -c "rv 0 daemon_version" $* | awk '/daemon_version/ { print $2 }' ntpsec-1.1.0+dfsg1/attic/ntpdate0000755000175000017500000000510213252364117016314 0ustar rlaagerrlaager#!/bin/sh # # ntpdate - emulate the crufty old ntpdate utility from NTP Classic # # Not documented, as this is strictly a backward-compatibility shim. It's # based on the recipes at # http://support.ntp.org/bin/view/Dev/DeprecatingNtpdate # with corrections for modern ntpdig options. # # Debug this by giving the -n option, which causes it to echo the # generated ntpdig command rather than executing it. # # Known bugs: # * The -e and -p options of ntpdate are not yet implemented. # * ntpdate took 4 samples and chose the best (shortest trip time). # This takes the first. # # ntpdate ntpdig ntpd What it does # -4 -4 -q -4 Resolve DNS lookups to A records # -6 -6 -q -6 Resolve DNS lookups to AAAA records # -a N -a N -q Authentication # -b -S -q step time adjustments # -B -s -q slew time adjustments # -d -d -d debugging mode (implies -q) # -e N.N -q authdelay # -k file -k file -k file key file # -o N -o N -q NTP Protocol version # -p N -q How many samples to take # -q default -q query/report only, don't set clock # (implies -u for ntpdate) # -s -p log to syslog (always enabled in ntpd) # -t N.N -t N.N request timeout # -u default unpriv port # -v verbose (ntpd is always more verbose than ntpdate) # -c name Send concurrent requests to resolved IPs for name # # -l file Log to file # -M msec Slew adjustments less than msec, # step adjustments larger than msec. # # SPDX-License-Identifier: BSD-2-Clause PASSTHROUGH="" TIMEOUT="-t 1" setclock=yes echo="" while getopts 46a:bBe:k:no:p:qst:uv opt do case $opt in 4) PASSTHROUGH="$PASSTHROUGH -4";; 6) PASSTHROUGH="$PASSTHROUGH -6";; a) PASSTHROUGH="$PASSTHROUGH -a $OPTARG";; b) ADJUST="$ADJUST -S";; B) ADJUST="$ADJUST -s";; d) PASSTHROUGH="$PASSTHROUGH -d";; e) echo "ntpdate: -e is no longer supported." >&2;; k) PASSTHROUGH="$PASSTHROUGH -k $OPTARG";; n) echo=echo ;; # Echo generated command, don't execute o) PASSTHROUGH="$PASSTHROUGH -o $OPTARG";; p) echo "ntpdate: -p is no longer supported." >&2;; q) setclock=no;; s) PASSTHROUGH="$PASSTHROUGH -p";; t) PASSTHROUGH="$PASSTHROUGH -t $OPTARG"; TIMEOUT="";; u) ;; v) ;; esac done shift $(($OPTIND - 1)) if [ "$setclock" = yes -a -z "$ADJUST" ] then ADJUST="-s -j" fi $echo ntpdig $ADJUST $TIMEOUT $PASSTHROUGH $* #end ntpsec-1.1.0+dfsg1/attic/README0000644000175000017500000000215713252364117015616 0ustar rlaagerrlaager= README file for directory attic/ = This is a dusty attic containing code we have not quite decided to discard. Programs in it are not installed by default. Not much documentation, alas. Read the header comments. calc_tickadj:: Calculates "optimal" value for tick given ntp.drift file Tested: 20160226 digest-find.c:: Hack to see if various digests are supported by OpenSSL digest-timing.c:: Hack to measure execution times for various digests and key lengths kern.c:: Header comment from deep in the mists of past time says: "This program simulates a first-order, type-II phase-lock loop using actual code segments from modified kernel distributions for SunOS, Ultrix and OSF/1 kernels. These segments do not use any licensed code." Anybody who has a clue *why* it is doing this and what it's supposed to be used for should explain it to us, please. ntpdate:: Wrapper script to maintain compatibility. Maps options to ntpdig and calls it. Tested: 20160226 ntpver:: Simple script using ntpq to print out the suite version. Tested: 20160226 sht.c:: Test program for shared memory refclock. // end ntpsec-1.1.0+dfsg1/attic/calc_tickadj/0000775000175000017500000000000013252650651017327 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/attic/calc_tickadj/calc_tickadj0000755000175000017500000000470713252364117021655 0ustar rlaagerrlaager#!/usr/bin/env python # # drift of 104.8576 -> +1 tick. Base of 10000 ticks. # # 970306 HMS Deal with nanoseconds. Fix sign of adjustments. # # Translated from a very old Perl script - the comment above is a clue # to how old. # ''' calc_tickadj - Calculates "optimal" value for tick given ntp drift file. USAGE: calc_tickadj [-t tick] [-d drift-file] -d, --drift-file=str Ntp drift file to use -t, --tick=num Tick value of this host -h, --help Display usage message and exit Options are specified by doubled hyphens and their name or by a single hyphen and the flag character. ''' # SPDX-License-Identifier: BSD-2-Clause from __future__ import print_function, division import getopt import os import re import sys if __name__ == '__main__': try: (options, arguments) = getopt.getopt( sys.argv[1:], "d:t:h", ["drift-file=", "tick=", "--help"]) except getopt.GetoptError as err: sys.stderr.write(str(err) + "\n") raise SystemExit(1) tick = 0 drift_file = "/etc/ntp/drift" for (switch, val) in options: if switch == "-d" or switch == "--drift-file": drift_file = val elif switch == "-t" or switch == "--tick": tick = int(val) elif switch == "-h" or switch == "--help": print(__doc__) sys.exit(0) if tick == 0: try: with os.popen("ntpfrob -A") as rp: response = rp.read() m = re.search("[0-9]+", response) if m: tick = int(m.group(0)) except: pass if tick == 0: sys.stderr.write("Could not get tick value, try manually with -t/--tick\n\n") sys.exit(1) # Drift file is in PPM where Million is actually 2**20 cvt = (2 ** 20) / tick drift = 0.0 try: with open(drift_file) as dp: drift = dp.read() except OSError: sys.stderr.write("Could not open drift file: %s\n" % drift_file) sys.exit(1) m = re.match("[+-]?\d+\.?[0-9]+", drift) if not m: sys.stderr.write("Invalid drift file value '%s'\n" % drift) sys.exit(1) else: drift = float(drift) while drift < 0: drift += cvt tick -= 1 while drift > cvt: drift -= cvt tick += 1 print("%.3f (drift)" % drift) print("%d usec; %d nsec" % (tick, (tick + (drift/cvt)) * 1000)) sys.exit(0) # end ntpsec-1.1.0+dfsg1/attic/calc_tickadj/calc_tickadj.txt0000644000175000017500000000346613252364117022471 0ustar rlaagerrlaager= calc_tickadj(8) = :doctype: manpage == NAME == calc_tickadj - Calculates optimal value for tick given NTP drift file. == SYNOPSIS == calc_tickadj [-d 'string' | --drift-file='string'] [-t 'number' | --tick='number'] == DESCRIPTION == The _calc_tickadj_ script uses provided NTP drift file to generate optimal tick value. Generally, +ntpd+ can do better job if the drift value is the smallest possible number. The example output of ----------------------- $ ./calc_tickadj 81.699 (drift) 9999 usec; 9999779 nsec $ cat /etc/ntp/drift -23.159 ----------------------- means the following. If tick on that box is 10,000, by making the value 9999 we'll shift the box from its current drift of -23.159 to a drift of 81.699, and in doing so we'll speed the clock up a little every second instead of slowing the clock down a little. If 'tick' on that box is 10,000,000 then by setting it to 9999779 the drift value will be somewhere around 0.0. _calc_tickadj_ tries to determine the tick value by using the +ntpfrob+ program from the +NTPSec+ package. If this doesn't work you can specify current tick manually on command line. == OPTIONS == -d string, --drift-file=string:: Ntp drift file to use. The default string for this option is: '/etc/ntp/drift'. + Use the specified drift file for calculations -t number, --tick=number:: Tick value of this host. This option takes an integer number as its argument. The default value is obtained by calling ntpfrob(1). + The current tick which to adjustment will be calculated == EXIT STATUS == One of the following exit values will be returned: 0 (EXIT_SUCCESS):: Successful program execution. 1 (EXIT_FAILURE):: The operation failed or the command syntax was not valid. == STATUS == calc_tickadj has been updated to work in the NTPSec distribution, but is not actively maintained. ntpsec-1.1.0+dfsg1/attic/digest-find.c0000644000175000017500000000407713252364117017302 0ustar rlaagerrlaager/* * digest.c - Hack to test various digest types. * * Fedora mentions blake2b and friends. They are in the man page * and header files, but not available via OBJ_sn2n so I assume they * are not interesting and dropped them to reduce clutter. * * If the type column is blank, the OpenSSL package doesn't know * about that digest, maybe because it isn't spelled correctly. * * If the type column is non-blank but the length column is empty, * the library recognizes the type but doesn't support it. * * If the length column is filled in, that's the length of the digest. * */ #include #include #include #define UNUSED_ARG(arg) ((void)(arg)) const char* digests[] = { "MD2", "MD4", "MD5", "SHA", "SHA1", "RMD160", "RIPEMD160", "SHA224", "SHA256", "SHA384", "SHA512", "MDC2", "GOST", "DSS1", NULL }; unsigned char pkt[100]; int main ( int argc, char *argv[] ) { UNUSED_ARG(argc); UNUSED_ARG(argv); unsigned int versionNumber = OPENSSL_VERSION_NUMBER; const char *versionText = OPENSSL_VERSION_TEXT; printf("OpenSSL xVersion is %x, %s\n", versionNumber, versionText); /* needed if OPENSSL_VERSION_NUMBER < 0x10100000L */ OpenSSL_add_all_digests(); printf(" name type length\n"); for (int i = 0; NULL != digests[i]; i++) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int length = 0; EVP_MD_CTX *ctx; int keytype; const EVP_MD *md; keytype = OBJ_sn2nid(digests[i]); if (NID_undef == keytype) { printf("%10s\n", digests[i]); continue; } md = EVP_get_digestbynid(keytype); if (NULL == md) { printf("%10s %4d\n", digests[i], keytype); continue; } ctx = EVP_MD_CTX_create(); /* libntp/macencrypt.c has an ifdef for this */ EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); EVP_DigestInit_ex(ctx, md, NULL); EVP_DigestUpdate(ctx, pkt, sizeof(pkt)); EVP_DigestFinal_ex(ctx, digest, &length); EVP_MD_CTX_destroy(ctx); printf("%10s %4d %6u\n", digests[i], keytype, length); } return 0; } ntpsec-1.1.0+dfsg1/attic/kern.c0000644000175000017500000001275113252364117016042 0ustar rlaagerrlaager/* * This program simulates a first-order, type-II phase-lock loop using * actual code segments from modified kernel distributions for SunOS, * Ultrix and OSF/1 kernels. These segments do not use any licensed code. */ #include "config.h" #include #include #include #include #ifdef HAVE_SYS_TIMEX_H # include /* prerequisite on NetBSD */ # include #endif /* * Phase-lock loop definitions */ #define HZ 100 /* timer interrupt frequency (Hz) */ #define MAXPHASE 512000 /* max phase error (us) */ #define MAXFREQ 200 /* max frequency error (ppm) */ #define TAU 2 /* time constant (shift 0 - 6) */ #define POLL 16 /* interval between updates (s) */ #define MAXSEC 1200 /* max interval between updates (s) */ /* * Function declarations */ void hardupdate(); void hardclock(); void second_overflow(); /* * Kernel variables */ int tick; /* timer interrupt period (us) */ int fixtick; /* amortization constant (ppm) */ struct timeval timex; /* ripoff of kernel time variable */ /* * Phase-lock loop variables */ int time_status = TIME_BAD; /* clock synchronization status */ long time_offset = 0; /* time adjustment (us) */ long time_constant = 0; /* pll time constant */ long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ long time_precision = 1000000 / HZ; /* clock precision (us) */ long time_maxerror = MAXPHASE; /* maximum error (us) */ long time_esterror = MAXPHASE; /* estimated error (us) */ long time_phase = 0; /* phase offset (scaled us) */ long time_freq = 0; /* frequency offset (scaled ppm) */ long time_adj = 0; /* tick adjust (scaled 1 / HZ) */ long time_reftime = 0; /* time at last adjustment (s) */ /* * Simulation variables */ double timey = 0; /* simulation time (us) */ long timez = 0; /* current error (us) */ long poll_interval = 0; /* poll counter */ /* * Simulation test program */ int main( int argc, char *argv[] ) { tick = 1000000 / HZ; fixtick = 1000000 % HZ; timex.tv_sec = 0; timex.tv_usec = MAXPHASE; time_freq = 0; time_constant = TAU; printf("tick %d us, fixtick %d us\n", tick, fixtick); printf(" time offset freq _offset _freq _adj\n"); /* * Grind the loop until ^C */ while (1) { timey += (double)(1000000) / HZ; if (timey >= 1000000) timey -= 1000000; hardclock(); if (timex.tv_usec >= 1000000) { timex.tv_usec -= 1000000; timex.tv_sec++; second_overflow(); poll_interval++; if (!(poll_interval % POLL)) { timez = (long)timey - timex.tv_usec; if (timez > 500000) timez -= 1000000; if (timez < -500000) timez += 1000000; hardupdate(timez); printf("%10li%10li%10.2f %08lx %08lx %08lx\n", timex.tv_sec, timez, (double)time_freq / (1 << SHIFT_KF), time_offset, time_freq, time_adj); } } } } /* * This routine simulates the ntp_adjtime() call * * For default SHIFT_UPDATE = 12, offset is limited to +-512 ms, the * maximum interval between updates is 4096 s and the maximum frequency * offset is +-31.25 ms/s. */ void hardupdate( long offset ) { long ltemp, mtemp; time_offset = offset << SHIFT_UPDATE; mtemp = timex.tv_sec - time_reftime; time_reftime = timex.tv_sec; if (mtemp > MAXSEC) mtemp = 0; /* ugly multiply should be replaced */ if (offset < 0) time_freq -= (-offset * mtemp) >> (time_constant + time_constant); else time_freq += (offset * mtemp) >> (time_constant + time_constant); ltemp = time_tolerance << SHIFT_KF; if (time_freq > ltemp) time_freq = ltemp; else if (time_freq < -ltemp) time_freq = -ltemp; if (time_status == TIME_BAD) time_status = TIME_OK; } /* * This routine simulates the timer interrupt */ void hardclock(void) { int ltemp, time_update; time_update = tick; /* computed by adjtime() */ time_phase += time_adj; if (time_phase < -FINEUSEC) { ltemp = -time_phase >> SHIFT_SCALE; time_phase += ltemp << SHIFT_SCALE; time_update -= ltemp; } else if (time_phase > FINEUSEC) { ltemp = time_phase >> SHIFT_SCALE; time_phase -= ltemp << SHIFT_SCALE; time_update += ltemp; } timex.tv_usec += time_update; } /* * This routine simulates the overflow of the microsecond field * * With SHIFT_SCALE = 23, the maximum frequency adjustment is +-256 us * per tick, or 25.6 ms/s at a clock frequency of 100 Hz. The time * contribution is shifted right a minimum of two bits, while the frequency * contribution is a right shift. Thus, overflow is prevented if the * frequency contribution is limited to half the maximum or 15.625 ms/s. */ void second_overflow(void) { int ltemp; time_maxerror += time_tolerance; if (time_offset < 0) { ltemp = -time_offset >> (SHIFT_KG + time_constant); time_offset += ltemp; time_adj = -(ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE)); } else { ltemp = time_offset >> (SHIFT_KG + time_constant); time_offset -= ltemp; time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); } if (time_freq < 0) time_adj -= -time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE); else time_adj += time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE); time_adj += fixtick << (SHIFT_SCALE - SHIFT_HZ); /* ugly divide should be replaced */ if (timex.tv_sec % 86400 == 0) { switch (time_status) { case TIME_INS: timex.tv_sec--; /* !! */ time_status = TIME_OOP; break; case TIME_DEL: timex.tv_sec++; time_status = TIME_OK; break; case TIME_OOP: time_status = TIME_OK; break; } } } ntpsec-1.1.0+dfsg1/attic/sht.c0000644000175000017500000001134513252364117015677 0ustar rlaagerrlaager/* * sht.c - Testprogram for shared memory refclock * read/write shared memory segment; see usage */ #include "config.h" #include #include #include #include #include #include #include #include #include "ntp.h" #include "ntp_stdlib.h" char *progname; struct shmTime { int mode; /* 0 - if valid set * use values, * clear valid * 1 - if valid set * if count before and after read of values is equal, * use values * clear valid */ volatile int count; time_t clockTimeStampSec; int clockTimeStampUSec; time_t receiveTimeStampSec; int receiveTimeStampUSec; int leap; int precision; int nsamples; volatile int valid; unsigned clockTimeStampNSec; /* Unsigned ns timestamps */ unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */ }; static struct shmTime * getShmTime ( int unit ) { int shmid=shmget (0x4e545030+unit, sizeof (struct shmTime), IPC_CREAT|0777); if (shmid==-1) { perror ("shmget"); exit (1); } else { struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0); if ((int)(long)p==-1) { perror ("shmat"); p=0; } assert (p!=0); return p; } } int main ( int argc, char *argv[] ) { volatile struct shmTime *p; int unit; char *argp; progname = argv[0]; if (argc<=1) { usage: printf ("usage: %s [uu:]{r[c][l]|w|snnn}\n",argv[0]); printf (" uu use clock unit uu (default: 2)\n"); printf (" r read shared memory\n"); printf (" c clear valid-flag\n"); printf (" l loop (so, rcl will read and clear in a loop\n"); printf (" w write shared memory with current time\n"); printf (" snnnn set nsamples to nnn\n"); printf (" lnnnn set leap to nnn\n"); printf (" pnnnn set precision to -nnn\n"); exit (0); } unit = (int)strtoul(argv[1], &argp, 10); if (argp == argv[1]) unit = 2; else if (*argp == ':') argp++; else goto usage; p=getShmTime(unit); switch (*argp) { case 's': p->nsamples=atoi(argp+1); break; case 'l': p->leap=atoi(argp+1); break; case 'p': p->precision=-atoi(argp+1); break; case 'r': { int clear=0; int loop=0; printf ("reader\n"); while (*++argp) { switch (*argp) { case 'l': loop=1; break; case 'c': clear=1; break; default : goto usage; } } again: printf ("mode=%d, count=%d, clock=%ld.%09u, rec=%ld.%09u,\n", p->mode,p->count, (long)p->clockTimeStampSec,p->clockTimeStampNSec, (long)p->receiveTimeStampSec,p->receiveTimeStampNSec); printf (" leap=%d, precision=%d, nsamples=%d, valid=%d\n", p->leap, p->precision, p->nsamples, p->valid); if (!p->valid) printf ("***\n"); if (clear) { p->valid=0; printf ("cleared\n"); } if (loop) { sleep (1); goto again; } break; } case 'w': { /* To show some life action, we read the system * clock and use a bit of fuzz from 'ntp_random()' to get a * bit of wobbling into the values (so we can observe a * certain jitter!) */ time_t clk_sec, rcv_sec; unsigned int clk_frc, rcv_frc; /* Here we have a high-resolution system clock, and * we're not afraid to use it! */ struct timespec tmptime; if (0 == clock_gettime(CLOCK_REALTIME, &tmptime)) { rcv_sec = tmptime.tv_sec; rcv_frc = (unsigned int)tmptime.tv_nsec; } else { time(&rcv_sec); rcv_frc = (unsigned int)ntp_random() % 1000000000u; } /* add a wobble of ~3.5msec to the clock time */ clk_sec = rcv_sec; clk_frc = rcv_frc + (unsigned int)(ntp_random()%7094713 - 3547356); /* normalise result -- the SHM driver is picky! */ while ((int)clk_frc < 0) { clk_frc += 1000000000; clk_sec -= 1; } while ((int)clk_frc >= 1000000000) { clk_frc -= 1000000000; clk_sec += 1; } /* Most 'real' time sources would create a clock * (reference) time stamp where the fraction is zero, * but that's not an actual requirement. So we show how * to deal with the time stamps in general; changing the * behaviour for cases where the fraction of the * clock time is zero should be trivial. */ printf ("writer\n"); p->mode=0; if (!p->valid) { p->clockTimeStampSec = clk_sec; p->clockTimeStampUSec = (int)(clk_frc / 1000); /* truncate! */ p->clockTimeStampNSec = clk_frc; p->receiveTimeStampSec = rcv_sec; p->receiveTimeStampUSec = (int)(rcv_frc / 1000); /* truncate! */ p->receiveTimeStampNSec = rcv_frc; printf ("%ld.%09u %ld.%09u\n", (long)p->clockTimeStampSec , p->clockTimeStampNSec , (long)p->receiveTimeStampSec, p->receiveTimeStampNSec); p->valid=1; } else { printf ("p->valid still set\n"); /* not an error! */ } break; } default: break; } return 0; } ntpsec-1.1.0+dfsg1/attic/digest-timing.c0000644000175000017500000001724613252364117017653 0ustar rlaagerrlaager/* Last modified on Sat Aug 28 14:30:11 PDT 1999 by murray */ /* Hack to time the digest calculations for various algorithms. * * This is just the digest timing. * It doesn't include the copy or compare or finding the right key. * * Beware of overflows in the timing computations. * * Disable AES-NI (Intel hardware: NI == New Instruction) with: * OPENSSL_ia32cap="~0x200000200000000" * Check /proc/cpuinfo flags for "aes" to see if you have it. */ /* This may not be high enough. * 0x10000003 1.0.0b fails * 0x1000105fL 1.0.1e works. */ #define CMAC_VERSION_CUTOFF 0x10000003 #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER > CMAC_VERSION_CUTOFF #include #endif #include #include #include #include #define UNUSED_ARG(arg) ((void)(arg)) #ifndef EVP_MD_CTX_reset /* Slightly older version of OpenSSL */ /* Similar hack in ssl_init.c */ #define EVP_MD_CTX_new() EVP_MD_CTX_create() #define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) #define EVP_MD_CTX_reset(ctx) EVP_MD_CTX_init(ctx) #endif /* Get timing for old slower way too. Pre Feb 2018 */ #define DoSLOW 1 int NUM = 1000000; #define PACKET_LENGTH 48 /* Nothing magic about these key lengths. * ntpkeygen just happens to label things this way. */ #define AES_KEY_LENGTH 16 #define MD5_KEY_LENGTH 16 #define SHA1_KEY_LENGTH 20 #define MAX_KEY_LENGTH 64 EVP_MD_CTX *ctx; #if OPENSSL_VERSION_NUMBER > CMAC_VERSION_CUTOFF CMAC_CTX *cmac; #endif static void ssl_init(void) { ERR_load_crypto_strings(); OpenSSL_add_all_digests(); ctx = EVP_MD_CTX_new(); #if OPENSSL_VERSION_NUMBER > CMAC_VERSION_CUTOFF cmac = CMAC_CTX_new(); #endif } static unsigned int SSL_Digest( const EVP_MD *digest, /* hash algorithm */ uint8_t *key, /* key pointer */ int keylength, /* key size */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { unsigned char answer[EVP_MAX_MD_SIZE]; unsigned int len; EVP_MD_CTX_reset(ctx); EVP_DigestInit(ctx, digest); EVP_DigestUpdate(ctx, key, keylength); EVP_DigestUpdate(ctx, pkt, pktlength); EVP_DigestFinal(ctx, answer, &len); return len; } static unsigned int SSL_DigestSlow( int type, /* hash algorithm */ uint8_t *key, /* key pointer */ int keylength, /* key size */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { EVP_MD_CTX *ctxx; unsigned char answer[EVP_MAX_MD_SIZE]; unsigned int len; ctxx = EVP_MD_CTX_new(); EVP_DigestInit(ctxx, EVP_get_digestbynid(type)); EVP_DigestUpdate(ctxx, key, keylength); EVP_DigestUpdate(ctxx, pkt, pktlength); EVP_DigestFinal(ctxx, answer, &len); EVP_MD_CTX_free(ctxx); return len; } #if OPENSSL_VERSION_NUMBER > CMAC_VERSION_CUTOFF static size_t SSL_CMAC( const EVP_CIPHER *cipher, /* cipher algorithm */ uint8_t *key, /* key pointer */ int keylength, /* key size */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { unsigned char answer[EVP_MAX_MD_SIZE]; size_t len; CMAC_resume(cmac); CMAC_Init(cmac, key, keylength, cipher, NULL); CMAC_Update(cmac, pkt, pktlength); CMAC_Final(cmac, answer, &len); return len; } #endif static void DoDigest( const char *name, /* type of digest */ uint8_t *key, /* key pointer */ int keylength, /* key size */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { int type = OBJ_sn2nid(name); const EVP_MD *digest = EVP_get_digestbynid(type); struct timespec start, stop; int i; double fast, slow; unsigned int digestlength = 0; if (NULL == digest) return; clock_gettime(CLOCK_MONOTONIC, &start); for (i = 0; i < NUM; i++) { digestlength = SSL_Digest(digest, key, keylength, pkt, pktlength); } clock_gettime(CLOCK_MONOTONIC, &stop); fast = (stop.tv_sec-start.tv_sec)*1E9 + (stop.tv_nsec-start.tv_nsec); printf("%10s %2d %2d %2u %6.0f %6.3f", name, keylength, pktlength, digestlength, fast/NUM, fast/1E9); #ifdef DoSLOW clock_gettime(CLOCK_MONOTONIC, &start); for (i = 0; i < NUM; i++) { digestlength = SSL_DigestSlow(type, key, keylength, pkt, pktlength); } clock_gettime(CLOCK_MONOTONIC, &stop); slow = (stop.tv_sec-start.tv_sec)*1E9 + (stop.tv_nsec-start.tv_nsec); printf(" %6.0f %2.0f %4.0f", slow/NUM, (slow-fast)*100.0/slow, (slow-fast)/NUM); #endif printf("\n"); } #if OPENSSL_VERSION_NUMBER > CMAC_VERSION_CUTOFF static void DoCMAC( const char *name, /* name of cipher */ const EVP_CIPHER *cipher, uint8_t *key, /* key pointer */ int keylength, /* key length */ uint8_t *pkt, /* packet pointer */ int pktlength /* packet length */ ) { struct timespec start, stop; int i; double fast; unsigned long digestlength = 0; if (NULL == cipher) return; clock_gettime(CLOCK_MONOTONIC, &start); for (i = 0; i < NUM; i++) { digestlength = SSL_CMAC(cipher, key, keylength, pkt, pktlength); } clock_gettime(CLOCK_MONOTONIC, &stop); fast = (stop.tv_sec-start.tv_sec)*1E9 + (stop.tv_nsec-start.tv_nsec); printf("%10s %2d %2d %2lu %6.0f %6.3f", name, keylength, pktlength, digestlength, fast/NUM, fast/1E9); printf("\n"); } #endif int main(int argc, char *argv[]) { uint8_t key[MAX_KEY_LENGTH]; uint8_t packet[PACKET_LENGTH]; UNUSED_ARG(argc); UNUSED_ARG(argv); ssl_init(); RAND_bytes((unsigned char *)&key, MAX_KEY_LENGTH); RAND_bytes((unsigned char *)&packet, PACKET_LENGTH); printf("# %s\n", OPENSSL_VERSION_TEXT); printf("# KL=key length, PL=packet length, DL=digest length\n"); printf("# Digest KL PL DL ns/op sec/run slow %% diff\n"); DoDigest("MD5", key, MD5_KEY_LENGTH, packet, PACKET_LENGTH); DoDigest("MD5", key, MD5_KEY_LENGTH-1, packet, PACKET_LENGTH); DoDigest("MD5", key, SHA1_KEY_LENGTH, packet, PACKET_LENGTH); DoDigest("SHA1", key, MD5_KEY_LENGTH, packet, PACKET_LENGTH); DoDigest("SHA1", key, SHA1_KEY_LENGTH, packet, PACKET_LENGTH); DoDigest("SHA1", key, SHA1_KEY_LENGTH-1, packet, PACKET_LENGTH); DoDigest("SHA224", key, 16, packet, PACKET_LENGTH); DoDigest("SHA224", key, 20, packet, PACKET_LENGTH); DoDigest("SHA256", key, 16, packet, PACKET_LENGTH); DoDigest("SHA256", key, 20, packet, PACKET_LENGTH); DoDigest("SHA384", key, 16, packet, PACKET_LENGTH); DoDigest("SHA384", key, 20, packet, PACKET_LENGTH); DoDigest("SHA512", key, 16, packet, PACKET_LENGTH); DoDigest("SHA512", key, 20, packet, PACKET_LENGTH); DoDigest("SHA512", key, 24, packet, PACKET_LENGTH); DoDigest("SHA512", key, 32, packet, PACKET_LENGTH); DoDigest("RIPEMD160", key, 16, packet, PACKET_LENGTH); DoDigest("RIPEMD160", key, 20, packet, PACKET_LENGTH); DoDigest("RIPEMD160", key, 32, packet, PACKET_LENGTH); #if OPENSSL_VERSION_NUMBER > CMAC_VERSION_CUTOFF printf("\n"); printf("# KL=key length, PL=packet length, CL=CMAC length\n"); printf("# CMAC KL PL CL ns/op sec/run\n"); DoCMAC("DES", EVP_des_cbc(), key, 8, packet, PACKET_LENGTH); DoCMAC("AES-128", EVP_aes_128_cbc(), key, 16, packet, PACKET_LENGTH); DoCMAC("AES-192", EVP_aes_192_cbc(), key, 24, packet, PACKET_LENGTH); DoCMAC("AES-256", EVP_aes_256_cbc(), key, 32, packet, PACKET_LENGTH); DoCMAC("CAM-128", EVP_camellia_128_cbc(), key, 16, packet, PACKET_LENGTH); DoCMAC("CAM-192", EVP_camellia_192_cbc(), key, 24, packet, PACKET_LENGTH); DoCMAC("CAM-256", EVP_camellia_256_cbc(), key, 32, packet, PACKET_LENGTH); #endif return 0; } ntpsec-1.1.0+dfsg1/attic/wscript0000644000175000017500000000062113252364117016346 0ustar rlaagerrlaagerdef build(ctx): bldnode = ctx.bldnode.abspath() util = ['sht', 'digest-find', 'digest-timing'] for name in util: ctx( target=name, features="c cprogram bld_include src_include", source=[name + ".c"], includes=["%s/%s/" % (bldnode, name)], use="ntp M SSL CRYPTO RT THR PTHREAD", install_path=None, ) ntpsec-1.1.0+dfsg1/tests/0000775000175000017500000000000013252650651014772 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/tests/time-startup.sh0000755000175000017500000000036513252364117017770 0ustar rlaagerrlaager#!/bin/sh # Hack to measure startup timing if test "$#" -ge 1 then CONF=$1 else CONF=/etc/ntp.conf fi killall ntpd sleep 5 time /usr/local/sbin/ntpd -u ntp:ntp -g -c $CONF /usr/local/bin/ntpwait -v -n 999 -s 1 /usr/local/bin/ntpq -np ntpsec-1.1.0+dfsg1/tests/common/0000775000175000017500000000000013252650651016262 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/tests/common/tests_main.c0000644000175000017500000000317713252364117020601 0ustar rlaagerrlaager #include "tests_main.h" const char *progname = "ntpsectest"; static const char** args_argv; static int args_argc; /* XXX: This needs to have named arguments and error out if one is not supplied */ const char* tests_main_args(int arg) { int a; int count = 0; for (a = 1; a < args_argc; ++a) { if (args_argv[a][0] != '-') { if (count == arg) { return args_argv[a]; } ++count; } } return NULL; } static void RunAllTests(void) { syslogit = false; termlogit = false; #ifdef TEST_NTPDIG RUN_TEST_GROUP(crypto); RUN_TEST_GROUP(keyFile); RUN_TEST_GROUP(kodDatabase); RUN_TEST_GROUP(kodFile); RUN_TEST_GROUP(packetHandling); RUN_TEST_GROUP(packetProcessing); RUN_TEST_GROUP(utilities); #endif #ifdef TEST_LIBNTP RUN_TEST_GROUP(authkeys); RUN_TEST_GROUP(calendar); RUN_TEST_GROUP(clocktime); RUN_TEST_GROUP(decodenetnum); RUN_TEST_GROUP(hextolfp); RUN_TEST_GROUP(lfpfunc); RUN_TEST_GROUP(lfptostr); RUN_TEST_GROUP(macencrypt); RUN_TEST_GROUP(msyslog); RUN_TEST_GROUP(netof6); RUN_TEST_GROUP(numtoa); RUN_TEST_GROUP(prettydate); RUN_TEST_GROUP(recvbuff); RUN_TEST_GROUP(refidsmear); RUN_TEST_GROUP(socktoa); RUN_TEST_GROUP(statestr); RUN_TEST_GROUP(strtolfp); RUN_TEST_GROUP(timespecops); RUN_TEST_GROUP(vi64ops); RUN_TEST_GROUP(ymd2yd); #endif #ifdef TEST_LIBPARSE RUN_TEST_GROUP(binio); RUN_TEST_GROUP(gpstolfp); RUN_TEST_GROUP(ieee754io); #endif #ifdef TEST_NTPD RUN_TEST_GROUP(leapsec); RUN_TEST_GROUP(hackrestrict); #endif } int main(int argc, const char * argv[]) { ssl_init(); init_auth(); init_network(); args_argc = argc; args_argv = argv; return UnityMain(argc, argv, RunAllTests); } ntpsec-1.1.0+dfsg1/tests/common/sockaddrtest.h0000644000175000017500000000051513252364117021123 0ustar rlaagerrlaager#ifndef GUARD_TESTS_SOCKADDRTEST_H #define GUARD_TESTS_SOCKADDRTEST_H #include "ntp.h" bool IsEqualS(const sockaddr_u *expected, const sockaddr_u *actual); bool IsDiffS(const sockaddr_u *expected, const sockaddr_u *actual); sockaddr_u CreateSockaddr4(const char* address, unsigned int port); #endif // GUARD_TESTS_SOCKADDRTEST_H ntpsec-1.1.0+dfsg1/tests/common/caltime.c0000644000175000017500000000123613252364117020043 0ustar rlaagerrlaager#include "config.h" #include #include "caltime.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" time_t nowtime = 0; time_t settime(int y, int m, int d, int H, int M, int S) { time_t days = ntpcal_edate_to_eradays(y-1, m-1, d-1) + (1 - DAY_UNIX_STARTS); time_t secs = ntpcal_etime_to_seconds(H, M, S); return days * SECSPERDAY + secs; } const char *CalendarToString(const struct calendar *cal) { char *str = malloc(255); snprintf(str, 255, "%hu-%u-%u (%u) %u:%u:%u\n", cal->year, (unsigned int)cal->month, (unsigned int)cal->monthday, cal->yearday, (unsigned int)cal->hour, (unsigned int)cal->minute, (unsigned int)cal->second); return str; } ntpsec-1.1.0+dfsg1/tests/common/sockaddrtest.c0000644000175000017500000000424313252364117021120 0ustar rlaagerrlaager#include #include "config.h" #include "ntp.h" #include "sockaddrtest.h" bool IsEqualS(const sockaddr_u *expected, const sockaddr_u *actual) { if (AF(expected) != AF(actual)) { printf("Expected sa_family: %" PRIuMAX " but got: %" PRIuMAX "\n", (uintmax_t)AF(expected), (uintmax_t)AF(actual)); return false; } if (AF(actual) == AF_INET) { // IPv4 if (NSRCPORT(expected) == NSRCPORT(actual) && memcmp(&SOCK_ADDR4(expected), &SOCK_ADDR4(actual), sizeof(in_addr_t)) == 0) { return true; } else { printf("IPv4 comparison failed, expected: %u (%s) but was: %u (%s)\n", SOCK_ADDR4(expected).s_addr, socktoa(expected), SOCK_ADDR4(actual).s_addr, socktoa(actual)); return false; } } else if (AF(actual) == AF_INET6) { //IPv6 if (expected->sa6.sin6_port == actual->sa6.sin6_port && memcmp(&SOCK_ADDR6(expected), &SOCK_ADDR6(actual), sizeof(struct in6_addr)) == 0) { return true; } else { printf("IPv6 comparison failed\n"); return false; } } else { // Unknown family printf("Unknown sa_family: %" PRIuMAX "\n", (uintmax_t)AF(actual)); return false; } } /* Similar to IsEqualS, but doesn't print misleading messages */ bool IsDiffS(const sockaddr_u *expected, const sockaddr_u *actual) { if (AF(expected) != AF(actual)) { return true; } if (AF(actual) == AF_INET) { // IPv4 if (NSRCPORT(expected) == NSRCPORT(actual) && memcmp(&SOCK_ADDR4(expected), &SOCK_ADDR4(actual), sizeof(in_addr_t)) == 0) { printf("IPv4 address matches: %u (%s)\n", SOCK_ADDR4(expected).s_addr, socktoa(expected)); return false; } else { return true; } } else if (AF(actual) == AF_INET6) { //IPv6 if (expected->sa6.sin6_port == actual->sa6.sin6_port && memcmp(&SOCK_ADDR6(expected), &SOCK_ADDR6(actual), sizeof(struct in6_addr)) == 0) { printf("IPv6 address matches\n"); return false; } else { return true; } } else { // Unknown family printf("Can't compare unknown address family\n"); return false; } } sockaddr_u CreateSockaddr4(const char* address, unsigned int port) { sockaddr_u s; SET_AF(&s, AF_INET); PSOCK_ADDR4(&s)->s_addr = inet_addr(address); SET_PORT(&s, port); return s; } ntpsec-1.1.0+dfsg1/tests/common/tests_main.h0000644000175000017500000000033013252364117020572 0ustar rlaagerrlaager#ifndef GUARD_TESTS_MAIN_H #define GUARD_TESTS_MAIN_H #include "config.h" #include "unity.h" #include "unity_fixture.h" #include "ntp_stdlib.h" const char* tests_main_args(int arg); #endif // GUARD_TESTS_MAIN_H ntpsec-1.1.0+dfsg1/tests/common/caltime.h0000644000175000017500000000024213252364117020044 0ustar rlaagerrlaager#include #include "ntp_calendar.h" time_t settime(int y, int m, int d, int H, int M, int S); const char *CalendarToString(const struct calendar *cal); ntpsec-1.1.0+dfsg1/tests/pylib/0000775000175000017500000000000013252650651016111 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/tests/pylib/test_packet.py0000644000175000017500000024316713252364117021003 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function, division import unittest import ntp.packet import ntp.control import ntp.util import ntp.magic import socket import select import sys import getpass import jigs import time odict = ntp.util.OrderedDict ntpp = ntp.packet ctlerr = ntp.packet.ControlException master_encoding = 'latin-1' if str is bytes: # Python 2 polystr = str polybytes = bytes polyord = ord polychr = str input = raw_input def string_escape(s): return s.decode('string_escape') else: # Python 3 import io 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 polyord(c): "Polymorphic ord() function" if isinstance(c, str): return ord(c) else: return c def polychr(c): "Polymorphic chr() function" if isinstance(c, int): return chr(c) else: return c 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_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) class SessionJig: def __init__(self): self.readvars_calls = 0 def readvars(self): self.readvars_calls += 1 return {"foo": 23, "bar": 42} class ControlPacketJig: HEADER_LEN = ntpp.ControlPacket.HEADER_LEN def __init__(self, session, opcode, associd, data): self.session = session self.opcode = opcode self.associd = associd self.extension = data self.sequence = None self.send_call_count = 0 def send(self): self.send_call_count += 1 return self def flatten(self): return self.extension class AuthenticatorJig: compute_mac_calls = [] def __init__(self): self.control_call_count = 0 self.control_fail = 0 self.fail_getitem = False self.compute_mac_calls = [] def __getitem__(self, key): if self.fail_getitem is True: raise IndexError return ("passtype", "pass") def control(self): self.control_call_count += 1 if self.control_fail > 0: self.control_fail -= 1 raise ValueError return (23, "keytype", "miranda") @staticmethod def compute_mac(flatpkt, keyid, keytype, passwd): AuthenticatorJig.compute_mac_calls.append((flatpkt, keyid, keytype, passwd)) return "mac" # ========================================================== # Tests # ========================================================== class TestPacket(unittest.TestCase): target = ntpp.Packet def test_VN_MODE(self): f = self.target.VN_MODE self.assertEqual(f(0, 0), 0x00) self.assertEqual(f(0, 6), 0x06) self.assertEqual(f(1, 6), 0x0E) self.assertEqual(f(7, 6), 0x3E) self.assertEqual(f(9, 6), 0x0E) # overflow version self.assertEqual(f(0, 9), 0x01) # overflow mode def test_PKT_LI_VM_MODE(self): f = self.target.PKT_LI_VN_MODE self.assertEqual(f(0, 0, 0), 0x00) self.assertEqual(f(1, 4, 6), 0x66) self.assertEqual(f(3, 4, 6), 0xE6) self.assertEqual(f(5, 4, 6), 0x66) # overflow leap def test_Packet(self): # Test __init__, basic cls = self.target() self.assertEqual(cls.session, None) self.assertEqual(cls.li_vn_mode, 0xE3) self.assertEqual(cls.extension, b'') # Test __init__, custom cls = self.target(2, 2, "foo") self.assertEqual(cls.session, "foo") self.assertEqual(cls.li_vn_mode, 0xD2) # Test leap self.assertEqual(cls.leap(), "unsync") cls.li_vn_mode = self.target.PKT_LI_VN_MODE(0, 1, 1) self.assertEqual(cls.leap(), "no-leap") cls.li_vn_mode = self.target.PKT_LI_VN_MODE(1, 1, 1) self.assertEqual(cls.leap(), "add-leap") cls.li_vn_mode = self.target.PKT_LI_VN_MODE(2, 3, 4) self.assertEqual(cls.leap(), "del-leap") # Test version self.assertEqual(cls.version(), 3) # Test mode self.assertEqual(cls.mode(), 4) class TestSyncPacket(unittest.TestCase): target = ntpp.SyncPacket def test___init__(self): # Test without data (that will be tested via analyze()) cls = self.target() self.assertEqual(cls.status, 0) self.assertEqual(cls.stratum, 0) self.assertEqual(cls.poll, 0) self.assertEqual(cls.precision, 0) self.assertEqual(cls.root_delay, 0) self.assertEqual(cls.root_dispersion, 0) self.assertEqual(cls.refid, 0) self.assertEqual(cls.reference_timestamp, 0) self.assertEqual(cls.origin_timestamp, 0) self.assertEqual(cls.receive_timestamp, 0) self.assertEqual(cls.transmit_timestamp, 0) self.assertEqual(cls.extension, polybytes('')) self.assertEqual(cls.extfields, []) self.assertEqual(cls.mac, '') self.assertEqual(cls.hostname, None) self.assertEqual(cls.resolved, None) # In theory should test cls.received here, but it is assigned to time() self.assertEqual(cls.trusted, True) self.assertEqual(cls.rescaled, False) def test_analyze(self): # Test data less than packet data = "blah blah" try: cls = self.target(data) errored = False except ntpp.SyncException as e: errored = e.message self.assertEqual(errored, "impossible packet length") # Test data not word aligned data = "\x5C\x10\x01\xFF" \ "\x00\x00\x01\x01\x00\x00\x01\x02\x00\x00\x01\x03" \ "\x00\x01\x02\x03\x04\x05\x06\x07" \ "\x01\x01\x02\x03\x04\x05\x06\x07" \ "\x02\x01\x02\x03\x04\x05\x06\x07" \ "\x03\x01\x02\x03\x04\x05\x06\x07\x0B\xAD" try: cls = self.target(data) errored = False except ntpp.SyncException as e: errored = e.message self.assertEqual(errored, "impossible packet length") # Test without extension data = "\x5C\x10\x01\xFF" \ "\x00\x00\x01\x01\x00\x00\x01\x02\x00\x00\x01\x03" \ "\x00\x01\x02\x03\x04\x05\x06\x07" \ "\x01\x01\x02\x03\x04\x05\x06\x07" \ "\x02\x01\x02\x03\x04\x05\x06\x07" \ "\x03\x01\x02\x03\x04\x05\x06\x07" cls = self.target(data) # This calls analyze self.assertEqual(cls.li_vn_mode, 0x5C) self.assertEqual(cls.stratum, 16) self.assertEqual(cls.poll, 1) self.assertEqual(cls.precision, -1) self.assertEqual(cls.root_delay, 257) self.assertEqual(cls.root_dispersion, 258) self.assertEqual(cls.refid, 259) self.assertEqual(cls.reference_timestamp, 0x0001020304050607) self.assertEqual(cls.origin_timestamp, 0x0101020304050607) self.assertEqual(cls.receive_timestamp, 0x0201020304050607) self.assertEqual(cls.transmit_timestamp, 0x0301020304050607) self.assertEqual(cls.extfields, []) # Test with extension, Crypto-NAK ext = "\x00\x00\x00\x01\x00\x00\x00\x04blah" \ "\x00\x00\x00\x02\x00\x00\x00\x0Cjabberjabber" \ "\x00\x00\x00\x03\x00\x00\x00\x20" \ "In the end, our choices make us." mac = "\x11\x22\x33\x44" data2 = data + ext + mac cls = self.target(data2) self.assertEqual(cls.li_vn_mode, 0x5C) self.assertEqual(cls.stratum, 16) self.assertEqual(cls.poll, 1) self.assertEqual(cls.precision, -1) self.assertEqual(cls.root_delay, 257) self.assertEqual(cls.root_dispersion, 258) self.assertEqual(cls.refid, 259) self.assertEqual(cls.reference_timestamp, 0x0001020304050607) self.assertEqual(cls.origin_timestamp, 0x0101020304050607) self.assertEqual(cls.receive_timestamp, 0x0201020304050607) self.assertEqual(cls.transmit_timestamp, 0x0301020304050607) self.assertEqual(cls.extfields, [(1, polybytes("blah")), (2, polybytes("jabberjabber")), (3, polybytes("In the end, our choices make us."))]) self.assertEqual(cls.extension, polybytes(ext + mac)) self.assertEqual(cls.mac, polybytes(mac)) # Test with extension, DES data2 = data + ext + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC" try: cls = self.target(data2) errored = False except ntpp.SyncException as e: errored = e.message self.assertEqual(errored, "Unsupported DES authentication") # Test with extension, runt 8 data2 = data + ext + "\x11\x22\x33\x44\x55\x66\x77\x88" try: cls = self.target(data2) errored = False except ntpp.SyncException as e: errored = e.message self.assertEqual(errored, "Packet is a runt") # Test with extension, runt 16 data2 = data + ext + "\x00\x11\x22\x33\x44\x55\x66\x77" \ "\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF" try: cls = self.target(data2) errored = False except ntpp.SyncException as e: errored = e.message self.assertEqual(errored, "Packet is a runt") # Test with extension, MD5 or SHA1, 20 mac = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09" \ "\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13" data2 = data + ext + mac cls = self.target(data2) self.assertEqual(cls.mac, polybytes(mac)) self.assertEqual(cls.extension, polybytes(ext + mac)) # Test with extension, MD5 or SHA1, 24 mac += "\x14\x15\x16\x17" data2 = data + ext + mac cls = self.target(polybytes(data2)) self.assertEqual(cls.mac, polybytes(mac)) self.assertEqual(cls.extension, polybytes(ext + mac)) # ==================================================================== # The tests referring to the Timeless Void are testing outside of NTP's # representation range, they are left in on the off chance that they # catch a bug in the future. If they become problematic they can be # removed without loss. # ==================================================================== def test_ntp_to_posix(self): f = self.target.ntp_to_posix # Test the Timeless Void Before Existence self.assertAlmostEqual(f(-1), -2208988800.0) # Test beginning of NTP epoch self.assertAlmostEqual(f(0), -2208988800.0) # Test just before the Age of Unix self.assertAlmostEqual(f(2208988799 * 2**32), -1.0) # Test the beginning of the Age of Unix self.assertAlmostEqual(f(2208988800 * 2**32), 0.0) # Test the End of Time self.assertAlmostEqual(f(0xFFFFFFFF00000000), 2085978495.0) # Test the Timeless Void Beyond Existence self.assertEqual(f(0x10000000000000000), 2085978496.0) # Doesn't clip def test_posix_to_ntp(self): # This function may develop float precision issues, however it # returns an integer so testing for that is complicated. For the # moment the tests are being left as-is until such issues appear. f = self.target.posix_to_ntp # Test the Timeless Void Before Existence self.assertEqual(f(-2208988801), -0x100000000) # It doesn't clip # Test beginning of NTP epoch self.assertEqual(f(-2208988800), 0) # Test just before the Age of Unix self.assertEqual(f(-1), 2208988799 * 2**32) # Test the beginning of the Age of Unix self.assertEqual(f(0), 2208988800 * 2**32) # Test the End of Time self.assertEqual(f(2085978495), 0xFFFFFFFF00000000) # Test the Timeless Void Beyond Existence self.assertEqual(f(2085978496), 0x10000000000000000) # It doesn't clip def test_posixize(self): cls = self.target() # Test already scaled cls.rescaled = True cls.received = 0 # __init__ sets to current time cls.posixize() self.assertEqual(cls.rescaled, True) self.assertEqual(cls.root_delay, 0) self.assertEqual(cls.root_dispersion, 0) self.assertEqual(cls.reference_timestamp, 0) self.assertEqual(cls.origin_timestamp, 0) self.assertEqual(cls.receive_timestamp, 0) self.assertEqual(cls.transmit_timestamp, 0) self.assertEqual(cls.received, 0) # Test scaling cls.rescaled = False cls.root_delay = 131072 cls.root_dispersion = 131072 cls.posixize() self.assertEqual(cls.rescaled, True) self.assertEqual(cls.root_delay, 2) self.assertEqual(cls.root_dispersion, 2) self.assertAlmostEqual(cls.reference_timestamp, -2208988800.0) self.assertAlmostEqual(cls.origin_timestamp, -2208988800.0) self.assertAlmostEqual(cls.receive_timestamp, -2208988800.0) self.assertAlmostEqual(cls.transmit_timestamp, -2208988800.0) self.assertAlmostEqual(cls.received, -2208988800.0) def test_t1_4(self): cls = self.target() cls.origin_timestamp = 1 cls.receive_timestamp = 2 cls.transmit_timestamp = 3 cls.received = 4 self.assertEqual(cls.t1(), 1) self.assertEqual(cls.t2(), 2) self.assertEqual(cls.t3(), 3) self.assertEqual(cls.t4(), 4) def test_delta_epsilon_synchd_adjust(self): # Combined because they share setup, and are short tests cls = self.target() cls.origin_timestamp = 1 cls.receive_timestamp = 2 cls.transmit_timestamp = 3 cls.received = 4 cls.precision = 4 # Test delta self.assertEqual(cls.delta(), 2) # Test epsilon self.assertAlmostEqual(cls.epsilon(), 16.000045) # Test synchd self.assertAlmostEqual(cls.synchd(), 17.000045) # Test adjust self.assertAlmostEqual(cls.adjust(), 0.0) def test_flatten(self): data = "\x5C\x10\x01\xFF" \ "\x00\x00\x01\x01\x00\x00\x01\x02\x00\x00\x01\x03" \ "\x00\x01\x02\x03\x04\x05\x06\x07" \ "\x01\x01\x02\x03\x04\x05\x06\x07" \ "\x02\x01\x02\x03\x04\x05\x06\x07" \ "\x03\x01\x02\x03\x04\x05\x06\x07" ext = "\x00\x00\x00\x01\x00\x00\x00\x04blah" \ "\x00\x00\x00\x02\x00\x00\x00\x0Cjabberjabber" \ "\x00\x00\x00\x03\x00\x00\x00\x20" \ "In the end, our choices make us." \ "\x11\x22\x33\x44" pkt = polybytes(data + ext) cls = self.target(pkt) self.assertEqual(cls.flatten(), pkt) def test_refid_octets(self): cls = self.target() cls.refid = 0x12345678 self.assertEqual(cls.refid_octets(), (0x12, 0x34, 0x56, 0x78)) def test_refid_as_string(self): cls = self.target() cls.refid = 0x12345678 self.assertEqual(cls.refid_as_string(), "\x12\x34\x56\x78") def test_refid_as_address(self): cls = self.target() cls.refid = 0x01020304 self.assertEqual(cls.refid_as_address(), "1.2.3.4") def test_is_crypto_nak(self): cls = self.target() # Test True cls.mac = "blah" self.assertEqual(cls.is_crypto_nak(), True) # Test too low cls.mac = "bla" self.assertEqual(cls.is_crypto_nak(), False) # Test too high cls.mac = "blah!" self.assertEqual(cls.is_crypto_nak(), False) def test_MD5(self): cls = self.target() # Test True cls.mac = "blah" * 5 # 20 bytes self.assertEqual(cls.has_MD5(), True) # Test too low cls.mac = "bla" * 5 # 15 bytes self.assertEqual(cls.has_MD5(), False) # Test too high cls.mac = "blah!" * 5 # 24 bytes self.assertEqual(cls.has_MD5(), False) def test_SHA1(self): cls = self.target() # Test True cls.mac = "blah" * 6 # 24 bytes self.assertEqual(cls.has_SHA1(), True) # Test too low cls.mac = "bla" * 6 # 18 bytes self.assertEqual(cls.has_SHA1(), False) # Test too high cls.mac = "blah!" * 6 # 30 bytes self.assertEqual(cls.has_SHA1(), False) def test___repr__(self): cls = self.target() cls.reference_timestamp = self.target.posix_to_ntp(0) cls.origin_timestamp = self.target.posix_to_ntp(1) cls.receive_timestamp = self.target.posix_to_ntp(2) cls.transmit_timestamp = self.target.posix_to_ntp(3) # Test without extensions self.assertEqual(cls.__repr__(), "") # Test with extensions cls.extfields = "foobar" cls.mac = "machinations" self.assertEqual(cls.__repr__(), "") class TestMisc(unittest.TestCase): def test_Peer(self): session = SessionJig() # Test init cls = ntpp.Peer(session, 2, 3) self.assertEqual(cls.session, session) self.assertEqual(cls.associd, 2) self.assertEqual(cls.status, 3) self.assertEqual(cls.variables, {}) # Test readvars cls.readvars() self.assertEqual(cls.variables, {"foo": 23, "bar": 42}) # Test __str__ self.assertEqual(str(cls), "") # Test __repr__ self.assertEqual(repr(cls), "") def test_dump_hex_printable(self): f = ntpp.dump_hex_printable fp = jigs.FileJig() data = "\x00\x01\x02\x03\x04\x05\x06\x07" \ "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" # Test a single line f(data, fp) total = "".join(fp.data) # easier than 300 million separate strings fp.data = [] self.assertEqual(total, "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f " "................\n") # Test >1 lines, partial line data += "Would you kindly test this?" f(data, fp) total = "".join(fp.data) fp.data = [] self.assertEqual(total, "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f " "................\n" "57 6f 75 6c 64 20 79 6f 75 20 6b 69 6e 64 6c 79 " "Would you kindly\n" "20 74 65 73 74 20 74 68 69 73 3f " " test this?\n") def test_MRUEntry(self): # Test init cls = ntpp.MRUEntry() self.assertEqual(cls.addr, None) self.assertEqual(cls.last, None) self.assertEqual(cls.first, None) self.assertEqual(cls.ct, 0) self.assertEqual(cls.mv, None) self.assertEqual(cls.rs, None) # Test avgint cls.last = "0x00000200.00000000" cls.first = "0x00000100.00000000" cls.ct = 4 self.assertEqual(cls.avgint(), 64) # Test sortaddr, ipv6 cls.addr = "[11:22:33::44:55]:42" self.assertEqual(cls.sortaddr(), polybytes("\x00\x11\x00\x22\x00\x33\x00\x00" "\x00\x00\x00\x00\x00\x44\x00\x55")) # Test sortaddr, ipv6, local cls.addr = "[11:22:33::44:55%8]:42" self.assertEqual(cls.sortaddr(), polybytes("\x00\x11\x00\x22\x00\x33\x00\x00" "\x00\x00\x00\x00\x00\x44\x00\x55")) # Test sortaddr, ipv4 cls.addr = "11.22.33.44:23" self.assertEqual(cls.sortaddr(), polybytes((("\0" * 16) + "\x0b\x16\x21\x2c"))) # Test __repr__ # Python dicts enumeration order changes with different versions if sys.version_info[0] < 3: # Python 2 self.assertEqual(cls.__repr__(), "") elif sys.version_info[1] >= 6: # Already know it is 3.something # Python 3.6+, dicts enumerate in assignment order self.assertEqual(cls.__repr__(), "") pass else: # Python 3.x < 3.6, dicts enumerate randomly # I can not test randomness of this type pass def test_MRUList(self): # Test init cls = ntpp.MRUList() self.assertEqual(cls.entries, []) self.assertEqual(cls.now, None) # Test is_complete, no self.assertEqual(cls.is_complete(), False) # Test is_complete, yes cls.now = "0x01234567.89ABCDEF" self.assertEqual(cls.is_complete(), True) # Test __repr__, simplified cls.entries = [1, 2, 3, 4] self.assertEqual(cls.__repr__(), "") class TestControlPacket(unittest.TestCase): target = ntpp.ControlPacket session = ntpp.ControlSession def test___init__(self): ses = self.session() cls = self.target(ses) self.assertEqual(cls.r_e_m_op, 0) self.assertEqual(cls.sequence, 1) self.assertEqual(cls.status, 0) self.assertEqual(cls.associd, 0) self.assertEqual(cls.offset, 0) self.assertEqual(cls.extension, polybytes("")) self.assertEqual(cls.count, 0) def test_is_response(self): cls = self.target(self.session()) # Test True cls.r_e_m_op = 0x80 self.assertEqual(cls.is_response(), True) # Test False cls.r_e_m_op = 0x7F self.assertEqual(cls.is_response(), False) def test_is_error(self): cls = self.target(self.session()) # Test True cls.r_e_m_op = 0x40 self.assertEqual(cls.is_error(), True) # Test False cls.r_e_m_op = 0xBF self.assertEqual(cls.is_error(), False) def test_more(self): cls = self.target(self.session()) # Test True cls.r_e_m_op = 0x20 self.assertEqual(cls.more(), True) # Test False cls.r_e_m_op = 0xDF self.assertEqual(cls.more(), False) def test_opcode(self): cls = self.target(self.session()) # Test normal cls.r_e_m_op = 0x06 self.assertEqual(cls.opcode(), 6) # Test maximum cls.r_e_m_op = 0xFF self.assertEqual(cls.opcode(), 31) # Test maximum, no bits cls.r_e_m_op = 0x1F self.assertEqual(cls.opcode(), 31) def test_errcode(self): cls = self.target(self.session()) # Test none cls.status = 0x00FF self.assertEqual(cls.errcode(), 0) # Test midling cls.status = 0x11FF self.assertEqual(cls.errcode(), 17) # Test maximum cls.status = 0xFF00 self.assertEqual(cls.errcode(), 255) def test_end(self): cls = self.target(self.session()) cls.count = 5 cls.offset = 10 self.assertEqual(cls.end(), 15) def test_stats(self): cls = self.target(self.session()) cls.count = 25 cls.offset = 10 self.assertEqual(cls.stats(), " 10 35\t 25 octets\n") def test_analyze_flatten_send(self): cls = self.target(self.session()) header = "\x8A\x3F\x00\x01\x00\x02\x00\x03\x00\x20\x00\x10" payload = "\x00\x11\x22\x33\x44\x55\x66\x77" \ "\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF" ext = "\x01\x02\x03\x04" \ "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7" \ "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF" totaldata = header + payload + ext # Test analyze result = cls.analyze(totaldata) self.assertEqual(result, (1, 2, 3, 32)) self.assertEqual(cls.li_vn_mode, 0x8A) self.assertEqual(cls.r_e_m_op, 0x3F) self.assertEqual(cls.sequence, 1) self.assertEqual(cls.status, 2) self.assertEqual(cls.associd, 3) self.assertEqual(cls.offset, 32) self.assertEqual(cls.count, 16) # Test flatten self.assertEqual(cls.flatten(), polybytes(totaldata)) # Test send send_data = [] def send_jig(pkt): send_data.append(pkt) cls.session.sendpkt = send_jig cls.send() self.assertEqual(send_data, [polybytes(totaldata)]) class TestControlSession(unittest.TestCase): target = ntpp.ControlSession def test___init__(self): # Test cls = self.target() self.assertEqual(cls.debug, 0) self.assertEqual(cls.ai_family, socket.AF_UNSPEC) self.assertEqual(cls.primary_timeout, ntpp.DEFTIMEOUT) self.assertEqual(cls.secondary_timeout, ntpp.DEFSTIMEOUT) self.assertEqual(cls.pktversion, ntp.magic.NTP_OLDVERSION + 1) self.assertEqual(cls.always_auth, False) self.assertEqual(cls.keytype, "MD5") self.assertEqual(cls.keyid, None) self.assertEqual(cls.passwd, None) self.assertEqual(cls.auth, None) self.assertEqual(cls.hostname, None) self.assertEqual(cls.isnum, False) self.assertEqual(cls.sock, None) self.assertEqual(cls.port, 0) self.assertEqual(cls.sequence, 0) self.assertEqual(cls.response, "") self.assertEqual(cls.rstatus, 0) self.assertEqual(cls.ntpd_row_limit, self.target.MRU_ROW_LIMIT) self.assertEqual(cls.logfp, sys.stdout) self.assertEqual(cls.nonce_xmit, 0) def test_close(self): # Init sockjig = jigs.SocketJig() cls = self.target() cls.sock = sockjig # Test cls.close() self.assertEqual(sockjig.closed, True) self.assertEqual(cls.sock, None) def test_havehost(self): # Init cls = self.target() # Test empty self.assertEqual(cls.havehost(), False) # Test full cls.sock = True self.assertEqual(cls.havehost(), True) def test___lookuphost(self): logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() faketimemod.time_returns = [0] * 10 try: timetemp = ntp.util.time ntp.util.time = faketimemod fakesockmod = jigs.SocketModuleJig() ntpp.socket = fakesockmod # Init cls = self.target() cls.debug = 3 cls.logfp = logjig # Test first type fakesockmod.gai_returns = [42] result = cls._ControlSession__lookuphost("[blah.com]", "family") self.assertEqual(result, 42) self.assertEqual(fakesockmod.gai_calls, [("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, socket.AI_NUMERICHOST)]) self.assertEqual(logjig.data, []) # Test second type logjig.__init__() # reset fakesockmod.__init__() fakesockmod.gai_returns = [42] fakesockmod.gai_error_count = 1 result = cls._ControlSession__lookuphost("blah.com", "family") self.assertEqual(result, 42) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z ntpq: numeric-mode lookup " "of blah.com failed, None\n"]) self.assertEqual(fakesockmod.gai_calls, [("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, socket.AI_NUMERICHOST), ("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, 0)]) # Test third type logjig.__init__() # reset fakesockmod.__init__() fakesockmod.gai_returns = [42] fakesockmod.gai_error_count = 2 result = cls._ControlSession__lookuphost("blah.com", "family") self.assertEqual(result, 42) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z ntpq: numeric-mode lookup " "of blah.com failed, None\n", "ntpq: standard-mode lookup of blah.com failed, " "None\n"]) self.assertEqual(fakesockmod.gai_calls, [("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, socket.AI_NUMERICHOST), ("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, 0), ("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, 0)]) # Test all types failed logjig.__init__() # reset fakesockmod.__init__() fakesockmod.gai_error_count = 3 result = cls._ControlSession__lookuphost("blah.com", "family") self.assertEqual(result, None) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z ntpq: numeric-mode lookup " "of blah.com failed, None\n", "ntpq: standard-mode lookup of blah.com failed, " "None\n", "ntpq: ndp lookup failed, None\n"]) self.assertEqual(fakesockmod.gai_calls, [("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, socket.AI_NUMERICHOST), ("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, 0), ("blah.com", "ntp", cls.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, 0)]) finally: ntpp.socket = socket ntp.util.time = timetemp def test_openhost(self): lookups = [] returnNothing = True noCanon = False def lookup_jig(hostname, family): lookups.append((hostname, family)) if returnNothing is True: return None elif noCanon is True: return [("family", "socktype", "protocol", None, ("1.2.3.4", 80)), ] else: return [("family", "socktype", "protocol", "canon", ("1.2.3.4", 80)), ] logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() try: fakesockmod = jigs.SocketModuleJig() ntpp.socket = fakesockmod timetemp = ntp.util.time ntp.util.time = faketimemod faketimemod.time_returns = [0] * 10 # Init cls = self.target() cls.debug = 3 cls.logfp = logjig cls._ControlSession__lookuphost = lookup_jig # Test, lookup failure result = cls.openhost("foo.org") self.assertEqual(result, False) self.assertEqual(lookups, [("foo.org", socket.AF_UNSPEC)]) # Test, with canon, and success returnNothing = False lookups = [] result = cls.openhost("foo.org") self.assertEqual(result, True) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Opening host canon\n"]) self.assertEqual(lookups, [("foo.org", socket.AF_UNSPEC)]) self.assertEqual(fakesockmod.socket_calls, [("family", "socktype", "protocol")]) sock = fakesockmod.socketsReturned[0] self.assertEqual(sock.connected, ("1.2.3.4", 80)) self.assertEqual(cls.hostname, "canon") self.assertEqual(cls.isnum, False) self.assertEqual(cls.port, 80) self.assertEqual(cls.sock, sock) # Test, without canon, and success noCanon = True lookups = [] logjig.__init__() fakesockmod.__init__() cls.sock = None result = cls.openhost("foo.org") self.assertEqual(result, True) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Opening host canon.com\n"]) self.assertEqual(lookups, [("foo.org", socket.AF_UNSPEC)]) self.assertEqual(fakesockmod.socket_calls, [("family", "socktype", "protocol")]) sock = fakesockmod.socketsReturned[0] self.assertEqual(sock.connected, ("1.2.3.4", 80)) self.assertEqual(cls.hostname, "canon.com") self.assertEqual(cls.isnum, True) self.assertEqual(cls.port, 80) self.assertEqual(cls.sock, sock) # Test, with canon, and socket creation failure noCanon = False cls.sock = None fakesockmod.socket_fail = True try: result = cls.openhost("foo.org") errored = False except ctlerr as e: errored = e self.assertEqual(errored.message, "Error opening foo.org: error! [23]") # Test, with canon, and socket connection failure fakesockmod.socket_fail = False fakesockmod.socket_fail_connect = True cls.sock = None try: result = cls.openhost("foo.org") errored = False except ctlerr as e: errored = e self.assertEqual(errored.message, "Error connecting to foo.org: socket! [16]") finally: ntpp.socket = socket ntp.util.time = timetemp def test_password(self): iojig = jigs.FileJig() fakegetpmod = jigs.GetpassModuleJig() fakeosmod = jigs.OSModuleJig() # Init cls = self.target() try: tempauth = ntpp.Authenticator ntpp.Authenticator = AuthenticatorJig ntpp.getpass = fakegetpmod tempstdin = sys.stdin sys.stdin = iojig tempstdout = sys.stdout sys.stdout = iojig tempos = ntpp.os ntpp.os = fakeosmod # Test with nothing iojig.readline_return = ["1\n"] * 10 fakeosmod.isatty_returns = [True] * 10 cls.password() self.assertEqual(isinstance(cls.auth, AuthenticatorJig), True) self.assertEqual(cls.keyid, 1) self.assertEqual(cls.passwd, "pass") # Test with auth and localhost, fail cls.keyid = None cls.passwd = None cls.hostname = "localhost" cls.auth.control_fail = 1 try: cls.password() errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_NOTRUST) # Test with auth and localhost cls.keyid = None cls.passwd = None cls.hostname = "localhost" cls.password() self.assertEqual(cls.keyid, 23) self.assertEqual(cls.keytype, "keytype") self.assertEqual(cls.passwd, "miranda") # Test with all but password, normal password cls.passwd = None cls.auth.fail_getitem = True fakegetpmod.getpass_returns = ["xyzzy"] cls.password() self.assertEqual(fakegetpmod.getpass_calls, [("keytype Password: ", None)]) self.assertEqual(cls.passwd, "xyzzy") # Test with all but password, hex password fakegetpmod.getpass_calls = [] cls.passwd = None cls.auth.fail_getitem = True fakegetpmod.getpass_returns = ["0102030405060708090A" "0B0C0D0E0F1011121314"] # 40 char cls.password() self.assertEqual(fakegetpmod.getpass_calls, [("keytype Password: ", None)]) self.assertEqual(cls.passwd, "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A" "\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14") finally: ntpp.Authenticator = tempauth ntpp.getpass = getpass sys.stdin = tempstdin sys.stdout = tempstdout ntpp.os = tempos def test_sendpkt(self): logjig = jigs.FileJig() sockjig = jigs.SocketJig() faketimemod = jigs.TimeModuleJig() faketimemod.time_returns = [0] * 10 # Init cls = self.target() cls.logfp = logjig cls.sock = sockjig cls.debug = 3 try: timetemp = ntp.util.time ntp.util.time = faketimemod # Test res = cls.sendpkt(polybytes("blahfoo")) self.assertEqual(res, 0) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Sending 8 octets. " "seq=0\n"]) self.assertEqual(sockjig.data, [polybytes("blahfoo\x00")]) # Test error logjig.__init__() sockjig.fail_send = 1 res = cls.sendpkt("blah") self.assertEqual(res, -1) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Sending 4 octets. seq=0\n", "Write to None failed\n"]) finally: ntp.util.time = timetemp def test_sendrequest(self): logjig = jigs.FileJig() try: tempcpkt = ntpp.ControlPacket ntpp.ControlPacket = ControlPacketJig tempauth = ntpp.Authenticator ntpp.Authenticator = AuthenticatorJig cls = self.target() cls.logfp = logjig cls.debug = 3 # Test oversize data datalen = ntp.control.CTL_MAX_DATA_LEN + 1 data = "a" * datalen result = cls.sendrequest(1, 2, data) self.assertEqual(result, -1) self.assertEqual(logjig.data, ["\n", "sendrequest: opcode=1, associd=2, " "qdata=" + data + "\n", "***Internal error! Data too large " "(" + str(len(data)) + ")\n"]) # Test no auth result = cls.sendrequest(1, 2, polybytes("foo")) self.assertEqual(result.sequence, 1) self.assertEqual(result.extension, polybytes("foo\x00")) # Test with auth cls.keyid = 1 cls.passwd = "qwerty" result = cls.sendrequest(1, 2, polybytes("foo"), True) self.assertEqual(result.sequence, 2) self.assertEqual(result.extension, polybytes("foo\x00mac")) # Test with auth keyid / password failure cls.keyid = None try: cls.sendrequest(1, 2, "foo", True) errored = False except ctlerr: errored = True self.assertEqual(errored, True) finally: ntpp.ControlPacket = tempcpkt ntpp.Authenticator = tempauth def test_getresponse(self): logjig = jigs.FileJig() sockjig = jigs.SocketJig() fakeselectmod = jigs.SelectModuleJig() # Init cls = self.target() cls.debug = 3 cls.logfp = logjig cls.sock = sockjig try: ntpp.select = fakeselectmod # Test empty sockjig.return_data = [ "\x0E\x81\x00\x00\x00\x03\x00\x02\x00\x00\x00\x00"] cls.getresponse(1, 2, True) self.assertEqual(cls.response, polybytes("")) # Test with data sockjig.return_data = [ "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x00\x00\x09" "foo=4223,\x00\x00\x00", "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x09\x00\x0E" "blah=248,x=23,\x00\x00", "\x0E\x81\x00\x01\x00\x02\x00\x03\x00\x17\x00\x06" "quux=1\x00\x00"] cls.sequence = 1 cls.getresponse(1, 3, True) self.assertEqual(cls.response, polybytes("foo=4223,blah=248,x=23,quux=1")) # Test with data, duplicate packet sockjig.return_data = [ "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x00\x00\x09" "foo=4223,\x00\x00\x00", "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x09\x00\x0E" "blah=248,x=23,\x00\x00", "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x09\x00\x0E" "blah=248,x=23,\x00\x00", "\x0E\x81\x00\x01\x00\x02\x00\x03\x00\x17\x00\x06" "quux=1\x00\x00"] cls.sequence = 1 cls.getresponse(1, 3, True) self.assertEqual(cls.response, polybytes("foo=4223,blah=248,x=23,quux=1")) # Test MAXFRAGS bail maxtemp = ntpp.MAXFRAGS ntpp.MAXFRAGS = 1 sockjig.return_data = [ "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x00\x00\x09" "foo=4223,\x00\x00\x00", "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x09\x00\x0E" "blah=248,x=23,\x00\x00", "\x0E\x81\x00\x01\x00\x02\x00\x03\x00\x17\x00\x06" "quux=1\x00\x00"] cls.sequence = 1 try: cls.getresponse(1, 3, True) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_TOOMUCH) ntpp.MAXFRAGS = maxtemp # Test select fail fakeselectmod.select_fail = 1 try: cls.getresponse(1, 2, True) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_SELECT) # Test no data and timeout fakeselectmod.do_return = [False] try: cls.getresponse(1, 2, True) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_TIMEOUT) # Test partial data and no timeout sockjig.return_data = [ "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x00\x00\x09" "foo=4223,\x00\x00\x00", "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x09\x00\x0E" "blah=248,x=23,\x00\x00", "\x0E\x81\x00\x01\x00\x02\x00\x03\x00\x17\x00\x06" "quux=1\x00\x00"] fakeselectmod.do_return = [True, False] try: cls.getresponse(1, 2, False) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_INCOMPLETE) # Test header parse fail sockjig.return_data = [ "\x0E\x81\x00\x00\x00\x03"] try: cls.getresponse(1, 2, True) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_UNSPEC) finally: ntpp.select = select def test___validate_packet(self): logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() # Init cls = self.target() cls.debug = 5 cls.logfp = logjig try: timetemp = ntp.util.time ntp.util.time = faketimemod faketimemod.time_returns = [0] * 10 # Test good packet, empty data raw = "\x0E\x81\x00\x00\x00\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), True) self.assertEqual(logjig.data, []) # Test good packet, with data logjig.data = [] raw = "\x0E\xA1\x00\x01\x00\x02\x00\x03\x00\x00\x00\x09" \ "foo=4223,\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) cls.sequence = 1 self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 3), True) self.assertEqual(logjig.data, []) # Test bad packet, bad version # Need to fix version logic 0x46 can be ver == 5, or 0 cls.sequence = 0 logjig.data = [] raw = "\x46\x81\x00\x00\x00\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), False) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Fragment " "received with version 0\n"]) # Test bad packet, bad mode logjig.data = [] raw = "\x0D\x81\x00\x00\x00\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), False) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Fragment received " "with mode 5\n"]) # Test bad packet, bad response bit logjig.data = [] raw = "\x0E\x01\x00\x00\x00\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), False) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Received request, " "wanted response\n"]) # Test bad packet, bad sequence logjig.data = [] raw = "\x0E\x81\x00\x01\x00\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), False) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Received sequence " "number 1, wanted 0\n"]) # Test bad packet, bad opcode logjig.data = [] raw = "\x0E\x80\x00\x00\x00\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), False) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Received opcode 0, " "wanted 1\n"]) # Test error packet logjig.data = [] raw = "\x0E\xC1\x00\x00" + \ chr(ntp.control.CERR_BADVALUE) + \ "\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) try: cls._ControlSession__validate_packet(pkt, raw, 1, 2) self.assertEqual(False, True) # it should have errored here except ctlerr as e: self.assertEqual(e.errorcode, ntp.control.CERR_BADVALUE) self.assertEqual(logjig.data, []) # Test error packet, with more bit logjig.data = [] errcs = chr(ntp.control.CERR_BADVALUE) raw = "\x0E\xE1\x00\x00" + errcs + "\x03\x00\x02\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) try: cls._ControlSession__validate_packet(pkt, raw, 1, 2) self.assertEqual(False, True) # it should have errored here except ctlerr as e: self.assertEqual(e.errorcode, ntp.control.CERR_BADVALUE) errstr = "Error " + str(ntp.control.CERR_BADVALUE) + \ " received on non-final fragment\n" self.assertEqual(logjig.data, [errstr]) # Test ok-ish packet, bad associd logjig.data = [] raw = "\x0E\x81\x00\x00\x00\x03\x00\xFF\x00\x00\x00\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), True) self.assertEqual(logjig.data, ["Association ID 255 doesn't match expected 2\n"]) # Test bad data padding logjig.data = [] raw = "\x0E\x81\x00\x00\x00\x03\x00\x02\x00\x00\x00\x01@" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) self.assertEqual(cls._ControlSession__validate_packet(pkt, raw, 1, 2), False) self.assertEqual(logjig.data, ["Response fragment not padded, size = 13\n"]) # Test too little data logjig.data = [] raw = "\x0E\x81\x00\x00\x00\x03\x00\x02\x00\x00\x00\x10foo\x00" pkt = ntpp.ControlPacket(cls) pkt.analyze(raw) try: cls._ControlSession__validate_packet(pkt, raw, 1, 2) self.assertEqual(True, False) # should have errored here except ctlerr as e: self.assertEqual(e.message, ntpp.SERR_INCOMPLETE) self.assertEqual(logjig.data, ["Response fragment claims 16 octets payload, " "above 4 received\n"]) finally: ntp.util.time = timetemp def test_doquery(self): sends = [] def sendrequest_jig(opcode, associd, qdata, auth): sends.append((opcode, associd, qdata, auth)) gets = [] doerror = [False] def getresponse_jig(opcode, associd, retry): gets.append((opcode, associd, retry)) if doerror[0]: doerror[0] = False raise ctlerr(ntpp.SERR_TIMEOUT) return "flax!" # Init cls = self.target() cls.sendrequest = sendrequest_jig cls.getresponse = getresponse_jig # Test no socket try: cls.doquery(1, 2, "blah") errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_NOHOST) # Test no retry cls.sock = True # to fool havehost() result = cls.doquery(1, 2, "blah") self.assertEqual(result, "flax!") self.assertEqual(len(sends), 1) self.assertEqual(sends[0], (1, 2, "blah", False)) self.assertEqual(len(gets), 1) self.assertEqual(gets[0], (1, 2, False)) # Reset sends = [] gets = [] doerror[0] = True # Test retry result = cls.doquery(1, 2, "quux") self.assertEqual(result, "flax!") self.assertEqual(len(sends), 2) self.assertEqual(sends, [(1, 2, "quux", False), (1, 2, "quux", False)]) self.assertEqual(len(gets), 2) self.assertEqual(gets, [(1, 2, False), (1, 2, True)]) def test_readstat(self): # Init queries = [] def doquery_jig(opcode, associd=0, qdata="", auth=False): queries.append((opcode, associd, qdata, auth)) cls = self.target() cls.doquery = doquery_jig # Test empty cls.response = "" idlist = cls.readstat(42) self.assertEqual(idlist, []) self.assertEqual(queries, [(ntp.control.CTL_OP_READSTAT, 42, "", False)]) # Test normal queries = [] cls.response = polybytes("\xDE\xAD\xF0\x0D") idlist = cls.readstat() self.assertEqual(len(idlist), 1) self.assertEqual(isinstance(idlist[0], ntpp.Peer), True) self.assertEqual(idlist[0].associd, 0xDEAD) self.assertEqual(idlist[0].status, 0xF00D) self.assertEqual(queries, [(ntp.control.CTL_OP_READSTAT, 0, "", False)]) # Test incorrect response cls.response = "foo" try: cls.readstat() errored = False except ctlerr: errored = True self.assertEqual(errored, True) def test___parse_varlist(self): # Init cls = self.target() cls.response = 'srcadr=0.0.0.0, srcport=0, srchost="0.ubuntu.pool.ntp.org",\r\ndstadr=0.0.0.0, dstport=0, leap=3, stratum=16, precision=-22,\r\nrootdelay=0.000, rootdisp=0.000, refid=POOL,\r\nreftime=0x00000000.00000000, rec=0x00000000.00000000, reach=0x0,\r\nunreach=0, hmode=3, pmode=0, hpoll=6, ppoll=10, headway=0, flash=0x1600,\r\nkeyid=0, offset=0.000, delay=0.000, dispersion=16000.000, jitter=0.000,\r\nfiltdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltoffset= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltdisp= 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00,\r\nnovalue, blankvalue=, quotedvalue="jabber"' # Test with basic packet self.assertEqual(cls._ControlSession__parse_varlist(), odict((("srcadr", "0.0.0.0"), ("srcport", 0), ("srchost", "0.ubuntu.pool.ntp.org"), ("dstadr", "0.0.0.0"), ("dstport", 0), ("leap", 3), ("stratum", 16), ("precision", -22), ("rootdelay", 0.0), ("rootdisp", 0.0), ("refid", "POOL"), ("reftime", "0x00000000.00000000"), ("rec", "0x00000000.00000000"), ("reach", 0), ("unreach", 0), ("hmode", 3), ("pmode", 0), ("hpoll", 6), ("ppoll", 10), ("headway", 0), ("flash", 5632), ("keyid", 0), ("offset", 0.0), ("delay-s", "0.000"), ("delay", 0.0), ("dispersion", 16000.0), ("jitter", 0.0), ("filtdelay", "0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00"), ("filtoffset", "0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00"), ("filtdisp", "16000.00 16000.00 16000.00 16000.00 " "16000.00 16000.00 16000.00 16000.00"), ("novalue", ""), ("blankvalue", ""), ("quotedvalue", "jabber")))) # Test with basic packet, raw mode self.assertEqual(cls._ControlSession__parse_varlist(raw=True), odict((("srcadr", ("0.0.0.0", "0.0.0.0")), ("srcport", (0, "0")), ("srchost", ("0.ubuntu.pool.ntp.org", "0.ubuntu.pool.ntp.org")), ("dstadr", ("0.0.0.0", "0.0.0.0")), ("dstport", (0, "0")), ("leap", (3, "3")), ("stratum", (16, "16")), ("precision", (-22, "-22")), ("rootdelay", (0.0, "0.000")), ("rootdisp", (0.0, "0.000")), ("refid", ("POOL", "POOL")), ("reftime", ("0x00000000.00000000", "0x00000000.00000000")), ("rec", ("0x00000000.00000000", "0x00000000.00000000")), ("reach", (0, "0x0")), ("unreach", (0, "0")), ("hmode", (3, "3")), ("pmode", (0, "0")), ("hpoll", (6, "6")), ("ppoll", (10, "10")), ("headway", (0, "0")), ("flash", (5632, "0x1600")), ("keyid", (0, "0")), ("offset", (0.0, "0.000")), ("delay", (0.0, "0.000")), ("dispersion", (16000.0, "16000.000")), ("jitter", (0.0, "0.000")), ("filtdelay", ("0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00", "0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00")), ("filtoffset", ("0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00", "0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00")), ("filtdisp", ("16000.00 16000.00 16000.00 16000.00 " "16000.00 16000.00 16000.00 16000.00", "16000.00 16000.00 16000.00 16000.00 " "16000.00 16000.00 16000.00 16000.00")), ("novalue", ("", "")), ("blankvalue", ("", "")), ("quotedvalue", ("jabber", "jabber"))))) def test_readvar(self): queries = [] def doquery_jig(opcode, associd=0, qdata="", auth=False): queries.append((opcode, associd, qdata, auth)) # Init cls = self.target() cls.doquery = doquery_jig cls.response = "foo=bar, murphy=42" # Test basic result = cls.readvar() self.assertEqual(result, odict((("foo", "bar"), ("murphy", 42)))) self.assertEqual(queries, [(ntp.control.CTL_OP_READVAR, 0, "", False)]) # Test raw queries = [] result = cls.readvar(raw=True) self.assertEqual(result, odict((("foo", ("bar", "bar")), ("murphy", (42, "42"))))) self.assertEqual(queries, [(ntp.control.CTL_OP_READVAR, 0, "", False)]) # Test with varlist queries = [] result = cls.readvar(varlist=("foo", "bar", "quux")) self.assertEqual(result, odict((("foo", "bar"), ("murphy", 42)))) self.assertEqual(queries, [(ntp.control.CTL_OP_READVAR, 0, "foo,bar,quux", False)]) def test_config(self): queries = [] def doquery_jig(opcode, associd=0, qdata="", auth=False): queries.append((opcode, associd, qdata, auth)) # Init cls = self.target() cls.doquery = doquery_jig cls.response = polybytes("Config Succeeded \n \x00 blah blah") # Test success result = cls.config(polybytes("Boo!")) self.assertEqual(result, True) self.assertEqual(queries, [(ntp.control.CTL_OP_CONFIGURE, 0, polybytes("Boo!"), True)]) # Test failure queries = [] cls.response = polybytes("whatever man...") result = cls.config(polybytes("Boo!")) self.assertEqual(result, False) self.assertEqual(queries, [(ntp.control.CTL_OP_CONFIGURE, 0, polybytes("Boo!"), True)]) # Test no response queries = [] cls.response = "" try: cls.config(polybytes("blah")) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_PERMISSION) def test_fetch_nonce(self): queries = [] def doquery_jig(opcode, associd=0, qdata="", auth=False): queries.append((opcode, associd, qdata, auth)) # Init filefp = jigs.FileJig() cls = self.target() cls.doquery = doquery_jig # Test success cls.response = polybytes("nonce=blah blah ") result = cls.fetch_nonce() self.assertEqual(result, "nonce=blah blah") self.assertEqual(queries, [(ntp.control.CTL_OP_REQ_NONCE, 0, "", False)]) # Test failure queries = [] cls.logfp = filefp cls.response = polybytes("blah blah") try: result = cls.fetch_nonce() errored = False except ctlerr: errored = True self.assertEqual(errored, True) self.assertEqual(filefp.data, ["## Nonce expected: blah blah"]) self.assertEqual(queries, [(ntp.control.CTL_OP_REQ_NONCE, 0, "", False), (ntp.control.CTL_OP_REQ_NONCE, 0, "", False), (ntp.control.CTL_OP_REQ_NONCE, 0, "", False), (ntp.control.CTL_OP_REQ_NONCE, 0, "", False)]) def test_mrulist(self): def setresponse(data): # needed for doquery_jig cls.response = data nonce_fetch_count = [0] def fetch_nonce_jig(): nonce_fetch_count[0] += 1 return "nonce=foo" queries = [] qrm = ["addr.1=1.2.3.4:23,last.1=40,first.1=23,ct.1=1,mv.1=2,rs.1=3", "addr.2=1.2.3.4:23,last.2=41,first.2=23,ct.2=1,mv.2=2,rs.2=3", "addr.1=10.20.30.40:23,last.1=42,first.1=23,ct.1=1," "mv.1=2,rs.1=3", "now=0x00000000.00000000"] query_results = qrm[:] # qrm == query results master query_fail = [0] query_fail_code = [] def doquery_jig(opcode, associd=0, qdata="", auth=False): queries.append((opcode, associd, qdata, auth)) if query_fail[0] > 0: query_fail[0] -= 1 code = query_fail_code.pop(0) raise ctlerr("foo", errorcode=code) if len(query_results) > 0: setresponse(query_results.pop(0)) logjig = jigs.FileJig() # Init cls = self.target() cls.fetch_nonce = fetch_nonce_jig cls.doquery = doquery_jig cls.logfp = logjig faketimemod = jigs.TimeModuleJig() faketimemod.time_returns = [0] * 500 try: timetemp = ntp.packet.time ntp.util.time = faketimemod # Test empty varlist result = cls.mrulist() self.assertEqual(nonce_fetch_count, [4]) self.assertEqual(queries, [(10, 0, "nonce=foo, frags=32", False), (10, 0, "nonce=foo, frags=32, addr.0=1.2.3.4:23, " "last.0=40", False), (10, 0, "nonce=foo, frags=32, addr.0=1.2.3.4:23, " "last.0=41, addr.1=1.2.3.4:23, last.1=40", False), (10, 0, "nonce=foo, frags=32, addr.0=10.20.30.40:23, " "last.0=42, addr.1=1.2.3.4:23, last.1=41, " "addr.2=1.2.3.4:23, last.2=40", False)]) self.assertEqual(isinstance(result, ntpp.MRUList), True) self.assertEqual(len(result.entries), 2) mru = result.entries[0] self.assertEqual(mru.addr, "1.2.3.4:23") self.assertEqual(mru.last, 41) self.assertEqual(mru.first, 23) self.assertEqual(mru.ct, 1) self.assertEqual(mru.mv, 2) self.assertEqual(mru.rs, 3) mru = result.entries[1] self.assertEqual(mru.addr, "10.20.30.40:23") self.assertEqual(mru.last, 42) self.assertEqual(mru.first, 23) self.assertEqual(mru.ct, 1) self.assertEqual(mru.mv, 2) self.assertEqual(mru.rs, 3) # Test with sort and frags (also test frag increment) nonce_fetch_count = [0] query_results = qrm[:] queries = [] result = cls.mrulist(variables={"sort": "addr", "frags": 24, "resall":5}) self.assertEqual(nonce_fetch_count, [4]) self.assertEqual(queries, [(10, 0, "nonce=foo, frags=24, resall=0x5", False), (10, 0, "nonce=foo, frags=25, resall=0x5, " "addr.0=1.2.3.4:23, last.0=40", False), (10, 0, "nonce=foo, frags=26, resall=0x5, " "addr.0=1.2.3.4:23, last.0=41, addr.1=1.2.3.4:23, " "last.1=40", False), (10, 0, "nonce=foo, frags=27, resall=0x5, " "addr.0=10.20.30.40:23, " "last.0=42, addr.1=1.2.3.4:23, last.1=41, " "addr.2=1.2.3.4:23, last.2=40", False)]) self.assertEqual(isinstance(result, ntpp.MRUList), True) self.assertEqual(len(result.entries), 2) mru = result.entries[0] self.assertEqual(mru.addr, "10.20.30.40:23") self.assertEqual(mru.last, 42) self.assertEqual(mru.first, 23) self.assertEqual(mru.ct, 1) self.assertEqual(mru.mv, 2) self.assertEqual(mru.rs, 3) mru = result.entries[1] self.assertEqual(mru.addr, "1.2.3.4:23") self.assertEqual(mru.last, 41) self.assertEqual(mru.first, 23) self.assertEqual(mru.ct, 1) self.assertEqual(mru.mv, 2) self.assertEqual(mru.rs, 3) # Test sorter error nonce_fetch_count = [0] try: cls.mrulist(variables={"sort": "foo"}) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_BADSORT % "foo") # Test varbind error nonce_fetch_count = [0] try: cls.mrulist(variables={"foo": 1}) errored = False except ctlerr as e: errored = e.message self.assertEqual(errored, ntpp.SERR_BADPARAM % "foo") # Test add to request errors # Test None error nonce_fetch_count = [0] queries = [] query_fail = [1] query_fail_code = [None] try: cls.mrulist() errored = False except ctlerr as e: errored = e.errorcode self.assertEqual(errored, None) # Test random error nonce_fetch_count = [0] queries = [] query_fail = [1] query_fail_code = ["therdaglib"] try: cls.mrulist() errored = False except ctlerr as e: errored = e.errorcode self.assertEqual(errored, "therdaglib") # Test unknown var error nonce_fetch_count = [0] query_results = qrm[:] queries = [] query_fail = [1] query_fail_code = [ntp.control.CERR_UNKNOWNVAR] * 2 cls.response = "" result = cls.mrulist() self.assertEqual(nonce_fetch_count, [5]) self.assertEqual(queries, [(10, 0, "nonce=foo, frags=32", False), (10, 0, "nonce=foo, frags=32", False), (10, 0, "nonce=foo, frags=32, addr.0=1.2.3.4:23, last.0=40", False), (10, 0, "nonce=foo, frags=32, addr.0=1.2.3.4:23, " "last.0=41, addr.1=1.2.3.4:23, last.1=40", False), (10, 0, "nonce=foo, frags=32, addr.0=10.20.30.40:23, " "last.0=42, addr.1=1.2.3.4:23, last.1=41, " "addr.2=1.2.3.4:23, last.2=40", False)]) self.assertEqual(isinstance(result, ntpp.MRUList), True) self.assertEqual(len(result.entries), 2) # Test bad value error nonce_fetch_count = [0] query_results = qrm[:] queries = [] query_fail = [2] query_fail_code = [ntp.control.CERR_BADVALUE] * 2 cls.response = "" logjig.data = [] cls.debug = 1 result = cls.mrulist() self.assertEqual(nonce_fetch_count, [6]) self.assertEqual(queries, [(10, 0, "nonce=foo, frags=32", False), (10, 0, "nonce=foo, limit=96", False), (10, 0, "nonce=foo, limit=96", False), (10, 0, "nonce=foo, limit=96, addr.0=1.2.3.4:23, last.0=40", False), (10, 0, "nonce=foo, limit=96, addr.0=1.2.3.4:23, " "last.0=41, addr.1=1.2.3.4:23, last.1=40", False), (10, 0, "nonce=foo, limit=96, addr.0=10.20.30.40:23, " "last.0=42, addr.1=1.2.3.4:23, last.1=41, " "addr.2=1.2.3.4:23, last.2=40", False)]) self.assertEqual(isinstance(result, ntpp.MRUList), True) self.assertEqual(len(result.entries), 2) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Reverted to row limit " "from fragments limit.\n", "1970-01-01T00:00:00Z Row limit reduced to 96 " "following CERR_BADVALUE.\n"]) # Test incomplete error nonce_fetch_count = [0] query_results = qrm[:] queries = [] query_fail = [3] query_fail_code = [ntpp.SERR_INCOMPLETE, ntp.control.CERR_BADVALUE, # Trigger cap_frags ntpp.SERR_INCOMPLETE] cls.response = "" logjig.data = [] cls.debug = 1 result = cls.mrulist() self.assertEqual(nonce_fetch_count, [7]) self.assertEqual(queries, [(10, 0, "nonce=foo, frags=32", False), (10, 0, "nonce=foo, frags=16", False), (10, 0, "nonce=foo, limit=96", False), (10, 0, "nonce=foo, limit=48", False), (10, 0, "nonce=foo, limit=49, addr.0=1.2.3.4:23, last.0=40", False), (10, 0, "nonce=foo, limit=51, addr.0=1.2.3.4:23, " "last.0=41, addr.1=1.2.3.4:23, last.1=40", False), (10, 0, "nonce=foo, limit=52, addr.0=10.20.30.40:23, " "last.0=42, addr.1=1.2.3.4:23, last.1=41, " "addr.2=1.2.3.4:23, last.2=40", False)]) self.assertEqual(len(result.entries), 2) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Frag limit reduced to 16 " "following incomplete response.\n", "1970-01-01T00:00:00Z Reverted to row limit " "from fragments limit.\n", "1970-01-01T00:00:00Z Row limit reduced to 48 " "following incomplete response.\n"]) # Test timeout error nonce_fetch_count = [0] query_results = qrm[:] queries = [] query_fail = [3] query_fail_code = [ntpp.SERR_TIMEOUT, ntp.control.CERR_BADVALUE, # Trigger cap_frags ntpp.SERR_TIMEOUT] cls.response = "" logjig.data = [] cls.debug = 1 result = cls.mrulist() self.assertEqual(nonce_fetch_count, [7]) self.assertEqual(queries, [(10, 0, "nonce=foo, frags=32", False), (10, 0, "nonce=foo, frags=16", False), (10, 0, "nonce=foo, limit=96", False), (10, 0, "nonce=foo, limit=48", False), (10, 0, "nonce=foo, limit=49, addr.0=1.2.3.4:23, last.0=40", False), (10, 0, "nonce=foo, limit=51, addr.0=1.2.3.4:23, " "last.0=41, addr.1=1.2.3.4:23, last.1=40", False), (10, 0, "nonce=foo, limit=52, addr.0=10.20.30.40:23, " "last.0=42, addr.1=1.2.3.4:23, last.1=41, " "addr.2=1.2.3.4:23, last.2=40", False)]) self.assertEqual(len(result.entries), 2) self.assertEqual(logjig.data, ["1970-01-01T00:00:00Z Frag limit reduced to 16 " "following incomplete response.\n", "1970-01-01T00:00:00Z Reverted to row limit " "from fragments limit.\n", "1970-01-01T00:00:00Z Row limit reduced to 48 " "following incomplete response.\n"]) finally: ntp.util.time = timetemp def test___ordlist(self): queries = [] def doquery_jig(opcode, associd=0, qdata="", auth=False): queries.append((opcode, associd, qdata, auth)) # Init cls = self.target() cls.doquery = doquery_jig # Test cls.response = "foo.0=42, bar.2=songbird" result = cls._ControlSession__ordlist("blah") self.assertEqual(result, [odict((("foo", 42),)), odict(), odict((("bar", "songbird"),))]) self.assertEqual(queries, [(ntp.control.CTL_OP_READ_ORDLIST_A, 0, "blah", True)]) def test_reslist(self): ords = [] def ordlist_jig(listtype): ords.append(listtype) return 23 # Init cls = self.target() cls._ControlSession__ordlist = ordlist_jig # Test result = cls.reslist() self.assertEqual(result, 23) self.assertEqual(ords, ["addr_restrictions"]) def test_ifstats(self): ords = [] def ordlist_jig(listtype): ords.append(listtype) return 23 # Init cls = self.target() cls._ControlSession__ordlist = ordlist_jig # Test result = cls.ifstats() self.assertEqual(result, 23) self.assertEqual(ords, ["ifstats"]) class TestAuthenticator(unittest.TestCase): target = ntpp.Authenticator open_calls = [] open_files = [] open_data = [] def openjig(self, filename): self.open_calls.append(filename) fd = jigs.FileJig() fd.readline_return = self.open_data self.open_files.append(fd) return fd def test___init__(self): try: ntpp.open = self.openjig # Test without file cls = self.target() self.assertEqual(cls.passwords, {}) self.assertEqual(self.open_calls, []) self.assertEqual(self.open_files, []) # Test with file self.open_data = ["# blah blah\n", "0 foo sw0rdf1sh\n", "\n", "1 bar password1 # blargh\n"] cls = self.target("file") self.assertEqual(cls.passwords, {0: ("foo", "sw0rdf1sh"), 1: ("bar", "password1")}) self.assertEqual(self.open_calls, ["file"]) self.assertEqual(len(self.open_files), 1) finally: ntpp.open = open self.open_calls = [] self.open_files = [] self.open_data = [] def test___len__(self): cls = self.target() cls.passwords = {0: ("a", "z"), 2: ("b", "y")} self.assertEqual(cls.__len__(), 2) def test___getitem__(self): cls = self.target() cls.passwords = {0: ("a", "z"), 2: ("b", "y")} self.assertEqual(cls[2], ("b", "y")) def test_control(self): cls = self.target() cls.passwords = {0: ("a", "z"), 2: ("b", "y")} # Keyid in passwords self.assertEqual(cls.control(0), (0, "a", "z")) # Keyid not in passwords self.assertEqual(cls.control(1), (1, None, None)) try: # Read keyid from /etc/ntp.conf ntpp.open = self.openjig self.open_data = ["blah blah", "control 2"] self.assertEqual(cls.control(), (2, "b", "y")) # Fail to read keyid from /etc/ntp.conf self.open_data = ["blah blah"] try: cls.control() errored = False except ValueError: errored = True self.assertEqual(errored, True) finally: ntpp.open = open def test_compute_mac(self): f = self.target.compute_mac try: temphash = ntpp.hashlib fakehashlibmod = jigs.HashlibModuleJig() ntpp.hashlib = fakehashlibmod # Test no digest self.assertEqual(f("", 0, None, polybytes("")), None) # Test with digest self.assertEqual(f("foo", 0x42, "bar", "quux"), polybytes("\x00\x00\x00\x42blahblahblahblah")) finally: ntpp.hashlib = temphash def test_have_mac(self): f = self.target.have_mac # Test under limit pkt = "@" * ntp.magic.LEN_PKT_NOMAC self.assertEqual(f(pkt), False) # Test over limit pkt += "foo" self.assertEqual(f(pkt), True) def test_verify_mac(self): cls = self.target() cls.passwords[0x23] = ("a", "z") good_pkt = "foobar\x00\x00\x00\x23blahblahblahblah" bad_pkt = "foobar\xDE\xAD\xDE\xAFblahblahblah" try: temphash = ntpp.hashlib fakehashlibmod = jigs.HashlibModuleJig() ntpp.hashlib = fakehashlibmod # Test good self.assertEqual(cls.verify_mac(polybytes(good_pkt)), True) # Test bad self.assertEqual(cls.verify_mac(polybytes(bad_pkt)), False) finally: ntpp.hashlib = temphash if __name__ == "__main__": unittest.main() ntpsec-1.1.0+dfsg1/tests/pylib/test_statfiles.py0000644000175000017500000006653313252364117021532 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import ntp.statfiles import jigs import sys class TestPylibStatfiles(unittest.TestCase): def test_iso_to_posix(self): f = ntp.statfiles.iso_to_posix self.assertEqual(f(1480999786), 1480999786) self.assertEqual(f("2016-12-06T04:49:46"), 1480999786) def test_posix_to_iso(self): f = ntp.statfiles.posix_to_iso self.assertEqual(f(1480999786), "2016-12-06T04:49:46") def test_iso_to_posix_inverts_posix_to_iso(self): self.assertEqual(ntp.statfiles.iso_to_posix( ntp.statfiles.posix_to_iso(1480999786)), 1480999786) def test_posix_to_iso_inverts_iso_to_posix(self): self.assertEqual(ntp.statfiles.posix_to_iso( ntp.statfiles.iso_to_posix("2016-12-06T04:49:46")), "2016-12-06T04:49:46") class TestNTPStats(unittest.TestCase): target = ntp.statfiles.NTPStats open_calls = [] open_returns = [] load_stem_calls = [] load_stem_returns = [] process_stem_calls = [] process_stem_returns = [] def open_jig(self, filename, filemode): self.open_calls.append((filename, filemode)) ret = self.open_returns.pop(0) if ret is None: raise IOError else: return ret @staticmethod def load_stem_jig(cls, statsdir, stem): TestNTPStats.load_stem_calls.append((statsdir, stem)) ret = TestNTPStats.load_stem_returns.pop(0) if ret is None: raise IOError return ret @staticmethod def process_stem_jig(cls, stem, lines): TestNTPStats.process_stem_calls.append((stem, lines)) return TestNTPStats.process_stem_returns.pop(0) def test_unixize(self): f = self.target.unixize # Test empty self.assertEqual(f([], 0, 0xFFFFFFFF), []) # Test everything in range self.assertEqual(f(["5 3600", "6 86399", "40587 .125"], -3600000000, 10000), [[-3506281200000, "-3506281200.0"], [-3506112001000, "-3506112001.0"], [125, "0.125"]]) # Test with unparseables, all in range self.assertEqual(f(["heeeey duuuude!", "40588 0"], 0, 86401), [[86400000, "86400.0"]]) # Test something not in range self.assertEqual(f(["40587 0", "40587 1.0625", "40587 86399", "40588 1"], 1, 86400), [[1062, "1.0625"], [86399000, "86399.0"]]) def test_timestamp(self): f = self.target.timestamp # Test self.assertEqual(f("12345.6789 blah blah"), 12345.6789) def test_percentiles(self): f = self.target.percentiles # Test empty self.assertEqual(f([], []), {}) # Test 1 item, empty percentile self.assertEqual(f([], [42]), {}) # Test 1 item, non-empty percentile self.assertEqual(f([10, 90], [42]), {"p10": 42, "p90": 42}) # Test several items, empty percentile self.assertEqual(f([], [1, 23, 42, 99]), {}) # Test several items, non-empty percentile self.assertEqual(f([10, 25, 50, 90, 100], [1, 23, 42, 99]), {"p10": 1, "p25": 23, "p90": 99, "p50": 42, "p100": 99}) def test_ip_label(self): f = self.target.ip_label fakesockmod = jigs.SocketModuleJig() try: socktemp = ntp.statfiles.socket ntp.statfiles.socket = fakesockmod # Test no label self.assertEqual(f("blah"), "blah") # Test hostname, success fakesockmod.ghba_returns = [("result.com", None, None)] self.assertEqual(f("1.2.3.4"), "result.com") # Test hostname, failure fakesockmod.ghba_returns = [None] self.assertEqual(f("1.2.3.4"), "1.2.3.4") # Test old style NTP fakesockmod.ghba_returns = [("foo.org", None, None)] self.assertEqual(f("127.127.42.23"), "REFCLOCK(type=42,unit=23)") finally: ntp.statfiles.socket = socktemp def test___init__(self): # Create jigs fakegzipmod = jigs.GzipModuleJig() fakesockmod = jigs.SocketModuleJig() logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() fakeosmod = jigs.OSModuleJig() fakeglobmod = jigs.GlobModuleJig() load_args = [] load_returns = [] def loadjig(self, statsdir, stem): load_args.append((statsdir, stem)) return load_returns.pop(0) process_args = [] def processjig(self, stem, lines): process_args.append((stem, lines)) return [stem + " " + line for line in lines] try: # Splice in jigs socktemp = ntp.statfiles.socket ntp.statfiles.socket = fakesockmod errtemp = sys.stderr sys.stderr = logjig timetemp = ntp.statfiles.time ntp.statfiles.time = faketimemod ostemp = ntp.statfiles.os ntp.statfiles.os = fakeosmod stemtemp = self.target._NTPStats__load_stem self.target._NTPStats__load_stem = loadjig processtemp = self.target._NTPStats__process_stem self.target._NTPStats__process_stem = processjig # Test simplest TDP = self.target.DefaultPeriod faketimemod.time_returns = [TDP * 2] fakeosmod.path.isdir_returns = [True] load_returns = [["clock0", "clock1"], ["peer0", "peer1"], ["loop0", "loop1"], ["raw0", "raw1"], ["temp0", "temp1"], ["gpsd0", "gpsd1"]] cls = self.target("/foo/bar") self.assertEqual(cls.endtime, TDP * 2) self.assertEqual(cls.starttime, TDP) self.assertEqual(cls.period, TDP) self.assertEqual(cls.sitename, "bar") self.assertEqual(logjig.data, []) self.assertEqual(cls.clockstats, ["clockstats clock0", "clockstats clock1"]) self.assertEqual(cls.peerstats, ["peerstats peer0", "peerstats peer1"]) self.assertEqual(cls.loopstats, ["loopstats loop0", "loopstats loop1"]) self.assertEqual(cls.rawstats, ["rawstats raw0", "rawstats raw1"]) self.assertEqual(cls.temps, ["temps temp0", "temps temp1"]) self.assertEqual(cls.gpsd, ["gpsd gpsd0", "gpsd gpsd1"]) # Test all arguments faketimemod.time_returns = [TDP * 2] fakesockmod.fqdn_returns = ["jabber"] fakeosmod.path.isdir_returns = [True] load_returns = [[], [], [], [], [], []] cls = self.target("/foo/bar", "Sitename", 200, 50, 150) self.assertEqual(cls.endtime, 150) self.assertEqual(cls.starttime, 50) self.assertEqual(cls.period, 200) self.assertEqual(cls.sitename, "Sitename") self.assertEqual(logjig.data, []) self.assertEqual(cls.clockstats, []) self.assertEqual(cls.peerstats, []) self.assertEqual(cls.loopstats, []) self.assertEqual(cls.rawstats, []) self.assertEqual(cls.temps, []) self.assertEqual(cls.gpsd, []) # Test endtime, but no starttime faketimemod.time_returns = [TDP * 2] fakesockmod.fqdn_returns = ["jabber"] fakeosmod.path.isdir_returns = [True] load_returns = [[], [], [], [], [], []] cls = self.target("/foo/bar", "Sitename", 100, endtime=150) self.assertEqual(cls.endtime, 150) self.assertEqual(cls.starttime, 50) self.assertEqual(cls.period, 100) self.assertEqual(cls.sitename, "Sitename") self.assertEqual(logjig.data, []) self.assertEqual(cls.clockstats, []) self.assertEqual(cls.peerstats, []) self.assertEqual(cls.loopstats, []) self.assertEqual(cls.rawstats, []) self.assertEqual(cls.temps, []) self.assertEqual(cls.gpsd, []) # Test endtime, but no starttime faketimemod.time_returns = [TDP * 2] fakesockmod.fqdn_returns = ["jabber"] fakeosmod.path.isdir_returns = [True] load_returns = [[], [], [], [], [], []] cls = self.target("/foo/bar", "Sitename", 100, starttime=150) self.assertEqual(cls.endtime, 250) self.assertEqual(cls.starttime, 150) self.assertEqual(cls.period, 100) self.assertEqual(cls.sitename, "Sitename") self.assertEqual(logjig.data, []) self.assertEqual(cls.clockstats, []) self.assertEqual(cls.peerstats, []) self.assertEqual(cls.loopstats, []) self.assertEqual(cls.rawstats, []) self.assertEqual(cls.temps, []) self.assertEqual(cls.gpsd, []) # Test fqdn sitename logjig.data = [] faketimemod.time_returns = [TDP * 2] fakesockmod.getfqdn_returns = ["jabber"] fakeosmod.path.isdir_returns = [True] load_returns = [[], [], [], [], [], []] cls = self.target("/foo/bar", "ntpstats", 100, 50, 150) self.assertEqual(cls.endtime, 150) self.assertEqual(cls.starttime, 50) self.assertEqual(cls.period, 100) self.assertEqual(cls.sitename, "jabber") self.assertEqual(logjig.data, []) self.assertEqual(cls.clockstats, []) self.assertEqual(cls.peerstats, []) self.assertEqual(cls.loopstats, []) self.assertEqual(cls.rawstats, []) self.assertEqual(cls.temps, []) self.assertEqual(cls.gpsd, []) finally: ntp.statfiles.os = ostemp ntp.statfiles.time = timetemp self.target._NTPStats__load_stem = stemtemp self.target._NTPStats__process_stem = processtemp sys.stderr = errtemp def test___load_statfiles(self): # Create jigs fakegzipmod = jigs.GzipModuleJig() fakesockmod = jigs.SocketModuleJig() logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() fakeosmod = jigs.OSModuleJig() fakeglobmod = jigs.GlobModuleJig() try: # Splice in jigs gziptemp = ntp.statfiles.gzip ntp.statfiles.gzip = fakegzipmod socktemp = ntp.statfiles.socket ntp.statfiles.socket = fakesockmod errtemp = sys.stderr sys.stderr = logjig timetemp = ntp.statfiles.time ntp.statfiles.time = faketimemod ostemp = ntp.statfiles.os ntp.statfiles.os = fakeosmod globtemp = ntp.statfiles.glob ntp.statfiles.glob = fakeglobmod opentemp = open ntp.statfiles.open = self.open_jig prostemp = self.target._NTPStats__process_stem self.target._NTPStats__process_stem = self.process_stem_jig # Set up repetable data TDP = self.target.DefaultPeriod faketimemod.time_returns = [TDP * 2] fakeosmod.path.isdir_returns = [True] getmtime_data = [TDP+1, TDP+2, TDP+3, TDP+4, TDP+5, TDP+6, TDP+7, TDP+8, TDP+9, TDP+10, TDP+11, TDP-1] glob_data = [("/foo/bar/clockstats.0", "/foo/bar/clockstats.1gz"), ("/foo/bar/peerstats.0", "/foo/bar/peerstats.1"), ("/foo/bar/loopstats.0", "/foo/bar/loopstats.1"), ("/foo/bar/rawstats.0", "/foo/bar/rawstats.1"), ("/foo/bar/temps0", "/foo/bar/temps1"), ("/foo/bar/gpsd0", "/foo/bar/gpsd1")] # time kicked # Load data into jigs # need to do it this time so that __init__ will work fakeosmod.path.getmtime_returns = getmtime_data[:] fakeglobmod.glob_returns = glob_data[:] self.open_returns = [jigs.FileJig(["40594 10\n", "40594 11"]), jigs.FileJig(["40594 30\n", "40594 31"]), jigs.FileJig(["40594 40\n", "40594 41"]), jigs.FileJig(["40594 50\n", "40594 51"]), jigs.FileJig(["40594 60\n", "40594 61"]), jigs.FileJig(["40594 70\n", "40594 71"]), jigs.FileJig(["40594 80\n", "40594 81"]), jigs.FileJig(["604801.25 40594 90\n", "#blah", "604802.25 40594 91"]), jigs.FileJig(["604803.25 40594 100\n", "#blah", "604804.25 40594 101"]), jigs.FileJig(["604805.25 40594 110\n", "#blah", "604806.25 40594 111"]), jigs.FileJig(["604807.25 40594 120\n", "#blah", "604808.25 40594 121"])] fakegzipmod.files_returned = [jigs.FileJig(["40594 20\n", "40594 21"])] TestNTPStats.process_stem_returns = [[]] * 6 cls = self.target("/foo/bar", "sitename") # Reload and test fakeosmod.path.getmtime_returns = getmtime_data[:] fakeglobmod.glob_returns = glob_data[:] self.open_returns = [jigs.FileJig(["40594 10\n", "40594 11"]), jigs.FileJig(["40594 30\n", "40594 31"]), jigs.FileJig(["40594 40\n", "40594 41"]), jigs.FileJig(["40594 50\n", "40594 51"]), jigs.FileJig(["40594 60\n", "40594 61"]), jigs.FileJig(["40594 70\n", "40594 71"]), jigs.FileJig(["40594 80\n", "40594 81"]), jigs.FileJig(["604801.25 40594 90\n", "#blah", "604802.25 40594 91"]), jigs.FileJig(["604803.25 40594 100\n", "#blah", "604804.25 40594 101"]), jigs.FileJig(["604805.25 40594 110\n", "#blah", "604806.25 40594 111"]), jigs.FileJig(["604807.25 40594 120\n", "#blah", "604808.25 40594 121"])] fakegzipmod.files_returned = [jigs.FileJig(["40594 20\n", "40594 21"])] TestNTPStats.process_stem_returns = [[]] * 6 self.assertEqual(cls._NTPStats__load_stem("/foo/bar", "clockstats"), ['40594 10\n', '40594 11', '40594 20\n', '40594 21']) self.assertEqual(cls._NTPStats__load_stem("/foo/bar", "peerstats"), ['40594 30\n', '40594 31', '40594 40\n', '40594 41']) self.assertEqual(cls._NTPStats__load_stem("/foo/bar", "loopstats"), ['40594 50\n', '40594 51', '40594 60\n', '40594 61']) self.assertEqual(cls._NTPStats__load_stem("/foo/bar", "rawstats"), ['40594 70\n', '40594 71', '40594 80\n', '40594 81']) self.assertEqual(cls._NTPStats__load_stem("/foo/bar", "temps"), ["604801.25 40594 90\n", "#blah", "604802.25 40594 91", "604803.25 40594 100\n", "#blah", "604804.25 40594 101"]) self.assertEqual(cls._NTPStats__load_stem("/foo/bar", "gpsd"), ['604805.25 40594 110\n', '#blah', '604806.25 40594 111']) finally: ntp.statfiles.gzip = gziptemp ntp.statfiles.socket = socktemp ntp.statfiles.os = ostemp ntp.statfiles.time = timetemp ntp.statfiles.glob = globtemp ntp.statfiles.open = opentemp sys.stderr = errtemp self.target._NTPStats__process_stem = prostemp def test___process_stem_lines(self): try: fakeosmod = jigs.OSModuleJig() ostemp = ntp.statfiles.os ntp.statfiles.os = fakeosmod loadtemp = self.target._NTPStats__load_stem self.target._NTPStats__load_stem = self.load_stem_jig dataOne = ["#clockstats0\n", "40587 5 foo\n", "40587 10 bar\n", "40587 15 baz\n", "#clockstats1\n", "40587 20 quux"] dataTwo = ["#gpsdstats0 foo bar quux\n", "20.5 5 foo\n", "21.5 10 bar\n", "22.5 15 baz\n", "#gpsdstats1\n", "23.5 20 quux"] TestNTPStats.load_stem_returns = [[]] * 6 fakeosmod.path.isdir_returns = [True] * 10 cls = self.target("/foo/bar", "sitename", starttime=0, endtime=(86400 * 7)) self.assertEqual(cls._NTPStats__process_stem("clockstats", dataOne), [[5000, '5.0', 'foo'], [10000, '10.0', 'bar'], [15000, '15.0', 'baz'], [20000, '20.0', 'quux']]) self.assertEqual(cls._NTPStats__process_stem("peerstats", dataOne), [[5000, '5.0', 'foo'], [10000, '10.0', 'bar'], [15000, '15.0', 'baz'], [20000, '20.0', 'quux']]) self.assertEqual(cls._NTPStats__process_stem("loopstats", dataOne), [[5000, '5.0', 'foo'], [10000, '10.0', 'bar'], [15000, '15.0', 'baz'], [20000, '20.0', 'quux']]) self.assertEqual(cls._NTPStats__process_stem("rawstats", dataOne), [[5000, '5.0', 'foo'], [10000, '10.0', 'bar'], [15000, '15.0', 'baz'], [20000, '20.0', 'quux']]) self.assertEqual(cls._NTPStats__process_stem("temps", dataTwo), [[20500, '20.5', '5', 'foo'], [21500, '21.5', '10', 'bar'], [22500, '22.5', '15', 'baz'], [23500, '23.5', '20', 'quux']]) self.assertEqual(cls._NTPStats__process_stem("gpsd", dataTwo), [[20500, '20.5', '5', 'foo'], [21500, '21.5', '10', 'bar'], [22500, '22.5', '15', 'baz'], [23500, '23.5', '20', 'quux']]) finally: self.target._NTPStats__load_stem = loadtemp ntp.statfiles.os = ostemp def test_peersplit(self): # Create jigs fakesockmod = jigs.SocketModuleJig() logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() fakeosmod = jigs.OSModuleJig() try: # Splice in jigs loadtemp = self.target._NTPStats__load_stem self.target._NTPStats__load_stem = self.load_stem_jig prostemp = self.target._NTPStats__process_stem self.target._NTPStats__process_stem = self.process_stem_jig socktemp = ntp.statfiles.socket ntp.statfiles.socket = fakesockmod errtemp = sys.stderr sys.stderr = logjig timetemp = ntp.statfiles.time ntp.statfiles.time = faketimemod ostemp = ntp.statfiles.os ntp.statfiles.os = fakeosmod TDP = self.target.DefaultPeriod faketimemod.time_returns = [TDP * 2] fakeosmod.path.isdir_returns = [True] TestNTPStats.load_stem_returns = [[]] * 6 TestNTPStats.process_stem_returns = [[]] * 6 cls = self.target("/foo/bar") cls.peerstats = [[604830000, "604830.0", "1.2.3.4"], [604831000, "604831.0", "1.2.3.4"], [604840000, "604840.0", "1.2.3.4"], [604841000, "604841.0", "5.6.7.8"]] # Test self.assertEqual(cls.peersplit(), {'1.2.3.4': [[604830000, '604830.0', '1.2.3.4'], [604831000, '604831.0', '1.2.3.4'], [604840000, '604840.0', '1.2.3.4']], '5.6.7.8': [[604841000, '604841.0', '5.6.7.8']]}) self.target.peermap["1.2.3.4"][0][0] = 42 # Test that it uses cache self.assertEqual(cls.peersplit(), {'1.2.3.4': [[42, '604830.0', '1.2.3.4'], [604831000, '604831.0', '1.2.3.4'], [604840000, '604840.0', '1.2.3.4']], '5.6.7.8': [[604841000, '604841.0', '5.6.7.8']]}) finally: ntp.statfiles.socket = socktemp ntp.statfiles.os = ostemp ntp.statfiles.time = timetemp sys.stderr = errtemp self.target._NTPStats__load_stem = loadtemp self.target._NTPStats__process_stem = prostemp def test_gpssplit(self): # Create jigs fakesockmod = jigs.SocketModuleJig() logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() fakeosmod = jigs.OSModuleJig() try: # Splice in jigs loadtemp = self.target._NTPStats__load_stem self.target._NTPStats__load_stem = self.load_stem_jig prostemp = self.target._NTPStats__process_stem self.target._NTPStats__process_stem = self.process_stem_jig socktemp = ntp.statfiles.socket ntp.statfiles.socket = fakesockmod errtemp = sys.stderr sys.stderr = logjig timetemp = ntp.statfiles.time ntp.statfiles.time = faketimemod ostemp = ntp.statfiles.os ntp.statfiles.os = fakeosmod TDP = self.target.DefaultPeriod faketimemod.time_returns = [TDP * 2] fakeosmod.path.isdir_returns = [True] TestNTPStats.load_stem_returns = [[]] * 6 TestNTPStats.process_stem_returns = [[]] * 6 cls = self.target("/foo/bar") cls.gpsd = [["604801.25", "40594", "90"], ["604802.25", "40594", "90"], ["argh!"], ["604803.25", "40594", "100"], ["604804.25", "40594", "100"], ["604804.25", "40594", "110"], ["604804.25", "40594", "110"], ["604804.25", "40594", "120"], ["604804.25", "40594", "120"]] # Test self.assertEqual(cls.gpssplit(), {'90': [['604801.25', '40594', '90'], ['604802.25', '40594', '90']], '100': [['604803.25', '40594', '100'], ['604804.25', '40594', '100']], '110': [['604804.25', '40594', '110'], ['604804.25', '40594', '110']], '120': [['604804.25', '40594', '120'], ['604804.25', '40594', '120']]}) finally: ntp.statfiles.socket = socktemp ntp.statfiles.os = ostemp ntp.statfiles.time = timetemp sys.stderr = errtemp self.target._NTPStats__load_stem = loadtemp self.target._NTPStats__process_stem = prostemp def test_tempssplit(self): # Create jigs fakesockmod = jigs.SocketModuleJig() logjig = jigs.FileJig() faketimemod = jigs.TimeModuleJig() fakeosmod = jigs.OSModuleJig() try: # Splice in jigs loadtemp = self.target._NTPStats__load_stem self.target._NTPStats__load_stem = self.load_stem_jig prostemp = self.target._NTPStats__process_stem self.target._NTPStats__process_stem = self.process_stem_jig socktemp = ntp.statfiles.socket ntp.statfiles.socket = fakesockmod errtemp = sys.stderr sys.stderr = logjig timetemp = ntp.statfiles.time ntp.statfiles.time = faketimemod ostemp = ntp.statfiles.os ntp.statfiles.os = fakeosmod TDP = self.target.DefaultPeriod faketimemod.time_returns = [TDP * 2] fakeosmod.path.isdir_returns = [True] TestNTPStats.load_stem_returns = [[]] * 6 TestNTPStats.process_stem_returns = [[]] * 6 cls = self.target("/foo/bar") cls.temps = [["604801.25", "40594", "90"], ["604802.25", "40594", "90"], ["argh!"], ["604803.25", "40594", "100"], ["604804.25", "40594", "100"], ["604804.25", "40594", "110"], ["604804.25", "40594", "110"], ["604804.25", "40594", "120"], ["604804.25", "40594", "120"]] # Test self.assertEqual(cls.tempssplit(), {'90': [['604801.25', '40594', '90'], ['604802.25', '40594', '90']], '100': [['604803.25', '40594', '100'], ['604804.25', '40594', '100']], '110': [['604804.25', '40594', '110'], ['604804.25', '40594', '110']], '120': [['604804.25', '40594', '120'], ['604804.25', '40594', '120']]}) finally: ntp.statfiles.socket = socktemp ntp.statfiles.os = ostemp ntp.statfiles.time = timetemp sys.stderr = errtemp self.target._NTPStats__load_stem = loadtemp self.target._NTPStats__process_stem = prostemp if __name__ == '__main__': unittest.main() ntpsec-1.1.0+dfsg1/tests/pylib/jigs.py0000644000175000017500000002441113252364117017416 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function, division import socket import select import os.path import time master_encoding = 'latin-1' if str is bytes: # Python 2 polystr = str polybytes = bytes polyord = ord polychr = str input = raw_input def string_escape(s): return s.decode('string_escape') else: # Python 3 import io 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 polyord(c): "Polymorphic ord() function" if isinstance(c, str): return ord(c) else: return c def polychr(c): "Polymorphic chr() function" if isinstance(c, int): return chr(c) else: return c 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_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) class FileJig: def __init__(self, returns=[""]): self.data = [] self.flushed = False self.readline_return = returns def __enter__(self): return self def __exit__(self, *args): return False def __iter__(self): return self.readline_return.__iter__() def write(self, data): self.data.append(data) self.flushed = False def flush(self): self.flushed = True def readline(self): if len(self.readline_return) > 0: return self.readline_return.pop(0) return "" def readlines(self): ret = self.readline_return self.readline_return = [] return ret class SocketJig: def __init__(self): self.data = [] self.return_data = [] self.closed = False self.connected = None self.fail_connect = False self.fail_send = 0 def sendall(self, data): if self.fail_send > 0: self.fail_send -= 1 raise socket.error() self.data.append(data) def close(self): self.closed = True def connect(self, addr): if self.fail_connect is True: err = socket.error() err.strerror = "socket!" err.errno = 16 raise err self.connected = addr def recv(self, bytecount): if len(self.return_data) > 0: current = self.return_data.pop(0) if len(current) > bytecount: ret = current[:bytecount] current = current[bytecount:] self.return_data.insert(0, current) # push unwanted data return ret else: return current return None class HasherJig: def __init__(self): self.update_calls = [] self.digest_size = 0 def update(self, data): self.update_calls.append(data) if len(data) > 0: self.digest_size += 1 def digest(self): return polybytes("blah" * 4) # 16 byte hash class SocketModuleJig: error = socket.error gaierror = socket._socket.gaierror herror = socket.herror SOCK_DGRAM = socket.SOCK_DGRAM IPPROTO_UDP = socket.IPPROTO_UDP AF_UNSPEC = socket.AF_UNSPEC AI_NUMERICHOST = socket.AI_NUMERICHOST AI_CANONNAME = socket.AI_CANONNAME EAI_NONAME = socket.EAI_NONAME if hasattr(socket, "EAI_NODATA"): EAI_NODATA = socket.EAI_NODATA else: # FreeBSD does not have EAI_NODATA (removed from POSIX) EAI_NODATA = None NI_NAMEREQD = socket.NI_NAMEREQD def __init__(self): self.gai_calls = [] self.gai_error_count = 0 self.gai_returns = [] self.gni_calls = [] self.gni_error_count = 0 self.gni_returns = [] self.socket_calls = [] self.socket_fail = False self.socket_fail_connect = False self.socketsReturned = [] self.inet_ntop_calls = [] self.getfqdn_calls = [] self.getfqdn_returns = [] self.ghba_calls = [] self.ghba_returns = [] def getaddrinfo(self, host, port, family=None, socktype=None, proto=None, flags=None): self.gai_calls.append((host, port, family, socktype, proto, flags)) if self.gai_error_count > 0: self.gai_error_count -= 1 err = self.gaierror("blah") err.errno = socket.EAI_NONAME raise err return self.gai_returns.pop(0) def getnameinfo(self, addr, flags): self.gni_calls.append((addr, flags)) if self.gni_error_count > 0: self.gni_error_count -= 1 err = self.gaierror("blah") err.errno = socket.EAI_NONAME raise err return self.gni_returns.pop(0) def socket(self, family, socktype, protocol): self.socket_calls.append((family, socktype, protocol)) if self.socket_fail is True: err = self.error() err.strerror = "error!" err.errno = 23 raise err sock = SocketJig() if self.socket_fail_connect is True: sock.fail_connect = True self.socketsReturned.append(sock) return sock def inet_ntop(self, addr, family): self.inet_ntop_calls.append((addr, family)) return "canon.com" def getfqdn(self, name=""): self.getfqdn_calls.append(name) return self.getfqdn_returns.pop(0) def gethostbyaddr(self, addr): self.ghba_calls.append(addr) ret = self.ghba_returns.pop(0) if ret is None: raise self.herror return ret class GetpassModuleJig: def __init__(self): self.getpass_calls = [] self.getpass_returns = [] def getpass(self, prompt, stream=None): self.getpass_calls.append((prompt, stream)) return self.getpass_returns.pop(0) class HashlibModuleJig: def __init__(self): self.new_calls = [] self.hashers_returned = [] def new(self, name): self.new_calls.append(name) h = HasherJig() self.hashers_returned.append(h) return h class SelectModuleJig: error = select.error def __init__(self): self.select_calls = [] self.select_fail = 0 self.do_return = [] self.fqdn_calls = [] self.fqdn_returns = [] def select(self, ins, outs, excepts, timeout=0): self.select_calls.append((ins, outs, excepts, timeout)) if self.select_fail > 0: self.select_fail -= 1 raise select.error if len(self.do_return) == 0: # simplify code that doesn't need it self.do_return.append(True) doreturn = self.do_return.pop(0) if doreturn is True: return (ins, [], []) else: return ([], [], []) def getfqdn(self, name=""): self.fqdn_calls.append(name) return self.fqdn_returns.pop(0) class path_mod: def __init__(self): self.isdir_calls = [] self.isdir_returns = [] self.getmtime_calls = [] self.getmtime_returns = [] self.join_calls = [] def isdir(self, dirname): self.isdir_calls.append(dirname) return self.isdir_returns.pop(0) def join(self, *args): self.join_calls.append(args) return os.path.join(*args) def basename(self, pathname): self.join_calls.append(pathname) return os.path.basename(pathname) def getmtime(self, filename): self.getmtime_calls.append(filename) return self.getmtime_returns.pop(0) class OSModuleJig: def __init__(self): self.isatty_calls = [] self.isatty_returns = [] self.path = path_mod() # Need os.path def isatty(self, fd): self.isatty_calls.append(fd) return self.isatty_returns.pop(0) class FcntlModuleJig: def __init__(self): self.ioctl_calls = [] self.ioctl_returns = [] def ioctl(self, fd, op, arg=0, mutate_flag=False): self.ioctl_calls.append((fd, op, arg, mutate_flag)) return self.ioctl_returns.pop(0) class ShutilModuleJig: def __init__(self): self.gts_calls = [] self.gts_returns = [] def get_terminal_size(self, default=(80, 24)): self.gts_calls.append(default) return self.gts_returns.pop(0) class TimeModuleJig: def __init__(self): self.time_calls = 0 self.time_returns = [] def time(self): self.time_calls += 1 return self.time_returns.pop(0) def strftime(self, f, t=None): if t is None: return time.strftime(f) else: return time.strftime(f, t) def gmtime(self, *args, **kwargs): return time.gmtime(*args, **kwargs) class GzipModuleJig: def __init__(self): self.open_calls = [] self.files_returned = [] def open(self, filename, filemode): self.open_calls.append((filename, filemode)) return self.files_returned.pop(0) class GlobModuleJig: def __init__(self): self.glob_calls = [] self.glob_returns = [] def glob(self, pathname): self.glob_calls.append(pathname) ret = self.glob_returns.pop(0) return ret ntpsec-1.1.0+dfsg1/tests/pylib/test_agentx_packet.py0000644000175000017500000037717213252364117022355 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import ntp.agentx_packet as AX from ntp.agentx_packet import slicedata, decode_pduheader, makeflags extraData = b"Would you kindly ignore this?" maximumOIDsubs = tuple(range(1, 129)) maximumOIDstr = b"""\x80\x00\x00\x00\ \x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\ \x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\ \x00\x00\x00\x09\x00\x00\x00\x0A\x00\x00\x00\x0B\x00\x00\x00\x0C\ \x00\x00\x00\x0D\x00\x00\x00\x0E\x00\x00\x00\x0F\x00\x00\x00\x10\ \ \x00\x00\x00\x11\x00\x00\x00\x12\x00\x00\x00\x13\x00\x00\x00\x14\ \x00\x00\x00\x15\x00\x00\x00\x16\x00\x00\x00\x17\x00\x00\x00\x18\ \x00\x00\x00\x19\x00\x00\x00\x1A\x00\x00\x00\x1B\x00\x00\x00\x1C\ \x00\x00\x00\x1D\x00\x00\x00\x1E\x00\x00\x00\x1F\x00\x00\x00\x20\ \ \x00\x00\x00\x21\x00\x00\x00\x22\x00\x00\x00\x23\x00\x00\x00\x24\ \x00\x00\x00\x25\x00\x00\x00\x26\x00\x00\x00\x27\x00\x00\x00\x28\ \x00\x00\x00\x29\x00\x00\x00\x2A\x00\x00\x00\x2B\x00\x00\x00\x2C\ \x00\x00\x00\x2D\x00\x00\x00\x2E\x00\x00\x00\x2F\x00\x00\x00\x30\ \ \x00\x00\x00\x31\x00\x00\x00\x32\x00\x00\x00\x33\x00\x00\x00\x34\ \x00\x00\x00\x35\x00\x00\x00\x36\x00\x00\x00\x37\x00\x00\x00\x38\ \x00\x00\x00\x39\x00\x00\x00\x3A\x00\x00\x00\x3B\x00\x00\x00\x3C\ \x00\x00\x00\x3D\x00\x00\x00\x3E\x00\x00\x00\x3F\x00\x00\x00\x40\ \ \x00\x00\x00\x41\x00\x00\x00\x42\x00\x00\x00\x43\x00\x00\x00\x44\ \x00\x00\x00\x45\x00\x00\x00\x46\x00\x00\x00\x47\x00\x00\x00\x48\ \x00\x00\x00\x49\x00\x00\x00\x4A\x00\x00\x00\x4B\x00\x00\x00\x4C\ \x00\x00\x00\x4D\x00\x00\x00\x4E\x00\x00\x00\x4F\x00\x00\x00\x50\ \ \x00\x00\x00\x51\x00\x00\x00\x52\x00\x00\x00\x53\x00\x00\x00\x54\ \x00\x00\x00\x55\x00\x00\x00\x56\x00\x00\x00\x57\x00\x00\x00\x58\ \x00\x00\x00\x59\x00\x00\x00\x5A\x00\x00\x00\x5B\x00\x00\x00\x5C\ \x00\x00\x00\x5D\x00\x00\x00\x5E\x00\x00\x00\x5F\x00\x00\x00\x60\ \ \x00\x00\x00\x61\x00\x00\x00\x62\x00\x00\x00\x63\x00\x00\x00\x64\ \x00\x00\x00\x65\x00\x00\x00\x66\x00\x00\x00\x67\x00\x00\x00\x68\ \x00\x00\x00\x69\x00\x00\x00\x6A\x00\x00\x00\x6B\x00\x00\x00\x6C\ \x00\x00\x00\x6D\x00\x00\x00\x6E\x00\x00\x00\x6F\x00\x00\x00\x70\ \ \x00\x00\x00\x71\x00\x00\x00\x72\x00\x00\x00\x73\x00\x00\x00\x74\ \x00\x00\x00\x75\x00\x00\x00\x76\x00\x00\x00\x77\x00\x00\x00\x78\ \x00\x00\x00\x79\x00\x00\x00\x7A\x00\x00\x00\x7B\x00\x00\x00\x7C\ \x00\x00\x00\x7D\x00\x00\x00\x7E\x00\x00\x00\x7F\x00\x00\x00\x80\ """ # The most commonly used flag setups, some tests use custom flags standardFlags_bare = makeflags(False, False, False, False, True) standardFlags = {"flags": standardFlags_bare} lilEndianFlags = {"flags": makeflags(False, False, False, False, False)} contextFlags = {"flags": makeflags(False, False, False, True, True)} def test_pducore(tester, pdu, pduType, endian, sID, tactID, pktID): tester.assertEqual(pdu.pduType, pduType) tester.assertEqual(pdu.bigEndian, endian) tester.assertEqual(pdu.sessionID, sID) tester.assertEqual(pdu.transactionID, tactID) tester.assertEqual(pdu.packetID, pktID) class TestNtpclientsNtpsnmpd(unittest.TestCase): # # PDU tests # def test_AgentXPDU(self): cls = AX.AgentXPDU # Test these so we don't need a bunch of redundant tests # Test basic, without context test = cls(0, True, 1, 2, 3, context=extraData) self.assertEqual(repr(test), "AgentXPDU(bigEndian=True, packetID=3, " "pduType=0, sessionID=1, transactionID=2)") # Test basic, with context test = cls(0, True, 1, 2, 3, hascontext=True) self.assertEqual(repr(test), "AgentXPDU(bigEndian=True, context=None, packetID=3, " "pduType=0, sessionID=1, transactionID=2)") # Test with added variables test.foo = 42 test._wyk = extraData # this should be ignored test.context = "jabber jabber jabber" self.assertEqual(repr(test), "AgentXPDU(bigEndian=True, " "context='jabber jabber jabber', " "foo=42, packetID=3, " "pduType=0, sessionID=1, transactionID=2)") # Test __eq__ a = cls(0, True, 1, 2, 3) b = cls(0, True, 1, 2, 3) # Test all equal self.assertEqual(a == b, True) # Test same class, false b = "blah blah" self.assertEqual(a == b, False) # Test different pdu b = cls(1, True, 1, 2, 3) self.assertEqual(a == b, False) # Test different endianness b = cls(0, False, 1, 2, 3) self.assertEqual(a == b, False) # Test different session ID b = cls(0, True, 42, 2, 3) self.assertEqual(a == b, False) # Test different transaction ID b = cls(0, True, 1, 23, 3) self.assertEqual(a == b, False) # Test different packet ID b = cls(0, True, 1, 2, 13) self.assertEqual(a == b, False) # Test different _hascontext b = cls(0, True, 1, 2, 3, hascontext=True) self.assertEqual(a == b, False) # Test with context, equal a = cls(0, True, 1, 2, 3, True, "foo") b = cls(0, True, 1, 2, 3, True, "foo") self.assertEqual(a == b, True) # Test with context, not equal b = cls(0, True, 1, 2, 3, True, "bar") self.assertEqual(a == b, False) def test_OpenPDU(self): dec = AX.decode_OpenPDU cls = AX.OpenPDU # Test PDU init, null packet nullPkt = cls(True, 1, 2, 3, 4, (), "") test_pducore(self, nullPkt, AX.PDU_OPEN, True, 1, 2, 3) self.assertEqual(nullPkt.timeout, 4) self.assertEqual(nullPkt.oid, AX.OID((), False)) self.assertEqual(nullPkt.description, "") # Test PDU init, basic packet basicPkt = cls(False, 1, 2, 3, 4, (1, 2, 3, 4), "foo") test_pducore(self, basicPkt, AX.PDU_OPEN, False, 1, 2, 3) self.assertEqual(basicPkt.timeout, 4) self.assertEqual(basicPkt.oid, AX.OID((1, 2, 3, 4), False)) self.assertEqual(basicPkt.description, "foo") # Test encoding, null packet nullPkt_str = nullPkt.encode() self.assertEqual(nullPkt_str, b"\x01\x01\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x0C" b"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") # Test encoding, basic packet basicPkt_str = basicPkt.encode() self.assertEqual(basicPkt_str, b"\x01\x01\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x20\x00\x00\x00" b"\x04\x00\x00\x00" b"\x04\x00\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x03\x00\x00\x00foo\x00") # Test decoding, null packet header, body = slicedata(nullPkt_str, 20) header = decode_pduheader(header) nullPkt_new = dec(body, header) test_pducore(self, nullPkt_new, AX.PDU_OPEN, True, 1, 2, 3) self.assertEqual(nullPkt_new.timeout, 4) self.assertEqual(nullPkt_new.oid, AX.OID((), False)) self.assertEqual(nullPkt_new.description, "") # Test decoding, basic packet header, body = slicedata(basicPkt_str, 20) header = decode_pduheader(header) basicPkt_new = dec(body, header) test_pducore(self, basicPkt_new, AX.PDU_OPEN, False, 1, 2, 3) self.assertEqual(basicPkt_new.timeout, 4) self.assertEqual(basicPkt_new.oid, AX.OID((1, 2, 3, 4), False)) self.assertEqual(basicPkt_new.description, "foo") # Test packetVars self.assertEqual(basicPkt_new.packetVars(), {"pduType": 1, "bigEndian": False, "sessionID": 1, "transactionID": 2, "packetID": 3, "timeout": 4, "oid": AX.OID((1, 2, 3, 4), False), "description": "foo"}) def test_ClosePDU(self): dec = AX.decode_ClosePDU cls = AX.ClosePDU # Test init pkt = cls(True, 1, 2, 3, AX.RSN_OTHER) test_pducore(self, pkt, AX.PDU_CLOSE, True, 1, 2, 3) self.assertEqual(pkt.reason, AX.RSN_OTHER) # Test init, little endian pkt_LE = cls(False, 1, 2, 3, AX.RSN_OTHER) test_pducore(self, pkt_LE, AX.PDU_CLOSE, False, 1, 2, 3) self.assertEqual(pkt_LE.reason, AX.RSN_OTHER) # Test encoding pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x02\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x01\x00\x00\x00") # Test encoding, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x02\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x01\x00\x00\x00") # Test decoding header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_CLOSE, True, 1, 2, 3) self.assertEqual(pkt_new.reason, AX.RSN_OTHER) # Test decoding, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_CLOSE, False, 1, 2, 3) self.assertEqual(pkt_LE_new.reason, AX.RSN_OTHER) # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 2, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "reason": AX.RSN_OTHER}) def test_RegisterPDU(self): dec = AX.decode_xRegisterPDU cls = AX.RegisterPDU # Test init, basic packet basicPkt = cls(True, 1, 2, 3, 4, 5, (1, 2, 3)) test_pducore(self, basicPkt, AX.PDU_REGISTER, True, 1, 2, 3) self.assertEqual(basicPkt.timeout, 4) self.assertEqual(basicPkt.priority, 5) self.assertEqual(basicPkt.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt.rangeSubid, 0) self.assertEqual(basicPkt.upperBound, None) self.assertEqual(basicPkt.context, None) # Test init, basic packet, little endian basicPkt_LE = cls(False, 1, 2, 3, 4, 5, (1, 2, 3)) test_pducore(self, basicPkt_LE, AX.PDU_REGISTER, False, 1, 2, 3) self.assertEqual(basicPkt_LE.timeout, 4) self.assertEqual(basicPkt_LE.priority, 5) self.assertEqual(basicPkt_LE.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt_LE.rangeSubid, 0) self.assertEqual(basicPkt_LE.upperBound, None) self.assertEqual(basicPkt_LE.context, None) # Test init, fancy packet fancyPkt = cls(True, 1, 2, 3, 4, 5, (1, 2, 3), rangeSubid=5, upperBound=23, context="blah") test_pducore(self, fancyPkt, AX.PDU_REGISTER, True, 1, 2, 3) self.assertEqual(fancyPkt.timeout, 4) self.assertEqual(fancyPkt.priority, 5) self.assertEqual(fancyPkt.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(fancyPkt.rangeSubid, 5) self.assertEqual(fancyPkt.upperBound, 23) self.assertEqual(fancyPkt.context, "blah") # Test encode, basic packet basicPkt_str = basicPkt.encode() self.assertEqual(basicPkt_str, b"\x01\x03\x11\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x14" b"\x04\x05\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03") # Test encode, basic packet, little endian basicPkt_LE_str = basicPkt_LE.encode() self.assertEqual(basicPkt_LE_str, b"\x01\x03\x01\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x14\x00\x00\x00" b"\x04\x05\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00") # Test encode, fancy packet fancyPkt_str = fancyPkt.encode() self.assertEqual(fancyPkt_str, b"\x01\x03\x19\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x20" b"\x00\x00\x00\x04blah" b"\x04\x05\x05\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x17") # Test decoding, basic packet header, body = slicedata(basicPkt_str, 20) header = decode_pduheader(header) basicPkt_new = dec(body, header) test_pducore(self, basicPkt_new, AX.PDU_REGISTER, True, 1, 2, 3) self.assertEqual(basicPkt_new.timeout, 4) self.assertEqual(basicPkt_new.priority, 5) self.assertEqual(basicPkt_new.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt_new.rangeSubid, 0) self.assertEqual(basicPkt_new.upperBound, None) self.assertEqual(basicPkt_new.context, None) # Test decoding, basic packet, little endian header, body = slicedata(basicPkt_LE_str, 20) header = decode_pduheader(header) basicPkt_LE_new = dec(body, header) test_pducore(self, basicPkt_LE_new, AX.PDU_REGISTER, False, 1, 2, 3) self.assertEqual(basicPkt_LE_new.timeout, 4) self.assertEqual(basicPkt_LE_new.priority, 5) self.assertEqual(basicPkt_LE_new.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt_LE_new.rangeSubid, 0) self.assertEqual(basicPkt_LE_new.upperBound, None) self.assertEqual(basicPkt_LE_new.context, None) # Test decoding, fancy packet header, body = slicedata(fancyPkt_str, 20) header = decode_pduheader(header) fancyPkt_new = dec(body, header) test_pducore(self, fancyPkt_new, AX.PDU_REGISTER, True, 1, 2, 3) self.assertEqual(fancyPkt_new.timeout, 4) self.assertEqual(fancyPkt_new.priority, 5) self.assertEqual(fancyPkt_new.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(fancyPkt_new.rangeSubid, 5) self.assertEqual(fancyPkt_new.upperBound, 23) self.assertEqual(fancyPkt_new.context, "blah") # Test packetVars self.assertEqual(basicPkt_new.packetVars(), {"pduType": 3, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "timeout": 4, "priority": 5, "subtree": AX.OID((1, 2, 3), False), "rangeSubid": 0, "upperBound": None, "context": None}) # Test __eq__ gap, equal a = cls(True, 1, 2, 3, 4, 5, ()) b = cls(True, 1, 2, 3, 4, 5, ()) self.assertEqual(a == b, True) # Test __eq__ gap, unequal b = cls(False, 1, 2, 3, 4, 5, ()) self.assertEqual(a == b, False) def test_UnregisterPDU(self): dec = AX.decode_xRegisterPDU cls = AX.UnregisterPDU # Test init, basic packet basicPkt = cls(True, 1, 2, 3, 5, (1, 2, 3)) test_pducore(self, basicPkt, AX.PDU_UNREGISTER, True, 1, 2, 3) self.assertEqual(basicPkt.priority, 5) self.assertEqual(basicPkt.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt.rangeSubid, 0) self.assertEqual(basicPkt.upperBound, None) self.assertEqual(basicPkt.context, None) # Test init, basic packet, little endian basicPkt_LE = cls(False, 1, 2, 3, 5, (1, 2, 3)) test_pducore(self, basicPkt_LE, AX.PDU_UNREGISTER, False, 1, 2, 3) self.assertEqual(basicPkt_LE.priority, 5) self.assertEqual(basicPkt_LE.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt_LE.rangeSubid, 0) self.assertEqual(basicPkt_LE.upperBound, None) self.assertEqual(basicPkt_LE.context, None) # Test init, fancy packet fancyPkt = cls(True, 1, 2, 3, 5, (1, 2, 3), rangeSubid=5, upperBound=23, context="blah") test_pducore(self, fancyPkt, AX.PDU_UNREGISTER, True, 1, 2, 3) self.assertEqual(fancyPkt.priority, 5) self.assertEqual(fancyPkt.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(fancyPkt.rangeSubid, 5) self.assertEqual(fancyPkt.upperBound, 23) self.assertEqual(fancyPkt.context, "blah") # Test encode, basic packet basicPkt_str = basicPkt.encode() self.assertEqual(basicPkt_str, b"\x01\x04\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x14" b"\x00\x05\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03") # Test encode, basic packet, little endian basicPkt_LE_str = basicPkt_LE.encode() self.assertEqual(basicPkt_LE_str, b"\x01\x04\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x14\x00\x00\x00" b"\x00\x05\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00") # Test encode, fancy packet fancyPkt_str = fancyPkt.encode() self.assertEqual(fancyPkt_str, b"\x01\x04\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x20" b"\x00\x00\x00\x04blah" b"\x00\x05\x05\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x17") # Test decoding, basic packet header, body = slicedata(basicPkt_str, 20) header = decode_pduheader(header) basicPkt_new = dec(body, header) test_pducore(self, basicPkt, AX.PDU_UNREGISTER, True, 1, 2, 3) self.assertEqual(basicPkt_new.priority, 5) self.assertEqual(basicPkt_new.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt_new.rangeSubid, 0) self.assertEqual(basicPkt_new.upperBound, None) self.assertEqual(basicPkt_new.context, None) # Test decoding, basic packet, little endian header, body = slicedata(basicPkt_LE_str, 20) header = decode_pduheader(header) basicPkt_LE_new = dec(body, header) test_pducore(self, basicPkt_LE, AX.PDU_UNREGISTER, False, 1, 2, 3) self.assertEqual(basicPkt_LE_new.priority, 5) self.assertEqual(basicPkt_LE_new.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(basicPkt_LE_new.rangeSubid, 0) self.assertEqual(basicPkt_LE_new.upperBound, None) self.assertEqual(basicPkt_LE_new.context, None) # Test decoding, fancy packet header, body = slicedata(fancyPkt_str, 20) header = decode_pduheader(header) fancyPkt_new = dec(body, header) test_pducore(self, fancyPkt_new, AX.PDU_UNREGISTER, True, 1, 2, 3) self.assertEqual(fancyPkt_new.priority, 5) self.assertEqual(fancyPkt_new.subtree, AX.OID((1, 2, 3), False)) self.assertEqual(fancyPkt_new.rangeSubid, 5) self.assertEqual(fancyPkt_new.upperBound, 23) self.assertEqual(fancyPkt_new.context, "blah") # Test packetVars self.assertEqual(basicPkt_new.packetVars(), {"pduType": 4, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "priority": 5, "subtree": AX.OID((1, 2, 3), False), "rangeSubid": 0, "upperBound": None, "context": None}) def test_GetPDU(self): dec = AX.decode_xGetPDU cls = AX.GetPDU srch = AX.SearchRange # Test init, null packet nullPkt = cls(True, 1, 2, 3, ()) test_pducore(self, nullPkt, AX.PDU_GET, True, 1, 2, 3) self.assertEqual(nullPkt.oidranges, ()) self.assertEqual(nullPkt.context, None) # Test init, full packet fullPkt = cls(True, 1, 2, 3, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True)), context="blah") test_pducore(self, fullPkt, AX.PDU_GET, True, 1, 2, 3) self.assertEqual(fullPkt.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt.context, "blah") # Test init, full packet, little endian fullPkt_LE = cls(False, 1, 2, 3, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True)), context="blah") test_pducore(self, fullPkt_LE, AX.PDU_GET, False, 1, 2, 3) self.assertEqual(fullPkt_LE.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt_LE.context, "blah") # Test encode, null packet nullPkt_str = nullPkt.encode() self.assertEqual(nullPkt_str, b"\x01\x05\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00") # Test encode, full packet fullPkt_str = fullPkt.encode() self.assertEqual(fullPkt_str, b"\x01\x05\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x40" b"\x00\x00\x00\x04blah" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x05" b"\x02\x00\x01\x00\x00\x00\x00\x0A\x00\x00\x00\x14" b"\x02\x00\x00\x00\x00\x00\x00\x1E\x00\x00\x00\x28") # Test encode, full packet, little endian fullPkt_LE_str = fullPkt_LE.encode() self.assertEqual(fullPkt_LE_str, b"\x01\x05\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x40\x00\x00\x00" b"\x04\x00\x00\x00blah" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x05\x00\x00\x00" b"\x02\x00\x01\x00\x0A\x00\x00\x00\x14\x00\x00\x00" b"\x02\x00\x00\x00\x1E\x00\x00\x00\x28\x00\x00\x00") # Test decoding, null packet header, body = slicedata(nullPkt_str, 20) header = decode_pduheader(header) nullPkt_new = dec(body, header) test_pducore(self, nullPkt_new, AX.PDU_GET, True, 1, 2, 3) self.assertEqual(nullPkt_new.oidranges, ()) self.assertEqual(nullPkt_new.context, None) # Test decoding, full packet header, body = slicedata(fullPkt_str, 20) header = decode_pduheader(header) fullPkt_new = dec(body, header) test_pducore(self, fullPkt_new, AX.PDU_GET, True, 1, 2, 3) self.assertEqual(fullPkt_new.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt_new.context, "blah") # Test decoding, full packet, little endian header, body = slicedata(fullPkt_LE_str, 20) header = decode_pduheader(header) fullPkt_LE_new = dec(body, header) test_pducore(self, fullPkt_LE_new, AX.PDU_GET, False, 1, 2, 3) self.assertEqual(fullPkt_LE_new.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt_LE_new.context, "blah") # Test packetVars self.assertEqual(nullPkt_new.packetVars(), {"pduType": 5, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "oidranges": (), "context": None}) def test_GetNextPDU(self): dec = AX.decode_xGetPDU cls = AX.GetNextPDU srch = AX.SearchRange # Test init, null packet nullPkt = cls(True, 1, 2, 3, ()) test_pducore(self, nullPkt, AX.PDU_GET_NEXT, True, 1, 2, 3) self.assertEqual(nullPkt.oidranges, ()) self.assertEqual(nullPkt.context, None) # Test init, full packet fullPkt = cls(True, 1, 2, 3, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True)), context="blah") test_pducore(self, fullPkt, AX.PDU_GET_NEXT, True, 1, 2, 3) self.assertEqual(fullPkt.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt.context, "blah") # Test init, full packet, little endian fullPkt_LE = cls(False, 1, 2, 3, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True)), context="blah") test_pducore(self, fullPkt_LE, AX.PDU_GET_NEXT, False, 1, 2, 3) self.assertEqual(fullPkt_LE.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt_LE.context, "blah") # Test encode, null packet nullPkt_str = nullPkt.encode() self.assertEqual(nullPkt_str, b"\x01\x06\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00") # Test encode, full packet fullPkt_str = fullPkt.encode() self.assertEqual(fullPkt_str, b"\x01\x06\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x40" b"\x00\x00\x00\x04blah" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x05" b"\x02\x00\x01\x00\x00\x00\x00\x0A\x00\x00\x00\x14" b"\x02\x00\x00\x00\x00\x00\x00\x1E\x00\x00\x00\x28") # Test encode, full packet, little endian fullPkt_LE_str = fullPkt_LE.encode() self.assertEqual(fullPkt_LE_str, b"\x01\x06\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x40\x00\x00\x00" b"\x04\x00\x00\x00blah" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x05\x00\x00\x00" b"\x02\x00\x01\x00\x0A\x00\x00\x00\x14\x00\x00\x00" b"\x02\x00\x00\x00\x1E\x00\x00\x00\x28\x00\x00\x00") # Test decoding, null packet header, body = slicedata(nullPkt_str, 20) header = decode_pduheader(header) nullPkt_new = dec(body, header) test_pducore(self, nullPkt_new, AX.PDU_GET_NEXT, True, 1, 2, 3) self.assertEqual(nullPkt_new.oidranges, ()) self.assertEqual(nullPkt_new.context, None) # Test decoding, full packet header, body = slicedata(fullPkt_str, 20) header = decode_pduheader(header) fullPkt_new = dec(body, header) test_pducore(self, fullPkt_new, AX.PDU_GET_NEXT, True, 1, 2, 3) self.assertEqual(fullPkt_new.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt_new.context, "blah") # Test decoding, full packet, little endian header, body = slicedata(fullPkt_LE_str, 20) header = decode_pduheader(header) fullPkt_LE_new = dec(body, header) test_pducore(self, fullPkt_LE_new, AX.PDU_GET_NEXT, False, 1, 2, 3) self.assertEqual(fullPkt_LE_new.oidranges, (srch((1, 2, 3), (1, 2, 5), False), srch((10, 20), (30, 40), True))) self.assertEqual(fullPkt_LE_new.context, "blah") # Test packetVars self.assertEqual(nullPkt_new.packetVars(), {"pduType": 6, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "oidranges": (), "context": None}) def test_GetBulkPDU(self): dec = AX.decode_GetBulkPDU cls = AX.GetBulkPDU srch = AX.SearchRange # Test init pkt = cls(True, 1, 2, 3, 1, 5, (srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True)), context="blah") test_pducore(self, pkt, AX.PDU_GET_BULK, True, 1, 2, 3) self.assertEqual(pkt.nonReps, 1) self.assertEqual(pkt.maxReps, 5) self.assertEqual(pkt.oidranges, (srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True))) self.assertEqual(pkt.context, "blah") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, 1, 5, (srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True)), context="blah") test_pducore(self, pkt_LE, AX.PDU_GET_BULK, False, 1, 2, 3) self.assertEqual(pkt_LE.nonReps, 1) self.assertEqual(pkt_LE.maxReps, 5) self.assertEqual(pkt_LE.oidranges, (srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True))) self.assertEqual(pkt_LE.context, "blah") # Test encoding pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x07\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x3C" b"\x00\x00\x00\x04blah" b"\x00\x01\x00\x05" b"\x02\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02" b"\x02\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04" b"\x02\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x07" b"\x02\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x09") # Test encoding, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x07\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x3C\x00\x00\x00" b"\x04\x00\x00\x00blah" b"\x01\x00\x05\x00" b"\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00" b"\x02\x00\x01\x00\x06\x00\x00\x00\x07\x00\x00\x00" b"\x02\x00\x00\x00\x08\x00\x00\x00\x09\x00\x00\x00") # Test decoding header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_GET_BULK, True, 1, 2, 3) self.assertEqual(pkt_new.nonReps, 1) self.assertEqual(pkt_new.maxReps, 5) self.assertEqual(pkt_new.oidranges, ((srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True)))) self.assertEqual(pkt_new.context, "blah") # Test decoding, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_GET_BULK, False, 1, 2, 3) self.assertEqual(pkt_LE_new.nonReps, 1) self.assertEqual(pkt_LE_new.maxReps, 5) self.assertEqual(pkt_LE_new.oidranges, ((srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True)))) self.assertEqual(pkt_LE_new.context, "blah") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 7, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "nonReps": 1, "maxReps": 5, "oidranges": (srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True)), "context": "blah"}) def test_TestSetPDU(self): dec = AX.decode_TestSetPDU cls = AX.TestSetPDU # Test init pkt = cls(True, 1, 2, 3, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt, AX.PDU_TEST_SET, True, 1, 2, 3) self.assertEqual(pkt.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt.context, "blah") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt_LE, AX.PDU_TEST_SET, False, 1, 2, 3) self.assertEqual(pkt_LE.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_LE.context, "blah") # Test encoding pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x08\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x48" b"\x00\x00\x00\x04blah" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah") # Test encoding, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x08\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x48\x00\x00\x00" b"\x04\x00\x00\x00blah" b"\x06\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x04\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x04\x00\x00\x00" b"\x04\x00\x00\x00blah") # Test decoding header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_TEST_SET, True, 1, 2, 3) self.assertEqual(pkt_new.varbinds, (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 4), False), "blah"))) self.assertEqual(pkt_new.context, "blah") # Test decoding, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_TEST_SET, False, 1, 2, 3) self.assertEqual(pkt_LE_new.varbinds, (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 4), False), "blah"))) self.assertEqual(pkt_LE_new.context, "blah") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 8, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "varbinds": (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 4), False), "blah")), "context": "blah"}) def test_CommitSetPDU(self): dec = AX.decode_CommitSetPDU cls = AX.CommitSetPDU # Test init pkt = cls(True, 1, 2, 3) test_pducore(self, pkt, AX.PDU_COMMIT_SET, True, 1, 2, 3) # Test init, little endian pkt_LE = cls(False, 1, 2, 3) test_pducore(self, pkt_LE, AX.PDU_COMMIT_SET, False, 1, 2, 3) # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x09\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x09\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x00") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_COMMIT_SET, True, 1, 2, 3) # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_COMMIT_SET, False, 1, 2, 3) # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 9, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3}) def test_UndoSetPDU(self): dec = AX.decode_UndoSetPDU cls = AX.UndoSetPDU # Test init pkt = cls(True, 1, 2, 3) test_pducore(self, pkt, AX.PDU_UNDO_SET, True, 1, 2, 3) # Test init, little endian pkt_LE = cls(False, 1, 2, 3) test_pducore(self, pkt_LE, AX.PDU_UNDO_SET, False, 1, 2, 3) # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x0A\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x0A\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x00") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_UNDO_SET, True, 1, 2, 3) # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_UNDO_SET, False, 1, 2, 3) # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 10, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3}) def test_CleanupSetPDU(self): dec = AX.decode_CleanupSetPDU cls = AX.CleanupSetPDU # Test init pkt = cls(True, 1, 2, 3) test_pducore(self, pkt, AX.PDU_CLEANUP_SET, True, 1, 2, 3) # Test init, little endian pkt_LE = cls(False, 1, 2, 3) test_pducore(self, pkt_LE, AX.PDU_CLEANUP_SET, False, 1, 2, 3) # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x0B\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x0B\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x00") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_CLEANUP_SET, True, 1, 2, 3) # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_CLEANUP_SET, False, 1, 2, 3) # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 11, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3}) def test_PingPDU(self): dec = AX.decode_PingPDU cls = AX.PingPDU # Test init pkt = cls(True, 1, 2, 3, "blah") test_pducore(self, pkt, AX.PDU_PING, True, 1, 2, 3) self.assertEqual(pkt.context, "blah") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, "blah") test_pducore(self, pkt_LE, AX.PDU_PING, False, 1, 2, 3) self.assertEqual(pkt_LE.context, "blah") # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x0D\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x08" b"\x00\x00\x00\x04blah") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x0D\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x08\x00\x00\x00" b"\x04\x00\x00\x00blah") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_PING, True, 1, 2, 3) self.assertEqual(pkt_new.context, "blah") # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_PING, False, 1, 2, 3) self.assertEqual(pkt_LE_new.context, "blah") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 13, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "context": "blah"}) def test_NotifyPDU(self): dec = AX.decode_NotifyPDU cls = AX.NotifyPDU # Test init pkt = cls(True, 1, 2, 3, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt, AX.PDU_NOTIFY, True, 1, 2, 3) self.assertEqual(pkt.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt.context, "blah") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt_LE, AX.PDU_NOTIFY, False, 1, 2, 3) self.assertEqual(pkt_LE.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_LE.context, "blah") # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x0C\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x48" b"\x00\x00\x00\x04blah" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x0C\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x48\x00\x00\x00" b"\x04\x00\x00\x00blah" b"\x06\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x04\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x04\x00\x00\x00" b"\x04\x00\x00\x00blah") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_NOTIFY, True, 1, 2, 3) self.assertEqual(pkt_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_new.context, "blah") # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_NOTIFY, False, 1, 2, 3) self.assertEqual(pkt_LE_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_LE_new.context, "blah") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 12, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "varbinds": (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), "context": "blah"}) def test_IndexAllocPDU(self): dec = AX.decode_xIndexAllocPDU cls = AX.IndexAllocPDU # Test init pkt = cls(True, 1, 2, 3, True, True, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt, AX.PDU_INDEX_ALLOC, True, 1, 2, 3) self.assertEqual(pkt.newIndex, True) self.assertEqual(pkt.anyIndex, True) self.assertEqual(pkt.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt.context, "blah") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, True, True, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt_LE, AX.PDU_INDEX_ALLOC, False, 1, 2, 3) self.assertEqual(pkt_LE.newIndex, True) self.assertEqual(pkt_LE.anyIndex, True) self.assertEqual(pkt_LE.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_LE.context, "blah") # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x0E\x1E\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x48" b"\x00\x00\x00\x04blah" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x0E\x0E\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x48\x00\x00\x00" b"\x04\x00\x00\x00blah" b"\x06\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x04\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x04\x00\x00\x00" b"\x04\x00\x00\x00blah") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_INDEX_ALLOC, True, 1, 2, 3) self.assertEqual(pkt_new.newIndex, True) self.assertEqual(pkt_new.anyIndex, True) self.assertEqual(pkt_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_INDEX_ALLOC, False, 1, 2, 3) self.assertEqual(pkt_LE_new.newIndex, True) self.assertEqual(pkt_LE_new.anyIndex, True) self.assertEqual(pkt_LE_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_LE_new.context, "blah") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 14, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "newIndex": True, "anyIndex": True, "varbinds": (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), "context": "blah"}) def test_IndexDeallocPDU(self): dec = AX.decode_xIndexAllocPDU cls = AX.IndexDeallocPDU # Test init pkt = cls(True, 1, 2, 3, True, True, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt, AX.PDU_INDEX_DEALLOC, True, 1, 2, 3) self.assertEqual(pkt.newIndex, True) self.assertEqual(pkt.anyIndex, True) self.assertEqual(pkt.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt.context, "blah") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, True, True, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), context="blah") test_pducore(self, pkt_LE, AX.PDU_INDEX_DEALLOC, False, 1, 2, 3) self.assertEqual(pkt_LE.newIndex, True) self.assertEqual(pkt_LE.anyIndex, True) self.assertEqual(pkt_LE.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_LE.context, "blah") # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x0F\x1E\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x48" b"\x00\x00\x00\x04blah" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x0F\x0E\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x48\x00\x00\x00" b"\x04\x00\x00\x00blah" b"\x06\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x04\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x04\x00\x00\x00" b"\x04\x00\x00\x00blah") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_INDEX_DEALLOC, True, 1, 2, 3) self.assertEqual(pkt_new.newIndex, True) self.assertEqual(pkt_new.anyIndex, True) self.assertEqual(pkt_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_new.context, "blah") # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_INDEX_DEALLOC, False, 1, 2, 3) self.assertEqual(pkt_LE_new.newIndex, True) self.assertEqual(pkt_LE_new.anyIndex, True) self.assertEqual(pkt_LE_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) self.assertEqual(pkt_LE_new.context, "blah") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 15, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "newIndex": True, "anyIndex": True, "varbinds": (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah")), "context": "blah"}) def test_AddAgentCapsPDU(self): dec = AX.decode_AddAgentCapsPDU cls = AX.AddAgentCapsPDU # Test init pkt = cls(True, 1, 2, 3, (4, 5, 6), "blah", context="bluh") test_pducore(self, pkt, AX.PDU_ADD_AGENT_CAPS, True, 1, 2, 3) self.assertEqual(pkt.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt.description, "blah") self.assertEqual(pkt.context, "bluh") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, (4, 5, 6), "blah", context="bluh") test_pducore(self, pkt_LE, AX.PDU_ADD_AGENT_CAPS, False, 1, 2, 3) self.assertEqual(pkt_LE.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt_LE.description, "blah") self.assertEqual(pkt_LE.context, "bluh") # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x10\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x20" b"\x00\x00\x00\x04bluh" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x00\x00\x04blah") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x10\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x20\x00\x00\x00" b"\x04\x00\x00\x00bluh" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x04\x00\x00\x00blah") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_ADD_AGENT_CAPS, True, 1, 2, 3) self.assertEqual(pkt_new.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt_new.description, "blah") self.assertEqual(pkt_new.context, "bluh") # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_ADD_AGENT_CAPS, False, 1, 2, 3) self.assertEqual(pkt_LE_new.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt_LE_new.description, "blah") self.assertEqual(pkt_LE_new.context, "bluh") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 16, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "oid": AX.OID((4, 5, 6), False), "description": "blah", "context": "bluh"}) def test_RMAgentCapsPDU(self): dec = AX.decode_RMAgentCapsPDU cls = AX.RMAgentCapsPDU # Test init pkt = cls(True, 1, 2, 3, (4, 5, 6), context="bluh") test_pducore(self, pkt, AX.PDU_RM_AGENT_CAPS, True, 1, 2, 3) self.assertEqual(pkt.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt.context, "bluh") # Test init, little endian pkt_LE = cls(False, 1, 2, 3, (4, 5, 6), context="bluh") test_pducore(self, pkt_LE, AX.PDU_RM_AGENT_CAPS, False, 1, 2, 3) self.assertEqual(pkt_LE.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt_LE.context, "bluh") # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x11\x18\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x18" b"\x00\x00\x00\x04bluh" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x11\x08\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x18\x00\x00\x00" b"\x04\x00\x00\x00bluh" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_RM_AGENT_CAPS, True, 1, 2, 3) self.assertEqual(pkt_new.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt_new.context, "bluh") # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_RM_AGENT_CAPS, False, 1, 2, 3) self.assertEqual(pkt_LE_new.oid, AX.OID((4, 5, 6), False)) self.assertEqual(pkt_LE_new.context, "bluh") # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 17, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "oid": AX.OID((4, 5, 6), False), "context": "bluh"}) def test_ResponsePDU(self): dec = AX.decode_ResponsePDU cls = AX.ResponsePDU # Test init pkt = cls(True, 1, 2, 3, 4, 5, 6, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) test_pducore(self, pkt, AX.PDU_RESPONSE, True, 1, 2, 3) self.assertEqual(pkt.sysUptime, 4) self.assertEqual(pkt.resError, 5) self.assertEqual(pkt.resIndex, 6) self.assertEqual(pkt.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) # Test init, little endian pkt_LE = cls(False, 1, 2, 3, 4, 5, 6, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) test_pducore(self, pkt_LE, AX.PDU_RESPONSE, False, 1, 2, 3) self.assertEqual(pkt_LE.sysUptime, 4) self.assertEqual(pkt_LE.resError, 5) self.assertEqual(pkt_LE.resIndex, 6) self.assertEqual(pkt_LE.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) # Test encode pkt_str = pkt.encode() self.assertEqual(pkt_str, b"\x01\x12\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x48" b"\x00\x00\x00\x04\x00\x05\x00\x06" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah") # Test encode, little endian pkt_LE_str = pkt_LE.encode() self.assertEqual(pkt_LE_str, b"\x01\x12\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x48\x00\x00\x00" b"\x04\x00\x00\x00\x05\x00\x06\x00" b"\x06\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x04\x00\x00\x00" b"\x03\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x04\x00\x00\x00" b"\x04\x00\x00\x00blah") # Test decode header, body = slicedata(pkt_str, 20) header = decode_pduheader(header) pkt_new = dec(body, header) test_pducore(self, pkt_new, AX.PDU_RESPONSE, True, 1, 2, 3) self.assertEqual(pkt_new.sysUptime, 4) self.assertEqual(pkt_new.resError, 5) self.assertEqual(pkt_new.resIndex, 6) self.assertEqual(pkt_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) # Test decode, little endian header, body = slicedata(pkt_LE_str, 20) header = decode_pduheader(header) pkt_LE_new = dec(body, header) test_pducore(self, pkt_LE_new, AX.PDU_RESPONSE, False, 1, 2, 3) self.assertEqual(pkt_LE_new.sysUptime, 4) self.assertEqual(pkt_LE_new.resError, 5) self.assertEqual(pkt_LE_new.resIndex, 6) self.assertEqual(pkt_LE_new.varbinds, (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))) # Test packetVars self.assertEqual(pkt_new.packetVars(), {"pduType": 18, "bigEndian": True, "sessionID": 1, "transactionID": 2, "packetID": 3, "sysUptime": 4, "resError": 5, "resIndex": 6, "varbinds": (AX.Varbind(AX.VALUE_OID, (1, 2, 3), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, (1, 2, 4), "blah"))}) # # Data type tests # def test_integer32(self): enc = AX.encode_integer32 dec = AX.decode_integer32 # Encode self.assertEqual(enc(True, 42), b"\x00\x00\x00\x2A") # Encode, little endian self.assertEqual(enc(False, 42), b"\x2A\x00\x00\x00") # Decode self.assertEqual(dec(b"\x00\x00\x00\x2A" + extraData, standardFlags), (42, extraData)) # Decode, little endian self.assertEqual(dec(b"\x2A\x00\x00\x00" + extraData, lilEndianFlags), (42, extraData)) def test_nullvalue(self): enc = AX.encode_nullvalue dec = AX.decode_nullvalue # Encode self.assertEqual(enc(True, "this is ignored"), b"") # Decode self.assertEqual(dec(extraData, standardFlags), (None, extraData)) def test_integer64(self): enc = AX.encode_integer64 dec = AX.decode_integer64 # Encode self.assertEqual(enc(True, 42), b"\x00\x00\x00\x00\x00\x00\x00\x2A") # Encode, little endian self.assertEqual(enc(False, 42), b"\x2A\x00\x00\x00\x00\x00\x00\x00") # Decode self.assertEqual(dec(b"\x00\x00\x00\x00\x00\x00\x00\x2A" + extraData, standardFlags), (42, extraData)) # Decode, little endian self.assertEqual(dec(b"\x2A\x00\x00\x00\x00\x00\x00\x00" + extraData, lilEndianFlags), (42, extraData)) def test_ipaddr(self): enc = AX.encode_ipaddr dec = AX.decode_ipaddr # Encode correct self.assertEqual(enc(True, (1, 2, 3, 4)), b"\x00\x00\x00\x04\x01\x02\x03\x04") # Encode correct, little endian self.assertEqual(enc(False, (1, 2, 3, 4)), b"\x04\x00\x00\x00\x01\x02\x03\x04") # Encode incorrect try: enc(True, (1, 2, 3, 4, 5)) errored = False except ValueError: errored = True self.assertEqual(errored, True) # Decode self.assertEqual(dec(b"\x00\x00\x00\x04\x01\x02\x03\x04" + extraData, standardFlags), ((1, 2, 3, 4), extraData)) # Decode, little endian self.assertEqual(dec(b"\x04\x00\x00\x00\x01\x02\x03\x04" + extraData, lilEndianFlags), ((1, 2, 3, 4), extraData)) def test_OID(self): target = AX.OID dec = AX.decode_OID # Encode empty OID cls = target((), False) self.assertEqual(cls.encode(True), b"\x00\x00\x00\x00") # Encode basic OID cls = target((1, 2, 3, 4, 5), False) self.assertEqual(cls.encode(True), b"\x05\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x00\x00\x00\x05") # Encode basic OID, little endian self.assertEqual(cls.encode(False), b"\x05\x00\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x05\x00\x00\x00") # Encode prefixed OID cls = target((1, 3, 6, 1, 23, 1, 2, 3), False) self.assertEqual(cls.encode(True), b"\x03\x17\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03") # Encode include cls = target((1, 2), True) self.assertEqual(cls.encode(True), b"\x02\x00\x01\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02") # Encode together cls = target((1, 3, 6, 1, 1, 3, 4, 5, 6), True) self.assertEqual(cls.encode(True), b"\x04\x01\x01\x00" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06") # Encode maximum size cls = target(maximumOIDsubs, False) self.assertEqual(cls.encode(True), maximumOIDstr) # Encode over maximum size try: cls = target(maximumOIDsubs + (42,), False) cls.encode(True) errored = False except ValueError: errored = True self.assertEqual(errored, True) # Decode empty OID, extra data cls, xtr = dec(b"\x00\x00\x00\x00" + extraData, standardFlags) self.assertEqual(isinstance(cls, target), True) self.assertEqual(cls.subids, ()) self.assertEqual(cls.include, False) self.assertEqual(xtr, extraData) # Decode basic OID, extra data cls, xtr = dec(b"\x05\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x04\x00\x00\x00\x05" + extraData, standardFlags) self.assertEqual(isinstance(cls, target), True) self.assertEqual(cls.subids, (1, 2, 3, 4, 5)) self.assertEqual(cls.include, False) self.assertEqual(xtr, extraData) # Decode basic OID, little endian cls, xtr = dec(b"\x05\x00\x00\x00\x01\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x04\x00\x00\x00\x05\x00\x00\x00", lilEndianFlags) self.assertEqual(isinstance(cls, target), True) self.assertEqual(cls.subids, (1, 2, 3, 4, 5)) self.assertEqual(cls.include, False) self.assertEqual(xtr, b"") # Decode prefixed OID cls, xtr = dec(b"\x03\x17\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03", standardFlags) self.assertEqual(isinstance(cls, target), True) self.assertEqual(cls.subids, (1, 3, 6, 1, 23, 1, 2, 3)) self.assertEqual(cls.include, False) self.assertEqual(xtr, b"") # Decode include cls, xtr = dec(b"\x02\x00\x05\x00\x00\x00\x00\x01\x00\x00\x00\x02", standardFlags) self.assertEqual(isinstance(cls, target), True) self.assertEqual(cls.subids, (1, 2)) self.assertEqual(cls.include, True) self.assertEqual(xtr, b"") # Decode together cls, xtr = dec(b"\x04\x01\x02\x00\x00\x00\x00\x03" b"\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06", standardFlags) self.assertEqual(isinstance(cls, target), True) self.assertEqual(cls.subids, (1, 3, 6, 1, 1, 3, 4, 5, 6)) self.assertEqual(cls.include, True) self.assertEqual(xtr, b"") # Decode maximum size cls, xtr = dec(maximumOIDstr, standardFlags) self.assertEqual(isinstance(cls, target), True) self.assertEqual(cls.subids, maximumOIDsubs) self.assertEqual(cls.include, False) self.assertEqual(xtr, b"") # Decode over maximum size # Need to replace the hardcoded n_subid=128 with 129 fatOID = b"\x81" + maximumOIDstr[1:] + b"\xDE\xAD\xBE\xEF" try: cls, xtr = dec(fatOID, standardFlags) errored = False except ValueError: errored = True self.assertEqual(errored, True) # Test compareOID # Test equal a = target((1, 2, 3, 4)) b = target((1, 2, 3, 4)) self.assertEqual(a.compareOID(b), 0) # Test equal length, one < two b = target((1, 2, 3, 5)) self.assertEqual(a.compareOID(b), -1) # Test equal length, one > two b = target((1, 2, 3, 0)) self.assertEqual(a.compareOID(b), 1) # Test one shorter, less than two, equal for length a = target((1, 2, 3)) b = target((1, 2, 3, 4)) self.assertEqual(a.compareOID(b), -1) # Test one shorter, less than two b = target((1, 2, 4, 5)) self.assertEqual(a.compareOID(b), -1) # Test one shorter, greater than two b = target((1, 2, 2, 4)) self.assertEqual(a.compareOID(b), 1) # Test two shorter, less than one, equal for length a = target((1, 2, 3, 4)) b = target((1, 2, 3)) self.assertEqual(a.compareOID(b), 1) # Test two shorter, less than one a = target((1, 2, 4, 5)) self.assertEqual(a.compareOID(b), 1) # Test two shorter, greater than one a = target((1, 2, 2, 4)) b = target((1, 2, 3)) self.assertEqual(a.compareOID(b), -1) # Test direct comparisons # Test == self.assertEqual(target((1, 2, 3)) == target((1, 2, 3)), True) # Test != self.assertEqual(target((1, 2, 3)) != target((1, 2)), True) # Test < self.assertEqual(target((1, 2)) < target((1, 2, 3)), True) # Test > self.assertEqual(target((1, 2, 3)) > target((1, 2)), True) # Test <= self.assertEqual(target((1, 2)) <= target((1, 2, 3)), True) self.assertEqual(target((1, 2, 3)) <= target((1, 2, 3)), True) # Test >= self.assertEqual(target((1, 2, 3)) >= target((1, 2)), True) self.assertEqual(target((1, 2, 3)) >= target((1, 2, 3)), True) # Test insane subids type try: errored = target("foo") except TypeError: errored = True self.assertEqual(errored, True) # Test isNull self.assertEqual(target((1, 2)).isNull(), False) self.assertEqual(target(()).isNull(), True) def test_searchrange(self): target = AX.SearchRange dec = AX.decode_SearchRange oid = AX.OID # Test init # Basic cls = target(oid((1, 2), True), oid((3, 4), False)) self.assertEqual(cls.start.subids, (1, 2)) self.assertEqual(cls.start.include, True) self.assertEqual(cls.end.subids, (3, 4)) self.assertEqual(cls.end.include, False) # Override cls = target(oid((1, 2), True), oid((3, 4), True), False) self.assertEqual(cls.start.subids, (1, 2)) self.assertEqual(cls.start.include, False) self.assertEqual(cls.end.subids, (3, 4)) self.assertEqual(cls.end.include, False) # Turn tuples into OIDs cls = target((1, 2), (3, 4), True) self.assertEqual(cls.start.subids, (1, 2)) self.assertEqual(cls.start.include, True) self.assertEqual(cls.end.subids, (3, 4)) self.assertEqual(cls.end.include, False) # Test encoding # Encode minimum size cls = target((), (), False) self.assertEqual(cls.encode(True), b"\x00\x00\x00\x00\x00\x00\x00\x00") # Encode inclusive cls = target((1, 2, 3, 4), (5, 6, 7, 8), True) self.assertEqual(cls.encode(True), b"\x04\x00\x01\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x04\x00\x00\x00" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x00\x00\x07\x00\x00\x00\x08") # Encode exclusive cls = target((1, 2, 3, 4), (5, 6, 7, 8), False) self.assertEqual(cls.encode(True), b"\x04\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x04\x00\x00\x00" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x00\x00\x07\x00\x00\x00\x08") # Encode exclusive, little endian self.assertEqual(cls.encode(False), b"\x04\x00\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x07\x00\x00\x00\x08\x00\x00\x00") # Test decode # Decode minimum size, extra data self.assertEqual(dec(b"\x00\x00\x00\x00\x00\x00\x00\x00" + extraData, standardFlags), (target((), (), False), extraData)) # Decode inclusive self.assertEqual(dec(b"\x04\x00\x01\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x04\x00\x00\x00" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x00\x00\x07\x00\x00\x00\x08", standardFlags), (target((1, 2, 3, 4), (5, 6, 7, 8), True), b"")) # Decode exclusive self.assertEqual(dec(b"\x04\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x04\x00\x00\x00" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x00\x00\x07\x00\x00\x00\x08", standardFlags), (target((1, 2, 3, 4), (5, 6, 7, 8), False), b"")) # Decode little endian self.assertEqual(dec(b"\x04\x00\x01\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00" b"\x04\x00\x00\x00" b"\x05\x00\x00\x00\x06\x00\x00\x00" b"\x07\x00\x00\x00\x08\x00\x00\x00", lilEndianFlags), (target((1, 2, 3, 4), (5, 6, 7, 8), True), b"")) # Test __eq__ # Test equal a = target((1, 2, 3), (1, 2, 3)) b = target((1, 2, 3), (1, 2, 3)) self.assertEqual(a == b, True) # Test start unequal b = target((1, 2, 3), (1, 2, 3), True) self.assertEqual(a == b, False) # Test end unequal b = target((1, 2, 3), (1, 2, 3, 4)) self.assertEqual(a == b, False) # Test __ne__ # Test equal a = target((1, 2, 3), (1, 2, 3)) b = target((1, 2, 3), (1, 2, 3)) self.assertEqual(a != b, False) # Test start unequal b = target((1, 2, 3), (1, 2, 3), True) self.assertEqual(a != b, True) # Test end unequal b = target((1, 2, 3), (1, 2, 3, 4)) self.assertEqual(a != b, True) # Test __repr__ self.assertEqual(repr(target((1, 2), (1, 3))), "SearchRange(OID((1, 2), False), OID((1, 3), False))") def test_encode_searchrange_list(self): enc = AX.encode_searchrange_list srch = AX.SearchRange # Encode self.assertEqual(enc(True, (srch((1, 2), (1, 2), True), srch((2, 3), (3, 4)))), b"\x02\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00\x02" b"\x02\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02" b"\x02\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03" b"\x02\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04") # Encode, little endian self.assertEqual(enc(False, (srch((1, 2), (1, 2), True), srch((2, 3), (3, 4)))), b"\x02\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00" b"\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00" b"\x02\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00") def test_decode_searchrange_list(self): dec = AX.decode_searchrange_list srch = AX.SearchRange # Decode self.assertEqual(dec(b"\x02\x00\x01\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x02\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x02\x00\x00\x00" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x02\x00\x00\x00" b"\x00\x00\x00\x03\x00\x00\x00\x04", standardFlags), (srch((1, 2), (1, 2), True), srch((2, 3), (3, 4), False))) # Test, little endian self.assertEqual(dec(b"\x02\x00\x01\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x02\x00\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00" b"\x02\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00" b"\x02\x00\x00\x00" b"\x03\x00\x00\x00\x04\x00\x00\x00", lilEndianFlags), (srch((1, 2), (1, 2), True), srch((2, 3), (3, 4), False))) def test_xcode_octetstr(self): enc = AX.encode_octetstr dec = AX.decode_octetstr san = AX.sanity_octetstr # Encode empty self.assertEqual(enc(True, ()), b"\x00\x00\x00\x00") # Encode word multiple self.assertEqual(enc(True, (1, 2, 3, 4)), b"\x00\x00\x00\x04\x01\x02\x03\x04") # Encode non word multiple self.assertEqual(enc(True, (1, 2, 3, 4, 5)), b"\x00\x00\x00\x05\x01\x02\x03\x04\x05\x00\x00\x00") # Encode string self.assertEqual(enc(True, "blah"), b"\x00\x00\x00\x04blah") # Encode string, little endian self.assertEqual(enc(False, "blah"), b"\x04\x00\x00\x00blah") # Decode empty self.assertEqual(dec(b"\x00\x00\x00\x00", standardFlags), ("", b"")) # Decode word multiple, extra data self.assertEqual(dec(b"\x00\x00\x00\x04blah" + extraData, standardFlags), ("blah", extraData)) # Decode word multiple, little endian self.assertEqual(dec(b"\x04\x00\x00\x00blah", lilEndianFlags), ("blah", b"")) # Decode non word multiple, extra data self.assertEqual(dec(b"\x00\x00\x00\x05" b"blarg\x00\x00\x00" + extraData, standardFlags), ("blarg", extraData)) # Test sanity # Test str try: errored = san("foo") # str is always sane except Exception as e: errored = e self.assertEqual(errored, None) # Test sane list try: errored = san([1, 2, 3]) except Exception as e: errored = e self.assertEqual(errored, None) # Test sane tuple try: errored = san((1, 2, 3)) except Exception as e: errored = e self.assertEqual(errored, None) # Test insane list try: errored = san([23, 300, 42]) except ValueError: errored = True self.assertEqual(errored, True) # Test insane tuple try: errored = san((23, 300, 42)) except ValueError: errored = True self.assertEqual(errored, True) # Test insane type try: errored = san(42.23) except TypeError: errored = True self.assertEqual(errored, True) def test_Varbind(self): target = AX.Varbind # Test init cls = target(AX.VALUE_INTEGER, (1, 2, 3), 42) self.assertEqual(cls.valueType, AX.VALUE_INTEGER) self.assertEqual(cls.oid, AX.OID((1, 2, 3), False)) self.assertEqual(cls.payload, 42) # Test repr self.assertEqual(repr(cls), "Varbind(vtype=2, oid=OID((1, 2, 3), False), " "payload=42)") # Test payloadless types cls = target(AX.VALUE_NULL, (1, 2, 3)) self.assertEqual(cls.encode(True), b"\x00\x05\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03") cls = target(AX.VALUE_NO_SUCH_OBJECT, (1, 2, 3)) self.assertEqual(cls.encode(True), b"\x00\x80\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03") cls = target(AX.VALUE_NO_SUCH_INSTANCE, (1, 2, 3)) self.assertEqual(cls.encode(True), b"\x00\x81\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03") cls = target(AX.VALUE_END_OF_MIB_VIEW, (1, 2, 3)) self.assertEqual(cls.encode(True), b"\x00\x82\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03") # Test octet based types cls = target(AX.VALUE_OCTET_STR, (1, 2, 3), (1, 2, 3, 4, 5)) self.assertEqual(cls.encode(True), b"\x00\x04\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x05" b"\x01\x02\x03\x04\x05\x00\x00\x00") cls = target(AX.VALUE_IP_ADDR, (1, 2, 3), (16, 32, 48, 64)) self.assertEqual(cls.encode(True), b"\x00\x40\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x04\x10\x20\x30\x40") # Test integer32 type cls = target(AX.VALUE_INTEGER, (1, 2, 3), -42) self.assertEqual(cls.encode(True), b"\x00\x02\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\xFF\xFF\xFF\xD6") # Test unsigned32 types cls = target(AX.VALUE_COUNTER32, (1, 2, 3), 42) self.assertEqual(cls.encode(True), b"\x00\x41\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x2A") cls = target(AX.VALUE_GAUGE32, (1, 2, 3), 42) self.assertEqual(cls.encode(True), b"\x00\x42\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x2A") cls = target(AX.VALUE_TIME_TICKS, (1, 2, 3), 42) self.assertEqual(cls.encode(True), b"\x00\x43\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x2A") # Test integer64 type cls = target(AX.VALUE_COUNTER64, (1, 2, 3), 42) self.assertEqual(cls.encode(True), b"\x00\x46\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x00\x00\x00\x00\x2A") # Test oid type cls = target(AX.VALUE_OID, (1, 2, 3), AX.OID((16, 42, 256), False)) self.assertEqual(cls.encode(True), b"\x00\x06\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x10" b"\x00\x00\x00\x2A\x00\x00\x01\x00") # Test oid type, little endian cls = target(AX.VALUE_OID, (1, 2, 3), AX.OID((16, 42, 256), False)) self.assertEqual(cls.encode(False), b"\x06\x00\x00\x00\x03\x00\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00" b"\x03\x00\x00\x00\x10\x00\x00\x00" b"\x2A\x00\x00\x00\x00\x01\x00\x00") # Test __eq__ one = target(AX.VALUE_INTEGER, (1, 2, 3), 1) two = target(AX.VALUE_INTEGER, (1, 2, 3), 1) # Test equal self.assertEqual(one == two, True) # Test different type two = target(AX.VALUE_GAUGE32, (1, 2, 3), 1) self.assertEqual(one == two, False) # Test different OID two = target(AX.VALUE_INTEGER, (1, 2, 3, 4), 1) self.assertEqual(one == two, False) # Test different payload two = target(AX.VALUE_INTEGER, (1, 2, 3), 2) self.assertEqual(one == two, False) def test_decode_varbind(self): f = AX.decode_Varbind # Test payloadless types self.assertEqual(f(b"\x00\x05\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03", standardFlags), (AX.Varbind(AX.VALUE_NULL, AX.OID((1, 2, 3), False)), b"")) self.assertEqual(f(b"\x00\x80\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03", standardFlags), (AX.Varbind(AX.VALUE_NO_SUCH_OBJECT, AX.OID((1, 2, 3), False)), b"")) self.assertEqual(f(b"\x00\x81\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03", standardFlags), (AX.Varbind(AX.VALUE_NO_SUCH_INSTANCE, AX.OID((1, 2, 3), False)), b"")) self.assertEqual(f(b"\x00\x82\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03", standardFlags), (AX.Varbind(AX.VALUE_END_OF_MIB_VIEW, AX.OID((1, 2, 3), False)), b"")) # Test octet based types self.assertEqual(f(b"\x00\x04\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x0512345\x00\x00\x00", standardFlags), (AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 3), False), "12345"), b"")) self.assertEqual(f(b"\x00\x40\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x04\x10\x20\x30\x40", standardFlags), (AX.Varbind(AX.VALUE_IP_ADDR, AX.OID((1, 2, 3), False), (16, 32, 48, 64)), b"")) # Test integer32 type self.assertEqual(f(b"\x00\x02\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\xFF\xFF\xFF\xD6", standardFlags), (AX.Varbind(AX.VALUE_INTEGER, AX.OID((1, 2, 3), False), -42), b"")) self.assertEqual(f(b"\x00\x41\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x2A", standardFlags), (AX.Varbind(AX.VALUE_COUNTER32, AX.OID((1, 2, 3), False), 42), b"")) # Test unsigned32 types self.assertEqual(f(b"\x00\x42\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x2A", standardFlags), (AX.Varbind(AX.VALUE_GAUGE32, AX.OID((1, 2, 3), False), 42), b"")) self.assertEqual(f(b"\x00\x43\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x2A", standardFlags), (AX.Varbind(AX.VALUE_TIME_TICKS, AX.OID((1, 2, 3), False), 42), b"")) # Test integer64 type self.assertEqual(f(b"\x00\x46\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x00\x00\x00\x00\x00\x00\x00\x2A", standardFlags), (AX.Varbind(AX.VALUE_COUNTER64, AX.OID((1, 2, 3), False), 42), b"")) # Test oid type self.assertEqual(f(b"\x00\x06\x00\x00\x03\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x10" b"\x00\x00\x00\x2A\x00\x00\x01\x00", standardFlags), (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((16, 42, 256), False)), b"")) # Test integer32 with little endian self.assertEqual(f(b"\x43\x00\x00\x00\x03\x00\x00\x00" b"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00" b"\x2A\x00\x00\x00", lilEndianFlags), (AX.Varbind(AX.VALUE_TIME_TICKS, AX.OID((1, 2, 3), False), 42), b"")) def test_xcode_varbindlist(self): enc = AX.encode_varbindlist dec = AX.decode_varbindlist vb = AX.Varbind # Test encode empty self.assertEqual(enc(True, []), b"") # Test encode big endian big = enc(True, [vb(AX.VALUE_INTEGER, (1, 2), 1), vb(AX.VALUE_INTEGER, (3, 4), 2)]) self.assertEqual(big, b"\x00\x02\x00\x00" b"\x02\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x01" b"\x00\x02\x00\x00" b"\x02\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04" b"\x00\x00\x00\x02") # Test encode little endian little = enc(False, [vb(AX.VALUE_INTEGER, (1, 2), 1), vb(AX.VALUE_INTEGER, (3, 4), 2)]) self.assertEqual(little, b"\x02\x00\x00\x00" b"\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00" b"\x01\x00\x00\x00" b"\x02\x00\x00\x00" b"\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00" b"\x02\x00\x00\x00") # Test decode empty self.assertEqual(dec(b"", standardFlags), None) # Test decode big endian self.assertEqual(dec(big, standardFlags), (vb(AX.VALUE_INTEGER, (1, 2), 1), vb(AX.VALUE_INTEGER, (3, 4), 2))) # Test decode little endian self.assertEqual(dec(little, lilEndianFlags), (vb(AX.VALUE_INTEGER, (1, 2), 1), vb(AX.VALUE_INTEGER, (3, 4), 2))) def test_encode_flagbyte(self): f = AX.encode_flagbyte self.assertEqual(f(makeflags(False, True, False, True, False)), 0x0A) self.assertEqual(f(makeflags(True, False, True, False, True)), 0x15) def test_decode_flagbyte(self): f = AX.decode_flagbyte self.assertEqual(f(0x0A), makeflags(False, True, False, True, False)) self.assertEqual(f(0x15), makeflags(True, False, True, False, True)) # # Misc tests # def test_makeflags(self): f = AX.makeflags self.assertEqual(f(True, False, True, False, True), {"instReg": True, "newIndex": False, "anyIndex": True, "contextP": False, "bigEndian": True}) def test_getendian(self): f = AX.getendian # Test big endian self.assertEqual(f(True), ">") # Test little endian self.assertEqual(f(False), "<") def test_encode_pduheader(self): f = AX.encode_pduheader # Test "empty" header self.assertEqual(f(AX.PDU_OPEN, False, False, False, False, False, 0xDEADBEEF, 0xCAFEBABE, 0xFACEF00D, 0), b"\x01\x01\x00\x00" b"\xEF\xBE\xAD\xDE\xBE\xBA\xFE\xCA" b"\x0D\xF0\xCE\xFA\x00\x00\x00\x00") # Test flags self.assertEqual(f(AX.PDU_OPEN, True, True, True, True, True, 0xDEADBEEF, 0xCAFEBABE, 0xFACEF00D, 0), b"\x01\x01\x1F\x00" b"\xDE\xAD\xBE\xEF\xCA\xFE\xBA\xBE" b"\xFA\xCE\xF0\x0D\x00\x00\x00\x00") def test_decode_pduheader(self): f = AX.decode_pduheader # Test "empty" header self.assertEqual(f(b"\x01\x01\x10\x00" b"\xDE\xAD\xBE\xEF\xCA\xFE\xBA\xBE" b"\xFA\xCE\xF0\x0D\x00\x00\x00\x00"), {"version": 1, "type": AX.PDU_OPEN, "flags": {"instReg": False, "newIndex": False, "anyIndex": False, "contextP": False, "bigEndian": True}, "session_id": 0xDEADBEEF, "transaction_id": 0xCAFEBABE, "packet_id": 0xFACEF00D, "length": 0}) # Test "empty" header, little endian self.assertEqual(f(b"\x01\x01\x00\x00" b"\xEF\xBE\xAD\xDE\xBE\xBA\xFE\xCA" b"\x0D\xF0\xCE\xFA\x00\x00\x00\x00"), {"version": 1, "type": AX.PDU_OPEN, "flags": {"instReg": False, "newIndex": False, "anyIndex": False, "contextP": False, "bigEndian": False}, "session_id": 0xDEADBEEF, "transaction_id": 0xCAFEBABE, "packet_id": 0xFACEF00D, "length": 0}) # Test "empty" header, extra data self.assertEqual(f(b"\x01\x01\x10\x00" b"\xDE\xAD\xBE\xEF\xCA\xFE\xBA\xBE" b"\xFA\xCE\xF0\x0D\x00\x00\x00\x00" + extraData), {"version": 1, "type": AX.PDU_OPEN, "flags": {"instReg": False, "newIndex": False, "anyIndex": False, "contextP": False, "bigEndian": True}, "session_id": 0xDEADBEEF, "transaction_id": 0xCAFEBABE, "packet_id": 0xFACEF00D, "length": 0}) # Test flags self.assertEqual(f(b"\x01\x01\x1F\x00" b"\xDE\xAD\xBE\xEF\xCA\xFE\xBA\xBE" b"\xFA\xCE\xF0\x0D\x00\x00\x00\x00"), {"version": 1, "type": AX.PDU_OPEN, "flags": {"instReg": True, "newIndex": True, "anyIndex": True, "contextP": True, "bigEndian": True}, "session_id": 0xDEADBEEF, "transaction_id": 0xCAFEBABE, "packet_id": 0xFACEF00D, "length": 0}) def test_decode_packet(self): f = AX.decode_packet srch = AX.SearchRange # Not testing all the variants of each packet type, that is # the job of the other tests. self.maxDiff = None # Test open self.assertEqual(f(b"\x01\x01\x10\x00" b"\x00\x00\x00\x0C\x00\x00\x00\x22" b"\x00\x00\x00\x38\x00\x00\x00\x20" b"\x4E\x00\x00\x00" b"\x04\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x00\x00\x00\x03foo\x00"), (AX.OpenPDU(True, 12, 34, 56, 78, AX.OID((1, 2, 3, 4), False), "foo"), b"")) # Test open, extraData self.assertEqual(f(b"\x01\x01\x10\x00" b"\x00\x00\x00\x0C\x00\x00\x00\x22" b"\x00\x00\x00\x38\x00\x00\x00\x20" b"\x4E\x00\x00\x00" b"\x04\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x00\x00\x00\x03foo\x00" + extraData), (AX.OpenPDU(True, 12, 34, 56, 78, AX.OID((1, 2, 3, 4), False), "foo"), extraData)) # Test close self.assertEqual(f(b"\x01\x02\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x01\x00\x00\x00"), (AX.ClosePDU(True, 1, 2, 3, AX.RSN_OTHER), b"")) # Test register self.assertEqual(f(b"\x01\x03\x11\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x14" b"\x04\x05\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03"), (AX.RegisterPDU(True, 1, 2, 3, 4, 5, AX.OID((1, 2, 3), False), 0, None, None), b"")) # Test unregister self.assertEqual(f(b"\x01\x04\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x14" b"\x00\x05\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03"), (AX.UnregisterPDU(True, 1, 2, 3, 5, AX.OID((1, 2, 3), False), 0, None, None), b"")) # Test get self.assertEqual(f(b"\x01\x05\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00"), (AX.GetPDU(True, 1, 2, 3, ()), b"")) # Test get next self.assertEqual(f(b"\x01\x06\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00"), (AX.GetNextPDU(True, 1, 2, 3, ()), b"")) # Test get bulk self.assertEqual(f(b"\x01\x07\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x34" b"\x00\x01\x00\x05" b"\x02\x00\x00\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x02\x00\x00\x00" b"\x00\x00\x00\x03\x00\x00\x00\x04" b"\x02\x00\x01\x00" b"\x00\x00\x00\x06\x00\x00\x00\x07" b"\x02\x00\x00\x00" b"\x00\x00\x00\x08\x00\x00\x00\x09"), (AX.GetBulkPDU(True, 1, 2, 3, 1, 5, (srch((1, 2), (3, 4), False), srch((6, 7), (8, 9), True))), b"")) # Test test set self.assertEqual(f(b"\x01\x08\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x40" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah"), (AX.TestSetPDU(True, 1, 2, 3, (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 4), False), "blah"))), b"")) # Test commit set self.assertEqual(f(b"\x01\x09\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00"), (AX.CommitSetPDU(True, 1, 2, 3), b"")) # Test undo set self.assertEqual(f(b"\x01\x0A\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00"), (AX.UndoSetPDU(True, 1, 2, 3), b"")) # Test cleanup set self.assertEqual(f(b"\x01\x0B\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00"), (AX.CleanupSetPDU(True, 1, 2, 3), b"")) # Test notify self.assertEqual(f(b"\x01\x0C\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x40" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah"), (AX.NotifyPDU(True, 1, 2, 3, (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 4), False), "blah"))), b"")) # Test ping self.assertEqual(f(b"\x01\x0D\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x00"), (AX.PingPDU(True, 1, 2, 3), b"")) # Test index alloc self.assertEqual(f(b"\x01\x0E\x16\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x40" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah"), (AX.IndexAllocPDU(True, 1, 2, 3, True, True, (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 4), False), "blah"))), b"")) # Test index dealloc self.assertEqual(f(b"\x01\x0F\x16\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x40" b"\x00\x06\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x03" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x04\x00\x00" b"\x03\x00\x00\x00\x00\x00\x00\x01" b"\x00\x00\x00\x02\x00\x00\x00\x04" b"\x00\x00\x00\x04blah"), (AX.IndexDeallocPDU(True, 1, 2, 3, True, True, (AX.Varbind(AX.VALUE_OID, AX.OID((1, 2, 3), False), AX.OID((4, 5, 6), False)), AX.Varbind(AX.VALUE_OCTET_STR, AX.OID((1, 2, 4), False), "blah"))), b"")) # Test add agent caps self.assertEqual(f(b"\x01\x10\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x18" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06" b"\x00\x00\x00\x04blah"), (AX.AddAgentCapsPDU(True, 1, 2, 3, AX.OID((4, 5, 6), False), "blah"), b"")) # Test rm agent caps self.assertEqual(f(b"\x01\x11\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x10" b"\x03\x00\x00\x00\x00\x00\x00\x04" b"\x00\x00\x00\x05\x00\x00\x00\x06"), (AX.RMAgentCapsPDU(True, 1, 2, 3, AX.OID((4, 5, 6), False)), b"")) # Test response self.assertEqual(f(b"\x01\x12\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x08" b"\x00\x00\x00\x04\x00\x05\x00\x06"), (AX.ResponsePDU(True, 1, 2, 3, 4, 5, 6), b"")) # Test errors # Test insufficient data for header try: self.assertEqual(f(b""), None) errored = False except AX.ParseError as e: errored = e self.assertEqual(errored.message, "Data too short for header") # Test insufficient data for packet try: f(b"\x01\x11\x10\x00" b"\x00\x00\x00\x01\x00\x00\x00\x02" b"\x00\x00\x00\x03\x00\x00\x00\x10" b"\x03\x00\x00\x00\x00\x00\x00\x04") errored = False except AX.ParseError as e: errored = e self.assertEqual(errored.message, "Packet data too short") # Test wrong version try: f(b"\x02\x11\x10\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x08" b"blahblahjabber") errored = False except AX.ParseError as e: errored = e self.assertEqual(errored.message, "Unknown packet version 2") self.assertEqual(errored.packetData, b"blahblah") self.assertEqual(errored.remainingData, b"jabber") # Test unrecognized packet type try: f(b"\x01\xFF\x10\x00" b"\x00\x00\x00\x00\x00\x00\x00\x00" b"\x00\x00\x00\x00\x00\x00\x00\x08" b"blahblah") errored = False except AX.ParseError as e: errored = e self.assertEqual(errored.message, "PDU type 255 not in defined types") def test_walkMIBTree(self): f = AX.walkMIBTree # Test empty tree self.assertEqual(tuple(f({})), ()) # Test flat, fully static tree self.assertEqual(tuple(f({0: {"reader": None, "writer": None, "subids": None}, 1: {}, 2: {"reader": None, "writer": None, "subids": None}, 5: {"reader": None, "writer": None, "subids": None}})), ((AX.OID((0,)), None, None), (AX.OID((1,)), None, None), (AX.OID((2,)), None, None), (AX.OID((5,)), None, None))) # Test nested, fully static tree self.assertEqual(tuple(f({0: {"reader": None, "writer": None, "subids": None}, 1: {"reader": None, "writer": None, "subids": {0: {"reader": None, "writer": None, "subids": None}, 1: {"reader": None, "writer": None, "subids": {42: {"reader": None, "writer": None, "subids": None}}}}}, 5: {"reader": None, "writer": None, "subids": None}})), ((AX.OID((0,)), None, None), (AX.OID((1,)), None, None), (AX.OID((1, 0)), None, None), (AX.OID((1, 1)), None, None), (AX.OID((1, 1, 42)), None, None), (AX.OID((5,)), None, None))) # Test nested, fully static tree, with rootpath self.assertEqual(tuple(f({0: {"reader": None, "writer": None, "subids": None}, 1: {"reader": None, "writer": None, "subids": {0: {"reader": None, "writer": None, "subids": None}, 1: {"reader": None, "writer": None, "subids": {42: {"reader": None, "writer": None, "subids": None}}}}}, 5: {"reader": None, "writer": None, "subids": None}}, (23,))), ((AX.OID((23, 0)), None, None), (AX.OID((23, 1)), None, None), (AX.OID((23, 1, 0)), None, None), (AX.OID((23, 1, 1)), None, None), (AX.OID((23, 1, 1, 42)), None, None), (AX.OID((23, 5)), None, None))) # subid lambda for dynamic tree testing submaker = (lambda: {0: {"reader": None, "writer": None, "subids": None}, 1: {"reader": None, "writer": None, "subids": {0: {"reader": None, "writer": None, "subids": None}}}, 2: {"reader": None, "writer": None, "subids": None}}) # Test tree with dynamic nodes self.assertEqual(tuple(f({0: {"reader": None, "writer": None, "subids": None}, 1: {"reader": None, "writer": None, "subids": submaker}, 2: {"reader": None, "writer": None, "subids": None}})), ((AX.OID((0,)), None, None), (AX.OID((1,)), None, None), (AX.OID((1, 0)), None, None), (AX.OID((1, 1)), None, None), (AX.OID((1, 1, 0)), None, None), (AX.OID((1, 2)), None, None), (AX.OID((2,)), None, None))) # Test tree with dynamic nodes and root path self.assertEqual(tuple(f({0: {"reader": None, "writer": None, "static": True, "subids": None}, 1: {"reader": None, "writer": None, "static": False, "subids": submaker}, 2: {"reader": None, "writer": None, "static": True, "subids": None}}, (23,))), ((AX.OID((23, 0)), None, None), (AX.OID((23, 1)), None, None), (AX.OID((23, 1, 0)), None, None), (AX.OID((23, 1, 1)), None, None), (AX.OID((23, 1, 1, 0)), None, None), (AX.OID((23, 1, 2)), None, None), (AX.OID((23, 2)), None, None))) def test_bits2Bools(self): bits2bool = AX.bits2Bools # Test empty self.assertEqual(bits2bool(""), []) # Test round bytes self.assertEqual(bits2bool("\xFA\xCE"), [True, True, True, True, True, False, True, False, True, True, False, False, True, True, True, False]) # Test partial bytes self.assertEqual(bits2bool("\xFF\xE1", 12), # The extra bit is to [True, True, True, True, # confirm crop True, True, True, True, True, True, True, False]) def test_bools2bits(self): bool2bits = AX.bools2Bits # Test empty self.assertEqual(bool2bits([]), "") # Test round bytes self.assertEqual(bool2bits([True, True, True, True, True, False, True, False, True, True, False, False, True, True, True, False]), "\xFA\xCE") # Test partial bytes self.assertEqual(bool2bits([True, True, True, True, True, True, True, True, True, True, True, False]), "\xFF\xE0") if __name__ == "__main__": unittest.main() ntpsec-1.1.0+dfsg1/tests/pylib/test_util.py0000644000175000017500000015405713252364117020510 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function import unittest import ntp.util import ntp.packet import shutil import sys import socket import jigs class TestPylibUtilMethods(unittest.TestCase): def test_deunicode_units(self): u = ntp.util self.assertEqual(u.UNIT_US, u"µs") self.assertEqual(u.UNIT_PPK, u"‰") self.assertEqual(u.UNITS_SEC, ["ns", u"µs", "ms", "s", "ks"]) self.assertEqual(u.UNITS_PPX, ["ppt", "ppb", "ppm", u"‰"]) u.deunicode_units() self.assertEqual(u.UNIT_US, "us") self.assertEqual(u.UNIT_PPK, "ppk") self.assertEqual(u.UNITS_SEC, ["ns", "us", "ms", "s", "ks"]) self.assertEqual(u.UNITS_PPX, ["ppt", "ppb", "ppm", "ppk"]) u.UNIT_US = u"µs" u.UNIT_PPK = u"‰" u.UNITS_SEC[1] = u.UNIT_US u.UNITS_PPX[3] = u.UNIT_PPK def test_dolog(self): f = ntp.util.dolog faketimemod = jigs.TimeModuleJig() # We need a test jig class LogTester: def __init__(self): self.written = None self.flushed = False def write(self, text): if self.written is None: self.written = "" self.written += text def flush(self): self.flushed = True try: timetemp = ntp.util.time ntp.util.time = faketimemod # Test with logging off (fd == None) # uh... if someone can think of a way to do that please tell me # Test with logging on, below threshold jig = LogTester() faketimemod.time_returns = [0, 1] f(jig, "blah", 0, 3) self.assertEqual((jig.written, jig.flushed), (None, False)) # Test with logging on, above threshold jig.__init__() # reset f(jig, "blah", 4, 3) self.assertEqual((jig.written, jig.flushed), ("1970-01-01T00:00:01Z blah\n", True)) finally: ntp.util.time = timetemp def test_safeargcast(self): f = ntp.util.safeargcast errjig = jigs.FileJig() try: errtemp = sys.stderr sys.stderr = errjig # Test successful int self.assertEqual(f("42", int, "blah %s", "\nDo the needful\n"), 42) self.assertEqual(errjig.data, []) # Test successful float self.assertAlmostEqual(f("5.23", float, "blah %s", "\nDo the needful\n"), 5.23) self.assertEqual(errjig.data, []) # Test failure try: f("23.5", int, "blah %s", "\nDo the needful\n") errored = False except SystemExit: errored = True self.assertEqual(errored, True) self.assertEqual(errjig.data, ["blah 23.5", "\nDo the needful\n"]) finally: sys.stderr = errtemp def test_stdversion(self): f = ntp.util.stdversion ver = str(ntp.version.VERSION) tick = str(ntp.version.VCS_TICK) date = str(ntp.version.VCS_DATE) self.assertEqual(f(), "ntpsec-" + ver + "+" + tick + " " + date) def test_rfc3339(self): f = ntp.util.rfc3339 self.assertEqual(f(1480999786), '2016-12-06T04:49:46Z') self.assertEqual(f(1480999786.5), '2016-12-06T04:49:46.5Z') # RFC 3339, 2 digits of seconds. # we round, but the spec is silent on rounding # Python 2 and 3 round differently if sys.version_info[0] < 3: self.assertEqual(f(1480999786.025), "2016-12-06T04:49:46.03Z") else: self.assertEqual(f(1480999786.025), "2016-12-06T04:49:46.025Z") def test_deformatNTPTime(self): f = ntp.util.deformatNTPTime # Test standard self.assertEqual(f("0x0001020304050607.08090A0B0C0D0E0F"), "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F") # Test empty self.assertEqual(f(""), "") def test_hexstr2octets(self): f = ntp.util.hexstr2octets # Test self.assertEqual(f("f00dface"), "\xF0\x0D\xFA\xCE") # Test odd length self.assertEqual(f("cafebabe1"), "\xCA\xFE\xBA\xBE") def test_slicedata(self): f = ntp.util.slicedata # Test normal self.assertEqual(f("foobaz", 2), ("fo", "obaz")) # Test overflow self.assertEqual(f("foobaz", 8), ("foobaz", "")) # Test underflow self.assertEqual(f("foobaz", -2), ("foob", "az")) def test_portsplit(self): self.assertEqual(ntp.util.portsplit("host.invalid"), ("host.invalid", "")) self.assertEqual(ntp.util.portsplit("host.invalid:123"), ("host.invalid", ":123")) self.assertEqual(ntp.util.portsplit( "[0000:1111:2222:3333:4444:5555:6666:7777]:123"), ("0000:1111:2222:3333:4444:5555:6666:7777", ":123")) def test_stringfilt(self): f = ntp.util.stringfilt self.assertEqual(f("1.2345 2.3456 3.4567 4.5678 5.6789 6.789"), " 1.2345 2.3456 3.4567 4.5678 5.6789 6.789") def test_oomsbetweenunits(self): f = ntp.util.oomsbetweenunits self.assertEqual(f(ntp.util.UNIT_KS, ntp.util.UNIT_MS), 6) self.assertEqual(f(ntp.util.UNIT_PPM, ntp.util.UNIT_PPB), 3) self.assertEqual(f(ntp.util.UNIT_S, None), 9) def test_scalestring(self): f = ntp.util.scalestring # Scale all decimals self.assertEqual(f("0.042"), ("42", -1)) # Unscalable self.assertEqual(f(".23"), ("0.23", 0)) # Typical length, positive value, no scaling self.assertEqual(f("1.23450"), ("1.23450", 0)) # Ditto, negative self.assertEqual(f("-1.23450"), ("-1.23450", 0)) # Long, positive value, no scaling self.assertEqual(f("1.234567890123456"), ("1.234567890123456", 0)) # ditto, negative self.assertEqual(f("-1.234567890123456"), ("-1.234567890123456", 0)) # Zero self.assertEqual(f("0.000000"), ("0", -2)) # Zero with point self.assertEqual(f("0.0000"), ("0.0", -1)) # Negative zero self.assertEqual(f("-0.000"), ("0", -1)) # Large, positive, non scaled self.assertEqual(f("987.654"), ("987.654", 0)) # ditto, negative self.assertEqual(f("-987.654"), ("-987.654", 0)) # Scaled to larger unit, positive self.assertEqual(f("12345.67890"), ("12.34567890", 1)) # ditto, negative self.assertEqual(f("-12345.67890"), ("-12.34567890", 1)) # Scaled to smaller unit, position self.assertEqual(f("0.1234567890"), ("123.4567890", -1)) # ditto, negative self.assertEqual(f("-0.1234567890"), ("-123.4567890", -1)) # Bizarre 1 self.assertEqual(f("-000.000012345678900987654321"), ("-12.345678900987654321", -2)) # Bizarre 2 self.assertEqual(f("1234567890987654321000.00000000000042"), ("1.23456789098765432100000000000000042", 7)) # Bizarre 3 self.assertEqual(f("00000000.000000000000"), ("0", -4)) def test_rescalestring(self): f = ntp.util.rescalestring # No-op self.assertEqual(f("1000.42", 0), "1000.42") # Scale to higher unit self.assertEqual(f("123.456", 1), "0.123456") # ditto, negative self.assertEqual(f("-123.456", 1), "-0.123456") # Scale to higher unit, beyond available digits self.assertEqual(f("12.34", 2), "0.00001234") # ditto, negative self.assertEqual(f("-12.34", 2), "-0.00001234") # Scale to lower unit self.assertEqual(f("1.23456", -1), "1234.56") # ditto, negative self.assertEqual(f("-1.23456", -1), "-1234.56") # Scale to lower unit, beyond available digits self.assertEqual(f("1.23456", -2), None) # ditto, negative self.assertEqual(f("-1.23456", -2), None) # Scale from below the decimal self.assertEqual(f("0.420", -1), "420") def test_breaknumberstring(self): f = ntp.util.breaknumberstring # No decimals, positive self.assertEqual(f("1234567890"), ("1234567890", "", False)) # ditto, negative self.assertEqual(f("-1234567890"), ("1234567890", "", True)) # No whole, positive self.assertEqual(f(".12345"), ("", "12345", False)) # ditto, negative self.assertEqual(f("-.12345"), ("", "12345", True)) # Both sides, position self.assertEqual(f("123.456"), ("123", "456", False)) # ditto, negative self.assertEqual(f("-123.456"), ("123", "456", True)) def test_gluenumberstring(self): f = ntp.util.gluenumberstring # No decimals, positive self.assertEqual(f("123", "", False), "123") # ditto, negative self.assertEqual(f("123", "", True), "-123") # No whole, positive self.assertEqual(f("", "456", False), "0.456") # ditto, negative self.assertEqual(f("", "456", True), "-0.456") # Both sides, positive self.assertEqual(f("123", "456", False), "123.456") # ditto, negative self.assertEqual(f("123", "456", True), "-123.456") def test_fitinfield(self): f = ntp.util.fitinfield # Field too small, crop decimals self.assertEqual(f("123.456", 5), "123.5") # ditto, negative self.assertEqual(f("-123.456", 5), "-123.") # Field too small, blow field self.assertEqual(f("12345.678", 4), "12346.") # Goldilocks self.assertEqual(f("123.456", 7), "123.456") # Field too big self.assertEqual(f("123.456", 10), " 123.456") # Rounding test, round down self.assertEqual(f("1.994", 4), "1.99") # Rounding test, round up self.assertEqual(f("1.995", 4), "2.00") # Attempt to catch bug self.assertEqual(f("15937.5", None), "15937.5") # Test to bid and no decimals self.assertEqual(f("123456", 4), "123456") def test_cropprecision(self): f = ntp.util.cropprecision # No decimals self.assertEqual(f("1234", 6), "1234") # Decimals, no crop self.assertEqual(f("12.3456", 6), "12.3456") # Decimals, crop self.assertEqual(f("12.3456", 3), "12.345") # At baseunit self.assertEqual(f("1.234", 0), "1") def test_isstringzero(self): f = ntp.util.isstringzero # Non-zero self.assertEqual(f("0.0000001"), False) # Zero self.assertEqual(f("-0.00"), True) def test_unitify(self): f = ntp.util.unitify nu = ntp.util # Zero self.assertEqual(f("0.000", nu.UNIT_MS), u" 0µs") # Zero, spaced self.assertEqual(f("0.000", nu.UNIT_MS, unitSpace=True), u" 0 µs") # Standard, width=8 self.assertEqual(f("1.234", nu.UNIT_MS), " 1.234ms") # ditto, negative self.assertEqual(f("-1.234", nu.UNIT_MS), "-1.234ms") # Scale to larger unit, width=8 self.assertEqual(f("1234.5", nu.UNIT_MS), " 1.2345s") # ditto, negative self.assertEqual(f("-1234.5", nu.UNIT_MS), "-1.2345s") # Scale to smaller unit, width=8 self.assertEqual(f("0.01234", nu.UNIT_MS), u" 12.34µs") # ditto, negative self.assertEqual(f("-0.01234", nu.UNIT_MS), u"-12.34µs") # At baseunit self.assertEqual(f("12.0", nu.UNIT_NS), " 12ns") # Scale to baseunit self.assertEqual(f(".042", nu.UNIT_US), " 42ns") # Below baseunit self.assertEqual(f("23.42", nu.UNIT_NS), " 23ns") # Different units self.assertEqual(f("12.345", nu.UNIT_PPM), "12.35ppm") # Strip self.assertEqual(f("1.23", nu.UNIT_MS, width=None), "1.23ms") # Different width self.assertEqual(f("1.234", nu.UNIT_MS, width=12), " 1.234ms") # Outside of available units self.assertEqual(f("1234.5", nu.UNIT_KS), "1234.5ks") # Seconds self.assertEqual(f("42.23", nu.UNIT_S), " 42.23s") # Attempt to catch bug self.assertEqual(f("15937.5", nu.UNIT_MS, width=None), "15.9375s") def test_stringfiltcooker(self): # No scale self.assertEqual(ntp.util.stringfiltcooker( "1.02 34.5 0.67835 -23.0 9 6.7 1.00 .1"), " 1.02 34.5 0.67835 -23.0 9 6.7 1.00 " "0.1 ms" ) # Scale to larger unit self.assertEqual(ntp.util.stringfiltcooker( "1000.02 3400.5 0.67835 -23.0 9001 6.7 1.00 1234"), "1.00002 3.4005 0.00068 -0.0230 9.001 0.0067 0.00100 " "1.234 s" ) # Scale to smaller unit self.assertEqual(ntp.util.stringfiltcooker( "0.470 0.420 0.430 0.500 0.460 0.4200 0.490 0.480"), u" 470 420 430 500 460 420.0 490 " u"480 µs") # Can't scale self.assertEqual(ntp.util.stringfiltcooker( "0.47 0.42 0.43 0.50 0.46 0.42 0.49 0.48"), " 0.47 0.42 0.43 0.50 0.46 0.42 0.49 " "0.48 ms") # Can't scale, only one value blocking self.assertEqual(ntp.util.stringfiltcooker( "0.47 0.4200 0.4300 0.5000 0.4600 0.4200 0.4900 0.4800"), " 0.47 0.4200 0.4300 0.5000 0.4600 0.4200 0.4900 " "0.4800 ms") def test_unitrelativeto(self): f = ntp.util.unitrelativeto # Scale to smaller unit self.assertEqual(f(ntp.util.UNIT_S, -1), ntp.util.UNIT_MS) # Scale to larger unit self.assertEqual(f(ntp.util.UNIT_S, 1), ntp.util.UNIT_KS) # Scale smaller, outside of range self.assertEqual(f(ntp.util.UNIT_S, -10), None) # Scale larger, outside of range self.assertEqual(f(ntp.util.UNIT_NS, 10), None) # Get base unit self.assertEqual(f(ntp.util.UNIT_KS, None), ntp.util.UNIT_NS) # Different unitgroup self.assertEqual(f(ntp.util.UNIT_PPM, -1), ntp.util.UNIT_PPB) def test_formatzero(self): f = ntp.util.formatzero # No scaling possible self.assertEqual(f("0.00"), ("0.00", 0)) # Scaling possible self.assertEqual(f("0.0000000"), ("0.0", -2)) # Scaling without a remainder self.assertEqual(f("0.000"), ("0", -1)) def test_unitifyvar(self): f = ntp.util.unitifyvar # Second var self.assertEqual(f("1.234", "tai"), " 1.234s") # Millisecond var self.assertEqual(f("1.234", "offset"), " 1.234ms") # PPM var self.assertEqual(f("1.234", "frequency"), "1.234ppm") # No unit type self.assertEqual(f("1.234", "blahblahblah"), "1.234") def test_f8dot4(self): f = ntp.util.f8dot4 # Test signal self.assertEqual(f("foo"), " foo") # Test bad type self.assertEqual(f({"food": "problematic"}), " X") # Test not a number self.assertEqual(f(float("nan")), " nan") # Test Positives # Test xxxxxxxx self.assertEqual(f(12345678.9), "12345678") # Test xxxxxx.x self.assertEqual(f(123456.789), "123456.8") # Test xxxxx.xx self.assertEqual(f(12345.6789), "12345.68") # Test xxxx.xxx self.assertEqual(f(1234.56789), "1234.568") # Test xxx.xxxx self.assertEqual(f(123.456789), "123.4568") # Test Negatives # Test -xxxxxxx self.assertEqual(f(-1234567.89), "-1234567") # Test -xxxxx.x self.assertEqual(f(-12345.6789), "-12345.7") # Test -xxxx.xx self.assertEqual(f(-1234.56789), "-1234.57") # Test -xxx.xxx self.assertEqual(f(-123.456789), "-123.457") # Test -xx.xxxx self.assertEqual(f(-12.3456789), "-12.3457") def test_f8dot3(self): f = ntp.util.f8dot3 # Test signal self.assertEqual(f("foo"), " foo") # Test bad type self.assertEqual(f({"food": "problematic"}), " X") # Test not a number self.assertEqual(f(float("nan")), " nan") # Test Positives # Test xxxxxxxx self.assertEqual(f(12345678.9), "12345678") # Test xxxxxx.x self.assertEqual(f(123456.789), "123456.8") # Test xxxxx.xx self.assertEqual(f(12345.6789), "12345.68") # Test xxxx.xxx self.assertEqual(f(1234.56789), "1234.568") # Test Negatives # Test -xxxxxxx self.assertEqual(f(-1234567.89), "-1234567") # Test -xxxxx.x self.assertEqual(f(-12345.6789), "-12345.7") # Test -xxxx.xx self.assertEqual(f(-1234.56789), "-1234.57") # Test -xxx.xxx self.assertEqual(f(-123.456789), "-123.457") def test_Cache(self): c = ntp.util.Cache monodata = [] def monoclock_jig(): return monodata.pop(0) # Test init cls = c() self.assertEqual(cls._cache, {}) try: monotemp = ntp.util.monoclock ntp.util.monoclock = monoclock_jig # Test set monodata = [5, 10, 315, 20] cls.set("foo", 42) cls.set("bar", 23) self.assertEqual(cls._cache, {"foo": (42, 5, 300), "bar": (23, 10, 300)}) self.assertEqual(monodata, [315, 20]) # Test get, expired result = cls.get("foo") self.assertEqual(result, None) self.assertEqual(monodata, [20]) self.assertEqual(cls._cache, {"bar": (23, 10, 300)}) # Test get, valid result = cls.get("bar") self.assertEqual(result, 23) self.assertEqual(monodata, []) self.assertEqual(cls._cache, {"bar": (23, 10, 300)}) # Test set, custom TTL monodata = [0, 0, 11, 15] cls.set("foo", 42, 10) cls.set("bar", 23, 20) self.assertEqual(cls._cache, {"foo": (42, 0, 10), "bar": (23, 0, 20)}) self.assertEqual(monodata, [11, 15]) # Test get, expired, custom TTL result = cls.get("foo") self.assertEqual(result, None) self.assertEqual(monodata, [15]) self.assertEqual(cls._cache, {"bar": (23, 0, 20)}) # Test get, valid, custom TTL result = cls.get("bar") self.assertEqual(result, 23) self.assertEqual(monodata, []) self.assertEqual(cls._cache, {"bar": (23, 0, 20)}) finally: ntp.util.monoclock = monotemp def test_canonicalize_dns(self): f = ntp.util.canonicalize_dns fakesockmod = jigs.SocketModuleJig() mycache = ntp.util.Cache() mycache.set("foo", "bar") try: cachetemp = ntp.util.canonicalization_cache ntp.util.canonicalization_cache = mycache sockettemp = ntp.util.socket ntp.util.socket = fakesockmod # Test cache hit self.assertEqual(f("foo"), "bar") self.assertEqual(fakesockmod.gai_calls, []) # Test addrinfo fail fakesockmod.__init__() fakesockmod.gai_error_count = 1 self.assertEqual(f("nothing"), "DNSFAIL:nothing") self.assertEqual(fakesockmod.gai_calls, [("nothing", None, 0, 0, 0, socket.AI_CANONNAME)]) self.assertEqual(fakesockmod.gni_calls, []) # Test nameinfo fail fakesockmod.__init__() fakesockmod.gni_error_count = 1 fakesockmod.gni_returns = [("www.Hastur.invalid", 42)] fakesockmod.gai_returns = [(("family", "socktype", "proto", "san.Hastur.invalid", "42.23.%$.(#"),)] self.assertEqual(f("bar:42"), "san.hastur.invalid:42") # Test nameinfo fail, no canonname fakesockmod.__init__() mycache.__init__() fakesockmod.gni_error_count = 1 fakesockmod.gni_returns = [("www.Hastur.invalid", 42)] fakesockmod.gai_returns = [(("family", "socktype", "proto", None, "42.23.%$.(#"),)] self.assertEqual(f("bar:42"), "bar:42") # Test success fakesockmod.__init__() mycache.__init__() fakesockmod.gni_returns = [("www.Hastur.invalid", 42)] fakesockmod.gai_returns = [(("family", "socktype", "proto", None, "42.23.%$.(#"),)] self.assertEqual(f("bar:42"), "www.hastur.invalid:42") finally: ntp.util.canonicalization_cache = cachetemp ntp.util.socket = sockettemp def test_termsize(self): f = ntp.util.termsize fakeosmod = jigs.OSModuleJig() fakefcntlmod = jigs.FcntlModuleJig() fakeshutilmod = jigs.ShutilModuleJig() try: ostemp = ntp.util.os ntp.util.os = fakeosmod # Test default fakeosmod.isatty_returns = [False] self.assertEqual(f(), (80, 24)) self.assertEqual(fakeosmod.isatty_calls, [1]) # termsize takes different code paths for different # versions of Python if str is not bytes: # Python 3 version try: shutiltemp = ntp.util.shutil ntp.util.shutil = fakeshutilmod fakeosmod.isatty_returns = [True] fakeshutilmod.gts_returns = [(42, 23)] self.assertEqual(f(), (42, 23)) finally: ntp.util.shutil = shutiltemp else: # Python 2.x version try: import fcntl fcntltemp = fcntl ntp.util.fcntl = fakefcntlmod fakeosmod.isatty_returns = [True] data = ["\x11\x11\x22\x22\x33\x33\x44\x44"] fakefcntlmod.ioctl_returns = data self.assertEqual(f(), (0x2222, 0x1111)) self.assertEqual(fakefcntlmod.ioctl_calls, [(2, ntp.util.termios.TIOCGWINSZ, "\0\0\0\0\0\0\0\0", False)]) finally: ntp.util.fcntl = fcntltemp finally: ntp.util.os = ostemp def test_PeerStatusWord(self): c = ntp.util.PeerStatusWord # Test blank status cls = c(0) self.assertEqual(cls.event, 0) self.assertEqual(cls.event_count, 0) self.assertEqual(cls.conf, "no") self.assertEqual(cls.reach, "no") self.assertEqual(cls.auth, "none") self.assertEqual(cls.condition, "reject") self.assertEqual(cls.last_event, "") # Test max status cls = c(0xFFFF) self.assertEqual(cls.event, 15) self.assertEqual(cls.event_count, 15) self.assertEqual(cls.conf, "yes") self.assertEqual(cls.reach, "none") self.assertEqual(cls.auth, "ok ") self.assertEqual(cls.condition, "pps.peer") self.assertEqual(cls.last_event, "") # Test __str__ of max status self.assertEqual(str(cls), "conf=yes, reach=none, auth=ok , " "cond=pps.peer, event= ec=15") # Test third options cls = c(0x57FF) self.assertEqual(cls.event, 15) self.assertEqual(cls.event_count, 15) self.assertEqual(cls.conf, "no") self.assertEqual(cls.reach, "yes") self.assertEqual(cls.auth, "bad") self.assertEqual(cls.condition, "pps.peer") self.assertEqual(cls.last_event, "") # Test all newer than OLDVERSION conditions cls = c(0x0000) self.assertEqual(cls.condition, "reject") cls = c(0x0100) self.assertEqual(cls.condition, "falsetick") cls = c(0x0200) self.assertEqual(cls.condition, "excess") cls = c(0x0300) self.assertEqual(cls.condition, "outlier") cls = c(0x0400) self.assertEqual(cls.condition, "candidate") cls = c(0x0500) self.assertEqual(cls.condition, "backup") cls = c(0x0600) self.assertEqual(cls.condition, "sys.peer") cls = c(0x0700) self.assertEqual(cls.condition, "pps.peer") # Test all older than OLDVERSION conditions cls = c(0xF400, 0) self.assertEqual(cls.condition, "insane") cls = c(0xF800, 0) self.assertEqual(cls.condition, "hi_disp") cls = c(0xFC00, 0) self.assertEqual(cls.condition, "") cls = c(0xF100, 0) self.assertEqual(cls.condition, "sel_cand") cls = c(0xF200, 0) self.assertEqual(cls.condition, "sync_cand") cls = c(0xF300, 0) self.assertEqual(cls.condition, "sys_peer") # Test all last events cls = c(0xFFF1) self.assertEqual(cls.last_event, "mobilize") cls = c(0xFFF2) self.assertEqual(cls.last_event, "demobilize") cls = c(0xFFF3) self.assertEqual(cls.last_event, "unreachable") cls = c(0xFFF4) self.assertEqual(cls.last_event, "reachable") cls = c(0xFFF5) self.assertEqual(cls.last_event, "restart") cls = c(0xFFF6) self.assertEqual(cls.last_event, "no_reply") cls = c(0xFFF7) self.assertEqual(cls.last_event, "rate_exceeded") cls = c(0xFFF8) self.assertEqual(cls.last_event, "access_denied") cls = c(0xFFF9) self.assertEqual(cls.last_event, "leap_armed") cls = c(0xFFFA) self.assertEqual(cls.last_event, "sys_peer") cls = c(0xFFFB) self.assertEqual(cls.last_event, "clock_alarm") def test_cook(self): f = ntp.util.cook od = ntp.util.OrderedDict termsize = (80, 24) def termsize_jig(): return ntp.util.TermSize(*termsize) try: termtemp = ntp.util.termsize ntp.util.termsize = termsize_jig # Test empty self.assertEqual(f(od()), "\n") # Test prettydates data = od((("reftime", ("0x00000000.00000000", "0x00000000.00000000")), ("clock", ("0x10000000.00000000", "0x10000000.00000000")), ("org", ("0x20000000.00000000", "0x20000000.00000000")), ("rec", ("0x30000000.00000000", "0x30000000.00000000")), ("xmt", ("0x40000000.00000000", "0x40000000.00000000")))) self.assertEqual(f(data), "reftime=00000000.00000000 " "2036-02-07T06:28:16.000Z,\n" "clock=10000000.00000000 " "1908-07-04T21:24:16.000Z,\n" "org=20000000.00000000 " "1917-01-05T18:48:32.000Z,\n" "rec=30000000.00000000 " "1925-07-09T16:12:48.000Z,\n" "xmt=40000000.00000000 " "1934-01-10T13:37:04.000Z\n") # Test prettydates, with units self.assertEqual(f(data, showunits=True), "reftime=00000000.00000000 " "2036-02-07T06:28:16.000Z,\n" "clock=10000000.00000000 " "1908-07-04T21:24:16.000Z,\n" "org=20000000.00000000 " "1917-01-05T18:48:32.000Z,\n" "rec=30000000.00000000 " "1925-07-09T16:12:48.000Z,\n" "xmt=40000000.00000000 " "1934-01-10T13:37:04.000Z\n") # Test wide terminal termsize = (160, 24) self.assertEqual(f(data), "reftime=00000000.00000000 " "2036-02-07T06:28:16.000Z, " "clock=10000000.00000000 " "1908-07-04T21:24:16.000Z, " "org=20000000.00000000 " "1917-01-05T18:48:32.000Z,\n" "rec=30000000.00000000 " "1925-07-09T16:12:48.000Z, " "xmt=40000000.00000000 " "1934-01-10T13:37:04.000Z\n") # Test narrow terminal termsize = (40, 24) self.assertEqual(f(data), "\nreftime=00000000.00000000 " "2036-02-07T06:28:16.000Z,\n" "clock=10000000.00000000 " "1908-07-04T21:24:16.000Z,\n" "org=20000000.00000000 " "1917-01-05T18:48:32.000Z,\n" "rec=30000000.00000000 " "1925-07-09T16:12:48.000Z,\n" "xmt=40000000.00000000 " "1934-01-10T13:37:04.000Z\n") termsize = (80, 24) # Test ex-obscure cooking data = od((("srcadr", ("1.1.1.1", "1.1.1.1")), ("peeradr", ("2.2.2.2", "2.2.2.2")), ("dstadr", ("3.3.3.3", "3.3.3.3")), ("refid", ("blah", "blah")))) self.assertEqual(f(data), "srcadr=1.1.1.1, peeradr=2.2.2.2, " "dstadr=3.3.3.3, refid=blah\n") # Test ex-obscure cooking self.assertEqual(f(data, showunits=True), "srcadr=1.1.1.1, peeradr=2.2.2.2, " "dstadr=3.3.3.3, refid=blah\n") # Test leap self.assertEqual(f(od((("leap", (0, "0")),)), showunits=True), "leap=00\n") self.assertEqual(f(od((("leap", (1, "1")),)), showunits=True), "leap=01\n") self.assertEqual(f(od((("leap", (2, "2")),)), showunits=True), "leap=10\n") self.assertEqual(f(od((("leap", (3, "3")),)), showunits=True), "leap=11\n") # Test leap self.assertEqual(f(od((("leap", (0, "0")),))), "leap=00\n") self.assertEqual(f(od((("leap", (1, "1")),))), "leap=01\n") self.assertEqual(f(od((("leap", (2, "2")),))), "leap=10\n") self.assertEqual(f(od((("leap", (3, "3")),))), "leap=11\n") # Test leap, with units self.assertEqual(f(od((("leap", (0, "0")),)), showunits=True), "leap=00\n") self.assertEqual(f(od((("leap", (1, "1")),)), showunits=True), "leap=01\n") self.assertEqual(f(od((("leap", (2, "2")),)), showunits=True), "leap=10\n") self.assertEqual(f(od((("leap", (3, "3")),)), showunits=True), "leap=11\n") # Test reach self.assertEqual(f(od((("reach", (1, "1")),))), "reach=001\n") # Test reach, with units self.assertEqual(f(od((("reach", (1, "1")),)), showunits=True), "reach=001\n") # Test specials data = od((("filtdelay", ("1 2 3", "1 2 3")), ("filtoffset", ("2 3 4", "2 3 4")), ("filtdisp", ("3 4 5", "3 4 5")), ("filterror", ("4 5 6", "4 5 6")))) self.assertEqual(f(data), " filtdelay =1\t2\t3, filtoffset =2\t3\t4, " "filtdisp =3\t4\t5, filterror =4\t5\t6\n") # Test specials, with units self.assertEqual(f(data, showunits=True), " filtdelay = 1 2 3 ms,\n" "filtoffset = 2 3 4 ms,\n" " filtdisp = 3 4 5 ms,\n" " filterror = 4 5 6 ms\n") # Test flash null self.assertEqual(f(od((("flash", (0, "0")),))), "flash=00 ok\n") # Test flash all bits self.assertEqual(f(od((("flash", (65535, "65535")),))), "\nflash=ffff pkt_dup pkt_bogus pkt_unsync " "pkt_denied pkt_auth pkt_stratum pkt_header " "pkt_autokey pkt_crypto peer_stratum " "peer_dist peer_loop peer_unreach\n") # Test MS_VARS data = od((("rootdelay", (0, "0")), ("rootdisp", (1, "1")), ("offset", (2, "2")), ("sys_jitter", (3, "3")), ("clk_jitter", (4, "4")), ("leapsmearoffset", (5, "5")), ("authdelay", (6, "6")), ("koffset", (7, "7")), ("kmaxerr", (8, "8")), ("kesterr", (9, "9")), ("kprecis", (10, "10")), ("kppsjitter", (11, "11")), ("fuzz", (12, "12")), ("clk_wander_threshold", (13, "13")), ("tick", (14, "14")), ("in", (15, "15")), ("out", (16, "16")), ("bias", (17, "17")), ("delay", (18, "18")), ("jitter", (19, "19")), ("dispersion", (20, "20")), ("fudgetime1", (21, "21")), ("fudgetime2", (21, "21")))) self.assertEqual(f(data), "rootdelay=0, rootdisp=1, offset=2, " "sys_jitter=3, clk_jitter=4,\nleapsmearoffset=5, " "authdelay=6, koffset=7, kmaxerr=8, kesterr=9, " "kprecis=10,\nkppsjitter=11, fuzz=12, " "clk_wander_threshold=13, tick=14, in=15, " "out=16,\nbias=17, delay=18, jitter=19, " "dispersion=20, fudgetime1=21, fudgetime2=21\n") # Test MS_VARS, with units self.assertEqual(f(data, showunits=True), "rootdelay=0ms, rootdisp=1ms, offset=2ms, " "sys_jitter=3ms, clk_jitter=4ms,\n" "leapsmearoffset=5ms, authdelay=6ms, " "koffset=7ms, kmaxerr=8ms, kesterr=9ms,\n" "kprecis=10ms, kppsjitter=11ms, fuzz=12ms, " "clk_wander_threshold=13ms,\ntick=14ms, in=15ms, " "out=16ms, bias=17ms, delay=18ms, jitter=19ms,\n" "dispersion=20ms, fudgetime1=21ms, " "fudgetime2=21ms\n") # Test S_VARS data = od((("tai", (0, "0")), ("poll", (1, "1")))) self.assertEqual(f(data), "tai=0, poll=1\n") # Test S_VARS, with units self.assertEqual(f(data, showunits=True), "tai=0s, poll=1s\n") # Test PPM_VARS data = od((("frequency", (0, "0")), ("clk_wander", (1, "1")))) self.assertEqual(f(data), "frequency=0, clk_wander=1\n") # Test PPM_VARS, with units self.assertEqual(f(data, showunits=True), "frequency=0ppm, clk_wander=1ppm\n") # Test unrecognized variable data = od((("yeahyeah", (1, "1")),)) self.assertEqual(f(data), "yeahyeah=1\n") finally: ntp.util.termsize = termtemp def test_MRUSummary(self): c = ntp.util.MRUSummary e = ntp.packet.MRUEntry fakesockmod = jigs.SocketModuleJig() mycache = ntp.util.Cache() cdns_jig_calls = [] cdns_jig_returns = [] def cdns_jig(ip): # canonicalize_dns() cdns_jig_calls.append(ip) return cdns_jig_returns.pop(0) # Test init cls = c(False, debug=3) self.assertEqual(cls.debug, 3) self.assertEqual(cls.logfp, sys.stderr) self.assertEqual(cls.now, None) self.assertEqual(cls.showhostnames, False) self.assertEqual(cls.wideremote, False) try: socktemp = ntp.util.socket ntp.util.socket = fakesockmod cachetemp = ntp.util.canonicalization_cache ntp.util.canonicalization_cache = mycache cdnstemp = ntp.util.canonicalize_dns ntp.util.canonicalize_dns = cdns_jig # Test summary, first options ent = e() ent.first = "0x00000000.00000000" ent.last = "0xFFFFFFFF.FFFFFFFF" ent.ct = 1 ent.rs = ntp.magic.RES_KOD ent.addr = "1.2.3.4:42" ent.mv = 0x17 fakesockmod.gai_returns = [("fam", "type", "proto", "foo.bar.com", "1.2.3.4")] self.assertEqual(cls.summary(ent), "64730 23296 0 400 K 7 2" " 1 42 1.2.3.4") # Test summary, second options mycache._cache = {} cls.now = 0x00000000 cls.showhostnames = True ent.first = "0x00000100.00000000" ent.last = "0x00000200.00000000" ent.ct = 65 ent.rs = ntp.magic.RES_LIMITED fakesockmod.gai_returns = [[("fam", "type", "proto", "foo.bar.com", ("1.2.3.4", 42))]] cdns_jig_returns = ["foo.com"] self.assertEqual(cls.summary(ent), "64730 23808 4.00 20 L 7 2 65" " 42 foo.com") # Test summary, third options mycache._cache = {} ent.ct = 2 ent.rs = 0 fakesockmod.gai_error_count = 1 cdns_jig_returns = ["foobarbaz" * 5] # 45 chars, will be cropped self.assertEqual(cls.summary(ent), "64730 23808 256 0 . 7 2 2 42" " 1.2.3.4 (foobarbazfoobarbazfoobarbazfoob") # Test summary, wide mycache._cache = {} cls.wideremote = True fakesockmod.gai_error_count = 1 cdns_jig_returns = ["foobarbaz" * 5] # 45 chars, will be cropped self.assertEqual(cls.summary(ent), "64730 23808 256 0 . 7 2 2" " 42 1.2.3.4 " "(foobarbazfoobarbazfoobarbazfoobarbazfoobarbaz)") finally: ntp.util.socket = socktemp ntp.util.canonicalization_cache = cachetemp ntp.util.canonicalize_dns = cdnstemp def test_ReslistSummary(self): c = ntp.util.ReslistSummary m = ntp.util.ReslistSummary._ReslistSummary__getPrefix # Test __getPrefix # Test empty self.assertEqual(m(""), "/0") # Test base 16 self.assertEqual(m("FF:FF:F0:00"), "/20") # Test base 10 self.assertEqual(m("255.240.0.0"), "/12") # Test summary # Test with everything cls = c() data = {"hits": "blah", "addr": "42.23.1.2", "mask": "FF:FF:0:0", "flags": "qwerty"} self.assertEqual(cls.summary(data), " blah 42.23.1.2/16\n qwerty\n") # Test with missing data data = {"addr": "42.23.1.2", "mask": "FF:FF:0:0"} self.assertEqual(cls.summary(data), "") def test_IfstatsSummary(self): c = ntp.util.IfstatsSummary od = ntp.util.OrderedDict cls = c() # Test with all variables available data = od((("addr", "1.2.3.4"), ("bcast", "foo"), ("name", "Namus Maximus"), ("en", True), ("flags", 0xFACE), ("rx", 12), ("tx", 34), ("txerr", 56), ("pc", 78), ("up", 90))) self.assertEqual(cls.summary(1, data), " 1 Namus Maximus . face 12 34" " 56 78 90\n 1.2.3.4\n foo\n") # Test without bcast data = od((("addr", "1.2.3.4"), ("name", "Namus Maximus"), ("en", True), ("flags", 0xFACE), ("rx", 12), ("tx", 34), ("txerr", 56), ("pc", 78), ("up", 90))) self.assertEqual(cls.summary(1, data), " 1 Namus Maximus . face 12 34" " 56 78 90\n 1.2.3.4\n") # Test with missing data self.assertEqual(cls.summary(1, od()), "") class TestPeerSummary(unittest.TestCase): target = ntp.util.PeerSummary def test___init__(self): cls = self.target("peers", 4, True, False) self.assertEqual(cls.displaymode, "peers") self.assertEqual(cls.pktversion, 4) self.assertEqual(cls.showhostnames, True) self.assertEqual(cls.showunits, False) self.assertEqual(cls.wideremote, False) self.assertEqual(cls.debug, 0) self.assertEqual(cls.logfp, sys.stderr) self.assertEqual(cls.termwidth, None) self.assertEqual(cls.horizontal_slack, 0) self.assertEqual(cls.namewidth, 15) self.assertEqual(cls.refidwidth, 15) self.assertEqual(cls._PeerSummary__remote, " remote ") self.assertEqual(cls._PeerSummary__common, "st t when poll reach delay offset ") self.assertEqual(cls._PeerSummary__header, None) self.assertEqual(cls.polls, []) def test_prettyinterval(self): m = self.target.prettyinterval # Test invalid self.assertEqual(m("Failure"), "-") # Test <=2048 self.assertEqual(m(2048), "2048") # Test <=300 self.assertEqual(m(17971), "300m") # Test <=96 self.assertEqual(m(343831), "96h") # Test final self.assertEqual(m(350000), "4d") def test_high_truncate(self): m = self.target.high_truncate # Test fit self.assertEqual(m("foo", 10), "foo") # Test doesn't fit self.assertEqual(m("lorem ipsum", 5), "-psum") def test_is_clock(self): m = self.target.is_clock # Test True data = {"srchost": "(would)"} self.assertEqual(m(data), True) # Test False, not a clock data = {"srchost": "wouldn't"} self.assertEqual(m(data), False) # Test False, no host data = {"blah": "definitely wouldn't"} self.assertEqual(m(data), False) def test_header(self): # Test peers cls = self.target("peers", 4, True, False) self.assertEqual(cls.header(), " remote refid st t when " "poll reach delay offset jitter") # Test opeers cls = self.target("opeers", 4, True, False) self.assertEqual(cls.header(), " remote local st t when " "poll reach delay offset disp") # Test apeers cls = self.target("apeers", 4, True, False) self.assertEqual(cls.header(), " remote refid assid st t when " "poll reach delay offset jitter") def test_width(self): cls = self.target("peers", 4, True, False) self.assertEqual(cls.width(), 79) cls.horizontal_slack = 10 self.assertEqual(cls.width(), 89) def test_summary(self): cls = self.target("peers", 4, True, False) cls.header() cdns_jig_calls = [] cdns_jig_returns = [] def cdns_jig(ip): # canonicalize_dns() cdns_jig_calls.append(ip) return cdns_jig_returns.pop(0) data = ntp.util.OrderedDict((("delay", (1.234567, "1.234567")), ("dstadr", ("1.2.3.4", "1.2.3.4")), ("dstport", ("blah0", "blah0")), ("filtdelay", ("blah1", "blah1")), ("filtdisp", ("blah2", "blah2")), ("filtoffset", ("blah3", "blah3")), ("flash", ("blah4", "blah4")), ("headway", ("blah5", "blah5")), ("hmode", (6, "6")), ("hpoll", (12, "12")), ("jitter", (3.14159, "3.14159")), ("keyid", ("blah6", "blah6")), ("leap", ("blah7", "blah7")), ("offset", (2.71828, "2.71828")), ("pmode", ("blah8", "blah8")), ("ppoll", (5, "5")), ("precision", ("blah9", "blah9")), ("reach", (500, "500")), ("refid", ("FAIL", "FAIL")), ("rec", ("0x00000000.00000000", "0x00000000.00000000")), ("reftime", ("0x00001000.00000000", "0x00001000.00000000")), ("rootdelay", ("blah10", "blah10")), ("rootdisp", (299792.458, "299792.458")), ("srcadr", ("10.20.30.40", "10.20.30.40")), ("srchost", ("15.25.35.45", "15.25.35.45")), ("scrport", ("blah11", "blah11")), ("stratum", (8, "8")), ("ttl", ("blah12", "blah12")), ("unreach", ("blah13", "blah13")), ("xmt", ("blah14", "blah14")), ("randomness!", ("foo!", "bar!")))) faketimemod = jigs.TimeModuleJig() try: timetemp = ntp.util.time ntp.util.time = faketimemod cdnstemp = ntp.util.canonicalize_dns ntp.util.canonicalize_dns = cdns_jig # Test, no units, hmode=BCLIENTX, peers cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 b 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=BROADCAST, not multicast data["hmode"] = (5, "5") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 B 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=BROADCAST, not multicast data["srcadr"] = ("224.2.3.4", "224.2.3.4") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 M 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=CLIENT, local refclock data["srcadr"] = ("10.20.30.40", "10.20.30.40") data["hmode"] = (3, "3") data["srchost"] = ("(blah)", "(blah)") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 l 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=CLIENT, pool data["srchost"] = ("15.25.35.45", "15.25.35.45") data["refid"] = ("POOL", "POOL") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .POOL. 8 p 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=CLIENT, manycast client data["srcadr"] = ("224.2.3.4", "224.2.3.4") data["refid"] = ("FAIL", "FAIL") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 a 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=CLIENT, unicast data["srcadr"] = ("10.20.30.40", "10.20.30.40") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 u 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=ACTIVE data["hmode"] = (1, "1") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 s 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, hmode=PASSIVE data["hmode"] = (2, "2") cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 S 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, don't show hostnames cls.showhostnames = False cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#10.20.30.40 .FAIL. 8 S 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, name crop cls.showhostnames = True cdns_jig_returns = ["clock_canon_blah_jabber_quantum"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon_bla .FAIL. 8 S 6926" " 32 764 1.2346 2.7183 3.1416\n") # Test, no units, no name crop cls.wideremote = True cdns_jig_returns = ["clock_canon_blah_jabber_quantum"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon_blah_jabber_quantum\n" " .FAIL. 8 S" " 6926 32 764 1.2346 2.7183 3.1416\n") # Test, with units cls.showunits = True cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 8 S 6926" " 32 764 1.2346ms 2.7183ms 3.1416ms\n") # Test, low precision formatting with units lowpdata = data.copy() lowpdata["delay"] = (1.234, "1.234") cls.showunits = True cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, lowpdata, 12345), "#clock_canon .FAIL. 8 S 6926" " 32 764 1.234ms 2.7183ms 3.1416ms\n") # Test, low precision formatting with units cls.showunits = False cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, lowpdata, 12345), "#clock_canon .FAIL. 8 S 6926" " 32 764 1.234 2.718 3.142\n") # Test, apeers cls.showunits = True cls.displaymode = "apeers" cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] self.assertEqual(cls.summary(0x500, data, 12345), "#clock_canon .FAIL. 12345 8 S 6926" " 32 764 1.2346ms 2.7183ms 3.1416ms\n") # Test rstatus, previous version cls.showunits = True cls.displaymode = "apeers" cdns_jig_returns = ["clock_canon"] faketimemod.time_returns = [0xA0000000] cls.pktversion = 0 self.assertEqual(cls.summary(0x300, data, 12345), "*clock_canon .FAIL. 12345 8 S 6926" " 32 764 1.2346ms 2.7183ms 3.1416ms\n") finally: ntp.util.time = timetemp ntp.util.canonicalize_dns = cdnstemp def test_intervals(self): cls = self.target("peers", 4, True, False) cls.polls = [1, 2, 3, 4, 5] self.assertEqual(cls.intervals(), [1, 2, 3, 4, 5]) self.assertEqual(cls.polls, []) if __name__ == '__main__': unittest.main() ntpsec-1.1.0+dfsg1/tests/unity/0000775000175000017500000000000013252650651016142 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/tests/unity/unity.h0000644000175000017500000020166513252364117017472 0ustar rlaagerrlaager/* ========================================== Unity Project - A Test Framework for C Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams [Released under MIT License. Please refer to license.txt for details] ========================================== */ #ifndef UNITY_FRAMEWORK_H #define UNITY_FRAMEWORK_H #define UNITY #ifdef __cplusplus extern "C" { #endif #include "unity_internals.h" /*------------------------------------------------------- * Test Setup / Teardown *-------------------------------------------------------*/ /* These functions are intended to be called before and after each test. */ void setUp(void); void tearDown(void); /* These functions are intended to be called at the beginning and end of an * entire test suite. suiteTearDown() is passed the number of tests that * failed, and its return value becomes the exit code of main(). */ void suiteSetUp(void); int suiteTearDown(int num_failures); /* If the compiler supports it, the following block provides stub * implementations of the above functions as weak symbols. Note that on * some platforms (MinGW for example), weak function implementations need * to be in the same translation unit they are called from. This can be * achieved by defining UNITY_INCLUDE_SETUP_STUBS before including unity.h. */ #ifdef UNITY_INCLUDE_SETUP_STUBS #ifdef UNITY_WEAK_ATTRIBUTE UNITY_WEAK_ATTRIBUTE void setUp(void) { } UNITY_WEAK_ATTRIBUTE void tearDown(void) { } UNITY_WEAK_ATTRIBUTE void suiteSetUp(void) { } UNITY_WEAK_ATTRIBUTE int suiteTearDown(int num_failures) { return num_failures; } #elif defined(UNITY_WEAK_PRAGMA) #pragma weak setUp void setUp(void) { } #pragma weak tearDown void tearDown(void) { } #pragma weak suiteSetUp void suiteSetUp(void) { } #pragma weak suiteTearDown int suiteTearDown(int num_failures) { return num_failures; } #endif #endif /*------------------------------------------------------- * Configuration Options *------------------------------------------------------- * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. * Integers/longs/pointers * - Unity attempts to automatically discover your integer sizes * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in * - If you cannot use the automatic methods above, you can force Unity by using these options: * - define UNITY_SUPPORT_64 * - set UNITY_INT_WIDTH * - set UNITY_LONG_WIDTH * - set UNITY_POINTER_WIDTH * Floats * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE * - define UNITY_DOUBLE_TYPE to specify something other than double * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors * Output * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure * Optimization * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. * Test Cases * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script * Parameterized Tests * - you'll want to create a define of TEST_CASE(...) which basically evaluates to nothing * Tests with Arguments * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity *------------------------------------------------------- * Basic Fail and Ignore *-------------------------------------------------------*/ #define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) #define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) #define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) #define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) #define TEST_ONLY() /* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ #define TEST_PASS() TEST_ABORT() /* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */ #define TEST_FILE(a) /*------------------------------------------------------- * Test Asserts (simple) *-------------------------------------------------------*/ /* Boolean */ #define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") #define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") #define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") #define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") #define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") #define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") /* Integers (of all sizes) */ #define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") #define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) #define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, NULL) #define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) #define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, NULL) /* Integer Greater Than/ Less Than (of all sizes) */ #define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) #define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) /* Integer Ranges (of all sizes) */ #define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) /* Structs and Strings */ #define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) #define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) /* Arrays */ #define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) /* Arrays Compared To Single Value */ #define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) /* Floating Point (If Enabled) */ #define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) #define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) /* Double (If Enabled) */ #define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) #define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) #define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) /*------------------------------------------------------- * Test Asserts (with additional messages) *-------------------------------------------------------*/ /* Boolean */ #define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) #define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) #define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) #define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) #define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) #define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) /* Integers (of all sizes) */ #define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) #define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) #define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) #define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) /* Integer Greater Than/ Less Than (of all sizes) */ #define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) #define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) /* Integer Ranges (of all sizes) */ #define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) /* Structs and Strings */ #define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) #define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) /* Arrays */ #define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) /* Arrays Compared To Single Value*/ #define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) /* Floating Point (If Enabled) */ #define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) #define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) /* Double (If Enabled) */ #define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) #define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) /* end of UNITY_FRAMEWORK_H */ #ifdef __cplusplus } #endif #endif ntpsec-1.1.0+dfsg1/tests/unity/unity_internals.h0000644000175000017500000020771113252364117021547 0ustar rlaagerrlaager/* ========================================== Unity Project - A Test Framework for C Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams [Released under MIT License. Please refer to license.txt for details] ========================================== */ #ifndef UNITY_INTERNALS_H #define UNITY_INTERNALS_H #ifdef UNITY_INCLUDE_CONFIG_H #include "unity_config.h" #endif #ifndef UNITY_EXCLUDE_SETJMP_H #include #endif #ifndef UNITY_EXCLUDE_MATH_H #include #endif /* Unity Attempts to Auto-Detect Integer Types * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits * Attempt 2: UINTPTR_MAX in , or default to same size as long * The user may override any of these derived constants: * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ #ifndef UNITY_EXCLUDE_STDINT_H #include #endif #ifndef UNITY_EXCLUDE_LIMITS_H #include #endif /*------------------------------------------------------- * Guess Widths If Not Specified *-------------------------------------------------------*/ /* Determine the size of an int, if not already specified. * We cannot use sizeof(int), because it is not yet defined * at this stage in the translation of the C program. * Therefore, infer it from UINT_MAX if possible. */ #ifndef UNITY_INT_WIDTH #ifdef UINT_MAX #if (UINT_MAX == 0xFFFF) #define UNITY_INT_WIDTH (16) #elif (UINT_MAX == 0xFFFFFFFF) #define UNITY_INT_WIDTH (32) #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) #define UNITY_INT_WIDTH (64) #endif #else /* Set to default */ #define UNITY_INT_WIDTH (32) #endif /* UINT_MAX */ #endif /* Determine the size of a long, if not already specified. */ #ifndef UNITY_LONG_WIDTH #ifdef ULONG_MAX #if (ULONG_MAX == 0xFFFF) #define UNITY_LONG_WIDTH (16) #elif (ULONG_MAX == 0xFFFFFFFF) #define UNITY_LONG_WIDTH (32) #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) #define UNITY_LONG_WIDTH (64) #endif #else /* Set to default */ #define UNITY_LONG_WIDTH (32) #endif /* ULONG_MAX */ #endif /* Determine the size of a pointer, if not already specified. */ #ifndef UNITY_POINTER_WIDTH #ifdef UINTPTR_MAX #if (UINTPTR_MAX <= 0xFFFF) #define UNITY_POINTER_WIDTH (16) #elif (UINTPTR_MAX <= 0xFFFFFFFF) #define UNITY_POINTER_WIDTH (32) #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) #define UNITY_POINTER_WIDTH (64) #endif #else /* Set to default */ #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH #endif /* UINTPTR_MAX */ #endif /*------------------------------------------------------- * Int Support (Define types based on detected sizes) *-------------------------------------------------------*/ #if (UNITY_INT_WIDTH == 32) typedef unsigned char UNITY_UINT8; typedef unsigned short UNITY_UINT16; typedef unsigned int UNITY_UINT32; typedef signed char UNITY_INT8; typedef signed short UNITY_INT16; typedef signed int UNITY_INT32; #elif (UNITY_INT_WIDTH == 16) typedef unsigned char UNITY_UINT8; typedef unsigned int UNITY_UINT16; typedef unsigned long UNITY_UINT32; typedef signed char UNITY_INT8; typedef signed int UNITY_INT16; typedef signed long UNITY_INT32; #else #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) #endif /*------------------------------------------------------- * 64-bit Support *-------------------------------------------------------*/ #ifndef UNITY_SUPPORT_64 #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 #define UNITY_SUPPORT_64 #endif #endif #ifndef UNITY_SUPPORT_64 /* No 64-bit Support */ typedef UNITY_UINT32 UNITY_UINT; typedef UNITY_INT32 UNITY_INT; #else /* 64-bit Support */ #if (UNITY_LONG_WIDTH == 32) typedef unsigned long long UNITY_UINT64; typedef signed long long UNITY_INT64; #elif (UNITY_LONG_WIDTH == 64) typedef unsigned long UNITY_UINT64; typedef signed long UNITY_INT64; #else #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) #endif typedef UNITY_UINT64 UNITY_UINT; typedef UNITY_INT64 UNITY_INT; #endif /*------------------------------------------------------- * Pointer Support *-------------------------------------------------------*/ #if (UNITY_POINTER_WIDTH == 32) #define UNITY_PTR_TO_INT UNITY_INT32 #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 #elif (UNITY_POINTER_WIDTH == 64) #define UNITY_PTR_TO_INT UNITY_INT64 #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 #elif (UNITY_POINTER_WIDTH == 16) #define UNITY_PTR_TO_INT UNITY_INT16 #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 #else #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) #endif #ifndef UNITY_PTR_ATTRIBUTE #define UNITY_PTR_ATTRIBUTE #endif #ifndef UNITY_INTERNAL_PTR #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* #endif /*------------------------------------------------------- * Float Support *-------------------------------------------------------*/ #ifdef UNITY_EXCLUDE_FLOAT /* No Floating Point Support */ #ifndef UNITY_EXCLUDE_DOUBLE #define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ #endif #ifndef UNITY_EXCLUDE_FLOAT_PRINT #define UNITY_EXCLUDE_FLOAT_PRINT #endif #else /* Floating Point Support */ #ifndef UNITY_FLOAT_PRECISION #define UNITY_FLOAT_PRECISION (0.00001f) #endif #ifndef UNITY_FLOAT_TYPE #define UNITY_FLOAT_TYPE float #endif typedef UNITY_FLOAT_TYPE UNITY_FLOAT; /* isinf & isnan macros should be provided by math.h */ #ifndef isinf /* The value of Inf - Inf is NaN */ #define isinf(n) (isnan((n) - (n)) && !isnan(n)) #endif #ifndef isnan /* NaN is the only floating point value that does NOT equal itself. * Therefore if n != n, then it is NaN. */ #define isnan(n) ((n != n) ? 1 : 0) #endif #endif /*------------------------------------------------------- * Double Float Support *-------------------------------------------------------*/ /* unlike float, we DON'T include by default */ #if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) /* No Floating Point Support */ #ifndef UNITY_EXCLUDE_DOUBLE #define UNITY_EXCLUDE_DOUBLE #else #undef UNITY_INCLUDE_DOUBLE #endif #ifndef UNITY_EXCLUDE_FLOAT #ifndef UNITY_DOUBLE_TYPE #define UNITY_DOUBLE_TYPE double #endif typedef UNITY_FLOAT UNITY_DOUBLE; /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ #endif #else /* Double Floating Point Support */ #ifndef UNITY_DOUBLE_PRECISION #define UNITY_DOUBLE_PRECISION (1e-12) #endif #ifndef UNITY_DOUBLE_TYPE #define UNITY_DOUBLE_TYPE double #endif typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; #endif /*------------------------------------------------------- * Output Method: stdout (DEFAULT) *-------------------------------------------------------*/ #ifndef UNITY_OUTPUT_CHAR /* Default to using putchar, which is defined in stdio.h */ #include #define UNITY_OUTPUT_CHAR(a) (void)putchar(a) #else /* If defined as something else, make sure we declare it here so it's ready for use */ #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; #endif #endif #ifndef UNITY_OUTPUT_FLUSH #ifdef UNITY_USE_FLUSH_STDOUT /* We want to use the stdout flush utility */ #include #define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) #else /* We've specified nothing, therefore flush should just be ignored */ #define UNITY_OUTPUT_FLUSH() #endif #else /* We've defined flush as something else, so make sure we declare it here so it's ready for use */ #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION extern void UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION; #endif #endif #ifndef UNITY_OUTPUT_FLUSH #define UNITY_FLUSH_CALL() #else #define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() #endif #ifndef UNITY_PRINT_EOL #define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') #endif #ifndef UNITY_OUTPUT_START #define UNITY_OUTPUT_START() #endif #ifndef UNITY_OUTPUT_COMPLETE #define UNITY_OUTPUT_COMPLETE() #endif /*------------------------------------------------------- * Footprint *-------------------------------------------------------*/ #ifndef UNITY_LINE_TYPE #define UNITY_LINE_TYPE UNITY_UINT #endif #ifndef UNITY_COUNTER_TYPE #define UNITY_COUNTER_TYPE UNITY_UINT #endif /*------------------------------------------------------- * Language Features Available *-------------------------------------------------------*/ #if !defined(UNITY_WEAK_ATTRIBUTE) && !defined(UNITY_WEAK_PRAGMA) # if defined(__GNUC__) || defined(__ghs__) /* __GNUC__ includes clang */ # if !(defined(__WIN32__) && defined(__clang__)) && !defined(__TMS470__) # define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) # endif # endif #endif #ifdef UNITY_NO_WEAK # undef UNITY_WEAK_ATTRIBUTE # undef UNITY_WEAK_PRAGMA #endif /*------------------------------------------------------- * Internal Structs Needed *-------------------------------------------------------*/ typedef void (*UnityTestFunction)(void); #define UNITY_DISPLAY_RANGE_INT (0x10) #define UNITY_DISPLAY_RANGE_UINT (0x20) #define UNITY_DISPLAY_RANGE_HEX (0x40) typedef enum { UNITY_DISPLAY_STYLE_INT = sizeof(int)+ UNITY_DISPLAY_RANGE_INT, UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, #ifdef UNITY_SUPPORT_64 UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, #endif UNITY_DISPLAY_STYLE_UINT = sizeof(unsigned) + UNITY_DISPLAY_RANGE_UINT, UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, #ifdef UNITY_SUPPORT_64 UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, #endif UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, #ifdef UNITY_SUPPORT_64 UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, #endif UNITY_DISPLAY_STYLE_UNKNOWN } UNITY_DISPLAY_STYLE_T; typedef enum { UNITY_EQUAL_TO = 1, UNITY_GREATER_THAN = 2, UNITY_GREATER_OR_EQUAL = 2 + UNITY_EQUAL_TO, UNITY_SMALLER_THAN = 4, UNITY_SMALLER_OR_EQUAL = 4 + UNITY_EQUAL_TO } UNITY_COMPARISON_T; #ifndef UNITY_EXCLUDE_FLOAT typedef enum UNITY_FLOAT_TRAIT { UNITY_FLOAT_IS_NOT_INF = 0, UNITY_FLOAT_IS_INF, UNITY_FLOAT_IS_NOT_NEG_INF, UNITY_FLOAT_IS_NEG_INF, UNITY_FLOAT_IS_NOT_NAN, UNITY_FLOAT_IS_NAN, UNITY_FLOAT_IS_NOT_DET, UNITY_FLOAT_IS_DET, UNITY_FLOAT_INVALID_TRAIT } UNITY_FLOAT_TRAIT_T; #endif typedef enum { UNITY_ARRAY_TO_VAL = 0, UNITY_ARRAY_TO_ARRAY } UNITY_FLAGS_T; struct UNITY_STORAGE_T { const char* TestFile; const char* CurrentTestName; #ifndef UNITY_EXCLUDE_DETAILS const char* CurrentDetail1; const char* CurrentDetail2; #endif UNITY_LINE_TYPE CurrentTestLineNumber; UNITY_COUNTER_TYPE NumberOfTests; UNITY_COUNTER_TYPE TestFailures; UNITY_COUNTER_TYPE TestIgnores; UNITY_COUNTER_TYPE CurrentTestFailed; UNITY_COUNTER_TYPE CurrentTestIgnored; #ifndef UNITY_EXCLUDE_SETJMP_H jmp_buf AbortFrame; #endif }; extern struct UNITY_STORAGE_T Unity; /*------------------------------------------------------- * Test Suite Management *-------------------------------------------------------*/ void UnityBegin(const char* filename); int UnityEnd(void); void UnityConcludeTest(void); void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); /*------------------------------------------------------- * Details Support *-------------------------------------------------------*/ #ifdef UNITY_EXCLUDE_DETAILS #define UNITY_CLR_DETAILS() #define UNITY_SET_DETAIL(d1) #define UNITY_SET_DETAILS(d1,d2) #else #define UNITY_CLR_DETAILS() { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } #define UNITY_SET_DETAIL(d1) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = 0; } #define UNITY_SET_DETAILS(d1,d2) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = d2; } #ifndef UNITY_DETAIL1_NAME #define UNITY_DETAIL1_NAME "Function" #endif #ifndef UNITY_DETAIL2_NAME #define UNITY_DETAIL2_NAME "Argument" #endif #endif /*------------------------------------------------------- * Test Output *-------------------------------------------------------*/ void UnityPrint(const char* string); void UnityPrintLen(const char* string, const UNITY_UINT32 length); void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); void UnityPrintNumber(const UNITY_INT number); void UnityPrintNumberUnsigned(const UNITY_UINT number); void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles); #ifndef UNITY_EXCLUDE_FLOAT_PRINT void UnityPrintFloat(const UNITY_DOUBLE input_number); #endif /*------------------------------------------------------- * Test Assertion Functions *------------------------------------------------------- * Use the macros below this section instead of calling * these directly. The macros have a consistent naming * convention and will pull in file and line information * for you. */ void UnityAssertEqualNumber(const UNITY_INT expected, const UNITY_INT actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style); void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, const UNITY_INT actual, const UNITY_COMPARISON_T compare, const char *msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style); void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_PTR actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style, const UNITY_FLAGS_T flags); void UnityAssertBits(const UNITY_INT mask, const UNITY_INT expected, const UNITY_INT actual, const char* msg, const UNITY_LINE_TYPE lineNumber); void UnityAssertEqualString(const char* expected, const char* actual, const char* msg, const UNITY_LINE_TYPE lineNumber); void UnityAssertEqualStringLen(const char* expected, const char* actual, const UNITY_UINT32 length, const char* msg, const UNITY_LINE_TYPE lineNumber); void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected, const char** actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags); void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_PTR actual, const UNITY_UINT32 length, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags); void UnityAssertNumbersWithin(const UNITY_UINT delta, const UNITY_INT expected, const UNITY_INT actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style); void UnityFail(const char* message, const UNITY_LINE_TYPE line); void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); #ifndef UNITY_EXCLUDE_FLOAT void UnityAssertFloatsWithin(const UNITY_FLOAT delta, const UNITY_FLOAT expected, const UNITY_FLOAT actual, const char* msg, const UNITY_LINE_TYPE lineNumber); void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags); void UnityAssertFloatSpecial(const UNITY_FLOAT actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLOAT_TRAIT_T style); #endif #ifndef UNITY_EXCLUDE_DOUBLE void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, const UNITY_DOUBLE expected, const UNITY_DOUBLE actual, const char* msg, const UNITY_LINE_TYPE lineNumber); void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags); void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLOAT_TRAIT_T style); #endif /*------------------------------------------------------- * Helpers *-------------------------------------------------------*/ UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); #ifndef UNITY_EXCLUDE_FLOAT UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); #endif #ifndef UNITY_EXCLUDE_DOUBLE UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); #endif /*------------------------------------------------------- * Error Strings We Might Need *-------------------------------------------------------*/ extern const char UnityStrErrFloat[]; extern const char UnityStrErrDouble[]; extern const char UnityStrErr64[]; /*------------------------------------------------------- * Test Running Macros *-------------------------------------------------------*/ #ifndef UNITY_EXCLUDE_SETJMP_H #define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) #define TEST_ABORT() longjmp(Unity.AbortFrame, 1) #else #define TEST_PROTECT() 1 #define TEST_ABORT() return #endif /* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ #ifndef RUN_TEST #ifdef __STDC_VERSION__ #if __STDC_VERSION__ >= 199901L #define RUN_TEST(...) UnityDefaultTestRun(RUN_TEST_FIRST(__VA_ARGS__), RUN_TEST_SECOND(__VA_ARGS__)) #define RUN_TEST_FIRST(...) RUN_TEST_FIRST_HELPER(__VA_ARGS__, throwaway) #define RUN_TEST_FIRST_HELPER(first, ...) (first), #first #define RUN_TEST_SECOND(...) RUN_TEST_SECOND_HELPER(__VA_ARGS__, __LINE__, throwaway) #define RUN_TEST_SECOND_HELPER(first, second, ...) (second) #endif #endif #endif /* If we can't do the tricky version, we'll just have to require them to always include the line number */ #ifndef RUN_TEST #ifdef CMOCK #define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) #else #define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) #endif #endif #define TEST_LINE_NUM (Unity.CurrentTestLineNumber) #define TEST_IS_IGNORED (Unity.CurrentTestIgnored) #define UNITY_NEW_TEST(a) \ Unity.CurrentTestName = (a); \ Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ Unity.NumberOfTests++; #ifndef UNITY_BEGIN #define UNITY_BEGIN() UnityBegin(__FILE__) #endif #ifndef UNITY_END #define UNITY_END() UnityEnd() #endif /*----------------------------------------------- * Command Line Argument Support *-----------------------------------------------*/ #ifdef UNITY_USE_COMMAND_LINE_ARGS int UnityParseOptions(int argc, char** argv); int UnityTestMatches(void); #endif /*------------------------------------------------------- * Basic Fail and Ignore *-------------------------------------------------------*/ #define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) #define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) /*------------------------------------------------------- * Test Asserts *-------------------------------------------------------*/ #define UNITY_TEST_ASSERT(condition, line, message) if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));} #define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) #define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) #define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) #define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) #define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) #define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) #define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) #define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) #define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) #define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) #define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) #define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) #define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) #define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) #define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) #define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) #define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) #define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) #define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) #define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) #define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) #define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) #define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) #define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) #define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) #define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) #define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) #define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) #define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) #define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) #define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) #define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) #define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) #define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) #define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) #define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) #define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) #define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) #define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) expected, sizeof(int)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) expected, sizeof(unsigned int)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )expected, 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )expected, 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )expected, 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) expected, sizeof(int*)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) #ifdef UNITY_SUPPORT_64 #define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #else #define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #endif #ifdef UNITY_EXCLUDE_FLOAT #define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) #else #define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) #define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) #define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray((UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray(UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) #define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) #define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) #define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) #define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) #endif #ifdef UNITY_EXCLUDE_DOUBLE #define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) #else #define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)line) #define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual, (UNITY_LINE_TYPE)(line), message) #define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray((UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)line, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray(UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)line, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) #define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) #define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) #define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) #define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) #endif /* End of UNITY_INTERNALS_H */ #endif ntpsec-1.1.0+dfsg1/tests/unity/unity_fixture_internals.h0000644000175000017500000000275613252364117023317 0ustar rlaagerrlaager/* Copyright (c) 2010 James Grenning and Contributed to Unity Project * ========================================== * Unity Project - A Test Framework for C * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams * [Released under MIT License. Please refer to license.txt for details] * ========================================== */ #ifndef UNITY_FIXTURE_INTERNALS_H_ #define UNITY_FIXTURE_INTERNALS_H_ #ifdef __cplusplus extern "C" { #endif struct UNITY_FIXTURE_T { int Verbose; unsigned int RepeatCount; const char* NameFilter; const char* GroupFilter; }; extern struct UNITY_FIXTURE_T UnityFixture; typedef void unityfunction(void); void UnityTestRunner(unityfunction* setup, unityfunction* body, unityfunction* teardown, const char* printableName, const char* group, const char* name, const char* file, unsigned int line); void UnityIgnoreTest(const char* printableName, const char* group, const char* name); void UnityMalloc_StartTest(void); void UnityMalloc_EndTest(void); int UnityGetCommandLineOptions(int argc, const char* argv[]); void UnityConcludeFixtureTest(void); void UnityPointer_Set(void** ptr, void* newValue, UNITY_LINE_TYPE line); void UnityPointer_UndoAllSets(void); void UnityPointer_Init(void); #ifndef UNITY_MAX_POINTERS #define UNITY_MAX_POINTERS 5 #endif #ifdef __cplusplus } #endif #endif /* UNITY_FIXTURE_INTERNALS_H_ */ ntpsec-1.1.0+dfsg1/tests/unity/unity_fixture.c0000644000175000017500000002422213252364117021223 0ustar rlaagerrlaager/* Copyright (c) 2010 James Grenning and Contributed to Unity Project * ========================================== * Unity Project - A Test Framework for C * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams * [Released under MIT License. Please refer to license.txt for details] * ========================================== */ #include "unity_fixture.h" #include "unity_internals.h" #include struct UNITY_FIXTURE_T UnityFixture; /* If you decide to use the function pointer approach. * Build with -D UNITY_OUTPUT_CHAR=outputChar and include * int (*outputChar)(int) = putchar; */ #if !defined(UNITY_WEAK_ATTRIBUTE) && !defined(UNITY_WEAK_PRAGMA) void setUp(void) { /*does nothing*/ } void tearDown(void) { /*does nothing*/ } #endif static void announceTestRun(unsigned int runNumber) { UnityPrint("Unity test run "); UnityPrintNumberUnsigned(runNumber+1); UnityPrint(" of "); UnityPrintNumberUnsigned(UnityFixture.RepeatCount); UNITY_PRINT_EOL(); } int UnityMain(int argc, const char* argv[], void (*runAllTests)(void)) { int result = UnityGetCommandLineOptions(argc, argv); unsigned int r; if (result != 0) return result; for (r = 0; r < UnityFixture.RepeatCount; r++) { UnityBegin(argv[0]); announceTestRun(r); runAllTests(); if (!UnityFixture.Verbose) UNITY_PRINT_EOL(); UnityEnd(); } return (int)Unity.TestFailures; } static int selected(const char* filter, const char* name) { if (filter == 0) return 1; return strstr(name, filter) ? 1 : 0; } static int testSelected(const char* test) { return selected(UnityFixture.NameFilter, test); } static int groupSelected(const char* group) { return selected(UnityFixture.GroupFilter, group); } void UnityTestRunner(unityfunction* setup, unityfunction* testBody, unityfunction* teardown, const char* printableName, const char* group, const char* name, const char* file, unsigned int line) { if (testSelected(name) && groupSelected(group)) { Unity.TestFile = file; Unity.CurrentTestName = printableName; Unity.CurrentTestLineNumber = line; if (!UnityFixture.Verbose) UNITY_OUTPUT_CHAR('.'); else { UnityPrint(printableName); #ifndef UNITY_REPEAT_TEST_NAME Unity.CurrentTestName = NULL; #endif } Unity.NumberOfTests++; UnityMalloc_StartTest(); UnityPointer_Init(); if (TEST_PROTECT()) { setup(); testBody(); } if (TEST_PROTECT()) { teardown(); } if (TEST_PROTECT()) { UnityPointer_UndoAllSets(); if (!Unity.CurrentTestFailed) UnityMalloc_EndTest(); } UnityConcludeFixtureTest(); } } void UnityIgnoreTest(const char* printableName, const char* group, const char* name) { if (testSelected(name) && groupSelected(group)) { Unity.NumberOfTests++; Unity.TestIgnores++; if (!UnityFixture.Verbose) UNITY_OUTPUT_CHAR('!'); else { UnityPrint(printableName); UNITY_PRINT_EOL(); } } } /*------------------------------------------------- */ /* Malloc and free stuff */ #define MALLOC_DONT_FAIL -1 static int malloc_count; static int malloc_fail_countdown = MALLOC_DONT_FAIL; void UnityMalloc_StartTest(void) { malloc_count = 0; malloc_fail_countdown = MALLOC_DONT_FAIL; } void UnityMalloc_EndTest(void) { malloc_fail_countdown = MALLOC_DONT_FAIL; if (malloc_count != 0) { UNITY_TEST_FAIL(Unity.CurrentTestLineNumber, "This test leaks!"); } } void UnityMalloc_MakeMallocFailAfterCount(int countdown) { malloc_fail_countdown = countdown; } /* These definitions are always included from unity_fixture_malloc_overrides.h */ /* We undef to use them or avoid conflict with per the C standard */ #undef malloc #undef free #undef calloc #undef realloc #ifdef UNITY_EXCLUDE_STDLIB_MALLOC static unsigned char unity_heap[UNITY_INTERNAL_HEAP_SIZE_BYTES]; static size_t heap_index; #else #include #endif typedef struct GuardBytes { size_t size; size_t guard_space; } Guard; static const char end[] = "END"; void* unity_malloc(size_t size) { char* mem; Guard* guard; size_t total_size = size + sizeof(Guard) + sizeof(end); if (malloc_fail_countdown != MALLOC_DONT_FAIL) { if (malloc_fail_countdown == 0) return NULL; malloc_fail_countdown--; } if (size == 0) return NULL; #ifdef UNITY_EXCLUDE_STDLIB_MALLOC if (heap_index + total_size > UNITY_INTERNAL_HEAP_SIZE_BYTES) { guard = NULL; } else { guard = (Guard*)&unity_heap[heap_index]; heap_index += total_size; } #else guard = (Guard*)UNITY_FIXTURE_MALLOC(total_size); #endif if (guard == NULL) return NULL; malloc_count++; guard->size = size; guard->guard_space = 0; mem = (char*)&(guard[1]); memcpy(&mem[size], end, sizeof(end)); return (void*)mem; } static int isOverrun(void* mem) { Guard* guard = (Guard*)mem; char* memAsChar = (char*)mem; guard--; return guard->guard_space != 0 || strcmp(&memAsChar[guard->size], end) != 0; } static void release_memory(void* mem) { Guard* guard = (Guard*)mem; guard--; malloc_count--; #ifdef UNITY_EXCLUDE_STDLIB_MALLOC if (mem == unity_heap + heap_index - guard->size - sizeof(end)) { heap_index -= (guard->size + sizeof(Guard) + sizeof(end)); } #else UNITY_FIXTURE_FREE(guard); #endif } void unity_free(void* mem) { int overrun; if (mem == NULL) { return; } overrun = isOverrun(mem); release_memory(mem); if (overrun) { UNITY_TEST_FAIL(Unity.CurrentTestLineNumber, "Buffer overrun detected during free()"); } } void* unity_calloc(size_t num, size_t size) { void* mem = unity_malloc(num * size); if (mem == NULL) return NULL; memset(mem, 0, num * size); return mem; } void* unity_realloc(void* oldMem, size_t size) { Guard* guard = (Guard*)oldMem; void* newMem; if (oldMem == NULL) return unity_malloc(size); guard--; if (isOverrun(oldMem)) { release_memory(oldMem); UNITY_TEST_FAIL(Unity.CurrentTestLineNumber, "Buffer overrun detected during realloc()"); } if (size == 0) { release_memory(oldMem); return NULL; } if (guard->size >= size) return oldMem; #ifdef UNITY_EXCLUDE_STDLIB_MALLOC /* Optimization if memory is expandable */ if (oldMem == unity_heap + heap_index - guard->size - sizeof(end) && heap_index + size - guard->size <= UNITY_INTERNAL_HEAP_SIZE_BYTES) { release_memory(oldMem); /* Not thread-safe, like unity_heap generally */ return unity_malloc(size); /* No memcpy since data is in place */ } #endif newMem = unity_malloc(size); if (newMem == NULL) return NULL; /* Do not release old memory */ memcpy(newMem, oldMem, guard->size); release_memory(oldMem); return newMem; } /*-------------------------------------------------------- */ /*Automatic pointer restoration functions */ struct PointerPair { void** pointer; void* old_value; }; static struct PointerPair pointer_store[UNITY_MAX_POINTERS]; static int pointer_index = 0; void UnityPointer_Init(void) { pointer_index = 0; } void UnityPointer_Set(void** pointer, void* newValue, UNITY_LINE_TYPE line) { if (pointer_index >= UNITY_MAX_POINTERS) { UNITY_TEST_FAIL(line, "Too many pointers set"); } else { pointer_store[pointer_index].pointer = pointer; pointer_store[pointer_index].old_value = *pointer; *pointer = newValue; pointer_index++; } } void UnityPointer_UndoAllSets(void) { while (pointer_index > 0) { pointer_index--; *(pointer_store[pointer_index].pointer) = pointer_store[pointer_index].old_value; } } int UnityGetCommandLineOptions(int argc, const char* argv[]) { int i; UnityFixture.Verbose = 0; UnityFixture.GroupFilter = 0; UnityFixture.NameFilter = 0; UnityFixture.RepeatCount = 1; if (argc == 1) return 0; for (i = 1; i < argc; ) { if (strcmp(argv[i], "-v") == 0) { UnityFixture.Verbose = 1; i++; } else if (strcmp(argv[i], "-g") == 0) { i++; if (i >= argc) return 1; UnityFixture.GroupFilter = argv[i]; i++; } else if (strcmp(argv[i], "-n") == 0) { i++; if (i >= argc) return 1; UnityFixture.NameFilter = argv[i]; i++; } else if (strcmp(argv[i], "-r") == 0) { UnityFixture.RepeatCount = 2; i++; if (i < argc) { if (*(argv[i]) >= '0' && *(argv[i]) <= '9') { unsigned int digit = 0; UnityFixture.RepeatCount = 0; while (argv[i][digit] >= '0' && argv[i][digit] <= '9') { UnityFixture.RepeatCount *= 10; UnityFixture.RepeatCount += (unsigned int)argv[i][digit++] - '0'; } i++; } } } else { /* ignore unknown parameter */ i++; } } return 0; } void UnityConcludeFixtureTest(void) { if (Unity.CurrentTestIgnored) { Unity.TestIgnores++; UNITY_PRINT_EOL(); } else if (!Unity.CurrentTestFailed) { if (UnityFixture.Verbose) { UnityPrint(" PASS"); UNITY_PRINT_EOL(); } } else /* Unity.CurrentTestFailed */ { Unity.TestFailures++; UNITY_PRINT_EOL(); } Unity.CurrentTestFailed = 0; Unity.CurrentTestIgnored = 0; } ntpsec-1.1.0+dfsg1/tests/unity/unity_fixture_malloc_overrides.h0000644000175000017500000000354413252364117024645 0ustar rlaagerrlaager/* Copyright (c) 2010 James Grenning and Contributed to Unity Project * ========================================== * Unity Project - A Test Framework for C * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams * [Released under MIT License. Please refer to license.txt for details] * ========================================== */ #ifndef UNITY_FIXTURE_MALLOC_OVERRIDES_H_ #define UNITY_FIXTURE_MALLOC_OVERRIDES_H_ #include #ifdef UNITY_EXCLUDE_STDLIB_MALLOC /* Define this macro to remove the use of stdlib.h, malloc, and free. * Many embedded systems do not have a heap or malloc/free by default. * This internal unity_malloc() provides allocated memory deterministically from * the end of an array only, unity_free() only releases from end-of-array, * blocks are not coalesced, and memory not freed in LIFO order is stranded. */ #ifndef UNITY_INTERNAL_HEAP_SIZE_BYTES #define UNITY_INTERNAL_HEAP_SIZE_BYTES 256 #endif #endif /* These functions are used by the Unity Fixture to allocate and release memory * on the heap and can be overridden with platform-specific implementations. * For example, when using FreeRTOS UNITY_FIXTURE_MALLOC becomes pvPortMalloc() * and UNITY_FIXTURE_FREE becomes vPortFree(). */ #if !defined(UNITY_FIXTURE_MALLOC) || !defined(UNITY_FIXTURE_FREE) #include #define UNITY_FIXTURE_MALLOC(size) malloc(size) #define UNITY_FIXTURE_FREE(ptr) free(ptr) #else extern void* UNITY_FIXTURE_MALLOC(size_t size); extern void UNITY_FIXTURE_FREE(void* ptr); #endif #define malloc unity_malloc #define calloc unity_calloc #define realloc unity_realloc #define free unity_free void* unity_malloc(size_t size); void* unity_calloc(size_t num, size_t size); void* unity_realloc(void * oldMem, size_t size); void unity_free(void * mem); #endif /* UNITY_FIXTURE_MALLOC_OVERRIDES_H_ */ ntpsec-1.1.0+dfsg1/tests/unity/unity.c0000644000175000017500000014031413252364117017456 0ustar rlaagerrlaager/* ========================================================================= Unity Project - A Test Framework for C Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams [Released under MIT License. Please refer to license.txt for details] ============================================================================ */ #define UNITY_INCLUDE_SETUP_STUBS #include "unity.h" #include /* If omitted from header, declare overrideable prototypes here so they're ready for use */ #ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION void UNITY_OUTPUT_CHAR(int); #endif /* Helpful macros for us to use here in Assert functions */ #define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; TEST_ABORT(); } #define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; TEST_ABORT(); } #define RETURN_IF_FAIL_OR_IGNORE if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) return struct UNITY_STORAGE_T Unity; #ifdef UNITY_OUTPUT_COLOR static const char UnityStrOk[] = "\033[42mOK\033[00m"; static const char UnityStrPass[] = "\033[42mPASS\033[00m"; static const char UnityStrFail[] = "\033[41mFAIL\033[00m"; static const char UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; #else static const char UnityStrOk[] = "OK"; static const char UnityStrPass[] = "PASS"; static const char UnityStrFail[] = "FAIL"; static const char UnityStrIgnore[] = "IGNORE"; #endif static const char UnityStrNull[] = "NULL"; static const char UnityStrSpacer[] = ". "; static const char UnityStrExpected[] = " Expected "; static const char UnityStrWas[] = " Was "; static const char UnityStrGt[] = " to be greater than "; static const char UnityStrLt[] = " to be less than "; static const char UnityStrOrEqual[] = "or equal to "; static const char UnityStrElement[] = " Element "; static const char UnityStrByte[] = " Byte "; static const char UnityStrMemory[] = " Memory Mismatch."; static const char UnityStrDelta[] = " Values Not Within Delta "; static const char UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; static const char UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; static const char UnityStrNullPointerForActual[] = " Actual pointer was NULL"; #ifndef UNITY_EXCLUDE_FLOAT static const char UnityStrNot[] = "Not "; static const char UnityStrInf[] = "Infinity"; static const char UnityStrNegInf[] = "Negative Infinity"; static const char UnityStrNaN[] = "NaN"; static const char UnityStrDet[] = "Determinate"; static const char UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; #endif const char UnityStrErrFloat[] = "Unity Floating Point Disabled"; const char UnityStrErrDouble[] = "Unity Double Precision Disabled"; const char UnityStrErr64[] = "Unity 64-bit Support Disabled"; static const char UnityStrBreaker[] = "-----------------------"; static const char UnityStrResultsTests[] = " Tests "; static const char UnityStrResultsFailures[] = " Failures "; static const char UnityStrResultsIgnored[] = " Ignored "; static const char UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; static const char UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; /*----------------------------------------------- * Pretty Printers & Test Result Output Handlers *-----------------------------------------------*/ void UnityPrint(const char* string) { const char* pch = string; if (pch != NULL) { while (*pch) { /* printable characters plus CR & LF are printed */ if ((*pch <= 126) && (*pch >= 32)) { UNITY_OUTPUT_CHAR(*pch); } /* write escaped carriage returns */ else if (*pch == 13) { UNITY_OUTPUT_CHAR('\\'); UNITY_OUTPUT_CHAR('r'); } /* write escaped line feeds */ else if (*pch == 10) { UNITY_OUTPUT_CHAR('\\'); UNITY_OUTPUT_CHAR('n'); } #ifdef UNITY_OUTPUT_COLOR /* print ANSI escape code */ else if (*pch == 27 && *(pch + 1) == '[') { while (*pch && *pch != 'm') { UNITY_OUTPUT_CHAR(*pch); pch++; } UNITY_OUTPUT_CHAR('m'); } #endif /* unprintable characters are shown as codes */ else { UNITY_OUTPUT_CHAR('\\'); UNITY_OUTPUT_CHAR('x'); UnityPrintNumberHex((UNITY_UINT)*pch, 2); } pch++; } } } void UnityPrintLen(const char* string, const UNITY_UINT32 length) { const char* pch = string; if (pch != NULL) { while (*pch && (UNITY_UINT32)(pch - string) < length) { /* printable characters plus CR & LF are printed */ if ((*pch <= 126) && (*pch >= 32)) { UNITY_OUTPUT_CHAR(*pch); } /* write escaped carriage returns */ else if (*pch == 13) { UNITY_OUTPUT_CHAR('\\'); UNITY_OUTPUT_CHAR('r'); } /* write escaped line feeds */ else if (*pch == 10) { UNITY_OUTPUT_CHAR('\\'); UNITY_OUTPUT_CHAR('n'); } /* unprintable characters are shown as codes */ else { UNITY_OUTPUT_CHAR('\\'); UNITY_OUTPUT_CHAR('x'); UnityPrintNumberHex((UNITY_UINT)*pch, 2); } pch++; } } } /*-----------------------------------------------*/ void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) { if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) { UnityPrintNumber(number); } else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) { UnityPrintNumberUnsigned((UNITY_UINT)number); } else { UNITY_OUTPUT_CHAR('0'); UNITY_OUTPUT_CHAR('x'); UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); } } /*-----------------------------------------------*/ void UnityPrintNumber(const UNITY_INT number_to_print) { UNITY_UINT number = (UNITY_UINT)number_to_print; if (number_to_print < 0) { /* A negative number, including MIN negative */ UNITY_OUTPUT_CHAR('-'); number = (UNITY_UINT)(-number_to_print); } UnityPrintNumberUnsigned(number); } /*----------------------------------------------- * basically do an itoa using as little ram as possible */ void UnityPrintNumberUnsigned(const UNITY_UINT number) { UNITY_UINT divisor = 1; /* figure out initial divisor */ while (number / divisor > 9) { divisor *= 10; } /* now mod and print, then divide divisor */ do { UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); divisor /= 10; } while (divisor > 0); } /*-----------------------------------------------*/ void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) { int nibble; char nibbles = nibbles_to_print; if ((unsigned)nibbles > (2 * sizeof(number))) nibbles = 2 * sizeof(number); while (nibbles > 0) { nibbles--; nibble = (int)(number >> (nibbles * 4)) & 0x0F; if (nibble <= 9) { UNITY_OUTPUT_CHAR((char)('0' + nibble)); } else { UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); } } } /*-----------------------------------------------*/ void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) { UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); UNITY_INT32 i; for (i = 0; i < UNITY_INT_WIDTH; i++) { if (current_bit & mask) { if (current_bit & number) { UNITY_OUTPUT_CHAR('1'); } else { UNITY_OUTPUT_CHAR('0'); } } else { UNITY_OUTPUT_CHAR('X'); } current_bit = current_bit >> 1; } } /*-----------------------------------------------*/ #ifndef UNITY_EXCLUDE_FLOAT_PRINT /* This function prints a floating-point value in a format similar to * printf("%.6g"). It can work with either single- or double-precision, * but for simplicity, it prints only 6 significant digits in either case. * Printing more than 6 digits accurately is hard (at least in the single- * precision case) and isn't attempted here. */ void UnityPrintFloat(const UNITY_DOUBLE input_number) { UNITY_DOUBLE number = input_number; /* print minus sign (including for negative zero) */ if (number < 0.0f || (number == 0.0f && 1.0f / number < 0.0f)) { UNITY_OUTPUT_CHAR('-'); number = -number; } /* handle zero, NaN, and +/- infinity */ if (number == 0.0f) UnityPrint("0"); else if (isnan(number)) UnityPrint("nan"); else if (isinf(number)) UnityPrint("inf"); else { int exponent = 0; int decimals, digits; UNITY_INT32 n; char buf[16]; /* scale up or down by powers of 10 */ while (number < 100000.0f / 1e6f) { number *= 1e6f; exponent -= 6; } while (number < 100000.0f) { number *= 10.0f; exponent--; } while (number > 1000000.0f * 1e6f) { number /= 1e6f; exponent += 6; } while (number > 1000000.0f) { number /= 10.0f; exponent++; } /* round to nearest integer */ n = ((UNITY_INT32)(number + number) + 1) / 2; if (n > 999999) { n = 100000; exponent++; } /* determine where to place decimal point */ decimals = (exponent <= 0 && exponent >= -9) ? -exponent : 5; exponent += decimals; /* truncate trailing zeroes after decimal point */ while (decimals > 0 && n % 10 == 0) { n /= 10; decimals--; } /* build up buffer in reverse order */ digits = 0; while (n != 0 || digits < decimals + 1) { buf[digits++] = (char)('0' + n % 10); n /= 10; } while (digits > 0) { if(digits == decimals) UNITY_OUTPUT_CHAR('.'); UNITY_OUTPUT_CHAR(buf[--digits]); } /* print exponent if needed */ if (exponent != 0) { UNITY_OUTPUT_CHAR('e'); if(exponent < 0) { UNITY_OUTPUT_CHAR('-'); exponent = -exponent; } else { UNITY_OUTPUT_CHAR('+'); } digits = 0; while (exponent != 0 || digits < 2) { buf[digits++] = (char)('0' + exponent % 10); exponent /= 10; } while (digits > 0) { UNITY_OUTPUT_CHAR(buf[--digits]); } } } } #endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ /*-----------------------------------------------*/ static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) { UnityPrint(file); UNITY_OUTPUT_CHAR(':'); UnityPrintNumber((UNITY_INT)line); UNITY_OUTPUT_CHAR(':'); UnityPrint(Unity.CurrentTestName); UNITY_OUTPUT_CHAR(':'); } /*-----------------------------------------------*/ static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) { UnityTestResultsBegin(Unity.TestFile, line); UnityPrint(UnityStrFail); UNITY_OUTPUT_CHAR(':'); } /*-----------------------------------------------*/ void UnityConcludeTest(void) { if (Unity.CurrentTestIgnored) { Unity.TestIgnores++; } else if (!Unity.CurrentTestFailed) { UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); UnityPrint(UnityStrPass); } else { Unity.TestFailures++; } Unity.CurrentTestFailed = 0; Unity.CurrentTestIgnored = 0; UNITY_PRINT_EOL(); UNITY_FLUSH_CALL(); } /*-----------------------------------------------*/ static void UnityAddMsgIfSpecified(const char* msg) { if (msg) { UnityPrint(UnityStrSpacer); #ifndef UNITY_EXCLUDE_DETAILS if (Unity.CurrentDetail1) { UnityPrint(UnityStrDetail1Name); UnityPrint(Unity.CurrentDetail1); if (Unity.CurrentDetail2) { UnityPrint(UnityStrDetail2Name); UnityPrint(Unity.CurrentDetail2); } UnityPrint(UnityStrSpacer); } #endif UnityPrint(msg); } } /*-----------------------------------------------*/ static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) { UnityPrint(UnityStrExpected); if (expected != NULL) { UNITY_OUTPUT_CHAR('\''); UnityPrint(expected); UNITY_OUTPUT_CHAR('\''); } else { UnityPrint(UnityStrNull); } UnityPrint(UnityStrWas); if (actual != NULL) { UNITY_OUTPUT_CHAR('\''); UnityPrint(actual); UNITY_OUTPUT_CHAR('\''); } else { UnityPrint(UnityStrNull); } } /*-----------------------------------------------*/ static void UnityPrintExpectedAndActualStringsLen(const char* expected, const char* actual, const UNITY_UINT32 length) { UnityPrint(UnityStrExpected); if (expected != NULL) { UNITY_OUTPUT_CHAR('\''); UnityPrintLen(expected, length); UNITY_OUTPUT_CHAR('\''); } else { UnityPrint(UnityStrNull); } UnityPrint(UnityStrWas); if (actual != NULL) { UNITY_OUTPUT_CHAR('\''); UnityPrintLen(actual, length); UNITY_OUTPUT_CHAR('\''); } else { UnityPrint(UnityStrNull); } } /*----------------------------------------------- * Assertion & Control Helpers *-----------------------------------------------*/ static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_PTR actual, const UNITY_LINE_TYPE lineNumber, const char* msg) { if (expected == actual) return 0; /* Both are NULL or same pointer */ /* print and return true if just expected is NULL */ if (expected == NULL) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrNullPointerForExpected); UnityAddMsgIfSpecified(msg); return 1; } /* print and return true if just actual is NULL */ if (actual == NULL) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrNullPointerForActual); UnityAddMsgIfSpecified(msg); return 1; } return 0; /* return false if neither is NULL */ } /*----------------------------------------------- * Assertion Functions *-----------------------------------------------*/ void UnityAssertBits(const UNITY_INT mask, const UNITY_INT expected, const UNITY_INT actual, const char* msg, const UNITY_LINE_TYPE lineNumber) { RETURN_IF_FAIL_OR_IGNORE; if ((mask & expected) != (mask & actual)) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrExpected); UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); UnityPrint(UnityStrWas); UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } /*-----------------------------------------------*/ void UnityAssertEqualNumber(const UNITY_INT expected, const UNITY_INT actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style) { RETURN_IF_FAIL_OR_IGNORE; if (expected != actual) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrExpected); UnityPrintNumberByStyle(expected, style); UnityPrint(UnityStrWas); UnityPrintNumberByStyle(actual, style); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } /*-----------------------------------------------*/ void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, const UNITY_INT actual, const UNITY_COMPARISON_T compare, const char *msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style) { int failed = 0; RETURN_IF_FAIL_OR_IGNORE; if (threshold == actual && compare & UNITY_EQUAL_TO) return; if (threshold == actual) failed = 1; if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) { if (actual > threshold && compare & UNITY_SMALLER_THAN) failed = 1; if (actual < threshold && compare & UNITY_GREATER_THAN) failed = 1; } else /* UINT or HEX */ { if ((UNITY_UINT)actual > (UNITY_UINT)threshold && compare & UNITY_SMALLER_THAN) failed = 1; if ((UNITY_UINT)actual < (UNITY_UINT)threshold && compare & UNITY_GREATER_THAN) failed = 1; } if (failed) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrExpected); UnityPrintNumberByStyle(actual, style); if (compare & UNITY_GREATER_THAN) UnityPrint(UnityStrGt); if (compare & UNITY_SMALLER_THAN) UnityPrint(UnityStrLt); if (compare & UNITY_EQUAL_TO) UnityPrint(UnityStrOrEqual); UnityPrintNumberByStyle(threshold, style); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } #define UnityPrintPointlessAndBail() \ { \ UnityTestResultsFailBegin(lineNumber); \ UnityPrint(UnityStrPointless); \ UnityAddMsgIfSpecified(msg); \ UNITY_FAIL_AND_BAIL; } /*-----------------------------------------------*/ void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_PTR actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style, const UNITY_FLAGS_T flags) { UNITY_UINT32 elements = num_elements; unsigned int length = style & 0xF; RETURN_IF_FAIL_OR_IGNORE; if (num_elements == 0) { UnityPrintPointlessAndBail(); } if (expected == actual) return; /* Both are NULL or same pointer */ if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) UNITY_FAIL_AND_BAIL; while (elements--) { UNITY_INT expect_val; UNITY_INT actual_val; switch (length) { case 1: expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; break; case 2: expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; break; #ifdef UNITY_SUPPORT_64 case 8: expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; break; #endif default: /* length 4 bytes */ expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; length = 4; break; } if (expect_val != actual_val) { if (style & UNITY_DISPLAY_RANGE_UINT && length < sizeof(expect_val)) { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ UNITY_INT mask = 1; mask = (mask << 8 * length) - 1; expect_val &= mask; actual_val &= mask; } UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(num_elements - elements - 1); UnityPrint(UnityStrExpected); UnityPrintNumberByStyle(expect_val, style); UnityPrint(UnityStrWas); UnityPrintNumberByStyle(actual_val, style); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } if (flags == UNITY_ARRAY_TO_ARRAY) { expected = (UNITY_INTERNAL_PTR)(length + (const char*)expected); } actual = (UNITY_INTERNAL_PTR)(length + (const char*)actual); } } /*-----------------------------------------------*/ #ifndef UNITY_EXCLUDE_FLOAT /* Wrap this define in a function with variable types as float or double */ #define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ if (isinf(expected) && isinf(actual) && ((expected < 0) == (actual < 0))) return 1; \ if (UNITY_NAN_CHECK) return 1; \ diff = actual - expected; \ if (diff < 0) diff = -diff; \ if (delta < 0) delta = -delta; \ return !(isnan(diff) || isinf(diff) || (diff > delta)) /* This first part of this condition will catch any NaN or Infinite values */ #ifndef UNITY_NAN_NOT_EQUAL_NAN #define UNITY_NAN_CHECK isnan(expected) && isnan(actual) #else #define UNITY_NAN_CHECK 0 #endif #ifndef UNITY_EXCLUDE_FLOAT_PRINT #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ { \ UnityPrint(UnityStrExpected); \ UnityPrintFloat(expected); \ UnityPrint(UnityStrWas); \ UnityPrintFloat(actual); } #else #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ UnityPrint(UnityStrDelta) #endif /* UNITY_EXCLUDE_FLOAT_PRINT */ static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) { UNITY_FLOAT diff; UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); } void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags) { UNITY_UINT32 elements = num_elements; UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; RETURN_IF_FAIL_OR_IGNORE; if (elements == 0) { UnityPrintPointlessAndBail(); } if (expected == actual) return; /* Both are NULL or same pointer */ if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) UNITY_FAIL_AND_BAIL; while (elements--) { if (!UnityFloatsWithin(*ptr_expected * UNITY_FLOAT_PRECISION, *ptr_expected, *ptr_actual)) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(num_elements - elements - 1); UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } if (flags == UNITY_ARRAY_TO_ARRAY) { ptr_expected++; } ptr_actual++; } } /*-----------------------------------------------*/ void UnityAssertFloatsWithin(const UNITY_FLOAT delta, const UNITY_FLOAT expected, const UNITY_FLOAT actual, const char* msg, const UNITY_LINE_TYPE lineNumber) { RETURN_IF_FAIL_OR_IGNORE; if (!UnityFloatsWithin(delta, expected, actual)) { UnityTestResultsFailBegin(lineNumber); UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } /*-----------------------------------------------*/ void UnityAssertFloatSpecial(const UNITY_FLOAT actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLOAT_TRAIT_T style) { const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; UNITY_INT should_be_trait = ((UNITY_INT)style & 1); UNITY_INT is_trait = !should_be_trait; UNITY_INT trait_index = (UNITY_INT)(style >> 1); RETURN_IF_FAIL_OR_IGNORE; switch (style) { case UNITY_FLOAT_IS_INF: case UNITY_FLOAT_IS_NOT_INF: is_trait = isinf(actual) && (actual > 0); break; case UNITY_FLOAT_IS_NEG_INF: case UNITY_FLOAT_IS_NOT_NEG_INF: is_trait = isinf(actual) && (actual < 0); break; case UNITY_FLOAT_IS_NAN: case UNITY_FLOAT_IS_NOT_NAN: is_trait = isnan(actual) ? 1 : 0; break; case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ case UNITY_FLOAT_IS_NOT_DET: is_trait = !isinf(actual) && !isnan(actual); break; default: trait_index = 0; trait_names[0] = UnityStrInvalidFloatTrait; break; } if (is_trait != should_be_trait) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrExpected); if (!should_be_trait) UnityPrint(UnityStrNot); UnityPrint(trait_names[trait_index]); UnityPrint(UnityStrWas); #ifndef UNITY_EXCLUDE_FLOAT_PRINT UnityPrintFloat((UNITY_DOUBLE)actual); #else if (should_be_trait) UnityPrint(UnityStrNot); UnityPrint(trait_names[trait_index]); #endif UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } #endif /* not UNITY_EXCLUDE_FLOAT */ /*-----------------------------------------------*/ #ifndef UNITY_EXCLUDE_DOUBLE static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) { UNITY_DOUBLE diff; UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); } void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags) { UNITY_UINT32 elements = num_elements; UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; RETURN_IF_FAIL_OR_IGNORE; if (elements == 0) { UnityPrintPointlessAndBail(); } if (expected == actual) return; /* Both are NULL or same pointer */ if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) UNITY_FAIL_AND_BAIL; while (elements--) { if (!UnityDoublesWithin(*ptr_expected * UNITY_DOUBLE_PRECISION, *ptr_expected, *ptr_actual)) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(num_elements - elements - 1); UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } if (flags == UNITY_ARRAY_TO_ARRAY) { ptr_expected++; } ptr_actual++; } } /*-----------------------------------------------*/ void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, const UNITY_DOUBLE expected, const UNITY_DOUBLE actual, const char* msg, const UNITY_LINE_TYPE lineNumber) { RETURN_IF_FAIL_OR_IGNORE; if (!UnityDoublesWithin(delta, expected, actual)) { UnityTestResultsFailBegin(lineNumber); UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } /*-----------------------------------------------*/ void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLOAT_TRAIT_T style) { const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; UNITY_INT should_be_trait = ((UNITY_INT)style & 1); UNITY_INT is_trait = !should_be_trait; UNITY_INT trait_index = (UNITY_INT)(style >> 1); RETURN_IF_FAIL_OR_IGNORE; switch (style) { case UNITY_FLOAT_IS_INF: case UNITY_FLOAT_IS_NOT_INF: is_trait = isinf(actual) && (actual > 0); break; case UNITY_FLOAT_IS_NEG_INF: case UNITY_FLOAT_IS_NOT_NEG_INF: is_trait = isinf(actual) && (actual < 0); break; case UNITY_FLOAT_IS_NAN: case UNITY_FLOAT_IS_NOT_NAN: is_trait = isnan(actual) ? 1 : 0; break; case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ case UNITY_FLOAT_IS_NOT_DET: is_trait = !isinf(actual) && !isnan(actual); break; default: trait_index = 0; trait_names[0] = UnityStrInvalidFloatTrait; break; } if (is_trait != should_be_trait) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrExpected); if (!should_be_trait) UnityPrint(UnityStrNot); UnityPrint(trait_names[trait_index]); UnityPrint(UnityStrWas); #ifndef UNITY_EXCLUDE_FLOAT_PRINT UnityPrintFloat(actual); #else if (should_be_trait) UnityPrint(UnityStrNot); UnityPrint(trait_names[trait_index]); #endif UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } #endif /* not UNITY_EXCLUDE_DOUBLE */ /*-----------------------------------------------*/ void UnityAssertNumbersWithin(const UNITY_UINT delta, const UNITY_INT expected, const UNITY_INT actual, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style) { RETURN_IF_FAIL_OR_IGNORE; if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) { if (actual > expected) Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(actual - expected) > delta); else Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(expected - actual) > delta); } else { if ((UNITY_UINT)actual > (UNITY_UINT)expected) Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(actual - expected) > delta); else Unity.CurrentTestFailed = (UNITY_UINT)((UNITY_UINT)(expected - actual) > delta); } if (Unity.CurrentTestFailed) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrDelta); UnityPrintNumberByStyle((UNITY_INT)delta, style); UnityPrint(UnityStrExpected); UnityPrintNumberByStyle(expected, style); UnityPrint(UnityStrWas); UnityPrintNumberByStyle(actual, style); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } /*-----------------------------------------------*/ void UnityAssertEqualString(const char* expected, const char* actual, const char* msg, const UNITY_LINE_TYPE lineNumber) { UNITY_UINT32 i; RETURN_IF_FAIL_OR_IGNORE; /* if both pointers not null compare the strings */ if (expected && actual) { for (i = 0; expected[i] || actual[i]; i++) { if (expected[i] != actual[i]) { Unity.CurrentTestFailed = 1; break; } } } else { /* handle case of one pointers being null (if both null, test should pass) */ if (expected != actual) { Unity.CurrentTestFailed = 1; } } if (Unity.CurrentTestFailed) { UnityTestResultsFailBegin(lineNumber); UnityPrintExpectedAndActualStrings(expected, actual); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } /*-----------------------------------------------*/ void UnityAssertEqualStringLen(const char* expected, const char* actual, const UNITY_UINT32 length, const char* msg, const UNITY_LINE_TYPE lineNumber) { UNITY_UINT32 i; RETURN_IF_FAIL_OR_IGNORE; /* if both pointers not null compare the strings */ if (expected && actual) { for (i = 0; (i < length) && (expected[i] || actual[i]); i++) { if (expected[i] != actual[i]) { Unity.CurrentTestFailed = 1; break; } } } else { /* handle case of one pointers being null (if both null, test should pass) */ if (expected != actual) { Unity.CurrentTestFailed = 1; } } if (Unity.CurrentTestFailed) { UnityTestResultsFailBegin(lineNumber); UnityPrintExpectedAndActualStringsLen(expected, actual, length); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } /*-----------------------------------------------*/ void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, const char** actual, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags) { UNITY_UINT32 i = 0; UNITY_UINT32 j = 0; const char* expd = NULL; const char* act = NULL; RETURN_IF_FAIL_OR_IGNORE; /* if no elements, it's an error */ if (num_elements == 0) { UnityPrintPointlessAndBail(); } if ((const void*)expected == (const void*)actual) { return; /* Both are NULL or same pointer */ } if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) { UNITY_FAIL_AND_BAIL; } if (flags != UNITY_ARRAY_TO_ARRAY) { expd = (const char*)expected; } do { act = actual[j]; if (flags == UNITY_ARRAY_TO_ARRAY) { expd = ((const char* const*)expected)[j]; } /* if both pointers not null compare the strings */ if (expd && act) { for (i = 0; expd[i] || act[i]; i++) { if (expd[i] != act[i]) { Unity.CurrentTestFailed = 1; break; } } } else { /* handle case of one pointers being null (if both null, test should pass) */ if (expd != act) { Unity.CurrentTestFailed = 1; } } if (Unity.CurrentTestFailed) { UnityTestResultsFailBegin(lineNumber); if (num_elements > 1) { UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(j); } UnityPrintExpectedAndActualStrings(expd, act); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } } while (++j < num_elements); } /*-----------------------------------------------*/ void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_PTR actual, const UNITY_UINT32 length, const UNITY_UINT32 num_elements, const char* msg, const UNITY_LINE_TYPE lineNumber, const UNITY_FLAGS_T flags) { UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; UNITY_UINT32 elements = num_elements; UNITY_UINT32 bytes; RETURN_IF_FAIL_OR_IGNORE; if ((elements == 0) || (length == 0)) { UnityPrintPointlessAndBail(); } if (expected == actual) return; /* Both are NULL or same pointer */ if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) UNITY_FAIL_AND_BAIL; while (elements--) { bytes = length; while (bytes--) { if (*ptr_exp != *ptr_act) { UnityTestResultsFailBegin(lineNumber); UnityPrint(UnityStrMemory); if (num_elements > 1) { UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(num_elements - elements - 1); } UnityPrint(UnityStrByte); UnityPrintNumberUnsigned(length - bytes - 1); UnityPrint(UnityStrExpected); UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); UnityPrint(UnityStrWas); UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } ptr_exp++; ptr_act++; } if (flags == UNITY_ARRAY_TO_VAL) { ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; } } } /*-----------------------------------------------*/ static union { UNITY_INT8 i8; UNITY_INT16 i16; UNITY_INT32 i32; #ifdef UNITY_SUPPORT_64 UNITY_INT64 i64; #endif #ifndef UNITY_EXCLUDE_FLOAT float f; #endif #ifndef UNITY_EXCLUDE_DOUBLE double d; #endif } UnityQuickCompare; UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) { switch(size) { case 1: UnityQuickCompare.i8 = (UNITY_INT8)num; return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); case 2: UnityQuickCompare.i16 = (UNITY_INT16)num; return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); #ifdef UNITY_SUPPORT_64 case 8: UnityQuickCompare.i64 = (UNITY_INT64)num; return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); #endif default: /* 4 bytes */ UnityQuickCompare.i32 = (UNITY_INT32)num; return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); } } #ifndef UNITY_EXCLUDE_FLOAT UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) { UnityQuickCompare.f = num; return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); } #endif #ifndef UNITY_EXCLUDE_DOUBLE UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) { UnityQuickCompare.d = num; return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); } #endif /*----------------------------------------------- * Control Functions *-----------------------------------------------*/ void UnityFail(const char* msg, const UNITY_LINE_TYPE line) { RETURN_IF_FAIL_OR_IGNORE; UnityTestResultsBegin(Unity.TestFile, line); UnityPrint(UnityStrFail); if (msg != NULL) { UNITY_OUTPUT_CHAR(':'); #ifndef UNITY_EXCLUDE_DETAILS if (Unity.CurrentDetail1) { UnityPrint(UnityStrDetail1Name); UnityPrint(Unity.CurrentDetail1); if (Unity.CurrentDetail2) { UnityPrint(UnityStrDetail2Name); UnityPrint(Unity.CurrentDetail2); } UnityPrint(UnityStrSpacer); } #endif if (msg[0] != ' ') { UNITY_OUTPUT_CHAR(' '); } UnityPrint(msg); } UNITY_FAIL_AND_BAIL; } /*-----------------------------------------------*/ void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) { RETURN_IF_FAIL_OR_IGNORE; UnityTestResultsBegin(Unity.TestFile, line); UnityPrint(UnityStrIgnore); if (msg != NULL) { UNITY_OUTPUT_CHAR(':'); UNITY_OUTPUT_CHAR(' '); UnityPrint(msg); } UNITY_IGNORE_AND_BAIL; } /*-----------------------------------------------*/ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) { Unity.CurrentTestName = FuncName; Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; Unity.NumberOfTests++; UNITY_CLR_DETAILS(); if (TEST_PROTECT()) { setUp(); Func(); } if (TEST_PROTECT()) { tearDown(); } UnityConcludeTest(); } /*-----------------------------------------------*/ void UnityBegin(const char* filename) { Unity.TestFile = filename; Unity.CurrentTestName = NULL; Unity.CurrentTestLineNumber = 0; Unity.NumberOfTests = 0; Unity.TestFailures = 0; Unity.TestIgnores = 0; Unity.CurrentTestFailed = 0; Unity.CurrentTestIgnored = 0; UNITY_CLR_DETAILS(); UNITY_OUTPUT_START(); } /*-----------------------------------------------*/ int UnityEnd(void) { UNITY_PRINT_EOL(); UnityPrint(UnityStrBreaker); UNITY_PRINT_EOL(); UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); UnityPrint(UnityStrResultsTests); UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); UnityPrint(UnityStrResultsFailures); UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); UnityPrint(UnityStrResultsIgnored); UNITY_PRINT_EOL(); if (Unity.TestFailures == 0U) { UnityPrint(UnityStrOk); } else { UnityPrint(UnityStrFail); #ifdef UNITY_DIFFERENTIATE_FINAL_FAIL UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); #endif } UNITY_PRINT_EOL(); UNITY_FLUSH_CALL(); UNITY_OUTPUT_COMPLETE(); return (int)(Unity.TestFailures); } /*----------------------------------------------- * Command Line Argument Support *-----------------------------------------------*/ #ifdef UNITY_USE_COMMAND_LINE_ARGS char* UnityOptionIncludeNamed = NULL; char* UnityOptionExcludeNamed = NULL; int UnityVerbosity = 1; int UnityParseOptions(int argc, char** argv) { UnityOptionIncludeNamed = NULL; UnityOptionExcludeNamed = NULL; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 'l': /* list tests */ return -1; case 'n': /* include tests with name including this string */ case 'f': /* an alias for -n */ if (argv[i][2] == '=') UnityOptionIncludeNamed = &argv[i][3]; else if (++i < argc) UnityOptionIncludeNamed = argv[i]; else { UnityPrint("ERROR: No Test String to Include Matches For"); UNITY_PRINT_EOL(); return 1; } break; case 'q': /* quiet */ UnityVerbosity = 0; break; case 'v': /* verbose */ UnityVerbosity = 2; break; case 'x': /* exclude tests with name including this string */ if (argv[i][2] == '=') UnityOptionExcludeNamed = &argv[i][3]; else if (++i < argc) UnityOptionExcludeNamed = argv[i]; else { UnityPrint("ERROR: No Test String to Exclude Matches For"); UNITY_PRINT_EOL(); return 1; } break; default: UnityPrint("ERROR: Unknown Option "); UNITY_OUTPUT_CHAR(argv[i][1]); UNITY_PRINT_EOL(); return 1; } } } return 0; } int IsStringInBiggerString(const char* longstring, const char* shortstring) { const char* lptr = longstring; const char* sptr = shortstring; const char* lnext = lptr; if (*sptr == '*') return 1; while (*lptr) { lnext = lptr + 1; /* If they current bytes match, go on to the next bytes */ while (*lptr && *sptr && (*lptr == *sptr)) { lptr++; sptr++; /* We're done if we match the entire string or up to a wildcard */ if (*sptr == '*') return 1; if (*sptr == ',') return 1; if (*sptr == '"') return 1; if (*sptr == '\'') return 1; if (*sptr == ':') return 2; if (*sptr == 0) return 1; } /* Otherwise we start in the long pointer 1 character further and try again */ lptr = lnext; sptr = shortstring; } return 0; } int UnityStringArgumentMatches(const char* str) { int retval; const char* ptr1; const char* ptr2; const char* ptrf; /* Go through the options and get the substrings for matching one at a time */ ptr1 = str; while (ptr1[0] != 0) { if ((ptr1[0] == '"') || (ptr1[0] == '\'')) ptr1++; /* look for the start of the next partial */ ptr2 = ptr1; ptrf = 0; do { ptr2++; if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) ptrf = &ptr2[1]; } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) ptr2++; /* done if complete filename match */ retval = IsStringInBiggerString(Unity.TestFile, ptr1); if (retval == 1) return retval; /* done if testname match after filename partial match */ if ((retval == 2) && (ptrf != 0)) { if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) return 1; } /* done if complete testname match */ if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) return 1; ptr1 = ptr2; } /* we couldn't find a match for any substrings */ return 0; } int UnityTestMatches(void) { /* Check if this test name matches the included test pattern */ int retval; if (UnityOptionIncludeNamed) { retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); } else retval = 1; /* Check if this test name matches the excluded test pattern */ if (UnityOptionExcludeNamed) { if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) retval = 0; } return retval; } #endif /* UNITY_USE_COMMAND_LINE_ARGS */ /*-----------------------------------------------*/ ntpsec-1.1.0+dfsg1/tests/unity/unity_fixture.h0000644000175000017500000000642413252364117021234 0ustar rlaagerrlaager/* Copyright (c) 2010 James Grenning and Contributed to Unity Project * ========================================== * Unity Project - A Test Framework for C * Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams * [Released under MIT License. Please refer to license.txt for details] * ========================================== */ #ifndef UNITY_FIXTURE_H_ #define UNITY_FIXTURE_H_ #include "unity.h" #include "unity_internals.h" #include "unity_fixture_malloc_overrides.h" #include "unity_fixture_internals.h" int UnityMain(int argc, const char* argv[], void (*runAllTests)(void)); #define TEST_GROUP(group)\ static const char* TEST_GROUP_##group = #group #define TEST_SETUP(group) void TEST_##group##_SETUP(void);\ void TEST_##group##_SETUP(void) #define TEST_TEAR_DOWN(group) void TEST_##group##_TEAR_DOWN(void);\ void TEST_##group##_TEAR_DOWN(void) #define TEST(group, name) \ void TEST_##group##_##name##_(void);\ void TEST_##group##_##name##_run(void);\ void TEST_##group##_##name##_run(void)\ {\ UnityTestRunner(TEST_##group##_SETUP,\ TEST_##group##_##name##_,\ TEST_##group##_TEAR_DOWN,\ "TEST(" #group ", " #name ")",\ TEST_GROUP_##group, #name,\ __FILE__, __LINE__);\ }\ void TEST_##group##_##name##_(void) #define IGNORE_TEST(group, name) \ void TEST_##group##_##name##_(void);\ void TEST_##group##_##name##_run(void);\ void TEST_##group##_##name##_run(void)\ {\ UnityIgnoreTest("IGNORE_TEST(" #group ", " #name ")", TEST_GROUP_##group, #name);\ }\ void TEST_##group##_##name##_(void) /* Call this for each test, insider the group runner */ #define RUN_TEST_CASE(group, name) \ { void TEST_##group##_##name##_run(void);\ TEST_##group##_##name##_run(); } /* This goes at the bottom of each test file or in a separate c file */ #define TEST_GROUP_RUNNER(group)\ void TEST_##group##_GROUP_RUNNER(void);\ void TEST_##group##_GROUP_RUNNER(void) /* Call this from main */ #define RUN_TEST_GROUP(group)\ { void TEST_##group##_GROUP_RUNNER(void);\ TEST_##group##_GROUP_RUNNER(); } /* CppUTest Compatibility Macros */ #ifndef UNITY_EXCLUDE_CPPUTEST_ASSERTS /* Sets a pointer and automatically restores it to its old value after teardown */ #define UT_PTR_SET(ptr, newPointerValue) UnityPointer_Set((void**)&(ptr), (void*)(newPointerValue), __LINE__) #define TEST_ASSERT_POINTERS_EQUAL(expected, actual) TEST_ASSERT_EQUAL_PTR((expected), (actual)) #define TEST_ASSERT_BYTES_EQUAL(expected, actual) TEST_ASSERT_EQUAL_HEX8(0xff & (expected), 0xff & (actual)) #define FAIL(message) TEST_FAIL_MESSAGE((message)) #define CHECK(condition) TEST_ASSERT_TRUE((condition)) #define LONGS_EQUAL(expected, actual) TEST_ASSERT_EQUAL_INT((expected), (actual)) #define STRCMP_EQUAL(expected, actual) TEST_ASSERT_EQUAL_STRING((expected), (actual)) #define DOUBLES_EQUAL(expected, actual, delta) TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual)) #endif /* You must compile with malloc replacement, as defined in unity_fixture_malloc_overrides.h */ void UnityMalloc_MakeMallocFailAfterCount(int count); #endif /* UNITY_FIXTURE_H_ */ ntpsec-1.1.0+dfsg1/tests/wscript0000644000175000017500000001020013252364117016376 0ustar rlaagerrlaagerimport os from waflib import Utils # pylint: disable=import-error def build(ctx): srcnode = ctx.srcnode.abspath() testsrc = ctx.srcnode.make_node('tests') pylib = ctx.srcnode.make_node('pylib') testpylib = testsrc.make_node('pylib') testpysrc = testpylib.ant_glob('*.py') # Unity source unity_source = [ "unity/unity.c", "unity/unity_fixture.c", ] unity_config = ["UNITY_INCLUDE_DOUBLE", "UNITY_SUPPORT_64"] ctx( defines=unity_config, features="c", target="unity", source=unity_source ) # Test main. common_source = [ "common/tests_main.c", "common/caltime.c", "common/sockaddrtest.c", ] # libntp/ libntp_source = [ "libntp/authkeys.c", "libntp/calendar.c", "libntp/clocktime.c", "libntp/decodenetnum.c", "libntp/hextolfp.c", "libntp/lfpfunc.c", "libntp/lfptostr.c", "libntp/macencrypt.c", "libntp/msyslog.c", "libntp/netof.c", "libntp/numtoa.c", "libntp/prettydate.c", "libntp/recvbuff.c", "libntp/refidsmear.c", "libntp/socktoa.c", "libntp/statestr.c", "libntp/strtolfp.c", "libntp/timespecops.c", "libntp/vi64ops.c", "libntp/ymd2yd.c" ] + common_source ctx.ntp_test( features="c cprogram bld_include src_include test", target="test_libntp", install_path=None, defines=unity_config + ["TEST_LIBNTP=1"], includes=["%s/tests/unity/" % srcnode, "%s/tests/libntp/" % srcnode, "%s/tests/ntpdig/" % srcnode, "%s/tests/common/" % srcnode ] + ctx.env.PLATFORM_INCLUDES, use="unity ntp parse M PTHREAD CRYPTO RT SOCKET NSL", source=libntp_source, ) if ctx.env.REFCLOCK_GENERIC or ctx.env.REFCLOCK_TRIMBLE: # libparse available/required wth generic and Trimble refclocks # libparse/ libparse_source = [ "libparse/binio.c", "libparse/gpstolfp.c", "libparse/ieee754io.c", ] + common_source ctx.ntp_test( defines=unity_config + ["TEST_LIBPARSE=1"], features="c cprogram bld_include src_include test", includes=["%s/tests/unity/" % srcnode, "%s/tests/libparse/" % srcnode, "%s/tests/common/" % srcnode ] + ctx.env.PLATFORM_INCLUDES, install_path=None, lib=["parse"], libpath=["libparse"], source=libparse_source, target="test_libparse", use="unity ntp parse M PTHREAD CRYPTO RT SOCKET NSL", ) ntpd_source = [ # "ntpd/filegen.c", "ntpd/leapsec.c", "ntpd/restrict.c", ] + common_source ctx.ntp_test( defines=unity_config + ["TEST_NTPD=1"], features="c cprogram bld_include src_include test", includes=["%s/tests/unity/" % srcnode, "%s/ntpd/" % srcnode, "%s/tests/libntp/" % srcnode, "%s/tests/common/" % srcnode, ], install_path=None, source=ntpd_source, target="test_ntpd", use="ntpd_lib libntpd_obj unity ntp " "M PTHREAD CRYPTO RT SOCKET NSL", ) testpylib.get_bld().mkdir() pypath = pylib.get_bld() linkpath = ctx.bldnode.make_node("tests/pylib/ntp") if (not linkpath.exists()) or os.readlink(linkpath.abspath()): try: os.remove(linkpath.abspath()) except OSError: pass os.symlink(pypath.abspath(), linkpath.abspath()) pytests = ["pylib/test_util.py", "pylib/test_agentx_packet.py", "pylib/test_packet.py", "pylib/test_statfiles.py"] for path in pytests: ctx( features="pytest", pytest_source=path, ut_str="${PYTHON} ${SRC}" ) ctx( features="subst", source=testpysrc, target=[x.get_bld() for x in testpysrc], chmod=Utils.O755, ) ntpsec-1.1.0+dfsg1/tests/libparse/0000775000175000017500000000000013252650651016573 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/tests/libparse/ieee754io.c0000644000175000017500000002327213252364117020441 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "ntp_fp.h" #include "ieee754io.h" #include "timespecops.h" #include "unity.h" #include "unity_fixture.h" /* * Tests for libparse/ieee754io.c, in -lparse * * It would be nice to use C floats and doubles here, but * the byte order is not the same as trimble or meinberg, * and C floats and doubles need not be IEEE 754 */ TEST_GROUP(ieee754io); TEST_SETUP(ieee754io) {} TEST_TEAR_DOWN(ieee754io) {} static offsets_t native_off = { 0, 1, 2, 3, 4, 5, 6, 7 }; /* byte order for meinberg doubles */ static offsets_t mbg_double = { 1, 0, 3, 2, 5, 4, 7, 6 }; /* unsigned char *p = (void *)&fp; printf("fp: %d %d %d %d %d %d %d %d\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); */ TEST(ieee754io, test_zero32) { int ret; unsigned char one[4] = { 0, 0, 0, 0}; unsigned char *bp = &one[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); } TEST(ieee754io, test_one32) { int ret; unsigned char one[4] = { 0x3f, 0x80, 0, 0}; unsigned char *bp = &one[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( 1LL << 32, (int64_t)fp ); } TEST(ieee754io, test_negone32) { int ret; unsigned char negone[4] = { 0xbf, 0x80, 0, 0}; unsigned char *bp = &negone[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( -(1LL << 32), (int64_t)fp ); } TEST(ieee754io, test_nan32) { int ret; unsigned char buf[4] = { 0x7f, 0x80, 0, 0}; unsigned char *bp = &buf[0]; l_fp fp; /* +INF */ ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_POSINFINITY == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); /* -INF */ buf[0] = 0xff; bp = &buf[0]; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_NEGINFINITY == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); /* NAN */ buf[0] = 0x7f; buf[1] = 0xf8; bp = &buf[0]; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_NAN == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); } TEST(ieee754io, test_max32) { int ret; /* not enough precision in a float to get precision of l_fp */ unsigned char buf[4] = { 0x4e, 0xff, 255, 255}; unsigned char buf_n[4] = { 0xce, 0xff, 255, 255}; unsigned char *bp = &buf[0]; l_fp fp; /* max that fits */ ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( 0x7FFFFF8000000000ULL, fp ); /* negative max that fits */ bp = &buf_n[0]; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_UINT64( 0x8000008000000000ULL, fp ); } TEST(ieee754io, test_order32) { int ret; /* not enough precision in a double to get max l_fp */ unsigned char buf[4] = { 0x3e, 127, 1, 2}; /* not enough precision in a double to get negative max l_fp */ unsigned char buf_n[4] = { 127, 0x3e, 2, 1}; unsigned char *bp = &buf[0]; l_fp fp; /* normal linear order */ ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_UINT64((uint64_t)0x3FC04080ULL, (uint64_t)fp); /* meinberg order */ bp = &buf_n[0]; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, mbg_double); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_UINT64((uint64_t)0x3FC04080ULL, (uint64_t)fp); } TEST(ieee754io, test_small32) { int ret; /* small number, one LSB of l_fp */ unsigned char buf[4] = { 0x2F, 255, 255, 255}; unsigned char *bp = &buf[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_SINGLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( 1, fp ); } TEST(ieee754io, test_zero64) { int ret; unsigned char one[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char *bp = &one[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); } TEST(ieee754io, test_one64) { int ret; unsigned char one[8] = { 0x3f, 0xf0, 0, 0, 0, 0, 0, 0 }; unsigned char *bp = &one[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( 1LL << 32, (int64_t)fp ); } TEST(ieee754io, test_negone64) { int ret; unsigned char negone[8] = { 0xbf, 0xf0, 0, 0, 0, 0, 0, 0 }; unsigned char *bp = &negone[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( -(1LL << 32), (int64_t)fp ); } TEST(ieee754io, test_nan64) { int ret; unsigned char buf[8] = { 0x7f, 0xF0, 0, 0, 0, 0, 0, 0 }; unsigned char *bp = &buf[0]; l_fp fp; /* +INF */ ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_POSINFINITY == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); /* -INF */ buf[0] = 0xff; bp = &buf[0]; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_NEGINFINITY == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); /* +OVERFLOW */ buf[0] = 0x41; buf[1] = 0xff; bp = &buf[0]; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_POSOVERFLOW == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); /* -OVERFLOW */ buf[0] = 0xC1; buf[1] = 0xff; bp = &buf[0]; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_NEGOVERFLOW == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); /* NAN */ buf[0] = 0x7f; buf[1] = 0xf8; bp = &buf[0]; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_NAN == ret); /* not IEEE754, but check for 0 anyway */ TEST_ASSERT_EQUAL_INT64( 0, (int64_t)fp ); } TEST(ieee754io, test_max64) { int ret; /* not enough precision in a double to get max l_fp */ unsigned char buf[8] = { 65, 239, 255, 255, 255, 255, 255, 255 }; /* not enough precision in a double to get negative max l_fp */ unsigned char buf_n[8] = { 65, 239, 255, 255, 255, 255, 255, 254 }; unsigned char *bp = &buf[0]; l_fp fp; /* max that fits */ ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_UINT64( (uint64_t)0xFFFFFFFFFFFFF800ULL, fp ); /* negative max that fits */ bp = &buf_n[0]; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_UINT64( (uint64_t)0xFFFFFFFFFFFFF000ULL, fp ); } TEST(ieee754io, test_order64) { int ret; /* not enough precision in a double to get max l_fp */ unsigned char buf[8] = { 65, 239, 1, 2, 3, 4, 5, 6 }; /* not enough precision in a double to get negative max l_fp */ unsigned char buf_n[8] = { 239, 65, 2, 1, 4, 3, 6, 5 }; unsigned char *bp = &buf[0]; l_fp fp; /* normal linear order */ ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_UINT64( (uint64_t)0xF808101820283000ULL, fp ); /* meinberg order */ bp = &buf_n[0]; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, mbg_double); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_UINT64( (uint64_t)0xF808101820283000ULL, fp ); } TEST(ieee754io, test_small64) { int ret; /* small number, one LSB of l_fp */ unsigned char buf[8] = { 0x3d, 255, 255, 255, 255, 255, 255, 255}; unsigned char *bp = &buf[0]; l_fp fp; ret = fetch_ieee754( &bp, IEEE_DOUBLE, &fp, native_off); TEST_ASSERT( IEEE_OK == ret); TEST_ASSERT_EQUAL_INT64( 1, fp ); } TEST_GROUP_RUNNER(ieee754io) { RUN_TEST_CASE(ieee754io, test_zero32); RUN_TEST_CASE(ieee754io, test_one32); RUN_TEST_CASE(ieee754io, test_negone32); RUN_TEST_CASE(ieee754io, test_small32); RUN_TEST_CASE(ieee754io, test_nan32); RUN_TEST_CASE(ieee754io, test_max32); RUN_TEST_CASE(ieee754io, test_order32); RUN_TEST_CASE(ieee754io, test_zero64); RUN_TEST_CASE(ieee754io, test_one64); RUN_TEST_CASE(ieee754io, test_negone64); RUN_TEST_CASE(ieee754io, test_small64); RUN_TEST_CASE(ieee754io, test_nan64); RUN_TEST_CASE(ieee754io, test_max64); RUN_TEST_CASE(ieee754io, test_order64); } ntpsec-1.1.0+dfsg1/tests/libparse/gpstolfp.c0000644000175000017500000000235013252364117020572 0ustar rlaagerrlaager#include "gpstolfp.h" #include "ntp_types.h" #include "unity.h" #include "unity_fixture.h" #include "config.h" #include "ntp_stdlib.h" TEST_GROUP(gpstolfp); TEST_SETUP(gpstolfp){}; TEST_TEAR_DOWN(gpstolfp){}; TEST(gpstolfp, check) { uint64_t build_t, gps_t; struct calendar in, out; unsigned int build_week, week, TOW; unsigned int bw[] = {MIN_BUILD_GPSWEEK, 2048, MAX_BUILD_GPSWEEK}; uint16_t by[] = {2016, 2019, 2096}; uint8_t bm[] = {6, 4, 7}; uint8_t bd[] = {5, 7, 1}; for (int i = 0; i < 3; i++) { ZERO(in); in.year=by[i]; in.month=bm[i]; in.monthday = bd[i]; caltogps(&in, 0, &week, &TOW); TEST_ASSERT_TRUE(week == bw[i] && TOW == 0); } for (uint32_t b = MIN_BUILD_GPSWEEK; b <= MAX_BUILD_GPSWEEK; b++) { build_week = b; week = b; gpstocal(week, 0, 0, &out); build_t = ntpcal_dayjoin(ntpcal_date_to_rd(&out) - DAY_NTP_STARTS, ntpcal_date_to_daysec(&out)); for (week = 0; week < GPSWEEKS; week++) { gpsweekadj(&week, build_week); ZERO(out); gpstocal(week, 0, 0, &out); gps_t = ntpcal_dayjoin(ntpcal_date_to_rd(&out) - DAY_NTP_STARTS, ntpcal_date_to_daysec(&out)); TEST_ASSERT_FALSE(build_t > gps_t); } } } TEST_GROUP_RUNNER(gpstolfp) { RUN_TEST_CASE(gpstolfp, check); } ntpsec-1.1.0+dfsg1/tests/libparse/binio.c0000644000175000017500000002531113252364117020036 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "ntp_fp.h" #include "binio.h" #include "unity.h" #include "unity_fixture.h" /* * Tests for libparse/binio.c, in -lparse * */ TEST_GROUP(binio); TEST_SETUP(binio) {} TEST_TEAR_DOWN(binio) {} /* LSB int tests */ TEST(binio, get_lsb_int160) { long ret; unsigned char zero[2] = { 0, 0}; unsigned char *bp = &zero[0]; ret = get_lsb_int16( &bp); TEST_ASSERT_EQUAL_INT64( 0, ret ); } TEST(binio, get_lsb_int161) { long ret; unsigned char zero[2] = { 1, 2}; unsigned char *bp = &zero[0]; ret = get_lsb_int16( &bp); TEST_ASSERT_EQUAL_HEX64( 0x0201UL, ret ); } TEST(binio, get_lsb_int162) { long ret; unsigned char zero[2] = { 2, 1}; unsigned char *bp = &zero[0]; ret = get_lsb_int16( &bp); TEST_ASSERT_EQUAL_HEX64( 0x0102UL, ret ); } TEST(binio, get_lsb_int163) { long ret; unsigned char zero[2] = { 0xff, 0xff}; unsigned char *bp = &zero[0]; ret = get_lsb_int16( &bp); TEST_ASSERT_EQUAL_HEX64( -1L, ret ); } TEST(binio, get_lsb_int164) { long ret; unsigned char zero[2] = { 0, 0x80}; unsigned char *bp = &zero[0]; ret = get_lsb_int16( &bp); TEST_ASSERT_EQUAL_HEX64( -0x8000L, ret ); } TEST(binio, get_lsb_int320) { int32_t ret; unsigned char zero[4] = { 0, 0, 0, 0}; unsigned char *bp = &zero[0]; ret = get_lsb_int32( &bp); TEST_ASSERT_EQUAL_INT32( 0, ret ); } TEST(binio, get_lsb_int321) { int32_t ret; unsigned char zero[4] = { 1, 2, 3, 4}; unsigned char *bp = &zero[0]; ret = get_lsb_int32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x04030201UL, ret ); } TEST(binio, get_lsb_int322) { int32_t ret; unsigned char zero[4] = { 4, 3, 2, 1}; unsigned char *bp = &zero[0]; ret = get_lsb_int32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x01020304UL, ret ); } TEST(binio, get_lsb_int323) { int32_t ret; unsigned char zero[4] = { 0xff, 0xff, 0xff, 0xff}; unsigned char *bp = &zero[0]; ret = get_lsb_int32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x0FFFFFFFFUL, ret ); } TEST(binio, get_lsb_int324) { int32_t ret; unsigned char zero[4] = { 0, 0, 0, 0x80}; unsigned char *bp = &zero[0]; ret = get_lsb_int32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x080000000UL, ret ); } TEST(binio, put_lsb_uint160) { unsigned char ret[2] = { 0, 0}; unsigned char expect[2] = { 0, 0}; unsigned char *bp = &ret[0]; put_lsb_uint16( &bp, 0); TEST_ASSERT_EQUAL_HEX8_ARRAY( expect, ret, 2 ); } TEST(binio, put_lsb_uint161) { unsigned char ret[2] = { 0, 0}; unsigned char expect[2] = { 1, 0}; unsigned char *bp = &ret[0]; put_lsb_uint16( &bp, 1); TEST_ASSERT_EQUAL_HEX8_ARRAY( expect, ret, 2 ); } TEST(binio, put_lsb_uint162) { unsigned char ret[2] = { 0, 0}; unsigned char expect[2] = { 0, 1}; unsigned char *bp = &ret[0]; put_lsb_uint16( &bp, 256); TEST_ASSERT_EQUAL_HEX8_ARRAY( expect, ret, 2 ); } TEST(binio, put_lsb_uint163) { unsigned char ret[2] = { 0, 0}; unsigned char expect[2] = { 0xff, 0xff }; unsigned char *bp = &ret[0]; put_lsb_uint16( &bp, 0xffff); TEST_ASSERT_EQUAL_HEX8_ARRAY( expect, ret, 2 ); } TEST(binio, put_lsb_uint164) { unsigned char ret[2] = { 0, 0}; unsigned char expect[2] = { 1, 0x80}; unsigned char *bp = &ret[0]; put_lsb_uint16( &bp, 0x8001); TEST_ASSERT_EQUAL_HEX8_ARRAY( expect, ret, 2 ); } /* LSB unsigned int tests */ TEST(binio, get_lsb_uint160) { long ret; unsigned char zero[2] = { 0, 0}; unsigned char *bp = &zero[0]; ret = get_lsb_uint16( &bp); TEST_ASSERT_EQUAL_INT64( 0, ret ); } TEST(binio, get_lsb_uint161) { long ret; unsigned char zero[2] = { 1, 2}; unsigned char *bp = &zero[0]; ret = get_lsb_uint16( &bp); TEST_ASSERT_EQUAL_HEX64( 0x0201UL, ret ); } TEST(binio, get_lsb_uint162) { long ret; unsigned char zero[2] = { 2, 1}; unsigned char *bp = &zero[0]; ret = get_lsb_uint16( &bp); TEST_ASSERT_EQUAL_HEX64( 0x0102UL, ret ); } TEST(binio, get_lsb_uint163) { long ret; unsigned char zero[2] = { 0xff, 0xff}; unsigned char *bp = &zero[0]; ret = get_lsb_uint16( &bp); TEST_ASSERT_EQUAL_HEX64( 0x0ffffUL, ret ); } TEST(binio, get_lsb_uint164) { long ret; unsigned char zero[2] = { 0, 0x80}; unsigned char *bp = &zero[0]; ret = get_lsb_uint16( &bp); TEST_ASSERT_EQUAL_HEX64( 0x8000L, ret ); } TEST(binio, get_lsb_uint320) { uint32_t ret; unsigned char zero[4] = { 0, 0, 0, 0}; unsigned char *bp = &zero[0]; ret = get_lsb_uint32( &bp); TEST_ASSERT_EQUAL_UINT32( 0, ret ); } TEST(binio, get_lsb_uint321) { uint32_t ret; unsigned char zero[4] = { 1, 2, 3, 4}; unsigned char *bp = &zero[0]; ret = get_lsb_uint32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x04030201UL, ret ); } TEST(binio, get_lsb_uint322) { uint32_t ret; unsigned char zero[4] = { 4, 3, 2, 1}; unsigned char *bp = &zero[0]; ret = get_lsb_uint32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x01020304UL, ret ); } TEST(binio, get_lsb_uint323) { uint32_t ret; unsigned char zero[4] = { 0xff, 0xff, 0xff, 0xff}; unsigned char *bp = &zero[0]; ret = get_lsb_uint32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x0FFFFFFFFUL, ret ); } TEST(binio, get_lsb_uint324) { uint32_t ret; unsigned char zero[4] = { 0, 0, 0, 0x80}; unsigned char *bp = &zero[0]; ret = get_lsb_uint32( &bp); TEST_ASSERT_EQUAL_HEX32( 0x080000000UL, ret ); } /* MSB tests */ TEST(binio, get_msb_short0) { long ret; unsigned char zero[2] = { 0, 0}; unsigned char *bp = &zero[0]; ret = get_msb_short( &bp); TEST_ASSERT_EQUAL_INT64( 0, ret ); } TEST(binio, get_msb_short1) { long ret; unsigned char zero[2] = { 2, 1}; unsigned char *bp = &zero[0]; ret = get_msb_short( &bp); TEST_ASSERT_EQUAL_HEX64( 0x0201UL, ret ); } TEST(binio, get_msb_short2) { long ret; unsigned char zero[2] = { 1, 2}; unsigned char *bp = &zero[0]; ret = get_msb_short( &bp); TEST_ASSERT_EQUAL_HEX64( 0x0102UL, ret ); } TEST(binio, get_msb_short3) { long ret; unsigned char zero[2] = { 0xff, 0xff}; unsigned char *bp = &zero[0]; ret = get_msb_short( &bp); TEST_ASSERT_EQUAL_HEX64( -1L, ret ); } TEST(binio, get_msb_short4) { long ret; unsigned char zero[2] = { 0x80, 0}; unsigned char *bp = &zero[0]; ret = get_msb_short( &bp); TEST_ASSERT_EQUAL_HEX64( -0x8000L, ret ); } TEST(binio, getmsb_short0) { int ret; unsigned char zero[2] = { 0, 0}; ret = getmsb_short( zero); TEST_ASSERT_EQUAL_INT( 0, ret ); } TEST(binio, getmsb_short1) { int ret; unsigned char zero[2] = { 2, 1}; ret = getmsb_short( zero); TEST_ASSERT_EQUAL_INT( 0x0201L, ret ); } TEST(binio, getmsb_short2) { int ret; unsigned char zero[2] = { 1, 2}; ret = getmsb_short( zero); TEST_ASSERT_EQUAL_INT( 0x0102L, ret ); } TEST(binio, getmsb_short3) { int ret; unsigned char zero[2] = { 0xff, 0xff}; ret = getmsb_short( zero); TEST_ASSERT_EQUAL_INT( -1, ret ); } TEST(binio, getmsb_short4) { int ret; unsigned char zero[2] = { 0x80, 0}; ret = getmsb_short( zero); TEST_ASSERT_EQUAL_INT( -0x8000L, ret ); } TEST(binio, get_msb_ushort0) { uint32_t ret; unsigned char zero[2] = { 0, 0}; ret = get_msb_ushort( zero); TEST_ASSERT_EQUAL_HEX32( 0, ret ); } TEST(binio, get_msb_ushort1) { uint32_t ret; unsigned char zero[2] = { 2, 1}; ret = get_msb_ushort( zero); TEST_ASSERT_EQUAL_HEX32( 0x0201UL, ret ); } TEST(binio, get_msb_ushort2) { uint32_t ret; unsigned char zero[2] = { 1, 2}; ret = get_msb_ushort( zero); TEST_ASSERT_EQUAL_HEX32( 0x0102UL, ret ); } TEST(binio, get_msb_ushort3) { uint32_t ret; unsigned char zero[2] = { 0xff, 0xff}; ret = get_msb_ushort( zero); TEST_ASSERT_EQUAL_HEX32( 0x0FFFFUL, ret ); } TEST(binio, get_msb_ushort4) { uint32_t ret; unsigned char zero[2] = { 0x80, 0}; ret = get_msb_ushort( zero); TEST_ASSERT_EQUAL_HEX32( 0x8000UL, ret ); } TEST_GROUP_RUNNER(binio) { /* LSB int tests */ RUN_TEST_CASE(binio, get_lsb_int160); RUN_TEST_CASE(binio, get_lsb_int161); RUN_TEST_CASE(binio, get_lsb_int162); RUN_TEST_CASE(binio, get_lsb_int163); RUN_TEST_CASE(binio, get_lsb_int164); RUN_TEST_CASE(binio, get_lsb_int320); RUN_TEST_CASE(binio, get_lsb_int321); RUN_TEST_CASE(binio, get_lsb_int322); RUN_TEST_CASE(binio, get_lsb_int323); RUN_TEST_CASE(binio, get_lsb_int324); RUN_TEST_CASE(binio, put_lsb_uint160); RUN_TEST_CASE(binio, put_lsb_uint161); RUN_TEST_CASE(binio, put_lsb_uint162); RUN_TEST_CASE(binio, put_lsb_uint163); RUN_TEST_CASE(binio, put_lsb_uint164); /* LSB unsigned int tests */ RUN_TEST_CASE(binio, get_lsb_uint160); RUN_TEST_CASE(binio, get_lsb_uint161); RUN_TEST_CASE(binio, get_lsb_uint162); RUN_TEST_CASE(binio, get_lsb_uint163); RUN_TEST_CASE(binio, get_lsb_uint164); RUN_TEST_CASE(binio, get_lsb_uint320); RUN_TEST_CASE(binio, get_lsb_uint321); RUN_TEST_CASE(binio, get_lsb_uint322); RUN_TEST_CASE(binio, get_lsb_uint323); RUN_TEST_CASE(binio, get_lsb_uint324); /* MSB tests */ RUN_TEST_CASE(binio, get_msb_short0); RUN_TEST_CASE(binio, get_msb_short1); RUN_TEST_CASE(binio, get_msb_short2); RUN_TEST_CASE(binio, get_msb_short3); RUN_TEST_CASE(binio, get_msb_short4); RUN_TEST_CASE(binio, getmsb_short0); RUN_TEST_CASE(binio, getmsb_short1); RUN_TEST_CASE(binio, getmsb_short2); RUN_TEST_CASE(binio, getmsb_short3); RUN_TEST_CASE(binio, getmsb_short4); RUN_TEST_CASE(binio, get_msb_ushort0); RUN_TEST_CASE(binio, get_msb_ushort1); RUN_TEST_CASE(binio, get_msb_ushort2); RUN_TEST_CASE(binio, get_msb_ushort3); RUN_TEST_CASE(binio, get_msb_ushort4); } ntpsec-1.1.0+dfsg1/tests/libntp/0000775000175000017500000000000013252650651016262 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/tests/libntp/msyslog.c0000644000175000017500000001151613252364117020124 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(msyslog); TEST_SETUP(msyslog) {} TEST_TEAR_DOWN(msyslog) {} #include #include #include static int msnprintf(char *, size_t, const char *, ...) NTP_PRINTF(3, 4); static int msnprintf( char * buf, size_t bufsiz, const char * fmt, ... ) { va_list ap; int rc; va_start(ap, fmt); rc = mvsnprintf(buf, bufsiz, fmt, ap); va_end(ap); return rc; } #ifndef VSNPRINTF_PERCENT_M // format_errmsg() is normally private to msyslog.c void format_errmsg (char *, size_t, const char *, int); TEST(msyslog, format_errmsgHangingPercent) { static char fmt[] = "percent then nul term then non-nul %\0oops!"; char act_buf[64]; ZERO(act_buf); format_errmsg(act_buf, sizeof(act_buf), fmt, ENOENT); TEST_ASSERT_EQUAL_STRING(fmt, act_buf); TEST_ASSERT_EQUAL_STRING("", act_buf + 1 + strlen(act_buf)); } #endif // msnprintf() TEST(msyslog, msnprintf) { #define FMT_PREFIX "msyslog.cpp ENOENT: " char exp_buf[512]; char act_buf[512]; int exp_cnt; int act_cnt; exp_cnt = snprintf(exp_buf, sizeof(exp_buf), FMT_PREFIX "%s", strerror(ENOENT)); errno = ENOENT; act_cnt = msnprintf(act_buf, sizeof(act_buf), FMT_PREFIX "%m"); TEST_ASSERT_EQUAL(exp_cnt, act_cnt); TEST_ASSERT_EQUAL_STRING(exp_buf, act_buf); } TEST(msyslog, msnprintfLiteralPercentm) { char exp_buf[32]; char act_buf[32]; int exp_cnt; int act_cnt; exp_cnt = snprintf(exp_buf, sizeof(exp_buf), "%%m"); errno = ENOENT; act_cnt = msnprintf(act_buf, sizeof(act_buf), "%%m"); TEST_ASSERT_EQUAL(exp_cnt, act_cnt); TEST_ASSERT_EQUAL_STRING(exp_buf, act_buf); } TEST(msyslog, msnprintfBackslashLiteralPercentm) { char exp_buf[32]; char act_buf[32]; int exp_cnt; int act_cnt; exp_cnt = snprintf(exp_buf, sizeof(exp_buf), "\%%m"); errno = ENOENT; act_cnt = msnprintf(act_buf, sizeof(act_buf), "\%%m"); TEST_ASSERT_EQUAL(exp_cnt, act_cnt); TEST_ASSERT_EQUAL_STRING(exp_buf, act_buf); } TEST(msyslog, msnprintfBackslashPercent) { char exp_buf[32]; char act_buf[32]; int exp_cnt; int act_cnt; exp_cnt = snprintf(exp_buf, sizeof(exp_buf), "\%s", strerror(ENOENT)); errno = ENOENT; act_cnt = msnprintf(act_buf, sizeof(act_buf), "\%m"); TEST_ASSERT_EQUAL(exp_cnt, act_cnt); TEST_ASSERT_EQUAL_STRING(exp_buf, act_buf); } TEST(msyslog, msnprintfNullTarget) { int exp_cnt; int act_cnt; exp_cnt = snprintf(NULL, 0, "%d", 123); errno = ENOENT; act_cnt = msnprintf(NULL, 0, "%d", 123); TEST_ASSERT_EQUAL(exp_cnt, act_cnt); } TEST(msyslog, msnprintfTruncate) { char undist[] = "undisturbed"; char exp_buf[512]; char act_buf[512]; int exp_cnt; int act_cnt; memcpy(exp_buf + 3, undist, sizeof(undist)); memcpy(act_buf + 3, undist, sizeof(undist)); exp_cnt = snprintf(exp_buf, 3, "%s", strerror(ENOENT)); errno = ENOENT; act_cnt = msnprintf(act_buf, 3, "%m"); TEST_ASSERT_EQUAL('\0', exp_buf[2]); TEST_ASSERT_EQUAL('\0', act_buf[2]); TEST_ASSERT_TRUE(act_cnt > 0); TEST_ASSERT_EQUAL(exp_cnt, act_cnt); TEST_ASSERT_EQUAL_STRING(exp_buf, act_buf); TEST_ASSERT_EQUAL_STRING(exp_buf + 3, undist); TEST_ASSERT_EQUAL_STRING(act_buf + 3, undist); } /* gcc 4.6 added support for push/pop * gcc 4.4 added support for -Wformat-contains-nul * Put this test last since we can't undo turning off some warnings. */ #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wformat" #else /* GCC */ #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) # pragma GCC diagnostic push #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) # pragma GCC diagnostic ignored "-Wformat-contains-nul" #endif #pragma GCC diagnostic ignored "-Wformat=" #pragma GCC diagnostic ignored "-Wformat" #endif TEST(msyslog, msnprintfHangingPercent) { char exp_buf[64]; char act_buf[64]; int exp_cnt; int act_cnt; ZERO(exp_buf); ZERO(act_buf); /* warning: format string contains '\0' within the string body [-Wformat] */ exp_cnt = snprintf(exp_buf, sizeof(exp_buf), "percent then nul term then non-nul %\0oops!"); act_cnt = msnprintf(act_buf, sizeof(act_buf), "percent then nul term then non-nul %\0oops!"); TEST_ASSERT_EQUAL(exp_cnt, act_cnt); TEST_ASSERT_EQUAL_STRING(exp_buf, act_buf); TEST_ASSERT_EQUAL_STRING("", act_buf + 1 + strlen(act_buf)); } #ifdef __clang__ # pragma clang diagnostic pop #else #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) # pragma GCC diagnostic pop #endif #endif TEST_GROUP_RUNNER(msyslog) { RUN_TEST_CASE(msyslog, msnprintf) RUN_TEST_CASE(msyslog, msnprintfLiteralPercentm) RUN_TEST_CASE(msyslog, msnprintfBackslashLiteralPercentm) RUN_TEST_CASE(msyslog, msnprintfBackslashPercent) RUN_TEST_CASE(msyslog, msnprintfHangingPercent) #ifndef VSNPRINTF_PERCENT_M RUN_TEST_CASE(msyslog, format_errmsgHangingPercent) #endif RUN_TEST_CASE(msyslog, msnprintfNullTarget) RUN_TEST_CASE(msyslog, msnprintfTruncate) } ntpsec-1.1.0+dfsg1/tests/libntp/strtolfp.c0000644000175000017500000001536513252364117020312 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "ntp_fp.h" #include #include #include #include "ntp_assert.h" #include "unity.h" #include "unity_fixture.h" static bool mstolfp (const char *, l_fp *); static bool atolfp (const char *, l_fp *); /* * atolfp - convert an ascii string to an l_fp number */ /* * Powers of 10 */ static unsigned long ten_to_the_n[10] = { 0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, }; static bool atolfp( const char *str, l_fp *lfp ) { const char *cp; unsigned long dec_i; unsigned long dec_f; char *ind; int ndec; bool isneg; static const char *digits = "0123456789"; REQUIRE(str != NULL); isneg = false; dec_i = dec_f = 0; ndec = 0; cp = str; /* * We understand numbers of the form: * * [spaces][-|+][digits][.][digits][spaces|\n|\0] */ while (isspace((unsigned char)*cp)) cp++; if (*cp == '-') { cp++; isneg = true; } if (*cp == '+') cp++; if (*cp != '.' && !isdigit((unsigned char)*cp)) return false; while (*cp != '\0' && (ind = strchr(digits, *cp)) != NULL) { dec_i = (dec_i << 3) + (dec_i << 1); /* multiply by 10 */ dec_i += (unsigned long)(ind - digits); cp++; } if (*cp != '\0' && !isspace((unsigned char)*cp)) { if (*cp++ != '.') return false; while (ndec < 9 && *cp != '\0' && (ind = strchr(digits, *cp)) != NULL) { ndec++; dec_f = (dec_f << 3) + (dec_f << 1); /* *10 */ dec_f += (unsigned long)(ind - digits); cp++; } while (isdigit((unsigned char)*cp)) cp++; if (*cp != '\0' && !isspace((unsigned char)*cp)) return false; } if (ndec > 0) { unsigned long tmp; unsigned long bit; unsigned long ten_fact; ten_fact = ten_to_the_n[ndec]; tmp = 0; bit = 0x80000000; while (bit != 0) { dec_f <<= 1; if (dec_f >= ten_fact) { tmp |= bit; dec_f -= ten_fact; } bit >>= 1; } if ((dec_f << 1) > ten_fact) tmp++; dec_f = tmp; } if (isneg) { (dec_f) = ~(dec_f) + 1u; (dec_i) = ~(dec_i) + ((dec_f) == 0); } *lfp = lfpinit_u(dec_i, dec_f); return true; } /* * mstolfp - convert an ascii string in milliseconds to an l_fp number */ static bool mstolfp( const char *str, l_fp *lfp ) { const char *cp; char *bp; const char *cpdec; char buf[100]; /* * We understand numbers of the form: * * [spaces][-][digits][.][digits][spaces|\n|\0] * * This is one enormous hack. Since I didn't feel like * rewriting the decoding routine for milliseconds, what * is essentially done here is to make a copy of the string * with the decimal moved over three places so the seconds * decoding routine can be used. */ bp = buf; cp = str; while (isspace((unsigned char)*cp)) cp++; if (*cp == '-') { *bp++ = '-'; cp++; } if (*cp != '.' && !isdigit((unsigned char)*cp)) return false; /* * Search forward for the decimal point or the end of the string. */ cpdec = cp; while (isdigit((unsigned char)*cpdec)) cpdec++; /* * Found something. If we have more than three digits copy the * excess over, else insert a leading 0. */ if ((cpdec - cp) > 3) { do { *bp++ = (char)*cp++; } while ((cpdec - cp) > 3); } else { *bp++ = '0'; } /* * Stick the decimal in. If we've got less than three digits in * front of the millisecond decimal we insert the appropriate number * of zeros. */ *bp++ = '.'; if ((cpdec - cp) < 3) { int i = 3 - (cpdec - cp); do { *bp++ = '0'; } while (--i > 0); } /* * Copy the remainder up to the millisecond decimal. If cpdec * is pointing at a decimal point, copy in the trailing number too. */ while (cp < cpdec) *bp++ = (char)*cp++; if (*cp == '.') { cp++; while (isdigit((unsigned char)*cp)) *bp++ = (char)*cp++; } *bp = '\0'; /* * Check to make sure the string is properly terminated. If * so, give the buffer to the decoding routine. */ if (*cp != '\0' && !isspace((unsigned char)*cp)) return false; return atolfp(buf, lfp); } TEST_GROUP(strtolfp); TEST_SETUP(strtolfp) {} TEST_TEAR_DOWN(strtolfp) {} #include "lfptest.h" /* This class tests both atolfp and mstolfp */ TEST(strtolfp, PositiveInteger) { const char *str = "500"; const char *str_ms = "500000"; l_fp expected = lfpinit(500, 0); l_fp actual, actual_ms; TEST_ASSERT_TRUE(atolfp(str, &actual)); TEST_ASSERT_TRUE(mstolfp(str_ms, &actual_ms)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual_ms)); } TEST(strtolfp, NegativeInteger) { const char *str = "-300"; const char *str_ms = "-300000"; l_fp expected = lfpinit(-300, 0); l_fp actual, actual_ms; TEST_ASSERT_TRUE(atolfp(str, &actual)); TEST_ASSERT_TRUE(mstolfp(str_ms, &actual_ms)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual_ms)); } TEST(strtolfp, PositiveFraction) { const char *str = "+500.5"; const char *str_ms = "500500.0"; l_fp expected = lfpinit(500, HALF); l_fp actual, actual_ms; TEST_ASSERT_TRUE(atolfp(str, &actual)); TEST_ASSERT_TRUE(mstolfp(str_ms, &actual_ms)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual_ms)); } TEST(strtolfp, NegativeFraction) { const char *str = "-300.75"; const char *str_ms = "-300750"; l_fp expected = lfpinit(-301, QUARTER); l_fp actual, actual_ms; TEST_ASSERT_TRUE(atolfp(str, &actual)); TEST_ASSERT_TRUE(mstolfp(str_ms, &actual_ms)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual_ms)); } TEST(strtolfp, PositiveMsFraction) { const char *str = "300.00025"; const char *str_ms = "300000.25"; l_fp expected = lfpinit(300, QUARTER_PROMILLE_APPRX); l_fp actual, actual_ms; TEST_ASSERT_TRUE(atolfp(str, &actual)); TEST_ASSERT_TRUE(mstolfp(str_ms, &actual_ms)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual_ms)); } TEST(strtolfp, NegativeMsFraction) { const char *str = "-199.99975"; const char *str_ms = "-199999.75"; l_fp expected = lfpinit(-200, QUARTER_PROMILLE_APPRX); l_fp actual, actual_ms; TEST_ASSERT_TRUE(atolfp(str, &actual)); TEST_ASSERT_TRUE(mstolfp(str_ms, &actual_ms)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual_ms)); } TEST(strtolfp, InvalidChars) { const char *str = "500.4a2"; l_fp actual, actual_ms; TEST_ASSERT_FALSE(atolfp(str, &actual)); TEST_ASSERT_FALSE(mstolfp(str, &actual_ms)); } TEST_GROUP_RUNNER(strtolfp) { RUN_TEST_CASE(strtolfp, PositiveInteger); RUN_TEST_CASE(strtolfp, NegativeInteger); RUN_TEST_CASE(strtolfp, PositiveFraction); RUN_TEST_CASE(strtolfp, NegativeFraction); RUN_TEST_CASE(strtolfp, PositiveMsFraction); RUN_TEST_CASE(strtolfp, NegativeMsFraction); RUN_TEST_CASE(strtolfp, InvalidChars); } ntpsec-1.1.0+dfsg1/tests/libntp/clocktime.c0000644000175000017500000001313413252364117020377 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" #include "ntp_calendar.h" #include "caltime.h" TEST_GROUP(clocktime); // --------------------------------------------------------------------- // test fixture // // The clocktimeTest uses the NTP calendar feature to use a mockup // function for getting the current system time, so the tests are not // dependent on the actual system time. static time_t fixedpivot; TEST_SETUP(clocktime) { fixedpivot = settime(2000, 1, 1, 0, 0, 0); } TEST_TEAR_DOWN(clocktime) { } // --------------------------------------------------------------------- // test cases TEST(clocktime, CurrentYear) { // Timestamp: 2010-06-24 12:50:00Z, no year passed in const uint32_t timestamp = 3486372600UL; const uint32_t expected = timestamp; // exactly the same. const int year=0, yday=175, hour=12, minute=50, second=0; uint32_t yearstart=0; uint32_t actual; TEST_ASSERT_TRUE(clocktime(year, yday, hour, minute, second, fixedpivot, timestamp, &yearstart, &actual)); TEST_ASSERT_EQUAL(expected, actual); TEST_ASSERT_EQUAL(yearstart, 3471292800); } TEST(clocktime, CurrentYearExplicit) { // Timestamp: 2010-06-24 12:50:00Z, explicit year passed in const uint32_t timestamp = 3486372600UL; const uint32_t expected = timestamp; // exactly the same. const int year=2010, yday=175, hour=12, minute=50, second=0; uint32_t yearstart=0; uint32_t actual; TEST_ASSERT_TRUE(clocktime(year, yday, hour, minute, second, fixedpivot, timestamp, &yearstart, &actual)); /* If this assertion fails with "Expected 3486372600 was * 104913720" that's a 32-bit integer overflow and your compiler * is failing to cast to int properly inside clocktime. * Observed on Mac OS X. */ TEST_ASSERT_EQUAL(expected, actual); TEST_ASSERT_EQUAL(yearstart, 3471292800); } TEST(clocktime, CurrentYearFuzz) { /* * Timestamp (rec_ui) is: 2010-06-24 12:50:00 * Time sent into function is 12:00:00. * * Since the fuzz is rather small, we should get a NTP * timestamp for the 12:00:00 time. */ const uint32_t timestamp = 3486372600UL; // 2010-06-24 12:50:00Z const uint32_t expected = 3486369600UL; // 2010-06-24 12:00:00Z const int year=0, yday=175, hour=12, minute=0, second=0; uint32_t yearstart=0; uint32_t actual; TEST_ASSERT_TRUE(clocktime(year, yday, hour, minute, second, fixedpivot, timestamp, &yearstart, &actual)); TEST_ASSERT_EQUAL(expected, actual); } TEST(clocktime, WrongYearStart) { /* * Timestamp (rec_ui) is: 2010-01-02 11:00:00Z * Time sent into function is 11:00:00. * Yearstart sent into function is the yearstart of 2009! */ const uint32_t timestamp = 3471418800UL; const uint32_t expected = timestamp; const int year=0, yday=2, hour=11, minute=0, second=0; uint32_t yearstart = 302024100UL; // Yearstart of 2009. uint32_t actual; TEST_ASSERT_TRUE(clocktime(year, yday, hour, minute, second, fixedpivot, timestamp, &yearstart, &actual)); TEST_ASSERT_EQUAL(expected, actual); } TEST(clocktime, PreviousYear) { /* * Timestamp is: 2010-01-01 01:00:00Z * Time sent into function is 23:00:00 * (which is meant to be 2009-12-31 23:00:00Z) */ const uint32_t timestamp = 3471296400UL; const uint32_t expected = 3471289200UL; const int year=0, yday=365, hour=23, minute=0, second=0; uint32_t yearstart = 0; uint32_t actual; TEST_ASSERT_TRUE(clocktime(year, yday, hour, minute, second, fixedpivot, timestamp, &yearstart, &actual)); TEST_ASSERT_EQUAL(expected, actual); } TEST(clocktime, NextYear) { /* * Timestamp is: 2009-12-31 23:00:00Z * Time sent into function is 01:00:00 * (which is meant to be 2010-01-01 01:00:00Z) */ const uint32_t timestamp = 3471289200UL; const uint32_t expected = 3471296400UL; const int year=0, yday=1, hour=1, minute=0, second=0; uint32_t yearstart = 0; uint32_t actual; TEST_ASSERT_TRUE(clocktime(year, yday, hour, minute, second, fixedpivot, timestamp, &yearstart, &actual)); TEST_ASSERT_EQUAL(expected, actual); } TEST(clocktime, NoReasonableConversion) { /* Timestamp is: 2010-01-02 11:00:00Z */ const uint32_t timestamp = 3471418800UL; const int year=0, yday=100, hour=12, minute=0, second=0; uint32_t yearstart = 0; uint32_t actual; TEST_ASSERT_FALSE(clocktime(year, yday, hour, minute, second, fixedpivot, timestamp, &yearstart, &actual)); } TEST(clocktime, AlwaysInLimit) { /* Timestamp is: 2010-01-02 11:00:00Z */ const uint32_t timestamp = 3471418800UL; const unsigned short prime_incs[] = { 127, 151, 163, 179 }; int cyc; int yday; unsigned char whichprime; unsigned short ydayinc; int hour; int minute; uint32_t yearstart; uint32_t actual; uint32_t diff; yearstart = 0; for (cyc = 0; cyc < 5; cyc++) { settime(1900 + cyc * 65, 1, 1, 0, 0, 0); for (yday = -26000; yday < 26000; yday += ydayinc) { whichprime = abs(yday) % (int)COUNTOF(prime_incs); ydayinc = prime_incs[whichprime]; for (hour = -204; hour < 204; hour += 2) { for (minute = -60; minute < 60; minute++) { (void)clocktime(0, yday, hour, minute, 30, fixedpivot, timestamp, &yearstart, &actual); diff = actual - timestamp; if (diff >= 0x80000000UL) { diff = ~diff + 1; } TEST_ASSERT_TRUE(diff <= (183u * SECSPERDAY)); } } } } } TEST_GROUP_RUNNER(clocktime) { RUN_TEST_CASE(clocktime, CurrentYear); RUN_TEST_CASE(clocktime, CurrentYearExplicit); RUN_TEST_CASE(clocktime, CurrentYearFuzz); RUN_TEST_CASE(clocktime, WrongYearStart); RUN_TEST_CASE(clocktime, PreviousYear); RUN_TEST_CASE(clocktime, NextYear); RUN_TEST_CASE(clocktime, NoReasonableConversion); RUN_TEST_CASE(clocktime, AlwaysInLimit); } ntpsec-1.1.0+dfsg1/tests/libntp/refidsmear.c0000644000175000017500000000570313252364117020551 0ustar rlaagerrlaager#include "config.h" #include "ntp.h" #include "ntp_fp.h" #include "unity.h" #include "unity_fixture.h" #include static l_fp convertRefIDToLFP(uint32_t r) __attribute__((const)); static l_fp convertRefIDToLFP(uint32_t r) { l_fp temp = 0; r = ntohl(r); // printf("%03d %08x: ", (r >> 24) & 0xFF, (r & 0x00FFFFFF) ); setlfpfrac(temp, r << 10); /* 22 fractional bits */ r = (r >> 22) & 0x3; r |= ~(r & 2) + 1; setlfpuint(temp, r); return temp; } TEST_GROUP(refidsmear); TEST_SETUP(refidsmear) {} TEST_TEAR_DOWN(refidsmear) {} /* * we want to test a refid format of: * 254.x.y.x * * where x.y.z are 24 bits containing 2 (signed) integer bits * and 22 fractional bits. * * we want functions to convert to/from this format, with unit tests. * * Interesting test cases include: * 254.0.0.0 * 254.0.0.1 * 254.127.255.255 * 254.128.0.0 * 254.255.255.255 */ static void rtol(uint32_t r, const char *es) { l_fp l; char *as; char msg[100]; TEST_ASSERT_NOT_NULL(es); snprintf(msg, 100, "rtol was called with r=%#.8x, es=%s", r, es); l = convertRefIDToLFP(htonl(r)); as = lfptoa(l, 8); //printf("refid %#x, smear %s\n", r, as); TEST_ASSERT_NOT_NULL_MESSAGE(as, msg); TEST_ASSERT_EQUAL_STRING_MESSAGE(es, as, msg); return; } static void rtoltor(uint32_t er, const char *es) { l_fp l; char *as; uint32_t ar; char msg[100]; TEST_ASSERT_NOT_NULL(es); snprintf(msg, 100, "rtoltor was called with er=%#.8x, es=%s", er, es); l = convertRefIDToLFP(htonl(er)); as = lfptoa(l, 8); ar = convertLFPToRefID(l); //printf("smear %s, refid %#.8x\n", lfptoa(&l, 8), ntohl(ar)); TEST_ASSERT_NOT_NULL_MESSAGE(as, msg); TEST_ASSERT_EQUAL_STRING_MESSAGE(es, as, msg); TEST_ASSERT_EQUAL_UINT_MESSAGE(er, ntohl(ar), msg); return; } TEST(refidsmear, Main) { rtol(0xfe800000, "-2.00000000"); rtol(0xfe800001, "-1.99999976"); rtol(0xfe8ffffe, "-1.75000048"); rtol(0xfe8fffff, "-1.75000024"); rtol(0xfef00000, "-0.25000000"); rtol(0xfef00001, "-0.24999976"); rtol(0xfefffffe, "-0.00000048"); rtol(0xfeffffff, "-0.00000024"); rtol(0xfe000000, "0.00000000"); rtol(0xfe000001, "0.00000024"); rtol(0xfe6ffffe, "1.74999952"); rtol(0xfe6fffff, "1.74999976"); rtol(0xfe700000, "1.75000000"); rtol(0xfe700001, "1.75000024"); rtol(0xfe7ffffe, "1.99999952"); rtol(0xfe7fffff, "1.99999976"); rtoltor(0xfe800000, "-2.00000000"); rtoltor(0xfe800001, "-1.99999976"); rtoltor(0xfe8ffffe, "-1.75000048"); rtoltor(0xfe8fffff, "-1.75000024"); rtoltor(0xfef00000, "-0.25000000"); rtoltor(0xfef00001, "-0.24999976"); rtoltor(0xfefffffe, "-0.00000048"); rtoltor(0xfeffffff, "-0.00000024"); rtoltor(0xfe000000, "0.00000000"); rtoltor(0xfe000001, "0.00000024"); rtoltor(0xfe6ffffe, "1.74999952"); rtoltor(0xfe6fffff, "1.74999976"); rtoltor(0xfe700000, "1.75000000"); rtoltor(0xfe700001, "1.75000024"); rtoltor(0xfe7ffffe, "1.99999952"); rtoltor(0xfe7fffff, "1.99999976"); return; } TEST_GROUP_RUNNER(refidsmear) { RUN_TEST_CASE(refidsmear, Main); } ntpsec-1.1.0+dfsg1/tests/libntp/decodenetnum.c0000644000175000017500000000753613252364117021110 0ustar rlaagerrlaager#include /* for close() */ #include #include #include #include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(decodenetnum); TEST_SETUP(decodenetnum) {} TEST_TEAR_DOWN(decodenetnum) {} #include "sockaddrtest.h" TEST(decodenetnum, Services) { const char *str = "/etc/services"; int ret; /* try to open /etc/services for read */ ret = open(str, O_RDONLY); TEST_ASSERT_NOT_EQUAL(-1, ret); if ( -1 != ret) { close(ret); } } TEST(decodenetnum, IPv4AddressOnly) { const char *str = "192.0.2.1"; int ret; sockaddr_u actual; sockaddr_u expected; SET_AF(&expected, AF_INET); PSOCK_ADDR4(&expected)->s_addr = inet_addr("192.0.2.1"); SET_PORT(&expected, NTP_PORT); ret = decodenetnum(str, &actual); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_TRUE(IsEqualS(&expected, &actual)); } /* test with numeric port */ TEST(decodenetnum, IPv4AddressWithPort) { const char *str = "192.0.2.2:2000"; sockaddr_u actual; int ret; sockaddr_u expected; SET_AF(&expected, AF_INET); PSOCK_ADDR4(&expected)->s_addr = inet_addr("192.0.2.2"); SET_PORT(&expected, 2000); ret = decodenetnum(str, &actual); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_TRUE(IsEqualS(&expected, &actual)); } /* test for named port */ TEST(decodenetnum, IPv4AddressWithPort2) { const char *str = "192.168.2.2:ntp"; sockaddr_u actual; int ret; sockaddr_u expected; SET_AF(&expected, AF_INET); PSOCK_ADDR4(&expected)->s_addr = inet_addr("192.168.2.2"); SET_PORT(&expected, 123); ret = decodenetnum(str, &actual); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_TRUE(IsEqualS(&expected, &actual)); } TEST(decodenetnum, IPv6AddressOnly) { int ret; const struct in6_addr address = {{{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x08, 0xd3, 0x13, 0x19, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }}}; const char *str = "2001:0db8:85a3:08d3:1319:8a2e:0370:7334"; sockaddr_u actual; sockaddr_u expected; SET_AF(&expected, AF_INET6); SET_SOCK_ADDR6(&expected, address); SET_PORT(&expected, NTP_PORT); ret = decodenetnum(str, &actual); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_TRUE(IsEqualS(&expected, &actual)); } TEST(decodenetnum, IPv6AddressWithPort) { const struct in6_addr address = {{{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x08, 0xd3, 0x13, 0x19, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }}}; int ret; const char *str = "[2001:0db8:85a3:08d3:1319:8a2e:0370:7334]:3000"; sockaddr_u actual; sockaddr_u expected; SET_AF(&expected, AF_INET6); SET_SOCK_ADDR6(&expected, address); SET_PORT(&expected, 3000); ret = decodenetnum(str, &actual); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_TRUE(IsEqualS(&expected, &actual)); } TEST(decodenetnum, IllegalAddress) { const char *str = "192.0.2.270:2000"; sockaddr_u actual; int ret; ret = decodenetnum(str, &actual); TEST_ASSERT_NOT_EQUAL(0, ret); } TEST(decodenetnum, IllegalCharInPort) { /* An illegal port does not make the decodenetnum fail, but instead * makes it use the standard port. */ const char *str = "192.0.2.1:a700"; sockaddr_u actual; int ret; sockaddr_u expected; SET_AF(&expected, AF_INET); PSOCK_ADDR4(&expected)->s_addr = inet_addr("192.0.2.1"); SET_PORT(&expected, NTP_PORT); ret = decodenetnum(str, &actual); TEST_ASSERT_NOT_EQUAL(0, ret); TEST_ASSERT_TRUE(IsDiffS(&expected, &actual)); } TEST_GROUP_RUNNER(decodenetnum) { RUN_TEST_CASE(decodenetnum, Services); RUN_TEST_CASE(decodenetnum, IPv4AddressOnly); RUN_TEST_CASE(decodenetnum, IPv4AddressWithPort); RUN_TEST_CASE(decodenetnum, IPv4AddressWithPort2); RUN_TEST_CASE(decodenetnum, IPv6AddressOnly); RUN_TEST_CASE(decodenetnum, IPv6AddressWithPort); RUN_TEST_CASE(decodenetnum, IllegalAddress); RUN_TEST_CASE(decodenetnum, IllegalCharInPort); } ntpsec-1.1.0+dfsg1/tests/libntp/ymd2yd.c0000644000175000017500000000154413252364117017637 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(ymd2yd); TEST_SETUP(ymd2yd) {} TEST_TEAR_DOWN(ymd2yd) {} TEST(ymd2yd, NonLeapYearFebruary) { TEST_ASSERT_EQUAL(31+20, ymd2yd(2010,2,20)); //2010-02-20 } TEST(ymd2yd, NonLeapYearJune) { int expected = 31+28+31+30+31+18; // 18 June non-leap year TEST_ASSERT_EQUAL(expected, ymd2yd(2011,6,18)); } TEST(ymd2yd, LeapYearFebruary) { TEST_ASSERT_EQUAL(31+20, ymd2yd(2012,2,20)); //2012-02-20 (leap year) } TEST(ymd2yd, LeapYearDecember) { // 2012-12-31 int expected = 31+29+31+30+31+30+31+31+30+31+30+31; TEST_ASSERT_EQUAL(expected, ymd2yd(2012,12,31)); } TEST_GROUP_RUNNER(ymd2yd) { RUN_TEST_CASE(ymd2yd, NonLeapYearFebruary); RUN_TEST_CASE(ymd2yd, NonLeapYearJune); RUN_TEST_CASE(ymd2yd, LeapYearFebruary); RUN_TEST_CASE(ymd2yd, LeapYearDecember); } ntpsec-1.1.0+dfsg1/tests/libntp/hextolfp.c0000644000175000017500000000340713252364117020260 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(hextolfp); TEST_SETUP(hextolfp) {} TEST_TEAR_DOWN(hextolfp) {} #include "lfptest.h" TEST(hextolfp, PositiveInteger) { const char *str = "00001000.00000000"; l_fp actual; l_fp expected = lfpinit(4096, 0); // 16^3, no fraction part. TEST_ASSERT_TRUE(hextolfp(str, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); } TEST(hextolfp, NegativeInteger) { const char *str = "ffffffff.00000000"; // -1 decimal l_fp actual; l_fp expected = lfpinit(-1, 0); TEST_ASSERT_TRUE(hextolfp(str, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); } TEST(hextolfp, PositiveFraction) { const char *str = "00002000.80000000"; // 8196.5 decimal l_fp actual; l_fp expected = lfpinit(8192, HALF); TEST_ASSERT_TRUE(hextolfp(str, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); } TEST(hextolfp, NegativeFraction) { const char *str = "ffffffff.40000000"; // -1 + 0.25 decimal l_fp actual; l_fp expected = lfpinit(-1, QUARTER); //-1 + 0.25 TEST_ASSERT_TRUE(hextolfp(str, &actual)); TEST_ASSERT_TRUE(IsEqual(&expected, &actual)); } TEST(hextolfp, IllegalNumberOfInteger) { const char *str = "1000000.00000000"; // Missing one digit in integral part. l_fp actual; TEST_ASSERT_FALSE(hextolfp(str, &actual)); } TEST(hextolfp, IllegalChar) { const char *str = "10000000.0000h000"; // Illegal character h. l_fp actual; TEST_ASSERT_FALSE(hextolfp(str, &actual)); } TEST_GROUP_RUNNER(hextolfp) { RUN_TEST_CASE(hextolfp, PositiveInteger); RUN_TEST_CASE(hextolfp, NegativeInteger); RUN_TEST_CASE(hextolfp, PositiveFraction); RUN_TEST_CASE(hextolfp, NegativeFraction); RUN_TEST_CASE(hextolfp, IllegalNumberOfInteger); RUN_TEST_CASE(hextolfp, IllegalChar); } ntpsec-1.1.0+dfsg1/tests/libntp/statestr.c0000644000175000017500000000156113252364117020277 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(statestr); TEST_SETUP(statestr) {} TEST_TEAR_DOWN(statestr) {} #include "ntp.h" // Needed for MAX_MAC_LEN used in ntp_control.h #include "ntp_control.h" // eventstr() TEST(statestr, PeerRestart) { TEST_ASSERT_EQUAL_STRING("restart", eventstr(PEVNT_RESTART)); } TEST(statestr, SysUnspecified) { TEST_ASSERT_EQUAL_STRING("unspecified", eventstr(EVNT_UNSPEC)); } // ceventstr() TEST(statestr, ClockCodeExists) { TEST_ASSERT_EQUAL_STRING("clk_unspec", ceventstr(CTL_CLK_OKAY)); } TEST(statestr, ClockCodeUnknown) { TEST_ASSERT_EQUAL_STRING("clk_-1", ceventstr(-1)); } TEST_GROUP_RUNNER(statestr) { RUN_TEST_CASE(statestr, PeerRestart); RUN_TEST_CASE(statestr, SysUnspecified); RUN_TEST_CASE(statestr, ClockCodeExists); RUN_TEST_CASE(statestr, ClockCodeUnknown); } ntpsec-1.1.0+dfsg1/tests/libntp/lfptostr.c0000644000175000017500000001173413252364117020306 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" #include "ntp_fp.h" TEST_GROUP(lfptostr); TEST_SETUP(lfptostr) {} TEST_TEAR_DOWN(lfptostr) {} /* * This file contains test for both mfptoa and mfptoms (which uses dolfptoa), * since all these functions are very similar. It also tests ulfptoa, which is * a macro. */ static const int LFP_MAX_PRECISION = 10; static const int LFP_MAX_PRECISION_MS = 7; static const uint32_t ONE_FOURTH = 1073741824U; // (1 << 30) static const uint32_t HALF = 2147483648U; // (1 << 31) static const uint32_t THREE_FOURTH = 3221225472U; static const uint32_t HALF_PROMILLE_UP = 2147484; // slightly more than 0.0005 static const uint32_t HALF_PROMILLE_DOWN = 2147483; // slightly less than 0.0005 TEST(lfptostr, PositiveInteger) { l_fp test = lfpinit(200, 0); // exact 200.0000000000 TEST_ASSERT_EQUAL_STRING("200.0000000000", mfptoa(test, LFP_MAX_PRECISION)); TEST_ASSERT_EQUAL_STRING("200000.0000000", mfptoms(test, LFP_MAX_PRECISION_MS)); } TEST(lfptostr, NegativeInteger) { l_fp test = lfpinit(-100, 0); // -100 TEST_ASSERT_EQUAL_STRING("-100.0000000000", lfptoa(test, LFP_MAX_PRECISION)); TEST_ASSERT_EQUAL_STRING("-100000.0000000", lfptoms(test, LFP_MAX_PRECISION_MS)); } TEST(lfptostr, PositiveIntegerWithFraction) { l_fp test = lfpinit(200, ONE_FOURTH); // 200.25 TEST_ASSERT_EQUAL_STRING("200.2500000000", lfptoa(test, LFP_MAX_PRECISION)); TEST_ASSERT_EQUAL_STRING("200250.0000000", lfptoms(test, LFP_MAX_PRECISION_MS)); } TEST(lfptostr, NegativeIntegerWithFraction) { l_fp test = lfpinit(-100, ONE_FOURTH); // -99.75 TEST_ASSERT_EQUAL_STRING("-99.7500000000", lfptoa(test, LFP_MAX_PRECISION)); TEST_ASSERT_EQUAL_STRING("-99750.0000000", lfptoms(test, LFP_MAX_PRECISION_MS)); } TEST(lfptostr, RoundingDownToInteger) { l_fp test = lfpinit(10, ONE_FOURTH); // 10.25 TEST_ASSERT_EQUAL_STRING("10", lfptoa(test, 0)); TEST_ASSERT_EQUAL_STRING("10250", lfptoms(test, 0)); } TEST(lfptostr, RoundingMiddleToInteger) { l_fp test = lfpinit(10, HALF); // 10.5 TEST_ASSERT_EQUAL_STRING("11", lfptoa(test, 0)); TEST_ASSERT_EQUAL_STRING("10500", lfptoms(test, 0)); } TEST(lfptostr, RoundingUpToInteger) { l_fp test = lfpinit(5, THREE_FOURTH); // 5.75 TEST_ASSERT_EQUAL_STRING("6", lfptoa(test, 0)); TEST_ASSERT_EQUAL_STRING("5750", lfptoms(test, 0)); } TEST(lfptostr, SingleDecimal) { l_fp test = lfpinit(8, ONE_FOURTH); // 8.25 TEST_ASSERT_EQUAL_STRING("8.3", lfptoa(test, 1)); TEST_ASSERT_EQUAL_STRING("8250.0", lfptoms(test, 1)); } TEST(lfptostr, MillisecondsRoundingUp) { l_fp test = lfpinit(1, HALF_PROMILLE_UP); //slightly more than 1.0005 TEST_ASSERT_EQUAL_STRING("1.0", lfptoa(test, 1)); TEST_ASSERT_EQUAL_STRING("1000.5", lfptoms(test, 1)); TEST_ASSERT_EQUAL_STRING("1001", lfptoms(test, 0)); } TEST(lfptostr, MillisecondsRoundingDown) { l_fp test = lfpinit(1, HALF_PROMILLE_DOWN); // slightly less than 1.0005 TEST_ASSERT_EQUAL_STRING("1.0", lfptoa(test, 1)); TEST_ASSERT_EQUAL_STRING("1000.5", lfptoms(test, 1)); TEST_ASSERT_EQUAL_STRING("1000", lfptoms(test, 0)); } TEST(lfptostr, UnsignedInteger) { l_fp test1 = lfpinit((int32_t)3000000000LL, 0); l_fp test2 = lfpinit((int32_t)3000000L, 0x80000000); l_fp test3 = lfpinit((int32_t)13L, 0xC0000000); l_fp test4 = lfpinit((int32_t)13L, 0x028F5C28); l_fp test5 = lfpinit((int32_t)4212665562LL, 0x3C6BE7E6); l_fp test6 = lfpinit((int32_t)4212665562LL, 0x36222683); l_fp test7 = lfpinit((int32_t)4212665562LL, 0xBD3F2F5A); l_fp test8a = lfpinit((int32_t)1444359386LL, 0x2E0C7582); l_fp test8b = lfpinit((int32_t)1444359386LL, 0x2E0C7583); l_fp test9 = lfpinit((int32_t)3660323067LL, 0x1CD3101C); TEST_ASSERT_EQUAL_STRING("3000000000.0", ulfptoa(test1, 1)); TEST_ASSERT_EQUAL_STRING("3000000000.000000", ulfptoa(test1, 6)); TEST_ASSERT_EQUAL_STRING("3000000.5", ulfptoa(test2, 1)); TEST_ASSERT_EQUAL_STRING("3000000.500", ulfptoa(test2, 3)); TEST_ASSERT_EQUAL_STRING("13.750000", ulfptoa(test3, 6)); TEST_ASSERT_EQUAL_STRING("13.010000", ulfptoa(test4, 6)); TEST_ASSERT_EQUAL_STRING("4212665562.2360215127", ulfptoa(test5, 10)); TEST_ASSERT_EQUAL_STRING("4212665562.2114585943", ulfptoa(test6, 10)); TEST_ASSERT_EQUAL_STRING("4212665562.739245376", ulfptoa(test7, 9)); TEST_ASSERT_EQUAL_STRING("1444359386.1798776095", ulfptoa(test8a, 10)); TEST_ASSERT_EQUAL_STRING("1444359386.1798776097", ulfptoa(test8b, 10)); TEST_ASSERT_EQUAL_STRING("3660323067.1125955647", ulfptoa(test9, 10)); } TEST_GROUP_RUNNER(lfptostr) { RUN_TEST_CASE(lfptostr, PositiveInteger); RUN_TEST_CASE(lfptostr, NegativeInteger); RUN_TEST_CASE(lfptostr, PositiveIntegerWithFraction); RUN_TEST_CASE(lfptostr, NegativeIntegerWithFraction); RUN_TEST_CASE(lfptostr, RoundingDownToInteger); RUN_TEST_CASE(lfptostr, RoundingMiddleToInteger); RUN_TEST_CASE(lfptostr, RoundingUpToInteger); RUN_TEST_CASE(lfptostr, SingleDecimal); RUN_TEST_CASE(lfptostr, MillisecondsRoundingUp); RUN_TEST_CASE(lfptostr, MillisecondsRoundingDown); RUN_TEST_CASE(lfptostr, UnsignedInteger); } ntpsec-1.1.0+dfsg1/tests/libntp/vi64ops.c0000644000175000017500000000170013252364117017733 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(vi64ops); TEST_SETUP(vi64ops) {} TEST_TEAR_DOWN(vi64ops) {} TEST(vi64ops, SetVUI64s_pos) { time64_t expx = 0; settime64s(expx, 0x0123456789ABCDEF); TEST_ASSERT_EQUAL(time64s(expx), 81985529216486895); } TEST(vi64ops, SetVUI64s_neg) { time64_t expx = 0; settime64s(expx, 0xFEDCBA9876543210); TEST_ASSERT_EQUAL(time64s(expx), -81985529216486896); } TEST(vi64ops, SetVUI64u) { time64_t expx = 0; settime64u(expx, 0xFEDCBA9876543210); /* sign bit is on */ TEST_ASSERT_EQUAL(time64s(expx), 18364758544493064720UL); } TEST(vi64ops, NegVUI64) { time64_t expx = 0; settime64s(expx, 71985529216486896); TEST_ASSERT_EQUAL(negtime64(expx), -71985529216486896); } TEST_GROUP_RUNNER(vi64ops) { RUN_TEST_CASE(vi64ops, SetVUI64s_pos); RUN_TEST_CASE(vi64ops, SetVUI64s_neg); RUN_TEST_CASE(vi64ops, SetVUI64u); RUN_TEST_CASE(vi64ops, NegVUI64); } ntpsec-1.1.0+dfsg1/tests/libntp/timespecops.c0000644000175000017500000003644013252364117020765 0ustar rlaagerrlaager#include "config.h" #include "ntp_types.h" #include "ntp_fp.h" #include "timespecops.h" #include "ntp_calendar.h" #include "unity.h" #include "unity_fixture.h" #include #include #define TEST_ASSERT_EQUAL_timespec(a, b) { \ TEST_ASSERT_EQUAL_MESSAGE(a.tv_sec, b.tv_sec, "Field tv_sec"); \ TEST_ASSERT_EQUAL_MESSAGE(a.tv_nsec, b.tv_nsec, "Field tv_nsec"); \ } #define TEST_ASSERT_EQUAL_l_fp(a, b) { \ TEST_ASSERT_EQUAL_MESSAGE(lfpsint(a), lfpsint(b), "Integral part"); \ TEST_ASSERT_EQUAL_UINT_MESSAGE(lfpfrac(a), lfpfrac(b), "Fractional part"); \ } static uint32_t my_tick_to_tsf(uint32_t ticks); static uint32_t my_tsf_to_tick(uint32_t tsf); // that's it... struct lfpfracdata { long nsec; uint32_t frac; }; TEST_GROUP(timespecops); TEST_SETUP(timespecops) {} TEST_TEAR_DOWN(timespecops) {} //***************************MY CUSTOM FUNCTIONS*************************** static bool timespec_isValid(struct timespec V) { return V.tv_nsec >= 0 && V.tv_nsec < 1000000000; } static struct timespec timespec_init(time_t hi, long lo) { struct timespec V; V.tv_sec = hi; V.tv_nsec = lo; return V; } static bool AssertFpClose(const l_fp m, const l_fp n, const l_fp limit) { int64_t diff = (int64_t)(m - n); if ((l_fp)llabs(diff) <= limit) return true; else { printf("m_expr which is %s \nand\nn_expr which is %s\nare not close; diff=%susec\n", lfptoa(m, 10), lfptoa(n, 10), lfptoa((l_fp)diff, 10)); return false; } } static bool AssertTimespecClose(const struct timespec m, const struct timespec n, const struct timespec limit) { struct timespec diff; diff = abs_tspec(sub_tspec(m, n)); if (cmp_tspec(limit, diff) >= 0) return true; else { printf("m_expr which is %ld.%ld \nand\nn_expr which is %ld.%ld\nare not close; diff=%ld.%ldnsec\n", (long)m.tv_sec, m.tv_nsec, (long)n.tv_sec, n.tv_nsec, (long)diff.tv_sec, diff.tv_nsec); return false; } } //----------------------------------------------- static const struct lfpfracdata fdata[] = { { 0, 0x00000000 }, { 2218896, 0x00916ae6 }, { 16408100, 0x0433523d }, { 125000000, 0x20000000 }, { 250000000, 0x40000000 }, { 287455871, 0x4996b53d }, { 375000000, 0x60000000 }, { 500000000, 0x80000000 }, { 518978897, 0x84dbcd0e }, { 563730222, 0x90509fb3 }, { 563788007, 0x9054692c }, { 583289882, 0x95527c57 }, { 607074509, 0x9b693c2a }, { 625000000, 0xa0000000 }, { 645184059, 0xa52ac851 }, { 676497788, 0xad2ef583 }, { 678910895, 0xadcd1abb }, { 679569625, 0xadf84663 }, { 690926741, 0xb0e0932d }, { 705656483, 0xb4a5e73d }, { 723553854, 0xb93ad34c }, { 750000000, 0xc0000000 }, { 763550253, 0xc3780785 }, { 775284917, 0xc6791284 }, { 826190764, 0xd3813ce8 }, { 875000000, 0xe0000000 }, { 956805507, 0xf4f134a9 }, { 982570733, 0xfb89c16c } }; static uint32_t my_tick_to_tsf(uint32_t ticks) { // convert nanoseconds to l_fp fractional units, using double // precision float calculations or, if available, 64bit integer // arithmetic. This should give the precise fraction, rounded to // the nearest representation. return (uint32_t )((( ((uint64_t)(ticks)) << 32) + 500000000) / 1000000000); // And before you ask: if ticks >= 1000000000, the result is // truncated nonsense, so don't use it out-of-bounds. } static uint32_t my_tsf_to_tick(uint32_t tsf) { // Inverse operation: converts fraction to microseconds. return (uint32_t)(( ((uint64_t)(tsf)) * 1000000000 + 0x80000000) >> 32); // Beware: The result might be 10^9 due to rounding! } // --------------------------------------------------------------------- // test support stuff -- part 1 // --------------------------------------------------------------------- TEST(timespecops, Helpers1) { struct timespec x; for (x.tv_sec = -2; x.tv_sec < 3; x.tv_sec++) { x.tv_nsec = -1; TEST_ASSERT_FALSE(timespec_isValid(x)); x.tv_nsec = 0; TEST_ASSERT_TRUE(timespec_isValid(x)); x.tv_nsec = 999999999; TEST_ASSERT_TRUE(timespec_isValid(x)); x.tv_nsec = 1000000000; TEST_ASSERT_FALSE(timespec_isValid(x)); } return; } //---------------------------------------------------------------------- // test normalisation //---------------------------------------------------------------------- TEST(timespecops, Normalise) { long ns; for ( ns = -2000000000; ns <= 2000000000; ns += 10000000) { struct timespec x = timespec_init(0, ns); x = normalize_tspec(x); TEST_ASSERT_TRUE(timespec_isValid(x)); } return; } //---------------------------------------------------------------------- // test classification //---------------------------------------------------------------------- TEST(timespecops, SignNoFrac) { // sign test, no fraction int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i, 0); int E = (i > 0) - (i < 0); int r = test_tspec(a); TEST_ASSERT_EQUAL(E, r); } return; } TEST(timespecops, SignWithFrac) { // sign test, with fraction int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i, 10); int E = (i >= 0) - (i < 0); int r = test_tspec(a); TEST_ASSERT_EQUAL(E, r); } return; } //---------------------------------------------------------------------- // test compare //---------------------------------------------------------------------- TEST(timespecops, CmpFracEQ) { // fractions are equal int i, j; for (i = -4; i <= 4; ++i) for (j = -4; j <= 4; ++j) { struct timespec a = timespec_init( i , 200); struct timespec b = timespec_init( j , 200); int E = (i > j) - (i < j); int r = cmp_tspec_denorm(a, b); TEST_ASSERT_EQUAL(E, r); } return; } TEST(timespecops, CmpFracGT) { // fraction a bigger fraction b int i, j; for (i = -4; i <= 4; ++i) for (j = -4; j <= 4; ++j) { struct timespec a = timespec_init(i, 999999800); struct timespec b = timespec_init(j, 200); int E = (i >= j) - (i < j); int r = cmp_tspec_denorm(a, b); TEST_ASSERT_EQUAL(E, r); } return; } TEST(timespecops, CmpFracLT) { // fraction a less fraction b int i, j; for (i = -4; i <= 4; ++i) for (j = -4; j <= 4; ++j) { struct timespec a = timespec_init(i, 200); struct timespec b = timespec_init(j, 999999800); int E = (i > j) - (i <= j); int r = cmp_tspec_denorm(a, b); TEST_ASSERT_EQUAL(E, r); } return; } //---------------------------------------------------------------------- // Test addition (sum) //---------------------------------------------------------------------- TEST(timespecops, AddFullNorm) { int i, j; for (i = -4; i <= 4; ++i) for (j = -4; j <= 4; ++j) { struct timespec a = timespec_init(i, 200); struct timespec b = timespec_init(j, 400); struct timespec E = timespec_init(i + j, 200 + 400); struct timespec c; c = add_tspec(a, b); TEST_ASSERT_EQUAL_timespec(E, c); } return; } TEST(timespecops, AddFullOflow1) { int i, j; for (i = -4; i <= 4; ++i) for (j = -4; j <= 4; ++j) { struct timespec a = timespec_init(i, 200); struct timespec b = timespec_init(j, 999999900); struct timespec E = timespec_init(i + j + 1, 100); struct timespec c; c = add_tspec(a, b); TEST_ASSERT_EQUAL_timespec(E, c); } return; } TEST(timespecops, AddNsecNorm) { int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i, 200); struct timespec E = timespec_init(i, 600); struct timespec c; c = add_tspec_ns(a, 600 - 200); TEST_ASSERT_EQUAL_timespec(E, c); } return; } TEST(timespecops, AddNsecOflow1) { int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i, 200); struct timespec E = timespec_init(i + 1, 100); struct timespec c; c = add_tspec_ns(a, NS_PER_S - 100); TEST_ASSERT_EQUAL_timespec(E, c); } return; } //---------------------------------------------------------------------- // test subtraction (difference) //---------------------------------------------------------------------- TEST(timespecops, SubFullNorm) { int i, j; for (i = -4; i <= 4; ++i) for (j = -4; j <= 4; ++j) { struct timespec a = timespec_init( i , 600); struct timespec b = timespec_init( j , 400); struct timespec E = timespec_init(i-j, 200); struct timespec c; c = sub_tspec(a, b); TEST_ASSERT_EQUAL_timespec(E, c); } return; } TEST(timespecops, SubFullOflow) { int i, j; for (i = -4; i <= 4; ++i) for (j = -4; j <= 4; ++j) { struct timespec a = timespec_init(i, 100); struct timespec b = timespec_init(j, 999999900); struct timespec E = timespec_init(i - j - 1, 200); struct timespec c; c = sub_tspec(a, b); TEST_ASSERT_EQUAL_timespec(E, c); } return; } TEST(timespecops, SubNsecNorm) { int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i, 600); struct timespec E = timespec_init(i, 200); struct timespec c; c = sub_tspec_ns(a, 600 - 200); TEST_ASSERT_EQUAL_timespec(E, c); } return; } TEST(timespecops, SubNsecOflow) { int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init( i , 100); struct timespec E = timespec_init(i-1, 200); struct timespec c; c = sub_tspec_ns(a, NS_PER_S - 100); TEST_ASSERT_EQUAL_timespec(E, c); } return; } //---------------------------------------------------------------------- // test negation //---------------------------------------------------------------------- TEST(timespecops, test_Neg) { int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i, 100); struct timespec b; struct timespec c; b = neg_tspec(a); c = add_tspec(a, b); TEST_ASSERT_EQUAL(0, test_tspec(c)); } return; } //---------------------------------------------------------------------- // test abs value //---------------------------------------------------------------------- TEST(timespecops, test_AbsNoFrac) { int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i , 0); struct timespec b; b = abs_tspec(a); TEST_ASSERT_EQUAL((i != 0), test_tspec(b)); } return; } TEST(timespecops, test_AbsWithFrac) { int i; for (i = -4; i <= 4; ++i) { struct timespec a = timespec_init(i, 100); struct timespec b; b = abs_tspec(a); TEST_ASSERT_EQUAL(1, test_tspec(b)); } return; } // --------------------------------------------------------------------- // test support stuff -- part 2 // --------------------------------------------------------------------- /* FIXME: temporarily disabled - spews cryptic messages into test log */ TEST(timespecops, test_Helpers2) { struct timespec limit = timespec_init(0, 2); struct timespec x, y; long i; for (x.tv_sec = -2; x.tv_sec < 3; x.tv_sec++) for (x.tv_nsec = 1; x.tv_nsec < 1000000000; x.tv_nsec += 499999999) { for (i = -4; i < 5; ++i) { y = x; y.tv_nsec += i; if (i >= -2 && i <= 2) { TEST_ASSERT_TRUE(AssertTimespecClose(x, y, limit)); } else { TEST_ASSERT_FALSE(AssertTimespecClose(x, y, limit)); } } } return; } //---------------------------------------------------------------------- // conversion to l_fp //---------------------------------------------------------------------- TEST(timespecops, test_ToLFPbittest) { l_fp lfpClose = lfpinit(0, 1); uint32_t i; for (i = 0; i < 1000000000; i+=1000) { struct timespec a = timespec_init(1, (long)i); l_fp E= lfpinit(1, my_tick_to_tsf(i)); l_fp r; r = tspec_intv_to_lfp(a); TEST_ASSERT_TRUE(AssertFpClose(E, r, lfpClose)); } return; } TEST(timespecops, test_ToLFPrelPos) { int i; for (i = 0; i < (int)COUNTOF(fdata); ++i) { struct timespec a = timespec_init(1, fdata[i].nsec); l_fp E = lfpinit(1, fdata[i].frac); l_fp r; r = tspec_intv_to_lfp(a); TEST_ASSERT_EQUAL_l_fp(E, r); } return; } TEST(timespecops, test_ToLFPrelNeg) { int i; for (i = 0; i < (int)COUNTOF(fdata); ++i) { struct timespec a = timespec_init(-1, fdata[i].nsec); l_fp E = lfpinit(~0, fdata[i].frac); l_fp r; r = tspec_intv_to_lfp(a); TEST_ASSERT_EQUAL_l_fp(E, r); } return; } TEST(timespecops, test_ToLFPabs) { int i; for (i = 0; i < (int)COUNTOF(fdata); ++i) { struct timespec a = timespec_init(1, fdata[i].nsec); l_fp E = lfpinit((int)(1 + JAN_1970), fdata[i].frac); l_fp r; r = tspec_stamp_to_lfp(a); TEST_ASSERT_EQUAL_l_fp(E, r); } return; } //---------------------------------------------------------------------- // conversion from l_fp //---------------------------------------------------------------------- TEST(timespecops, test_FromLFPbittest) { struct timespec limit = timespec_init(0, 2); // Not *exactly* a bittest, because 2**32 tests would take a // really long time even on very fast machines! So we do test // every 1000 fractional units. uint32_t tsf; for (tsf = 0; tsf < ~((uint32_t)(1000)); tsf += 1000) { struct timespec E = timespec_init(1, (long)my_tsf_to_tick(tsf)); l_fp a = lfpinit(1, tsf); struct timespec r; r = lfp_intv_to_tspec(a); // The conversion might be off by one nanosecond when // comparing to calculated value. TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit)); } return; } TEST(timespecops, test_FromLFPrelPos) { struct timespec limit = timespec_init(0, 2); int i; for (i = 0; i < (int)COUNTOF(fdata); ++i) { l_fp a = lfpinit(1, fdata[i].frac); struct timespec E = timespec_init(1, fdata[i].nsec); struct timespec r; r = lfp_intv_to_tspec(a); TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit)); } return; } TEST(timespecops, test_FromLFPrelNeg) { struct timespec limit = timespec_init(0, 2); int i; for (i = 0; i < (int)COUNTOF(fdata); ++i) { l_fp a = lfpinit(~0, fdata[i].frac); struct timespec E = timespec_init(-1, fdata[i].nsec); struct timespec r; r = lfp_intv_to_tspec(a); TEST_ASSERT_TRUE(AssertTimespecClose(E, r, limit)); } return; } // nsec -> frac -> nsec roundtrip, using a prime start and increment TEST(timespecops, test_LFProundtrip) { int32_t t; uint32_t i; for (t = -1; t < 2; ++t) for (i = 4999; i < 1000000000; i += 10007) { struct timespec E = timespec_init(t, (long)i); l_fp a; struct timespec r; a = tspec_intv_to_lfp(E); r = lfp_intv_to_tspec(a); TEST_ASSERT_EQUAL_timespec(E, r); } return; } //---------------------------------------------------------------------- // string formatting //---------------------------------------------------------------------- TEST_GROUP_RUNNER(timespecops) { RUN_TEST_CASE(timespecops, Helpers1); RUN_TEST_CASE(timespecops, Normalise); RUN_TEST_CASE(timespecops, SignNoFrac); RUN_TEST_CASE(timespecops, SignWithFrac); RUN_TEST_CASE(timespecops, CmpFracEQ); RUN_TEST_CASE(timespecops, CmpFracGT); RUN_TEST_CASE(timespecops, CmpFracLT); RUN_TEST_CASE(timespecops, AddFullNorm); RUN_TEST_CASE(timespecops, AddFullOflow1); RUN_TEST_CASE(timespecops, AddNsecNorm); RUN_TEST_CASE(timespecops, AddNsecOflow1); RUN_TEST_CASE(timespecops, SubFullNorm); RUN_TEST_CASE(timespecops, SubFullOflow); RUN_TEST_CASE(timespecops, SubNsecNorm); RUN_TEST_CASE(timespecops, SubNsecOflow); RUN_TEST_CASE(timespecops, test_Neg); RUN_TEST_CASE(timespecops, test_AbsNoFrac); RUN_TEST_CASE(timespecops, test_AbsWithFrac); /* RUN_TEST_CASE(timespecops, test_Helpers2); */ RUN_TEST_CASE(timespecops, test_ToLFPbittest); RUN_TEST_CASE(timespecops, test_ToLFPrelPos); RUN_TEST_CASE(timespecops, test_ToLFPrelNeg); RUN_TEST_CASE(timespecops, test_ToLFPabs); RUN_TEST_CASE(timespecops, test_FromLFPbittest); RUN_TEST_CASE(timespecops, test_FromLFPrelPos); RUN_TEST_CASE(timespecops, test_FromLFPrelNeg); RUN_TEST_CASE(timespecops, test_LFProundtrip); } // -*- EOF -*- ntpsec-1.1.0+dfsg1/tests/libntp/netof.c0000644000175000017500000000223113252364117017534 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(netof6); TEST_SETUP(netof6) {} TEST_TEAR_DOWN(netof6) {} #include "sockaddrtest.h" TEST(netof6, IPv6Address) { /* IPv6 addresses are assumed to have 64-bit host * and 64-bit network parts. */ const struct in6_addr input_address = {{{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x08, 0xd3, 0x13, 0x19, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }}}; // 2001:0db8:85a3:08d3:1319:8a2e:0370:7334 const struct in6_addr expected_address = {{{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x08, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}; // 2001:0db8:85a3:08d3:0000:0000:0000:0000 sockaddr_u input; SET_AF(&input, AF_INET6); SET_SOCK_ADDR6(&input, input_address); SET_PORT(&input, 3000); sockaddr_u expected; SET_AF(&expected, AF_INET6); SET_SOCK_ADDR6(&expected, expected_address); SET_PORT(&expected, 3000); sockaddr_u* actual = netof6(&input); TEST_ASSERT_NOT_NULL(actual); TEST_ASSERT_TRUE(IsEqualS(&expected, actual)); } TEST_GROUP_RUNNER(netof6) { RUN_TEST_CASE(netof6, IPv6Address); } ntpsec-1.1.0+dfsg1/tests/libntp/lfpfunc.c0000644000175000017500000002325213252364117020064 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "ntp_fp.h" #include "unity.h" #include "unity_fixture.h" #include #include static double eps(double); TEST_GROUP(lfpfunc); TEST_SETUP(lfpfunc) {} TEST_TEAR_DOWN(lfpfunc) {} //---------------------------------------------------------------------- // reference comparison // This is implemented as a full signed MP-subtract in 3 limbs, where // the operands are zero or sign extended before the subtraction is // executed. //---------------------------------------------------------------------- // maybe rename it to lf_cmp_work static int cmp_work(uint32_t a[3], uint32_t b[3]) { uint32_t cy, idx, tmp; for (cy = idx = 0; idx < 3; ++idx) { tmp = a[idx]; cy = (a[idx] -= cy ) > tmp; tmp = a[idx]; cy |= (a[idx] -= b[idx]) > tmp; } if (a[2]) return -1; return a[0] || a[1]; } static int l_fp_scmp(const l_fp first, const l_fp second) { uint32_t a[3], b[3]; const l_fp op1 = first; const l_fp op2 = second; a[0] = lfpfrac(op1); a[1] = lfpuint(op1); a[2] = 0; b[0] = lfpfrac(op2); b[1] = lfpuint(op2); b[2] = 0; a[2] -= (lfpsint(op1) < 0); b[2] -= (lfpsint(op2) < 0); return cmp_work(a,b); } static int l_fp_ucmp(const l_fp first, l_fp second) { uint32_t a[3], b[3]; const l_fp op1 = first; const l_fp op2 = second; a[0] = lfpfrac(op1); a[1] = lfpuint(op1); a[2] = 0; b[0] = lfpfrac(op2); b[1] = lfpuint(op2); b[2] = 0; return cmp_work(a,b); } //---------------------------------------------------------------------- // imlementation of the LFP stuff // This should be easy enough... //---------------------------------------------------------------------- static l_fp l_fp_negate(const l_fp first) { l_fp temp = first; L_NEG(temp); return temp; } static l_fp l_fp_abs(const l_fp first) { l_fp temp = first; if (L_ISNEG(temp)) L_NEG(temp); return temp; } static int l_fp_signum(const l_fp first) { if (lfpuint(first) & 0x80000000u) return -1; return (lfpuint(first) || lfpfrac(first)); } //---------------------------------------------------------------------- // testing the relational macros works better with proper predicate // formatting functions; it slows down the tests a bit, but makes for // readable failure messages. //---------------------------------------------------------------------- static bool l_isgt (const l_fp first, const l_fp second) { return L_ISGT(first, second); } static bool l_isgtu(const l_fp first, const l_fp second) { return L_ISGTU(first, second); } //---------------------------------------------------------------------- // test data table for add/sub and compare //---------------------------------------------------------------------- static const l_fp_w addsub_tab[][3] = { // trivial identity: {{0 ,0 }, { 0,0 }, { 0,0}}, // with carry from fraction and sign change: {{(uint32_t)-1,0x80000000}, { 0,0x80000000}, { 0,0}}, // without carry from fraction {{ 1,0x40000000}, { 1,0x40000000}, { 2,0x80000000}}, // with carry from fraction: {{ 1,0xC0000000}, { 1,0xC0000000}, { 3,0x80000000}}, // with carry from fraction and sign change: {{0x7FFFFFFF, 0x7FFFFFFF}, {0x7FFFFFFF,0x7FFFFFFF}, {0xFFFFFFFE,0xFFFFFFFE}}, // two tests w/o carry (used for l_fp<-->double): {{0x55555555,0xAAAAAAAA}, {0x11111111,0x11111111}, {0x66666666,0xBBBBBBBB}}, {{0x55555555,0x55555555}, {0x11111111,0x11111111}, {0x66666666,0x66666666}}, // wide-range test, triggers compare trouble {{0x80000000,0x00000001}, {0xFFFFFFFF,0xFFFFFFFE}, {0x7FFFFFFF,0xFFFFFFFF}} }; static const size_t addsub_cnt = (sizeof(addsub_tab)/sizeof(addsub_tab[0])); static const size_t addsub_tot = (sizeof(addsub_tab)/sizeof(addsub_tab[0][0])); //---------------------------------------------------------------------- // epsilon estimation for the precision of a conversion double --> l_fp // // The error estimation limit is as follows: // * The 'l_fp' fixed point fraction has 32 bits precision, so we allow // for the LSB to toggle by clamping the epsilon to be at least 2^(-31) // // * The double mantissa has a precsion 54 bits, so the other minimum is // dval * (2^(-53)) // // The maximum of those two boundaries is used for the check. // // Note: once there are more than 54 bits between the highest and lowest // '1'-bit of the l_fp value, the roundtrip *will* create truncation // errors. This is an inherent property caused by the 54-bit mantissa of // the 'double' type. static double eps(double d) { return fmax(ldexp(1.0, -31), ldexp(fabs(d), -53)); } //---------------------------------------------------------------------- // test extractor functions //---------------------------------------------------------------------- TEST(lfpfunc, Extraction) { const uint32_t hi = 0xFFEEDDBB; const uint32_t lo = 0x66554433; l_fp lfp = lfpinit_u(hi, lo); TEST_ASSERT_EQUAL(lfpuint(lfp), hi); TEST_ASSERT_EQUAL(lfpfrac(lfp), lo); TEST_ASSERT_EQUAL(lfpsint(lfp), -1122885); l_fp bumpable = lfpinit(333, 444); bumplfpuint(bumpable, 1); TEST_ASSERT_EQUAL(lfpuint(bumpable), 334); TEST_ASSERT_EQUAL(lfpfrac(bumpable), 444); } //---------------------------------------------------------------------- // test negation //---------------------------------------------------------------------- TEST(lfpfunc, Negation) { size_t idx = 0; for (idx = 0; idx < addsub_cnt; ++idx) { l_fp op1 = lfpinit_u(addsub_tab[idx][0].l_ui, addsub_tab[idx][0].l_uf); l_fp op2 = l_fp_negate(op1); l_fp sum = op1 + op2; TEST_ASSERT_EQUAL(0, sum); } return; } //---------------------------------------------------------------------- // test absolute value //---------------------------------------------------------------------- TEST(lfpfunc, Absolute) { size_t idx = 0; for (idx = 0; idx < addsub_cnt; ++idx) { l_fp op1 = lfpinit_u(addsub_tab[idx][0].l_ui, addsub_tab[idx][0].l_uf); l_fp op2 = l_fp_abs(op1); TEST_ASSERT_TRUE(l_fp_signum(op2) >= 0); if (l_fp_signum(op1) >= 0) op1 = op1 - op2; else op1 = op1 + op2; TEST_ASSERT_EQUAL(0, op1); } // There is one special case we have to check: the minimum // value cannot be negated, or, to be more precise, the // negation reproduces the original pattern. l_fp minVal = lfpinit_u(0x80000000, 0x00000000); l_fp minAbs = l_fp_abs(minVal); TEST_ASSERT_EQUAL(-1, l_fp_signum(minVal)); TEST_ASSERT_EQUAL(minVal, minAbs); return; } static const l_fp roundtab[] = { 0, 0x140000000, 0x1c0000000, 0xffffffff80000000, 0x7fffffff7fffffff, 0x7fffffffffffffff, 0x55555555aaaaaaaa, 0x5555555555555555, 0x8000000000000001 }; static const size_t round_cnt = (sizeof(roundtab)/sizeof(roundtab[0])); //---------------------------------------------------------------------- // fp -> double -> fp roundtrip test //---------------------------------------------------------------------- TEST(lfpfunc, FDF_RoundTrip) { size_t idx = 0; char msg[512]; // since a l_fp has 64 bits in its mantissa and a double has // only 54 bits available (including the hidden '1') we have to // make a few concessions on the roundtrip precision. The 'eps()' // function makes an educated guess about the available precision // and checks the difference in the two 'l_fp' values against // that limit. for (idx = 0; idx < round_cnt; ++idx) { l_fp op1 = roundtab[idx]; double op2 = lfptod(op1); l_fp op3 = dtolfp(op2); l_fp temp = op1 - op3; double d = lfptod(temp); snprintf(msg, sizeof(msg), "\nop2: %f op3: %s diff %f not within %e", op2, mfptoa(op3, 8), d, eps(op2)); TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(eps(op2), 0.0, fabs(d), msg); } return; } //---------------------------------------------------------------------- // test the compare stuff // // This uses the local compare and checks if the operations using the // macros in 'ntp_fp.h' produce mathing results. // ---------------------------------------------------------------------- TEST(lfpfunc, SignedRelOps) { const l_fp_w * tv = (&addsub_tab[0][0]); size_t lc ; for (lc = addsub_tot - 1; lc; --lc, ++tv) { l_fp op1 = lfpinit_u(tv[0].l_ui, tv[0].l_uf); l_fp op2 = lfpinit_u(tv[1].l_ui, tv[1].l_uf); int cmp = l_fp_scmp(op1, op2); switch (cmp) { case -1: TEST_ASSERT_TRUE (l_isgt(op2, op1)); TEST_ASSERT_FALSE(l_isgt(op1, op2)); break; case 1: TEST_ASSERT_TRUE (l_isgt(op1, op2)); TEST_ASSERT_FALSE(l_isgt(op2, op1)); break; case 0: TEST_ASSERT_FALSE(l_isgt(op1, op2)); TEST_ASSERT_FALSE(l_isgt(op2, op1)); break; default: TEST_FAIL_MESSAGE("unexpected UCMP result: "); break; } } return; } TEST(lfpfunc, UnsignedRelOps) { const l_fp_w *tv = (&addsub_tab[0][0]); size_t lc; for (lc = addsub_tot - 1; lc; --lc, ++tv) { l_fp op1 = lfpinit_u(tv[0].l_ui, tv[0].l_uf); l_fp op2 = lfpinit_u(tv[1].l_ui, tv[1].l_uf); int cmp = l_fp_ucmp(op1, op2); switch (cmp) { case -1: TEST_ASSERT_TRUE (l_isgtu(op2, op1)); TEST_ASSERT_FALSE(l_isgtu(op1, op2)); break; case 1: TEST_ASSERT_TRUE (l_isgtu(op1, op2)); TEST_ASSERT_FALSE(l_isgtu(op2, op1)); break; case 0: TEST_ASSERT_FALSE(l_isgtu(op1, op2)); TEST_ASSERT_FALSE(l_isgtu(op2, op1)); break; default: TEST_FAIL_MESSAGE("unexpected UCMP result: "); } } return; } /* */ //---------------------------------------------------------------------- // that's all folks... but feel free to add things! //---------------------------------------------------------------------- TEST_GROUP_RUNNER(lfpfunc) { RUN_TEST_CASE(lfpfunc, Extraction); RUN_TEST_CASE(lfpfunc, Negation); RUN_TEST_CASE(lfpfunc, Absolute); RUN_TEST_CASE(lfpfunc, FDF_RoundTrip); RUN_TEST_CASE(lfpfunc, SignedRelOps); RUN_TEST_CASE(lfpfunc, UnsignedRelOps); } ntpsec-1.1.0+dfsg1/tests/libntp/lfptest.h0000644000175000017500000000145413252364117020115 0ustar rlaagerrlaager#ifndef GUARD_NTP_TESTS_LFPTEST_H #define GUARD_NTP_TESTS_LFPTEST_H #include "ntp_fp.h" static bool IsEqual(const l_fp *expected, const l_fp *actual) { if (*expected == *actual) { return true; } printf("Expected: %s (%u.%u) but was: %s (%u.%u)\n", lfptoa(*expected, FRACTION_PREC), lfpuint(*expected), lfpfrac(*expected), lfptoa(*actual, FRACTION_PREC), lfpuint(*actual), lfpfrac(*actual)); return false; } static const uint32_t HALF = 2147483648U; // (1 << 31) static const uint32_t HALF_PROMILLE_UP = 2147484; // slightly more than 0.0005 static const uint32_t HALF_PROMILLE_DOWN = 2147483; // slightly less than 0.0005 static const uint32_t QUARTER = 1073741824U; // (1 << 30) static const uint32_t QUARTER_PROMILLE_APPRX = 1073742U; #endif /* GUARD_NTP_TESTS_LFPTEST_H */ ntpsec-1.1.0+dfsg1/tests/libntp/recvbuff.c0000644000175000017500000000210613252364117020224 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" #include "recvbuff.h" TEST_GROUP(recvbuff); TEST_SETUP(recvbuff) { init_recvbuff(RECV_INIT); } TEST_TEAR_DOWN(recvbuff) {} TEST(recvbuff, Initialization) { TEST_ASSERT_EQUAL(RECV_INIT, free_recvbuffs()); TEST_ASSERT_EQUAL(0, full_recvbuffs()); TEST_ASSERT_FALSE(has_full_recv_buffer()); TEST_ASSERT_NULL(get_full_recv_buffer()); } TEST(recvbuff, GetAndFree) { unsigned long initial = free_recvbuffs(); recvbuf_t* buf = get_free_recv_buffer(); TEST_ASSERT_EQUAL(initial-1, free_recvbuffs()); freerecvbuf(buf); TEST_ASSERT_EQUAL(initial, free_recvbuffs()); } TEST(recvbuff, GetAndFill) { recvbuf_t* buf = get_free_recv_buffer(); recvbuf_t* buf1; add_full_recv_buffer(buf); TEST_ASSERT_EQUAL(1, full_recvbuffs()); TEST_ASSERT_TRUE(has_full_recv_buffer()); buf1 = get_full_recv_buffer(); TEST_ASSERT_POINTERS_EQUAL(buf, buf1); } TEST_GROUP_RUNNER(recvbuff) { RUN_TEST_CASE(recvbuff, Initialization); RUN_TEST_CASE(recvbuff, GetAndFree); RUN_TEST_CASE(recvbuff, GetAndFill); } ntpsec-1.1.0+dfsg1/tests/libntp/macencrypt.c0000644000175000017500000000504213252364117020571 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(macencrypt); TEST_SETUP(macencrypt) {} TEST_TEAR_DOWN(macencrypt) {} #include #include "ntp.h" /* * Example packet with MD5 hash calculated manually. */ const int keytype = NID_md5; char key[] = "abcdefgh"; const unsigned short keyLength = 8; const char *packet = "ijklmnopqrstuvwx"; const int packetLength = 16; const int keyIdLength = 4; const int digestLength = 16; const int totalLength = 36; //error: initializer element is not constant packetLength + keyIdLength + digestLength; char expectedPacket[] = "ijklmnopqrstuvwx\0\0\0\0\x0c\x0e\x84\xcf\x0b\xb7\xa8\x68\x8e\x52\x38\xdb\xbc\x1c\x39\x53"; TEST(macencrypt, Encrypt) { char *packetPtr[totalLength]; memset(packetPtr+packetLength, 0, (size_t)keyIdLength); memcpy(packetPtr, packet, (size_t)packetLength); int length = mac_authencrypt(keytype, (unsigned char*)key, keyLength, (uint32_t*)packetPtr, packetLength); TEST_ASSERT_TRUE(mac_authdecrypt(keytype, (unsigned char*)key, keyLength, (uint32_t*)packetPtr, packetLength, length)); TEST_ASSERT_EQUAL(20, length); //XXX TEST_ASSERT_TRUE(memcmp(expectedPacket, packetPtr, totalLength) == 0); Does not pass } TEST(macencrypt, DecryptValid) { TEST_ASSERT_TRUE(mac_authdecrypt(keytype, (unsigned char*)key, keyLength, (uint32_t*)expectedPacket, packetLength, 20)); } TEST(macencrypt, DecryptInvalid) { char invalidPacket[] = "ijklmnopqrstuvwx\0\0\0\0\x0c\x0e\x84\xcf\x0b\xb7\xa8\x68\x8e\x52\x38\xdb\xbc\x1c\x39\x54"; TEST_ASSERT_FALSE(mac_authdecrypt(keytype, (unsigned char*)key, keyLength, (uint32_t*)invalidPacket, packetLength, 20)); } TEST(macencrypt, IPv4AddressToRefId) { sockaddr_u addr; SET_AF(&addr, AF_INET); SET_NSRCPORT(&addr, htons(80)); uint32_t address = inet_addr("192.0.2.1"); PSOCK_ADDR4(&addr)->s_addr = address; TEST_ASSERT_EQUAL(address, addr2refid(&addr)); } TEST(macencrypt, IPv6AddressToRefId) { const struct in6_addr address = {{{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x08, 0xd3, 0x13, 0x19, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }}}; sockaddr_u addr; SET_AF(&addr, AF_INET6); SET_SOCK_ADDR6(&addr, address); const unsigned int expected = htonl(0x52fdcf75); TEST_ASSERT_EQUAL(expected, addr2refid(&addr)); } TEST_GROUP_RUNNER(macencrypt) { RUN_TEST_CASE(macencrypt, Encrypt); RUN_TEST_CASE(macencrypt, DecryptValid); RUN_TEST_CASE(macencrypt, DecryptInvalid); RUN_TEST_CASE(macencrypt, IPv4AddressToRefId); RUN_TEST_CASE(macencrypt, IPv6AddressToRefId); } ntpsec-1.1.0+dfsg1/tests/libntp/authkeys.c0000644000175000017500000000317113252364117020262 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" #include #include "ntp.h" TEST_GROUP(authkeys); TEST_SETUP(authkeys) { /* * init_auth() is called by tests_main.cpp earlier. It * does not initialize global variables like * authnumkeys, so let's reset them to zero here. */ authnumkeys = 0; } TEST_TEAR_DOWN(authkeys) {} /* This file contains test for libntp/authkeys.c */ static const int KEYTYPE = NID_md5; static void AddTrustedKey(keyid_t); static void AddUntrustedKey(keyid_t); static void AddTrustedKey(keyid_t keyno) { /* * We need to add a MD5-key in addition to setting the * trust, because authhavekey() requires type != 0. */ mac_setkey(keyno, KEYTYPE, NULL, 0); authtrust(keyno, true); } static void AddUntrustedKey(keyid_t keyno) { authtrust(keyno, false); } TEST(authkeys, AddTrustedKeys) { const keyid_t KEYNO1 = 5; const keyid_t KEYNO2 = 8; AddTrustedKey(KEYNO1); AddTrustedKey(KEYNO2); TEST_ASSERT_TRUE(authistrusted(KEYNO1)); TEST_ASSERT_TRUE(authistrusted(KEYNO2)); } TEST(authkeys, AddUntrustedKey) { const keyid_t KEYNO = 3; AddUntrustedKey(KEYNO); TEST_ASSERT_FALSE(authistrusted(KEYNO)); } TEST(authkeys, HaveKeyCorrect) { const keyid_t KEYNO = 3; AddTrustedKey(KEYNO); TEST_ASSERT_TRUE(authhavekey(KEYNO)); } TEST(authkeys, HaveKeyIncorrect) { const keyid_t KEYNO = 2; TEST_ASSERT_FALSE(authhavekey(KEYNO)); } TEST_GROUP_RUNNER(authkeys) { RUN_TEST_CASE(authkeys, AddTrustedKeys); RUN_TEST_CASE(authkeys, AddUntrustedKey); RUN_TEST_CASE(authkeys, HaveKeyCorrect); RUN_TEST_CASE(authkeys, HaveKeyIncorrect); } ntpsec-1.1.0+dfsg1/tests/libntp/calendar.c0000644000175000017500000001732013252364117020177 0ustar rlaagerrlaager#include "config.h" #include "ntp.h" #include "ntp_stdlib.h" #include "parse.h" #include "unity.h" #include "unity_fixture.h" #include "caltime.h" TEST_GROUP(calendar); TEST_SETUP(calendar) {} TEST_TEAR_DOWN(calendar) {} #include "ntp_calendar.h" static const char *DateToString(char *, const struct calendar *); static const char *DateToString(char *str, const struct calendar *cal) { snprintf(str, 255, "%hu-%u-%u(%u)\n", cal->year, (unsigned int)cal->month, (unsigned int)cal->monthday, cal->yearday); return str; } static bool IsEqualDate(const struct calendar *expected, const struct calendar *actual) { char str[255]; char str1[255]; if (expected->year == actual->year && (!expected->yearday || expected->yearday == actual->yearday) && expected->month == actual->month && expected->monthday == actual->monthday) { return true; } else { printf("Expected: %s but was %s\n", DateToString(str, expected), DateToString(str1, actual)); return false; } } // --------------------------------------------------------------------- // test cases // --------------------------------------------------------------------- static const unsigned short real_month_table[2][13] = { /* -*- table for regular years -*- */ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, /* -*- table for leap years -*- */ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; // days in month, with one month wrap-around at both ends static const unsigned short real_month_days[2][14] = { /* -*- table for regular years -*- */ { 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 }, /* -*- table for leap years -*- */ { 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 } }; TEST(calendar, is_leapyear) { /* check is_leapyear() */ TEST_ASSERT_EQUAL(false, is_leapyear(1900)); TEST_ASSERT_EQUAL(false, is_leapyear(1970)); TEST_ASSERT_EQUAL(false, is_leapyear(1999)); TEST_ASSERT_EQUAL(true, is_leapyear(2000)); TEST_ASSERT_EQUAL(false, is_leapyear(2001)); TEST_ASSERT_EQUAL(true, is_leapyear(2004)); TEST_ASSERT_EQUAL(true, is_leapyear(2040)); } TEST(calendar, julian0) { /* check julian0() */ TEST_ASSERT_EQUAL(693961, julian0(1900)); TEST_ASSERT_EQUAL(719528, julian0(1970)); TEST_ASSERT_EQUAL(730120, julian0(1999)); TEST_ASSERT_EQUAL(730485, julian0(2000)); TEST_ASSERT_EQUAL(730851, julian0(2001)); TEST_ASSERT_EQUAL(745095, julian0(2040)); } TEST(calendar, days_per_year) { /* check is_leapyear() */ TEST_ASSERT_EQUAL(365, days_per_year(1900)); TEST_ASSERT_EQUAL(365, days_per_year(1970)); TEST_ASSERT_EQUAL(365, days_per_year(1999)); TEST_ASSERT_EQUAL(366, days_per_year(2000)); TEST_ASSERT_EQUAL(365, days_per_year(2001)); TEST_ASSERT_EQUAL(366, days_per_year(2004)); TEST_ASSERT_EQUAL(366, days_per_year(2040)); } #ifdef CLOCK_GENERIC TEST(calendar, parse_to_unixtime) { /* check is_leapyear() */ clocktime_t ct; time_t result; unsigned long Flag; ct.day = 1; ct.month = 1; ct.year = 1970; ct.hour = ct.minute = ct.second = ct.usecond = 0; ct.utcoffset = 0; ct.utctime = 0; ct.flags = 0; Flag = 0; result = parse_to_unixtime( &ct, &Flag ); TEST_ASSERT_EQUAL(0, result); ct.year = 2000; ct.hour = 2; ct.utctime = 0; result = parse_to_unixtime( &ct, &Flag ); TEST_ASSERT_EQUAL(946692000L, result); ct.year = 2037; ct.minute = 2; ct.second = 3; ct.utctime = 0; result = parse_to_unixtime( &ct, &Flag ); TEST_ASSERT_EQUAL(2114388123L, result); } #endif // test the day/sec join & split ops, making sure that 32bit // intermediate results would definitely overflow and the hi DWORD of // the 'time64_t' is definitely needed. TEST(calendar, DaySplitMerge) { int32_t day; int32_t sec; for (day = -1000000; day <= 1000000; day += 100) { for (sec = -100000; sec <= 186400; sec += 10000) { time64_t merge = ntpcal_dayjoin(day, sec); ntpcal_split split = ntpcal_daysplit(merge); int32_t eday = day; int32_t esec = sec; while (esec >= 86400) { eday += 1; esec -= 86400; } while (esec < 0) { eday -= 1; esec += 86400; } TEST_ASSERT_EQUAL(eday, split.hi); TEST_ASSERT_EQUAL(esec, split.lo); } } } TEST(calendar, SplitYearDays1) { int32_t eyd; for (eyd = -1; eyd <= 365; eyd++) { ntpcal_split split = ntpcal_split_yeardays(eyd, 0); if (split.lo >= 0 && split.hi >= 0) { TEST_ASSERT_LESS_THAN_INT32(12, split.hi); TEST_ASSERT_LESS_THAN_INT32(real_month_days[0][split.hi+1], split.lo); int32_t tyd = real_month_table[0][split.hi] + split.lo; TEST_ASSERT_EQUAL(eyd, tyd); } else TEST_ASSERT_TRUE(eyd < 0 || eyd > 364); } } TEST(calendar, SplitYearDays2) { int32_t eyd; for (eyd = -1; eyd <= 366; eyd++) { ntpcal_split split = ntpcal_split_yeardays(eyd, 1); if (split.lo >= 0 && split.hi >= 0) { TEST_ASSERT_LESS_THAN_INT32(12, split.hi); TEST_ASSERT_LESS_THAN_INT32(real_month_days[1][split.hi+1], split.lo); int32_t tyd = real_month_table[1][split.hi] + split.lo; TEST_ASSERT_EQUAL(eyd, tyd); } else TEST_ASSERT_TRUE(eyd < 0 || eyd > 365); } } TEST(calendar, RataDie1) { int32_t testDate = 1; // 0001-01-01 (proleptic date) struct calendar expected = { 1, 1, 1, 1, 0, 0, 0, 0}; struct calendar actual; ntpcal_rd_to_date(&actual, testDate); TEST_ASSERT_TRUE(IsEqualDate(&expected, &actual)); } // check last day of february for first 10000 years TEST(calendar, LeapYears1) { struct calendar dateIn, dateOut; for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) { dateIn.month = 2; dateIn.monthday = is_leapyear(dateIn.year) ? 29 : 28; dateIn.yearday = 31 + dateIn.monthday; ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn)); TEST_ASSERT_TRUE(IsEqualDate(&dateIn, &dateOut)); } } // check first day of march for first 10000 years TEST(calendar, LeapYears2) { struct calendar dateIn, dateOut; for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) { dateIn.month = 3; dateIn.monthday = 1; dateIn.yearday = is_leapyear(dateIn.year) ? 61 : 60; ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn)); TEST_ASSERT_TRUE(IsEqualDate(&dateIn, &dateOut)); } } // Full roundtrip for 1601-01-01 to 2400-12-31 // checks sequence of rata die numbers and validates date output // (since the input is all nominal days of the calendar in that range // and the result of the inverse calculation must match the input no // invalid output can occur.) TEST(calendar, RoundTripDate) { struct calendar truDate, expDate = { 1600, 0, 12, 31, 0, 0, 0, 0}; int32_t truRdn, expRdn = ntpcal_date_to_rd(&expDate); int leaps; while (expDate.year < 2400) { expDate.year++; expDate.month = 0; expDate.yearday = 0; leaps = is_leapyear(expDate.year) ? 1 : 0; while (expDate.month < 12) { expDate.month++; expDate.monthday = 0; while (expDate.monthday < real_month_days[leaps][expDate.month]) { expDate.monthday++; expDate.yearday++; expRdn++; truRdn = ntpcal_date_to_rd(&expDate); TEST_ASSERT_EQUAL(expRdn, truRdn); ntpcal_rd_to_date(&truDate, truRdn); TEST_ASSERT_TRUE(IsEqualDate(&expDate, &truDate)); } } } } TEST_GROUP_RUNNER(calendar) { RUN_TEST_CASE(calendar, is_leapyear); RUN_TEST_CASE(calendar, julian0); RUN_TEST_CASE(calendar, days_per_year); #ifdef CLOCK_GENERIC RUN_TEST_CASE(calendar, parse_to_unixtime); #endif RUN_TEST_CASE(calendar, DaySplitMerge); RUN_TEST_CASE(calendar, SplitYearDays1); RUN_TEST_CASE(calendar, SplitYearDays2); RUN_TEST_CASE(calendar, RataDie1); RUN_TEST_CASE(calendar, LeapYears1); RUN_TEST_CASE(calendar, LeapYears2); RUN_TEST_CASE(calendar, RoundTripDate); } ntpsec-1.1.0+dfsg1/tests/libntp/prettydate.c0000644000175000017500000000115013252364117020605 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "ntp_fp.h" #include "caltime.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(prettydate); TEST_SETUP(prettydate) { set_prettydate_pivot(settime(2000, 1, 1, 0, 0, 0)); } TEST_TEAR_DOWN(prettydate) {} #include "ntp_fp.h" static const uint32_t HALF = 2147483648UL; TEST(prettydate, ConstantDate) { l_fp t = lfpinit((int32_t)3485080800LL, HALF); // 2010-06-09 14:00:00.5 TEST_ASSERT_EQUAL_STRING("cfba1ce0.80000000 2010-06-09T14:00:00.500Z", prettydate(t)); } TEST_GROUP_RUNNER(prettydate) { RUN_TEST_CASE(prettydate, ConstantDate); } ntpsec-1.1.0+dfsg1/tests/libntp/socktoa.c0000644000175000017500000000577113252364117020100 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(socktoa); TEST_SETUP(socktoa) {} TEST_TEAR_DOWN(socktoa) {} #include "sockaddrtest.h" TEST(socktoa, IPv4AddressWithPort) { sockaddr_u input = CreateSockaddr4("192.0.2.10", 123); TEST_ASSERT_EQUAL_STRING("192.0.2.10", socktoa(&input)); TEST_ASSERT_EQUAL_STRING("192.0.2.10:123", sockporttoa(&input)); } TEST(socktoa, IPv6AddressWithPort) { const struct in6_addr address = {{{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x08, 0xd3, 0x13, 0x19, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }}}; const char* expected = "2001:db8:85a3:8d3:1319:8a2e:370:7334"; const char* expected_port = "[2001:db8:85a3:8d3:1319:8a2e:370:7334]:123"; sockaddr_u input; memset(&input, 0, sizeof(input)); AF(&input) = AF_INET6; SET_ADDR6N(&input, address); SET_PORT(&input, 123); TEST_ASSERT_EQUAL_STRING(expected, socktoa(&input)); TEST_ASSERT_EQUAL_STRING(expected_port, sockporttoa(&input)); } TEST(socktoa, ScopedIPv6AddressWithPort) { const struct in6_addr address = {{{ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x12, 0x3f, 0xff, 0xfe, 0x29, 0xff, 0xfa }}}; const char* expected = "fe80::212:3fff:fe29:fffa%5"; const char* expected_port = "[fe80::212:3fff:fe29:fffa%5]:123"; sockaddr_u input; memset(&input, 0, sizeof(input)); AF(&input) = AF_INET6; SET_ADDR6N(&input, address); SET_PORT(&input, 123); SCOPE_VAR(&input) = 5; TEST_ASSERT_EQUAL_STRING(expected, socktoa(&input)); TEST_ASSERT_EQUAL_STRING(expected_port, sockporttoa(&input)); } TEST(socktoa, HashEqual) { sockaddr_u input1 = CreateSockaddr4("192.00.2.2", 123); sockaddr_u input2 = CreateSockaddr4("192.0.2.2", 123); TEST_ASSERT_TRUE(IsEqualS(&input1, &input2)); TEST_ASSERT_EQUAL(sock_hash(&input1), sock_hash(&input2)); } TEST(socktoa, HashNotEqual) { /* These two addresses should not generate the same hash. */ sockaddr_u input1 = CreateSockaddr4("192.0.2.1", 123); sockaddr_u input2 = CreateSockaddr4("192.0.2.2", 123); TEST_ASSERT_TRUE(IsDiffS(&input1, &input2)); TEST_ASSERT_NOT_EQUAL(sock_hash(&input1), sock_hash(&input2)); } TEST(socktoa, IgnoreIPv6Fields) { const struct in6_addr address = {{{ 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x08, 0xd3, 0x13, 0x19, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34 }}}; sockaddr_u input1, input2; SET_AF(&input1, AF_INET6); SET_SOCK_ADDR6(&input1, address); input1.sa6.sin6_flowinfo = 30L; // This value differs from input2. SET_PORT(&input1, NTP_PORT); SET_AF(&input2, AF_INET6); SET_SOCK_ADDR6(&input2, address); input2.sa6.sin6_flowinfo = 10L; // This value differs from input1. SET_PORT(&input2, NTP_PORT); TEST_ASSERT_EQUAL(sock_hash(&input1), sock_hash(&input2)); } TEST_GROUP_RUNNER(socktoa) { RUN_TEST_CASE(socktoa, IPv4AddressWithPort); RUN_TEST_CASE(socktoa, IPv6AddressWithPort); RUN_TEST_CASE(socktoa, ScopedIPv6AddressWithPort); RUN_TEST_CASE(socktoa, HashEqual); RUN_TEST_CASE(socktoa, HashNotEqual); RUN_TEST_CASE(socktoa, IgnoreIPv6Fields); } ntpsec-1.1.0+dfsg1/tests/libntp/numtoa.c0000644000175000017500000000116313252364117017727 0ustar rlaagerrlaager#include "config.h" #include "ntp_stdlib.h" #include "unity.h" #include "unity_fixture.h" TEST_GROUP(numtoa); TEST_SETUP(numtoa) {} TEST_TEAR_DOWN(numtoa) {} TEST(numtoa, Address) { uint32_t input = htonl(3221225472UL+512UL+1UL); // 192.0.2.1 TEST_ASSERT_EQUAL_STRING("192.0.2.1", numtoa(input)); } TEST(numtoa, Netmask) { // 255.255.255.0 uint32_t hostOrder = 255UL*256UL*256UL*256UL + 255UL*256UL*256UL + 255UL*256UL; uint32_t input = htonl(hostOrder); TEST_ASSERT_EQUAL_STRING("255.255.255.0", numtoa(input)); } TEST_GROUP_RUNNER(numtoa) { RUN_TEST_CASE(numtoa, Address); RUN_TEST_CASE(numtoa, Netmask); } ntpsec-1.1.0+dfsg1/tests/option-tester.sh0000755000175000017500000000627713252364117020156 0ustar rlaagerrlaager#!/usr/bin/env bash # sh on NetBSD and FreeBSD says: # sh: ${PIPESTATUS[...}: Bad substitution # This is a hack to build with various configuration options. # The intent is to check building combinations that normal testing doesn't use. # Stuff goes into various test-* directories. # Running again starts by deleting everything in the directory. LINUX="" if [ `uname -s` = "Linux" -a -f /usr/include/seccomp.h ] then # Not supported on CentOS 6 LINUX="--enable-seccomp" fi doit () { DIR=test-$1 [ ! -d $DIR ] && mkdir $DIR rm -rf $DIR/* ./waf configure --out=$DIR $2 |& tee $DIR/test.log WAF1=${PIPESTATUS[0]} WAF2=0 WAF3=0 if [ "$WAF1" = 0 ] then echo |& tee -a $DIR/test.log ./waf build |& tee -a $DIR/test.log WAF2=${PIPESTATUS[0]} if [ "$WAF2" = 0 ] then echo |& tee -a $DIR/test.log ./waf check |& tee -a $DIR/test.log WAF3=${PIPESTATUS[0]} fi fi if [ "$WAF1" != 0 -o "$WAF2" != 0 -o "$WAF3" != 0 ] then echo |& tee -a $DIR/test.log echo "Trouble with $DIR" |& tee -a $DIR/test.log else echo -n "VERSION: " |& tee -a $DIR/test.log ./$DIR/main/ntpd/ntpd --version |& tee -a $DIR/test.log echo -n "VERSION: " |& tee -a $DIR/test.log ./$DIR/main/ntpclients/ntpq --version |& tee -a $DIR/test.log echo -n "VERSION: " |& tee -a $DIR/test.log ./$DIR/main/ntpclients/ntpdig --version |& tee -a $DIR/test.log echo -n "VERSION: " |& tee -a $DIR/test.log ./$DIR/main/ntpclients/ntpmon --version |& tee -a $DIR/test.log if [ "`which gpsmon 2>/dev/null`" != "" ] then # needs GPSD library echo -n "VERSION: " |& tee -a $DIR/test.log ./$DIR/main/ntpclients/ntploggps --version |& tee -a $DIR/test.log fi echo -n "VERSION: " |& tee -a $DIR/test.log ./$DIR/main/ntpclients/ntplogtemp --version |& tee -a $DIR/test.log fi echo echo } # no --disable-manpage on default and all doit default "" doit minimal "--disable-droproot --disable-dns-lookup --disable-mdns-registration --disable-manpage" # This also tests refclocks without DEBUG doit classic "--enable-classic-mode --refclock=all --disable-manpage" doit all "--enable-debug --enable-debug-gdb --enable-debug-timing --refclock=all --enable-lockclock --enable-leap-smear --enable-mssntp --enable-early-droproot $LINUX" if [ "`which asciidoc 2>/dev/null`" != "" -a \ "`which xsltproc 2>/dev/null`" != "" ] then doit doc "--enable-doc --disable-manpage" fi # should try cross compile echo if [ `uname -s` = "Linux" -a ! -f /usr/include/seccomp.h ] then echo echo "### Warning: Missing seccomp.h (on a Linux system)" echo fi echo "PYTHONPATH is" \"$PYTHONPATH\" grep VERSION: test*/test.log echo grep warning: test*/test.log grep error: test*/test.log grep "The configuration failed" test*/test.log grep ^Trouble test*/test.log echo ntpsec-1.1.0+dfsg1/tests/ntpd/0000775000175000017500000000000013252650651015737 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/tests/ntpd/leapsec.c0000644000175000017500000007305313252364117017524 0ustar rlaagerrlaager#include "config.h" #include "unity.h" #include "unity_fixture.h" #include "caltime.h" #include "ntp.h" #include "ntpd.h" #include "ntp_calendar.h" #include "ntp_leapsec.h" TEST_GROUP(leapsec); static time_t fixedpivot; TEST_SETUP(leapsec) { fixedpivot = settime(1970, 1, 1, 0, 0, 0); leapsec_ut_pristine(); } TEST_TEAR_DOWN(leapsec) { } static const char leap1 [] = "#\n" "#@ 3610569600\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" " \t \n" "2571782400 20 # 1 Jul 1981\n" "2603318400 21 # 1 Jul 1982\n" "2634854400 22 # 1 Jul 1983\n" "2698012800 23 # 1 Jul 1985\n" "2776982400 24 # 1 Jan 1988\n" "2840140800 25 # 1 Jan 1990\n" "2871676800 26 # 1 Jan 1991\n" "2918937600 27 # 1 Jul 1992\n" "2950473600 28 # 1 Jul 1993\n" "2982009600 29 # 1 Jul 1994\n" "3029443200 30 # 1 Jan 1996\n" "3076704000 31 # 1 Jul 1997\n" "3124137600 32 # 1 Jan 1999\n" "3345062400 33 # 1 Jan 2006\n" "3439756800 34 # 1 Jan 2009\n" "3550089600 35 # 1 Jul 2012\n" "#\n" "#h dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n" "#\n"; static const char leap2 [] = "#\n" "#@ 2950473700\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" "2571782400 20 # 1 Jul 1981\n" "2603318400 21 # 1 Jul 1982\n" "2634854400 22 # 1 Jul 1983\n" "2698012800 23 # 1 Jul 1985\n" "2776982400 24 # 1 Jan 1988\n" "2840140800 25 # 1 Jan 1990\n" "2871676800 26 # 1 Jan 1991\n" "2918937600 27 # 1 Jul 1992\n" "2950473600 28 # 1 Jul 1993\n" "#\n"; // Faked table with a leap second removal at 2009 static const char leap3 [] = "#\n" "#@ 3610569600\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" "2571782400 20 # 1 Jul 1981\n" "2603318400 21 # 1 Jul 1982\n" "2634854400 22 # 1 Jul 1983\n" "2698012800 23 # 1 Jul 1985\n" "2776982400 24 # 1 Jan 1988\n" "2840140800 25 # 1 Jan 1990\n" "2871676800 26 # 1 Jan 1991\n" "2918937600 27 # 1 Jul 1992\n" "2950473600 28 # 1 Jul 1993\n" "2982009600 29 # 1 Jul 1994\n" "3029443200 30 # 1 Jan 1996\n" "3076704000 31 # 1 Jul 1997\n" "3124137600 32 # 1 Jan 1999\n" "3345062400 33 # 1 Jan 2006\n" "3439756800 32 # 1 Jan 2009\n" "3550089600 33 # 1 Jul 2012\n" "#\n"; // short table with good hash static const char leap_ghash [] = "#\n" "#@ 3610569600\n" "#$ 3610566000\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" "#\n" "#h 4b304e10 95642b3f c10b91f9 90791725 25f280d0\n" "#\n"; // short table with bad hash static const char leap_bhash [] = "#\n" "#@ 3610569600\n" "#$ 3610566000\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" "#\n" "#h dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n" "#\n"; // short table with malformed hash static const char leap_mhash [] = "#\n" "#@ 3610569600\n" "#$ 3610566000\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" "#\n" "#h f2349a02 788b9534 a8f2e141 f2029Q6d 4064a7ee\n" "#\n"; // short table with only 4 hash groups static const char leap_shash [] = "#\n" "#@ 3610569600\n" "#$ 3610566000\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" "#\n" "#h f2349a02 788b9534 a8f2e141 f2029Q6d\n" "#\n"; // table with good hash and truncated/missing leading zeros static const char leap_gthash [] = { "#\n" "#$ 3535228800\n" "#\n" "# Updated through IERS Bulletin C46\n" "# File expires on: 28 June 2014\n" "#\n" "#@ 3612902400\n" "#\n" "2272060800 10 # 1 Jan 1972\n" "2287785600 11 # 1 Jul 1972\n" "2303683200 12 # 1 Jan 1973\n" "2335219200 13 # 1 Jan 1974\n" "2366755200 14 # 1 Jan 1975\n" "2398291200 15 # 1 Jan 1976\n" "2429913600 16 # 1 Jan 1977\n" "2461449600 17 # 1 Jan 1978\n" "2492985600 18 # 1 Jan 1979\n" "2524521600 19 # 1 Jan 1980\n" "2571782400 20 # 1 Jul 1981\n" "2603318400 21 # 1 Jul 1982\n" "2634854400 22 # 1 Jul 1983\n" "2698012800 23 # 1 Jul 1985\n" "2776982400 24 # 1 Jan 1988\n" "2840140800 25 # 1 Jan 1990\n" "2871676800 26 # 1 Jan 1991\n" "2918937600 27 # 1 Jul 1992\n" "2950473600 28 # 1 Jul 1993\n" "2982009600 29 # 1 Jul 1994\n" "3029443200 30 # 1 Jan 1996\n" "3076704000 31 # 1 Jul 1997\n" "3124137600 32 # 1 Jan 1999\n" "3345062400 33 # 1 Jan 2006\n" "3439756800 34 # 1 Jan 2009\n" "3550089600 35 # 1 Jul 2012\n" "#\n" "#h 1151a8f e85a5069 9000fcdb 3d5e5365 1d505b37" }; static time_t lsec2009 = 3439756800u - JAN_1970; // 1 Jan 2009, 00:00:00 utc static time_t lsec2012 = 3550089600u - JAN_1970; // 1 Jul 2012, 00:00:00 utc static int stringreader(void* farg) { const char ** cpp = (const char**)farg; if (**cpp) return *(*cpp)++; else return EOF; } static bool setup_load_table( const char * cp) { bool rc; leap_table_t * pt = leapsec_get_table(0); rc = (pt != NULL) && leapsec_load(pt, stringreader, &cp); rc = rc && leapsec_set_table(pt); return rc; } static bool setup_clear_table(void) { bool rc; leap_table_t * pt = leapsec_get_table(0); if (pt) leapsec_clear(pt); rc = leapsec_set_table(pt); return rc; } /* std::string CalendarToString(const calendar &cal) { std::ostringstream ss; ss << cal.year << "-" << (unsigned int)cal.month << "-" << (unsigned int)cal.monthday << " (" << cal.yearday << ") " << (unsigned int)cal.hour << ":" << (unsigned int)cal.minute << ":" << (unsigned int)cal.second; return ss.str(); } bool IsEqual(const calendar &expected, const calendar &actual) { if (expected.year == actual.year && (expected.yearday == actual.yearday || (expected.month == actual.month && expected.monthday == actual.monthday)) && expected.hour == actual.hour && expected.minute == actual.minute && expected.second == actual.second) { return true; } else { return false << "expected: " << CalendarToString(expected) << " but was " << CalendarToString(actual); } } */ // ===================================================================== // VALIDATION TESTS // ===================================================================== // ---------------------------------------------------------------------- TEST(leapsec, ValidateGood) { const char *cp = leap_ghash; int rc = leapsec_validate(stringreader, &cp); TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc); } // ---------------------------------------------------------------------- TEST(leapsec, ValidateNoHash) { const char *cp = leap2; int rc = leapsec_validate(stringreader, &cp); TEST_ASSERT_EQUAL(LSVALID_NOHASH, rc); } // ---------------------------------------------------------------------- TEST(leapsec, ValidateBad) { const char *cp = leap_bhash; int rc = leapsec_validate(stringreader, &cp); TEST_ASSERT_EQUAL(LSVALID_BADHASH, rc); } // ---------------------------------------------------------------------- TEST(leapsec, ValidateMalformed) { const char *cp = leap_mhash; int rc = leapsec_validate(stringreader, &cp); TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc); } // ---------------------------------------------------------------------- TEST(leapsec, ValidateMalformedShort) { const char *cp = leap_shash; int rc = leapsec_validate(stringreader, &cp); TEST_ASSERT_EQUAL(LSVALID_BADFORMAT, rc); } // ---------------------------------------------------------------------- TEST(leapsec, ValidateNoLeadZero) { const char *cp = leap_gthash; int rc = leapsec_validate(stringreader, &cp); TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc); } // ===================================================================== // BASIC FUNCTIONS // ===================================================================== // ---------------------------------------------------------------------- // test table selection TEST(leapsec, tableSelect) { leap_table_t *pt1, *pt2, *pt3; pt1 = leapsec_get_table(0); pt2 = leapsec_get_table(0); TEST_ASSERT_POINTERS_EQUAL(pt1, pt2); pt1 = leapsec_get_table(1); pt2 = leapsec_get_table(1); TEST_ASSERT_POINTERS_EQUAL(pt1, pt2); pt1 = leapsec_get_table(1); pt2 = leapsec_get_table(0); TEST_ASSERT_NOT_EQUAL(pt1, pt2); pt1 = leapsec_get_table(0); pt2 = leapsec_get_table(1); TEST_ASSERT_NOT_EQUAL(pt1, pt2); leapsec_set_table(pt1); pt2 = leapsec_get_table(0); pt3 = leapsec_get_table(1); TEST_ASSERT_POINTERS_EQUAL(pt1, pt2); TEST_ASSERT_NOT_EQUAL(pt2, pt3); pt1 = pt3; leapsec_set_table(pt1); pt2 = leapsec_get_table(0); pt3 = leapsec_get_table(1); TEST_ASSERT_POINTERS_EQUAL(pt1, pt2); TEST_ASSERT_NOT_EQUAL(pt2, pt3); } // ---------------------------------------------------------------------- // load file & check expiration TEST(leapsec, loadFileExpire) { const char *cp = leap1; int rc; leap_table_t * pt = leapsec_get_table(0); rc = leapsec_load(pt, stringreader, &cp) && leapsec_set_table(pt); TEST_ASSERT_EQUAL(1, rc); rc = leapsec_expired(3439756800u - JAN_1970); TEST_ASSERT_EQUAL(0, rc); rc = leapsec_expired(3610569601u - JAN_1970); TEST_ASSERT_EQUAL(1, rc); } // ---------------------------------------------------------------------- // load file & check time-to-live TEST(leapsec, loadFileTTL) { const char *cp = leap1; int rc; leap_table_t * pt = leapsec_get_table(0); const time_t limit = 3610569600u - JAN_1970; rc = leapsec_load(pt, stringreader, &cp) && leapsec_set_table(pt); TEST_ASSERT_EQUAL(1, rc); // exactly 1 day to live rc = leapsec_daystolive(limit - 86400); TEST_ASSERT_EQUAL( 1, rc); // less than 1 day to live rc = leapsec_daystolive(limit - 86399); TEST_ASSERT_EQUAL( 0, rc); // hit expiration exactly rc = leapsec_daystolive(limit); TEST_ASSERT_EQUAL( 0, rc); // expired since 1 sec rc = leapsec_daystolive(limit + 1); TEST_ASSERT_EQUAL(-1, rc); } // ---------------------------------------------------------------------- // test query in pristine state (bug#2745 misbehaviour) TEST(leapsec, lsQueryPristineState) { int rc; leap_result_t qr; leap_table_t * pt = leapsec_get_table(0); leapsec_dump(pt, (leapsec_dumper)fprintf, stdout); rc = leapsec_query(&qr, lsec2012); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // ad-hoc jump: leap second at 2009.01.01 -60days TEST(leapsec, ls2009faraway) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_EQUAL(1, rc); // test 60 days before leap. Nothing scheduled or indicated. rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(33, qr.tai_offs); TEST_ASSERT_EQUAL(0, qr.tai_diff); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // ad-hoc jump: leap second at 2009.01.01 -1week TEST(leapsec, ls2009weekaway) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_EQUAL(1, rc); // test 7 days before leap. Leap scheduled, but not yet indicated. rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(33, qr.tai_offs); TEST_ASSERT_EQUAL(1, qr.tai_diff); TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity); } // ---------------------------------------------------------------------- // ad-hoc jump: leap second at 2009.01.01 -1hr TEST(leapsec, ls2009houraway) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_EQUAL(1, rc); // test 1 hour before leap. 61 true seconds to go. rc = leapsec_query(&qr, lsec2009 - SECSPERHR); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(33, qr.tai_offs); TEST_ASSERT_EQUAL(1, qr.tai_diff); TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity); } // ---------------------------------------------------------------------- // ad-hoc jump: leap second at 2009.01.01 -1sec TEST(leapsec, ls2009secaway) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_TRUE(rc); // test 1 second before leap (last boundary...) 2 true seconds to go. rc = leapsec_query(&qr, lsec2009 - 1); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(33, qr.tai_offs); TEST_ASSERT_EQUAL(1, qr.tai_diff); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); } // ---------------------------------------------------------------------- // ad-hoc jump to leap second at 2009.01.01 TEST(leapsec, ls2009onspot) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_TRUE(rc); // test on-spot: treat leap second as already gone. rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(34, qr.tai_offs); TEST_ASSERT_EQUAL(0, qr.tai_diff); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // test handling of the leap second at 2009.01.01 without table TEST(leapsec, ls2009nodata) { bool rc; leap_result_t qr; rc = setup_clear_table(); TEST_ASSERT_TRUE(rc); // test on-spot with empty table rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.tai_offs); TEST_ASSERT_EQUAL(0, qr.tai_diff); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // test handling of the leap second at 2009.01.01 with culled data TEST(leapsec, ls2009limdata) { leap_table_t * pt; bool rc; leap_result_t qr; rc = setup_load_table(leap1); pt = leapsec_get_table(0); leapsec_dump(pt, (leapsec_dumper)fprintf, stdout); // FIXME // This used to check against build date // That updated the header but didn't add slots. // So the last slot was the only answer it could return. TEST_ASSERT_TRUE(rc); // test on-spot with limited table rc = leapsec_query(&qr, lsec2009+10); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(34, qr.tai_offs); // TEST_ASSERT_EQUAL(35, qr.tai_offs); TEST_ASSERT_EQUAL(0, qr.tai_diff); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // add dynamic leap second (like from peer/clock) TEST(leapsec, addDynamic) { leap_table_t * pt; bool rc; int idx; /* Explicit (time_t) cast avoids warnings * 32 bit FreeBSD 11.1, Nov 2017 * FreeBSD clang version 4.0.0 ../../tests/ntpd/leapsec.c:582:3: warning: implicit conversion from 'long long' to 'time_t' (aka 'int') changes value from 2982009600 to -1312957696 [-Wconstant-conversion] 2982009600, // 29 # 1 Jul 1994 ^~~~~~~~~~ */ static const time_t insns[] = { (time_t)2982009600, // 29 # 1 Jul 1994 (time_t)3029443200, // 30 # 1 Jan 1996 (time_t)3076704000, // 31 # 1 Jul 1997 (time_t)3124137600, // 32 # 1 Jan 1999 (time_t)3345062400, // 33 # 1 Jan 2006 (time_t)3439756800, // 34 # 1 Jan 2009 (time_t)3550089600, // 35 # 1 Jul 2012 0 // sentinel }; rc = setup_load_table(leap2); TEST_ASSERT_TRUE(rc); for (idx=1; insns[idx]; ++idx) { rc = leapsec_add_dyn(true, insns[idx] - (time_t)JAN_1970 - 20*SECSPERDAY - 100); TEST_ASSERT_TRUE(rc); } // try to slip in a previous entry rc = leapsec_add_dyn(true, insns[0] - (time_t)JAN_1970 - 20*SECSPERDAY - 100); TEST_ASSERT_FALSE(rc); //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout); pt = leapsec_get_table(0); leapsec_dump(pt, (leapsec_dumper)fprintf, stdout); } // ---------------------------------------------------------------------- // add fixed leap seconds (like from network packet) TEST(leapsec, addFixed) { bool rc; int idx; static const struct { time_t tt; int of; } insns[] = { {2982009600-JAN_1970, 29},// # 1 Jul 1994 {3029443200-JAN_1970, 30},// # 1 Jan 1996 {3076704000-JAN_1970, 31},// # 1 Jul 1997 {3124137600-JAN_1970, 32},// # 1 Jan 1999 {3345062400-JAN_1970, 33},// # 1 Jan 2006 {3439756800-JAN_1970, 34},// # 1 Jan 2009 {3550089600-JAN_1970, 35},// # 1 Jul 2012 {0,0} // sentinel }; rc = setup_load_table(leap2); TEST_ASSERT_TRUE(rc); // try to get in BAD time stamps... for (idx=0; insns[idx].tt; ++idx) { rc = leapsec_add_fix( insns[idx].of, insns[idx].tt - 20*SECSPERDAY - 100, insns[idx].tt + SECSPERDAY); #ifdef ENABLE_LEAP_TESTING TEST_ASSERT_TRUE(rc); #else TEST_ASSERT_FALSE(rc); #endif } // no do it right for (idx=0; insns[idx].tt; ++idx) { rc = leapsec_add_fix( insns[idx].of, insns[idx].tt, insns[idx].tt + SECSPERDAY); #ifdef ENABLE_LEAP_TESTING TEST_ASSERT_FALSE(rc); #else TEST_ASSERT_TRUE(rc); #endif } // try to slip in a previous entry rc = leapsec_add_fix( insns[0].of, insns[0].tt, insns[0].tt + SECSPERDAY); TEST_ASSERT_FALSE(rc); //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout); } // ===================================================================== // SEQUENCE TESTS // ===================================================================== // ---------------------------------------------------------------------- // leap second insert at 2009.01.01, electric mode TEST(leapsec, ls2009seqInsElectric) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_TRUE(rc); leapsec_electric(1); TEST_ASSERT_TRUE(leapsec_electric(-1)); rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - SECSPERHR); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 1); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_TRUE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); // second call, same time frame: no trigger! rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // leap second insert at 2009.01.01, dumb mode TEST(leapsec, ls2009seqInsDumb) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_TRUE(rc); TEST_ASSERT_FALSE(leapsec_electric(-1)); rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - SECSPERHR); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 1); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); rc = leapsec_query(&qr, lsec2009+1); TEST_ASSERT_TRUE(rc) TEST_ASSERT_EQUAL(-1, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); // second call, same time frame: no trigger! rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // fake leap second remove at 2009.01.01, electric mode TEST(leapsec, ls2009seqDelElectric) { bool rc; leap_result_t qr; rc = setup_load_table(leap3); TEST_ASSERT_TRUE(rc); leapsec_electric(1); TEST_ASSERT_TRUE(leapsec_electric(-1)); rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - SECSPERHR); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 1); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_TRUE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); // second call, same time frame: no trigger! rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // fake leap second remove at 2009.01.01. dumb mode TEST(leapsec, ls2009seqDelDumb) { bool rc; leap_result_t qr; rc = setup_load_table(leap3); TEST_ASSERT_TRUE(rc); TEST_ASSERT_FALSE(leapsec_electric(-1)); rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - SECSPERHR); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 2); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); rc = leapsec_query(&qr, lsec2009 - 1); TEST_ASSERT_TRUE(rc); TEST_ASSERT_EQUAL(1, qr.warped); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); // second call, same time frame: no trigger! rc = leapsec_query(&qr, lsec2009); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // leap second insert at 2012.07.01, electric mode TEST(leapsec, ls2012seqInsElectric) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_TRUE(rc); leapsec_electric(1); TEST_ASSERT_TRUE(leapsec_electric(-1)); rc = leapsec_query(&qr, lsec2012 - 60*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); rc = leapsec_query(&qr, lsec2012 - 7*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity); rc = leapsec_query(&qr, lsec2012 - SECSPERHR); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity); rc = leapsec_query(&qr, lsec2012 - 1); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); rc = leapsec_query(&qr, lsec2012); TEST_ASSERT_TRUE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); // second call, same time frame: no trigger! rc = leapsec_query(&qr, lsec2012); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // leap second insert at 2012.07.01, dumb mode TEST(leapsec, ls2012seqInsDumb) { bool rc; leap_result_t qr; rc = setup_load_table(leap1); TEST_ASSERT_TRUE(rc); TEST_ASSERT_FALSE(leapsec_electric(-1)); rc = leapsec_query(&qr, lsec2012 - 60*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); rc = leapsec_query(&qr, lsec2012 - 7*SECSPERDAY); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_SCHEDULE, qr.proximity); rc = leapsec_query(&qr, lsec2012 - SECSPERHR); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ANNOUNCE, qr.proximity); rc = leapsec_query(&qr, lsec2012 - 1); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); // This is just 1 sec before transition! rc = leapsec_query(&qr, lsec2012); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_ALERT, qr.proximity); // NOW the insert/backwarp must happen rc = leapsec_query(&qr, lsec2012+1); TEST_ASSERT_TRUE(rc); TEST_ASSERT_EQUAL(-1, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); // second call with transition time: no trigger! rc = leapsec_query(&qr, lsec2012); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } // ---------------------------------------------------------------------- // test repeated query on empty table in dumb mode TEST(leapsec, lsEmptyTableDumb) { bool rc; leap_result_t qr; time_t t; const time_t t0 = lsec2012 - 10; const time_t tE = lsec2012 + 10; TEST_ASSERT_FALSE(leapsec_electric(-1)); for (t = t0; t != tE; ++t) { rc = leapsec_query(&qr, t); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } } // ---------------------------------------------------------------------- // test repeated query on empty table in electric mode TEST(leapsec, lsEmptyTableElectric) { bool rc; leap_result_t qr; time_t t; leapsec_electric(electric_on); TEST_ASSERT(electric_on == leapsec_electric(electric_query)); const time_t t0 = lsec2012 - 10; const time_t tE = lsec2012 + 10; for (t = t0; t != tE; ++t) { rc = leapsec_query(&qr, t); TEST_ASSERT_FALSE(rc); TEST_ASSERT_EQUAL(0, qr.warped ); TEST_ASSERT_EQUAL(LSPROX_NOWARN, qr.proximity); } } TEST_GROUP_RUNNER(leapsec) { RUN_TEST_CASE(leapsec, ValidateGood); RUN_TEST_CASE(leapsec, ValidateNoHash); RUN_TEST_CASE(leapsec, ValidateBad); RUN_TEST_CASE(leapsec, ValidateMalformed); RUN_TEST_CASE(leapsec, ValidateMalformedShort); RUN_TEST_CASE(leapsec, ValidateNoLeadZero); RUN_TEST_CASE(leapsec, tableSelect); RUN_TEST_CASE(leapsec, loadFileExpire); RUN_TEST_CASE(leapsec, loadFileTTL); RUN_TEST_CASE(leapsec, lsQueryPristineState); RUN_TEST_CASE(leapsec, ls2009faraway); RUN_TEST_CASE(leapsec, ls2009weekaway); RUN_TEST_CASE(leapsec, ls2009houraway); RUN_TEST_CASE(leapsec, ls2009secaway); RUN_TEST_CASE(leapsec, ls2009onspot); RUN_TEST_CASE(leapsec, ls2009nodata); RUN_TEST_CASE(leapsec, ls2009limdata); RUN_TEST_CASE(leapsec, addDynamic); RUN_TEST_CASE(leapsec, addFixed); RUN_TEST_CASE(leapsec, ls2009seqInsElectric); RUN_TEST_CASE(leapsec, ls2009seqInsDumb); RUN_TEST_CASE(leapsec, ls2009seqDelElectric); RUN_TEST_CASE(leapsec, ls2009seqDelDumb); RUN_TEST_CASE(leapsec, ls2012seqInsElectric); RUN_TEST_CASE(leapsec, ls2012seqInsDumb); RUN_TEST_CASE(leapsec, lsEmptyTableDumb); RUN_TEST_CASE(leapsec, lsEmptyTableElectric); } ntpsec-1.1.0+dfsg1/tests/ntpd/restrict.c0000644000175000017500000001363313252364117017745 0ustar rlaagerrlaager#include "config.h" #include "ntpd.h" #include "ntp_lists.h" #include "unity.h" #include "unity_fixture.h" /* Helper functions */ static sockaddr_u create_sockaddr_u(unsigned short sin_port, const char* ip_addr) { sockaddr_u sockaddr; memset(&sockaddr, 0, sizeof(sockaddr)); SET_AF(&sockaddr, AF_INET); NSRCPORT(&sockaddr) = htons(sin_port); PSOCK_ADDR4(&sockaddr)->s_addr = inet_addr(ip_addr); return sockaddr; } TEST_GROUP(hackrestrict); TEST_SETUP(hackrestrict) { init_restrict(); } uptime_t current_time; /* not used - restruct code needs it */ TEST_TEAR_DOWN(hackrestrict) { restrict_u *empty_restrict = malloc(sizeof(restrict_u)); memset(empty_restrict, 0, sizeof(restrict_u)); restrict_u *current; do { UNLINK_HEAD_SLIST(current, restrictlist4, link); if (current != NULL) { *current = *empty_restrict; } } while (current != NULL); do { UNLINK_HEAD_SLIST(current, restrictlist6, link); if (current != NULL) { *current = *empty_restrict; } } while (current != NULL); free(empty_restrict); } /* Tests */ TEST(hackrestrict, RestrictionsAreEmptyAfterInit) { restrict_u *rl4 = malloc(sizeof(restrict_u)); restrict_u *rl6 = malloc(sizeof(restrict_u)); memset(rl4, 0, sizeof(restrict_u)); memset(rl6, 0, sizeof(restrict_u)); TEST_ASSERT_EQUAL(rl4->hitcount, restrictlist4->hitcount); TEST_ASSERT_EQUAL(rl4->flags, restrictlist4->flags); TEST_ASSERT_EQUAL(rl4->mflags, restrictlist4->mflags); TEST_ASSERT_EQUAL(rl4->expire, restrictlist4->expire); TEST_ASSERT_EQUAL(rl4->u.v4.addr, restrictlist4->u.v4.addr); TEST_ASSERT_EQUAL(rl4->u.v4.mask, restrictlist4->u.v4.mask); TEST_ASSERT_EQUAL(rl6->hitcount, restrictlist6->hitcount); TEST_ASSERT_EQUAL(rl6->flags, restrictlist6->flags); TEST_ASSERT_EQUAL(rl6->mflags, restrictlist6->mflags); TEST_ASSERT_EQUAL(rl6->expire, restrictlist6->expire); free(rl4); free(rl6); } TEST(hackrestrict, ReturnsCorrectDefaultRestrictions) { sockaddr_u sockaddr = create_sockaddr_u(54321, "63.161.169.137"); unsigned short retval = restrictions(&sockaddr); TEST_ASSERT_EQUAL(0, retval); } TEST(hackrestrict, HackingDefaultRestriction) { /* * We change the flag of the default restriction, * and check if restriction() returns that flag */ const unsigned short flags = 42; sockaddr_u resaddr = create_sockaddr_u(54321, "0.0.0.0"); sockaddr_u resmask = create_sockaddr_u(54321, "0.0.0.0"); hack_restrict(RESTRICT_FLAGS, &resaddr, &resmask, 0, flags, 0); sockaddr_u sockaddr = create_sockaddr_u(54321, "111.123.251.124"); TEST_ASSERT_EQUAL(flags, restrictions(&sockaddr)); } TEST(hackrestrict, CantRemoveDefaultEntry) { sockaddr_u resaddr = create_sockaddr_u(54321, "0.0.0.0"); sockaddr_u resmask = create_sockaddr_u(54321, "0.0.0.0"); hack_restrict(RESTRICT_REMOVE, &resaddr, &resmask, 0, 0, 0); TEST_ASSERT_EQUAL(0, restrictions(&resaddr)); } TEST(hackrestrict, AddingNewRestriction) { sockaddr_u resaddr = create_sockaddr_u(54321, "11.22.33.44"); sockaddr_u resmask = create_sockaddr_u(54321, "128.0.0.0"); const unsigned short flags = 42; hack_restrict(RESTRICT_FLAGS, &resaddr, &resmask, 0, flags, 0); TEST_ASSERT_EQUAL(flags, restrictions(&resaddr)); } TEST(hackrestrict, TheMostFittingRestrictionIsMatched) { sockaddr_u resaddr_target = create_sockaddr_u(54321, "11.22.33.44"); sockaddr_u resaddr_not_matching = create_sockaddr_u(54321, "11.99.33.44"); sockaddr_u resmask_not_matching = create_sockaddr_u(54321, "255.255.0.0"); sockaddr_u resaddr_best_match = create_sockaddr_u(54321, "11.22.30.20"); sockaddr_u resmask_best_match = create_sockaddr_u(54321, "255.255.0.0"); /* it also matches, but we prefer the one above, as it's more specific */ sockaddr_u resaddr_second_match = create_sockaddr_u(54321, "11.99.33.44"); sockaddr_u resmask_second_match = create_sockaddr_u(54321, "255.0.0.0"); hack_restrict(RESTRICT_FLAGS, &resaddr_not_matching, &resmask_not_matching, 0, 11, 0); hack_restrict(RESTRICT_FLAGS, &resaddr_best_match, &resmask_best_match, 0, 22, 0); hack_restrict(RESTRICT_FLAGS, &resaddr_second_match, &resmask_second_match, 0, 128, 0); TEST_ASSERT_EQUAL(22, restrictions(&resaddr_target)); } TEST(hackrestrict, DeletedRestrictionIsNotMatched) { sockaddr_u resaddr_target = create_sockaddr_u(54321, "11.22.33.44"); sockaddr_u resaddr_not_matching = create_sockaddr_u(54321, "11.99.33.44"); sockaddr_u resmask_not_matching = create_sockaddr_u(54321, "255.255.0.0"); sockaddr_u resaddr_best_match = create_sockaddr_u(54321, "11.22.30.20"); sockaddr_u resmask_best_match = create_sockaddr_u(54321, "255.255.0.0"); sockaddr_u resaddr_second_match = create_sockaddr_u(54321, "11.99.33.44"); sockaddr_u resmask_second_match = create_sockaddr_u(54321, "255.0.0.0"); hack_restrict(RESTRICT_FLAGS, &resaddr_not_matching, &resmask_not_matching, 0, 11, 0); hack_restrict(RESTRICT_FLAGS, &resaddr_best_match, &resmask_best_match, 0, 22, 0); hack_restrict(RESTRICT_FLAGS, &resaddr_second_match, &resmask_second_match, 0, 128, 0); /* deleting the best match*/ hack_restrict(RESTRICT_REMOVE, &resaddr_best_match, &resmask_best_match, 0, 22, 0); TEST_ASSERT_EQUAL(128, restrictions(&resaddr_target)); } TEST(hackrestrict, RestrictUnflagWorks) { sockaddr_u resaddr = create_sockaddr_u(54321, "11.22.30.20"); sockaddr_u resmask = create_sockaddr_u(54321, "255.255.0.0"); hack_restrict(RESTRICT_FLAGS, &resaddr, &resmask, 0, 11, 0); hack_restrict(RESTRICT_UNFLAG, &resaddr, &resmask, 0, 10, 0); TEST_ASSERT_EQUAL(1, restrictions(&resaddr)); } TEST_GROUP_RUNNER(hackrestrict) { RUN_TEST_CASE(hackrestrict, RestrictionsAreEmptyAfterInit); RUN_TEST_CASE(hackrestrict, ReturnsCorrectDefaultRestrictions); RUN_TEST_CASE(hackrestrict, HackingDefaultRestriction); RUN_TEST_CASE(hackrestrict, CantRemoveDefaultEntry); RUN_TEST_CASE(hackrestrict, AddingNewRestriction); RUN_TEST_CASE(hackrestrict, TheMostFittingRestrictionIsMatched); RUN_TEST_CASE(hackrestrict, DeletedRestrictionIsNotMatched); RUN_TEST_CASE(hackrestrict, RestrictUnflagWorks); } ntpsec-1.1.0+dfsg1/tests/ntpd/filegen.c0000644000175000017500000000215413252364117017513 0ustar rlaagerrlaager#include "config.h" #include "unity.h" #include "unity_fixture.h" #include "ntp_filegen.h" FILEGEN day, week, month; TEST_GROUP(filegen); TEST_SETUP(filegen) { filegen_register("/tmp/foo/", "day", &day); filegen_register("/tmp/foo/", "week", &week); filegen_register("/tmp/foo/", "month", &month); } TEST_TEAR_DOWN(filegen) { #ifdef DEBUG filegen_unregister("day"); filegen_unregister("week"); filegen_unregister("month"); #endif } // ===================================================================== // VALIDATION TESTS // ===================================================================== TEST(filegen, Day) { time_t time; // TEST_ASSERT_EQUAL(LSVALID_GOODHASH, rc); } TEST(filegen, Week) { // const char *cp = leap2; // int rc = leapsec_validate(stringreader, &cp); // TEST_ASSERT_EQUAL(LSVALID_NOHASH, rc); } TEST(filegen, Month) { // const char *cp = leap_bhash; // int rc = leapsec_validate(stringreader, &cp); // TEST_ASSERT_EQUAL(LSVALID_BADHASH, rc); } TEST_GROUP_RUNNER(filegen) { RUN_TEST_CASE(filegen, Day); RUN_TEST_CASE(filegen, Week); RUN_TEST_CASE(filegen, Month); } ntpsec-1.1.0+dfsg1/waf0000755000175000017500000001003613252650652014332 0ustar rlaagerrlaager#!/usr/bin/env python # encoding: ISO8859-1 # Thomas Nagy, 2005-2017 # """ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import os, sys, inspect VERSION="1.9.14" REVISION="d7f6128a2aa20a656027b134f0b4f4a6" GIT="a50064fef518fa947e70ff62762268c39aaaca77" INSTALL='' C1='#(' C2='#&' C3='#$' cwd = os.getcwd() join = os.path.join WAF='waf' def b(x): return x if sys.hexversion>0x300000f: WAF='waf3' def b(x): return x.encode() def err(m): print(('\033[91mError: %s\033[0m' % m)) sys.exit(1) def unpack_wafdir(dir, src): f = open(src,'rb') c = 'corrupt archive (%d)' while 1: line = f.readline() if not line: err('run waf-light from a folder containing waflib') if line == b('#==>\n'): txt = f.readline() if not txt: err(c % 1) if f.readline() != b('#<==\n'): err(c % 2) break if not txt: err(c % 3) txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) import shutil, tarfile try: shutil.rmtree(dir) except OSError: pass try: for x in ('Tools', 'extras'): os.makedirs(join(dir, 'waflib', x)) except OSError: err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) os.chdir(dir) tmp = 't.bz2' t = open(tmp,'wb') try: t.write(txt) finally: t.close() try: t = tarfile.open(tmp) except: try: os.system('bunzip2 t.bz2') t = tarfile.open('t') tmp = 't' except: os.chdir(cwd) try: shutil.rmtree(dir) except OSError: pass err("Waf cannot be unpacked, check that bzip2 support is present") try: for x in t: t.extract(x) finally: t.close() for x in ('Tools', 'extras'): os.chmod(join('waflib',x), 493) if sys.hexversion<0x300000f: sys.path = [join(dir, 'waflib')] + sys.path import fixpy2 fixpy2.fixdir(dir) os.remove(tmp) os.chdir(cwd) try: dir = unicode(dir, 'mbcs') except: pass try: from ctypes import windll windll.kernel32.SetFileAttributesW(dir, 2) except: pass def test(dir): try: os.stat(join(dir, 'waflib')) return os.path.abspath(dir) except OSError: pass def find_lib(): src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) base, name = os.path.split(src) #devs use $WAFDIR w=test(os.environ.get('WAFDIR', '')) if w: return w #waf-light if name.endswith('waf-light'): w = test(base) if w: return w err('waf-light requires waflib -> export WAFDIR=/folder') dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) for i in (INSTALL,'/usr','/usr/local','/opt'): w = test(i + '/lib/' + dirname) if w: return w #waf-local dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) w = test(dir) if w: return w #unpack unpack_wafdir(dir, src) return dir wafdir = find_lib() sys.path.insert(0, wafdir) if __name__ == '__main__': from waflib import Scripting Scripting.waf_entry_point(cwd, VERSION, wafdir) ntpsec-1.1.0+dfsg1/build/0000775000175000017500000000000013252650651014727 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/build/main/0000775000175000017500000000000013252650651015653 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/build/main/ntpd/0000775000175000017500000000000013252650651016620 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/build/main/ntpd/ntpd.80000644000175000017500000007033713252367170017667 0ustar rlaagerrlaager'\" t .\" Title: ntpd .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPD" "8" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpd \- Network Time Protocol service daemon .SH "SYNOPSIS" .sp .nf ntpd [\-46aghLmnNqx] [assert] [\-c \fIconffile\fR] [\-f \fIdriftfile\fR] [\-i \fIjaildir\fR] [\-k \fIkeyfile\fR] [\-l \fIlogfile\fR] [\-p \fIpidfile\fR] [\-P \fIpriority\fR] [\-s \fIstatsdir\fR] [\-t \fIkey\fR] [\-u \fIuser\fR[:\*(Aqgroup\*(Aq]] [\-U \fIinterface_update_interval\fR] [\-v \fIvariable\fR] [\-V \fIvariable\fR] [server\&...] .fi .SH "DESCRIPTION" .sp The ntpd utility is an operating system daemon which sets and maintains the system time of day in synchronism with Internet standard time servers\&. It is a complete implementation of the Network Time Protocol (NTP) version 4, as defined by RFC 5905, but also retains compatibility with version 3, as defined by RFC 1305, and versions 1 and 2, as defined by RFC 1059 and RFC 1119, respectively\&. .sp The ntpd utility can synchronize time to a theoretical precision of about 232 picoseconds\&. In practice, this limit is unattainable due to quantum limits on the clock speed of ballistic\-electron logic\&. .sp Ordinarily, ntpd reads the ntp\&.conf(5) configuration file at startup time in order to determine the synchronization sources and operating modes\&. It is also possible to specify a working, although limited, configuration entirely on the command line, obviating the need for a configuration file\&. .sp The ntpd program normally operates continuously while adjusting the system time and frequency, but in some cases this might not be practical\&. With the \-q option ntpd operates as in continuous mode, but exits just after setting the clock for the first time\&. Most applications will probably want to specify the iburst option with the server command\&. With this option a volley of messages is exchanged to groom the data and set the clock in about ten seconds\&. With \-q, if nothing is heard after a few minutes, the daemon times out and exits without setting the clock\&. .sp Various internal ntpd variables can be displayed and configuration options altered while the ntpd is running using the ntpq(1) utility program\&. The state of ntpd can be continuously monitored using ntpmon(1)\&. .sp When ntpd starts it looks at the value of umask(2), and if zero ntpd will set the umask(2) to 022\&. .SH "OPTIONS" .PP \-4, \-\-ipv4 .RS 4 Force IPv4 DNS name resolution\&. This option must not appear in combination with any of the following options: ipv6\&. .sp Force DNS resolution of following host names on the command line to the IPv4 namespace\&. .RE .PP \-6, \-\-ipv6 .RS 4 Force IPv6 DNS name resolution\&. This option must not appear in combination with any of the following options: ipv4\&. .sp Force DNS resolution of following host names on the command line to the IPv6 namespace\&. .RE .PP \-a, \-\-assert .RS 4 REQUIRE(false) to test assert handler\&. .RE .PP \-c \fIstring\fR, \-\-configfile=\fIstring\fR .RS 4 configuration file name\&. .sp The name and path of the configuration file, /etc/ntp\&.conf by default\&. .RE .PP \-d, \-\-debug\-level .RS 4 Increase debug verbosity level\&. This option may appear an unlimited number of times\&. .RE .PP \-D \fInumber\fR, \-\-set\-debug\-level=\fInumber\fR .RS 4 Set the debug verbosity level\&. This option may appear an unlimited number of times\&. This option takes an integer number as its argument\&. .RE .PP \-f \fIstring\fR, \-\-driftfile=\fIstring\fR .RS 4 frequency drift file name\&. .sp The name and path of the frequency file, /etc/ntp\&.drift by default\&. This is the same operation as the \fIdriftfile\fR configuration specification in the /etc/ntp\&.conf file\&. .RE .PP \-g, \-\-panicgate .RS 4 Allow the first adjustment to be big\&. This option may appear an unlimited number of times\&. .sp Normally, ntpd exits with a message to the system log if the offset exceeds the panic threshold, which is 1000 s by default\&. This option allows the time to be set to any value without restriction; however, this can happen only once\&. If the threshold is exceeded after that, ntpd will exit with a message to the system log\&. This option can be used with the \-q and \-x options\&. See the \fItinker\fR configuration file directive for other options\&. .RE .PP \-G, \-\-force\-step\-once .RS 4 Step any initial offset correction\&. .sp Normally, ntpd steps the time if the time offset exceeds the step threshold, which is 128 ms by default, and otherwise slews the time\&. This option forces the initial offset correction to be stepped, so the highest time accuracy can be achieved quickly\&. However, this may also cause the time to be stepped back so this option must not be used if applications requiring monotonic time are running\&. See the \fItinker\fR configuration file directive for other options\&. .RE .PP \-h, \-\-help .RS 4 Print a usage message summarizing options and exit\&. .RE .PP \-i \fIstring\fR, \-\-jaildir=\fIstring\fR .RS 4 Jail directory\&. .sp Chroot the server to the directory \fIjaildir\fR This option also implies that the server attempts to drop root privileges at startup\&. You may need to also specify a \-u option\&. This option is only available if the OS supports adjusting the clock without full root privileges\&. This option is supported under Linux, NetBSD and Solaris\&. .RE .PP \-I \fIiface\fR, \-\-interface=\fIiface\fR .RS 4 Listen on an interface name or address\&. This option may appear an unlimited number of times\&. .sp Open the network address given, or all the addresses associated with the given interface name\&. This option may appear multiple times\&. This option also implies not opening other addresses, except wildcard and localhost\&. This option is deprecated\&. Please consider using the configuration file \fIinterface\fR command, which is more versatile\&. .RE .PP \-k \fIstring\fR, \-\-keyfile=\fIstring\fR .RS 4 path to symmetric keys\&. .sp Specify the name and path of the symmetric key file\&. /etc/ntp\&.keys is a common location\&. This is the same operation as the \fIkeys\fR configuration file directive\&. .RE .PP \-l \fIstring\fR, \-\-logfile=\fIstring\fR .RS 4 path to the log file\&. .sp Specify the name and path of the log file\&. The default is the system log file\&. This is the same operation as the \fIlogfile\fR configuration file directive\&. .RE .PP \-L, \-\-novirtualips .RS 4 Do not listen to virtual interfaces\&. .sp Do not listen to virtual interfaces, defined as those with names containing a colon\&. This option is deprecated\&. Please consider using the configuration file interface command, which is more versatile\&. .RE .PP \-m, \-\-mdns .RS 4 Register with mDNS as a NTP server\&. .sp Registers as an NTP server with the local mDNS server which allows the server to be discovered via mDNS client lookup\&. .RE .PP \-n, \-\-nofork .RS 4 Do not fork\&. This option must not appear in combination with any of the following options: wait\-sync\&. .RE .PP \-N, \-\-nice .RS 4 Run at high priority\&. .sp To the extent permitted by the operating system, run ntpd at the highest priority\&. .RE .PP \-p \fIstring\fR, \-\-pidfile=\fIstring\fR .RS 4 path to the PID file\&. .sp Specify the name and path of the file used to record ntpd\*(Aqs process ID\&. This is the same operation as the \fIpidfile\fR configuration file directive\&. .RE .PP \-P \fInumber\fR, \-\-priority=\fInumber\fR .RS 4 Process priority\&. This option takes an integer number as its argument\&. .sp To the extent permitted by the operating system, run ntpd at the specified \fIsched_setscheduler(SCHED_FIFO)\fR priority\&. .RE .PP \-q, \-\-quit .RS 4 Set the time and quit\&. This option must not appear in combination with wait\-sync\&. .sp ntpd will not daemonize and will exit after the clock is first synchronized\&. This behavior mimics that of the old \fIntpdate\fR program, which has been replaced with a shell script\&. The \-g and \-x options can be used with this option\&. Note: The kernel time discipline is disabled with this option\&. .RE .PP \-s \fIstring\fR, \-\-statsdir=\fIstring\fR .RS 4 Statistics file location\&. .sp Specify the directory path for files created by the statistics facility\&. This is the same operation as the \fIstatsdir\fR configuration file directive\&. .RE .PP \-t \fItkey\fR, \-\-trustedkey=\fItkey\fR .RS 4 Trusted key number\&. This option may appear an unlimited number of times\&. .sp Add the specified key number to the trusted key list\&. .RE .PP \-u \fIstring\fR, \-\-user=\fIstring\fR .RS 4 Run as userid (or userid:groupid)\&. .sp Specify a user, and optionally a group, to switch to\&. The user and group may be specified by name or numeric id\&. If no group is specified, then the default group for userid is used\&. This option is only available if the OS supports adjusting the clock without full root privileges\&. This option is supported under Linux, NetBSD, Solaris and other OS\&. .RE .PP \-U \fInumber\fR, \-\-updateinterval=\fInumber\fR .RS 4 interval in seconds between scans for new or dropped interfaces\&. This option takes an integer number as its argument\&. .sp Give the time in seconds between two scans for new or dropped interfaces\&. For systems with routing socket support the scans will be performed shortly after the interface change has been detected by the system\&. Use 0 to disable scanning\&. 60 seconds is the minimum time between scans\&. .RE .PP \-w \fInumber\fR, \-\-wait\-sync=\fInumber\fR .RS 4 Seconds to wait for first clock sync\&. This option must not appear in combination with any of the following options: nofork, quit\&. This option takes an integer number as its argument\&. .sp If greater than zero, alters ntpd\*(Aqs behavior when forking to daemonize\&. Instead of exiting with status 0 immediately after the fork, the parent waits up to the specified number of seconds for the child to first synchronize the clock\&. The exit status is zero (success) if the clock was synchronized, otherwise it is ETIMEDOUT\&. This provides the option for a script starting ntpd to easily wait for the first set of the clock before proceeding\&. .RE .PP \-x, \-\-slew .RS 4 Slew up to 600 seconds\&. .sp Normally, the time is slewed if the offset is less than the step threshold, which is 128 ms by default, and stepped if above the threshold\&. This option sets the threshold to 600 s, which is well within the accuracy window to set the clock manually\&. Note: Since the slew rate of typical Unix kernels is limited to 0\&.5 ms/s, each second of adjustment requires an amortization interval of 2000 s\&. Thus, an adjustment as much as 600 s will take almost 14 days to complete\&. This option can be used with the \-g and \-q options\&. See the \fItinker\fR configuration file directive for other options\&. Note: The kernel time discipline is disabled with this option\&. .RE .PP \-z \fInvar\fR, \-\-var=\fInvar\fR .RS 4 make ARG an ntp variable (RW)\&. This option may appear an unlimited number of times\&. .RE .PP \-Z \fInvar\fR, \-\-dvar=\fIndvar\fR .RS 4 make ARG an ntp variable (RW|DEF)\&. This option may appear an unlimited number of times\&. .RE .PP \-V, \-\-version .RS 4 Output version of program and exit\&. .RE .sp Any arguments given after options are interpreted as server addresses or hostnames, with the \fIiburst\fR option implied\&. Associations with these are formed before any associations implied by the configuration file\&. .SH "USAGE" .SS "How NTP Operates" .sp The ntpd utility operates by exchanging messages with one or more configured servers over a range of designated poll intervals\&. When started, whether for the first or subsequent times, the program requires several exchanges from the majority of these servers so the signal processing and mitigation algorithms can accumulate and groom the data and set the clock\&. In order to protect the network from bursts, the initial poll interval for each server is delayed an interval randomized over a few seconds\&. At the default initial poll interval of 64s, several minutes can elapse before the clock is set\&. This initial delay to set the clock can be safely and dramatically reduced using the \fIiburst\fR keyword with the \fIserver\fR configuration command, as described in ntp\&.conf(5)\&. .sp Most operating systems and hardware of today incorporate a time\-of\-year (TOY) chip to maintain the time during periods when the power is off\&. When the machine is booted, the chip is used to initialize the operating system time\&. After the machine has synchronized to a NTP server, the operating system corrects the chip from time to time\&. In the default case, if ntpd detects that the time on the host is more than 1000s from the server time, ntpd assumes something must be terribly wrong and the only reliable action is for the operator to intervene and set the clock by hand\&. (Reasons for this include there is no TOY chip, or its battery is dead, or that the TOY chip is just of poor quality\&.) This causes ntpd to exit with a panic message to the system log\&. The \-g option overrides this check and the clock will be set to the server time regardless of the chip time (up to 68 years in the past or future \(em this is a limitation of the NTPv4 protocol)\&. However, and to protect against broken hardware, such as when the CMOS battery fails or the clock counter becomes defective, once the clock has been set an error greater than 1000s will cause ntpd to exit anyway\&. .sp Under ordinary conditions, ntpd adjusts the clock in small steps so that the timescale is effectively continuous and without discontinuities\&. Under conditions of extreme network congestion, the roundtrip delay jitter can exceed three seconds and the synchronization distance, which is equal to one\-half the roundtrip delay plus error budget terms, can become very large\&. The ntpd algorithms discard sample offsets exceeding 128 ms, unless the interval during which no sample offset is less than 128 ms exceeds 900s\&. The first sample after that, no matter what the offset, steps the clock to the indicated time\&. In practice this reduces the false alarm rate where the clock is stepped in error to a vanishingly low incidence\&. .sp As the result of this behavior, once the clock has been set it very rarely strays more than 128 ms even under extreme cases of network path congestion and jitter\&. Sometimes, in particular when ntpd is first started without a valid drift file on a system with a large intrinsic drift the error might grow to exceed 128 ms, which would cause the clock to be set backwards if the local clock time is more than 128 ms in the future relative to the server\&. In some applications, this behavior may be unacceptable\&. There are several solutions, however\&. If the \-x option is included on the command line, the clock will never be stepped and only slew corrections will be used\&. But this choice comes with a cost that should be carefully explored before deciding to use the \-x option\&. The maximum slew rate possible is limited to 500 parts\-per\-million (PPM) as a consequence of the correctness principles on which the NTP protocol and algorithm design are based\&. As a result, the local clock can take a long time to converge to an acceptable offset, about 2,000 s for each second the clock is outside the acceptable range\&. During this interval the local clock will not be consistent with any other network clock and the system cannot be used for distributed applications that require correctly synchronized network time\&. .sp In spite of the above precautions, sometimes when large frequency errors are present the resulting time offsets stray outside the 128\-ms range and an eventual step or slew time correction is required\&. If following such a correction the frequency error is so large that the first sample is outside the acceptable range, ntpd enters the same state as when the \fIntp\&.drift\fR file is not present\&. The intent of this behavior is to quickly correct the frequency and restore operation to the normal tracking mode\&. In the most extreme cases (the host \fItime\&.ien\&.it\fR comes to mind), there may be occasional step/slew corrections and subsequent frequency corrections\&. It helps in these cases to use the \fIburst\fR keyword when configuring the server, but ONLY when you have permission to do so from the owner of the target host\&. .sp Finally, in the past many startup scripts would run a separate utility to get the system clock close to correct before starting ntpd(8), but this was never more than a mediocre hack and is no longer needed\&. If you are following the instructions in the section called \(lqStarting NTP (Best Current Practice)\(rq and you still need to set the system time before starting ntpd, please open a bug report and document what is going on, and then look at using ntpdig(1)\&. .sp There is a way to start ntpd(8) that often addresses all of the problems mentioned above\&. .SS "Starting NTP (Best Current Practice)" .sp First, use the \fIiburst\fR option on your \fIserver\fR and \fIpool\fR entries\&. .sp If you can also keep a good \fIntp\&.drift\fR file then ntpd(8) will effectively "warm\-start" and your system\(cqs clock will be stable in under 11 seconds\*(Aq time\&. .sp As soon as possible in the startup sequence, start ntpd(8) with at least the \-g and perhaps the \-N options\&. Then, start the rest of your "normal" processes\&. This will give ntpd(8) as much time as possible to get the system\(cqs clock synchronized and stable\&. .sp Finally, if you have processes like \fIdovecot\fR or database servers that require monotonically\-increasing time, run ntpwait(8) as late as possible in the boot sequence (perhaps with the \-v flag) and after ntpwait(8) exits successfully it is as safe as it will ever be to start any process that require stable time\&. .SS "Frequency Discipline" .sp The ntpd behavior at startup depends on whether the frequency file, usually \fIntp\&.drift\fR, exists\&. This file contains the latest estimate of clock frequency error\&. When the ntpd is started and the file does not exist, the ntpd enters a special mode designed to quickly adapt to the particular system clock oscillator time and frequency error\&. This takes approximately 15 minutes, after which the time and frequency are set to nominal values and the ntpd enters normal mode, where the time and frequency are continuously tracked relative to the server\&. After one hour the frequency file is created and the current frequency offset written to it\&. When the ntpd is started and the file does exist, the ntpd frequency is initialized from the file and enters normal mode immediately\&. After that the current frequency offset is written to the file at hourly intervals\&. .SS "Operating Modes" .sp ntpd normally operates continuously while monitoring for small changes in frequency and trimming the clock for the ultimate precision\&. However, it can operate in a one\-time mode where the time is set from an external server and frequency is set from a previously recorded frequency file\&. .sp By default, ntpd runs in continuous mode where each of possibly several external servers is polled at intervals determined by an intricate state machine\&. The state machine measures the incidental roundtrip delay jitter and oscillator frequency wander and determines the best poll interval using a heuristic algorithm\&. Ordinarily, and in most operating environments, the state machine will start with 64s intervals and eventually increase in steps to 1024s\&. A small amount of random variation is introduced in order to avoid bunching at the servers\&. In addition, should a server become unreachable for some time, the poll interval is increased in steps to 1024s in order to reduce network overhead\&. .sp In some cases it may not be practical for ntpd to run continuously\&. The \-q option is provided to support running ntpd periodically from a cron(8) job\&. Setting this option will cause ntpd to exit just after setting the clock for the first time\&. The procedure for initially setting the clock is the same as in continuous mode; most applications will probably want to specify the \fIiburst\fR keyword with the \fIserver\fR configuration command\&. With this keyword a volley of messages are exchanged to groom the data and the clock is set in about 10 sec\&. If nothing is heard after a couple of minutes, the daemon times out and exits\&. .sp When kernel support is available to discipline the clock frequency, which is the case for stock Solaris, Linux and FreeBSD, a useful feature is available to discipline the clock frequency\&. First, ntpd is run in continuous mode with selected servers in order to measure and record the intrinsic clock frequency offset in the frequency file\&. It may take some hours for the frequency and offset to settle down\&. Then the ntpd is stopped and run in one\-time mode as required\&. At each startup, the frequency is read from the file and initializes the kernel frequency\&. .SS "Poll Interval Control" .sp This version of NTP includes an intricate state machine to reduce the network load while maintaining a quality of synchronization consistent with the observed jitter and wander\&. There are a number of ways to tailor the operation in order enhance accuracy by reducing the interval or to reduce network overhead by increasing it\&. However, the user is advised to carefully consider the consequences of changing the poll adjustment range from the default minimum of 64 s to the default maximum of 1,024 s\&. The default minimum can be changed with the \fItinker\fR \fIminpoll\fR command to a value not less than 16 s\&. This value is used for all configured associations, unless overridden by the \fIminpoll\fR option on the configuration command\&. Note that most device drivers will not operate properly if the poll interval is less than 64 s and that the broadcast server and manycast client associations will also use the default, unless overridden\&. .sp In some cases involving dial up or toll services, it may be useful to increase the minimum interval to a few tens of minutes and maximum interval to a day or so\&. Under normal operation conditions, once the clock discipline loop has stabilized the interval will be increased in steps from the minimum to the maximum\&. However, this assumes the intrinsic clock frequency error is small enough for the discipline loop correct it\&. The capture range of the loop is 500 PPM at an interval of 64s decreasing by a factor of two for each doubling of interval\&. At a minimum of 1,024 s, for example, the capture range is only 31 PPM\&. If the intrinsic error is greater than this, the drift file \fIntp\&.drift\fR will have to be specially tailored to reduce the residual error below this limit\&. Once this is done, the drift file is automatically updated once per hour and is available to initialize the frequency on subsequent daemon restarts\&. .SS "The huff\-n\*(Aq\-puff Filter" .sp In scenarios where a considerable amount of data are to be downloaded or uploaded over telephone modems, timekeeping quality can be seriously degraded\&. This occurs because the differential delays on the two directions of transmission can be quite large\&. In many cases the apparent time errors are so large as to exceed the step threshold and a step correction can occur during and after the data transfer is in progress\&. .sp The huff\-n\*(Aq\-puff filter is designed to correct the apparent time offset in these cases\&. It depends on knowledge of the propagation delay when no other traffic is present\&. In common scenarios this occurs during other than work hours\&. The filter maintains a shift register that remembers the minimum delay over the most recent interval measured usually in hours\&. Under conditions of severe delay, the filter corrects the apparent offset using the sign of the offset and the difference between the apparent delay and minimum delay\&. The name of the filter reflects the negative (huff) and positive (puff) correction, which depends on the sign of the offset\&. .sp The filter is activated by the \fItinker\fR command and \fIhuffpuff\fR keyword, as described in ntp\&.conf(5)\&. .SH "FILES" .TS allbox tab(:); ltB ltB ltB ltB. T{ File T}:T{ Default T}:T{ Option T}:T{ Option T} .T& lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp configuration file T}:T{ .sp /etc/ntp\&.conf T}:T{ .sp \-c T}:T{ .sp conffile T} T{ .sp configuration directory T}:T{ .sp /etc/ntp\&.d T}:T{ .sp \-c T}:T{ .sp conffile T} T{ .sp frequency file T}:T{ .sp none T}:T{ .sp \-f T}:T{ .sp driftfile T} T{ .sp leapseconds file T}:T{ .sp none T}:T{ .sp T}:T{ .sp leapfile T} T{ .sp process ID file T}:T{ .sp none T}:T{ .sp \-p T}:T{ .sp pidfile T} T{ .sp log file T}:T{ .sp system log T}:T{ .sp \-l T}:T{ .sp logfile T} T{ .sp include file T}:T{ .sp none T}:T{ .sp none T}:T{ .sp includefile T} T{ .sp statistics path T}:T{ .sp /var/NTP T}:T{ .sp \-s T}:T{ .sp statsdir T} T{ .sp keys file T}:T{ .sp none T}:T{ .sp \-k T}:T{ .sp keys T} .TE .sp 1 .sp Configuration files are parsed according to the following rules: .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} The plain config file (normally /etc/ntp\&.conf but the path can be overridden by the \-c option) is read first if it exists\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} Then the configuration directory, if it exists, is scanned\&. Normally this directory is /etc/ntp\&.d, but if the \-c option is specified the /etc will be specified by the directory name of the \-c argument\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} Each file beneath the configuration directory with the extension "\&.conf" is interpreted\&. Files are interpreted in ASCII sort order of their pathnames\&. Files with other extensions or no extensions are ignored\&. .RE .SH "SIGNALS" .sp SIGQUIT, SIGINT, and SIGTERM will cause ntpd to clean up and exit\&. .sp SIGHUP will reopen the log file if it has changed and check for a new leapseconds file if one was specified\&. .sp On most systems, you can send SIGHUP to ntpd with .sp .if n \{\ .RS 4 .\} .nf # sigkill \-HUP ntpd .fi .if n \{\ .RE .\} .sp If built with debugging enabled (waf configured with \-\-enable\-debug) SIGUSR1 will increase the debug level by 1 and SIGUSR2 will decrease it by 1\&. This may be helpful if you are running with \-n, either just to see the logging on your screen or with gdb\&. .SH "BUGS" .sp The \fI\-V\fR option is not backward\-compatible with its use (as the equivalent of \-Z) in older versions\&. .SH "STANDARDS" .PP RFC 1059 .RS 4 David L\&. Mills, \fINetwork Time Protocol (Version 1)\fR, RFC 1059 .RE .PP RFC 1119 .RS 4 David L\&. Mills, \fINetwork Time Protocol (Version 2)\fR, RFC 1119 .RE .PP RFC 1305 .RS 4 David L\&. Mills, \fINetwork Time Protocol (Version 3)\fR, RFC 1305 .RE .PP RFC 5905 .RS 4 David L\&. Mills and J\&. Martin, Ed\&. and J\&. Burbank and W\&. Kasch, \fINetwork Time Protocol Version 4: Protocol and Algorithms Specification\fR, RFC 5905 .RE .PP RFC 5907 .RS 4 H\&. Gerstung and C\&. Elliott and B\&. Haberman, Ed\&., \fIDefinitions of Managed Objects for Network Time Protocol Version 4: (NTPv4)\fR, RFC 5907 .RE .PP RFC 5908 .RS 4 R\&. Gayraud and B\&. Lourdelet, \fINetwork Time Protocol (NTP) Server Option for DHCPv6\fR, RFC 5908 .RE .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 Execution failed \- examine system logfiles\&. .RE .SH "SEE ALSO" .sp ntp\&.conf(5), ntpq(1), ntpdig(1)\&. ntpsec-1.1.0+dfsg1/build/main/ntpd/ntp.conf.50000644000175000017500000016666313252367172020456 0ustar rlaagerrlaager'\" t .\" Title: ntp.conf .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTP\&.CONF" "5" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntp.conf \- Network Time Protocol (NTP) daemon configuration file format .SH "SYNOPSIS" .sp /etc/ntp\&.conf .SH "DESCRIPTION" .sp The ntp\&.conf configuration file is read at initial startup by the ntpd(8) daemon in order to specify the synchronization sources, modes and other related information\&. Usually, it is installed in the /etc directory, but could be installed elsewhere (see the daemon\(cqs \-c command line option)\&. .sp The file format is similar to other UNIX configuration files\&. Comments begin with a \(oq#\(cq character and extend to the end of the line; blank lines are ignored\&. Configuration commands consist of an initial keyword followed by a list of arguments, some of which may be optional, separated by whitespace\&. Commands may not be continued over multiple lines\&. Arguments may be host names, host addresses written in numeric, dotted\-quad form, integers, floating point numbers (when specifying times in seconds) and text strings\&. .sp Configuration files may have inclusion lines\&. The syntax is include followed by whitespace followed by a file or directory name\&. The configuration is evaluated as though the text of the file \- or all files of the directory with the extension "\&.conf" \- were textually spliced in at the point of the include\&. Relative paths will work, even when the \-c option changes the config directory root\&. .sp The rest of this page describes the configuration and control options\&. The "Notes on Configuring NTP and Setting up an NTP Subnet" page (available as part of the HTML documentation provided under /usr/share/doc/ntp) contains an extended discussion of these options\&. In addition to the discussion of general \fIConfiguration Options\fR, there are sections describing the following supported functionality and the options used to control it: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Authentication Support .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Monitoring Support .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Access Control Support .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Automatic NTP Configuration Options .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Reference Clock Support .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} Miscellaneous Options .RE .sp Following these is a section describing \fIMiscellaneous Options\fR\&. While there is a rich set of options available, the only required option is one or more pool, server, peer, or broadcast commands\&. .SH "CONFIGURATION SUPPORT" .sp Following is a description of the configuration commands in NTPv4\&. There are two classes of commands, association commands that configure a persistent association with a remote server or peer or reference clock, and auxiliary commands that specify environmental variables that control various related operations\&. .SS "Association Commands" .sp Only those options applicable to each command are listed below\&. Use of options not listed may not be caught as an error, but may result in some weird and even destructive behavior\&. .sp In contexts where a host name is expected, a \-4 or \-\-ipv4 qualifier preceding the host name forces DNS resolution to the IPv4 namespace, while a \-6 or \-\-ipv6 qualifier forces DNS resolution to the IPv6 namespace\&. .sp In these commands, an \fIaddress\fR can be any of (a) an IPV4 address in a\&.b\&.c\&.d format, (b) an IPV6 address in [a:b:c:d:e:f:g] format, (c) a link\-local IPV6 address with an interface specified in [a:b:c:d:e:f:g]%device format, or (d) a DNS hostname\&. .sp pool \fIaddress\fR [burst] [iburst] [version \fIversion\fR] [prefer] [minpoll \fIminpoll\fR] [maxpoll \fImaxpoll\fR] [preempt] .sp server \fIaddress\fR [key \fIkey\fR] [burst] [iburst] [version \fIversion\fR] [prefer] [minpoll \fIminpoll\fR] [maxpoll \fImaxpoll\fR] .sp peer \fIaddress\fR [key \fIkey\fR] [version \fIversion\fR] [prefer] [minpoll \fIminpoll\fR] [maxpoll \fImaxpoll\fR] .PP unpeer [\fIaddress\fR | \fIassocid\fR | clock \fIclocktype\fR [ unit \fIunitnum\fR]] .RS 4 These six commands specify the time server name or address to be used and the mode in which to operate\&. The \fIaddress\fR can be either a DNS name or an IP address in dotted\-quad notation\&. If it is a refclock, it can be clock followed by a type\-unit pair as in the refclock directive; a missing unit clause is interpreted as unit 0\&. .RE .PP pool .RS 4 For server addresses, this command mobilizes a persistent client mode association with a number of remote servers\&. In this mode the local clock can synchronized to the remote server, but the remote server can never be synchronized to the local clock\&. .RE .PP server .RS 4 For server addresses, this command mobilizes a persistent client mode association with the specified remote server or local radio clock\&. In this mode the local clock can synchronized to the remote server, but the remote server can never be synchronized to the local clock\&. .RE .PP peer .RS 4 NTP peer mode has been removed for security reasons\&. peer is now just an alias for the server keyword\&. See above\&. .RE .PP unpeer .RS 4 This command removes a previously configured association\&. An address or association ID can be used to identify the association\&. Either an IP address or DNS name can be used\&. This command is most useful when supplied via ntpq runtime configuration commands config and config\-from\-file\&. .RE .SS "Association Options" .PP bias .RS 4 Add the command argument, a floating\-point value in seconds, to the time offset () computed for this server\&. May be useful if you are a client on a network connection such as an ADSL line where there is a predictable asymmetry between upstream and downstream flight times\&. One way you night see this is if you use a fixed set of others and one has a stable offset that is an outlier from the others; in that case you might want to use bias to compensate out the offset\&. .RE .PP burst .RS 4 when the server is reachable, send a burst of eight packets instead of the usual one\&. The packet spacing is normally 2 s; however, the spacing between the first and second packets can be changed with the calldelay command to allow additional time for a modem or ISDN call to complete\&. This is designed to improve timekeeping quality with the server command\&. .RE .PP iburst .RS 4 When the server is unreachable, send a burst of six packets instead of the usual one\&. The packet spacing is normally 2 s; however, the spacing between the first two packets can be changed with the calldelay command to allow additional time for a modem or ISDN call to complete\&. This is designed to speed the initial synchronization acquisition with the server command, and when ntpd(8) is started with the \-q option\&. .RE .PP key \fIkey\fR .RS 4 All packets sent to and received from the server or peer are to include authentication fields encrypted using the specified \fIkey\fR identifier with values from 1 to 65534, inclusive\&. The default is to include no encryption field\&. .RE .PP minpoll \fIminpoll\fR, maxpoll \fImaxpoll\fR .RS 4 These options specify the minimum and maximum poll intervals for NTP messages, as a power of 2 in seconds\&. The maximum poll interval defaults to 10 (1,024 s), but can be increased by the \fImaxpoll\fR option to an upper limit of 17 (36\&.4 h)\&. The minimum poll interval defaults to 6 (64 s), but can be decreased by the \fIminpoll\fR option to a lower limit of 0 (1 s)\&. .RE .PP mode \fIoption\fR .RS 4 Pass the option to a reference clock driver\&. This option is valid only with refclock addresses\&. .RE .PP noselect .RS 4 Marks the server as unused, except for display purposes\&. The server is discarded by the selection algorithm\&. .RE .PP prefer .RS 4 Marks the server as preferred\&. All other things being equal, this host will be chosen for synchronization among a set of correctly operating hosts\&. See the "Mitigation Rules and the prefer Keyword" page for further information\&. .RE .PP true .RS 4 Mark the association to assume truechimer status; that is, always survive the selection and clustering algorithms\&. This option can be used with any association, but is most useful for reference clocks with large jitter on the serial port and precision pulse\-per\-second (PPS) signals\&. Caution: this option defeats the algorithms designed to cast out falsetickers and can allow these sources to set the system clock\&. This option is valid only with the server command\&. .RE .PP version \fIversion\fR .RS 4 Specifies the version number to be used for outgoing NTP packets\&. Versions 1\-4 are the choices, with version 4 the default\&. .RE .SS "Association Auxiliary Commands" .PP mdnstries \fInumber\fR .RS 4 If we are participating in mDNS, after we have synched for the first time we attempt to register with the mDNS system\&. If that registration attempt fails, we try again at one minute intervals for up to \fInumber\fR times\&. After all, ntpd may be starting before mDNS\&. The default value for mdnstries is 5\&. .RE .SH "AUTHENTICATION COMMANDS" .PP controlkey \fIkey\fR .RS 4 Specifies the key identifier to use with the ntpq(1) utility, which uses the standard protocol defined in RFC 5905\&. The \fIkey\fR argument is the key identifier for a trusted key, where the value can be in the range 1 to 65,534, inclusive\&. .RE .PP keys \fIkeyfile\fR .RS 4 Specifies the complete path and location of the MD5/SHA1 key file containing the keys and key identifiers used by ntpd(8), and ntpq(1) when operating with symmetric\-key cryptography\&. This is the same operation as the \-k command line option\&. .RE .PP trustedkey \fIkey\&...\fR .RS 4 Specifies the key identifiers which are trusted for the purposes of authenticating peers with symmetric key cryptography, as well as keys used by the ntpq(1) program\&. Multiple keys on the same line should be separated by spaces\&. Key ranges can be specified as (first \&... last)\&. The spaces around the \&... are necessary\&. Multiple trustedkey lines are supported and trusted keys can also be specified on the command line\&. .RE .sp The authentication procedures require that both the local and remote servers share the same key and key identifier for this purpose, although different keys can be used with different servers\&. The \fIkey\fR arguments are 32\-bit unsigned integers with values from 1 to 65,534\&. .SH "MONITORING SUPPORT" .sp ntpd(8) includes a comprehensive monitoring facility suitable for continuous, long term recording of server and client timekeeping performance\&. See the statistics command below for a listing and example of each type of statistics currently supported\&. Statistic files are managed using file generation sets and scripts in the \&./scripts directory of this distribution\&. Using these facilities and UNIX cron(8) jobs, the data can be automatically summarized and archived for retrospective analysis\&. .SS "Monitoring Commands" .PP statistics \fIname\&...\fR .RS 4 Enables writing of statistics records\&. Currently, eight kinds of \fIname\fR statistics are supported\&. .PP clockstats .RS 4 Enables recording of clock driver statistics information\&. Each update received from a clock driver appends a line of the following form to the file generation set named \fIclockstats\fR: .sp .if n \{\ .RS 4 .\} .nf 49213 525\&.624 SPECTRACOM(1) 93 226 00:08:29\&.606 .fi .if n \{\ .RE .\} .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 49213 T}:T{ MJD T}:T{ modified Julian day number T} T{ 525\&.624 T}:T{ s T}:T{ time of day (s) past midnight UTC T} T{ SPECTRACOM(1) T}:T{ T}:T{ receiver identifier (Spectracom unit 1) T} T{ 93 226 00:08:29\&.606 T}:T{ T}:T{ timecode (format varies by refclock) T} .TE .sp 1 .RE .RE .sp The first two fields show the date (Modified Julian Day) and time (seconds and fraction past UTC midnight)\&. The next normally shows clock type and unit (but if you are running in strict Classic compatibility mode it will show the magic clock address in dotted\-quad notation)\&. The final field last timecode received from the clock in decoded ASCII format, where meaningful\&. In some clock drivers a good deal of additional information can be gathered and displayed as well\&. See information specific to each clock for further details\&. .PP loopstats .RS 4 Enables recording of loop filter statistics information\&. Each update of the local clock outputs a line of the following form to the file generation set named \fIloopstats\fR: .sp .if n \{\ .RS 4 .\} .nf 50935 75440\&.031 0\&.000006019 13\&.778190 0\&.000351733 0\&.0133806 .fi .if n \{\ .RE .\} .sp .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 50935 T}:T{ MJD T}:T{ date T} T{ 75440\&.031 T}:T{ s T}:T{ time past midnight T} T{ 0\&.000006019 T}:T{ s T}:T{ clock offset T} T{ 13\&.778 T}:T{ PPM T}:T{ drift (frequency offset) T} T{ 0\&.000351733 T}:T{ s T}:T{ RMS jitter T} T{ 0\&.013380 T}:T{ PPM T}:T{ RMS frequency jitter (aka wander) T} T{ 6 T}:T{ log2 s T}:T{ clock discipline loop time constant T} .TE .sp 1 The first two fields show the date (Modified Julian Day) and time (seconds and fraction past UTC midnight)\&. The next five fields show time offset (seconds), frequency offset (parts per million \- PPM), RMS jitter (seconds), Allan deviation (PPM) and clock discipline time constant\&. .RE .PP protostats .RS 4 Record significant peer and system events\&. Each significant event appends one line to the protostats file set: .sp 49213 525\&.624 128\&.4\&.1\&.1 963a 8a\fImessage\fR .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 49213 T}:T{ MJD T}:T{ date T} T{ 525\&.624 T}:T{ s T}:T{ time past midnight T} T{ 128\&.4\&.1\&.1 T}:T{ IP T}:T{ source address (0\&.0\&.0\&.0 for system) T} T{ 963a T}:T{ code T}:T{ status word T} T{ 8a T}:T{ code T}:T{ event message code T} T{ \fImessage\fR T}:T{ text T}:T{ event message T} .TE .sp 1 The event message code and \fImessage\fR field are described on the "Event Messages and Status Words" page\&. .RE .PP peerstats .RS 4 Enables recording of peer statistics information\&. This includes statistics records of all peers of a NTP server and of special signals, where present and configured\&. Each valid update appends a line of the following form to the current element of a file generation set named \fIpeerstats\fR: .sp .if n \{\ .RS 4 .\} .nf 48773 10847\&.650 SPECTRACOM(4) 9714 \-0\&.001605376 0\&.000000000 0\&.001424877 0\&.000958674 .fi .if n \{\ .RE .\} .sp .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 48773 T}:T{ MJD T}:T{ date T} T{ 10847\&.650 T}:T{ s T}:T{ time past midnight T} T{ SPECTRACOM(4) T}:T{ T}:T{ clock name (unit) or source address T} T{ 9714 T}:T{ hex T}:T{ status word T} T{ \-0\&.001605376 T}:T{ s T}:T{ clock offset T} T{ 0\&.000000000 T}:T{ s T}:T{ roundtrip delay T} T{ 0\&.001424877 T}:T{ s T}:T{ dispersion T} T{ 0\&.000958674 T}:T{ s T}:T{ RMS jitter T} .TE .sp 1 The first two fields show the date (Modified Julian Day) and time (seconds and fraction past UTC midnight)\&. The third field shows the reference clock type and unit number (but if you are running in the peer address in dotted\-quad notation instead) The fourth field is a status word, encoded in hex in the format described in Appendix A of the NTP specification RFC 1305\&. The final four fields show the offset, delay, dispersion and RMS jitter, all in seconds\&. .RE .PP rawstats .RS 4 Enables recording of raw\-timestamp statistics information\&. This includes statistics records of all peers of a NTP server and of special signals, where present and configured\&. Each NTP message received from a peer or clock driver appends a line of the following form to the file generation set named \fIrawstats\fR: .sp .if n \{\ .RS 4 .\} .nf 56285 54575\&.160 128\&.4\&.1\&.1 192\&.168\&.1\&.5 3565350574\&.400229473 3565350574\&.442385200 3565350574\&.442436000 3565350575\&.154505763 0 4 4 1 8 \-21 0\&.000000 0\&.000320 PPS 0 .fi .if n \{\ .RE .\} .sp .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 56285 T}:T{ MJD T}:T{ date T} T{ 54575\&.160 T}:T{ s T}:T{ time past midnight T} T{ 128\&.4\&.1\&.1 T}:T{ IP T}:T{ source address T} T{ 192\&.168\&.1\&.5 T}:T{ IP T}:T{ destination address T} T{ 3565350574\&.400229473 T}:T{ NTP s T}:T{ origin timestamp T} T{ 3565350574\&.442385200 T}:T{ NTP s T}:T{ receive timestamp T} T{ 3565350574\&.442436000 T}:T{ NTP s T}:T{ transmit timestamp T} T{ 3565350575\&.154505763 T}:T{ NTP s T}:T{ destination timestamp T} T{ 0 T}:T{ 0: OK, 1: insert pending, 2: delete pending, 3: not synced T}:T{ leap warning indicator T} T{ 4 T}:T{ 4 was current in 2012 T}:T{ NTP version T} T{ 4 T}:T{ 3: client, 4: server, 6: ntpq T}:T{ mode T} T{ 1 T}:T{ 1\-15, 16: not synced T}:T{ stratum T} T{ 8 T}:T{ log2 seconds T}:T{ poll T} T{ \-21 T}:T{ log2 seconds T}:T{ precision T} T{ 0\&.000000 T}:T{ seconds T}:T{ total roundtrip delay from the remote server to the primary reference clock T} T{ 0\&.000320 T}:T{ seconds T}:T{ total dispersion from the remote server to the primary reference clock T} T{ \&.PPS\&. T}:T{ IP or text T}:T{ refid, association ID T} T{ 0 T}:T{ integer T}:T{ lost packets since last response T} .TE .sp 1 The first two fields show the date (Modified Julian Day) and time (seconds and fraction past UTC midnight)\&. The next two fields show the remote peer or clock identification followed by the local address in dotted\-quad notation\&. The final four fields show the originate, receive, transmit and final NTP timestamps in order\&. The timestamp values are as received and before processing by the various data smoothing and mitigation algorithms\&. .RE .PP sysstats .RS 4 Enables recording of ntpd statistics counters on a periodic basis\&. Each hour a line of the following form is appended to the file generation set named \fIsysstats\fR: .sp .if n \{\ .RS 4 .\} .nf 50928 2132\&.543 36000 81965 0 9546 56 71793 512 540 10 147 1 .fi .if n \{\ .RE .\} .sp .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 50928 T}:T{ MJD T}:T{ date T} T{ 2132\&.543 T}:T{ s T}:T{ time past midnight T} T{ 3600 T}:T{ s T}:T{ time since reset T} T{ 81965 T}:T{ # T}:T{ packets received T} T{ 0 T}:T{ # T}:T{ packets for this host T} T{ 9546 T}:T{ # T}:T{ current versions T} T{ 56 T}:T{ # T}:T{ old version T} T{ 512 T}:T{ # T}:T{ access denied T} T{ 540 T}:T{ # T}:T{ bad length or format T} T{ 10 T}:T{ # T}:T{ bad authentication T} T{ 4 T}:T{ # T}:T{ declined T} T{ 147 T}:T{ # T}:T{ rate exceeded T} T{ 1 T}:T{ # T}:T{ kiss\-o\*(Aq\-death packets sent T} .TE .sp 1 The first two fields show the date (Modified Julian Day) and time (seconds and fraction past UTC midnight)\&. The remaining ten fields show the statistics counter values accumulated since the last generated line\&. .RE .PP usestats .RS 4 Enables recording of ntpd resource usage statistics\&. Each hour a line of the following form is appended to the file generation set named \fIusestats\fR: .sp .if n \{\ .RS 4 .\} .nf 57570 83399\&.541 3600 0\&.902 1\&.451 164 0 0 0 2328 64226 1 0 4308 .fi .if n \{\ .RE .\} .sp .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 57570 T}:T{ MJD T}:T{ date T} T{ 83399\&.541 T}:T{ s T}:T{ time past midnight T} T{ 3600 T}:T{ s T}:T{ time since reset T} T{ 0\&.902 T}:T{ s T}:T{ ru_utime: CPU seconds \- user mode T} T{ 1\&.451 T}:T{ s T}:T{ ru_stime: CPU seconds \- system T} T{ 164 T}:T{ # T}:T{ ru_minflt: page faults \- reclaim/soft (no I/O) T} T{ 0 T}:T{ # T}:T{ ru_majflt: page faults \- I/O T} T{ 0 T}:T{ # T}:T{ ru_nswap: process swapped out T} T{ 0 T}:T{ # T}:T{ ru_inblock: file blocks in T} T{ 2328 T}:T{ # T}:T{ ru_oublock: file blocks out T} T{ 64226 T}:T{ # T}:T{ ru_nvcsw: context switches, wait T} T{ 1 T}:T{ # T}:T{ ru_nivcsw: context switches, preempts T} T{ 0 T}:T{ # T}:T{ ru_nsignals: signals T} T{ 4308 T}:T{ # T}:T{ ru_maxrss: resident set size, kilobytes T} .TE .sp 1 The first two fields show the date (Modified Julian Day) and time (seconds and fraction past UTC midnight)\&. The ru_ tags are the names from the rusage struct\&. See man getrusage for details\&. (The NetBSD and FreeBSD man pages have more details\&.) The maxrss column is the high water mark since the process was started\&. The remaining fields show the values used since the last report\&. .RE .PP timingstats .RS 4 (Only available when the daemon is compiled with process time debugging support (\-\-enable\-debug\-timing \- costs performance)\&. Record processing time statistics for various selected code paths\&. .sp 53876 36\&.920 10\&.0\&.3\&.5 1 0\&.000014592 input processing delay .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Item T}:T{ Units T}:T{ Description T} T{ 53876 T}:T{ MJD T}:T{ date T} T{ 36\&.920 T}:T{ s T}:T{ time past midnight T} T{ 10\&.0\&.3\&.5 T}:T{ IP T}:T{ server address T} T{ 1 T}:T{ # T}:T{ event count T} T{ 0\&.000014592 T}:T{ s T}:T{ total time T} T{ \fImessage\fR T}:T{ text T}:T{ code path description (see source) T} .TE .sp 1 .RE .PP statsdir \fIdirectory_path\fR .RS 4 Indicates the full path of a directory where statistics files should be created (see below)\&. This keyword allows the (otherwise constant) \fIfilegen\fR filename prefix to be modified for file generation sets, which is useful for handling statistics logs\&. .RE .PP filegen \fIname\fR [file \fIfilename\fR] [type \fItypename\fR] [link | nolink] [enable | disable] .RS 4 Configures setting of generation file set name\&. Generation file sets provide a means for handling files that are continuously growing during the lifetime of a server\&. Server statistics are a typical example for such files\&. Generation file sets provide access to a set of files used to store the actual data\&. At any time at most one element of the set is being written to\&. The type given specifies when and how data will be directed to a new element of the set\&. This way, information stored in elements of a file set that are currently unused are available for administrative operations without the risk of disturbing the operation of ntpd\&. (Most important: they can be removed to free space for new data produced\&.) .sp Note that this command can be sent from the ntpq(1) program running at a remote location\&. .PP name .RS 4 This is the type of the statistics records, as shown in the \fIstatistics\fR command\&. .RE .PP file \fIfilename\fR .RS 4 This is the file name for the statistics records\&. Filenames of set members are built from three concatenated elements \fIprefix\fR, \fIfilename\fR and \fIsuffix\fR: .RE .RE .PP \fIprefix\fR .RS 4 This is a constant filename path\&. It is not subject to modifications via the \fIfilegen\fR option\&. It is defined by the server, usually specified as a compile\-time constant\&. It may, however, be configurable for individual file generation sets via other commands\&. For example, the prefix used with \fIloopstats\fR and \fIpeerstats\fR generation can be configured using the \fIstatsdir\fR option explained above\&. .RE .PP \fIfilename\fR .RS 4 This string is directly concatenated to the prefix mentioned above (no intervening \(oq/\(cq)\&. This can be modified using the file argument to the \fIfilegen\fR statement\&. No \&.\&. elements are allowed in this component to prevent filenames referring to parts outside the filesystem hierarchy denoted by \fIprefix\fR\&. .RE .PP \fIsuffix\fR .RS 4 This part is reflects individual elements of a file set\&. It is generated according to the type of a file set\&. .PP type \fItypename\fR .RS 4 A file generation set is characterized by its type\&. The following types are supported: .RE .RE .PP none .RS 4 The file set is actually a single plain file\&. .RE .PP pid .RS 4 One element of file set is used per incarnation of a ntpd server\&. This type does not perform any changes to file set members during runtime, however it provides an easy way of separating files belonging to different ntpd(8) server incarnations\&. The set member filename is built by appending a \(oq\&.\(cq to concatenated prefix and filename strings, and appending the decimal representation of the process ID of the ntpd(8) server process\&. .RE .PP day .RS 4 One file generation set element is created per day\&. A day is defined as the period between 00:00 and 24:00 UTC\&. The file set member suffix consists of a \(oq\&.\(cq and a day specification in the form \fIYYYYMMdd\fR\&. \fIYYYY\fR is a 4\-digit year number (e\&.g\&., 1992)\&. \fIMM\fR is a two digit month number\&. \fIdd\fR is a two digit day number\&. Thus, all information written at 10 December 1992 would end up in a file named \fIprefix\fR\fIfilename\fR\&.19921210\&. .RE .PP week .RS 4 Any file set member contains data related to a certain week of a year\&. The term week is defined by computing day\-of\-year modulo 7\&. Elements of such a file generation set are distinguished by appending the following suffix to the file set filename base: A dot, a 4\-digit year number, the letter \fIW\fR, and a 2\-digit week number\&. For example, information from January, 10th 1992 would end up in a file with suffix \fI1992W1\fR\&. .RE .PP month .RS 4 One generation file set element is generated per month\&. The file name suffix consists of a dot, a 4\-digit year number, and a 2\-digit month\&. .RE .PP year .RS 4 One generation file element is generated per year\&. The filename suffix consists of a dot and a 4 digit year number\&. .RE .PP age .RS 4 This type of file generation sets changes to a new element of the file set every 24 hours of server operation\&. The filename suffix consists of a dot, the letter \fIa\fR, and an 8\-digit number\&. This number is taken to be the number of seconds the server is running at the start of the corresponding 24\-hour period\&. .PP link | nolink .RS 4 It is convenient to be able to access the current element of a file generation set by a fixed name\&. This feature is enabled by specifying link and disabled using nolink\&. If link is specified, a hard link from the current file set element to a file without suffix is created\&. When there is already a file with this name and the number of links of this file is one, it is renamed appending a dot, the letter \fIC\fR, and the pid of the ntpd server process\&. When the number of links is greater than one, the file is unlinked\&. This allows the current file to be accessed by a constant name\&. .RE .PP enable | disable .RS 4 Enables or disables the recording function\&. Information is only written to a file generation by specifying enable; output is prevented by specifying disable\&. .RE .RE .SH "ACCESS CONTROL SUPPORT" .sp The ntpd(8) daemon implements a general purpose address/mask based restriction list\&. The list contains address/match entries sorted first by increasing address values and then by increasing mask values\&. A match occurs when the bitwise AND of the mask and the packet source address is equal to the bitwise AND of the mask and address in the list\&. The list is searched in order with the last match found defining the restriction flags associated with the entry\&. Additional information and examples can be found in the "Notes on Configuring NTP and Setting up a NTP Subnet" page (available as part of the HTML documentation)\&. .sp The restriction facility was implemented in conformance with the access policies for the original NSFnet backbone time servers\&. Later the facility was expanded to deflect cryptographic and clogging attacks\&. While this facility may be useful for keeping unwanted or broken or malicious clients from congesting innocent servers, it should not be considered an alternative to the NTP authentication facilities\&. Source address based restrictions are easily circumvented by a determined cracker\&. .sp Clients can be denied service because they are explicitly included in the restrict list created by the restrict command or implicitly as the result of cryptographic or rate limit violations\&. Cryptographic violations include certificate or identity verification failure; rate limit violations generally result from defective NTP implementations that send packets at abusive rates\&. Some violations cause denied service only for the offending packet, others cause denied service for a timed period and others cause the denied service for an indefinite period\&. When a client or network is denied access for an indefinite period, the only way at present to remove the restrictions is by restarting the server\&. .SS "The Kiss\-of\-Death Packet" .sp Ordinarily, packets denied service are simply dropped with no further action except incrementing statistics counters\&. Sometimes a more proactive response is needed, such as a server message that explicitly requests the client to stop sending and leave a message for the system operator\&. A special packet format has been created for this purpose called the "kiss\-of\-death" (KoD) packet\&. KoD packets have the leap bits set unsynchronized and stratum set to zero and the reference identifier field set to a four\-byte ASCII code\&. If the noserve or notrust flag of the matching restrict list entry is set, the code is "DENY"; if the limited flag is set and the rate limit is exceeded, the code is "RATE"\&. Finally, if a cryptographic violation occurs, the code is "CRYP"\&. .sp A client receiving a KoD performs a set of sanity checks to minimize security exposure, then updates the stratum and reference identifier peer variables, sets the access denied (BOGON4) bit in the peer flash variable and sends a message to the log\&. As long as the BOGON4 bit is set, the client will send no further packets to the server\&. The only way at present to recover from this condition is to restart the protocol at both the client and server\&. This happens automatically at the client when the association times out\&. It will happen at the server only if the server operator cooperates\&. .SH "ACCESS CONTROL COMMANDS" .PP discard [average \fIavg\fR] [minimum \fImin\fR] [monitor \fIprob\fR] .RS 4 Set the parameters of the limited facility which protects the server from client abuse\&. The average subcommand specifies the minimum average packet spacing, while the minimum subcommand specifies the minimum packet spacing\&. Packets that violate these minima are discarded and a kiss\-o\*(Aq\-death packet returned if enabled\&. The default minimum average and minimum are 5 and 2, respectively\&. The monitor subcommand specifies the probability of discard for packets that overflow the rate\-control window\&. The options are: .PP average \fIavg\fR .RS 4 Specify the minimum average interpacket spacing (minimum average headway time) in log2 s with default 3\&. .RE .PP minimum \fImin\fR .RS 4 Specify the minimum interpacket spacing (guard time) in seconds with default 2\&. .RE .PP monitor .RS 4 Specify the probability of being recorded for packets that overflow the MRU list size limit set by mru maxmem or mru maxdepth\&. This is a performance optimization for servers with aggregate arrivals of 1000 packets per second or more\&. .RE .RE .PP restrict \fIaddress\fR[/\fIcidr\fR] [mask \fImask\fR] [flag \&...] .RS 4 The \fIaddress\fR argument expressed in dotted\-quad (for IPv4) or :\-delimited (for IPv6) form is the address of a host or network\&. Alternatively, the \fIaddress\fR argument can be a valid host DNS name\&. The \fImask\fR argument expressed in IPv4 or IPv6 numeric address form defaults to all mask bits on, meaning that the \fIaddress\fR is treated as the address of an individual host\&. Instead of an explicit \fImask\fR the \fIaddress/cidr\fR may be specified in CIDR notation\&. A default entry (address 0\&.0\&.0\&.0, mask 0\&.0\&.0\&.0) is always included and is always the first entry in the list\&. Note that text string \fIdefault\fR, with no mask option, may be used to indicate the default entry\&. In the current implementation, \fIflag\fR always restricts access, i\&.e\&., an entry with no flags indicates that free access to the server is to be given\&. The flags are not orthogonal, in that more restrictive flags will often make less restrictive ones redundant\&. The flags can generally be classed into two categories, those which restrict time service and those which restrict informational queries and attempts to do run\-time reconfiguration of the server\&. One or more of the following flags may be specified: .PP flake .RS 4 Discard received NTP packets with probability 0\&.1; that is, on average drop one packet in ten\&. This is for testing and amusement\&. The name comes from Bob Braden\(cqs \fIflakeway\fR, which once did a similar thing for early Internet testing\&. .RE .PP ignore .RS 4 Deny packets of all kinds, including ntpq(1) queries\&. .RE .PP kod .RS 4 If this flag is set when an access violation occurs, a kiss\-o\*(Aq\-death (KoD) packet is sent\&. KoD packets are rate limited to no more than one per second\&. If another KoD packet occurs within one second after the last one, the packet is dropped\&. .RE .PP limited .RS 4 Deny service if the packet spacing violates the lower limits specified in the discard command\&. A history of clients is kept using the monitoring capability of ntpd(8)\&. Thus, monitoring is always active as long as there is a restriction entry with the limited flag\&. .RE .PP mssntp .RS 4 Enable Microsoft Windows MS\-SNTP authentication using Active Directory services\&. \fBNote: Potential users should be aware that these services involve a TCP connection to another process that could potentially block, denying services to other users\&. Therefore, this flag should be used only for a dedicated server with no clients other than MS\-SNTP\&.\fR .RE .PP nomodify .RS 4 Deny ntpq(1) queries which attempt to modify the state of the server (i\&.e\&., run time reconfiguration)\&. Queries which return information are permitted\&. .RE .PP noquery .RS 4 Deny ntpq(1) queries\&. Time service is not affected\&. .RE .PP nopeer .RS 4 Deny packets which would result in mobilizing a new association\&. This includes symmetric active packets when a configured association does not exist\&. That used to happen when the remote client used the peer command in its config file\&. We don\(cqt support that mode\&. It used to include \fIpool\fR servers but they now poke a hole in any restrictions\&. .RE .PP noserve .RS 4 Deny all packets except ntpq(1) and queries\&. .RE .PP notrust .RS 4 Deny service unless the packet is cryptographically authenticated\&. .RE .PP ntpport .RS 4 This is actually a match algorithm modifier, rather than a restriction flag\&. Its presence causes the restriction entry to be matched only if the source port in the packet is the standard NTP UDP port (123)\&. Both ntpport and non\-ntpport may be specified\&. The ntpport is considered more specific and is sorted later in the list\&. .RE .PP nomrulist .RS 4 Do not accept MRU\-list requests\&. These can be expensive to service, and may generate a high volume of response traffic\&. .RE .PP version .RS 4 Deny packets that do not match the current NTP version\&. .RE .sp Default restriction list entries with the flags ignore, interface, ntpport, for each of the local host\(cqs interface addresses are inserted into the table at startup to prevent the server from attempting to synchronize to its own time\&. A default entry is also always present, though if it is otherwise unconfigured; no flags are associated with the default entry (i\&.e\&., everything besides your own NTP server is unrestricted)\&. .RE .PP unrestrict \fIaddress\fR[/\fIcidr\fR] [mask \fImask\fR] [flag \&...] .RS 4 Like a restrict command, but turns off the specified flags rather than turning them on (expected to be useful mainly with ntpq :config)\&. An unrestrict with no flags specified removes any rule with matching address and mask\&. Use only on an address/mask or CIDR\-format address mentioned in a previous restrict statement\&. .RE .SH "AUTOMATIC NTP CONFIGURATION OPTIONS" .SS "Manycasting" .sp For a detailed description of manycast operation, see the "Server Discovery" page (available as part of the HTML documentation)\&. .SS "Manycast Options" .PP tos [ceiling \fIceiling\fR | floor \fIfloor\fR | minclock \fIminclock\fR | minsane \fIminsane\fR] .RS 4 This command affects the clock selection and clustering algorithms\&. It can be used to select the quality and quantity of peers used to synchronize the system clock and is most useful in manycast mode\&. The variables operate as follows: .PP ceiling \fIceiling\fR .RS 4 Peers with strata above \fIceiling\fR will be discarded if there are at least \fIminclock\fR peers remaining\&. This value defaults to 15, but can be changed to any number from 1 to 15\&. .RE .PP floor \fIfloor\fR .RS 4 Peers with strata below \fIfloor\fR will be discarded if there are at least \fIminclock\fR peers remaining\&. This value defaults to 1, but can be changed to any number from 1 to 15\&. .RE .PP minclock \fIminclock\fR .RS 4 The clustering algorithm repeatedly casts out outlier associations until no more than \fIminclock\fR associations remain\&. This value defaults to 3, but can be changed to any number from 1 to the number of configured sources\&. .RE .PP minsane \fIminsane\fR .RS 4 This is the minimum number of candidates available to the clock selection algorithm in order to produce one or more truechimers for the clustering algorithm\&. If fewer than this number are available, the clock is undisciplined and allowed to run free\&. The default is 1 for legacy purposes\&. However, according to principles of Byzantine agreement, \fIminsane\fR should be at least 4 in order to detect and discard a single falseticker\&. .RE .RE .SH "REFERENCE CLOCK SUPPORT" .sp For a detailed description of reference\-clock configuration, see the "Reference Clock Drivers" page (available as part of the HTML documentation provided in /usr/share/doc/ntp)\&. .SH "REFERENCE CLOCK COMMANDS" .PP refclock \fIdrivername\fR [unit \fIu\fR] [prefer] [subtype \fIint\fR] [mode \fIint\fR] [minpoll \fIint\fR] [maxpoll \fIint\fR] [time1 \fIsec\fR] [time2 \fIsec\fR] [stratum \fIint\fR] [refid \fIstring\fR] [path \fIfilename\fR] [ppspath \fIfilename\fR] [baud \fInumber\fR] [flag1 {0 | 1}] [flag2 {0 | 1}] [flag3 {0 | 1}] [flag4 {0 | 1}] .RS 4 This command is used to configure reference clocks\&. The required \fIdrivername\fR argument is the shortname of a driver type (e\&.g\&. shm, nmea, generic; see the link:refclock\&.html [Reference Clock Drivers] page for a full list\&. The options are interpreted as follows: .PP unit .RS 4 The 0\-origin unit number of the device\&. Modifies the devicename\&. If not specified, defaults to zero\&. .RE .PP prefer .RS 4 Marks the reference clock as preferred\&. All other things being equal, this host will be chosen for synchronization among a set of correctly operating hosts and clocks\&. See the "Mitigation Rules and the prefer Keyword" page (available as part of the HTML documentation provided in /usr/share/doc/ntp) for further information\&. .RE .PP subtype \fIint\fR .RS 4 Some drivers (notably the generic and jjy drivers) support multiple device types\&. This option selects among them in a driver\-dependent way\&. .RE .PP mode \fIint\fR .RS 4 Specifies a mode number which is interpreted in a device\-specific fashion\&. For instance, it selects a dialing protocol in the ACTS driver and a sentence mix in the nmea driver\&. .RE .PP minpoll \fIint\fR; maxpoll \fIint\fR .RS 4 These options specify the minimum and maximum polling interval for reference clock messages, as a power of 2 in seconds\&. For most directly connected reference clocks, both \fIminpoll\fR and \fImaxpoll\fR default to 6 (64 sec)\&. For modem reference clocks, \fIminpoll\fR defaults to 10 (17\&.1 min) and \fImaxpoll\fR defaults to 14 (4\&.5 hours)\&. The allowable range is 0 (1 sec) to 17 (36\&.4 hours) inclusive\&. .RE .PP time1 \fIsec\fR .RS 4 Specifies a constant to be added to the time offset produced by the driver, a fixed\-point decimal number in seconds\&. This is used as a calibration constant to adjust the nominal time offset of a particular clock to agree with an external standard, such as a precision PPS signal\&. It also provides a way to correct a systematic error or bias due to serial port or operating system latencies, different cable lengths or receiver internal delay\&. The specified offset is in addition to the propagation delay provided by other means, such as internal DIP switches\&. Where a calibration for an individual system and driver is available, an approximate correction is noted in the driver documentation pages\&. Note: in order to facilitate calibration when more than one radio clock or PPS signal is supported, a special calibration feature is available\&. It takes the form of an argument to the enable command described in "Miscellaneous Options" page and operates as described in the "Reference Clock Drivers" page\&. .RE .PP time2 \fIsecs\fR .RS 4 Specifies a fixed\-point decimal number in seconds, which is interpreted in a driver\-dependent way\&. See the descriptions of specific drivers in the "Reference Clock Drivers" page\&. .RE .PP stratum \fIint\fR .RS 4 Specifies the stratum number assigned to the driver, an integer between 0 and 15\&. This number overrides the default stratum number ordinarily assigned by the driver itself, usually zero\&. .RE .PP refid \fIstring\fR .RS 4 Specifies an ASCII string of from one to four characters which defines the reference identifier used by the driver\&. This string overrides the default identifier ordinarily assigned by the driver itself\&. .RE .PP path \fIfilepath\fR .RS 4 Overrides the default device location for this refclock\&. .RE .PP ppspath \fIfilepath\fR .RS 4 Overrides the default PPS device location (if any) for this driver\&. .RE .PP baud \fInumber\fR .RS 4 Overrides the defaults baud rate for this driver\&. .RE .PP flag1 {0 | 1}; flag2 {0 | 1}; flag3 {0 | 1}; flag4 {0 | 1} .RS 4 These four flags are used for customizing the clock driver\&. The interpretation of these values, and whether they are used at all, is a function of the particular clock driver\&. However, by convention flag4 is used to enable recording monitoring data to the \fIclockstats\fR file configured with the \fIfilegen\fR command\&. Further information on the \fIfilegen\fR command can be found in "Monitoring Options"\&. .RE .RE .SH "MISCELLANEOUS OPTIONS" .PP calldelay \fIdelay\fR .RS 4 This option controls the delay in seconds between the first and second packets sent in burst or iburst mode to allow additional time for a modem or ISDN call to complete\&. .RE .PP driftfile \fIdriftfile\fR .RS 4 This command specifies the complete path and name of the file used to record the frequency of the local clock oscillator\&. This is the same operation as the \-f command line option\&. If the file exists, it is read at startup in order to set the initial frequency and then updated once per hour with the current frequency computed by the daemon\&. If the file name is specified, but the file itself does not exist, ntpd starts with an initial frequency of zero and creates the file when writing it for the first time\&. If this command is not given, the daemon will always start with an initial frequency of zero\&. .sp The file format consists of a single line containing a single floating point number, which records the frequency offset measured in parts\-per\-million (PPM)\&. The file is updated by first writing the current drift value into a temporary file and then renaming this file to replace the old version\&. This implies that ntpd(8) must have write permission for the directory the drift file is located in, and that file system links, symbolic or otherwise, should be avoided\&. .RE .PP enable [auth | calibrate | kernel | monitor | ntp | stats]; disable [auth | calibrate | kernel | monitor | ntp | stats] .RS 4 Provides a way to enable or disable various server options\&. Flags not mentioned are unaffected\&. Note that all of these flags can be controlled remotely using the ntpq(1) utility program\&. .PP auth .RS 4 Enables the server to synchronize with unconfigured peers only if the peer has been correctly authenticated\&. The default for this flag is enable\&. .RE .PP calibrate .RS 4 Enables the calibrate feature for reference clocks\&. The default for this flag is disable\&. .RE .PP kernel .RS 4 Enables the kernel time discipline, if available\&. The default for this flag is enable if support is available, otherwise disable\&. .RE .PP monitor .RS 4 Enables the monitoring facility\&. See the ntpq(1) program and the monlist command for further information\&. The default for this flag is enable\&. .RE .PP ntp .RS 4 Enables time and frequency discipline\&. In effect, this switch opens and closes the feedback loop, which is useful for testing\&. The default for this flag is enable\&. .RE .PP stats .RS 4 Enables the statistics facility\&. See the "Monitoring Options" section for further information\&. The default for this flag is disable\&. .RE .RE .PP includefile \fIincludefile\fR .RS 4 This command allows additional configuration commands to be included from a separate file\&. Include files may be nested to a depth of five; upon reaching the end of any include file, command processing resumes in the previous configuration file\&. Relative pathnames are evaluated not with respect to the current working directory but with respect to the directory name of the last pushed file in the stack\&. This option is useful for sites that run ntpd(8) on multiple hosts, with (mostly) common options (e\&.g\&., a restriction list)\&. .RE .PP interface [listen | ignore | drop] [all | ipv4 | ipv6 | wildcard | \fIname\fR | \fIaddress\fR[/\fIprefixlen\fR]] .RS 4 This command controls which network addresses ntpd opens, and whether input is dropped without processing\&. The first parameter determines the action for addresses which match the second parameter\&. That parameter specifies a class of addresses, or a specific interface name, or an address\&. In the address case, \fIprefixlen\fR determines how many bits must match for this rule to apply\&. ignore prevents opening matching addresses, drop causes ntpd to open the address and drop all received packets without examination\&. Multiple interface commands can be used\&. The last rule which matches a particular address determines the action for it\&. interface commands are disabled if any of the \-I, \-\-interface,\-L, or \-\-novirtualips command\-line options are used\&. If none of those options are used and no interface actions are specified in the configuration file, all available network addresses are opened\&. The nic command is an alias for interface\&. .RE .PP leapfile \fIleapfile\fR .RS 4 This command loads the NIST leapseconds file and initializes the leapsecond values for the next leapsecond time, expiration time and TAI offset\&. The file can be obtained directly from NIST national time servers using ftp as the ASCII file pub/leap\-seconds\&.list\&. .sp The \fIleapfile\fR is scanned when ntpd processes the leapfile directive or when ntpd detects that \fIleapfile\fR has changed\&. ntpd checks once a day to see if the \fIleapfile\fR has changed\&. .RE .PP leapsmearinterval \fIinterval\fR .RS 4 This \fBexperimental\fR option is only available if ntpd was built with the \-\-enable\-leap\-smear option, It specifies the interval over which a leap second correction will be applied\&. Recommended values for this option are between 7200 (2 hours) and 86400 (24 hours)\&. DO NOT USE THIS OPTION ON PUBLIC\-ACCESS SERVERS! See http://bugs\&.ntp\&.org/2855 for more information\&. .RE .PP logconfig \fIconfigkeyword\fR .RS 4 This command controls the amount and type of output written to the system \fIsyslog(3)\fR facility or the alternate log file\&. By default, all output is turned on\&. All \fIconfigkeyword\fR keywords can be prefixed with \(oq=\(cq, \(oq+\(cq and \(oq\-\(cq, where \(oq=\(cq sets the syslog(3) priority mask, \(oq+\(cq adds and \(oq\-\(cq removes messages\&. syslog(3) messages can be controlled in four classes (clock,peer,sys and sync)\&. Within these classes four types of messages can be controlled: informational messages (info), event messages (events), statistics messages (statistics) and status messages (status)\&. .sp Configuration keywords are formed by concatenating the message class with the event class\&. The \fIall\fR prefix can be used instead of a message class\&. A message class may also be followed by the \fIall\fR keyword to enable/disable all messages of the respective message class\&. Thus, a minimal log configuration could look like this: .sp .if n \{\ .RS 4 .\} .nf logconfig =syncstatus +sysevents .fi .if n \{\ .RE .\} .sp This would just list the synchronizations state of ntpd(8) and the major system events\&. For a simple reference server, the following minimum message configuration could be useful: .sp .if n \{\ .RS 4 .\} .nf logconfig =syncall +clockall .fi .if n \{\ .RE .\} .sp This configuration will list all clock information and synchronization information\&. All other events and messages about peers, system events and so on is suppressed\&. .RE .PP logfile \fIlogfile\fR .RS 4 This command specifies the location of an alternate log file to be used instead of the default system \fIsyslog(3)\fR facility\&. This is the same operation as the \-l command line option\&. .RE .PP mru [maxdepth \fIcount\fR | maxmem \fIkilobytes\fR | mindepth \fIcount\fR | maxage \fIseconds\fR | minage \fIseconds\fR | initalloc \fIcount\fR | initmem \fIkilobytes\fR | incalloc \fIcount\fR | incmem \fIkilobytes\fR] .RS 4 Controls size limits of the monitoring facility Most Recently Used (MRU) list of client addresses, which is also used by the rate control facility\&. .PP maxdepth \fIcount\fR, maxmem \fIkilobytes\fR .RS 4 Equivalent upper limits on the size of the MRU list, in terms of entries or kilobytes\&. The actual limit will be up to incalloc entries or incmem kilobytes larger\&. As with all of the mru options offered in units of entries or kilobytes, if both maxdepth and maxmem are used, the last one used controls\&. The default is 1024 kilobytes\&. .RE .PP mindepth \fIcount\fR .RS 4 Lower limit on the MRU list size\&. When the MRU list has fewer than mindepth entries, existing entries are never removed to make room for newer ones, regardless of their age\&. The default is 600 entries\&. .RE .PP maxage \fIseconds\fR, minage \fIseconds\fR .RS 4 If an address is not in the list, there are several possible ways to find a slot for it\&. .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} If the list has fewer than mindepth entries, a slot is allocated from the free list\&. This is the normal case for a server without a lot of clients\&. If clients come and go, for example, laptops going between home and work, the default setup shows only the long term average\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} If the age of the oldest slot is greater than maxage, the oldest slot is recycled (default 3600 seconds)\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} If the freelist is not empty, a slot is allocated from the free list\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 4.\h'+01'\c .\} .el \{\ .sp -1 .IP " 4." 4.2 .\} If the freelist is empty but not full (see maxmem), more memory is allocated (see incmem) and a new slot is used\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 5.\h'+01'\c .\} .el \{\ .sp -1 .IP " 5." 4.2 .\} If the age of the oldest slot is more than minage, the oldest slot is recycled (default 64 seconds)\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 6.\h'+01'\c .\} .el \{\ .sp -1 .IP " 6." 4.2 .\} Otherwise, no slot is available\&. .RE .RE .PP initalloc \fIcount\fR, initmem \fIkilobytes\fR .RS 4 Initial memory allocation at the time the monitoring facility is first enabled, in terms of entries or kilobytes\&. The default is 4 kilobytes\&. .RE .PP incalloc \fIcount\fR, incmem \fIkilobytes\fR .RS 4 Size of additional memory allocations when growing the MRU list, in entries or kilobytes\&. The default is 4 kilobytes\&. .RE .RE .PP nonvolatile \fIthreshold\fR .RS 4 Specify the \fIthreshold\fR in seconds to write the frequency file, with default of 1e\-7 (0\&.1 PPM)\&. The frequency file is inspected each hour\&. If the difference between the current frequency and the last value written exceeds the threshold, the file is written and the threshold becomes the new threshold value\&. If the threshold is not exceeded, it is reduced by half\&. This is intended to reduce the frequency of unnecessary file writes for embedded systems with nonvolatile memory\&. .RE .PP phone \fIdial \&...\fR .RS 4 This command is used in conjunction with the ACTS modem driver (type modem) or the JJY driver (type jjy)\&. For ACTS, the arguments consist of a maximum of 10 telephone numbers used to dial USNO, NIST or European time services\&. For the jjy driver in modes 100\-180, the argument is one telephone number used to dial the telephone JJY service\&. The Hayes command ATDT is normally prepended to the number, which can contain other modem control codes as well\&. .RE .PP reset [allpeers] [auth] [ctl] [io] [mem] [sys] [timer] .RS 4 Reset one or more groups of counters maintained by ntpd and exposed by ntpq\&. .RE .PP setvar \fIvariable\fR [\fIdefault\fR] .RS 4 This command adds an additional system variable\&. These variables can be used to distribute additional information such as the access policy\&. If the variable of the form \fIname=value\fR is followed by the default keyword, the variable will be listed as part of the default system variables (ntpq(1) rv command)\&. These additional variables serve informational purposes only\&. They are not related to the protocol other that they can be listed\&. The known protocol variables will always override any variables defined via the setvar mechanism\&. There are three special variables that contain the names of all variable of the same group\&. The sys_var_list holds the names of all system variables\&. The peer_var_list holds the names of all peer variables and the clock_var_list holds the names of the reference clock variables\&. .RE .PP tinker [allan \fIallan\fR | dispersion \fIdispersion\fR | freq \fIfreq\fR | huffpuff \fIhuffpuff\fR | panic \fIpanic\fR | step \fIstep\fR | stepback \fIstepback\fR | stepfwd \fIstepfwd\fR | stepout \fIstepout\fR] .RS 4 This command can be used to alter several system variables in very exceptional circumstances\&. It should occur in the configuration file before any other configuration options\&. The default values of these variables have been carefully optimized for a wide range of network speeds and reliability expectations\&. In general, they interact in intricate ways that are hard to predict and some combinations can result in some very nasty behavior\&. Very rarely is it necessary to change the default values; but, some folks cannot resist twisting the knobs anyway and this command is for them\&. Emphasis added: twisters are on their own and can expect no help from the support group\&. .sp The variables operate as follows: .PP allan \fIallan\fR .RS 4 The argument becomes the new value for the minimum Allan intercept, which is a parameter of the PLL/FLL clock discipline algorithm\&. The value in log2 seconds defaults to 11 (2048 s), which is also the lower limit\&. .RE .PP dispersion \fIdispersion\fR .RS 4 The argument becomes the new value for the dispersion increase rate, normally \&.000015 s/s\&. .RE .PP freq \fIfreq\fR .RS 4 The argument becomes the initial value of the frequency offset in parts\-per\-million\&. This overrides the value in the frequency file, if present, and avoids the initial training state if it is not\&. .RE .PP huffpuff \fIhuffpuff\fR .RS 4 The argument becomes the new value for the experimental huff\-n\*(Aq\-puff filter span, which determines the most recent interval the algorithm will search for a minimum delay\&. The lower limit is 900 s (15 m), but a more reasonable value is 7200 (2 hours)\&. There is no default, since the filter is not enabled unless this command is given\&. .RE .PP panic \fIpanic\fR .RS 4 The argument is the panic threshold, normally 1000 s\&. If set to zero, the panic sanity check is disabled and a clock offset of any value will be accepted\&. .RE .PP step \fIstep\fR .RS 4 The argument is the step threshold, which by default is 0\&.128 sec\&. It can be set to any positive number in seconds\&. If set to zero, step adjustments will never occur\&. Note: The kernel time discipline is disabled if the step threshold is set to zero or greater than the default\&. .RE .PP stepback \fIstepback\fR .RS 4 The argument is the step threshold for the backward direction, which by default is 0\&.128 sec\&. It can be set to any positive number in seconds\&. If both the forward and backward step thresholds are set to zero, step adjustments will never occur\&. Note: The kernel time discipline is disabled if each direction of step threshold are either set to zero or greater than \&.5 second\&. .RE .PP stepfwd \fIstepfwd\fR .RS 4 As for stepback, but for the forward direction\&. .RE .PP stepout \fIstepout\fR .RS 4 The argument is the stepout timeout, which by default is 900 s\&. It can be set to any positive number in seconds\&. If set to zero, the stepout pulses will not be suppressed\&. .RE .RE .PP rlimit [memlock \fImegabytes\fR | stacksize \fI4kPages\fR | filenum \fIfiledescriptors\fR] .RS 4 .PP memlock \fImegabytes\fR .RS 4 Ignored for backward compatibility\&. .RE .PP stacksize \fI4kPages\fR .RS 4 Specifies the maximum size of the process stack on systems with the mlockall() function\&. Defaults to 50 4k pages\&. .RE .PP filenum \fIfiledescriptors\fR .RS 4 Specifies the maximum number of file descriptors ntpd may have open at once\&. Defaults to the system default\&. .RE .RE .SH "FILES" .PP /etc/ntp\&.conf .RS 4 the default name of the configuration file .RE .PP ntp\&.keys .RS 4 private keys .RE .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE .SH "SEE ALSO" .sp ntpd(8), ntpq(1)\&. .sp In addition to the manual pages provided, comprehensive documentation is available on the world wide web at https://www\&.ntpsec\&.org\&. A snapshot of this documentation is available in HTML format in /usr/share/doc/ntp\&. .sp David L\&. Mills, \fINetwork Time Protocol (Version 4)\fR, RFC 5905 .SH "BUGS" .sp The syntax checking is not picky; some combinations of ridiculous and even hilarious options and modes may not be detected\&. ntpsec-1.1.0+dfsg1/build/main/ntpd/ntp.keys.50000644000175000017500000000763313252367172020473 0ustar rlaagerrlaager'\" t .\" Title: ntp.keys .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTP\&.KEYS" "5" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntp.keys \- NTP symmetric key file format .SH "DESCRIPTION" .sp This document describes the format of an NTP symmetric key file\&. For a description of the use of this type of file, see the "Authentication Support" page of the Web documentation\&. .sp ntpd(8) reads its keys from a file specified using the \-k command line option or the \fIkeys\fR statement in the configuration file\&. While key number 0 is fixed by the NTP standard (as 56 zero bits) and may not be changed, one or more keys numbered between 1 and 65534 may be arbitrarily set in the keys file\&. .sp The key file uses the same comment conventions as the configuration file\&. Key entries use a fixed format of the form .sp .if n \{\ .RS 4 .\} .nf keyno type key .fi .if n \{\ .RE .\} .sp where keyno is a positive integer (between 1 and 65534), type is the message digest algorithm, and key is the key itself\&. .sp The file does not need to be sorted by keyno\&. .sp type can be any digest type supported by your OpenSSL package\&. Digests longer than 20 bytes will be trucnated\&. .sp You can probably get a list from man 1 dgst or openssl help\&. (As of Jan 2018, they lie\&. Be sure to try it\&. ntpd(8) will print an error on startup if a selected type isn\(cqt supported\&.) .sp The following types are widely supported: .sp .if n \{\ .RS 4 .\} .nf md5, sha1, ripemd160, sha224, sha256, sha384, sha512 .fi .if n \{\ .RE .\} .sp FIPS 140\-2, FIPS 180\-4, and/or FIPS 202 may restrict your choices\&. If it matters to you, check with your lawyer\&. (Let us know if you find a good reference\&.) .sp The key may be printable ASCII excluding "#" or hex encoded\&. Keys longer than 20 characters are assumed to be hex\&. The max length of a (possibly de\-hexified) key is 32 bytes\&. If you want to use an ASCII key longer than 20 bytes, you must hexify it\&. .sp Note that the keys used by the ntpq(1) programs are checked against passwords entered by hand, so it is generally appropriate to specify these keys in ASCII format\&. Or you can cut\-paste a hex string from your password manager\&. .SH "USAGE" .sp In order to use symmetric keys, the client side configuration file needs: .sp .if n \{\ .RS 4 .\} .nf keys trustedkey server \&.\&.\&. key .fi .if n \{\ .RE .\} .sp The server side needs: .sp .if n \{\ .RS 4 .\} .nf keys trustedkey .fi .if n \{\ .RE .\} .sp Note that the client and server key files must both contain identical copies of the line specified by keyno\&. .SH "FILES" .PP /etc/ntp\&.keys .RS 4 is a common location for the keys file .RE .sp Reminder: You have to keep it secret\&. .SH "SEE ALSO" .sp ntp\&.conf(5), ntpd(8), ntpq(1), ntpkeygen(8), ntpdig(1)\&. ntpsec-1.1.0+dfsg1/build/main/ntpfrob/0000775000175000017500000000000013252650651017325 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/build/main/ntpfrob/ntpfrob.80000644000175000017500000001156113252367173021076 0ustar rlaagerrlaager'\" t .\" Title: ntpfrob .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPFROB" "8" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpfrob \- frob the clock hardware .SH "SYNOPSIS" .sp ntpfrob [\-A] [\-b \fIbump\fR] [\-a \fItick\fR] [\-p \fIppsdev\fR] [\-c] [\-e] [\-r] [\-?] [\-h] .SH "DESCRIPTION" .sp The ntpfrob program frobs the local clock hardware\&. It collects several small diagnostic functions into a set that will differ depending on your platform and underlying system calls\&. Portions of it formerly traveled as tickadj and some undocumented small utilities\&. .SH "COMMAND LINE OPTIONS" .PP \-a \fItick\fR .RS 4 Set the kernel variable tick to the value \fItick\fR specifies\&. .RE .PP \-A .RS 4 Display the kernel variable tick\&. .RE .PP \-b \fIbump\fR .RS 4 Bump the clock by a specified number of microseconds\&. .RE .PP \-c .RS 4 Compute and display clock jitter\&. .RE .PP \-e .RS 4 Measure clock precision\&. .RE .PP \-j .RS 4 Report in self\-describing JSON\&. .RE .PP \-p \fIppsdev\fR .RS 4 Look for PPS pulses on a specified device .RE .PP \-r .RS 4 Raw mode\&. Only applies to the jitter mode, and means the raw clock samples should be emitted to stdout for postanalysis\&. .RE .PP \-? .RS 4 Print usage and exit\&. .RE .PP \-h .RS 4 Print usage and exit\&. .RE .SH "USAGE" .sp Documentation for some of these functions is scanty; this is a problem inherited from ancient days along with their code\&. If you suspect you may need to use them, reading the source code may be wise\&. If you believe you understand the code in more detail than any of these descriptions, please explain it to the NTPsec maintainers\&. .sp Normally this tool reports in an eyeball\-friendly unstructured text format\&. With the \-j option (where applicable) it reports JSON records\&. Note that the \-j option should be given before any mode option\&. .sp The reporting formats of this tool should be considered unstable; they may change as diagnostics are added or improved\&. JSON reports will be kept forward\-compatible through changes\&. .SS "Clock tick adjustment" .sp The \-A function reads your clock\(cqs tick rate in microseconds\&. The \-a function sets it\&. Both rely on the adjtimex(2) system call\&. This mode finishes by reporting the tick value and (if available) the tick adjustment value\&. .sp The \-j option is applicable to this mode\&. .sp Tweaking your tick rate is almost never necessary on hardware new enough to have a fully POSIX\&.1\-2001\-conformant Unix\&. .SS "Clock bump" .sp Sometimes it is diagnostically interesting to perturb your clock in order to watch how ntpd responds and makes corrections\&. This option does that\&. .SS "Clock jitter measurement" .sp The \-c option can be used to determine the timing jitter due to the operating system in a gettimeofday() call\&. For most systems the dominant contribution to the jitter budget is the period of the hardware interrupt, usually in the range between 10 us and 1 ms\&. For those systems with microsecond counters the jitter is dominated only by the operating system\&. .sp The \-j option is applicable to this mode\&. With the \-r option, write the raw unsorted clock samples to standard output for post\-analysis\&. All but the last \-j or \-r option before the \-c mode flag is ignored\&. .SS "Pulse\-per\-second check" .sp The \-p option shows whether the PPS\-API (RFC 2783 kernel PPS interface) finds PPS on a specified device\&. .SS "Measure clock precision" .sp The \-e option measure the resolution of the system clock, watching how the current time changes as we read it repeatedly\&. .sp The \-j option is applicable to this mode\&. .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command invocation was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntptime/0000775000175000017500000000000013252650651017333 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/build/main/ntptime/ntptime.80000644000175000017500000000605313252367174021113 0ustar rlaagerrlaager'\" t .\" Title: ntptime .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPTIME" "8" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntptime \- read and set kernel time variables .SH "SYNOPSIS" .sp .nf ntptime [\-chr] [\-e \fIest_error\fR] [\-f \fIfrequency\fR] [\-j] [\-m \fImax_error\fR] [\-o \fIoffset\fR] [\-s \fIstatus\fR] [\-t \fItime_constant\fR] .fi .SH "DESCRIPTION" .sp Checks the kernel configuration for the NTP user interface syscalls ntp_gettime() and ntp_adjtime()\&. If present, the current timekeeping data are displayed\&. If not, a disappointment is displayed\&. See the kernel page file in the HTML documentation in distribution for further details\&. .sp A similar display can be obtained using the ntpq program and kerninfo command\&. .sp This program is useful only with kernels featuring the ntp_adjtime(2) system call or local equivalent, as described in the A Kernel Model for Precision Timekeeping page, .SH "OPTIONS" .PP \-c .RS 4 Display the execution time of ntptime itself\&. .RE .PP \-e \fIest_error\fR .RS 4 Specify estimated error, in microseconds\&. .RE .PP \-f \fIfrequency\fR .RS 4 Specify frequency offset, in parts per million\&. .RE .PP \-h .RS 4 Display help information\&. .RE .PP \-j .RS 4 Report in JSON rather than plain text\&. .RE .PP \-m \fImax_error\fR .RS 4 Specify max possible errors, in microseconds\&. .RE .PP \-o \fIoffset\fR .RS 4 Specify clock offset, in microseconds\&. .RE .PP \-r .RS 4 Display Unix and NTP times in raw format\&. .RE .PP \-s \fIstatus\fR .RS 4 Specify clock status\&. Better know what you are doing\&. .RE .PP \-t \fItime_constant\fR .RS 4 Specify time constant, an integer in the range 0\-10\&. .RE .SH "BUGS" .sp The format for modes and status in the JSON report is less than ideal\&. .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command invocation was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/0000775000175000017500000000000013252650651020036 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/build/main/ntpclients/ntploggps.10000644000175000017500000000744613252367202022143 0ustar rlaagerrlaager'\" t .\" Title: ntploggps .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPLOGGPS" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntploggps \- log gpsd data .SH "SYNOPSIS" .sp .nf ntploggps [\-h] [\-l LOGFILE] [\-o] [\-w WAIT] [\-v] [\-V] .fi .sp .nf \-h, \-\-help show this help message and exit \-l LOGFILE, \-\-logfile LOGFILE append log data to LOGFILE instead of stdout \-o, \-\-once log one line, then exit \-w WAIT, \-\-wait WAIT wait WAIT seconds after each log line, default 5 \-v, \-\-verbose be verbose \-V, \-\-version show program\*(Aqs version number and exit .fi .SH "DESCRIPTION" .sp ntploggps connects to a local gpsd daemon and logs the number of satellites in use and the Time Dilution of Precision (TDOP)\&. ntploggps can run as any user, no special privileges are required\&. .sp The default is to write the data to stdout about once every 5 seconds\&. The log file looks like: .sp .if n \{\ .RS 4 .\} .nf # Time Device TDOP nSat 1483668619\&.0 /dev/ttyS0 0\&.820000 7 1483668624\&.0 /dev/ttyS0 0\&.820000 7 1483668629\&.0 /dev/ttyS0 0\&.820000 7 .fi .if n \{\ .RE .\} .sp Time is the POSIX time of when the log line is written\&. .sp Device is the GPS device the data came from\&. .sp TDOP is the Time Dilution of Precision as reported by the GPS\&. Some GPS always output a static TDOP\&. .sp nSat is the number of satellites in use\&. .SH "OPTIONS" .PP \-h, \-\-help .RS 4 Displays usage information and exits\&. .RE .PP \-l LOGFILE, \-\-logfile LOGFILE .RS 4 Append log data to LOGFILE instead of stdout .RE .PP \-o, \-\-once .RS 4 Log one line, then exit\&. .RE .PP \-v, \-\-verbose .RS 4 Be verbose .RE .PP \-w WAIT, \-\-wait WAIT .RS 4 Wait WAIT seconds after each log line\&. The default is 5 seconds\&. This is just the minimum wait time\&. gpsd may be reporting data at a much slower interval\&. .RE .PP \-V, \-\-version .RS 4 show program\(cqs version number and exit .RE .SH "USAGE" .PP ntploggps .RS 4 This the simplest use of this program\&. It can be used to check the status of the local gpsd daemon\&. .RE .PP ntploggps \-l /var/log/ntpstats/gpsd \-w 60 & .RS 4 This will continuously log the gpsd data in the background to the file /var/log/ntpstats/gpsd\&. Only log every 60 seconds\&. .RE .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE .SH "AUTHORS" .sp Gary E\&. Miller .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpwait.80000644000175000017500000000605713252367211021620 0ustar rlaagerrlaager'\" t .\" Title: ntpwait .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPWAIT" "8" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpwait \- wait for ntpd to stabilize the system clock .SH "SYNOPSIS" .sp ntpwait [\-v] [\-n \fInumber\fR] [\-s \fIsecs\fR] .SH "DESCRIPTION" .sp The ntpwait program blocks until ntpd is in synchronized state\&. This can be useful at boot time, to delay the boot sequence until after "ntpd \-g" has set the time\&. .sp ntpwait will send at most \fInumber\fR queries to ntpd(8), sleeping for \fIsecs\fR seconds after each status return that says ntpd(8) has not yet produced a synchronized and stable system clock\&. .sp ntpwait will do this quietly, unless the \-v flag is provided\&. .SH "OPTIONS" .PP \-n \fInumber\fR, \-\-tries=\fInumber\fR .RS 4 Number of times to check ntpd\&. This option takes an integer number as its argument\&. The default \fInumber\fR for this option is: 100\&. .sp The maximum number of times we will check \fIntpd\fR to see if it has been able to synchronize and stabilize the system clock\&. .RE .PP \-s \fIsecs\-between\-tries\fR, \-\-sleep=\fIsecs\-between\-tries\fR .RS 4 How long to sleep between tries\&. This option takes an integer number as its argument\&. The default \fIsecs\-between\-tries\fR for this option is: 6\&. .sp We will sleep for \fIsecs\-between\-tries\fR after each query of \fIntpd\fR that returns "the time is not yet stable"\&. .RE .PP \-v, \-\-verbose .RS 4 Be verbose\&. .sp By default, ntpwait is silent\&. With this option, ntpwait will provide status information, including time to synchronization in seconds\&. .RE .SH "BUGS" .sp If you are running Python at a version older than 3\&.3, the report on time to synchronization may be thrown off by NTP clock stepping\&. .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 .RS 4 Successful program execution\&. .RE .PP 1 .RS 4 The operation failed or the command syntax was not valid\&. .RE .PP 2 .RS 4 Operation was interrupted by signal\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpleapfetch.80000644000175000017500000001113413252367210022576 0ustar rlaagerrlaager'\" t .\" Title: ntpleapfetch .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPLEAPFETCH" "8" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpleapfetch \- leap\-seconds file manager/updater .SH "SYNOPSIS" .sp .nf ntpleapfetch [\-46] [\-c \fIcommand\fR] [\-e \fIexpiry\fR] [\-f \fIconfig\fR] [\-F] [\-h] [\-i \fIinterval\fR] [\-l] [L] [\-p {4|6}] [\-P \fIlogfacility\fR] [\-q] [\-r \fIretries\fR] [\-s \fIsource\-url\fR] [\-t \fIpath\fR] [\-v] [\-z \fIpath\fR] [\-Z] [\fIleapfile\fR] .fi .SH "DESCRIPTION" .sp ntpleapfetch will validate the file currently on the local system and if necessary, updates leap\-second definition file\&. .sp Ordinarily, the file is found using the "leapfile" directive in \fIntp\&.conf(5)\fR\&. However, an alternate location can be specified on the command line\&. .sp If the file does not exist, is not valid, has expired, or is expiring soon, a new copy will be downloaded\&. If the new copy validates, it is installed and NTP is (optionally) restarted\&. .sp If the current file is acceptable, no download or restart occurs\&. .sp \-c can also be used to invoke another script to perform administrative functions, e\&.g\&. to copy the file to other local systems\&. .sp This can be run as a cron job\&. As the file is rarely updated, and leap seconds are announced at least one month in advance (usually longer), it need not be run more frequently than about once every three weeks\&. .sp For cron\-friendly behavior, define CRONJOB=1 in the crontab\&. .SH "OPTIONS" .PP \-4 .RS 4 Use only IPv4 addresses for DNS name resolution\&. This option must not appear in combination with any of the following options: \-6\&. .RE .PP \-6 .RS 4 Use only IPv6 addresses for DNS name resolution\&. This option must not appear in combination with any of the following options: \-4\&. .RE .PP \-c .RS 4 Command to restart ntpd after installing a new file\&. Without such a command\&. ntpd checks the file daily\&. .RE .PP \-e \fIexpiry\fR .RS 4 Refresh the leapfile this long before it expires\&. .sp Specify how long before expiration the file is to be refreshed\&. Units are required, e\&.g\&. "\-e 60 days"\&. Note that larger values imply more frequent refreshes\&. .RE .PP \-f \fIconfig\fR .RS 4 Location of the ntp\&.conf file\&. .sp Specify location of ntp\&.conf (used to make sure leapfile directive is present and to default leapfile) /etc/ntp\&.conf .RE .PP \-F .RS 4 Force update even if current file is OK and not close to expiring\&. .RE .PP \-i \fIinterval\fR .RS 4 Number of minutes between retries\&. .RE .PP \-h .RS 4 Display inline help\&. .RE .PP \-l .RS 4 Use syslog for output (Implied if CRONJOB is set)\&. .RE .PP \-L .RS 4 Don\(cqt use syslog for output .RE .PP \-p {4|6} .RS 4 Prefer IPv4 or IPv6 (as specified) addresses, but use either\&. .RE .PP \-P \fIlogfacility\fR .RS 4 Specify the syslog facility for logging\&. .RE .PP \-q .RS 4 Only report errors to stdout\&. .RE .PP \-r \fIretries\fR .RS 4 Number of retries\&. .RE .PP \-s \fIsource\-url\fR .RS 4 The URL of the master copy of the leapseconds file\&. .RE .PP \-t \fIpath\fR .RS 4 Name of temporary file used in validation\&. .RE .PP \-v .RS 4 Verbose output\&. .RE .PP \-z \fIpath\fR .RS 4 Specify path for utilities\&. .RE .PP \-Z .RS 4 Use system path only\&. .RE .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE .PP 2 .RS 4 Internal error \- restart command failed\&. .RE .SH "AUTHORS" .sp Timothe Litt ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntptrace.10000644000175000017500000000706713252367206021751 0ustar rlaagerrlaager'\" t .\" Title: ntptrace .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPTRACE" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntptrace \- trace peers of an NTP server .SH "SYNOPSIS" .sp .nf ntptrace [\-n |\-\-numeric] [\-m \fInumber\fR | \-\-max\-hosts=\fInumber\fR] [\-r \fIhost\fR | \-\-host=\fIremote\fR] \fIhostname\fR .fi .SH "DESCRIPTION" .sp ntptrace is a python script that uses the ntpq utility program to follow the chain of NTP servers from a given host back to the primary time source\&. .sp For ntptrace to work properly, each of these servers must implement the NTP Control and Monitoring Protocol specified in RFC 1305 and enable NTP Mode 6 control packets\&. Nowadays it is usual for public timeservers to disable Mode 6 queries, so this script is unlikely to be very useful unless you have a collection of specially\-configured timeservers on your LAN\&. .sp If given no arguments, ntptrace starts with localhost\&. Here is an example of the output from ntptrace: .sp .if n \{\ .RS 4 .\} .nf % ntptrace localhost: stratum 4, offset 0\&.0019529, synch distance 0\&.144135 server2ozo\&.com: stratum 2, offset 0\&.0124263, synch distance 0\&.115784 usndh\&.edu: stratum 1, offset 0\&.0019298, synch distance 0\&.011993, refid \*(AqGPS\*(Aq .fi .if n \{\ .RE .\} .sp On each line, the fields are (left to right): the host name, the host stratum, the time offset between that host and the local host (as measured by ntptrace; this is why it is not always zero for "localhost"), the host synchronization distance, and (only for stratum\-1 servers) the reference clock ID\&. All times are given in seconds\&. Note that the stratum is the server hop count to the primary source, while the synchronization distance is the estimated error relative to the primary source\&. These terms are precisely defined in RFC 1305\&. .SH "OPTIONS" .PP \-n, \-\-numeric .RS 4 Print IP addresses instead of hostnames\&. .sp Output hosts as dotted\-quad numeric format rather than converting to the canonical host names\&. .RE .PP \-m number, \-\-max\-hosts=\fInumber\fR .RS 4 Maximum number of peers to trace\&. This option takes an integer number as its argument\&. The default \fInumber\fR for this option is 99\&. .RE .PP \-r string, \-\-host=\fIstring\fR .RS 4 Trace a single remote host\&. .RE .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the invocation was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpviz.10000644000175000017500000002275613252367207021466 0ustar rlaagerrlaager'\" t .\" Title: ntpviz .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPVIZ" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpviz \- make visualizations of offset, jitter, etc\&. from stats file data .SH "SYNOPSIS" .sp .nf ntpviz [\-d LOGDIR] [\-g] [\-n name] [\-p DAYS] [\-s starttime] [\-e endtime] [\-o OUTDIR] [\-c | \-\-clip] [\-w SIZE | \-\-width SIZE] [\-\-all\-peer\-jitters | \-\-all\-peer\-offsets | \-\-local\-error | \-\-local\-freq\-temps | \-\-local\-gps | \-\-local\-jitter | \-\-local\-offset | \-\-local\-offset\-histogram | \-\-local\-offset\-multiplot | \-\-local\-stability | \-\-local\-temps | \-\-peer\-jitters=hosts | \-\-peer\-offsets=hosts] [\-D DLVL | \-\-debug DLVL] [\-N | \-\-nice] [\-V | \-\-version] [@OPTIONFILE] .fi .SH "DESCRIPTION" .sp This utility analyzes files in an NTP log directory and generates statistical plots from them\&. It can output either PNG images or the gnuplot programs to generate them to standard output\&. In its default mode it generates an HTML directory containing an index page and either (a) all plots, for a single statfiles directory, or (b) a subset of comparative plots for multiple directories\&. .SS "Basic Options" .PP \-d LOGDIR or \-\-datadir LOGDIR .RS 4 Specifies one or more logfile directories to examine; the default is the single directory /var/log/ntpstats\&. .RE .PP \-g or \-\-generate .RS 4 Run plot through gnuplot to make png\&. The default is to generate gnuplot programs\&. .RE .PP \-o OUTDIR or \-\-outdir OUTDIR .RS 4 Set the directory for all output to be OUTDIR\&. If OUTDIR does not exist it is created\&. The default OUTDIR is \fIntpgraphs\fR\&. Warning: existing PNG files and index\&.html in the output directory will be clobbered\&. .RE .PP \-n STR or \-\-name STR .RS 4 Set the sitename shown in the plot title, and is effective only for the single\-directory case\&. The default is the basename of the log directory\&. .RE .PP \-p DAYS or \-\-period DAYS .RS 4 The default DAYS is for the period of 7 days\&. DAYS can be a floating point number, so "\-p 0\&.5" plots 12 hours\&. DAYS is ignored if \-s and \-e are given\&. .RE .PP \-e TIME or \-\-endtime TIME .RS 4 With \-s and \-e you set the start and end times\&. TIME is either numeric POSIX time (seconds since the start of the epoch) or ISO 8601\-style timestamps (yyyy\-mmm\-ddThh:mm:ss)\&. The default end time is the last logfile entry timestamp\&. The default start time is computed as the end time minus DAYS\&. Alternatively you can specify either \-s or \-e (but not both) and use \-p to set the plot period in days\&. .RE .PP \-s TIME or \-\-starttime TIME .RS 4 See \-e and \-p\&. .RE .PP \-c or \-\-clip .RS 4 Normally all the data is plotted\&. This option limits the range of the plots to the data between 1% and 99%\&. This is useful for ignoring a few spikes in the data\&. .RE .PP \-w SIZE or \-\-width SIZE .RS 4 Set the size of the output plots\&. SIZE can be one of \fIs\fR, \fIm\fR, or \fIl\fR\&. \fIs\fR is for browser on small screens (1024x768)\&. \fIm\fR for medium screens (1388x768)\&. \fIl\fR for large screens (1920x1080)\&. \fIm\fR is the default\&. .RE .PP \-D DLVL or \-\-debug DLVL .RS 4 Set the debug level to DLVL\&. Larger DLVL leads to more verbosity\&. 0 is the default, quiet except for all ERRORs and some WARNINGs\&. 1 shows some environment info and basic program progress\&. 2 leaves the plot file in the system temp directory\&. 9 is painfully verbose\&. 9 also includes profile data\&. .RE .PP \-N or \-\-nice .RS 4 Run at the lowest possible priority\&. Only available on UNIX\&. .RE .sp \-V or \-\-version: Print program version and exit\&. .SS "Individual Plots" .sp The plot options choose what graph is generated; invoke only one\&. By default, the gnuplot for the graph is reported; with \-g you get the rendered PNG\&. .sp The following plots are available: .PP \-\-all\-peer\-jitters .RS 4 Report all peer jitters\&. This is a different option name from \-\-peer\-jitters only because of a limitation in the Python standard library\&. .RE .PP \-\-all\-peer\-offsets .RS 4 Report all peer offsets\&. This is a different option name from \-\-peer\-offsets only because of a minor limitation in the Python standard library\&. .RE .PP \-\-local\-error .RS 4 Clock frequency offset from the loop statistics (field 4) .RE .PP \-\-local\-freq\-temps .RS 4 Plot local frequency offset and local temperatures\&. This plot is only generated if there is a log file named temps in the log file directory\&. .RE .PP \-\-local\-jitter .RS 4 Clock time\-jitter plot from the loop statistics (field 5)\&. .RE .PP \-\-local\-gps .RS 4 Plot GPS Time Dilution of Precision (TDOP) and Number of Sats Used (nSats)\&. This plot is only generated if there is a log file named gpsd in the log file directory\&. .RE .PP \-\-local\-offset .RS 4 Clock time and clock frequency offsets from the loop statistics (fields 3 and 4)\&. .RE .PP \-\-local\-offset\-histogram .RS 4 Frequency histogram of distinct loopstats time offset values (field 3)\&. .RE .PP \-\-local\-offset\-multiplot .RS 4 Plot comparative local offsets for multiple directories\&. .RE .PP \-\-local\-temps .RS 4 Plot local temperatures\&. This plot is only generated if there is a log file named temps in the log file directory\&. .RE .PP \-\-local\-stability .RS 4 RMS frequency\-jitter plot from the loop statistics (field 6)\&. This is deviation from a root\-mean\-square extrapolation of the moving average of past frequency readings\&. .RE .PP \-\-peer\-jitters=host1[,host2\&...] .RS 4 Peer jitter from local clock time, from peerstats (field 7) A comma\-separated list of peer names must follow\&. It is a fatal error for any of these names not to appear in peerstats\&. .RE .PP \-\-peer\-offsets=host1[,host2\&...] .RS 4 Peer offset from local clock time from peerstats (field 4)\&. A comma\-separated list of peer names or IP addresses must follow\&. It is a fatal error for any of these names not to appear in peerstats\&. .RE .sp If no individual plot is specified, all plots and an index HTML page are generated into the output directory\&. .sp When an index is generated, ntpviz will look for two files in the output directory\&. Neither of these files need be present, and the \fIheader\fR and \fIfooter\fR files may contain arbitrary HTML\&. .sp The first file is named \fIheader\fR\&. The contents of that file will be added almost at the top of the body on the generated index page\&. .sp This is the place to put links to other web pages, or headline notes\&. .sp The second file is named \fIfooter\fR\&. The contents of that file will be added almost at the bottom of the body on the generated index\&. .sp It is suggested that notes on the server be included in the footer file: OS, versions, CPU speed, etc\&. You may also put links there\&. .sp The code includes various sanity checks and will bail out with a message to standard error on, for example, missing logfile data required for a plot\&. .SS "Argument File" .sp Any command line options can also be placed into a command file\&. The command file name (OPTIONFILE) is prefixed on the command line with an atsign (@)\&. .sp Each line in the command file should contain just one option\&. Multiple options per line are allowed, but discouraged\&. Blank lines are allowed\&. .sp Comments can be added, and start with an octothorpe (#)\&. Comments may appear on a new line, or trailing, after the # \&. .sp When an option takes a parameter, the option and parameter must be separated by an equal sign (=) or a space\&. .sp These two ways to invoke ntpviz are equivalent: .sp .if n \{\ .RS 4 .\} .nf $ ntpviz \-\-period 1 \-\-outdir day .fi .if n \{\ .RE .\} .sp .if n \{\ .RS 4 .\} .nf $ cat day/config \-\-period=1 \-\-outdir=day $ ntpviz @day/config .fi .if n \{\ .RE .\} .SH "REQUIREMENTS" .sp Python and gnuplot\&. The plots will look better with the \fIliberation\fR font package installed\&. .SH "AUTHORS" .sp Eric S\&. Raymond, Gary E\&. Miller, and Daniel Drown\&. The gnuplot in this package is largely based on templates in Daniel Drown\(cqs \fIchrony\-graph\fR project: https://github\&.com/ddrown/chrony\-graph/tree/ntpd .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 .RS 4 Successful program execution\&. .RE .PP 1 .RS 4 The operation failed, usually due to a missing logfile required for a plot\&. .RE .PP 2 .RS 4 Illegal command\-line option\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntplogtemp.10000644000175000017500000000742413252367207022320 0ustar rlaagerrlaager'\" t .\" Title: ntplogtemp .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPLOGTEMP" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntplogtemp \- log temperature data .SH "SYNOPSIS" .sp .nf ntplogtemp [\-h] [\-l LOGFILE] [\-o] [\-w WAIT] [\-v] [\-V] .fi .sp .nf \-h, \-\-help show this help message and exit \-l LOGFILE, \-\-logfile LOGFILE append log data to LOGFILE instead of stdout \-q, \-\-quiet be quiet \-o, \-\-once log one line, then exit \-w WAIT, \-\-wait WAIT wait WAIT seconds after each log line, default 60 \-v, \-\-verbose be verbose \-V, \-\-version show program\*(Aqs version number and exit .fi .SH "DESCRIPTION" .sp ntplogtemp gathers temperature readings across a system\&. The standard user is ntpuser and should have permissions to execute lm_sensors and smartctl\&. .sp The default is to write the data to stdout once every 60 seconds\&. The log file looks like: .sp .if n \{\ .RS 4 .\} .nf # time, sensor, value 1485816568 ZONE0 39\&.0 1485816568 ZONE1 20\&.0 1485816568 ZONE2 42\&.0 1485816568 ZONE3 0\&.0 .fi .if n \{\ .RE .\} .sp Time is the POSIX time of when the log line is written\&. .sp Sensor is the sensor the temperature reading was obtained from\&. .sp Value is the temperature reading in celsius\&. .SH "OPTIONS" .PP \-h, \-\-help .RS 4 Displays usage information and exits\&. .RE .PP \-l LOGFILE, \-\-logfile LOGFILE .RS 4 Append log data to LOGFILE instead of stdout .RE .PP \-q, \-\-quiet .RS 4 Quiet output, will not display errors .RE .PP \-o, \-\-once .RS 4 Log the data once, then exit .RE .PP \-v, \-\-verbose .RS 4 Be verbose .RE .PP \-w WAIT, \-\-wait WAIT .RS 4 Wait WAIT seconds after each log line\&. The default is 60 seconds\&. The minimum wait time is 5 seconds\&. .RE .PP \-V, \-\-version .RS 4 show program\(cqs version number and exit .RE .SH "USAGE" .PP ntplogtemp .RS 4 This the simplest use of this program\&. It can be used to check the temperature readings of Zones, CPU, and disk drives every minute\&. .RE .PP ntplogtemp \-l /var/log/ntpstats/temperature \-w 60 & .RS 4 This will continuously log the temperature data in the background to the file /var/log/ntpstats/temperature\&. Only log every 60 seconds\&. .RE .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE .SH "AUTHORS" .sp Gary E\&. Miller .sp Keane S\&. Wolter .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpsweep.10000644000175000017500000000605213252367205021766 0ustar rlaagerrlaager'\" t .\" Title: ntpsweep .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPSWEEP" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpsweep \- print various information about given NTP servers .SH "SYNOPSIS" .sp ntpsweep [\-l \fIhost\fR]\&... [\-p] [\-m \fInumber\fR] [\-s \fIprefix\fR] [\-h \fIstring\fR] [hostfile\&...] .SH "DESCRIPTION" .sp ntpsweep prints per host the NTP stratum level, the clock offset in seconds, the daemon version, the operating system and the processor\&. Optionally recursing through all peers\&. .sp If no hosts are specified, ntpsweep reports on localhost\&. .sp ntpsweep relies on ntpq and Mode 6 queries to probe servers\&. This depends on the remote host\(cqs \fIrestrict\fR configuration allowing queries\&. Nowadays effectively all public hosts set \fInoquery\fR, so this script is unlikely to be useful unless you have multiple specially\- configured timeservers on a LAN\&. .SH "OPTIONS" .PP \-l string, \-\-host\-list=\fIstring\fR .RS 4 Host to execute actions on\&. This option may appear an unlimited number of times\&. .sp Use this option to specify the host on which this script operates\&. May appear multiple times\&. Multiple hosts may be specified, delimited by commas\&. .RE .PP \-p, \-\-peers .RS 4 Recursively list all peers a host synchronizes to\&. .RE .PP \-m number, \-\-maxlevel=\fInumber\fR .RS 4 Traverse peers up to this level (4 is a reasonable number)\&. This option takes an integer number as its argument\&. .RE .PP \-s string, \-\-strip=\fIstring\fR .RS 4 Strip this string from hostnames\&. .RE .PP \-h string, \-\-host=\fIstring\fR .RS 4 Specify a single host\&. Deprecated option for backwards compatibility\&. .RE .sp If hostfiles are specified, they are treated as lists of hostnames to be swept, one per line\&. .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpsnmpd.80000644000175000017500000001174413252367212021775 0ustar rlaagerrlaager'\" t .\" Title: ntpsnmpd .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPSNMPD" "8" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpsnmpd \- NTP Simple Network Management Protcol sub\-sgent .SH "SYNOPSIS" .sp ntpsnmpd [\-nxdDlhV] [ntp host] .SH "DESCRIPTION" .sp [experimental] .sp ntpsnmpd is an AgentX Simple Network Management Protocol sub\-agent\&. .sp It functions as a bridge between the Mode 6 protocol used for Network Time Protocol monitoring, and an SNMP master agent\&. The user must have running NTP and SNMP daemons for ntpsnmpd to work\&. .SH "OPTIONS" .PP \-c filename, \-\-configfile=filename .RS 4 Alternate path for the configuration file, /etc/ntpsnmpd\&.conf by default .RE .PP \-n, \-\-nofork .RS 4 Do not fork .RE .PP \-x, \-\-master\-addr .RS 4 Specify address for connecting to the master agent .RE .PP \-d, \-\-debug .RS 4 Increase debugging level by 1\&. .RE .PP \-D num, \-\-set\-debug\-level=num .RS 4 The debug level is set to the following integer argument\&. .RE .PP \-l filename, \-\-logfile=filename .RS 4 Log debugging output to the specified file\&. .RE .PP \-h, \-\-help .RS 4 Print a usage message summarizing options end exit\&. .RE .PP \-V, \-\-version .RS 4 Print the version string and exit\&. .RE .SH "CONFIGURATION FILES" .sp .if n \{\ .RS 4 .\} .nf Note that filenames in the options should be enclosed in quotes\&. This is a temporary measure that will not be the case in future versions\&. .fi .if n \{\ .RE .\} .SS "/etc/ntpsnmpd\&.conf" .PP master\-addr \fIaddr\fR .RS 4 The address of the SNMP master agent to connect to\&. Can be either a named socket or an IP address / hostname and port number\&. .RE .PP ntp\-addr \fIaddr\fR .RS 4 The address of the NTP daemon to monitor\&. .RE .PP logfile \fIfilename\fR .RS 4 Log debugging output to the specified file\&. .RE .PP loglevel \fIlevel\fR .RS 4 Threshold for log messages\&. Equivilent to \-D .RE .SS "/var/ntpsnmpd/notify\&.conf" .sp .if n \{\ .RS 4 .\} .nf This file is used to store the state of options that can be set by writing to certain OIDs\&. Any comments will be overwritten by ntpsnmpd upon write\&. .fi .if n \{\ .RE .\} .PP notify\-mode\-change [ True | False ] .RS 4 Enable notifications on ntpd mode change\&. .RE .PP notify\-stratum\-change [ True | False ] .RS 4 Enable notifications on NTP stratum change\&. .RE .PP notify\-syspeer\-change [ True | False ] .RS 4 Enable notifications when ntpd selects a new system peer\&. .RE .PP notify\-add\-association [ True | False ] .RS 4 Enable notifications when ntpd adds a new peer\&. .RE .PP notify\-rm\-association [ True | False ] .RS 4 Enable notifications when ntpd removes a peer\&. .RE .PP notify\-leap\-announced [ True | False ] .RS 4 Enable notifications on leap second announcement\&. .RE .PP notify\-heartbeat [ True | False ] .RS 4 Send heartbeat notification\&. .RE .PP heartbeat\-interval \fIinterval\fR .RS 4 Frequency of heartbeat notification in integer seconds\&. .RE .SH "KNOWN LIMITATIONS" .sp This program is still experimental in nature\&. Parts of the AgentX standard are not yet fully implemented, logging not fleshed out yet, some options missing, etc\&. .SH "KNOWN DEVIATIONS FROM RFC 5907" .sp The ntpEntStatusEntityUptime OID specifies a bizzare time formant\&. This does not match what the SNMP tools expect to see from a TimeTicks variable\&. Instead the daemon returns a normal TimeTicks value\&. .sp The ntpEntNotifConfigChanged trap is left unimplemented\&. This is because there currently is no way to implement what it wants, and it demands alerts regarding changes that are not part of NTP\(cqs jurisdiction\&. .sp The ntpEntTimeResolution OID currently uses data from the sys_fuzz variable as the base for it\(cqs calculations\&. It is by no means certain that this is the correct choice, and the returned data should be taken with scepticism\&. .sp The ntpEntStatPktModeTable is unimplemented due to not currently having a data source\&. .SH "EXIT STATUS" .sp Always returns 0\&. ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpkeygen.80000644000175000017500000001143613252367210022132 0ustar rlaagerrlaager'\" t .\" Title: ntpkeygen .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPKEYGEN" "8" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpkeygen \- create and manage NTP host keys .SH "SYNOPSIS" .sp .nf ntpkeygen [\-M] .fi .SH "DESCRIPTION" .sp This program generates a file containing keys that can be used in NTP\(cqs symmetric key cryptography\&. .sp The program produces a file containing ten pseudo\-random printable ASCII strings suitable for the MD5 message digest algorithm\&. It also produces an additional ten hex\-encoded random bit strings suitable for the SHA1 and other message digest algorithms\&. .sp The keys file must be distributed and stored using secure means beyond the scope of NTP itself\&. The keys can also be used as passwords for the ntpq utility program\&. .SH "COMMAND LINE OPTIONS" .PP \-M, \-\-md5key .RS 4 Dummy option for backward compatibility in old scripts\&. This program always runs in \-M mode\&. .RE .SH "RUNNING THE PROGRAM" .sp The simplest way to run the ntpkeygen program is logged in directly as root\&. The recommended procedure is to change to the keys directory, usually /etc/ntp/, then run the program\&. Then chown the output file to ntp:ntp\&. It should be mode 400\&. .if n \{\ .sp .\} .RS 4 .it 1 an-trap .nr an-no-space-flag 1 .nr an-break-flag 1 .br .ps +1 \fBWarning\fR .ps -1 .br .sp ntpkeygen uses the system randomness source\&. On a POSIX system this is usually /dev/urandom\&. Immediately after a reboot, on any OS, there may not be sufficient entropy available for this program to perform well\&. Do not run this program from any startup scripts\&. Only run this program on an active host with a lot of available entropy\&. .sp .5v .RE .SH "KEY FILE ACCESS AND LOCATION" .sp File names begin with the prefix \fIntpkey\fR and end with the postfix \fIhostname\&.filestamp\fR, where \fIhostname\fR is the owner name, usually the string returned by the Unix gethostname() routine, and \fIfilestamp\fR is the NTP seconds when the file was generated, in decimal digits\&. .sp ntpkeygen also makes a soft link from ntp\&.keys to the generated file\&. ntp\&.keys is the normal file used in ntp\&.conf\&. .SH "RANDOM SEED FILE" .sp All key generation schemes must have means to randomize the entropy seed used to initialize the internal pseudo\-random number generator used by the library routines\&. .sp It is important to understand that entropy must be evolved for each generation, for otherwise the random number sequence would be predictable\&. Various means dependent on external events, such as keystroke intervals, can be used to do this and some systems have built\-in entropy sources\&. .sp This implementation uses Python\(cqs random\&.SystemRandom class, which relies on os\&.urandom()\&. The security of os\&.urandom() is improved in Python 3\&.5+\&. .SH "CRYPTOGRAPHIC DATA FILES" .sp Since the file contains private shared keys, it should be visible only to root or ntp\&. .sp In order to use a shared key, the line to be used must also be setup on the target server\&. .sp This file is also used to authenticate remote configuration commands used by the ntpq(1) utility\&. .sp Comments may appear in the file, and are preceded with the # character\&. .sp Following any headers the keys are entered one per line in the format: .TS allbox tab(:); ltB ltB. T{ Field T}:T{ Meaning T} .T& lt lt lt lt lt lt. T{ .sp keyno T}:T{ .sp Positive integer in the range 1\-65,535 T} T{ .sp type T}:T{ .sp MD5 or SHA1, type of key T} T{ .sp key T}:T{ .sp the actual key, printable ASCII or hex T} .TE .sp 1 .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpmon.10000644000175000017500000002107213252367204021432 0ustar rlaagerrlaager'\" t .\" Title: ntpmon .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPMON" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpmon \- real\-time NTP status monitor .SH "SYNOPSIS" .sp ntpmon [hostname] .SH "DESCRIPTION" .sp This program is a real\-time status monitor for NTP\&. It presents the same information as the \fIpeers\fR, \fImrulist\fR, \fIrv\fR, and \fIcv\fR commands of ntpq(1), but using a split\-window display that also includes a status summary bar, and updates at intervals guaranteed to show status changes almost as soon as they occur\&. .sp (Specifically, the display begins updating once per second and adjusts itself to poll at twice the frequency of the shortest polling interval reported in the last peers response\&.) .sp The status bar includes the version string of the server being watched, the (local) time at which it was last updated, and the current query interval\&. .sp There is a detail\-display mode that dumps full information about a single selected peer in a tabular format that makes it relatively easy to see changing values\&. However, note that a default\-sized terminal emulator window (usually 25 lines) doesn\(cqt have enough room for the clock variables portion\&. The only fix for this is to resize your terminal\&. .sp ^C cleanly terminates the program\&. Any keystroke will trigger a poll and update\&. A few single\-keystroke commands are also interpreted as commands\&. .sp If no hostname is specified on the command line, localhost is monitored\&. .sp Here\(cqs a breakdown of the peers display in the top window: .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Variable T}:T{ .sp Description T} T{ .sp tally T}:T{ .sp single\-character code indicating current value of the select field of the peer status word T} T{ .sp remote T}:T{ .sp host name (or IP number) of peer T} T{ .sp refid T}:T{ .sp association ID or kiss code T} T{ .sp st T}:T{ .sp stratum T} T{ .sp t T}:T{ .sp u: unicast or manycast client, l: local (reference clock), s: symmetric (peer), server, B: broadcast server, T} T{ .sp when T}:T{ .sp sec/min/hr since last received packet T} T{ .sp poll T}:T{ .sp poll interval (log2 s) T} T{ .sp reach T}:T{ .sp reach shift register (octal) T} T{ .sp delay T}:T{ .sp roundtrip delay T} T{ .sp offset T}:T{ .sp offset of server relative to this host T} T{ .sp jitter T}:T{ .sp jitter T} .TE .sp 1 .sp The tally code is one of the following: .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Code T}:T{ .sp Description T} T{ .sp T}:T{ .sp discarded as not valid T} T{ .sp x T}:T{ .sp discarded by intersection algorithm T} T{ .sp \&. T}:T{ .sp discarded by table overflow (not used) T} T{ .sp \- T}:T{ .sp discarded by the cluster algorithm T} T{ .sp + T}:T{ .sp included by the combine algorithm T} T{ .sp # T}:T{ .sp backup (more than tos maxclock sources) T} T{ .sp * T}:T{ .sp system peer T} T{ .sp o T}:T{ .sp PPS peer (when the prefer peer is valid) T} .TE .sp 1 .sp And the MRU list in the bottom window: .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Column T}:T{ .sp Description T} T{ .sp lstint T}:T{ .sp Interval in s between the receipt of the most recent packet from this address and the completion of the retrieval of the MRU list by ntpq\&. T} T{ .sp avgint T}:T{ .sp Average interval in s between packets from this address\&. T} T{ .sp rstr T}:T{ .sp Restriction flags associated with this address\&. Most are copied unchanged from the matching restrict command, however 0x400 (kod) and 0x20 (limited) flags are cleared unless the last packet from this address triggered a rate control response\&. T} T{ .sp r T}:T{ .sp Rate control indicator, either a period, L or K for no rate control response, rate limiting by discarding, or rate limiting with a KoD response, respectively\&. T} T{ .sp m T}:T{ .sp Packet mode\&. T} T{ .sp v T}:T{ .sp Packet version number\&. T} T{ .sp count T}:T{ .sp Packets received from this address\&. T} T{ .sp rport T}:T{ .sp Source port of last packet from this address\&. T} T{ .sp remote address T}:T{ .sp DNS name, numeric address, or address followed by claimed DNS name which could not be verified in parentheses\&. T} .TE .sp 1 .SH "COMMANDS" .PP a .RS 4 Change peer display to apeers mode, showing association IDs\&. .RE .PP d .RS 4 Toggle detail mode (some peer will be reverse\-video highlighted when on)\&. .RE .PP h .RS 4 Display help screen .RE .PP j .RS 4 Select next peer (in select mode); arrow down also works\&. .RE .PP k .RS 4 Select previous peer (in select mode); arrow up also works\&. .RE .PP m .RS 4 Toggle MRUlist\-only mode; suppresses peer display when on\&. .RE .PP n .RS 4 Toggle display of hostnames vs\&. IP addresses (default is hostnames)\&. .RE .PP o .RS 4 Change peer display to opeers mode, showing destination address\&. .RE .PP p .RS 4 Change peer display to default mode, showing refid\&. .RE .PP q .RS 4 Cleanly terminate the program\&. .RE .PP s .RS 4 Toggle display of only reachable hosts (default is all hosts)\&. .RE .PP u .RS 4 Toggle display of units for time values\&. (default is off) .RE .PP w .RS 4 Toggle wide mode\&. .RE .PP x .RS 4 Cleanly terminate the program\&. .RE .PP .RS 4 Rotate through a/n/o/p display modes\&. .RE .PP + .RS 4 Increase debugging level\&. Output goes to ntpmon\&.log .RE .PP \- .RS 4 Decrease debugging level\&. .RE .PP ? .RS 4 Display help screen .RE .SH "OPTIONS" .PP \-n .RS 4 Show IP addresses (vs\&. hostnames) .RE .PP \-u .RS 4 Show units .RE .PP \-V .RS 4 Display version and exit\&. .RE .SH "KNOWN BUGS" .sp When run in a terminal that does not allow UTF\-8 ntpmon will downgrade its unit display to a non\-unicode version\&. ntpmon has to interact with the curses and locale libraries, which prevents it from forcing the use of UTF\-8\&. .sp When querying a version of ntpd older than NTPsec 0\&.9\&.6, ntpmon will appear to hang when monitoring hosts with extremely long MRU lists \- in particular, public pool hosts\&. Correct behavior requires a Mode 6 protocol extension not yet present in those versions\&. .sp Even with this extension, monitoring a sufficiently high\-traffic server sometimes fails\&. .sp When using the \-u option, very old xterms may fail to render μ correctly\&. If this happens, be sure your xterm is started with the \-u8 option, or the \fIutf8\fR resource\*(Aq, and that your console font contains the UTF\-8 &mu character\&. Also confirm your LANG environment variable is set to a UTF\-8 language, like this: "export LANG=en_US\&.utf8"\&. .sp Timestamp interpretation in this program is likely to fail in flaky ways if the local system clock has not already been approximately synchronized to UTC\&. Querying a server based in a different NTP era than the current one is especially likely to fail\&. .sp This program will behave in apparently buggy and only semi\-predictable ways when fetching MRU lists from \fIany\fR server with sufficiently high traffic\&. .sp The problem is fundamental\&. The Mode 6 protocol can\(cqt ship (and your client cannot accept) MRU records as fast as the daemon accepts incoming traffic\&. Under these circumstances, the daemon will repeatedly fail to ship an entire report, leading to long hangs as your client repeatedly re\-sends the request\&. Eventually the Mode 6 client library will throw an error indicating that a maximum number of restarts has been exceeded\&. .sp To avoid this problem, avoid monitoring over links that don\(cqt have enough capacity to handle the monitored server\(cqs \fIentire\fR NTP load\&. .SH "EXIT STATUS" .sp Always returns 0\&. ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpdig.10000644000175000017500000002570013252367203021405 0ustar rlaagerrlaager'\" t .\" Title: ntpdig .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPDIG" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpdig \- standard Simple Network Time Protocol client program .SH "SYNOPSIS" .sp .nf ntpdig [\-\-help | \-?] [\-4 | \-6] [\-a keynum] [\-p samples] [\-c] [\-d] [\-D debug\-level] [\-g delay] [\-j] [\-k keyfile] [\-l logfile] [\-M steplimit] [\-S] [\-s] [\-\-wait] [\-\-no\-wait] [\-\-version] [address\&...]+ .fi .SH "DESCRIPTION" .sp ntpdig can be used as an SNTP client to query a NTP or SNTP server and either display the time or set the local system\(cqs time (given suitable privilege)\&. It can be run as an interactive command or from a \fIcron\fR job\&. NTP (the Network Time Protocol) and SNTP (the Simple Network Time Protocol) are defined and described by RFC 5905\&. .sp The default is to write the estimated correct local date and time (i\&.e\&. not UTC) to the standard output in a format like: .sp .if n \{\ .RS 4 .\} .nf 2015\-10\-14 13:46:04\&.534916 (+0500) \-0\&.000007 +/\- 0\&.084075 localhost 127\&.0\&.0\&.1 s2 no\-leap .fi .if n \{\ .RE .\} .sp where the (+0500) means that to get to UTC from the reported local time one must add 5 hours and 0 minutes, the \-0\&.000007 indicates the local clock is 0\&.000007 seconds ahead of correct time (so 0\&.000007 seconds must be subtracted from the local clock to get it to be correct)\&. Note that the number of decimals printed for this value will change based on the reported precision of the server\&. +/\- 0\&.084075 is the reported \fIsynchronization\fR \fIdistance\fR (in seconds), which represents the maximum error due to all causes\&. If the server does not report valid data needed to calculate the synchronization distance, this will be reported as +/\- ?\&. .sp If the \fIhost\fR is different from the \fIIP\fR, both will be displayed\&. Otherwise, only the \fIIP\fR is displayed\&. Finally, the \fIstratum\fR of the host is reported and the leap indicator is decoded and displayed\&. .sp With the \-j (JSON) option, the output format becomes a self\-describing JSON record: .sp .if n \{\ .RS 4 .\} .nf {"time":"2015\-10\-14T13:46:04\&.534916+0500", "offset":\-0\&.000007,"precision":"0\&.084075", "host":"localhost",ip:"127\&.0\&.0\&.1", "stratum":2,"leap":"noleap","adjusted":false} .fi .if n \{\ .RE .\} .sp In the JSON format, time is in ISO 8601 format; precision is the synch distance, with an unknown synch distance being reported as 0\&. Host and IP are always emitted even if duplicate\&. The "adjusted" boolean reports whether ntpdig determined it should have slewed or stepped the time\&. This may be shown as true even if time was not actually adjusted due to lack of clock\-setting privileges\&. .SH "OPTIONS" .PP \-h, \-\-help .RS 4 Displays usage information and exits\&. .RE .PP \-4, \-\-ipv4 .RS 4 Force IPv4 DNS name resolution\&. This option must not appear in combination with any of the following options: ipv6\&. .sp Force DNS resolution of the following host names on the command line to the IPv4 namespace\&. .RE .PP \-6, \-\-ipv6 .RS 4 Force IPv6 DNS name resolution\&. This option must not appear in combination with any of the following options: ipv4\&. .sp Force DNS resolution of the following host names on the command line to the IPv6 namespace\&. .RE .PP \-a \fIauth\-keynumber\fR, \-\-authentication=\fIauth\-keynumber\fR .RS 4 Enable authentication with the key \fIauth\-keynumber\fR\&. This option takes an integer number as its argument\&. Enable authentication using the key specified in this option\(cqs argument\&. The argument of this option is the \fIkeyid\fR, a number specified in the \fIkeyfile\fR as this key\(cqs identifier\&. See the \fIkeyfile\fR option (\-k) for more details\&. .RE .PP \-c \fIhost\-name\fR, \-\-concurrent=\fIhost\-name\fR .RS 4 Concurrently query all IPs returned for host\-name\&. This option may appear an unlimited number of times\&. .sp Requests from an NTP "client" to a "server" should never be sent more rapidly than one every 2 seconds\&. By default, any IPs returned as part of a DNS lookup are assumed to be for a single instance of ntpd, and therefore ntpdig will send queries to these IPs one after another, with a 2\-second gap in between each query\&. .sp The \-c or \-\-concurrent flag says that any IPs returned for the DNS lookup of the supplied host\-name are on different machines, so we can send concurrent queries\&. This is appropriate when using a server pool\&. .RE .PP \-d, \-\-debug\-level .RS 4 Increase debug verbosity level\&. This option may appear an unlimited number of times\&. .RE .PP \-D \fInumber\fR, \-\-set\-debug\-level=\fInumber\fR .RS 4 Set the debug verbosity level\&. This option may appear an unlimited number of times\&. This option takes an integer number as its argument\&. .RE .PP \-g \fImilliseconds\fR, \-\-gap=\fImilliseconds\fR .RS 4 The gap (in milliseconds) between time requests\&. This option takes an integer number as its argument\&. The default \fImilliseconds\fR for this option is 50\&. .sp Separate the queries we send out by the specified number of milliseconds\&. A larger \fIdelay\fR reduces the query load on the time sources, at the cost of increasing the time to receive a valid response if the first source attempted is slow or unreachable\&. .RE .PP \-j .RS 4 Output to stdout in JSON, suppressing syslog messages\&. .RE .PP \-k \fIfile\-name\fR, \-\-keyfile=\fIfile\-name\fR .RS 4 Look in this file for the key specified with \-a\&. .sp This option specifies the keyfile\&. ntpdig will search for the key specified with \-a keyno in this file\&. See \fIntp\&.keys(5)\fR for more information\&. .RE .PP \-l \fIfile\-name\fR, \-\-logfile=\fIfile\-name\fR .RS 4 Log to specified logfile\&. .sp This option causes the client to write log messages to the specified \fIlogfile\fR\&. .RE .PP \-M \fInumber\fR, \-\-steplimit=\fInumber\fR .RS 4 Adjustments less than \fIsteplimit\fR milliseconds will be slewed\&. This option takes an integer number as its argument\&. The value of \fInumber\fR is constrained to being greater than or equal to 0, .sp If the time adjustment is less than \fIsteplimit\fR milliseconds, slew the amount using \fIadjtime(2)\fR\&. Otherwise, step the correction using \fIclock_settime()\fR or local equivalent\&. The default value is 0, which means all adjustments will be stepped\&. This is a feature, as different situations demand different values\&. .RE .PP \-p, \-\-samples .RS 4 Number of samples to take (default 1)\&. The best one (chosen by, among other criteria, sync distance) is selected for display or use\&. .RE .PP \-S, \-\-step .RS 4 By default, ntpdig displays the clock offset but does not attempt to correct it\&. This option enables offset correction by stepping, that is, directly setting the clock to the corrected time\&. This typically requires ntpdig be invoked as the superuser ("root")\&. .RE .PP \-s, \-\-slew .RS 4 By default, ntpdig displays the clock offset but does not attempt to correct it\&. This option enables offset correction by slewing using adjtime(), which changes the rate of the clock for a period long enough to accomplish the required offset (phase) correction\&. This typically requires ntpdig be invoked as the superuser ("root")\&. .RE .PP \-t \fIseconds\fR, \-\-timeout=\fIseconds\fR .RS 4 The number of seconds to wait for responses\&. This option takes an integer number as its argument\&. The default \fIseconds\fR for this option is: 5\&. .sp When waiting for a reply, ntpdig will wait the number of seconds specified before giving up\&. The default should be more than enough for a unicast response\&. If ntpdig is only waiting for a broadcast response a longer timeout is likely needed\&. .RE .PP \-\-wait, \-\-no\-wait .RS 4 Wait for pending replies (if not setting the time)\&. The \fIno\-wait\fR form will disable the option\&. This option is enabled by default\&. .sp If we are not setting the time, wait for all pending responses\&. .RE .PP \-\-version .RS 4 Output version of program and exit\&. .RE .SH "USAGE" .PP ntpdig ntpserver\&.somewhere .RS 4 is the simplest use of this program and can be run as an unprivileged command to check the current time and error in the local clock\&. .RE .PP ntpdig \-S \-s \-M 128 ntpserver\&.somewhere .RS 4 With suitable privilege, run as a command or from a \fIcron\fR(8) job, ntpdig \-Ss \-M 128 ntpserver\&.somewhere will request the time from the server, and if that server reports that it is synchronized then if the offset adjustment is less than 128 milliseconds the correction will be slewed, and if the correction is more than 128 milliseconds the correction will be stepped\&. .RE .PP ntpdig \-S ntpserver\&.somewhere .RS 4 With suitable privilege, run as a command or from a \fIcron\fR(8) job, ntpdig \-S ntpserver\&.somewhere will set (step) the local clock from a synchronized specified server, like the ntpdate utility from older NTP implementations\&. .RE .SH "COMPATIBILITY" .sp Not all options of the NTP classic sntp(1) utility have been retained; don\(cqt expect \-b, \-K, \-o, \-r, \-w, or \-W to work\&. These have either been removed for security reasons or discarded as unnecessary in a modern environment\&. .sp This version does not log to syslog\&. Pipe standard output and standard error to logger(1) if you want this behavior\&. .sp The synchronization\-distance formula used in this version is slightly different from that found in sntp(1), tracking the newer formula used in ntpd(8)\&. Expect offset computations to match but synch\-distances not to\&. .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE .SH "AUTHORS" .sp Johannes Maximilian Kuehn, Harlan Stenn, Dave Hart\&. .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE ntpsec-1.1.0+dfsg1/build/main/ntpclients/ntpq.10000644000175000017500000007127513252367205021114 0ustar rlaagerrlaager'\" t .\" Title: ntpq .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.78.1 .\" Date: 03/15/2018 .\" Manual: NTPsec .\" Source: NTPsec 1.1.0 .\" Language: English .\" .TH "NTPQ" "1" "03/15/2018" "NTPsec 1\&.1\&.0" "NTPsec" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" ntpq \- standard NTP query program .SH "SYNOPSIS" .sp ntpq [\-46adhinpkwWu] [\-c \fIcommand\fR] [\fIhost\fR] [\&...] .SH "DESCRIPTION" .sp The ntpq utility program is used to monitor NTP daemon ntpd operations and determine performance\&. It uses the standard NTP mode 6 control message formats defined in Appendix B of the NTPv3 specification RFC 1305\&. The same formats are used in NTPv4, although some of the variable names have changed and new ones added\&. The description on this page is for the NTPv4 variables\&. .sp The program can be run either in interactive mode or controlled using command line arguments\&. Requests to read and write arbitrary variables can be assembled, with raw and pretty\-printed output options being available\&. It can also obtain and print a list of peers in a common format by sending multiple queries to the server\&. .sp If one or more request options is included on the command line when ntpq is executed, each of the requests will be sent to the NTP servers running on each of the hosts given as command line arguments, or on localhost by default\&. If no request options are given, ntpq will attempt to read commands from the standard input and execute these on the NTP server running on the first host given on the command line, again defaulting to localhost when no other host is specified\&. ntpq will prompt for commands if the standard input is a terminal device\&. .sp ntpq uses NTP mode 6 packets to communicate with the NTP server, and hence can be used to query any compatible server on the network which permits it\&. Note that since NTP is a UDP protocol this communication will be somewhat unreliable, especially over large distances in terms of network topology\&. ntpq makes one attempt to retransmit requests, and will time requests out if the remote host is not heard from within a suitable timeout time\&. .sp Note that in contexts where a host name is expected, a \-4 qualifier preceding the host name forces DNS resolution to the IPv4 namespace, while a \-6 qualifier forces DNS resolution to the IPv6 namespace\&. .sp For examples and usage, see the NTP Debugging Techniques page\&. .sp For a simpler near\-real\-time monitor, see ntpmon(1)\&. .SH "OPTIONS" .sp Command line options are described following\&. Specifying the command line options \-c or \-p will cause the specified query (queries) to be sent to the indicated host(s) immediately\&. Otherwise, ntpq will attempt to read interactive format commands from the standard input\&. .PP \-4, \-\-ipv4 .RS 4 Force DNS resolution of following host names on the command line to the IPv4 namespace\&. .RE .PP \-6, \-\-ipv6 .RS 4 Force DNS resolution of following host names on the command line to the IPv6 namespace\&. .RE .PP \-a num, \-\-authentication=num .RS 4 Enable authentication with the numbered key\&. .RE .PP \-c cmd, \-\-command=cmd .RS 4 The following argument is interpreted as an interactive format command and is added to the list of commands to be executed on the specified host(s)\&. Multiple \-c options may be given\&. .RE .PP \-d, \-\-debug .RS 4 Increase debugging level by 1\&. .RE .PP \-D num, \-\-set\-debug\-level=num .RS 4 The debug level is set to the following integer argument\&. .RE .PP \-l filename, \-\-logfile=filename .RS 4 Log debugging output to the specified file\&. .RE .PP \-h, \-\-help .RS 4 Print a usage message summarizing options end exit\&. .RE .PP \-n, \-\-numeric .RS 4 Output all host addresses in numeric format rather than converting to the canonical host names\&. You may get hostnames anyway for peers in initialization phase, before DNS has resolved the peer name\&. .RE .PP \-p, \-\-peers .RS 4 Print a list of the peers known to the server as well as a summary of their state\&. This is equivalent to the peers interactive command\&. .RE .PP \-k filename, \-\-keyfile=filename .RS 4 Specify a keyfile\&. ntpq will look in this file for the key specified with \-a\&. .RE .PP \-V, \-\-version .RS 4 Print the version string and exit\&. .RE .PP \-w, \-\-wide .RS 4 Wide mode: if the host name or IP Address doesn\(cqt fit, write the full name/address and indent the next line so columns line up\&. The default truncates the name or address\&. .RE .PP \-W num, \-\-width=num .RS 4 Force the terminal width\&. Only relevant for composition of the peers display\&. .RE .PP \-u, \-\-units .RS 4 Display timing information with units\&. .RE .SH "INTERNAL COMMANDS" .sp Interactive format commands consist of a keyword followed by zero to four arguments\&. Only enough characters of the full keyword to uniquely identify the command need be typed\&. The output of a command is normally sent to the standard output, but optionally the output of individual commands may be sent to a file by appending a >, followed by a file name, to the command line\&. A number of interactive format commands are executed entirely within the ntpq program itself and do not result in NTP mode\-6 requests being sent to a server\&. These are described following\&. .PP ? [\fIcommand_keyword\fR], help [\fIcommand_keyword\fR] .RS 4 A ? by itself will print a list of all the command keywords known to ntpq\&. A ? followed by a command keyword will print function and usage information about the command\&. .RE .PP addvars \fIname\fR [ = value] [\&...]; rmvars \fIname\fR [\&...]; clearvars .RS 4 The arguments to this command consist of a list of items of the form name = value, where the = value is ignored, and can be omitted in read requests\&. ntpq maintains an internal list in which data to be included in control messages can be assembled, and sent using the readlist and writelist commands described below\&. The addvars command allows variables and optional values to be added to the list\&. If more than one variable is to be added, the list should be comma\-separated and not contain white space\&. The rmvars command can be used to remove individual variables from the list, while the clearlist command removes all variables from the list\&. .RE .PP authenticate [yes | no] .RS 4 Normally ntpq does not authenticate requests unless they are write requests\&. The command authenticate yes causes ntpq to send authentication with all requests it makes\&. Authenticated requests causes some servers to handle requests slightly differently\&. The command authenticate without arguments causes ntpq to display whether or not ntpq is currently authenticating requests\&. .RE .PP cooked .RS 4 Display server messages in prettyprint format\&. .RE .PP debug more | less | off .RS 4 Turns internal query program debugging on and off\&. .RE .PP logfile | filename .RS 4 Displays or sets the file for debug logging\&. will send logs to standard error\&. .RE .PP delay \fImilliseconds\fR .RS 4 Specify a time interval to be added to timestamps included in requests which require authentication\&. This is used to enable (unreliable) server reconfiguration over long delay network paths or between machines whose clocks are unsynchronized\&. Actually the server does not now require timestamps in authenticated requests, so this command may be obsolete\&. .RE .PP exit .RS 4 Exit ntpq\&. .RE .PP host \fIname\fR .RS 4 Set the host to which future queries will be sent\&. The name may be either a DNS name or a numeric address\&. .RE .PP hostnames [yes | no] .RS 4 If yes is specified, host names are printed in information displays\&. If no is specified, numeric addresses are printed instead\&. The default is yes, unless modified using the command line \-n switch\&. .RE .PP keyid \fIkeyid\fR .RS 4 This command specifies the key number to be used to authenticate configuration requests\&. This must correspond to a key ID configured with the controlkey command in the server\(cqs ntp\&.conf .RE .PP keytype .RS 4 Specify the digest algorithm to use for authenticated requests, with default MD5\&. The keytype must match what the server is expecting for the the specified key ID\&. .RE .PP ntpversion 1 | 2 | 3 | 4 .RS 4 Sets the NTP version number which ntpq claims in packets\&. Defaults to 2, Note that mode\-6 control messages (and modes, for that matter) didn\(cqt exist in NTP version 1\&. .RE .PP passwd .RS 4 This command prompts for a password to authenticate requests\&. The password must match what the server is expecting\&. Passwords longer than 20 bytes are assumed to be hex encoding\&. .RE .PP quit .RS 4 Exit ntpq\&. .RE .PP raw .RS 4 Display server messages as received and without reformatting\&. The only formatting/interpretation done on the data is to transform nonascii data into a printable (but barely understandable) form\&. .RE .PP timeout \fImilliseconds\fR .RS 4 Specify a timeout period for responses to server queries\&. The default is about 5000 milliseconds\&. Note that since ntpq retries each query once after a timeout, the total waiting time for a timeout will be twice the timeout value set\&. .RE .PP units .RS 4 Toggle whether times in the peers display are shown with units\&. .RE .PP version .RS 4 Print the version of the ntpq program\&. .RE .SH "CONTROL MESSAGE COMMANDS" .sp Association IDs are used to identify system, peer and clock variables\&. System variables are assigned an association ID of zero and system name space, while each association is assigned a nonzero association ID and peer namespace\&. Most control commands send a single mode\-6 message to the server and expect a single response message\&. The exceptions are the peers command, which sends a series of messages, and the mreadlist and mreadvar commands, which iterate over a range of associations\&. .PP associations .RS 4 Display a list of mobilized associations in the form .sp .if n \{\ .RS 4 .\} .nf ind assid status conf reach auth condition last_event cnt .fi .if n \{\ .RE .\} .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ Variable T}:T{ Description T} T{ ind T}:T{ index on this list T} T{ assid T}:T{ association ID T} T{ status T}:T{ peer status word T} T{ conf T}:T{ yes: persistent, no: ephemeral T} T{ reach T}:T{ yes: reachable, no: unreachable T} T{ auth T}:T{ ok, yes, bad and none T} T{ condition T}:T{ selection status (see the select field of the peer status word) T} T{ last_event T}:T{ event report (see the event field of the peer status word) T} T{ cnt T}:T{ event count (see the count field of the peer status word) T} .TE .sp 1 .RE .PP authinfo .RS 4 Display the authentication statistics\&. .RE .PP clockvar \fIassocID\fR [\fIname\fR [ = \fIvalue\fR [\&...] ][\&...], cv \fIassocID\fR [\fIname\fR [ = \fIvalue\fR [\&...] ][\&...] .RS 4 Display a list of clock variables for those associations supporting a reference clock\&. .RE .PP :config [\&...] .RS 4 Send the remainder of the command line, including whitespace, to the server as a run\-time configuration command in the same format as the configuration file\&. This command is experimental until further notice and clarification\&. Authentication is of course required\&. .RE .PP config\-from\-file \fIfilename\fR .RS 4 Send the each line of \fIfilename\fR to the server as run\-time configuration commands in the same format as the configuration file\&. This command is experimental until further notice and clarification\&. Authentication is required\&. .RE .PP ifstats .RS 4 Display statistics for each local network address\&. Authentication is required\&. .RE .PP iostats .RS 4 Display network and reference clock I/O statistics\&. .RE .PP kerninfo .RS 4 Display kernel loop and PPS statistics\&. As with other ntpq output, times are in milliseconds\&. The precision value displayed is in milliseconds as well, unlike the precision system variable\&. .RE .PP lassociations .RS 4 Perform the same function as the associations command, except display mobilized and unmobilized associations\&. .RE .PP lpeers [\-4 | \-6] .RS 4 Print a peer spreadsheet for the appropriate IP version(s)\&. \fIdstadr\fR (associated with any given IP version)\&. .RE .PP monstats .RS 4 Display monitor facility statistics\&. .RE .PP direct .RS 4 Normally, the mrulist command retrieves an entire MRUreport (possibly consisting of more than one MRU span), sorts it, and presents the result\&. But attempting to fetch an entire MRU report may fail on a 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\&. .RE .PP mrulist [limited | kod | mincount=\fIcount\fR | laddr=\fIlocaladdr\fR | sort=\fIsortorder\fR | resany=\fIhexmask\fR | resall=\fIhexmask\fR] .RS 4 Obtain and print traffic counts collected and maintained by the monitor facility\&. This is useful for tracking who \fIuses\fR or \fIabuses\fR your server\&. .sp With the exception of sort=\fIsortorder\fR, 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=\fIcount\fR option filters entries representing less than \fIcount\fR packets\&. The laddr=\fIlocaladdr\fR option filters entries for packets received on any local address other than \fIlocaladdr\fR\&. resany=\fIhexmask\fR and resall=\fIhexmask\fR filter entries containing none or less than all, respectively, of the bits in \fIhexmask\fR, which must begin with 0x\&. .sp The \fIsortorder\fR defaults to lstint and may be any of addr, count, avgint, lstint, or any of those preceded by a minus sign (hyphen) to reverse the sort order\&. The output columns are: .RE .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Column T}:T{ .sp Description T} T{ .sp lstint T}:T{ .sp Interval in s between the receipt of the most recent packet from this address and the completion of the retrieval of the MRU list by ntpq\&. T} T{ .sp avgint T}:T{ .sp Average interval in s between packets from this address\&. T} T{ .sp rstr T}:T{ .sp Restriction flags associated with this address\&. Most are copied unchanged from the matching restrict command, however 0x400 (kod) and 0x20 (limited) flags are cleared unless the last packet from this address triggered a rate control response\&. T} T{ .sp r T}:T{ .sp Rate control indicator, either a period, L or K for no rate control response, rate limiting by discarding, or rate limiting with a KoD response, respectively\&. T} T{ .sp m T}:T{ .sp Packet mode\&. T} T{ .sp v T}:T{ .sp Packet version number\&. T} T{ .sp count T}:T{ .sp Packets received from this address\&. T} T{ .sp rport T}:T{ .sp Source port of last packet from this address\&. T} T{ .sp remote address T}:T{ .sp DNS name, numeric address, or address followed by claimed DNS name which could not be verified in parentheses\&. T} .TE .sp 1 .PP mreadvar \fIassocID\fR \fIassocID\fR [ \fIvariable_name\fR [ = \fIvalue\fR[ \&... ], mrv \fIassocID\fR \fIassocID\fR [ \fIvariable_name\fR [ = \fIvalue\fR[ \&... ] .RS 4 Perform the same function as the readvar command, except for a range of association IDs\&. This range is determined from the association list cached by the most recent associations command\&. .RE .PP opeers .RS 4 Obtain and print the old\-style list of all peers and clients showing \fIdstadr\fR (associated with any given IP version), rather than the \fIrefid\fR\&. .RE .PP passociations .RS 4 Perform the same function as the associations command, except that it uses previously stored data rather than making a new query\&. .RE .PP peers .RS 4 Display a list of peers in the form .sp tally remote refid st t when pool reach delay offset jitter .RE .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Variable T}:T{ .sp Description T} T{ .sp tally T}:T{ .sp single\-character code indicating current value of the select field of the peer status word T} T{ .sp remote T}:T{ .sp host name (or IP number) of peer T} T{ .sp refid T}:T{ .sp association ID or kiss code T} T{ .sp st T}:T{ .sp stratum T} T{ .sp t T}:T{ .sp u: unicast or manycast client, l: local (reference clock), s: symmetric (peer), server, B: broadcast server, T} T{ .sp when T}:T{ .sp sec/min/hr since last received packet T} T{ .sp poll T}:T{ .sp poll interval (log2 s) T} T{ .sp reach T}:T{ .sp reach shift register (octal) T} T{ .sp delay T}:T{ .sp roundtrip delay T} T{ .sp offset T}:T{ .sp offset of server relative to this host T} T{ .sp jitter T}:T{ .sp jitter T} .TE .sp 1 .sp The tally code is one of the following: .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Code T}:T{ .sp Description T} T{ .sp T}:T{ .sp discarded as not valid T} T{ .sp x T}:T{ .sp discarded by intersection algorithm T} T{ .sp \&. T}:T{ .sp discarded by table overflow (not used) T} T{ .sp \- T}:T{ .sp discarded by the cluster algorithm T} T{ .sp + T}:T{ .sp included by the combine algorithm T} T{ .sp # T}:T{ .sp backup (more than tos maxclock sources) T} T{ .sp * T}:T{ .sp system peer T} T{ .sp o T}:T{ .sp PPS peer (when the prefer peer is valid) T} .TE .sp 1 .PP apeers .RS 4 Display a list of peers in the form: .sp .if n \{\ .RS 4 .\} .nf [tally]remote refid assid st t when pool reach delay offset jitter .fi .if n \{\ .RE .\} .sp where the output is just like the peers command except that the refid is displayed in hex format and the association number is also displayed\&. .RE .PP pstats \fIassocID\fR .RS 4 Show the statistics for the peer with the given \fIassocID\fR\&. .RE .PP readvar \fIassocID\fR [ \fIname\fR ] [,\&...], rv \fIassocID\fR [ \fIname\fR ] [,\&...] .RS 4 Display the specified variables\&. If assocID is zero, the variables are from the system variables name space, otherwise they are from the peer variables name space\&. The assocID is required, as the same name can occur in both spaces\&. If no name is included, all operative variables in the name space are displayed\&. In this case only, if the assocID is omitted, it is assumed zero\&. Multiple names are specified with comma separators and without whitespace\&. Note that time values are represented in milliseconds and frequency values in parts\-per\-million (PPM)\&. Some NTP timestamps are represented in the format YYYYMMDDTTTT, where YYYY is the year, MM the month of year, DD the day of month and TTTT the time of day\&. .RE .PP reslist .RS 4 Show the access control (restrict) list for ntpq\&. .RE .PP timerstats .RS 4 Display interval timer counters\&. .RE .PP writelist \fIassocID\fR .RS 4 Write the system or peer variables included in the variable list\&. .RE .PP writevar \fIassocID\fR \fIname\fR = \fIvalue\fR [,\&...] .RS 4 Write the specified variables\&. If the assocID is zero, the variables are from the system variables name space, otherwise they are from the peer variables name space\&. The assocID is required, as the same name can occur in both spaces\&. .RE .PP sysinfo .RS 4 Display operational summary\&. .RE .PP sysstats .RS 4 Print statistics counters maintained in the protocol module\&. .RE .SH "AUTHENTICATION" .sp Four commands require authentication to the server: config\-from\-file, config, ifstats, and reslist\&. An authkey file must be in place and a control key declared in ntp\&.conf for these commands to work\&. .sp If you are running as root or otherwise have read access to the authkey and ntp\&.conf file, ntpq will mine the required credentials for you\&. Otherwise you will be prompted to enter a key ID and password\&. .sp Credentials, once entered, are retained and used for the duration of your ntpq session\&. .SH "STATUS WORDS AND KISS CODES" .sp The current state of the operating program is shown in a set of status words maintained by the system and each association separately\&. These words are displayed in the rv and as commands both in hexadecimal and decoded short tip strings\&. The codes, tips and short explanations are on the Event Messages and Status Words page\&. The page also includes a list of system and peer messages, the code for the latest of which is included in the status word\&. .sp Information resulting from protocol machine state transitions is displayed using an informal set of ASCII strings called kiss codes\&. The original purpose was for kiss\-o\*(Aq\-death (KoD) packets sent by the server to advise the client of an unusual condition\&. They are now displayed, when appropriate, in the reference identifier field in various billboards\&. .SH "SYSTEM VARIABLES" .sp The following system variables appear in the rv billboard\&. Not all variables are displayed in some configurations\&. .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Variable T}:T{ .sp Description T} T{ .sp status T}:T{ .sp system status word T} T{ .sp version T}:T{ .sp NTP software version and build time T} T{ .sp processor T}:T{ .sp hardware platform and version T} T{ .sp system T}:T{ .sp operating system and version T} T{ .sp leap T}:T{ .sp leap warning indicator (0\-3) T} T{ .sp stratum T}:T{ .sp stratum (1\-15) T} T{ .sp precision T}:T{ .sp precision (log2 s) T} T{ .sp rootdelay T}:T{ .sp total roundtrip delay to the primary reference clock T} T{ .sp rootdisp T}:T{ .sp total dispersion to the primary reference clock T} T{ .sp peer T}:T{ .sp system peer association ID T} T{ .sp tc T}:T{ .sp time constant and poll exponent (log2 s) (3\-17) T} T{ .sp mintc T}:T{ .sp minimum time constant (log2 s) (3\-10) T} T{ .sp clock T}:T{ .sp date and time of day T} T{ .sp refid T}:T{ .sp reference ID or kiss code T} T{ .sp reftime T}:T{ .sp reference time T} T{ .sp offset T}:T{ .sp combined offset of server relative to this host T} T{ .sp sys_jitter T}:T{ .sp combined system jitter T} T{ .sp frequency T}:T{ .sp frequency offset (PPM) relative to hardware clock T} T{ .sp clk_wander T}:T{ .sp clock frequency wander (PPM) T} T{ .sp clk_jitter T}:T{ .sp clock jitter T} T{ .sp tai T}:T{ .sp TAI\-UTC offset (s) T} T{ .sp leapsec T}:T{ .sp NTP seconds when the next leap second is/was inserted T} T{ .sp expire T}:T{ .sp NTP seconds when the NIST leapseconds file expires T} .TE .sp 1 .sp The jitter and wander statistics are exponentially\-weighted RMS averages\&. The system jitter is defined in the NTPv4 specification; the clock jitter statistic is computed by the clock discipline module\&. .SH "PEER VARIABLES" .sp The following peer variables appear in the rv billboard for each association\&. Not all variables are displayed in some configurations\&. .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Variable T}:T{ .sp Description T} T{ .sp associd T}:T{ .sp association ID T} T{ .sp status T}:T{ .sp peer status word T} T{ .sp srcadr srcport T}:T{ .sp source (remote) IP address and port T} T{ .sp dstadr dstport T}:T{ .sp destination (local) IP address and port T} T{ .sp leap T}:T{ .sp leap indicator (0\-3) T} T{ .sp stratum T}:T{ .sp stratum (0\-15) T} T{ .sp precision T}:T{ .sp precision (log2 s) T} T{ .sp rootdelay T}:T{ .sp total roundtrip delay to the primary reference clock T} T{ .sp rootdisp T}:T{ .sp total root dispersion to the primary reference clock T} T{ .sp refid T}:T{ .sp reference ID or kiss code T} T{ .sp reftime T}:T{ .sp reference time T} T{ .sp reach T}:T{ .sp reach register (octal) T} T{ .sp unreach T}:T{ .sp unreach counter T} T{ .sp hmode T}:T{ .sp host mode (1\-6) T} T{ .sp pmode T}:T{ .sp peer mode (1\-5) T} T{ .sp hpoll T}:T{ .sp host poll exponent (log2 s) (3\-17) T} T{ .sp ppoll T}:T{ .sp peer poll exponent (log2 s) (3\-17) T} T{ .sp headway T}:T{ .sp headway (see Rate Management and the Kiss\-o\*(Aq\-Death Packet) T} T{ .sp flash T}:T{ .sp flash status word T} T{ .sp offset T}:T{ .sp filter offset T} T{ .sp delay T}:T{ .sp filter delay T} T{ .sp dispersion T}:T{ .sp filter dispersion T} T{ .sp jitter T}:T{ .sp filter jitter T} T{ .sp bias T}:T{ .sp fudge for asymmetric links/paths T} .TE .sp 1 .SH "CLOCK VARIABLES" .sp The following clock variables appear in the cv billboard for each association with a reference clock\&. Not all variables are displayed in some configurations\&. .TS allbox tab(:); lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt. T{ .sp Variable T}:T{ .sp Description T} T{ .sp associd T}:T{ .sp association ID T} T{ .sp status T}:T{ .sp clock status word T} T{ .sp device T}:T{ .sp device description T} T{ .sp timecode T}:T{ .sp ASCII time code string (specific to device) T} T{ .sp poll T}:T{ .sp poll messages sent T} T{ .sp noreply T}:T{ .sp no reply T} T{ .sp badformat T}:T{ .sp bad format T} T{ .sp baddata T}:T{ .sp bad date or time T} T{ .sp fudgetime1 T}:T{ .sp fudge time 1 T} T{ .sp fudgetime2 T}:T{ .sp fudge time 2 T} T{ .sp stratum T}:T{ .sp driver stratum T} T{ .sp refid T}:T{ .sp driver reference ID T} T{ .sp flags T}:T{ .sp driver flags T} .TE .sp 1 .SH "COMPATIBILITY" .sp When listing refids, addresses of the form 127\&.127\&.x\&.x are no longer automatically interpreted as local refclocks as in older versions of ntpq\&. Instead, a clock\-format display is requested by the NTPsec daemon when appropriate (by setting the srcaddr peer variable)\&. This means that when used to query legacy versions of ntpd, which do not know how to request this, this program will do a slightly wrong thing\&. .sp In older versions, the \fItype\fR variable associated with a reference clock was a numeric driver type index\&. It has been replaced by \fIname\fR, a shortname for the driver type\&. .sp In older versions, no count of control packets was listed under sysstats\&. .sp The \-O (\-\-old\-rv) option of legacy versions has been retired\&. .SH "KNOWN LIMITATIONS" .sp It is possible for a ":config unpeer" command to fail silently, yielding "Config Succeeded", if it is given a peer identifier that looks like a driver type name or a hostname not present in the peer list\&. The error will however be reported in the system log\&. .sp The config command cannot be used to change a server\(cqs default restrictions\&. .sp Under some circumstances python 2 cannot emit unicode\&. When true, the display of units is downgraded to non\-unicode alternatives\&. One place a user is likely to encounter this is when diverting output through a pipe\&. Attempts have been made to force the use of UTF\-8, all of which break the command history feature\&. .sp When using the \-u option, very old xterms may fail to render μ correctly\&. If this happens, be sure your xterm is started with the \-u8 option, or the \fIutf8\fR resource\*(Aq, and that your console font contains the UTF\-8 &mu character\&. Also confirm your LANG environment variable is set to a UTF\-8 language, like this: "export LANG=en_US\&.utf8"\&. .sp Timestamp interpretation in this program is likely to fail in flaky ways if the local system clock has not already been approximately synchronized to UTC\&. Querying a server based in a different NTP era than the current one is especially likely to fail\&. .sp This program will behave in apparently buggy and only semi\-predictable ways when fetching MRU lists from \fIany\fR server with sufficiently high traffic\&. .sp The problem is fundamental\&. The Mode 6 protocol can\(cqt ship (and your client cannot accept) MRU records as fast as the daemon accepts incoming traffic\&. Under these circumstances, the daemon will repeatedly fail to ship an entire report, leading to long hangs as your client repeatedly re\-sends the request\&. Eventually the Mode 6 client library will throw an error indicating that a maximum number of restarts has been exceeded\&. .sp To avoid this problem, avoid monitoring over links that don\(cqt have enough capacity to handle the monitored server\(cqs \fIentire\fR NTP load\&. .sp You may be able to retrieve partial data in very high\-traffic conditions by using the \fIdirect\fR option\&. .SH "EXIT STATUS" .sp One of the following exit values will be returned: .PP 0 (EXIT_SUCCESS) .RS 4 Successful program execution\&. .RE .PP 1 (EXIT_FAILURE) .RS 4 The operation failed or the command syntax was not valid\&. .RE ntpsec-1.1.0+dfsg1/include/0000775000175000017500000000000013252650651015253 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/include/gpstolfp.h0000644000175000017500000000103513252364117017256 0ustar rlaagerrlaager#include "ntp_calendar.h" #include "ntp_fp.h" #include "parse.h" #define MIN_BUILD_GPSWEEK (1900) /* minimum GPS week number of compile*/ #define MAX_BUILD_GPSWEEK (6078) /* testcase limit */ extern void gpstolfp (int weeks, int days, unsigned long seconds, l_fp *); extern void gpsweekadj (unsigned int * week, unsigned int build_week); extern void gpstocal (unsigned int week, unsigned int TOW, int UTC_offset, struct calendar *); extern void caltogps (const struct calendar *, int UTC_offset, unsigned int * week, unsigned int * TOW); ntpsec-1.1.0+dfsg1/include/isc_error.h0000644000175000017500000000166013252364117017413 0ustar rlaagerrlaager/* * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ #ifndef GUARD_ISC_ERROR_H #define GUARD_ISC_ERROR_H 1 /* isc_error.h */ /* * ISC_FORMAT_PRINTF(). * * fmt is the location of the format string parameter. * args is the location of the first argument (or 0 for no argument checking). * * Note: * The first parameter is 1, not 0. */ #ifdef __GNUC__ #define ISC_FORMAT_PRINTF(fmt, args) __attribute__((__format__(__printf__, fmt, args))) #else #define ISC_FORMAT_PRINTF(fmt, args) #endif /* unexpected error */ void isc_error_unexpected(const char *, int, const char *, ...) ISC_FORMAT_PRINTF(3, 4); /* Unexpected Error */ #define UNEXPECTED_ERROR(fmt, ...) \ isc_error_unexpected(__FILE__, __LINE__, fmt, __VA_ARGS__) #endif /* GUARD_ISC_ERROR_H */ ntpsec-1.1.0+dfsg1/include/ntp_assert.h0000644000175000017500000000424013252364117017603 0ustar rlaagerrlaager/* * ntp_assert.h - design by contract stuff * * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2001 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC * * example: * * int foo(char *a) { * int result; * int value; * * REQUIRE(a != NULL); * ... * bar(&value); * INSIST(value > 2); * ... * * ENSURE(result != 12); * return result; * } * * open question: when would we use INVARIANT()? * */ #ifndef GUARD_NTP_ASSERT_H #define GUARD_NTP_ASSERT_H #include #if defined(__FLEXELINT__) #include #define REQUIRE(x) assert(x) #define INSIST(x) assert(x) #define INVARIANT(x) assert(x) #define ENSURE(x) assert(x) # else /* not FlexeLint */ /*% isc assertion type */ typedef enum { assertiontype_require, assertiontype_ensure, assertiontype_insist, assertiontype_invariant } assertiontype_t; /* our assertion catcher */ extern void assertion_failed(const char *, int, assertiontype_t, const char *) __attribute__ ((__noreturn__)); #define REQUIRE(cond) \ ((void) ((cond) || (assertion_failed(__FILE__, __LINE__, \ assertiontype_require, #cond), 0))) #define ENSURE(cond) \ ((void) ((cond) || (assertion_failed(__FILE__, __LINE__, \ assertiontype_ensure, #cond), 0))) #define INSIST(cond) \ ((void) ((cond) || (assertion_failed(__FILE__, __LINE__, \ assertiontype_insist, #cond), 0))) #define INVARIANT(cond) \ ((void) ((cond) || (assertion_failed(__FILE__, __LINE__, \ assertiontype_invariant, #cond), 0))) # endif /* not FlexeLint */ #if defined(USEBACKTRACE) && \ (defined(HAVE_BACKTRACE_SYMBOLS_FD) || defined (HAVE__UNWIND_BACKTRACE)) /* doing backtrace */ extern void backtrace_log(void); #else /* not doing backtrace */ # define BACKTRACE_DISABLED 1 #endif /* ! USEBACKTRACE */ #endif /* GUARD_NTP_ASSERT_H */ ntpsec-1.1.0+dfsg1/include/ntp_endian.h0000644000175000017500000000101213252364117017532 0ustar rlaagerrlaager/* ntp_endian.h - BSD-style big-endian encoding/decoding functions * * Copyright 2016 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-clause * * These are portable reimplementations of what BSD provides * in . */ #ifndef GUARD_NTP_ENDIAN_H #define GUARD_NTP_ENDIAN_H #include uint16_t ntp_be16dec(const void *buf) __attribute__((pure)); uint32_t ntp_be32dec(const void *buf) __attribute__((pure)); uint64_t ntp_be64dec(const void *buf) __attribute__((pure)); #endif ntpsec-1.1.0+dfsg1/include/ntp_syslog.h0000644000175000017500000000430613252364117017625 0ustar rlaagerrlaager/* * A hack for platforms which require specially built syslog facilities */ #ifndef GUARD_NTP_SYSLOG_H #define GUARD_NTP_SYSLOG_H #include #include #include "ntp_types.h" /* uint32_t type */ extern bool syslogit; /* log to syslogit */ extern bool termlogit; /* duplicate to stdout/err */ extern bool termlogit_pid; extern bool msyslog_include_timestamp; /* * syslog output control */ #define NLOG_INFO 0x00000001 #define NLOG_EVENT 0x00000002 #define NLOG_STATUS 0x00000004 #define NLOG_STATIST 0x00000008 #define NLOG_OSYS 0 /* offset for system flags */ #define NLOG_SYSMASK 0x0000000F /* system log events */ #define NLOG_SYSINFO 0x00000001 /* system info log events */ #define NLOG_SYSEVENT 0x00000002 /* system events */ #define NLOG_SYSSTATUS 0x00000004 /* system status (sync/unsync) */ #define NLOG_SYSSTATIST 0x00000008 /* system statistics output */ #define NLOG_OPEER 4 /* offset for peer flags */ #define NLOG_PEERMASK 0x000000F0 /* peer log events */ #define NLOG_PEERINFO 0x00000010 /* peer info log events */ #define NLOG_PEEREVENT 0x00000020 /* peer events */ #define NLOG_PEERSTATUS 0x00000040 /* peer status (sync/unsync) */ #define NLOG_PEERSTATIST 0x00000080 /* peer statistics output */ #define NLOG_OCLOCK 8 /* offset for clock flags */ #define NLOG_CLOCKMASK 0x00000F00 /* clock log events */ #define NLOG_CLOCKINFO 0x00000100 /* clock info log events */ #define NLOG_CLOCKEVENT 0x00000200 /* clock events */ #define NLOG_CLOCKSTATUS 0x00000400 /* clock status (sync/unsync) */ #define NLOG_CLOCKSTATIST 0x00000800 /* clock statistics output */ #define NLOG_OSYNC 12 /* offset for sync flags */ #define NLOG_SYNCMASK 0x0000F000 /* sync log events */ #define NLOG_SYNCINFO 0x00001000 /* sync info log events */ #define NLOG_SYNCEVENT 0x00002000 /* sync events */ #define NLOG_SYNCSTATUS 0x00004000 /* sync status (sync/unsync) */ #define NLOG_SYNCSTATIST 0x00008000 /* sync statistics output */ extern uint32_t ntp_syslogmask; #define NLOG(bits) if (ntp_syslogmask & (bits)) #define LOGIF(nlog_suffix, msl_args) \ do { \ NLOG(NLOG_##nlog_suffix) /* like "if (...) */ \ msyslog msl_args; \ } while (false) #endif /* GUARD_NTP_SYSLOG_H */ ntpsec-1.1.0+dfsg1/include/ntp_lists.h0000644000175000017500000002713113252364117017444 0ustar rlaagerrlaager/* * ntp_lists.h - linked lists common code * * SLIST: singly-linked lists * ========================== * * These macros implement a simple singly-linked list template. Both * the listhead and per-entry next fields are declared as pointers to * the list entry struct type. Initialization to NULL is typically * implicit (for globals and statics) or handled by zeroing of the * containing structure. * * The name of the next link field is passed as an argument to allow * membership in several lists at once using multiple next link fields. * * When possible, placing the link field first in the entry structure * allows slightly smaller code to be generated on some platforms. * * LINK_SLIST(listhead, pentry, nextlink) * add entry at head * * LINK_TAIL_SLIST(listhead, pentry, nextlink, entrytype) * add entry at tail. This is O(n), if this is a common * operation the FIFO template may be more appropriate. * * LINK_SORT_SLIST(listhead, pentry, beforecur, nextlink, entrytype) * add entry in sorted order. beforecur is an expression comparing * pentry with the current list entry. The current entry can be * referenced within beforecur as L_S_S_CUR(), which is short for * LINK_SORT_SLIST_CUR(). beforecur is nonzero if pentry sorts * before L_S_S_CUR(). * * UNLINK_HEAD_SLIST(punlinked, listhead, nextlink) * unlink first entry and point punlinked to it, or set punlinked * to NULL if the list is empty. * * UNLINK_SLIST(punlinked, listhead, ptounlink, nextlink, entrytype) * unlink entry pointed to by ptounlink. punlinked is set to NULL * if the entry is not found on the list, otherwise it is set to * ptounlink. * * UNLINK_EXPR_SLIST(punlinked, listhead, expr, nextlink, entrytype) * unlink entry where expression expr is nonzero. expr can refer * to the entry being tested using UNLINK_EXPR_SLIST_CURRENT(), * alias U_E_S_CUR(). See the implementation of UNLINK_SLIST() * below for an example. U_E_S_CUR() is NULL iff the list is empty. * punlinked is pointed to the removed entry or NULL if none * satisfy expr. * * FIFO: singly-linked lists plus tail pointer * =========================================== * * This is the same as FreeBSD's sys/queue.h STAILQ -- a singly-linked * list implementation with tail-pointer maintenance, so that adding * at the tail for first-in, first-out access is O(1). * * DECL_FIFO_ANCHOR(entrytype) * provides the type specification portion of the declaration for * a variable to refer to a FIFO queue (similar to listhead). The * anchor contains the head and indirect tail pointers. Example: * * #include "ntp_lists.h" * * typedef struct myentry_tag myentry; * struct myentry_tag { * myentry *next_link; * ... * }; * * DECL_FIFO_ANCHOR(myentry) my_fifo; * * void somefunc(myentry *pentry) * { * LINK_FIFO(my_fifo, pentry, next_link); * } * * If DECL_FIFO_ANCHOR is used with stack or heap storage, it * should be initialized to NULL pointers using a = { NULL }; * initializer or memset. * * HEAD_FIFO(anchor) * TAIL_FIFO(anchor) * Pointer to first/last entry, NULL if FIFO is empty. * * LINK_FIFO(anchor, pentry, nextlink) * add entry at tail. * * UNLINK_FIFO(punlinked, anchor, nextlink) * unlink head entry and point punlinked to it, or set punlinked * to NULL if the list is empty. * * CONCAT_FIFO(q1, q2, nextlink) * empty fifoq q2 moving its nodes to q1 following q1's existing * nodes. * * DLIST: doubly-linked lists * ========================== * * Elements on DLISTs always have non-NULL forward and back links, * because both link chains are circular. The beginning/end is marked * by the listhead, which is the same type as elements for simplicity. * An empty list's listhead has both links set to its own address. * * */ #ifndef GUARD_NTP_LISTS_H #define GUARD_NTP_LISTS_H #include "ntp_types.h" /* true and false */ #include "ntp_assert.h" #ifdef DEBUG # define NTP_DEBUG_LISTS #endif /* * If list debugging is not enabled, save a little time by not clearing * an entry's link pointer when it is unlinked, as the stale pointer * is harmless as long as it is ignored when the entry is not in a * list. */ #ifndef NTP_DEBUG_LISTS #define MAYBE_Z_LISTS(p) do { } while (false) #else #define MAYBE_Z_LISTS(p) (p) = NULL #endif #define LINK_SLIST(listhead, pentry, nextlink) \ do { \ (pentry)->nextlink = (listhead); \ (listhead) = (pentry); \ } while (false) #define LINK_TAIL_SLIST(listhead, pentry, nextlink, entrytype) \ do { \ entrytype **pptail; \ \ pptail = &(listhead); \ while (*pptail != NULL) \ pptail = &((*pptail)->nextlink); \ \ (pentry)->nextlink = NULL; \ *pptail = (pentry); \ } while (false) #define LINK_SORT_SLIST_CURRENT() (*ppentry) #define L_S_S_CUR() LINK_SORT_SLIST_CURRENT() #define LINK_SORT_SLIST(listhead, pentry, beforecur, nextlink, \ entrytype) \ do { \ entrytype **ppentry; \ \ ppentry = &(listhead); \ while (true) { \ if (NULL == *ppentry || (beforecur)) { \ (pentry)->nextlink = *ppentry; \ *ppentry = (pentry); \ break; \ } \ ppentry = &((*ppentry)->nextlink); \ if (NULL == *ppentry) { \ (pentry)->nextlink = NULL; \ *ppentry = (pentry); \ break; \ } \ } \ } while (false) #define UNLINK_HEAD_SLIST(punlinked, listhead, nextlink) \ do { \ (punlinked) = (listhead); \ if (NULL != (punlinked)) { \ (listhead) = (punlinked)->nextlink; \ MAYBE_Z_LISTS((punlinked)->nextlink); \ } \ } while (false) #define UNLINK_EXPR_SLIST_CURRENT() (*ppentry) #define U_E_S_CUR() UNLINK_EXPR_SLIST_CURRENT() #define UNLINK_EXPR_SLIST(punlinked, listhead, expr, nextlink, \ entrytype) \ do { \ entrytype **ppentry; \ \ ppentry = &(listhead); \ \ while (!(expr)) \ if (*ppentry != NULL && \ (*ppentry)->nextlink != NULL) { \ ppentry = &((*ppentry)->nextlink); \ } else { \ ppentry = NULL; \ break; \ } \ \ if (ppentry != NULL) { \ (punlinked) = *ppentry; \ *ppentry = (punlinked)->nextlink; \ MAYBE_Z_LISTS((punlinked)->nextlink); \ } else { \ (punlinked) = NULL; \ } \ } while (false) #define UNLINK_SLIST(punlinked, listhead, ptounlink, nextlink, \ entrytype) \ UNLINK_EXPR_SLIST(punlinked, listhead, (ptounlink) == \ U_E_S_CUR(), nextlink, entrytype) /* might be useful for debug someday #define CHECK_SLIST(listhead, nextlink, entrytype) \ do { \ entrytype *pentry; \ \ for (pentry = (listhead); \ pentry != NULL; \ pentry = pentry->nextlink){ \ INSIST(pentry != pentry->nextlink); \ INSIST((listhead) != pentry->nextlink); \ } \ } while (false) */ /* * FIFO */ #define DECL_FIFO_ANCHOR(entrytype) \ struct { \ entrytype * phead; /* NULL if list empty */ \ entrytype ** pptail; /* NULL if list empty */ \ } #define HEAD_FIFO(anchor) ((anchor).phead) /* * For DEBUG builds only, verify both or neither of the anchor pointers * are NULL with each operation. */ #if !defined(NTP_DEBUG_LISTS) #define CHECK_FIFO_CONSISTENCY(anchor) do { } while (false) #else #define CHECK_FIFO_CONSISTENCY(anchor) \ check_gen_fifo_consistency(&(anchor)) void check_gen_fifo_consistency(void *fifo); #endif /* * generic FIFO element used to access any FIFO where each element * begins with the link pointer */ typedef struct gen_node_tag gen_node; struct gen_node_tag { gen_node * link; }; /* generic FIFO */ typedef DECL_FIFO_ANCHOR(gen_node) gen_fifo; #define LINK_FIFO(anchor, pentry, nextlink) \ do { \ CHECK_FIFO_CONSISTENCY(anchor); \ \ (pentry)->nextlink = NULL; \ if (NULL != (anchor).pptail) { \ (*((anchor).pptail))->nextlink = (pentry); \ (anchor).pptail = \ &(*((anchor).pptail))->nextlink; \ } else { \ (anchor).phead = (pentry); \ (anchor).pptail = &(anchor).phead; \ } \ \ CHECK_FIFO_CONSISTENCY(anchor); \ } while (false) #define UNLINK_FIFO(punlinked, anchor, nextlink) \ do { \ CHECK_FIFO_CONSISTENCY(anchor); \ \ (punlinked) = (anchor).phead; \ if (NULL != (punlinked)) { \ (anchor).phead = (punlinked)->nextlink; \ if (NULL == (anchor).phead) \ (anchor).pptail = NULL; \ else if ((anchor).pptail == \ &(punlinked)->nextlink) \ (anchor).pptail = &(anchor).phead; \ MAYBE_Z_LISTS((punlinked)->nextlink); \ CHECK_FIFO_CONSISTENCY(anchor); \ } \ } while (false) #define UNLINK_MID_FIFO(punlinked, anchor, tounlink, nextlink, \ entrytype) \ do { \ entrytype **ppentry; \ \ CHECK_FIFO_CONSISTENCY(anchor); \ \ ppentry = &(anchor).phead; \ \ while ((tounlink) != *ppentry) \ if ((*ppentry)->nextlink != NULL) { \ ppentry = &((*ppentry)->nextlink); \ } else { \ ppentry = NULL; \ break; \ } \ \ if (ppentry != NULL) { \ (punlinked) = *ppentry; \ *ppentry = (punlinked)->nextlink; \ if (NULL == *ppentry) \ (anchor).pptail = NULL; \ else if ((anchor).pptail == \ &(punlinked)->nextlink) \ (anchor).pptail = &(anchor).phead; \ MAYBE_Z_LISTS((punlinked)->nextlink); \ CHECK_FIFO_CONSISTENCY(anchor); \ } else { \ (punlinked) = NULL; \ } \ } while (false) #define CONCAT_FIFO(f1, f2, nextlink) \ do { \ CHECK_FIFO_CONSISTENCY(f1); \ CHECK_FIFO_CONSISTENCY(f2); \ \ if ((f2).pptail != NULL) { \ if ((f1).pptail != NULL) { \ (*(f1).pptail)->nextlink = (f2).phead; \ if ((f2).pptail == &(f2).phead) \ (f1).pptail = \ &(*(f1).pptail)->nextlink; \ else \ (f1).pptail = (f2).pptail; \ CHECK_FIFO_CONSISTENCY(f1); \ } else { \ (f1) = (f2); \ } \ MAYBE_Z_LISTS((f2).phead); \ MAYBE_Z_LISTS((f2).pptail); \ } \ } while (false) /* * DLIST */ #define DECL_DLIST_LINK(entrytype, link) \ struct { \ entrytype * b; \ entrytype * f; \ } link #define INIT_DLIST(listhead, link) \ do { \ (listhead).link.f = &(listhead); \ (listhead).link.b = &(listhead); \ } while (false) #define HEAD_DLIST(listhead, link) \ ( \ (&(listhead) != (listhead).link.f) \ ? (listhead).link.f \ : NULL \ ) #define TAIL_DLIST(listhead, link) \ ( \ (&(listhead) != (listhead).link.b) \ ? (listhead).link.b \ : NULL \ ) #define PREV_DLIST(listhead, entry, link) \ ( \ (&(listhead) != (entry)->link.b) \ ? (entry)->link.b \ : NULL \ ) #define LINK_DLIST(listhead, pentry, link) \ do { \ (pentry)->link.f = (listhead).link.f; \ (pentry)->link.b = &(listhead); \ (listhead).link.f->link.b = (pentry); \ (listhead).link.f = (pentry); \ } while (false) #define LINK_TAIL_DLIST(listhead, pentry, link) \ do { \ (pentry)->link.b = (listhead).link.b; \ (pentry)->link.f = &(listhead); \ (listhead).link.b->link.f = (pentry); \ (listhead).link.b = (pentry); \ } while (false) #define UNLINK_DLIST(ptounlink, link) \ do { \ (ptounlink)->link.b->link.f = (ptounlink)->link.f; \ (ptounlink)->link.f->link.b = (ptounlink)->link.b; \ MAYBE_Z_LISTS((ptounlink)->link.b); \ MAYBE_Z_LISTS((ptounlink)->link.f); \ } while (false) #define ITER_DLIST_BEGIN(listhead, iter, link, entrytype) \ { \ entrytype *i_dl_nextiter; \ \ for ((iter) = (listhead).link.f; \ (iter) != &(listhead) \ && ((i_dl_nextiter = (iter)->link.f), true); \ (iter) = i_dl_nextiter) { #define ITER_DLIST_END() \ } \ } #endif /* GUARD_NTP_LISTS_H */ ntpsec-1.1.0+dfsg1/include/ntp_calendar.h0000644000175000017500000001575013252364117020063 0ustar rlaagerrlaager/* * ntp_calendar.h - definitions for the calendar time-of-day routine */ #ifndef GUARD_NTP_CALENDAR_H #define GUARD_NTP_CALENDAR_H #include #include "ntp_types.h" /* gregorian calendar date */ struct calendar { uint16_t year; /* year (A.D.) */ uint16_t yearday; /* day of year, 1 = January 1 */ uint8_t month; /* month, 1 = January */ uint8_t monthday; /* day of month */ uint8_t hour; /* hour of day, midnight = 0 */ uint8_t minute; /* minute of hour */ uint8_t second; /* second of minute */ uint8_t weekday; /* 0..7, 0=Sunday */ }; /* general split representation */ typedef struct { int32_t hi; int32_t lo; } ntpcal_split; typedef time_t (*systime_func_ptr)(time_t *); /* * We deal in a 4 year cycle starting at March 1, 1900. We assume * we will only want to deal with dates since then, and not to exceed * the rollover day in 2036. */ #define SECSPERMIN (60) /* seconds per minute */ #define MINSPERHR (60) /* minutes per hour */ #define HRSPERDAY (24) /* hours per day */ #define DAYSPERWEEK (7) /* days per week */ #define DAYSPERYEAR (365) /* days per year */ #define SECSPERHR (SECSPERMIN * MINSPERHR) #define SECSPERDAY (SECSPERHR * HRSPERDAY) #define SECSPERWEEK (DAYSPERWEEK * SECSPERDAY) #define SECSPERYEAR (365 * SECSPERDAY) /* regular year */ #define SECSPERLEAPYEAR (366 * SECSPERDAY) /* leap year */ #define SECSPERAVGYEAR 31556952 /* mean year length over 400yrs */ /* * Get the build date & time in UTC. This depends on the BUILD_EPOCH * which is fixed at configure time. */ extern int ntpcal_get_build_date(struct calendar * /* jd */); /* * Convert a timestamp in NTP scale to a time_t value in the UN*X * scale with proper epoch unfolding around a given pivot or the * current system time. */ extern time64_t ntpcal_ntp_to_time(uint32_t /* ntp */, time_t /* pivot */); /* * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP * scale with proper epoch unfolding around a given pivot or the current * system time. * Note: The pivot must be given in UN*X time scale! */ extern time64_t ntpcal_ntp_to_ntp(uint32_t /* ntp */, time_t /* pivot */); /* * Split a time stamp in seconds into elapsed days and elapsed seconds * since midnight. */ extern ntpcal_split ntpcal_daysplit(const time64_t); /* * Merge a number of days and a number of seconds into seconds, * expressed in 64 bits to avoid overflow. */ extern time64_t ntpcal_dayjoin(int32_t /* days */, int32_t /* seconds */) __attribute__((const)); /* * Convert elapsed years in Era into elapsed days in Era. */ extern int32_t ntpcal_days_in_years(int32_t /* years */) __attribute__((const)); #define days_per_year(x) ((x) % 4 ? 365 : ((x % 400) ? ((x % 100) ? 366 : 365) : 366)) /* * Convert ELAPSED years/months/days of gregorian calendar to elapsed * days in Gregorian epoch. No range checks done here! */ extern int32_t ntpcal_edate_to_eradays(int32_t /* years */, int32_t /* months */, int32_t /* mdays */); /* * Convert a time spec to seconds. No range checks done here! */ extern int32_t ntpcal_etime_to_seconds(int32_t /* hours */, int32_t /* minutes */, int32_t /* seconds */) __attribute__((const)); /* * Convert the date part of a 'struct tm' (that is, year, month, * day-of-month) into the RataDie of that day. */ extern int32_t ntpcal_tm_to_rd(const struct tm * /* utm */); /* * Convert the date part of a 'struct calendar' (that is, year, month, * day-of-month) into the RataDie of that day. */ extern int32_t ntpcal_date_to_rd(const struct calendar * /* jt */); /* * Given the number of elapsed days in the calendar era, split this * number into the number of elapsed years in 'res.quot' and the * number of elapsed days of that year in 'res.rem'. * * if 'isleapyear' is not NULL, it will receive an integer that is 0 * for regular years and a non-zero value for leap years. */ extern ntpcal_split ntpcal_split_eradays(int32_t /* days */, int32_t * /* isleapyear */); /* * Given a number of elapsed days in a year and a leap year indicator, * split the number of elapsed days into the number of elapsed months * in 'res.quot' and the number of elapsed days of that month in * 'res.rem'. */ extern ntpcal_split ntpcal_split_yeardays(int32_t /* eyd */, bool /* isleapyear */); /* * Convert a RataDie number into the date part of a 'struct * calendar'. Return 0 if the year is regular year, 1 if the year is * a leap year, -1 if the calculation overflowed. */ extern int ntpcal_rd_to_date(struct calendar * /* jt */, int32_t /* rd */); /* * Take a value of seconds since midnight and split it into hhmmss in * a 'struct calendar'. Return excessive days. */ /* used by ntpd/refclock_nmea.c */ extern int32_t ntpcal_daysec_to_date(struct calendar * /* jt */, int32_t /* secs */); /* * Take the time part of a 'struct calendar' and return the seconds * since midnight. */ /* used by ntpd/refclock_nmea.c */ extern int32_t ntpcal_date_to_daysec(const struct calendar *); extern int32_t ntpcal_tm_to_daysec(const struct tm * /* utm */); extern int ntpcal_time_to_date(struct calendar * /* jd */, const time64_t /* ts */); extern int32_t ntpcal_periodic_extend(int32_t /* pivot */, int32_t /* value */, int32_t /* cycle */) __attribute__((const)); extern int ntpcal_ntp64_to_date(struct calendar * /* jd */, const time64_t /* ntp */); extern int ntpcal_ntp_to_date(struct calendar * /* jd */, uint32_t /* ntp */, time_t /* pivot */); extern time_t ntpcal_date_to_time(const struct calendar * /* jd */); /* * Additional support stuff for Ed Rheingold's calendrical calculations */ /* * Start day of NTP time as days past the imaginary date 12/1/1 BC. * (This is the beginning of the Christian Era, or BCE.) */ #define DAY_NTP_STARTS 693596 /* * Start day of the UNIX epoch. This is the Rata Die of 1970-01-01. */ #define DAY_UNIX_STARTS 719163 /* * Difference between UN*X and NTP epoch (25567). */ #define NTP_TO_UNIX_DAYS (DAY_UNIX_STARTS - DAY_NTP_STARTS) /* * Days in a normal 4 year leap year calendar cycle (1461). */ #define GREGORIAN_NORMAL_LEAP_CYCLE_DAYS (3 * 365 + 366) /* * Days in a normal 100 year leap year calendar (36524). We lose a * leap day in years evenly divisible by 100 but not by 400. */ #define GREGORIAN_NORMAL_CENTURY_DAYS \ (25 * GREGORIAN_NORMAL_LEAP_CYCLE_DAYS - 1) /* * The Gregorian calendar is based on a 400 year cycle. This is the * number of days in each cycle (146097). We gain a leap day in years * divisible by 400 relative to the "normal" century. */ #define GREGORIAN_CYCLE_DAYS (4 * GREGORIAN_NORMAL_CENTURY_DAYS + 1) /* * Number of weeks in 400 years (20871). */ #define GREGORIAN_CYCLE_WEEKS (GREGORIAN_CYCLE_DAYS / 7) #define is_leapyear(y) (!((y) % 4) && !(!((y) % 100) && (y) % 400)) /* * Time of day conversion constant. NTP's time scale starts in 1900, * Unix in 1970. The value is 1970 - 1900 in seconds, 0x83aa7e80 or * 2208988800. This is larger than 32-bit INT_MAX, so unsigned * type is forced. */ #define JAN_1970 ((unsigned int)NTP_TO_UNIX_DAYS * (unsigned int)SECSPERDAY) #endif ntpsec-1.1.0+dfsg1/include/declcond.h0000644000175000017500000000146513252364117017202 0ustar rlaagerrlaager/* * declcond.h - declarations conditionalized for ntpd * * The NTP reference implementation distribution includes two distinct * declcond.h files, one in ntpd/ used only by ntpd, and another in * include/ used by libntp and utilities. This relies on the source * file's directory being ahead of include/ in the include search. * * The ntpd variant of declcond.h declares "debug" only #ifdef DEBUG, * as the --disable-debugging version of ntpd should not reference * "debug". The libntp and utilities variant always declares debug, * as it is used in those codebases even without DEBUG defined. */ #ifndef GUARD_DECLCOND_H #define GUARD_DECLCOND_H /* #ifdef DEBUG */ /* uncommented in ntpd/declcond.h */ extern int debug; /* #endif */ /* uncommented in ntpd/declcond.h */ #endif /* GUARD_DECLCOND_H */ ntpsec-1.1.0+dfsg1/include/ieee754io.h0000644000175000017500000000254413252364117017125 0ustar rlaagerrlaager/* * Created: Sun Jul 13 12:22:11 1997 * * Copyright (c) 1997-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-clause */ #ifndef GUARD_IEEE754IO_H #define GUARD_IEEE754IO_H #define IEEE_SINGLE 1 #define IEEE_DOUBLE 2 #define IEEE_MSB 1 #define IEEE_LSB 2 #define IEEE_OK 0 /* conversion ok */ #define IEEE_BADCALL 1 /* bad call parameters */ #define IEEE_NAN 2 /* found an NaN */ #define IEEE_POSINFINITY 3 /* positive infinity */ #define IEEE_NEGINFINITY 4 /* negative infinity */ #define IEEE_POSOVERFLOW 5 /* positive overflow */ #define IEEE_NEGOVERFLOW 6 /* negative overflow */ #define IEEE_OFFSETS 8 /* number of byte positions */ typedef unsigned char offsets_t[IEEE_OFFSETS]; int fetch_ieee754 (unsigned char **bufp, int size, l_fp *lfpp, offsets_t offsets); #endif /* * History: * * ieee754io.h,v * Revision 4.3 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.2 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.0 1998/04/10 19:50:40 kardel * Start 4.0 release version numbering * * Revision 1.1 1998/04/10 19:27:33 kardel * initial NTP VERSION 4 integration of PARSE with GPS166 binary support * * Revision 1.1 1997/10/06 20:55:37 kardel * new parse structure * */ ntpsec-1.1.0+dfsg1/include/ntp_fp.h0000644000175000017500000001420613252364117016712 0ustar rlaagerrlaager/* * ntp_fp.h - definitions for NTP fixed/floating-point arithmetic */ #ifndef GUARD_NTP_FP_H #define GUARD_NTP_FP_H #include #include "ntp_types.h" /* * NTP uses two fixed point formats. * * The first (l_fp) is the "long" format and is 64 bits wide in units * of 1/2^32 seconds (which is between 232 and 233 decimal * picoseconds). The zero value signifies the zero date of the * current NTP era; era zero began on the date 1900-00-00T00:00:00 in * proleptic UTC (leap second correction was not introduced until * 1972). * * The long format is used for timestamps in the NTP packet header (in * network byte order). It is defined in RFC 5905 in Section 6 (Data * Types). In the on-the-wire context, it is always unsigned. * * When it is convenient to compute in seconds, this type can * be interpreted as a fixed-point float with the radix point between * bits 31 and 32. This is why there are macros to extract the low and * high halves. * * Internally, this type is sometimes used for time offsets. In that * context it is interpreted as signed and can only express offsets * up to a half cycle. Offsets are normally much, much smaller than that; * for an offset to have a value even as large as 1 second would be * highly unusual after ntpd initialization. * * Anyway, an l_fp looks like: * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Integral Part | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Fractional Part | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * NTP time stamps will overflow in 2036. Until then we are in * NTP Epoch 0. After that NTP timestamps will be in Epoch 1. Negative * epochs can be used to represent time before Jan 1900. * * The epoch number is not explicit on the wire. It will seldom be an * issue: timestamp differences between two adjacent epochs are still * valid as long as the true time difference is less than half an * epoch. In other words, you don't have to explicitly check for the * epoch of either timestamp if you assume that these are less than 68 * years apart. */ typedef uint64_t l_fp; #define lfpfrac(n) ((uint32_t)(n)) #define lfptouint(n) ((uint64_t)((uint64_t)(n) << 32)) #define lfpsint(n) (( int32_t)((n) >> 32)) #define lfpuint(n) ((uint32_t)((n) >> 32)) #define bumplfpsint(n, i) ((n) += lfptouint(i)) #define bumplfpuint(n, i) ((n) += lfptouint(i)) #define setlfpfrac(n, v) ((n) = (lfptouint(lfpuint(n)) | lfpfrac(v))) #define setlfpuint(n, v) ((n) = (lfptouint(v) | lfpfrac(n))) static inline l_fp lfpinit(int32_t sec, uint32_t frac) { l_fp tmp = lfptouint(sec) | lfpfrac(frac); return tmp; } static inline l_fp lfpinit_u(uint32_t sec, uint32_t frac) { l_fp tmp = lfptouint(sec) | lfpfrac(frac); return tmp; } /* * Fractional precision (of an l_fp) is actually the number of * bits in an int32_t/uint32_t. */ #define FRACTION_PREC (32) /* * The second fixed point format is 32 bits, with the decimal between * bits 15 and 16. There is a signed version (s_fp) and an unsigned * version (u_fp). This is used to represent synchronizing distance * and synchronizing dispersion in the NTP packet header (again, in * network byte order) and internally to hold both distance and * dispersion values (in local byte order). In network byte order * it looks like: * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Integer Part | Fraction Part | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ typedef uint32_t u_fp; typedef struct { uint32_t l_ui; uint32_t l_uf; } l_fp_w; #define M_ISNEG(v_i) /* v < 0 */ \ (((v_i) & 0x80000000) != 0) /* * Operations on the long fp format. The only reason these aren't * native operations is to be independent of whether the l_fp * type is signed or unsigned. */ #define L_NEG(v) (v) = (l_fp)(-(int64_t)(v)) #define L_ISNEG(v) M_ISNEG(lfpuint(v)) #define L_ISGT(a, b) ((int64_t)(a) > (int64_t)(b)) #define L_ISGTU(a, b) ((a) > (b)) /* * scaling to 32bit FP format * double to u_fp conversion */ #define FP_SCALE(r) (ldexp((double)(r), 16)) #define FP_UNSCALE(r) (ldexp((double)(r), -16)) #define DTOUFP(r) ((u_fp)FP_SCALE(r)) /* * l_fp/double conversions */ #define FRAC 4294967296.0 /* 2^32 as a double */ #include /* ldexpl() */ static inline l_fp dtolfp(doubletime_t d) /* long double to l_fp * assumes signed l_fp, i.e. a time offset * undefined return if d in NaN */ { return (l_fp)(int64_t)(ldexpl(d, 32)); } static inline doubletime_t lfptod(l_fp r) /* l_fp to long double * assumes signed l_fp, i.e. a time offset */ { return ldexpl((double)((int64_t)r), -32); } /* * Prototypes */ extern char * dolfptoa (l_fp, bool, short, bool); extern char * mfptoa (l_fp, short); extern char * mfptoms (l_fp, short); extern bool hextolfp (const char *, l_fp *); extern void set_prettydate_pivot(time_t); extern char * prettydate (const l_fp); extern char * rfc3339date (const l_fp); extern char * rfc3339time (time_t); extern void set_sys_fuzz (double); extern void get_systime (l_fp *); extern bool step_systime (doubletime_t, int (*settime)(struct timespec *)); extern bool adj_systime (double, int (*adjtime)(const struct timeval *, struct timeval *)); #define lfptoa(fpv, ndec) mfptoa((fpv), (ndec)) #define lfptoms(fpv, ndec) mfptoms((fpv), (ndec)) #define ulfptoa(fpv, ndec) dolfptoa((fpv), false, (ndec), false) #define ulfptoms(fpv, ndec) dolfptoa((fpv), false, (ndec), true) #define umfptoa(lfp, ndec) dolfptoa((lfp), false, (ndec), false) /* * Optional callback from libntp step_systime() to ntpd. Optional * because other libntp clients like ntpdate don't use it. */ typedef void (*time_stepped_callback)(void); extern time_stepped_callback step_callback; extern uint32_t convertLFPToRefID(l_fp num) __attribute__((const)); #endif /* GUARD_NTP_FP_H */ ntpsec-1.1.0+dfsg1/include/timetoa.h0000644000175000017500000000342613252364117017070 0ustar rlaagerrlaager/* * timetoa.h -- time_t related string formatting * * Written by Juergen Perlinger for the NTP project. * * Printing a 'time_t' has some portability pitfalls, due to it's opaque * base type. The only requirement imposed by the standard is that it * must be a numeric type. For all practical purposes it's a signed int, * and 32 bits are common. * * Since the UN*X time epoch will cause a signed integer overflow for * 32-bit signed int values in the year 2038, implementations slowly * move to 64bit base types for time_t, even in 32-bit environments. In * such an environment sizeof(time_t) could be bigger than sizeof(long) * and the commonly used idiom of casting to long leads to truncation. * * As the printf() family has no standardised type specifier for time_t, * guessing the right output format specifier is a bit troublesome and * best done with the help of the preprocessor and "config.h". * * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: NTP */ #ifndef GUARD_TIMETOA_H #define GUARD_TIMETOA_H #include "ntp_fp.h" #include "ntp_stdlib.h" /* * general fractional time stamp formatting. * * secs - integral seconds of time stamp * frac - fractional units * prec - log10 of units per second (3=milliseconds, 6=microseconds,..) * or in other words: the count of decimal digits required. * If prec is < 0, abs(prec) is taken for the precision and secs * is treated as an unsigned value. * * The function will eventually normalise the fraction and adjust the * seconds accordingly. * * This function uses the string buffer library for the return value, * so do not keep the resulting pointers around. */ extern const char * format_time_fraction(time_t secs, long frac, int prec); #endif /* GUARD_TIMETOA_H */ ntpsec-1.1.0+dfsg1/include/ascii.h0000644000175000017500000000152413252364117016513 0ustar rlaagerrlaager/* * Created: Sun Jul 20 11:42:53 1997 * * Copyright (c) 1997-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-clause */ #ifndef GUARD_ASCII_H #define GUARD_ASCII_H /* * just name the common ASCII control codes */ #define NUL 0 #define STX 2 #define ETX 3 #define SOH 1 #define DLE 16 #endif /* * History: * * ascii.h,v * Revision 4.4 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.3 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.1 1998/07/11 10:05:22 kardel * Release 4.0.73d reconciliation * * Revision 4.0 1998/04/10 19:50:38 kardel * Start 4.0 release version numbering * * Revision 4.0 1998/04/10 19:50:38 kardel * Start 4.0 release version numbering * */ ntpsec-1.1.0+dfsg1/include/ntp_config.h0000644000175000017500000001466613252364117017564 0ustar rlaagerrlaager#ifndef GUARD_NTP_CONFIG_H #define GUARD_NTP_CONFIG_H #include #include #include #include #include "ntp_syslog.h" #include "ntp_fp.h" #include "ntp.h" #include "ntp_malloc.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "ntp_machine.h" /* * Configuration file name */ #ifndef CONFIG_FILE # define CONFIG_FILE "/etc/ntp.conf" #endif /* not CONFIG_FILE */ #define CONFIG_DIR "ntp.d" /* Limits */ #define MAXLINE 1024 /* Configuration sources */ #define CONF_SOURCE_FILE 0 #define CONF_SOURCE_NTPQ 1 /* list of servers from command line for config_peers() */ extern int cmdline_server_count; extern char ** cmdline_servers; typedef struct int_range_tag { int first; int last; } int_range; /* Structure for storing an attribute-value pair */ typedef struct attr_val_tag attr_val; struct attr_val_tag { attr_val * link; int attr; int type; /* T_String, T_Integer, ... */ union val { int i; unsigned int u; int_range r; double d; char * s; } value; }; typedef DECL_FIFO_ANCHOR(attr_val) attr_val_fifo; /* Structure for nodes on the syntax tree */ typedef struct address_node_tag address_node; struct address_node_tag { address_node * link; char * address; unsigned short type; /* family, AF_UNSPEC (0), AF_INET[6] */ }; typedef DECL_FIFO_ANCHOR(address_node) address_fifo; typedef struct int_node_tag int_node; struct int_node_tag { int_node * link; int i; }; typedef DECL_FIFO_ANCHOR(int_node) int_fifo; typedef struct string_node_tag string_node; struct string_node_tag { string_node * link; char * s; }; typedef DECL_FIFO_ANCHOR(string_node) string_fifo; typedef struct restrict_node_tag restrict_node; struct restrict_node_tag { restrict_node * link; int mode; /* restrict or unrestrict? */ address_node * addr; address_node * mask; int_fifo * flags; int line_no; }; typedef DECL_FIFO_ANCHOR(restrict_node) restrict_fifo; typedef struct peer_node_tag peer_node; struct peer_node_tag { peer_node * link; address_node * addr; int host_mode; struct peer_ctl ctl; struct refclockstat clock_stat; char * group; }; typedef DECL_FIFO_ANCHOR(peer_node) peer_fifo; typedef struct unpeer_node_tag unpeer_node; struct unpeer_node_tag { unpeer_node * link; associd_t assocID; address_node * addr; }; typedef DECL_FIFO_ANCHOR(unpeer_node) unpeer_fifo; typedef struct auth_node_tag auth_node; struct auth_node_tag { int control_key; char * keys; attr_val_fifo * trusted_key_list; char * ntp_signd_socket; }; typedef struct filegen_node_tag filegen_node; struct filegen_node_tag { filegen_node * link; int filegen_token; attr_val_fifo * options; }; typedef DECL_FIFO_ANCHOR(filegen_node) filegen_fifo; typedef struct setvar_node_tag setvar_node; struct setvar_node_tag { setvar_node * link; char * var; char * val; int isdefault; }; typedef DECL_FIFO_ANCHOR(setvar_node) setvar_fifo; typedef struct nic_rule_node_tag nic_rule_node; struct nic_rule_node_tag { nic_rule_node * link; int match_class; char * if_name; /* or numeric address */ int action; }; typedef DECL_FIFO_ANCHOR(nic_rule_node) nic_rule_fifo; typedef struct addr_opts_node_tag addr_opts_node; struct addr_opts_node_tag { addr_opts_node *link; address_node * addr; attr_val_fifo * options; }; typedef DECL_FIFO_ANCHOR(addr_opts_node) addr_opts_fifo; typedef struct sim_node_tag sim_node; struct sim_node_tag { sim_node * link; attr_val_fifo * init_opts; }; typedef DECL_FIFO_ANCHOR(sim_node) sim_fifo; /* The syntax tree */ typedef struct config_tree_tag config_tree; struct config_tree_tag { config_tree * link; attr_val source; time_t timestamp; peer_fifo * peers; unpeer_fifo * unpeers; /* Other Modes */ attr_val_fifo * orphan_cmds; /* s/b renamed tos_options */ /* Monitoring Configuration */ int_fifo * stats_list; char * stats_dir; filegen_fifo * filegen_opts; /* Access Control Configuration */ attr_val_fifo * discard_opts; attr_val_fifo * mru_opts; restrict_fifo * restrict_opts; addr_opts_fifo *fudge; attr_val_fifo * rlimit; attr_val_fifo * tinker; attr_val_fifo * enable_opts; attr_val_fifo * disable_opts; auth_node auth; attr_val_fifo * logconfig; string_fifo * phone; setvar_fifo * setvar; attr_val_fifo * vars; nic_rule_fifo * nic_rules; int_fifo * reset_counters; sim_fifo * sim_details; int mdnstries; }; extern void init_readconfig(void); extern void set_keys_file(char*); extern void set_trustedkey(keyid_t); extern int mdnstries; /* Structure for holding a remote configuration command */ struct REMOTE_CONFIG_INFO { char buffer[MAXLINE]; char err_msg[MAXLINE]; int pos; int err_pos; int no_errors; }; /* get text from T_ tokens */ const char * token_name(int token); /* generic fifo routines for structs linked by 1st member */ void* append_gen_fifo(void *fifo, void *entry); void * concat_gen_fifos(void *first, void *second); #define APPEND_G_FIFO(pf, pe) \ ((pf) = append_gen_fifo((pf), (pe))) #define CONCAT_G_FIFOS(first, second) \ ((first) = concat_gen_fifos((first), (second))) #define HEAD_PFIFO(pf) \ (((pf) != NULL) \ ? HEAD_FIFO(*(pf)) \ : NULL) address_node *addr_from_typeunit(char *type, int unit); peer_node *create_peer_node(int hmode, address_node *addr, attr_val_fifo *options); unpeer_node *create_unpeer_node(address_node *addr); address_node *create_address_node(char *addr, int type); void destroy_address_node(address_node *my_node); attr_val *create_attr_dval(int attr, double value); attr_val *create_attr_ival(int attr, int value); attr_val *create_attr_uval(int attr, unsigned int value); attr_val *create_attr_rangeval(int attr, int first, int last); attr_val *create_attr_sval(int attr, const char *s); filegen_node *create_filegen_node(int filegen_token, attr_val_fifo *options); string_node *create_string_node(char *str); restrict_node *create_restrict_node(int mode, address_node *addr, address_node *mask, int_fifo *flags, int line_no); int_node *create_int_node(int val); addr_opts_node *create_addr_opts_node(address_node *addr, attr_val_fifo *options); setvar_node *create_setvar_node(char *var, char *val, int isdefault); nic_rule_node *create_nic_rule_node(int match_class, char *if_name, int action); extern struct REMOTE_CONFIG_INFO remote_config; void config_remotely(sockaddr_u *); extern bool have_interface_option; extern char *stats_drift_file; /* name of the driftfile */ void ntp_rlimit(int, rlim_t, int, const char *); #endif /* GUARD_NTP_CONFIG_H */ ntpsec-1.1.0+dfsg1/include/README0000644000175000017500000000017513252364117016133 0ustar rlaagerrlaager= README file for directory include = This directory contains the include files used by most programs in this distribution. ntpsec-1.1.0+dfsg1/include/ntp_debug.h0000644000175000017500000000173113252364117017372 0ustar rlaagerrlaager/* * Created: Sat Aug 20 14:23:01 2005 * * Copyright (C) 2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-clause */ #ifndef GUARD_NTP_DEBUG_H #define GUARD_NTP_DEBUG_H /* * macro for debugging output - cut down on #ifdef pollution. * * DPRINT() is the new one debug logger to rule them all. * Uses mprintf() and so supports %m, replaced by strerror(errno). * * The calling convention is not attractive: * DPRINT(debuglevel, (fmt, ...)); * DPRINT(2, ("this will appear on stdout if debug >= %d\n", 2)); * * TPRINT is used where the logger needs to remain even when DEBUG is off. */ #ifdef DEBUG #define DPRINT(lvl, arg) \ do { \ if (debug >= (lvl)) \ mprintf arg; \ } while (0) #else #define DPRINT(lvl, arg) do {} while (0) #endif /* DEBUG */ #define TPRINT(lvl, arg) \ do { \ if (debug >= (lvl)) \ mprintf arg; \ } while (0) #endif /* GUARD_NTP_DEBUG_H */ ntpsec-1.1.0+dfsg1/include/ntp_machine.h0000644000175000017500000000262213252364117017710 0ustar rlaagerrlaager/* * ntp_machine.h * * Collect all machine dependent idiosyncrasies in one place. * */ #ifndef GUARD_NTP_MACHINE_H #define GUARD_NTP_MACHINE_H #include #include #ifndef CLOCK_REALTIME /* * Pacify platforms that don't have a real clock_gettime(2), * notably Mac OS X. */ #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 1 typedef int clockid_t; int clock_gettime(clockid_t clock_id, struct timespec *tp); #endif int ntp_set_tod (struct timespec *tvs); #ifdef NO_MAIN_ALLOWED /* we have no main routines so lets make a plan */ #define CALL(callname, progname, callmain) \ extern int callmain (int,char**); \ void callname (a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) \ char *a0; \ char *a1; \ char *a2; \ char *a3; \ char *a4; \ char *a5; \ char *a6; \ char *a7; \ char *a8; \ char *a9; \ char *a10; \ { \ char *x[11]; \ int argc; \ char *argv[] = {progname,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; \ int i; \ for (i=0;i<11;i++) \ x[i] = NULL; \ x[0] = a0; \ x[1] = a1; \ x[2] = a2; \ x[3] = a3; \ x[4] = a4; \ x[5] = a5; \ x[6] = a6; \ x[7] = a7; \ x[8] = a8; \ x[9] = a9; \ x[10] = a10; \ argc=1; \ for (i=0; i<11;i++) \ if (x[i]) \ { \ argv[argc++] = x[i]; \ } \ callmain(argc,argv); \ } #endif /* NO_MAIN_ALLOWED */ #endif /* GUARD_NTP_MACHINE_H */ ntpsec-1.1.0+dfsg1/include/ntp_stdlib.h0000644000175000017500000001466313252364117017575 0ustar rlaagerrlaager/* * ntp_stdlib.h - Prototypes for NTP lib. */ #ifndef GUARD_NTP_STDLIB_H #define GUARD_NTP_STDLIB_H #include #include #include #include #include #include #include "declcond.h" /* ntpd uses ntpd/declcond.h, others include/ */ #include "ntp_net.h" #include "ntp_debug.h" #include "ntp_malloc.h" #include "ntp_syslog.h" #ifdef HAVE_BSD_STRING_H #include #endif #ifdef __GNUC__ #define NTP_PRINTF(fmt, args) __attribute__((__format__(__printf__, fmt, args))) #else #define NTP_PRINTF(fmt, args) #endif extern const char *ntpd_version(void); extern int mprintf(const char *, ...) NTP_PRINTF(1, 2); extern int mvsnprintf(char *, size_t, const char *, va_list) NTP_PRINTF(3, 0); extern void msyslog(int, const char *, ...) NTP_PRINTF(2, 3); extern void init_logging (const char *, uint32_t, int); extern int change_logfile (const char *, bool); extern void reopen_logfile (void); extern void setup_logfile (const char *); /* authkeys.c */ extern void auth_delkeys (void); extern bool authdecrypt (keyid_t, uint32_t *, int, int); extern int authencrypt (keyid_t, uint32_t *, int); extern bool authhavekey (keyid_t); extern bool authistrusted (keyid_t); extern bool authreadkeys (const char *); extern void authtrust (keyid_t, bool); extern bool authusekey (keyid_t, int, const uint8_t *); extern int clocktime (int, int, int, int, int, time_t, uint32_t, uint32_t *, uint32_t *); extern void init_auth (void); extern void init_network (void); extern void auth_prealloc_symkeys(int); extern int ymd2yd (int, int, int); /* getopt.c */ struct option { const char* name; int has_arg; int* flag; int val; }; int ntp_getopt(int argc, char *const argv[], const char *optstring); int ntp_getopt_long(int argc, char* const argv[], const char *optstring, const struct option *longopts, int *longindex); /* mac_md5encrypt.c */ extern bool mac_authdecrypt (int, uint8_t *, int, uint32_t *, int, int); extern int mac_authencrypt (int, uint8_t *, int, uint32_t *, int); extern void mac_setkey (keyid_t, int, const uint8_t *, size_t); extern uint32_t addr2refid (sockaddr_u *); /* emalloc.c */ #ifndef EREALLOC_CALLSITE /* ntp_malloc.h defines */ extern void * ereallocz (void *, size_t, size_t, int); extern void * oreallocarray (void *optr, size_t nmemb, size_t size); #define erealloczsite(p, n, o, z, f, l) ereallocz((p), (n), (o), (z)) #define emalloc(n) ereallocz(NULL, (n), 0, false) #define emalloc_zero(c) ereallocz(NULL, (c), 0, true) #define erealloc(p, c) ereallocz((p), (c), 0, false) #define erealloc_zero(p, n, o) ereallocz((p), (n), (o), true) #define ereallocarray(p, n, s) oreallocarray((p), (n), (s)) #define eallocarray(n, s) oreallocarray(NULL, (n), (s)) extern char * estrdup_impl(const char *); #define estrdup(s) estrdup_impl(s) #else extern void * ereallocz (void *, size_t, size_t, int, const char *, int); extern void * oreallocarray (void *optr, size_t nmemb, size_t size, const char *, int); #define erealloczsite ereallocz #define emalloc(c) ereallocz(NULL, (c), 0, false, \ __FILE__, __LINE__) #define emalloc_zero(c) ereallocz(NULL, (c), 0, true, \ __FILE__, __LINE__) #define erealloc(p, c) ereallocz((p), (c), 0, false, \ __FILE__, __LINE__) #define erealloc_zero(p, n, o) ereallocz((p), n, (o), true, \ __FILE__, __LINE__) #define ereallocarray(p, n, s) oreallocarray((p), (n), (s), \ __FILE__, __LINE__) #define eallocarray(n, s) oreallocarray(NULL, (n), (s), \ __FILE__, __LINE__) extern char * estrdup_impl(const char *, const char *, int); #define estrdup(s) estrdup_impl((s), __FILE__, __LINE__) #endif extern const char * eventstr (int); extern const char * ceventstr (int); extern const char * res_match_flags(unsigned short); extern const char * res_access_flags(unsigned short); extern const char * k_st_flags (uint32_t); extern char * statustoa (int, int); extern sockaddr_u * netof6 (sockaddr_u *); extern char * numtoa (uint32_t); extern const char * socktoa (const sockaddr_u *); extern const char * sockporttoa(const sockaddr_u *); extern unsigned short sock_hash(const sockaddr_u *) __attribute__((pure)); extern const char *refid_str (uint32_t, int); extern int decodenetnum (const char *, sockaddr_u *); extern void signal_no_reset (int, void (*func)(int)); extern void signal_no_reset1(int, void (*func)(int, siginfo_t *, void *)); extern void getauthkeys (const char *); /* * Variable declarations for libntp. */ /* authkeys.c */ extern unsigned int authkeynotfound; /* keys not found */ extern unsigned int authkeylookups; /* calls to lookup keys */ extern unsigned int authnumkeys; /* number of active keys */ extern unsigned int authkeyuncached; /* cache misses */ extern unsigned int authencryptions; /* calls to encrypt */ extern unsigned int authdecryptions; /* calls to decrypt */ extern int authnumfreekeys; /* getopt.c */ extern char * ntp_optarg; /* global argument pointer */ extern int ntp_optind; /* global argv index */ /* lib_strbuf.c */ extern bool ipv4_works; extern bool ipv6_works; /* ssl_init.c */ extern void ssl_init (void); /* strl-obsd.c */ #ifndef HAVE_STRLCPY /* + */ /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ extern size_t strlcpy(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_STRLCAT /* + */ /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ extern size_t strlcat(char *dst, const char *src, size_t siz); #endif /* ntp_proto.c */ extern double measured_tick; /* non-overridable sys_tick */ /* systime.c */ extern double sys_tick; /* tick size or time to read */ extern double sys_fuzz; /* min clock read latency */ extern bool trunc_os_clock; /* sys_tick > measured_tick */ /* use these as return values for sort-comparison functions */ #define COMPARE_GREATERTHAN 1 #define COMPARE_EQUAL 0 #define COMPARE_LESSTHAN -1 /* hack to ignore GCC Unused Result */ #define IGNORE(r) do{if(r){}}while(0) extern bool sandbox(const bool droproot, char *user, const char *group, const char *chrootdir, bool want_dynamic_interface_tracking); #endif /* GUARD_NTP_STDLIB_H */ ntpsec-1.1.0+dfsg1/include/ntp.h0000644000175000017500000006163313252364117016233 0ustar rlaagerrlaager/* * ntp.h - NTP definitions for the masses * * pythonize-header: start ignoring */ #ifndef GUARD_NTP_H #define GUARD_NTP_H #include #include #include "ntp_fp.h" #include "ntp_types.h" #include "ntp_lists.h" #include "ntp_stdlib.h" #include "ntp_net.h" extern int32_t ntp_random (void); /* * Calendar arithmetic - contributed by G. Healton */ #define YEAR_BREAK 500 /* years < this are tm_year values: * Break < AnyFourDigitYear && Break > * Anytm_yearYear */ #define YEAR_PIVOT 98 /* 97/98: years < this are year 2000+ * FYI: official UNIX pivot year is * 68/69 */ /* * Number of Days since 1 BC Gregorian to 1 January of given year */ #define julian0(year) (((year) * 365 ) + ((year) > 0 ? (((year) + 3) \ / 4 - ((year - 1) / 100) + ((year - 1) / \ 400)) : 0)) /* * to convert simple two-digit years to tm_year style years: * * if (year < YEAR_PIVOT) * year += 100; * * to convert either two-digit OR tm_year years to four-digit years: * * if (year < YEAR_PIVOT) * year += 100; * * if (year < YEAR_BREAK) * year += 1900; */ /* pythonize-header: stop ignoring */ /* * NTP protocol parameters. See section 3.2.6 of the specification. */ #define NTP_VERSION 4 /* current version number */ #define NTP_OLDVERSION 1 /* oldest credible version */ #define NTP_PORT 123 /* included for non-unix machines */ /* pythonize-header: start ignoring */ /* * Poll interval parameters */ #define NTP_UNREACH 10 /* poll unreach threshold */ #define NTP_MINPOLL 0 /* log2 min poll interval (1 s) */ #define NTP_MINDPOLL 6 /* log2 default min poll (64 s) */ #define NTP_MAXDPOLL 10 /* log2 default max poll (~17 m) */ #define NTP_MAXPOLL 17 /* log2 max poll interval (~36 h) */ #define NTP_MAXPOLL_UNK 99 /* log2 max poll unset */ #define NTP_RETRY 3 /* max packet retries */ #define NTP_MINPKT 2 /* guard time (s) */ /* * Clock filter algorithm tuning parameter */ #define NTP_SHIFT 8 /* clock filter stages */ /* * Selection algorithm tuning parameters */ #define MAXDISTANCE 1.5 /* max root distance (select threshold) */ #define HUFFPUFF 900 /* huff-n'-puff sample interval (s) */ /* * Miscellaneous stuff */ /* * Limits of things */ #define MAXFILENAME 256 /* max length of file name */ /* * Operations for jitter calculations (these use doubles). * * Note that we carefully separate the jitter component from the * dispersion component (frequency error plus precision). The frequency * error component is computed as CLOCK_PHI times the difference between * the epoch of the time measurement and the reference time. The * precision component is computed as the square root of the mean of the * squares of a zero-mean, uniform distribution of unit maximum * amplitude. Whether this makes statistical sense may be arguable. */ #define SQUARE(x) ((x) * (x)) #define SQRT(x) (sqrt(x)) #define LOGTOD(a) ldexp(1., (int)(a)) /* log2 to double */ #define ULOGTOD(a) ldexp(1., (int)(a)) /* ulog2 to double */ /* * The endpt structure is used to hold the addresses and socket * numbers of each of the local network addresses we are using. * endpt is unrelated to the select algorithm's struct endpoint. */ typedef struct __endpt { struct __endpt *elink; /* endpt list link */ SOCKET fd; /* socket descriptor */ SOCKET bfd; /* for receiving broadcasts */ uint32_t ifnum; /* endpt instance count */ sockaddr_u sin; /* unicast address */ sockaddr_u mask; /* subnet mask */ sockaddr_u bcast; /* broadcast address */ char name[32]; /* name of interface */ unsigned short family; /* AF_INET/AF_INET6 */ unsigned short phase; /* phase in update cycle */ uint32_t flags; /* interface flags */ uint32_t addr_refid; /* IPv4 addr or IPv6 hash */ unsigned long starttime; /* current_time at creation */ volatile long received; /* number of incoming packets */ long sent; /* number of outgoing packets */ long notsent; /* number of send failures */ unsigned int ifindex; /* for IPV6_MULTICAST_IF */ bool ignore_packets; /* listen-read-drop this? */ struct peer * peers; /* list of peers using endpt */ unsigned int peercnt; /* count of same */ } endpt; /* * Flags for interfaces. Do not change these casually as they will be visible * in Mode 6 ifstats reports. */ #define INT_UP 0x001 /* Interface is up */ #define INT_PPP 0x002 /* Point-to-point interface */ #define INT_LOOPBACK 0x004U /* the loopback interface */ #define INT_BROADCAST 0x008 /* can broadcast out this interface */ #define INT_MULTICAST 0x010 /* can multicast out this interface */ #define INT_BCASTOPEN 0x020U /* broadcast receive socket is open */ #define INT_MCASTOPEN 0x040 /* multicasting enabled */ #define INT_WILDCARD 0x080 /* wildcard interface - usually skipped */ #define INT_MCASTIF 0x100 /* bound directly to MCAST address */ #define INT_PRIVACY 0x200 /* RFC 4941 IPv6 privacy address */ #define INT_BCASTXMIT 0x400 /* socket setup to allow broadcasts */ /* * Read-only control knobs for a peer structure. * Packaging these makes context copies a bit more succinct. */ struct peer_ctl { uint8_t version; uint8_t minpoll; uint8_t maxpoll; uint32_t flags; keyid_t peerkey; double bias; uint32_t mode; /* only used by refclocks */ #ifdef REFCLOCK uint32_t baud; char *path; char *ppspath; #endif /* REFCLOCK */ }; /* * Define flasher bits (tests 1 through 11 in packet procedure) * These reveal the state at the last grumble from the peer and are * most handy for diagnosing problems, even if not strictly a state * variable in the spec. These are recorded in the peer structure. * * Packet errors */ #define BOGON1 0X0001 /* duplicate packet */ #define BOGON2 0x0002 /* bogus packet */ #define BOGON3 0x0004 /* protocol unsynchronized */ #define BOGON4 0x0008 /* access denied */ #define BOGON5 0x0010 /* bad authentication */ #define BOGON6 0x0020 /* bad synch or stratum */ #define BOGON7 0x0040 /* bad header */ #define BOGON8 0x0080 /* bad autokey */ #define BOGON9 0x0100 /* bad crypto */ #define PKT_BOGON_MASK (BOGON1 | BOGON2 | BOGON3 | BOGON4 | BOGON5 |\ BOGON6 | BOGON7 | BOGON8 | BOGON9) /* * Peer errors */ #define BOGON10 0x0200 /* peer bad synch or stratum */ #define BOGON11 0x0400 /* peer distance exceeded */ #define BOGON12 0x0800 /* peer synchronization loop */ #define BOGON13 0x1000 /* peer unreacable */ #define PEER_BOGON_MASK (BOGON10 | BOGON11 | BOGON12 | BOGON13) /* * Does a peer node represent a reference clock? */ #ifdef REFCLOCK #define IS_PEER_REFCLOCK(p) ((p)->procptr != NULL) #else #define IS_PEER_REFCLOCK(p) false #endif /* * The peer structure. Holds state information relating to the guys * we are peering with. Most of this stuff is from section 3.2 of the * spec. * * The mode field is overloaded; it's used in the refclock case to pass * in a mode word that may contain a baud rate or subtype as well as flags. * Splitting this field would complicate some call sequences that are * already unpleasantly intricate. * It used to be called ttl which was used by multicast which we dropped. */ struct peer { struct peer *p_link; /* link pointer in free & peer lists */ struct peer *adr_link; /* link pointer in address hash */ struct peer *aid_link; /* link pointer in associd hash */ struct peer *ilink; /* list of peers for interface */ struct peer_ctl cfg; /* peer configuration block */ sockaddr_u srcadr; /* address of remote host */ char * hostname; /* if non-NULL, remote name */ endpt * dstadr; /* local address */ associd_t associd; /* association ID */ uint8_t hmode; /* local association mode */ uint8_t hpoll; /* local poll interval */ uint8_t cast_flags; /* additional flags */ uint8_t last_event; /* last peer error code */ uint8_t num_events; /* number of error events */ /* * Variables used by reference clock support */ #ifdef REFCLOCK struct refclockproc *procptr; /* refclock structure pointer */ bool is_pps_driver; /* is this the PPS driver? */ uint8_t sstclktype; /* clock type for system status word */ #endif /* REFCLOCK */ /* * Variables set by received packet */ uint8_t leap; /* local leap indicator */ uint8_t pmode; /* remote association mode */ uint8_t stratum; /* remote stratum */ uint8_t ppoll; /* remote poll interval */ int8_t precision; /* remote clock precision */ double rootdelay; /* roundtrip delay to primary source */ double rootdisp; /* dispersion to primary source */ uint32_t refid; /* remote reference ID */ l_fp reftime; /* update epoch */ #define clear_to_zero status /* * Ephemeral state variables */ uint8_t status; /* peer status */ uint8_t new_status; /* under-construction status */ uint8_t reach; /* reachability register */ int flash; /* protocol error test tally bits */ uptime_t epoch; /* reference epoch */ int burst; /* packets remaining in burst */ int retry; /* retry counter */ int filter_nextpt; /* index into filter shift register */ double filter_delay[NTP_SHIFT]; /* delay shift register */ double filter_offset[NTP_SHIFT]; /* offset shift register */ double filter_disp[NTP_SHIFT]; /* dispersion shift register */ uptime_t filter_epoch[NTP_SHIFT]; /* epoch shift register */ uint8_t filter_order[NTP_SHIFT]; /* filter sort index */ l_fp rec; /* receive time stamp */ l_fp xmt; /* transmit time stamp */ l_fp dst; /* destination timestamp */ l_fp org; /* origin timestamp */ double offset; /* peer clock offset */ double delay; /* peer roundtrip delay */ double jitter; /* peer jitter (squares) */ double disp; /* peer dispersion */ /* * Variables used to correct for packet length and asymmetry. */ double t21; /* outbound packet delay */ int t21_bytes; /* outbound packet length */ int t21_last; /* last outbound packet length */ double r21; /* outbound data rate */ double t34; /* inbound packet delay */ int t34_bytes; /* inbound packet length */ double r34; /* inbound data rate */ /* * End of clear-to-zero area */ unsigned int outcount; /* packets sent without reply */ unsigned long update; /* receive epoch */ #define end_clear_to_zero update int unreach; /* watchdog counter */ int throttle; /* rate control */ uptime_t outdate; /* send time last packet */ uptime_t nextdate; /* send time next packet */ /* * Statistic counters */ unsigned long timereset; /* time stat counters were reset */ unsigned long timereceived; /* last packet received time */ unsigned long timereachable; /* last reachable/unreachable time */ unsigned long sent; /* packets sent */ unsigned long received; /* packets received */ unsigned long processed; /* packets processed */ unsigned long badauth; /* bad authentication (BOGON5) */ unsigned long bogusorg; /* bogus origin (BOGON2, BOGON3) */ unsigned long oldpkt; /* old duplicate (BOGON1) */ unsigned long seldisptoolarge; /* bad header (BOGON6, BOGON7) */ unsigned long selbroken; /* KoD received */ }; /* pythonize-header: stop ignoring */ /* * Values for peer.leap, sys_leap */ #define LEAP_NOWARNING 0x0 /* normal, no leap second warning */ #define LEAP_ADDSECOND 0x1 /* last minute of day has 61 seconds */ #define LEAP_DELSECOND 0x2 /* last minute of day has 59 seconds */ #define LEAP_NOTINSYNC 0x3 /* overload, clock is free running */ /* * Values for peer mode and packet mode. Only the modes through * MODE_BROADCAST and MODE_BCLIENT appear in the transition * function. MODE_CONTROL and MODE_PRIVATE can appear in packets, * but those never survive to the translation function. * See MATCH_ASSOC in ntp_peer. */ #define MODE_UNSPEC 0 /* unspecified (old version) */ #define MODE_ACTIVE 1 /* symmetric active mode */ #define MODE_PASSIVE 2 /* symmetric passive mode */ #define MODE_CLIENT 3 /* client mode */ #define MODE_SERVER 4 /* server mode */ #define MODE_BROADCAST 5 /* broadcast mode */ /* * These can appear in packets */ #define MODE_CONTROL 6 /* control mode, ntpq*/ #define MODE_PRIVATE 7 /* Dead: private mode, was ntpdc */ /* * This is a madeup mode for broadcast client. No longer used by ntpd. */ /* #define MODE_BCLIENT 6 ** broadcast client mode */ #define MODE_BCLIENTX 6 /* for pylib/util.py */ #define LEN_PKT_NOMAC 48 /* min header length */ /* pythonize-header: start ignoring */ /* * Values for peer.stratum, sys_stratum */ #define STRATUM_REFCLOCK ((uint8_t)0) /* default stratum */ /* A stratum of 0 in the packet is mapped to 16 internally */ #define STRATUM_PKT_UNSPEC ((uint8_t)0) /* unspecified in packet */ #define STRATUM_UNSPEC ((uint8_t)16) /* unspecified */ /* * Values for peer.flags (unsigned int) */ #define FLAG_CONFIG 0x0001u /* association was configured */ #define FLAG_PREEMPT 0x0002u /* preemptable association */ #define FLAG_AUTHENTIC 0x0004u /* last message was authentic */ #define FLAG_REFCLOCK 0x0008u /* this is actually a reference clock */ #define FLAG_BC_VOL 0x0010u /* broadcast client volleying */ #define FLAG_PREFER 0x0020u /* prefer peer */ #define FLAG_BURST 0x0040u /* burst mode */ #define FLAG_PPS 0x0080u /* steered by PPS */ #define FLAG_IBURST 0x0100u /* initial burst mode */ #define FLAG_NOSELECT 0x0200u /* never select */ #define FLAG_TRUE 0x0400u /* force truechimer */ #define FLAG_DNS 0x0800u /* needs DNS lookup */ #define FLAG_TSTAMP_PPS 0x4cd000u /* PPS source provides absolute timestamp */ /* * It's ugly that refid is sometimes treated as a uint32_t and sometimes * as a string; that should be fixed. Using this in memcpy() at least * contains the problem. */ #define REFIDLEN sizeof(uint32_t) /* This is the new, sane way of representing packets. All fields are in host byte order, and the fixed-point time fields are just integers, with uints of 2^-16 or 2^-32 seconds as appropriate. */ struct parsed_pkt { uint8_t li_vn_mode; uint8_t stratum; uint8_t ppoll; int8_t precision; uint32_t rootdelay; uint32_t rootdisp; char refid[REFIDLEN]; uint64_t reftime; uint64_t org; uint64_t rec; uint64_t xmt; unsigned num_extensions; struct exten *extensions; bool keyid_present; uint32_t keyid; size_t mac_len; char mac[20]; }; struct exten { uint16_t type; uint16_t len; uint8_t *body; }; /* This is the old, insane way of representing packets. It'll gradually be phased out and removed. Packets are simply pulled off the wire and then type-punned into this structure, so all fields are in network byte order. Note that there is no pack pragma. The only reason this ever worked at all is that all the fields are self-aligned, so no ABI has been evil enough to insert padding between fields. */ struct pkt { uint8_t li_vn_mode; /* peer leap indicator */ uint8_t stratum; /* peer stratum */ uint8_t ppoll; /* peer poll interval */ int8_t precision; /* peer clock precision */ u_fp rootdelay; /* roundtrip delay to primary source */ u_fp rootdisp; /* dispersion to primary source*/ uint32_t refid; /* reference id */ l_fp_w reftime; /* last update time */ l_fp_w org; /* originate time stamp */ l_fp_w rec; /* receive time stamp */ l_fp_w xmt; /* transmit time stamp */ #define MIN_MAC_LEN (1 * sizeof(uint32_t)) /* crypto_NAK */ #define MAX_MD5_LEN (5 * sizeof(uint32_t)) /* MD5 */ #define MAX_MAC_LEN (6 * sizeof(uint32_t)) /* SHA */ uint32_t exten[(MAX_MAC_LEN) / sizeof(uint32_t)]; } __attribute__ ((aligned)); /* pythonize-header: stop ignoring */ /* * Stuff for extracting things from li_vn_mode */ #define PKT_MODE(li_vn_mode) ((li_vn_mode) & 0x7) #define PKT_VERSION(li_vn_mode) (((li_vn_mode) >> 3) & 0x7) #define PKT_LEAP(li_vn_mode) (((li_vn_mode) >> 6) & 0x3) /* * Stuff for putting things back into li_vn_mode in packets and vn_mode * in ntp_monitor.c's mon_entry. */ #define VN_MODE(v, m) ((((v) & 7) << 3) | ((m) & 0x7)) #define PKT_LI_VN_MODE(l, v, m) ((((l) & 3) << 6) | VN_MODE((v), (m))) /* * Event codes. Used for reporting errors/events to the control module */ #define PEER_EVENT 0x080 /* this is a peer event */ #define CRPT_EVENT 0x100 /* this is a crypto event */ /* * System event codes */ #define EVNT_UNSPEC 0 /* unspecified */ #define EVNT_NSET 1 /* freq not set */ #define EVNT_FSET 2 /* freq set */ #define EVNT_SPIK 3 /* spike detect */ #define EVNT_FREQ 4 /* freq mode */ #define EVNT_SYNC 5 /* clock sync */ #define EVNT_SYSRESTART 6 /* restart */ #define EVNT_SYSFAULT 7 /* panic stop */ #define EVNT_NOPEER 8 /* no sys peer */ #define EVNT_ARMED 9 /* leap armed */ #define EVNT_DISARMED 10 /* leap disarmed */ #define EVNT_LEAP 11 /* leap event */ #define EVNT_CLOCKRESET 12 /* clock step */ #define EVNT_KERN 13 /* kernel event */ #define EVNT_TAI 14 /* TAI */ #define EVNT_LEAPVAL 15 /* stale leapsecond values */ /* * Peer event codes */ #define PEVNT_MOBIL (1 | PEER_EVENT) /* mobilize */ #define PEVNT_DEMOBIL (2 | PEER_EVENT) /* demobilize */ #define PEVNT_UNREACH (3 | PEER_EVENT) /* unreachable */ #define PEVNT_REACH (4 | PEER_EVENT) /* reachable */ #define PEVNT_RESTART (5 | PEER_EVENT) /* restart */ #define PEVNT_REPLY (6 | PEER_EVENT) /* no reply */ #define PEVNT_RATE (7 | PEER_EVENT) /* rate exceeded */ #define PEVNT_DENY (8 | PEER_EVENT) /* access denied */ #define PEVNT_ARMED (9 | PEER_EVENT) /* leap armed */ #define PEVNT_NEWPEER (10 | PEER_EVENT) /* sys peer */ #define PEVNT_CLOCK (11 | PEER_EVENT) /* clock event */ #define PEVNT_AUTH (12 | PEER_EVENT) /* bad auth */ #define PEVNT_POPCORN (13 | PEER_EVENT) /* popcorn */ /* * Clock event codes */ #define CEVNT_NOMINAL 0 /* unspecified */ #define CEVNT_TIMEOUT 1 /* no reply */ #define CEVNT_BADREPLY 2 /* bad format */ #define CEVNT_FAULT 3 /* fault */ #define CEVNT_PROP 4 /* bad signal */ #define CEVNT_BADDATE 5 /* bad date */ #define CEVNT_BADTIME 6 /* bad time */ #define CEVNT_MAX CEVNT_BADTIME /* pythonize-header: start ignoring */ /* * To speed lookups, peers are hashed by the low order bits of the * remote IP address. These definitions relate to that. */ #define NTP_HASH_SIZE 128 #define NTP_HASH_MASK (NTP_HASH_SIZE-1) #define NTP_HASH_ADDR(src) (sock_hash(src) & NTP_HASH_MASK) /* * min, and max. Makes it easier to transliterate the spec without * thinking about it. */ #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) /* * Configuration items. These are for the protocol module (proto_config()) */ /* #define PROTO_BROADCLIENT 1 */ /* #define PROTO_PRECISION 2 */ /* #define PROTO_AUTHENTICATE 3 */ /* #define PROTO_BROADDELAY 4 */ /* #define PROTO_AUTHDELAY 5 */ /* #define PROTO_MULTICAST_ADD 6 */ /* #define PROTO_MULTICAST_DEL 7 */ #define PROTO_NTP 8 #define PROTO_KERNEL 9 #define PROTO_MONITOR 10 #define PROTO_FILEGEN 11 #define PROTO_PPS 12 #define PROTO_CAL 13 #define PROTO_MINCLOCK 14 #define PROTO_MAXCLOCK 15 #define PROTO_MINSANE 16 #define PROTO_FLOOR 17 #define PROTO_CEILING 18 /* #define PROTO_COHORT 19 */ #define PROTO_CALLDELAY 20 #define PROTO_MINDISP 21 #define PROTO_MAXDIST 22 #define PROTO_MAXDISP 23 #define PROTO_MAXHOP 24 /* #define PROTO_BEACON 25 */ #define PROTO_ORPHAN 26 #define PROTO_ORPHWAIT 27 /* #define PROTO_MODE7 28 was ntpdc */ /* * Configuration items for the loop filter */ #define LOOP_DRIFTINIT 1 /* iniitialize frequency */ #define LOOP_KERN_CLEAR 2 /* set initial frequency offset */ #define LOOP_MAX 3 /* set both step offsets */ #define LOOP_MAX_BACK 4 /* set bacward-step offset */ #define LOOP_MAX_FWD 5 /* set forward-step offset */ #define LOOP_PANIC 6 /* set panic offseet */ #define LOOP_PHI 7 /* set dispersion rate */ #define LOOP_MINSTEP 8 /* set step timeout */ #define LOOP_MINPOLL 9 /* set min poll interval (log2 s) */ #define LOOP_ALLAN 10 /* set minimum Allan intercept */ #define LOOP_HUFFPUFF 11 /* set huff-n'-puff filter length */ #define LOOP_FREQ 12 /* set initial frequency */ #define LOOP_LEAP 13 /* insert leap after second 23:59 */ #define LOOP_TICK 14 /* sim. low precision clock */ /* * Configuration items for the stats printer */ #define STATS_FREQ_FILE 1 /* configure drift file */ #define STATS_STATSDIR 2 /* directory prefix for stats files */ #define STATS_PID_FILE 3 /* configure ntpd PID file */ #define STATS_LEAP_FILE 4 /* configure ntpd leapseconds file */ /* * Structure used optionally for monitoring when this is turned on. */ typedef struct mon_data mon_entry; struct mon_data { mon_entry * hash_next; /* next structure in hash list */ DECL_DLIST_LINK(mon_entry, mru);/* MRU list link pointers */ endpt * lcladr; /* address on which this arrived */ l_fp first; /* first time seen */ l_fp last; /* last time seen */ int leak; /* leaky bucket accumulator */ int count; /* total packet count */ unsigned short flags; /* restrict flags */ uint8_t vn_mode; /* packet mode & version */ uint8_t cast_flags; /* flags MDF_?CAST */ sockaddr_u rmtadr; /* address of remote host */ }; /* * Values for cast_flags in mon_entry and struct peer. mon_entry uses * only MDF_UCAST and MDF_BCAST. */ #define MDF_UCAST 0x01 /* unicast client */ /* #define MDF_MCAST 0x02 ** multicast server (not used) */ #define MDF_BCAST 0x04 /* broadcast server */ #define MDF_POOL 0x08 /* pool client solicitor */ /* #define MDF_ACAST 0x10 ** manycast client solicitor (not used) */ #define MDF_BCLNT 0x20 /* eph. broadcast/multicast client */ #define MDF_UCLNT 0x40 /* preemptible manycast or pool client */ /* * In the context of struct peer in ntpd, one cast_flags bit * represent configured associations which never receive packets, and * whose reach is always 0: MDF_BCAST */ #define MDF_TXONLY_MASK (MDF_BCAST | MDF_POOL) /* * manycastclient-like solicitor association cast_flags bits */ #define MDF_SOLICIT_MASK MDF_POOL /* * Values used with mon_enabled to indicate reason for enabling monitoring */ #define MON_OFF 0x00 /* no monitoring */ #define MON_ON 0x01 /* monitoring explicitly enabled */ #define MON_RES 0x02 /* implicit monitoring for RES_LIMITED */ /* * Structure used for restrictlist entries */ typedef struct res_addr4_tag { uint32_t addr; /* IPv4 addr (host order) */ uint32_t mask; /* IPv4 mask (host order) */ } res_addr4; typedef struct res_addr6_tag { struct in6_addr addr; /* IPv6 addr (net order) */ struct in6_addr mask; /* IPv6 mask (net order) */ } res_addr6; typedef struct restrict_u_tag restrict_u; struct restrict_u_tag { restrict_u * link; /* link to next entry */ uint32_t hitcount; /* number of packets matched */ unsigned short flags; /* accesslist flags */ unsigned short mflags; /* match flags */ unsigned long expire; /* valid until time */ union { /* variant starting here */ res_addr4 v4; res_addr6 v6; } u; }; #define V4_SIZEOF_RESTRICT_U (offsetof(restrict_u, u) \ + sizeof(res_addr4)) #define V6_SIZEOF_RESTRICT_U (offsetof(restrict_u, u) \ + sizeof(res_addr6)) /* pythonize-header: stop ignoring */ /* * Access flags. Do not change or garbage-collect these, they are exposed * through the Mode 6 protocol. */ #define RES_IGNORE 0x0001 /* ignore packet */ #define RES_DONTSERVE 0x0002 /* access denied */ #define RES_DONTTRUST 0x0004 /* authentication required */ #define RES_VERSION 0x0008 /* version mismatch */ #define RES_NOPEER 0x0010 /* new association denied */ #define RES_LIMITED 0x0020 /* packet rate exceeded */ #define RES_FLAGS (RES_IGNORE | RES_DONTSERVE |\ RES_DONTTRUST | RES_VERSION |\ RES_NOPEER | RES_LIMITED) #define RES_NOQUERY 0x0040 /* mode 6 packet denied */ #define RES_NOMODIFY 0x0080 /* mode 6 modify denied */ #define RES_NOTRAP 0x0100 /* mode 6 set trap denied (not used) */ #define RES_LPTRAP 0x0200 /* mode 6 low priority trap (not used) */ #define RES_KOD 0x0400 /* send kiss of death packet */ #define RES_MSSNTP 0x0800 /* enable MS-SNTP authentication */ #define RES_FLAKE 0x1000 /* flakeway - drop 10% */ #define RES_NOMRULIST 0x2000 /* mode 6 mrulist denied */ #define RES_ALLFLAGS (RES_FLAGS | RES_NOQUERY | \ RES_NOMODIFY | RES_KOD | \ RES_MSSNTP | RES_FLAKE | \ RES_NOMRULIST) /* pythonize-header: start ignoring */ /* * Match flags */ #define RESM_INTERFACE 0x1000 /* this is an interface */ #define RESM_NTPONLY 0x2000 /* match source port 123 */ #define RESM_SOURCE 0x4000 /* from "restrict source" */ /* * Restriction configuration ops */ #define RESTRICT_FLAGS 1 /* add flags to restrict entry */ #define RESTRICT_UNFLAG 2 /* remove flags from restrict entry */ #define RESTRICT_REMOVE 3 /* remove a restrict entry */ #define RESTRICT_REMOVEIF 4 /* remove an interface restrict entry */ /* * Endpoint structure for the select algorithm */ struct endpoint { double val; /* offset of endpoint */ int type; /* interval entry/exit */ }; /* * Association matching AM[] return codes */ #define AM_ERR -1 /* error */ #define AM_NOMATCH 0 /* no match */ #define AM_PROCPKT 1 /* server/symmetric packet */ #define AM_BCST 2 /* broadcast packet */ #define AM_FXMIT 3 /* client packet */ #define AM_MANYCAST 4 /* manycast or pool */ #define AM_NEWPASS 5 /* new passive */ #define AM_NEWBCL 6 /* new broadcast */ #define AM_POSSBCL 7 /* discard broadcast */ /* ntpq -c mrulist rows per request limit in ntpd */ #define MRU_ROW_LIMIT 256 /* similar datagrams per response limit for ntpd */ #define MRU_FRAGS_LIMIT 128 #endif /* GUARD_NTP_H */ ntpsec-1.1.0+dfsg1/include/ntp_malloc.h0000644000175000017500000000163113252364117017552 0ustar rlaagerrlaager/* * Define malloc and friends. */ #ifndef GUARD_NTP_MALLOC_H #define GUARD_NTP_MALLOC_H #include /* * Deal with platform differences declaring alloca() * This comes nearly verbatim from: * * http://www.gnu.org/software/autoconf/manual/autoconf.html#Particular-Functions * * The only modifications were to remove C++ support and guard against * redefining alloca. */ #ifdef HAVE_ALLOCA_H # include #elif defined __GNUC__ # ifndef alloca # define alloca __builtin_alloca # endif #elif defined _AIX # ifndef alloca # define alloca __alloca # endif #else # include void * alloca(size_t); #endif #ifdef EREALLOC_IMPL # define EREALLOC_CALLSITE /* preserve __FILE__ and __LINE__ */ #else # define EREALLOC_IMPL(ptr, newsz, filenm, loc) \ realloc(ptr, (newsz)) #endif #include #define ZERO(var) memset(&(var), '\0', sizeof(var)) #endif /* GUARD_NTP_MALLOC_H */ ntpsec-1.1.0+dfsg1/include/ntp_net.h0000644000175000017500000001363513252364117017100 0ustar rlaagerrlaager/* * ntp_net.h - definitions for NTP network stuff */ #ifndef GUARD_NTP_NET_H #define GUARD_NTP_NET_H #include #include #include #include #include #include "isc_netaddr.h" #include "ntp_malloc.h" typedef union { struct sockaddr sa; struct sockaddr_in sa4; struct sockaddr_in6 sa6; } sockaddr_u; /* * Utilities for manipulating sockaddr_u v4/v6 unions */ #define SOCK_ADDR4(psau) ((psau)->sa4.sin_addr) #define SET_SOCK_ADDR4(psau, a) ((psau)->sa4.sin_addr = (a)) #define SOCK_ADDR6(psau) ((psau)->sa6.sin6_addr) #define SET_SOCK_ADDR6(psau, a) ((psau)->sa6.sin6_addr = (a)) #define PSOCK_ADDR4(psau) (&SOCK_ADDR4(psau)) #define PSOCK_ADDR6(psau) (&SOCK_ADDR6(psau)) #define AF(psau) ((psau)->sa.sa_family) #define SET_AF(psau, f) ((psau)->sa.sa_family = (f)) #define IS_IPV4(psau) (AF_INET == AF(psau)) #define IS_IPV6(psau) (AF_INET6 == AF(psau)) /* sockaddr_u v4 address in network byte order */ #define NSRCADR(psau) (SOCK_ADDR4(psau).s_addr) #define SET_NSRCADR(psau, a) (SOCK_ADDR4(psau).s_addr - (a)) /* sockaddr_u v4 address in host byte order */ #define SRCADR(psau) (ntohl(NSRCADR(psau))) /* sockaddr_u v6 address in network byte order */ #define NSRCADR6(psau) (SOCK_ADDR6(psau).s6_addr) #define SET_NSRCADR6(psau) (SOCK_ADDR6(psau).s6_addr = (a)) /* assign sockaddr_u v4 address from host byte order */ #define SET_ADDR4(psau, addr4) (NSRCADR(psau) = htonl(addr4)) /* assign sockaddr_u v4 address from network byte order */ #define SET_ADDR4N(psau, addr4n) (NSRCADR(psau) = (addr4n)); /* assign sockaddr_u v6 address from network byte order */ #define SET_ADDR6N(psau, s6_addr) (SOCK_ADDR6(psau) = (s6_addr)) /* sockaddr_u v4/v6 port in network byte order */ #define NSRCPORT(psau) ((psau)->sa4.sin_port) #define SET_NSRCPORT(psau, a) ((psau)->sa4.sin_port = (a)) /* sockaddr_u v4/v6 port in host byte order */ #define SRCPORT(psau) (ntohs(NSRCPORT(psau))) /* assign sockaddr_u v4/v6 port from host byte order */ #define SET_PORT(psau, port) (NSRCPORT(psau) = htons(port)) /* sockaddr_u v6 scope */ #define SCOPE_VAR(psau) ((psau)->sa6.sin6_scope_id) /* v4/v6 scope (always zero for v4) */ # define SCOPE(psau) (IS_IPV4(psau) \ ? 0 \ : SCOPE_VAR(psau)) /* are two v6 sockaddr_u scopes equal? */ # define SCOPE_EQ(psau1, psau2) \ (SCOPE_VAR(psau1) == SCOPE_VAR(psau2)) /* assign scope if supported */ # define SET_SCOPE(psau, s) \ do \ if (IS_IPV6(psau)) \ SCOPE_VAR(psau) = (s); \ while (0) /* v4/v6 is multicast address */ #define IS_MCAST(psau) \ (IS_IPV4(psau) \ ? IN_CLASSD(SRCADR(psau)) \ : IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(psau))) /* v6 is interface ID scope universal, as with MAC-derived addresses */ #define IS_IID_UNIV(psau) \ (!!(0x02 & NSRCADR6(psau)[8])) #define SIZEOF_INADDR(fam) \ ((AF_INET == (fam)) \ ? sizeof(struct in_addr) \ : sizeof(struct in6_addr)) #define SIZEOF_SOCKADDR(fam) \ ((AF_INET == (fam)) \ ? sizeof(struct sockaddr_in) \ : sizeof(struct sockaddr_in6)) #define SOCKLEN(psau) \ (IS_IPV4(psau) \ ? sizeof((psau)->sa4) \ : sizeof((psau)->sa6)) #define ZERO_SOCK(psau) \ ZERO(*(psau)) /* blast a byte value across sockaddr_u v6 address */ #define MEMSET_ADDR6(psau, v) \ memset((psau)->sa6.sin6_addr.s6_addr, (v), \ sizeof((psau)->sa6.sin6_addr.s6_addr)) #define SET_ONESMASK(psau) \ do { \ if (IS_IPV6(psau)) \ MEMSET_ADDR6((psau), 0xff); \ else \ NSRCADR(psau) = 0xffffffff; \ } while(0) /* zero sockaddr_u, fill in family and all-ones (host) mask */ #define SET_HOSTMASK(psau, family) \ do { \ ZERO_SOCK(psau); \ AF(psau) = (family); \ SET_ONESMASK(psau); \ } while (0) /* * compare two in6_addr returning negative, 0, or positive. * ADDR6_CMP is negative if *pin6A is lower than *pin6B, zero if they * are equal, positive if *pin6A is higher than *pin6B. IN6ADDR_ANY * is the lowest address (128 zero bits). */ #define ADDR6_CMP(pin6A, pin6B) \ memcmp((pin6A)->s6_addr, (pin6B)->s6_addr, \ sizeof(pin6A)->s6_addr) /* compare two in6_addr for equality only */ #define ADDR6_EQ(pin6A, pin6B) \ (!ADDR6_CMP(pin6A, pin6B)) /* compare a in6_addr with socket address */ #define S_ADDR6_EQ(psau, pin6) \ ADDR6_EQ(&(psau)->sa6.sin6_addr, pin6) /* are two sockaddr_u's addresses equal? (port excluded) */ #define SOCK_EQ(psau1, psau2) \ ((AF(psau1) != AF(psau2)) \ ? 0 \ : IS_IPV4(psau1) \ ? (NSRCADR(psau1) == NSRCADR(psau2)) \ : (S_ADDR6_EQ((psau1), PSOCK_ADDR6(psau2)) \ && SCOPE_EQ((psau1), (psau2)))) /* are two sockaddr_u's addresses and ports equal? */ #define ADDR_PORT_EQ(psau1, psau2) \ ((NSRCPORT(psau1) != NSRCPORT(psau2) \ ? 0 \ : SOCK_EQ((psau1), (psau2)))) /* is sockaddr_u address unspecified? */ #define SOCK_UNSPEC(psau) \ (IS_IPV4(psau) \ ? !NSRCADR(psau) \ : IN6_IS_ADDR_UNSPECIFIED(PSOCK_ADDR6(psau))) /* just how unspecified do you mean? (scope 0/unspec too) */ #define SOCK_UNSPEC_S(psau) \ (SOCK_UNSPEC(psau) && !SCOPE(psau)) /* choose a default net interface (endpt) for v4 or v6 */ #define ANY_INTERFACE_BYFAM(family) \ ((AF_INET == family) \ ? any_interface \ : any6_interface) /* choose a default interface for addresses' protocol (addr family) */ #define ANY_INTERFACE_CHOOSE(psau) \ ANY_INTERFACE_BYFAM(AF(psau)) /* * Macro for checking for invalid addresses. This is really, really * gross, but is needed so no one configures a host on net 127 now that * we're encouraging it the configuration file. */ #define LOOPBACKADR 0x7f000001 #define LOOPNETMASK 0xff000000 #define ISBADADR(srcadr) \ (IS_IPV4(srcadr) \ && ((SRCADR(srcadr) & LOOPNETMASK) \ == (LOOPBACKADR & LOOPNETMASK)) \ && SRCADR(srcadr) != LOOPBACKADR) #endif /* GUARD_NTP_NET_H */ ntpsec-1.1.0+dfsg1/include/trimble.h0000644000175000017500000001477213252364117017072 0ustar rlaagerrlaager/* * Created: Sun Aug 2 16:16:49 1998 * * Copyright (c) 1998-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-clause */ #ifndef GUARD_TRIMBLE_H #define GUARD_TRIMBLE_H /* * Trimble packet command codes - commands being sent/received * keep comments formatted as shown - they are used to generate * translation tables */ #define CMD_CCLROSC 0x1D /* clear oscillator offset */ #define CMD_CCLRRST 0x1E /* clear battery backup and RESET */ #define CMD_CVERSION 0x1F /* return software version */ #define CMD_CALMANAC 0x20 /* almanac */ #define CMD_CCURTIME 0x21 /* current time */ #define CMD_CMODESEL 0x22 /* mode select (2-d, 3-D, auto) */ #define CMD_CINITPOS 0x23 /* initial position */ #define CMD_CRECVPOS 0x24 /* receiver position fix mode */ #define CMD_CRESET 0x25 /* soft reset & selftest */ #define CMD_CRECVHEALTH 0x26 /* receiver health */ #define CMD_CSIGNALLV 0x27 /* signal levels */ #define CMD_CMESSAGE 0x28 /* GPS system message */ #define CMD_CALMAHEALTH 0x29 /* almanac healt page */ #define CMD_C2DALTITUDE 0x2A /* altitude for 2-D mode */ #define CMD_CINITPOSLLA 0x2B /* initial position LLA */ #define CMD_COPERPARAM 0x2C /* operating parameters */ #define CMD_COSCOFFSET 0x2D /* oscillator offset */ #define CMD_CSETGPSTIME 0x2E /* set GPS time */ #define CMD_CUTCPARAM 0x2F /* UTC parameters */ #define CMD_CACCPOSXYZ 0x31 /* accurate initial position (XYZ/ECEF) */ #define CMD_CACCPOS 0x32 /* accurate initial position */ #define CMD_CANALOGDIG 0x33 /* analog to digital */ #define CMD_CSAT1SAT 0x34 /* satellite for 1-Sat mode */ #define CMD_CIOOPTIONS 0x35 /* I/O options */ #define CMD_CVELOCAID 0x36 /* velocity aiding of acquisition */ #define CMD_CSTATLSTPOS 0x37 /* status and values of last pos. and vel. */ #define CMD_CLOADSSATDT 0x38 /* load satellite system data */ #define CMD_CSATDISABLE 0x39 /* satellite disable */ #define CMD_CLASTRAW 0x3A /* last raw measurement */ #define CMD_CSTATSATEPH 0x3B /* satellite ephemeris status */ #define CMD_CSTATTRACK 0x3C /* tracking status */ #define CMD_CCHANADGPS 0x3D /* configure channel A for differential GPS */ #define CMD_CADDITFIX 0x3E /* additional fix data */ #define CMD_CDGPSFIXMD 0x62 /* set/request differential GPS position fix mode */ #define CMD_CDGPSCORR 0x65 /* differential correction status */ #define CMD_CPOSFILT 0x71 /* position filter parameters */ #define CMD_CHEIGHTFILT 0x73 /* height filter control */ #define CMD_CHIGH8CNT 0x75 /* high-8 (best 4) / high-6 (overdetermined) control */ #define CMD_CMAXDGPSCOR 0x77 /* maximum rate of DGPS corrections */ #define CMD_CSUPER 0x8E /* super paket */ #define CMD_RDATAA 0x3D /* data channel A configuration:trimble_channelA:RO */ #define CMD_RALMANAC 0x40 /* almanac data for sat:gps_almanac:RO */ #define CMD_RCURTIME 0x41 /* GPS time:gps_time:RO */ #define CMD_RSPOSXYZ 0x42 /* single precision XYZ position:gps_position(XYZ):RO|DEF */ #define CMD_RVELOXYZ 0x43 /* velocity fix (XYZ ECEF):gps_velocity(XYZ):RO|DEF */ #define CMD_RBEST4 0x44 /* best 4 satellite selection:trimble_best4:RO|DEF */ #define CMD_RVERSION 0x45 /* software version:trimble_version:RO|DEF */ #define CMD_RRECVHEALTH 0x46 /* receiver health:trimble_receiver_health:RO|DEF */ #define CMD_RSIGNALLV 0x47 /* signal levels of all satellites:trimble_signal_levels:RO */ #define CMD_RMESSAGE 0x48 /* GPS system message:gps-message:RO|DEF */ #define CMD_RALMAHEALTH 0x49 /* almanac health page for all satellites:gps_almanac_health:RO */ #define CMD_RSLLAPOS 0x4A /* single LLA position:gps_position(LLA):RO|DEF */ #define CMD_RMACHSTAT 0x4B /* machine code / status:trimble_status:RO|DEF */ #define CMD_ROPERPARAM 0x4C /* operating parameters:trimble_opparam:RO */ #define CMD_ROSCOFFSET 0x4D /* oscillator offset:trimble_oscoffset:RO */ #define CMD_RSETGPSTIME 0x4E /* response to set GPS time:trimble_setgpstime:RO */ #define CMD_RUTCPARAM 0x4F /* UTC parameters:gps_utc_correction:RO|DEF */ #define CMD_RANALOGDIG 0x53 /* analog to digital:trimble_analogdigital:RO */ #define CMD_RSAT1BIAS 0x54 /* one-satellite bias & bias rate:trimble_sat1bias:RO */ #define CMD_RIOOPTIONS 0x55 /* I/O options:trimble_iooptions:RO */ #define CMD_RVELOCFIX 0x56 /* velocity fix (ENU):trimble_velocfix */ #define CMD_RSTATLSTFIX 0x57 /* status and values of last pos. and vel.:trimble_status_lastpos:RO */ #define CMD_RLOADSSATDT 0x58 /* response to load satellite system data:trimble_loaddata:RO */ #define CMD_RSATDISABLE 0x59 /* satellite disable:trimble_satdisble:RO */ #define CMD_RLASTRAW 0x5A /* last raw measurement:trimble_lastraw:RO */ #define CMD_RSTATSATEPH 0x5B /* satellite ephemeris status:trimble_ephstatus:RO */ #define CMD_RSTATTRACK 0x5C /* tracking status:trimble_tracking_status:RO|DEF */ #define CMD_RADDITFIX 0x5E /* additional fix data:trimble_addfix:RO */ #define CMD_RALLINVIEW 0x6D /* all in view satellite selection:trimble_satview:RO|DEF */ #define CMD_RPOSFILT 0x72 /* position filter parameters:trimble_posfilt:RO */ #define CMD_RHEIGHTFILT 0x74 /* height filter control:trimble_heightfilt:RO */ #define CMD_RHIGH8CNT 0x76 /* high-8 (best 4) / high-6 (overdetermined) control:trimble_high8control:RO */ #define CMD_RMAXAGE 0x78 /* DC MaxAge:trimble_dgpsmaxage:RO */ #define CMD_RDGPSFIX 0x82 /* differential position fix mode:trimble_dgpsfixmode:RO */ #define CMD_RDOUBLEXYZ 0x83 /* double precision XYZ:gps_position_ext(XYZ):RO|DEF */ #define CMD_RDOUBLELLA 0x84 /* double precision LLA:gps_position_ext(LLA):RO|DEF */ #define CMD_RDGPSSTAT 0x85 /* differential correction status:trimble_dgpsstatus:RO */ #define CMD_RSUPER 0x8F /* super paket::0 */ typedef struct cmd_info { unsigned char cmd; /* command code */ const char *cmdname; /* command name */ const char *cmddesc; /* command description */ const char *varname; /* name of variable */ int varmode; /* mode of variable */ } cmd_info_t; extern cmd_info_t trimble_rcmds[]; extern cmd_info_t *trimble_convert (unsigned int cmd, cmd_info_t *tbl) __attribute__((pure)); #endif /* * History: * * trimble.h,v * Revision 4.6 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.5 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.4 1999/02/28 11:41:11 kardel * (CMD_RUTCPARAM): control variable name unification * * Revision 4.3 1998/12/20 23:45:25 kardel * fix types and warnings * * Revision 4.2 1998/08/16 18:45:05 kardel * (CMD_RSTATTRACK): renamed mode 6 variable name * * Revision 4.1 1998/08/09 22:24:35 kardel * Trimble TSIP support * */ ntpsec-1.1.0+dfsg1/include/ntp_control.h0000644000175000017500000001260213252364117017763 0ustar rlaagerrlaager/* * ntp_control.h - definitions related to NTP mode 6 control messages */ #ifndef GUARD_NTP_CONTROL_H #define GUARD_NTP_CONTROL_H #include "ntp_types.h" /* The attribute after this structure is a gcc/clang extension that forces * the beginning of a structure instance to be 32-bit aligned. Without this * attempting to compile on a 32-bit host may throw warnings or errors when * a pointer to this tructure is passed to authdecrypt/authencrypt, both of * which expect to be able to treat the structure as an array of uint32_t * elements. Ideally, we'd get rid of that nasty type punning. */ struct ntp_control { uint8_t li_vn_mode; /* leap, version, mode */ uint8_t r_m_e_op; /* response, more, error, opcode */ uint16_t sequence; /* sequence number of request */ uint16_t status; /* status word for association */ uint16_t associd; /* association ID (associd_t) */ uint16_t offset; /* offset of this batch of data */ uint16_t count; /* count of data in this packet */ uint8_t data[480 + MAX_MAC_LEN]; /* data + auth */ } __attribute__((aligned(32))); /* * Length of the control header, in octets */ #define CTL_HEADER_LEN (offsetof(struct ntp_control, data)) #define CTL_MAX_DATA_LEN 468 /* * Limits and things */ #define CTL_MAXAUTHSIZE 64 /* maximum size of an authen'ed req */ /* * Decoding for the r_m_e_op field */ #define CTL_RESPONSE 0x80 #define CTL_ERROR 0x40 #define CTL_MORE 0x20 #define CTL_OP_MASK 0x1f #define CTL_ISRESPONSE(r_m_e_op) ((CTL_RESPONSE & (r_m_e_op)) != 0) #define CTL_ISMORE(r_m_e_op) ((CTL_MORE & (r_m_e_op)) != 0) #define CTL_ISERROR(r_m_e_op) ((CTL_ERROR & (r_m_e_op)) != 0) #define CTL_OP(r_m_e_op) (CTL_OP_MASK & (r_m_e_op)) /* * Opcodes */ #define CTL_OP_UNSPEC 0 /* unspeciffied */ #define CTL_OP_READSTAT 1 /* read status */ #define CTL_OP_READVAR 2 /* read variables */ #define CTL_OP_WRITEVAR 3 /* write variables */ #define CTL_OP_READCLOCK 4 /* read clock variables */ #define CTL_OP_WRITECLOCK 5 /* write clock variables */ #define CTL_OP_SETTRAP 6 /* set trap address (obsolete, unused) */ #define CTL_OP_ASYNCMSG 7 /* asynchronous message */ #define CTL_OP_CONFIGURE 8 /* runtime configuration */ #define CTL_OP_READ_MRU 10 /* retrieve MRU (mrulist) */ #define CTL_OP_READ_ORDLIST_A 11 /* ordered list req. auth. */ #define CTL_OP_REQ_NONCE 12 /* request a client nonce */ #define CTL_OP_UNSETTRAP 31 /* unset trap (obsolete, unused) */ /* * {En,De}coding of the system status word */ #define CTL_SST_TS_UNSPEC 0 /* unspec */ #define CTL_SST_TS_ATOM 1 /* pps */ #define CTL_SST_TS_LF 2 /* lf radio */ #define CTL_SST_TS_HF 3 /* hf radio */ #define CTL_SST_TS_UHF 4 /* uhf radio */ #define CTL_SST_TS_LOCAL 5 /* local */ #define CTL_SST_TS_NTP 6 /* ntp */ #define CTL_SST_TS_UDPTIME 7 /* other */ #define CTL_SST_TS_WRSTWTCH 8 /* wristwatch */ #define CTL_SST_TS_TELEPHONE 9 /* telephone */ #define CTL_SYS_MAXEVENTS 15 #define CTL_SYS_STATUS(li, source, nevnt, evnt) \ (((((li) & 0xffff) << 14)&0xc000) | \ (((source)<<8)&0x3f00) | \ (((nevnt)<<4)&0x00f0) | \ ((evnt)&0x000f)) #define CTL_SYS_LI(status) (((status)>>14) & 0x3) #define CTL_SYS_SOURCE(status) (((status)>>8) & 0x3f) #define CTL_SYS_NEVNT(status) (((status)>>4) & 0xf) #define CTL_SYS_EVENT(status) ((status) & 0xf) /* * {En,De}coding of the peer status word */ #define CTL_PST_CONFIG 0x80 #define CTL_PST_AUTHENABLE 0x40 #define CTL_PST_AUTHENTIC 0x20 #define CTL_PST_REACH 0x10 #define CTL_PST_BCAST 0x08 #define CTL_PST_SEL_REJECT 0 /* reject */ #define CTL_PST_SEL_SANE 1 /* x falsetick */ #define CTL_PST_SEL_CORRECT 2 /* . excess */ #define CTL_PST_SEL_SELCAND 3 /* - outlier */ #define CTL_PST_SEL_SYNCCAND 4 /* + candidate */ #define CTL_PST_SEL_EXCESS 5 /* # backup */ #define CTL_PST_SEL_SYSPEER 6 /* * sys.peer */ #define CTL_PST_SEL_PPS 7 /* o pps.peer */ #define CTL_PEER_MAXEVENTS 15 #define CTL_PEER_STATUS(status, nevnt, evnt) \ ((((status)<<8) & 0xff00) | \ (((nevnt)<<4) & 0x00f0) | \ ((evnt) & 0x000f)) #define CTL_PEER_STATVAL(status)(((status)>>8) & 0xff) #define CTL_PEER_NEVNT(status) (((status)>>4) & 0xf) #define CTL_PEER_EVENT(status) ((status) & 0xf) /* * {En,De}coding of the clock status word */ #define CTL_CLK_OKAY 0 #define CTL_CLK_NOREPLY 1 #define CTL_CLK_BADFORMAT 2 #define CTL_CLK_FAULT 3 #define CTL_CLK_PROPAGATION 4 #define CTL_CLK_BADDATE 5 #define CTL_CLK_BADTIME 6 #define CTL_CLK_STATUS(status, event) \ ((((status)<<8) & 0xff00) | \ ((event) & 0x00ff)) /* * Error code responses returned when the E bit is set. */ #define CERR_UNSPEC 0 #define CERR_PERMISSION 1 #define CERR_BADFMT 2 #define CERR_BADOP 3 #define CERR_BADASSOC 4 #define CERR_UNKNOWNVAR 5 #define CERR_BADVALUE 6 #define CERR_RESTRICT 7 #define CERR_NORESOURCE CERR_PERMISSION /* wish there was a different code */ /* * Types of things we may deal with * shared between ntpq and library */ #define TYPE_SYS 1 #define TYPE_PEER 2 #define TYPE_CLOCK 3 /* * IFSTATS_FIELDS is the number of fields ntpd supplies for each ifstats * row. Similarly RESLIST_FIELDS for reslist. */ #define IFSTATS_FIELDS 12 #define RESLIST_FIELDS 4 /* * To prevent replay attacks, MRU list nonces age out. Time is in seconds. * * Don't change this value casually. Lengthening it might extend an * attack window for DDoS amplification. Shortening it might make your * server (or client) incompatible with older versions. */ #define NONCE_TIMEOUT 16 #endif /* GUARD_NTP_CONTROL_H */ ntpsec-1.1.0+dfsg1/include/ntp_io.h0000644000175000017500000000153513252364117016715 0ustar rlaagerrlaager#ifndef GUARD_NTP_IO_H #define GUARD_NTP_IO_H /* * POSIX says use to get O_* symbols and * SEEK_SET symbol form . */ #include #include #include #include #include #include "isc_netaddr.h" /* * NIC rule match types */ typedef enum { MATCH_ALL, MATCH_IPV4, MATCH_IPV6, MATCH_WILDCARD, MATCH_IFNAME, MATCH_IFADDR } nic_rule_match; /* * NIC rule actions */ typedef enum { ACTION_LISTEN, ACTION_IGNORE, ACTION_DROP } nic_rule_action; extern int qos; extern bool is_ip_address(const char *, unsigned short, sockaddr_u *); extern void add_nic_rule(nic_rule_match match_type, const char *if_name, int prefixlen, nic_rule_action action); extern void make_socket_nonblocking( SOCKET fd ); extern SOCKET move_fd( SOCKET fd ); #endif /* GUARD_NTP_IO_H */ ntpsec-1.1.0+dfsg1/include/ntp_syscall.h0000644000175000017500000000166313252364117017762 0ustar rlaagerrlaager/* * ntp_syscall.h - various ways to perform the ntp_adjtime() system calls. * * On most systems including will bring in declarations * for the BSD function ntp_adjtime(2). (Linux using glibc has these, * though they're not visible in the manual pages.) */ #ifndef GUARD_NTP_SYSCALL_H #define GUARD_NTP_SYSCALL_H # include /* prerequisite on NetBSD */ # include extern int ntp_adjtime_ns(struct timex *); /* * The units of the maxerror and esterror fields vary by platform. If * STA_NANO is defined, they're in nanoseconds; otherwise in * microseconds. Hide the difference by normalizing everything to * float seconds. */ # ifdef STA_NANO #define ntp_error_in_seconds(n) ((n)/1.0e9) # else #define ntp_error_in_seconds(n) ((n)/1.0e6) # endif /* MUSL port shim */ #if !defined(HAVE_NTP_ADJTIME) && defined(HAVE_ADJTIMEX) #define ntp_adjtime adjtimex #endif #endif /* GUARD_NTP_SYSCALL_H */ ntpsec-1.1.0+dfsg1/include/ntp_refclock.h0000644000175000017500000002017213252364117020074 0ustar rlaagerrlaager/* * ntp_refclock.h - definitions for reference clock support */ #ifndef GUARD_NTP_REFCLOCK_H #define GUARD_NTP_REFCLOCK_H #if defined(HAVE_SYS_MODEM_H) #include #endif #include "ntp_types.h" #include "ntp_tty.h" #include "recvbuff.h" /* * Configuration flag values */ #define CLK_HAVETIME1 0x1 #define CLK_HAVETIME2 0x2 #define CLK_HAVEVAL1 0x4 #define CLK_HAVEVAL2 0x8 #define CLK_FLAG1 0x1 #define CLK_FLAG2 0x2 #define CLK_FLAG3 0x4 #define CLK_FLAG4 0x8 #define CLK_HOLDOVER 0x10 /* no corresponding HAVE_ flag */ #define CLK_HAVEFLAG1 0x10 #define CLK_HAVEFLAG2 0x20 #define CLK_HAVEFLAG3 0x40 #define CLK_HAVEFLAG4 0x80 /* * Structure for returning clock status */ struct refclockstat { uint8_t flags; /* clock flags */ uint8_t haveflags; /* bit array of valid flags */ unsigned short lencode; /* length of last timecode */ const char *p_lastcode; /* last timecode received */ uint32_t polls; /* transmit polls */ uint32_t noresponse; /* no response to poll */ uint32_t badformat; /* bad format timecode received */ uint32_t baddata; /* invalid data timecode received */ uint32_t timereset; /* driver resets */ const char *clockname; /* refclockname */ const char *clockdesc; /* ASCII description */ double fudgetime1; /* configure fudge time1 */ double fudgetime2; /* configure fudge time2 */ int32_t fudgeval1; /* configure fudge value1 */ uint32_t fudgeval2; /* configure fudge value2 */ uint8_t currentstatus; /* clock status */ uint8_t lastevent; /* last exception event */ uint8_t leap; /* leap bits */ struct ctl_var *kv_list; /* additional variables */ }; /* * Reference clock I/O structure. Used to provide an interface between * the reference clock drivers and the I/O module. */ struct refclockio { struct refclockio *next; /* link to next structure */ void (*clock_recv) (struct recvbuf *); /* completion routine */ int (*io_input) (struct recvbuf *); /* input routine - to avoid excessive buffer use due to small bursts of refclock input data */ struct peer *srcclock; /* refclock peer */ size_t datalen; /* length of data */ int fd; /* file descriptor */ unsigned long recvcount; /* count of receive completions */ bool active; /* true when in use */ }; /* * Structure for returning debugging info */ #define NCLKBUGVALUES 16 #define NCLKBUGTIMES 32 struct refclockbug { uint8_t nvalues; /* values following */ uint8_t ntimes; /* times following */ unsigned short svalues; /* values format sign array */ uint32_t stimes; /* times format sign array */ uint32_t values[NCLKBUGVALUES]; /* real values */ l_fp times[NCLKBUGTIMES]; /* real times */ }; /* * Structure interface between the reference clock support * ntp_refclock.c and the driver utility routines */ #define MAXSTAGE 60 /* max median filter stages */ #define NSTAGE 5 /* default median filter stages */ #define BMAX 128 /* max timecode length */ #define MAXDIAL 60 /* max length of modem dial strings */ struct refclockproc { void * unitptr; /* pointer to unit structure */ struct refclock * conf; /* pointer to driver method table */ struct refclockio io; /* I/O handler structure */ uint8_t refclkunit; /* reference clock unit number */ uint8_t leap; /* leap/synchronization code */ uint8_t currentstatus; /* clock status */ uint8_t lastevent; /* last exception event */ const char *clockname; /* clock name (tag for logging) */ const char *clockdesc; /* clock description */ unsigned long nextaction; /* local activity timeout */ void (*action)(struct peer *); /* timeout callback */ char a_lastcode[BMAX]; /* last timecode received */ int lencode; /* length of last timecode */ int year; /* year of eternity */ int day; /* day of year */ int hour; /* hour of day */ int minute; /* minute of hour */ int second; /* second of minute */ long nsec; /* nanosecond of second */ uint32_t yearstart; /* beginning of year */ int coderecv; /* put pointer */ int codeproc; /* get pointer */ l_fp lastref; /* reference timestamp */ l_fp lastrec; /* receive timestamp */ double offset; /* mean offset */ double disp; /* sample dispersion */ double jitter; /* jitter (mean squares) */ double filter[MAXSTAGE]; /* median filter */ /* * Configuration data */ double fudgetime1; /* fudge time1 */ double fudgetime2; /* fudge time2 */ uint8_t stratum; /* server stratum */ uint32_t refid; /* reference identifier */ uint8_t sloppyclockflag; /* driver options */ /* * Status tallies */ uptime_t timestarted; /* time we started this */ unsigned long polls; /* polls sent */ unsigned long noreply; /* no replies to polls */ unsigned long badformat; /* bad format reply */ unsigned long baddata; /* bad data reply */ }; /* * Structure interface between the reference clock support * ntp_refclock.c and particular clock drivers. This must agree with the * structure defined in the driver. */ struct refclock { const char *basename; bool (*clock_start) (int, struct peer *); void (*clock_shutdown) (struct refclockproc *); void (*clock_poll) (int, struct peer *); void (*clock_control) (int, const struct refclockstat *, struct refclockstat *, struct peer *); void (*clock_init) (void); void (*clock_timer) (int, struct peer *); }; /* * Function prototypes */ extern bool io_addclock (struct refclockio *); extern void io_closeclock (struct refclockio *); #ifdef REFCLOCK extern bool refclock_newpeer (uint8_t, int, struct peer *); extern void refclock_unpeer (struct peer *); extern void refclock_receive (struct peer *); extern void init_refclock (void); extern void refclock_control(sockaddr_u *, const struct refclockstat *, struct refclockstat *); extern int refclock_open (char *, unsigned int, unsigned int); extern void refclock_timer (struct peer *); extern void refclock_transmit(struct peer *); extern bool refclock_process(struct refclockproc *); extern bool refclock_process_f(struct refclockproc *, double); extern void refclock_process_offset(struct refclockproc *, l_fp, l_fp, double); extern void refclock_report (struct peer *, int); extern char *refclock_name (const struct peer *); extern int refclock_gtlin (struct recvbuf *, char *, int, l_fp *); extern size_t refclock_gtraw (struct recvbuf *, char *, size_t, l_fp *); extern bool indicate_refclock_packet(struct refclockio *, struct recvbuf *); extern struct refclock refclock_none; #ifdef CLOCK_ARBITER extern struct refclock refclock_arbiter; #else #define refclock_arbiter refclock_none #endif #ifdef CLOCK_GENERIC extern struct refclock refclock_parse; #else #define refclock_parse refclock_none #endif #if defined(CLOCK_GPSDJSON) extern struct refclock refclock_gpsdjson; #else #define refclock_gpsdjson refclock_none #endif #ifdef CLOCK_HPGPS extern struct refclock refclock_hpgps; #else #define refclock_hpgps refclock_none #endif #ifdef CLOCK_JJY extern struct refclock refclock_jjy; #else #define refclock_jjy refclock_none #endif #ifdef CLOCK_LOCAL extern struct refclock refclock_local; #else #define refclock_local refclock_none #endif #ifdef CLOCK_MODEM extern struct refclock refclock_modem; #else #define refclock_modem refclock_none #endif #ifdef CLOCK_NEOCLOCK extern struct refclock refclock_neoclock4x; #else #define refclock_neoclock4x refclock_none #endif #ifdef CLOCK_NMEA extern struct refclock refclock_nmea; #else #define refclock_nmea refclock_none #endif #if defined(CLOCK_ONCORE) extern struct refclock refclock_oncore; #else #define refclock_oncore refclock_none #endif #if defined (CLOCK_PPS) && defined(HAVE_PPSAPI) extern struct refclock refclock_pps; #else #define refclock_pps refclock_none #endif #ifdef CLOCK_SPECTRACOM extern struct refclock refclock_spectracom; #else #define refclock_spectracom refclock_none #endif #ifdef CLOCK_TRUETIME extern struct refclock refclock_true; #else #define refclock_true refclock_none #endif #ifdef CLOCK_SHM extern struct refclock refclock_shm; #else #define refclock_shm refclock_none #endif #ifdef CLOCK_TRIMBLE extern struct refclock refclock_trimble; #else #define refclock_trimble refclock_none #endif #ifdef CLOCK_ZYFER extern struct refclock refclock_zyfer; #else #define refclock_zyfer refclock_none #endif #endif /* REFCLOCK */ #endif /* GUARD_NTP_REFCLOCK_H */ ntpsec-1.1.0+dfsg1/include/parse.h0000644000175000017500000002650513252364117016543 0ustar rlaagerrlaager/* * Copyright (c) 1989-2015 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-clause */ #ifndef GUARD__PARSE_H #define GUARD__PARSE_H #include "ntp_types.h" #include "parse_conf.h" #include #include "ntp_syslog.h" #ifdef DEBUG #define DD_PARSE 5 #define DD_RAWDCF 4 #define parseprintf(LEVEL, ARGS) if (debug > LEVEL) printf ARGS #else /* DEBUG */ #define parseprintf(LEVEL, ARGS) #endif /* DEBUG */ /* * some constants useful for GPS time conversion */ #define GPSWRAP 990 /* assume week count less than this in the previous epoch */ #define GPSWEEKS 1024 /* number of weeks until the GPS epch rolls over */ /* * state flags */ #define PARSEB_POWERUP 0x00000001U /* no synchronisation */ #define PARSEB_NOSYNC 0x00000002U /* timecode currently not confirmed */ /* * time zone information */ #define PARSEB_ANNOUNCE 0x00000010U /* switch time zone warning (DST switch) */ #define PARSEB_DST 0x00000020U /* DST in effect */ #define PARSEB_UTC 0x00000040U /* UTC time */ /* * leap information */ #define PARSEB_LEAPDEL 0x00000100U /* LEAP deletion warning */ #define PARSEB_LEAPADD 0x00000200U /* LEAP addition warning */ #define PARSEB_LEAPS 0x00000300U /* LEAP warnings */ #define PARSEB_LEAPSECOND 0x00000400U /* actual leap second */ /* * optional status information */ #define PARSEB_CALLBIT 0x00001000U /* "call bit" used to signalize irregularities in the control facilities */ #define PARSEB_POSITION 0x00002000U /* position available */ #define PARSEB_MESSAGE 0x00004000U /* addtitional message data */ /* * feature information */ #define PARSEB_S_LEAP 0x00010000U /* supports LEAP */ #define PARSEB_S_CALLBIT 0x00020000U /* supports callbit information */ #define PARSEB_S_PPS 0x00040000U /* supports PPS time stamping */ #define PARSEB_S_POSITION 0x00080000U /* supports position information (GPS) */ /* * time stamp availability */ #define PARSEB_TIMECODE 0x10000000U /* valid time code sample */ #define PARSEB_PPS 0x20000000U /* valid PPS sample */ #define PARSE_TCINFO (PARSEB_ANNOUNCE|PARSEB_POWERUP|PARSEB_NOSYNC|PARSEB_DST|\ PARSEB_UTC|PARSEB_LEAPS|PARSEB_CALLBIT|PARSEB_S_LEAP|\ PARSEB_S_LOCATION|PARSEB_TIMECODE|PARSEB_MESSAGE) #define PARSE_POWERUP(x) ((x) & PARSEB_POWERUP) #define PARSE_NOSYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == PARSEB_NOSYNC) #define PARSE_SYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == 0) #define PARSE_ANNOUNCE(x) ((x) & PARSEB_ANNOUNCE) #define PARSE_DST(x) ((x) & PARSEB_DST) #define PARSE_UTC(x) ((x) & PARSEB_UTC) #define PARSE_LEAPADD(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPADD)) #define PARSE_LEAPDEL(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPDEL)) #define PARSE_CALLBIT(x) ((x) & PARSEB_CALLBIT) #define PARSE_LEAPSECOND(x) (PARSE_SYNC(x) && ((x) & PARSEB_LEAP_SECOND)) #define PARSE_S_LEAP(x) ((x) & PARSEB_S_LEAP) #define PARSE_S_CALLBIT(x) ((x) & PARSEB_S_CALLBIT) #define PARSE_S_PPS(x) ((x) & PARSEB_S_PPS) #define PARSE_S_POSITION(x) ((x) & PARSEB_S_POSITION) #define PARSE_TIMECODE(x) ((x) & PARSEB_TIMECODE) #define PARSE_PPS(x) ((x) & PARSEB_PPS) #define PARSE_POSITION(x) ((x) & PARSEB_POSITION) #define PARSE_MESSAGE(x) ((x) & PARSEB_MESSAGE) /* * operation flags - lower nibble contains driver option flags */ #define PARSE_TRUSTTIME CLK_FLAG1 /* use flag1 to indicate the time2 references mean the trust time */ #define PARSE_CLEAR CLK_FLAG2 /* use flag2 to control pps on assert */ #define PARSE_PPSKERNEL CLK_FLAG3 /* use flag3 to bind PPS to kernel */ #define PARSE_LEAP_DELETE CLK_FLAG4 /* use flag4 to force leap deletion - only necessary when earth slows down */ #define PARSE_FIXED_FMT 0x10 /* fixed format */ #define PARSE_PPSAPI 0x20 /* try to get PPS time stamp via API */ /* * size of buffers */ #define PARSE_TCMAX 400 /* maximum addition data size */ typedef l_fp timestamp_t; /* * standard time stamp structure */ struct parsetime { unsigned long parse_status; /* data status - CVT_OK, CVT_NONE, CVT_FAIL ... */ timestamp_t parse_time; /* PARSE timestamp */ timestamp_t parse_stime; /* telegram sample timestamp */ timestamp_t parse_ptime; /* PPS time stamp */ long parse_usecerror; /* sampled usec error */ unsigned long parse_state; /* current receiver state */ unsigned short parse_format; /* format code */ unsigned short parse_msglen; /* length of message */ unsigned char parse_msg[PARSE_TCMAX]; /* original messages */ }; typedef struct parsetime parsetime_t; /*------ IO handling flags (sorry) ------*/ #define PARSE_IO_CSIZE 0x00000003 #define PARSE_IO_CS5 0x00000000 #define PARSE_IO_CS6 0x00000001 #define PARSE_IO_CS7 0x00000002 #define PARSE_IO_CS8 0x00000003 /* * ioctl structure */ union parsectl { struct parsegettc { unsigned long parse_state; /* last state */ unsigned long parse_badformat; /* number of bad packets since last query */ unsigned short parse_format;/* last decoded format */ unsigned short parse_count; /* count of valid time code bytes */ char parse_buffer[PARSE_TCMAX+1]; /* timecode buffer */ } parsegettc; struct parseformat { unsigned short parse_format;/* number of examined format */ unsigned short parse_count; /* count of valid string bytes */ char parse_buffer[PARSE_TCMAX+1]; /* format code string */ } parseformat; struct parsesetcs { unsigned long parse_cs; /* character size (needed for stripping) */ } parsesetcs; }; typedef union parsectl parsectl_t; /*------ for conversion routines --------*/ struct parse /* parse module local data */ { int parse_flags; /* operation and current status flags */ int parse_ioflags; /* io handling flags (5-8 Bit control currently) */ /* * private data - fixed format only */ unsigned short parse_plen; /* length of private data */ void *parse_pdata; /* private data pointer */ /* * time code input buffer (from RS232 or PPS) */ unsigned short parse_index; /* current buffer index */ char *parse_data; /* data buffer */ unsigned short parse_dsize; /* size of data buffer */ unsigned short parse_lformat; /* last format used */ unsigned long parse_lstate; /* last state code */ char *parse_ldata; /* last data buffer */ unsigned short parse_ldsize; /* last data buffer length */ unsigned long parse_badformat; /* number of unparsable pakets */ timestamp_t parse_lastchar; /* last time a character was received */ parsetime_t parse_dtime; /* external data prototype */ }; typedef struct parse parse_t; struct clocktime /* clock time broken up from time code */ { long day; long month; long year; long hour; long minute; long second; long usecond; long utcoffset; /* in seconds */ time_t utctime; /* the actual time - alternative to date/time */ unsigned long flags; /* current clock status */ }; typedef struct clocktime clocktime_t; /* * parser related return/error codes */ #define CVT_MASK (unsigned)0x0000000F /* conversion exit code */ #define CVT_NONE (unsigned)0x00000001 /* format not applicable */ #define CVT_FAIL (unsigned)0x00000002 /* conversion failed - error code returned */ #define CVT_OK (unsigned)0x00000004 /* conversion succeeded */ #define CVT_SKIP (unsigned)0x00000008 /* conversion succeeded */ #define CVT_ADDITIONAL (unsigned)0x00000010 /* additional data is available */ #define CVT_BADFMT (unsigned)0x00000100 /* general format error - (unparsable) */ #define CVT_BADDATE (unsigned)0x00000200 /* date field incorrect */ #define CVT_BADTIME (unsigned)0x00000400 /* time field incorrect */ /* * return codes used by special input parsers */ #define PARSE_INP_SKIP 0x00 /* discard data - may have been consumed */ #define PARSE_INP_TIME 0x01 /* time code assembled */ #define PARSE_INP_PARSE 0x02 /* parse data using normal algorithm */ #define PARSE_INP_DATA 0x04 /* additional data to pass up */ #define PARSE_INP_SYNTH 0x08 /* just pass up synthesized time */ typedef unsigned long parse_inp_fnc_t(parse_t *, char, timestamp_t *); typedef unsigned long parse_cvt_fnc_t(unsigned char *, int, struct format *, clocktime_t *, void *); typedef unsigned long parse_pps_fnc_t(parse_t *, int, timestamp_t *); struct clockformat { /* special input protocol - implies fixed format */ parse_inp_fnc_t *input; /* conversion routine */ parse_cvt_fnc_t *convert; /* routine for handling RS232 sync events (time stamps) */ /* PPS input routine */ parse_pps_fnc_t *syncpps; /* time code synthesizer */ void *data; /* local parameters */ const char *name; /* clock format name */ unsigned short length; /* maximum length of data packet */ unsigned short plen; /* length of private data - implies fixed format */ }; typedef struct clockformat clockformat_t; extern clockformat_t *clockformats[]; extern clockformat_t clock_computime; extern clockformat_t clock_dcf7000; extern clockformat_t clock_hopf6021; extern clockformat_t clock_meinberg[]; extern clockformat_t clock_rawdcf; extern clockformat_t clock_rcc8000; extern clockformat_t clock_schmid; extern clockformat_t clock_sel240x; extern clockformat_t clock_trimtaip; extern clockformat_t clock_trimtsip; extern clockformat_t clock_varitext; extern clockformat_t clock_wharton_400a; extern unsigned short nformats; /* * parse interface */ extern bool parse_ioinit (parse_t *); extern void parse_ioend (parse_t *); extern int parse_ioread (parse_t *, char, timestamp_t *); extern void parse_iodone (parse_t *); extern bool parse_timecode (parsectl_t *, parse_t *); extern int parse_getfmt (parsectl_t *, parse_t *); extern bool parse_setfmt (parsectl_t *, parse_t *); extern bool parse_setcs (parsectl_t *, parse_t *); extern unsigned int parse_addchar (parse_t *, char); extern unsigned int parse_end (parse_t *); extern int Strok (const unsigned char *, const unsigned char *) __attribute__((pure)); extern int Stoi (const unsigned char *, long *, int); extern time_t parse_to_unixtime (clocktime_t *, unsigned long *); extern unsigned long updatetimeinfo (parse_t *, unsigned long); extern parse_pps_fnc_t pps_one; extern bool parse_timedout (parse_t *, timestamp_t *, struct timespec *); #endif /* * History: * * parse.h,v * Revision 4.12 2007/01/14 08:36:03 kardel * make timestamp union anonymous to avoid conflicts with * some OSes that choose to create a nameing conflic here. * * Revision 4.11 2005/06/25 10:58:45 kardel * add missing log keywords * * Revision 4.5 1998/08/09 22:23:32 kardel * 4.0.73e2 adjustments * * Revision 4.4 1998/06/14 21:09:27 kardel * Sun acc cleanup * * Revision 4.3 1998/06/13 11:49:25 kardel * STREAM macro gone in favor of HAVE_SYS_STREAM_H * * Revision 4.2 1998/06/12 15:14:25 kardel * fixed prototypes * * Revision 4.1 1998/05/24 10:07:59 kardel * removed old data structure cruft (new input model) * new PARSE_INP* macros for input handling * removed old SYNC_* macros from old input model * (struct clockformat): removed old parse functions in favor of the * new input model * updated prototypes * * form V3 3.31 - log info deleted 1998/04/11 kardel */ ntpsec-1.1.0+dfsg1/include/parse_conf.h0000644000175000017500000000173313252364117017544 0ustar rlaagerrlaager/* * Copyright (c) 1989-2005 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-clause */ #ifndef GUARD_PARSE_CONF_H #define GUARD_PARSE_CONF_H /* * field location structure */ #define O_DAY 0 #define O_MONTH 1 #define O_YEAR 2 #define O_HOUR 3 #define O_MIN 4 #define O_SEC 5 #define O_WDAY 6 #define O_FLAGS 7 #define O_ZONE 8 #define O_UTCHOFFSET 9 #define O_UTCMOFFSET 10 #define O_UTCSOFFSET 11 #define O_COUNT (O_UTCSOFFSET+1) /* * see below for field offsets */ struct format { struct foff { unsigned short offset; /* offset into buffer */ unsigned short length; /* length of field */ } field_offsets[O_COUNT]; const unsigned char *fixed_string; /* string with must be chars (blanks = wildcards) */ unsigned long flags; }; #endif /* * History: * * parse_conf.h,v * Revision 4.7 2005/06/25 10:58:45 kardel * add missing log keywords * */ ntpsec-1.1.0+dfsg1/include/timespecops.h0000644000175000017500000000442613252364117017762 0ustar rlaagerrlaager/* * timespecops.h -- calculations on 'struct timespec' values * * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: NTP */ #ifndef GUARD_TIMESPECOPS_H #define GUARD_TIMESPECOPS_H #include #include "ntp.h" /* milliseconds per second */ #define MS_PER_S 1000 /* seconds per millisecond */ #define S_PER_MS 1.0e-3 /* microseconds per second */ #define US_PER_S 1000000 /* seconds per microsecond */ #define S_PER_US 1.0e-6 /* nanoseconds per second */ #define NS_PER_S 1000000000 /* seconds per nanosecond */ #define S_PER_NS 1.0e-9 /* nano seconds per millisecond */ #define NS_PER_MS 1000000 /* returns true if a time stored as a double is close to zero */ #define D_ISZERO_NS(t) (fabs(t) < S_PER_NS/10.0) /* predicate: returns true if the nanoseconds are in nominal range */ #define timespec_isnormal(x) ((x)->tv_nsec >= 0 && (x)->tv_nsec < NS_PER_S) /* predicate: returns true if the nanoseconds are out-of-bounds */ #define timespec_isdenormal(x) (!timespec_isnormal(x)) /* conversion between l_fp fractions and nanoseconds */ #define FTOTVN(tsf) \ ((int32_t)(((uint64_t)(tsf) * NS_PER_S + 0x80000000) >> 32)) #define TVNTOF(tvu) \ ((uint32_t) ((((uint64_t)(tvu) << 32) + NS_PER_S / 2) / NS_PER_S)) extern struct timespec normalize_tspec(struct timespec); extern struct timespec d_to_tspec(double); extern struct timespec add_tspec(struct timespec, struct timespec); extern struct timespec add_tspec_ns(struct timespec, long); extern struct timespec sub_tspec(struct timespec, struct timespec); extern struct timespec sub_tspec_ns(struct timespec, long); extern struct timespec neg_tspec(struct timespec); extern struct timespec abs_tspec(struct timespec); extern int cmp_tspec(struct timespec a, struct timespec b); extern int cmp_tspec_denorm(struct timespec, struct timespec); extern int test_tspec( struct timespec); extern int test_tspec_denorm( struct timespec); extern const char * tspectoa( struct timespec); extern l_fp tspec_intv_to_lfp( struct timespec); extern l_fp tspec_stamp_to_lfp( struct timespec); extern struct timespec lfp_intv_to_tspec(l_fp); extern struct timespec lfp_uintv_to_tspec(l_fp); extern struct timespec lfp_stamp_to_tspec(l_fp, time_t); extern struct timespec tval_to_tspec(struct timeval); #endif /* GUARD_TIMESPECOPS_H */ ntpsec-1.1.0+dfsg1/include/ntpd.h0000644000175000017500000003241113252364117016367 0ustar rlaagerrlaager/* * ntpd.h - Prototypes and external variables for ntpd. * * Note the first half is primarily function prototypes, type * declarations, and preprocessor macros, with variables declared * primarily in the second half. * * Each half is further divided into sections for each source file. */ #include "ntp.h" #include "ntp_stdlib.h" #include "ntp_syslog.h" #include "ntp_debug.h" #include "ntp_syslog.h" #include "ntp_malloc.h" #include "ntp_refclock.h" #include "ntp_control.h" #include "recvbuff.h" /* * First half: ntpd types, functions, macros * ----------------------------------------- */ /* * macro to silence -Wimplicit-fallthrough * of course gcc and clang do it differently */ #ifdef __clang__ # define FALLTHRU #elif defined __GNUC__ && (6 < __GNUC__ ) # define FALLTHRU __attribute__ ((fallthrough)); #else # define FALLTHRU #endif /* ntp_config.c */ extern const char *getconfig (const char *); extern void readconfig(const char *); extern void ctl_clr_stats (void); extern unsigned short ctlpeerstatus (struct peer *); extern void init_control (void); extern void process_control (struct recvbuf *, int); extern void report_event (int, struct peer *, const char *); extern int mprintf_event (int, struct peer *, const char *, ...) NTP_PRINTF(3, 4); /* ntp_control.c */ /* * Structure for translation tables between internal system * variable indices and text format. */ struct ctl_var { unsigned short code; unsigned short flags; const char *text; }; /* * Flag values */ #define CAN_READ 0x01 #define CAN_WRITE 0x02 #define DEF 0x20 #define PADDING 0x40 #define EOV 0x80 #define RO (CAN_READ) #define RW (CAN_READ|CAN_WRITE) extern char * add_var (struct ctl_var **, unsigned long, unsigned short); extern void free_varlist (struct ctl_var *); extern void set_var (struct ctl_var **, const char *, unsigned long, unsigned short); extern void set_sys_var (const char *, unsigned long, unsigned short); extern const char * get_ext_sys_var(const char *tag); /* ntp_io.c */ typedef struct interface_info { endpt * ep; uint8_t action; } interface_info_t; typedef void (*interface_receiver_t) (void *, interface_info_t *); extern bool listen_to_virtual_ips; extern endpt * getinterface (sockaddr_u *, uint32_t); extern endpt * select_peerinterface (struct peer *, sockaddr_u *, endpt *); extern endpt * findinterface (sockaddr_u *); extern void enable_broadcast (endpt *, sockaddr_u *); extern void interface_update (interface_receiver_t, void *); extern void io_handler (void); extern void init_io (void); extern SOCKET open_socket (sockaddr_u *, bool, bool, endpt *); extern void io_open_sockets (void); extern void io_clr_stats (void); extern void sendpkt (sockaddr_u *, endpt *, void *, int); #ifdef ENABLE_DEBUG_TIMING extern void collect_timing (struct recvbuf *, const char *, int, l_fp); #endif extern const char * latoa(endpt *); /* ntp_loopfilter.c */ extern void init_loopfilter(void); extern int local_clock(struct peer *, double); extern void adj_host_clock(void); extern void loop_config(int, double); extern void select_loop(int); extern void huffpuff(void); extern unsigned int sys_tai; extern int freq_cnt; /* ntp_monitor.c */ #define MON_HASH_SIZE (1U << mon_hash_bits) #define MON_HASH_MASK (MON_HASH_SIZE - 1) #define MON_HASH(addr) (sock_hash(addr) & MON_HASH_MASK) extern void init_mon (void); extern void mon_start (int); extern void mon_stop (int); extern unsigned short ntp_monitor (struct recvbuf *, unsigned short); extern void mon_clearinterface(endpt *interface); extern int mon_get_oldest_age(l_fp); /* ntp_peer.c */ extern void init_peer (void); extern struct peer *findexistingpeer(sockaddr_u *, const char *, struct peer *, int); extern struct peer *findpeer (struct recvbuf *, int, int *); extern struct peer *findpeerbyassoc(associd_t); extern void set_peerdstadr (struct peer *, endpt *); extern struct peer *newpeer (sockaddr_u *, const char *, endpt *, uint8_t, struct peer_ctl *, uint8_t, const bool); extern void peer_update_hash (struct peer *); extern void peer_all_reset (void); extern void peer_clr_stats (void); extern void refresh_all_peerinterfaces(void); extern void peer_refresh_interface(struct peer *); extern void unpeer (struct peer *); extern void clear_all (void); extern int score_all (struct peer *); extern struct peer *findmanycastpeer(struct recvbuf *); extern void peer_cleanup (void); /* ntp_proto.c */ extern void transmit (struct peer *); extern void receive (struct recvbuf *); extern void peer_clear (struct peer *, const char *, const bool); extern void set_sys_leap (uint8_t); extern int sys_orphan; extern double sys_mindisp; extern double sys_maxdisp; extern void poll_update (struct peer *, uint8_t); extern void clock_filter (struct peer *, double, double, double); extern void init_proto (const bool); extern void set_sys_tick_precision(double); extern void proto_config (int, unsigned long, double); extern void proto_clr_stats (void); /* ntp_restrict.c */ extern void init_restrict (void); extern unsigned short restrictions (sockaddr_u *); extern void hack_restrict (int, sockaddr_u *, sockaddr_u *, unsigned short, unsigned short, unsigned long); extern void restrict_source (sockaddr_u *, bool, unsigned long); /* ntp_timer.c */ extern void init_timer (void); extern void reinit_timer (void); extern void timer (void); extern void timer_clr_stats (void); extern void timer_interfacetimeout (uptime_t); extern int interface_interval; extern uptime_t orphwait; /* orphan wait time */ /* ntp_util.c */ extern void init_util (void); extern void write_stats (void); extern void stats_config (int, const char *); extern void record_peer_stats (struct peer *, int); extern void record_proto_stats (char *); extern void record_loop_stats (double, double, double, double, int); extern void record_clock_stats (struct peer *, const char *); extern int mprintf_clock_stats(struct peer *, const char *, ...) NTP_PRINTF(2, 3); extern void record_raw_stats (struct peer *, int leap, int version, int mode, int stratum, int ppoll, int precision, double root_delay, double root_dispersion, uint32_t refid, unsigned int outcount); extern void check_leap_file (bool is_daily_check, time_t systime); #ifdef ENABLE_DEBUG_TIMING extern void record_timing_stats (const char *); #endif /* packetstamp.c */ extern void enable_packetstamps(int, sockaddr_u *); extern l_fp fetch_packetstamp(struct recvbuf *, struct msghdr *, l_fp); /* * Signals we catch for debugging. */ #define MOREDEBUGSIG SIGUSR1 #define LESSDEBUGSIG SIGUSR2 /* Signal we use for DNS completion * No (forked) children so this should be unused. */ #define SIGDNS SIGCHLD /* * Last half: ntpd variables * ------------------------- */ /* ntp_config.c */ extern const char *progname; extern char *sys_phone[]; /* ACTS phone numbers */ extern char *ntp_signd_socket; /* ntp_control.c */ extern keyid_t ctl_auth_keyid; /* keyid used for authenticating write requests */ extern void reset_auth_stats(void); /* * Other statistics of possible interest */ extern uint64_t packets_dropped; /* # packets dropped on reception */ extern uint64_t packets_ignored; /* received on wild card interface */ extern uint64_t packets_received; /* total number of packets received */ extern uint64_t packets_sent; /* total number of packets sent */ extern uint64_t packets_notsent; /* total number of packets which couldn't be sent */ /* There used to be a signal handler for received packets. */ /* It's not needed now that the kernel time stamps packets. */ extern uint64_t handler_calls; /* number of calls to interrupt handler */ extern uint64_t handler_pkts; /* number of pkts received by handler */ extern uptime_t io_timereset; /* time counters were reset */ /* ntp_io.c */ extern bool disable_dynamic_updates; extern unsigned int sys_ifnum; /* next .ifnum to assign */ extern endpt * any_interface; /* IPv4 wildcard */ extern endpt * any6_interface; /* IPv6 wildcard */ extern endpt * loopback_interface; /* IPv4 loopback for refclocks */ extern endpt * ep_list; /* linked list */ /* ntp_loopfilter.c */ extern double drift_comp; /* clock frequency (s/s) */ extern double clock_stability; /* clock stability (s/s) */ extern double clock_max_back; /* max backward offset before step (s) */ extern double clock_max_fwd; /* max forward offset before step (s) */ extern double clock_phi; /* dispersion rate (s/s) */ /* * Clock state machine control flags */ extern bool ntp_enable; /* clock discipline enabled */ extern bool pll_control; /* kernel support available */ extern bool kern_enable; /* kernel support enabled */ extern bool hardpps_enable; /* kernel PPS discipline enabled */ extern bool cal_enable; /* refclock calibrate enable */ extern bool allow_panic; /* allow panic correction (-g) */ extern bool force_step_once; /* always step time once at startup (-G) */ extern bool mode_ntpdate; /* exit on first clock set (-q) */ extern int peer_ntpdate; /* count of ntpdate peers */ /* * Clock state machine variables */ extern uint8_t sys_poll; /* system poll interval (log2 s) */ extern int tc_counter; /* poll-adjust counter */ extern double last_offset; /* last clock offset (s) */ extern uint8_t allan_xpt; /* Allan intercept (log2 s) */ extern double clock_jitter; /* clock jitter (s) */ extern double sys_offset; /* system offset (s) */ extern double sys_jitter; /* system jitter (s) */ /* ntp_monitor.c */ extern uint8_t mon_hash_bits; /* log2 size of hash table */ extern mon_entry ** mon_hash; /* MRU hash table */ extern mon_entry mon_mru_list; /* mru listhead */ extern unsigned int mon_enabled; /* MON_OFF (0) or other MON_* */ extern uint64_t mru_entries; /* mru list count */ extern uint64_t mru_peakentries; /* highest mru_entries */ extern uint64_t mru_initalloc; /* entries to preallocate */ extern uint64_t mru_incalloc; /* allocation batch factor */ extern uint64_t mru_mindepth; /* preempt above this */ extern int mru_maxage; /* recycle if older than this */ extern int mru_minage; /* recycle if older than this & full */ extern uint64_t mru_maxdepth; /* MRU size hard limit */ extern uint64_t mru_exists; /* slot already exists */ extern uint64_t mru_new; /* allocated new slot */ extern uint64_t mru_recycleold; /* recycle: age > maxage */ extern uint64_t mru_recyclefull; /* recycle: full and age > minage */ extern uint64_t mru_none; /* couldn't allocate slot */ extern int mon_age; /* preemption limit */ /* ntp_peer.c */ extern struct peer *peer_list; /* peer structures list */ /* * Miscellaneous statistic counters which may be queried. */ extern int peer_associations; /* mobilized associations */ /* ntp_proto.c */ /* * System variables are declared here. See Section 3.2 of the * specification. */ extern uint8_t sys_leap; /* system leap indicator */ extern uint8_t sys_stratum; /* system stratum */ extern int8_t sys_precision; /* local clock precision */ extern double sys_rootdelay; /* roundtrip delay to primary source */ extern double sys_rootdisp; /* dispersion to primary source */ extern double sys_rootdist; /* distance to primary source */ extern uint32_t sys_refid; /* reference id */ extern l_fp sys_reftime; /* last update time */ extern struct peer *sys_peer; /* current peer */ extern int sys_maxclock; /* maximum candidates */ /* * Nonspecified system state variables. */ extern l_fp sys_authdelay; /* authentication delay */ extern int sys_minsane; /* minimum candidates */ /* * Statistics counters */ extern uptime_t sys_stattime; /* time since sysstats reset */ extern uint64_t sys_received; /* packets received */ extern uint64_t sys_processed; /* packets for this host */ extern uint64_t sys_restricted; /* restricted packets */ extern uint64_t sys_newversion; /* current version */ extern uint64_t sys_oldversion; /* old version */ extern uint64_t sys_badlength; /* bad length or format */ extern uint64_t sys_badauth; /* bad authentication */ extern uint64_t sys_declined; /* declined */ extern uint64_t sys_limitrejected; /* rate exceeded */ extern uint64_t sys_kodsent; /* KoD sent */ extern uptime_t use_stattime; /* time since usestats reset */ /* Signalling: Set by signal handlers */ extern volatile bool sawALRM; extern volatile bool sawHUP; extern volatile bool sawDNS; extern volatile bool sawQuit; /* SIGQUIT, SIGINT, SIGTERM */ /* ntp_restrict.c */ extern restrict_u * restrictlist4; /* IPv4 restriction list */ extern restrict_u * restrictlist6; /* IPv6 restriction list */ extern int ntp_minpkt; extern uint8_t ntp_minpoll; /* ntp_signd.c */ #ifdef ENABLE_MSSNTP /* ntp_signd.c */ extern void send_via_ntp_signd(struct recvbuf *, int, keyid_t, int, struct pkt *); #endif /* ntp_timer.c */ extern unsigned long alarm_overflow; extern uptime_t current_time; /* seconds since startup */ extern uptime_t timer_timereset; extern unsigned long timer_xmtcalls; extern bool leap_sec_in_progress; #ifdef ENABLE_LEAP_SMEAR extern struct leap_smear_info leap_smear; extern unsigned int leap_smear_intv; #endif /* ntp_util.c */ extern char statsdir[MAXFILENAME]; extern bool stats_control; /* write stats to fileset? */ extern double wander_threshold; /* ntpd.c */ #ifdef HAVE_WORKING_FORK extern int waitsync_fd_to_close; /* -w/--wait-sync */ #endif /* refclock_conf.c */ #ifdef REFCLOCK /* refclock configuration table */ extern struct refclock * const refclock_conf[]; extern const uint8_t num_refclock_conf; #endif ntpsec-1.1.0+dfsg1/include/ntp_filegen.h0000644000175000017500000000326013252364117017714 0ustar rlaagerrlaager/* * definitions for NTP file generations support * * Copyright (C) 1992, 1996 by Rainer Pruy * Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany * * SPDX-License-Identifier: BSD-2-clause */ #include "ntp_types.h" /* * supported file generation types */ #define FILEGEN_NONE 255 /* no generations - use plain file name */ #define FILEGEN_PID 1 /* one filegen per process incarnation */ #define FILEGEN_DAY 2 /* one filegen per day */ #define FILEGEN_WEEK 3 /* one filegen per week */ #define FILEGEN_MONTH 4 /* one filegen per month */ #define FILEGEN_YEAR 5 /* one filegen per year */ #define FILEGEN_AGE 6 /* change filegen each FG_AGE_SECS */ /* * supported file generation flags */ #define FGEN_FLAG_LINK 0x01 /* make a link to base name */ #define FGEN_FLAG_ENABLED 0x80 /* set this to really create files */ /* without this, open is suppressed */ typedef struct filegen_tag { FILE * fp; /* file referring to current generation */ char * dir; /* currently always statsdir */ char * fname; /* filename prefix of generation file */ /* must be malloced, will be fed to free() */ time_t id_lo; /* lower bound of ident value */ time_t id_hi; /* upper bound of ident value */ uint8_t type; /* type of file generation */ uint8_t flag; /* flags modifying processing of file generation */ } FILEGEN; extern void filegen_setup (FILEGEN *, time_t); extern void filegen_config (FILEGEN *, const char *, const char *, unsigned int, unsigned int); extern void filegen_statsdir(void); extern FILEGEN *filegen_get (const char *); extern void filegen_register (const char *, const char *, FILEGEN *); #ifdef DEBUG extern void filegen_unregister(const char *); #endif ntpsec-1.1.0+dfsg1/include/lib_strbuf.h0000644000175000017500000000060213252364117017552 0ustar rlaagerrlaager/* * lib_strbuf.h - definitions for routines which use the common string buffers */ #ifndef GUARD_LIB_STRBUF_H #define GUARD_LIB_STRBUF_H #include "ntp_types.h" #include "ntp_malloc.h" /* for ZERO() */ /* * Sizes of things */ #define LIB_NUMBUF 16 #define LIB_BUFLENGTH 128 typedef char libbufstr[LIB_BUFLENGTH]; extern char *lib_getbuf(void); #endif /* GUARD_LIB_STRBUF_H */ ntpsec-1.1.0+dfsg1/include/ntp_tty.h0000644000175000017500000000216613252364117017127 0ustar rlaagerrlaager/* * ntp_tty.h - header file for serial lines handling */ #ifndef GUARD_NTP_TTY_H #define GUARD_NTP_TTY_H /* * use only one tty model - no use in initialising * a tty in three ways * only use HAVE_TERMIOS as it is POSIX-1:2001 */ #include #if defined(HAVE_SYS_MODEM_H) #include #endif /* * Line discipline flags. The depredated ones required line discipline * or streams modules to be installed/loaded in the kernel and are now * ignored. Leave the LDISC_CLK and other deprecated symbols defined * until 2013 or 2014 to avoid complicating the use of newer drivers on * older ntpd, which is often as easy as dropping in the refclock *.c. */ #define LDISC_STD 0x000 /* standard */ #define LDISC_CLK 0x001 /* depredated tty_clk \n */ #define LDISC_CLKPPS 0x002 /* depredated tty_clk \377 */ #define LDISC_ACTS 0x004 /* depredated tty_clk #* */ #define LDISC_CHU 0x008 /* depredated */ #define LDISC_PPS 0x010 /* depredated */ #define LDISC_RAW 0x020 /* raw binary */ #define LDISC_REMOTE 0x080 /* remote mode */ #define LDISC_7O1 0x100 /* 7-bit, odd parity for Z3801A */ #endif /* GUARD_NTP_TTY_H */ ntpsec-1.1.0+dfsg1/include/isc_netaddr.h0000644000175000017500000000302413252364117017677 0ustar rlaagerrlaager/* * Copyright (C) 2004-2007, 2009 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2002 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ #ifndef GUARD_ISC_NETADDR_H #define GUARD_ISC_NETADDR_H 1 /* isc_netaddr.h */ #include #include "isc_result.h" /* * Basic Networking Types * * This module is responsible for defining the following basic networking * types: * * struct in_addr * struct in6_addr * struct in6_pktinfo * struct sockaddr * struct sockaddr_in * struct sockaddr_in6 *i in_port_t * * It declares ntoh[sl]() and hton[sl](). * * MP: * No impact. * * Reliability: * No anticipated impact. * * Resources: * N/A. * * Security: * No anticipated impact. * * Standards: * BSD Socket API * RFC 2553 */ /*** *** Functions. ***/ bool isc_net_probeipv4_bool(void); /* * Check if the system's kernel supports IPv4. * * Returns: * True IPv4 is supported. * False IPv4 is not supported. */ bool isc_net_probeipv6_bool(void); /* * Check if the system's kernel supports IPv6. * * Returns: * True IPv4 is supported. * False IPv4 is not supported. */ isc_result_t isc_net_probeipv6(void); /* * Check if the system's kernel supports IPv6. * * Returns: * * #ISC_R_SUCCESS IPv6 is supported. * #ISC_R_NOTFOUND IPv6 is not supported. * #ISC_R_DISABLED IPv6 is disabled. * #ISC_R_UNEXPECTED */ bool isc_net_probe_ipv6only_bool(void); #endif /* GUARD_ISC_NETADDR_H */ ntpsec-1.1.0+dfsg1/include/ntp_types.h0000644000175000017500000000737013252364117017455 0ustar rlaagerrlaager/* * ntp_types.h - NTP basic types and macros. * * Assume C99 fixed width integer types are available: int32_t and uint32_t */ #ifndef GUARD_NTP_TYPES_H #define GUARD_NTP_TYPES_H #include #include #include #include #include "ntp_machine.h" typedef uint32_t uptime_t; /* seconds since startup */ /* * This is another naming conflict. * On NetBSD for MAC the macro "mac" is defined as 1 * this is fun for us as a packet structure contains an * optional "mac" member - severe confusion results 8-) * As we hopefully do not have to rely on that macro we * just undefine that. */ #ifdef mac #undef mac #endif /* * used to quiet compiler warnings */ #ifndef UNUSED_ARG #define UNUSED_ARG(arg) ((void)(arg)) #endif #ifndef UNUSED_LOCAL #define UNUSED_LOCAL(arg) ((void)(arg)) #endif /* * COUNTOF(array) - size of array in elements */ #define COUNTOF(arr) (sizeof(arr) / sizeof((arr)[0])) /* * We now assume the platform supports a 64-bit scalar type (the ISC * library wouldn't compile otherwise). */ typedef uint64_t time64_t; #define LOW32MASK 0x00000000ffffffffUL #define HIGH32MASK 0xffffffff00000000UL #define time64lo(n) ((uint32_t)((n) & LOW32MASK)) #define settime64lo(n, v) (n) = (((n) & HIGH32MASK) | ((v) & LOW32MASK)) #define time64s(n) ((int64_t)(n)) #define settime64s(n,v) (n) = ((uint64_t)(v)) #define time64u(n) (n) #define settime64u(n,v) (n) = (v) #define negtime64(n) ((uint64_t)((((int64_t)(n)) * -1))) typedef uint16_t associd_t; /* association ID */ #define ASSOCID_MAX USHRT_MAX /* In the old days... * Only 16 bits were used for shared keys. * Autokey used to use keys bigger than 16 bits. */ typedef uint32_t keyid_t; /* cryptographic key ID */ #define NTP_MAXKEY 0xffff /* max authentication key number */ /* Max digest length in non-extension MACs, add 4 for keyID */ #define MAX_BARE_DIGEST_LENGTH 20 /* * Ordinary double has only 53 bits of precision in IEEE754. But l_fp * needs 64 bits of precision, arguably 65 bits after 2026. Thus, to have * a float type coextensive with it, we need one with at least 65 bits of * precision. * * The C standard does not specify the precision of a double. C99 * Annex F makes IEEE754 compliance optional and very few C compilers * are fully IEEE754 compliant. C doubles may be 24 bits, 53 bits, or something * else. Only rarely would a C double be able to hold a 65 bit number without * loss of precision. * * This matters because initial steps may be large, such as when a host * has no valid RTC and thinks the current time is 1Jan70. Thus truncating * an l_fp conversion to double after 2026 risks loss of precision. * * On the other hand, long double is usually at least 80 bits. * See https://en.wikipedia.org/wiki/Long_double for discussion and caveats. */ typedef long double doubletime_t; /* * Cloning malloc()'s behavior of always returning pointers suitably * aligned for the strictest alignment requirement of any type is not * easy to do portably, as the maximum alignment required is not * exposed. Use the size of a struct of the types known to represent the * strictest alignment on some platform. This will force the struct to * have the strictest possible alignment. */ typedef struct max_alignment_tag { double d; } max_alignment; #define MAXALIGN sizeof(max_alignment) #define ALIGN_UNITS(sz) (((sz) + MAXALIGN - 1) / MAXALIGN) #define ALIGNED_SIZE(sz) (MAXALIGN * ALIGN_UNITS(sz)) #define INC_ALIGNED_PTR(b, m) ((void *)aligned_ptr((void *)(b), m)) static inline max_alignment * aligned_ptr( max_alignment * base, size_t minsize ) { return base + ALIGN_UNITS((minsize < 1) ? 1 : minsize); } typedef int SOCKET; # define INVALID_SOCKET (-1) # define SOCKET_ERROR (-1) #endif /* GUARD_NTP_TYPES_H */ ntpsec-1.1.0+dfsg1/include/refclock_pps.h0000644000175000017500000000102513252364117020071 0ustar rlaagerrlaager/* * Definitions for the PPS driver and its friends */ struct refclock_ppsctl { pps_handle_t handle; pps_params_t pps_params; struct timespec ts; unsigned long sequence; }; typedef enum { PPS_OK, PPS_SETUP, /* PPS not setup */ PPS_KERNEL, /* PPS error from kernel */ PPS_NREADY /* PPS not ready */ } pps_status; extern bool refclock_ppsapi(int, struct refclock_ppsctl *); extern bool refclock_params(int, struct refclock_ppsctl *); extern pps_status refclock_catcher(struct peer *, struct refclock_ppsctl *, int); ntpsec-1.1.0+dfsg1/include/ntp_dns.h0000644000175000017500000000121513252364117017065 0ustar rlaagerrlaager/* * ntp_dns.h - client interface to DNS name resolution. */ #ifndef GUARD_NTP_DNS_H #define GUARD_NTP_DNS_H #include "ntp_net.h" typedef enum {DNS_good, DNS_temp, DNS_error} DNS_Status; /* start DNS query (unless busy) */ extern bool dns_probe(struct peer*); /* called by main thread to do callbacks */ extern void dns_check(void); /* Callbacks to process answers */ extern void dns_take_server(struct peer*, sockaddr_u*); extern void dns_take_pool(struct peer*, sockaddr_u*); extern void dns_take_status(struct peer*, DNS_Status); /* New interface has appeared - try again */ extern void dns_new_interface(void); #endif /* GUARD_NTP_DNS_H */ ntpsec-1.1.0+dfsg1/include/isc_result.h0000644000175000017500000000137013252364117017576 0ustar rlaagerrlaager/* * Copyright (C) 2004-2009, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001, 2003 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ #ifndef GUARD_ISC_RESULT_H #define GUARD_ISC_RESULT_H 1 /* isc_result.h */ typedef unsigned int isc_result_t; /* Result */ #define ISC_R_SUCCESS 0 /* success */ #define ISC_R_NOMEMORY 1 /* out of memory */ #define ISC_R_NOTFOUND 23 /* not found */ #define ISC_R_FAILURE 25 /* generic failure */ #define ISC_R_NOTIMPLEMENTED 27 /* not implemented */ #define ISC_R_NOMORE 29 /* no more */ #define ISC_R_UNEXPECTED 34 /* unexpected error */ #define ISC_R_IGNORE 36 /* ignore */ #endif /* GUARD_ISC_RESULT_H */ ntpsec-1.1.0+dfsg1/include/mbg_gps166.h0000644000175000017500000005264613252364117017311 0ustar rlaagerrlaager/* * Created: Sun Jul 20 09:20:50 1997 * * File GPSSERIO.H Copyright (c) by Meinberg Funkuhren (www.meinberg.de) * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-clause * * Linkage to PARSE: * Copyright (c) 1997-2005 by Frank Kardel ntp.org> */ #ifndef GUARD_MBG_GPS166_H #define GUARD_MBG_GPS166_H /*************************************************************************** * * Definitions taken from Meinberg's gpsserio.h and gpsdefs.h files. * * Author: Martin Burnicki, Meinberg Funkuhren * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * * Description: * Structures and codes to be used to access Meinberg GPS clocks via * their serial interface COM0. COM0 should be set to a high baud rate, * default is 19200. * * Standard Meinberg GPS serial operation is to send the Meinberg * standard time string automatically once per second, once per * minute, or on request per ASCII '?'. * * GPS parameter setup or parameter readout uses blocks of binary * data which have to be isolated from the standard string. A block * of data starts with a SOH code (ASCII Start Of Header, 0x01) * followed by a message header with constant length and a block of * data with variable length. * * The first field (cmd) of the message header holds the command * code resp. the type of data to be transmitted. The next field (len) * gives the number of data bytes that follow the header. This number * ranges from 0 to sizeof( MSG_DATA ). The third field (data_csum) * holds a checksum of all data bytes and the last field of the header * finally holds the checksum of the header itself. * ***************************************************************************/ /** * @brief GPS epoch bias from ordinary time_t epoch * * The Unix time_t epoch is usually 1970-01-01 00:00 whereas * the GPS epoch is 1980-01-06 00:00, so the difference is 10 years, * plus 2 days due to leap years (1972 and 1976), plus the difference * of the day-of-month (6 - 1), so:
* * time_t t = ( gps_week * ::SECS_PER_WEEK ) + sec_of_week + ::GPS_SEC_BIAS */ #define GPS_SEC_BIAS 315964800UL // ( ( ( 10UL * 365UL ) + 2 + 5 ) * SECS_PER_DAY ) /** * @brief The type of a GPS command code * * @see ::GPS_CMD_CODES */ typedef uint16_t GPS_CMD; /** * @brief Command codes for the binary protocol * * These codes specify commands and associated data types used by Meinberg's * binary protocol to exchange data with a device via serial port, direct USB, * or socket I/O. * * Some commands and associated data structures can be read (r) from a device, others * can be written (w) to the device, and some can also be sent automatically (a) by * a device after a ::GPS_AUTO_ON command has been sent to the device. * The individual command codes are marked with (rwa) accordingly, where '-' is used * to indicate that a particular mode is not supported. * * @note Not all command code are supported by all devices. * See the hints for a particular command. * * @note If ::GPS_ALM, ::GPS_EPH or a code named ..._IDX is sent to retrieve * some data from a device then an uint16_t parameter must be also supplied * in order to specify the index number of the data set to be returned. * The valid index range depends on the command code. * For ::GPS_ALM and ::GPS_EPH the index is the SV number which may be 0 or * ::MIN_SVNO_GPS to ::MAX_SVNO_GPS. If the number is 0 then all ::N_SVNO_GPS * almanacs or ephemeris data structures are returned. * * @see ::GPS_CMD_CODES_TABLE */ enum GPS_CMD_CODES { /* system data */ GPS_AUTO_ON = 0x000, ///< (-w-) no data, enable auto-msgs from device GPS_AUTO_OFF, ///< (-w-) no data, disable auto-msgs from device GPS_SW_REV, ///< (r--) deprecated, ::SW_REV, software revision, use only if ::GPS_RECEIVER_INFO not supp. GPS_BVAR_STAT, ///< (r--) ::BVAR_STAT, status of buffered variables, only if ::GPS_MODEL_HAS_BVAR_STAT GPS_TIME, ///< (-wa) ::TTM, current time or capture, or init board time GPS_POS_XYZ, ///< (rw-) ::XYZ, current position in ECEF coordinates, only if ::GPS_MODEL_HAS_POS_XYZ GPS_POS_LLA, ///< (rw-) ::LLA, current position in geographic coordinates, only if ::GPS_MODEL_HAS_POS_LLA GPS_TZDL, ///< (rw-) ::TZDL, time zone / daylight saving, only if ::GPS_MODEL_HAS_TZDL GPS_PORT_PARM, ///< (rw-) deprecated, ::PORT_PARM, use ::PORT_SETTINGS etc. if ::GPS_RECEIVER_INFO supported GPS_SYNTH, ///< (rw-) ::SYNTH, synthesizer settings, only if ::GPS_HAS_SYNTH GPS_ANT_INFO, ///< (r-a) ::ANT_INFO, time diff after antenna disconnect, only if ::GPS_MODEL_HAS_ANT_INFO GPS_UCAP, ///< (r-a) ::TTM, user capture events, only if ::RECEIVER_INFO::n_ucaps > 0 /* GPS data */ GPS_CFGH = 0x100, ///< (rw-) ::CFGH, SVs' configuration and health codes GPS_ALM, ///< (rw-) req: uint16_t SV num, ::SV_ALM, one SV's almanac GPS_EPH, ///< (rw-) req: uint16_t SV num, ::SV_EPH, one SV's ephemeris GPS_UTC, ///< (rw-) ::UTC, GPS %UTC correction parameters GPS_IONO, ///< (rw-) ::IONO, GPS ionospheric correction parameters GPS_ASCII_MSG ///< (r--) ::ASCII_MSG, the GPS ASCII message }; /* checksum used by some structures stored in non-volatile memory */ typedef uint16_t CSUM; /** * @brief The header of a binary message. */ typedef struct { GPS_CMD cmd; ///< see ::GPS_CMD_CODES uint16_t len; ///< length of the data portion appended after the header CSUM data_csum; ///< checksum of the data portion appended after the header CSUM hdr_csum; ///< checksum of the preceding header bytes } GPS_MSG_HDR; #define GPS_ID_STR_LEN 16 #define GPS_ID_STR_SIZE ( GPS_ID_STR_LEN + 1 ) /** * @brief Software revision information * * Contains a software revision code, plus an optional * identifier for a customized version. */ typedef struct { uint16_t code; ///< Version number, e.g. 0x0120 means v1.20 char name[GPS_ID_STR_SIZE]; ///< Optional string identifying a customized version uint8_t reserved; ///< Reserved field to yield even structure size } SW_REV; /** * @brief GNSS satellite numbers * * @todo: Check if MAX_SVNO_GLN is 94 instead of 95, and thus * N_SVNO_GLN is 30 instead of 31, as reported by Wikipedia. */ enum GNSS_SVNOS { MIN_SVNO_GPS = 1, ///< min. GPS satellite PRN number MAX_SVNO_GPS = 32, ///< max. GPS satellite PRN number N_SVNO_GPS = 32, ///< max. number of active GPS satellites MIN_SVNO_WAAS = 33, ///< min. WAAS satellite number MAX_SVNO_WAAS = 64, ///< max. WAAS satellite number N_SVNO_WAAS = 32, ///< max. number of active WAAS satellites MIN_SVNO_GLONASS = 65, ///< min. Glonass satellite number (64 + sat slot ID) MAX_SVNO_GLONASS = 95, ///< max. Glonass satellite number (64 + sat slot ID) N_SVNO_GLONASS = 31 ///< max. number of active Glonass satellites }; typedef uint16_t SVNO; ///< the number of an SV (Space Vehicle, i.e. satellite) typedef uint16_t HEALTH; ///< an SV's 6 bit health code typedef uint16_t CFG; ///< an SV's 4 bit configuration code typedef uint16_t IOD; ///< Issue-Of-Data code /** * @brief Status flags of battery buffered data * * Related to data received from the satellites, or data derived thereof. * * All '0' means OK, single bits set to '1' indicate * the associated type of GPS data is not available. * * @see ::BVAR_FLAGS */ typedef uint16_t BVAR_STAT; /** * @brief Enumeration of flag bits used to define ::BVAR_FLAGS * * For each bit which is set this means the associated data set in * non-volatile memory is not available, or incomplete. * Most data sets will just be re-collected from the data streams sent * by the satellites. However, the receiver position has usually been * computed earlier during normal operation, and will be re-computed * when a sufficient number of satellites can be received. * * @see ::BVAR_STAT * @see ::BVAR_FLAGS * @see ::BVAR_FLAG_NAMES */ enum BVAR_FLAG_BITS { BVAR_BIT_CFGH_INVALID, ///< Satellite configuration and health parameters incomplete BVAR_BIT_ALM_NOT_COMPLETE, ///< Almanac parameters incomplete BVAR_BIT_UTC_INVALID, ///< %UTC offset parameters incomplete BVAR_BIT_IONO_INVALID, ///< Ionospheric correction parameters incomplete BVAR_BIT_RCVR_POS_INVALID, ///< No valid receiver position available N_BVAR_BIT ///< number of defined ::BVAR_STAT bits }; /** * @brief Bit masks associated with ::BVAR_FLAG_BITS * * Used with ::BVAR_STAT. * * @see ::BVAR_STAT * @see ::BVAR_FLAG_BITS * @see ::BVAR_FLAG_NAMES */ enum BVAR_FLAGS { BVAR_CFGH_INVALID = ( 1UL << BVAR_BIT_CFGH_INVALID ), ///< see ::BVAR_BIT_CFGH_INVALID BVAR_ALM_NOT_COMPLETE = ( 1UL << BVAR_BIT_ALM_NOT_COMPLETE ), ///< see ::BVAR_BIT_ALM_NOT_COMPLETE BVAR_UTC_INVALID = ( 1UL << BVAR_BIT_UTC_INVALID ), ///< see ::BVAR_BIT_UTC_INVALID BVAR_IONO_INVALID = ( 1UL << BVAR_BIT_IONO_INVALID ), ///< see ::BVAR_BIT_IONO_INVALID BVAR_RCVR_POS_INVALID = ( 1UL << BVAR_BIT_RCVR_POS_INVALID ), ///< see ::BVAR_BIT_RCVR_POS_INVALID }; /** * @brief A structure used to hold time in GPS format * * Date and time refer to the linear time scale defined by GPS, with * the epoch starting at %UTC midnight at the beginning of January 6, 1980. * * GPS time is counted by the week numbers since the epoch, plus second * of the week, plus fraction of the second. The week number transmitted * by the satellites rolls over from 1023 to 0, but Meinberg devices * just continue to count the weeks beyond the 1024 week limit to keep * the receiver's internal time. * * %UTC time differs from GPS time since a number of leap seconds have * been inserted in the %UTC time scale after the GPS epoche. The number * of leap seconds is disseminated by the satellites using the ::UTC * parameter set, which also provides info on pending leap seconds. */ typedef struct { uint16_t wn; ///< the week number since GPS has been installed uint32_t sec; ///< the second of that week uint32_t tick; ///< fractions of a second, 1/::RECEIVER_INFO::ticks_per_sec units } T_GPS; /** * @brief Local date and time computed from GPS time * * The current number of leap seconds have to be added to get %UTC * from GPS time. Additional corrections could have been made according * to the time zone/daylight saving parameters ::TZDL defined by the user. * The status field can be checked to see which corrections * have actually been applied. * * @note Conversion from GPS time to %UTC and/or local time can only be * done if some valid ::UTC correction parameters are available in the * receiver's non-volatile memory. */ typedef struct { int16_t year; ///< year number, 0..9999 int8_t month; ///< month, 1..12 int8_t mday; ///< day of month, 1..31 int16_t yday; ///< day of year, 1..365, or 366 in case of leap year int8_t wday; ///< day of week, 0..6 == Sun..Sat int8_t hour; ///< hours, 0..23 int8_t min; ///< minutes, 0..59 int8_t sec; ///< seconds, 0..59, or 60 in case of inserted leap second int32_t frac; ///< fractions of a second, 1/::RECEIVER_INFO::ticks_per_sec units int32_t offs_from_utc; ///< local time offset from %UTC [sec] uint16_t status; ///< status flags, see ::TM_GPS_STATUS_BIT_MASKS } TM_GPS; /** * @brief Status flag bits used to define ::TM_GPS_STATUS_BIT_MASKS * * These bits report info on the time conversion from GPS time to %UTC * and/or local time as well as device status info. * * @see ::TM_GPS_STATUS_BIT_MASKS */ enum TM_GPS_STATUS_BITS { TM_BIT_UTC, ///< %UTC correction has been made TM_BIT_LOCAL, ///< %UTC has been converted to local time according to ::TZDL settings TM_BIT_DL_ANN, ///< state of daylight saving is going to change TM_BIT_DL_ENB, ///< daylight saving is in effect TM_BIT_LS_ANN, ///< leap second pending TM_BIT_LS_ENB, ///< current second is leap second TM_BIT_LS_ANN_NEG, ///< set in addition to ::TM_BIT_LS_ANN if leap sec is negative TM_BIT_INVT, ///< invalid time, e.g. if RTC battery bas been empty TM_BIT_EXT_SYNC, ///< synchronized externally TM_BIT_HOLDOVER, ///< in holdover mode after previous synchronization TM_BIT_ANT_SHORT, ///< antenna cable short circuited TM_BIT_NO_WARM, ///< OCXO has not warmed up TM_BIT_ANT_DISCONN, ///< antenna currently disconnected TM_BIT_SYN_FLAG, ///< TIME_SYN output is low TM_BIT_NO_SYNC, ///< time sync actually not verified TM_BIT_NO_POS ///< position actually not verified, LOCK LED off }; /** * @brief Status flag masks used with ::TM_GPS::status * * These bits report info on the time conversion from GPS time to %UTC * and/or local time as well as device status info. * * @see ::TM_GPS_STATUS_BITS */ enum TM_GPS_STATUS_BIT_MASKS { TM_UTC = ( 1UL << TM_BIT_UTC ), ///< see ::TM_BIT_UTC TM_LOCAL = ( 1UL << TM_BIT_LOCAL ), ///< see ::TM_BIT_LOCAL TM_DL_ANN = ( 1UL << TM_BIT_DL_ANN ), ///< see ::TM_BIT_DL_ANN TM_DL_ENB = ( 1UL << TM_BIT_DL_ENB ), ///< see ::TM_BIT_DL_ENB TM_LS_ANN = ( 1UL << TM_BIT_LS_ANN ), ///< see ::TM_BIT_LS_ANN TM_LS_ENB = ( 1UL << TM_BIT_LS_ENB ), ///< see ::TM_BIT_LS_ENB TM_LS_ANN_NEG = ( 1UL << TM_BIT_LS_ANN_NEG ), ///< see ::TM_BIT_LS_ANN_NEG TM_INVT = ( 1UL << TM_BIT_INVT ), ///< see ::TM_BIT_INVT TM_EXT_SYNC = ( 1UL << TM_BIT_EXT_SYNC ), ///< see ::TM_BIT_EXT_SYNC TM_HOLDOVER = ( 1UL << TM_BIT_HOLDOVER ), ///< see ::TM_BIT_HOLDOVER TM_ANT_SHORT = ( 1UL << TM_BIT_ANT_SHORT ), ///< see ::TM_BIT_ANT_SHORT TM_NO_WARM = ( 1UL << TM_BIT_NO_WARM ), ///< see ::TM_BIT_NO_WARM TM_ANT_DISCONN = ( 1UL << TM_BIT_ANT_DISCONN ), ///< see ::TM_BIT_ANT_DISCONN TM_SYN_FLAG = ( 1UL << TM_BIT_SYN_FLAG ), ///< see ::TM_BIT_SYN_FLAG TM_NO_SYNC = ( 1UL << TM_BIT_NO_SYNC ), ///< see ::TM_BIT_NO_SYNC TM_NO_POS = ( 1UL << TM_BIT_NO_POS ) ///< see ::TM_BIT_NO_POS }; /* Two types of variables used to store a position. Type XYZ is */ /* used with a position in earth centered, earth fixed (ECEF) */ /* coordinates whereas type LLA holds such a position converted */ /* to geographic coordinates as defined by WGS84 (World Geodetic */ /* System from 1984). */ /** * @brief Sequence and number of components of a cartesian position */ enum XYZ_FIELDS { XP, YP, ZP, N_XYZ }; // x, y, z /** * @brief A position in cartesian coordinates * * Usually earth centered, earth fixed (ECEF) coordinates, * in [m]. * * @note In the original code this is an array of double. * * @see ::XYZ_FIELDS */ typedef l_fp XYZ[N_XYZ]; /** * @brief Sequence and number of components of a geographic position */ enum LLA_FIELDS { LAT, LON, ALT, N_LLA }; /* latitude, longitude, altitude */ /** * @brief A geographic position based on latitude, longitude, and altitude * * The geographic position associated to specific cartesian coordinates * depends on the characteristics of the ellipsoid used for the computation, * the so-called geographic datum. GPS uses the WGS84 (World Geodetic System * from 1984) ellipsoid by default. * * lon, lat in [rad], alt in [m] * * @note In the original code this is an array of double. * * @see ::LLA_FIELDS */ typedef l_fp LLA[N_LLA]; /** * @brief The name of a time zone * * @note Up to 5 printable characters, plus trailing zero */ typedef char TZ_NAME[6]; /** * @brief Antenna status and error at reconnect information * * The structure below reflects the status of the antenna, * the times of last disconnect/reconnect, and the board's * clock offset when it has synchronized again after the * disconnection interval. * * @note ::ANT_INFO::status changes back to ::ANT_RECONN only * after the antenna has been reconnected and the * receiver has re-synchronized to the satellite signal. * In this case ::ANT_INFO::delta_t reports the time offset * before resynchronization, i.e. how much the internal * time has drifted while the antenna was disconnected. */ typedef struct { int16_t status; ///< current status of antenna, see ::ANT_STATUS_CODES TM_GPS tm_disconn; ///< time of antenna disconnect TM_GPS tm_reconn; ///< time of antenna reconnect int32_t delta_t; ///< clock offs at reconn. time in 1/::RECEIVER_INFO::ticks_per_sec units } ANT_INFO; /** * @brief Status code used with ::ANT_INFO::status */ enum ANT_STATUS_CODES { ANT_INVALID, ///< No other fields valid since antenna has not yet been disconnected ANT_DISCONN, ///< Antenna is disconnected, tm_reconn and delta_t not yet set ANT_RECONN, ///< Antenna has been disconnect, and receiver sync. after reconnect, so all fields valid N_ANT_STATUS_CODES ///< the number of known status codes }; /** * @brief Summary of configuration and health data of all satellites */ typedef struct { CSUM csum; ///< checksum of the remaining bytes int16_t valid; ///< flag data are valid T_GPS tot_51; ///< time of transmission, page 51 T_GPS tot_63; ///< time of transmission, page 63 T_GPS t0a; ///< complete reference time almanac CFG cfg[N_SVNO_GPS]; ///< 4 bit SV configuration code from page 63 HEALTH health[N_SVNO_GPS]; ///< 6 bit SV health codes from pages 51, 63 } CFGH; /** * @brief GPS %UTC correction parameters * * %UTC correction parameters basically as sent by the GPS satellites. * * The csum field is only used by the card's firmware to check the * consistency of the structure in non-volatile memory. * * The field labeled valid indicates if the parameter set is valid, i.e. * if it contains data received from the satellites. * * t0t, A0 and A1 contain fractional correction parameters for the current * GPS-%UTC time offset in addition to the whole seconds. This is evaluated * by the receivers' firmware to convert GPS time to %UTC time. * * The delta_tls field contains the current full seconds offset between * GPS time and %UTC, which corresponds to the number of leap seconds inserted * into the %UTC time scale since GPS was put into operation in January 1980. * * delta_tlfs holds the number of "future" leap seconds, i.e. the %UTC offset * after the next leap second event defined by WNlsf and DNt. * * The fields WNlsf and DNt specify the GPS week number and the day number * in that week for the end of which a leap second has been scheduled. * * @note: The satellites transmit WNlsf only as a signed 8 bit value, so it * can only define a point in time which is +/- 127 weeks off the current time. * The firmware tries to expand this based on the current week number, but * the result is ambiguous if the leap second occurs or occurred more * than 127 weeks in the future or past. * * So the leap second date should only be evaluated and displayed * in a user interface if the fields delta_tls and delta_tlsf have * different values, in which case there is indeed a leap second announcement * inside the +/- 127 week range. * * @note In the original code the type of A0 and A1 is double. */ typedef struct { CSUM csum; ///< Checksum of the remaining bytes int16_t valid; ///< Flag indicating %UTC parameters are valid T_GPS t0t; ///< Reference Time %UTC Parameters [wn|sec] l_fp A0; ///< +- Clock Correction Coefficient 0 [sec] l_fp A1; ///< +- Clock Correction Coefficient 1 [sec/sec] uint16_t WNlsf; ///< Week number of nearest leap second int16_t DNt; ///< The day number at the end of which a leap second occurs int8_t delta_tls; ///< Current %UTC offset to GPS system time [sec] int8_t delta_tlsf; ///< Future %UTC offset to GPS system time after next leap second transition [sec] } UTC; /** * @brief GPS ASCII message */ typedef struct { CSUM csum; ///< checksum of the remaining bytes */ int16_t valid; ///< flag data are valid char s[23]; ///< 22 chars GPS ASCII message plus trailing zero } ASCII_MSG; void mbg_tm_str (char **, TM_GPS *, size_t, int); void mbg_tgps_str (char **, T_GPS *, size_t); void get_mbg_header (unsigned char **, GPS_MSG_HDR *); void put_mbg_header (unsigned char **, GPS_MSG_HDR *); void get_mbg_sw_rev (unsigned char **, SW_REV *); void get_mbg_ascii_msg (unsigned char **, ASCII_MSG *); void get_mbg_antinfo (unsigned char **, ANT_INFO *); void get_mbg_cfgh (unsigned char **, CFGH *); void get_mbg_utc (unsigned char **, UTC *); void get_mbg_lla (unsigned char **, LLA); void get_mbg_xyz (unsigned char **, XYZ); CSUM mbg_csum (unsigned char *, unsigned int) __attribute__((pure)); #endif /* * History: * * mbg_gps166.h,v * Revision 4.7 2006/06/22 18:41:43 kardel * clean up signedness (gcc 4) * * Revision 4.6 2005/10/07 22:11:56 kardel * bounded buffer implementation * * Revision 4.5.2.1 2005/09/25 10:23:48 kardel * support bounded buffers * * Revision 4.5 2005/06/25 10:58:45 kardel * add missing log keywords * * Revision 4.1 1998/06/12 15:07:30 kardel * fixed prototyping * * Revision 4.0 1998/04/10 19:50:42 kardel * Start 4.0 release version numbering * * Revision 1.1 1998/04/10 19:27:34 kardel * initial NTP VERSION 4 integration of PARSE with GPS166 binary support * * Revision 1.1 1997/10/06 20:55:38 kardel * new parse structure * */ ntpsec-1.1.0+dfsg1/include/recvbuff.h0000644000175000017500000000623113252364117017225 0ustar rlaagerrlaager#ifndef GUARD_RECVBUFF_H #define GUARD_RECVBUFF_H #include "ntp.h" #include "ntp_net.h" #include "ntp_lists.h" /* * recvbuf memory management */ #define RECV_INIT 10 /* 10 buffers initially */ #define RECV_LOWAT 3 /* when we're down to three buffers get more */ #define RECV_INC 5 /* get 5 more at a time */ #define RECV_TOOMANY 40 /* this is way too many buffers */ /* * Format of a recvbuf. Back when ntpd did true asynchronous * I/O, these were used by the asynchronous receive routine to store * incoming packets and related information. Now, with faster processors * and lower latency in the synchronous I/O loop, that complexity * has been dropped. */ /* * the maximum length NTP packet contains the NTP header, one Autokey * request, one Autokey response and the MAC (Autokey has been removed * from NTPsec, but we need to deal with the largest packets from legacy * versions). Assuming certificates don't get too big, the maximum packet * length is set arbitrarily at 1000. */ #define RX_BUFF_SIZE 1000 /* hail Mary */ typedef struct recvbuf recvbuf_t; struct recvbuf { recvbuf_t * link; /* next in list */ union { sockaddr_u X_recv_srcadr; struct peer * X_recv_peer; } X_from_where; #define recv_srcadr X_from_where.X_recv_srcadr #define recv_peer X_from_where.X_recv_peer sockaddr_u srcadr; /* where packet came from */ endpt * dstadr; /* address pkt arrived on */ SOCKET fd; /* fd on which it was received */ int cast_flags; /* unicast/broadcast/manycast mode */ l_fp recv_time; /* time of arrival */ void (*receiver)(struct recvbuf *); /* callback */ size_t recv_length; /* number of octets received */ union { struct pkt X_recv_pkt; uint8_t X_recv_buffer[RX_BUFF_SIZE]; } recv_space; #define recv_pkt recv_space.X_recv_pkt #define recv_buffer recv_space.X_recv_buffer int used; /* reference count */ #ifdef REFCLOCK bool network_packet; #endif /* REFCLOCK */ }; extern void init_recvbuff(unsigned int); /* not really pure */ /* freerecvbuf - make a single recvbuf available for reuse */ extern void freerecvbuf(struct recvbuf *); /* Get a free buffer (typically used so an async * read can directly place data into the buffer * * The buffer is removed from the free list. Make sure * you put it back with freerecvbuf() or */ /* signal safe - no malloc */ extern struct recvbuf *get_free_recv_buffer(void); /* Add a buffer to the full list */ extern void add_full_recv_buffer(struct recvbuf *); /* number of recvbufs on freelist */ extern unsigned long free_recvbuffs(void); /* not really pure */ extern unsigned long full_recvbuffs(void); /* not really pure */ extern unsigned long total_recvbuffs(void); /* not really pure */ extern unsigned long lowater_additions(void); /* not really pure */ /* Returns the next buffer in the full list. * */ extern struct recvbuf *get_full_recv_buffer(void); /* * purge_recv_buffers_for_fd() - purges any previously-received input * from a given file descriptor. */ extern void purge_recv_buffers_for_fd(SOCKET); /* * Checks to see if there are buffers to process */ extern bool has_full_recv_buffer(void); /* not really pure */ #endif /* GUARD_RECVBUFF_H */ ntpsec-1.1.0+dfsg1/include/binio.h0000644000175000017500000000247013252364117016524 0ustar rlaagerrlaager/* * Created: Sun Jul 20 13:03:05 1997 * * Copyright (c) 1997-2005 by Frank Kardel ntp.org> * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-clause */ #ifndef GUARD_BINIO_H #define GUARD_BINIO_H #include "ntp_stdlib.h" int16_t get_lsb_int16 (unsigned char **); void put_lsb_uint16 (unsigned char **, uint16_t); int32_t get_lsb_int32 (unsigned char **); #define get_lsb_uint16( _x_ ) ((uint16_t) get_lsb_int16( _x_ )) #define get_lsb_uint32( _x_ ) ((uint32_t) get_lsb_int32( _x_ )) unsigned short get_msb_ushort(unsigned char *); short getmsb_short(unsigned char *); long get_msb_short(unsigned char **); #endif /* * History: * * binio.h,v * Revision 4.5 2005/04/16 17:32:10 kardel * update copyright * * Revision 4.4 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.2 1998/06/28 16:52:15 kardel * added binio MSB prototypes for {get,put}_msb_{short,long} * * Revision 4.1 1998/06/12 15:07:40 kardel * fixed prototyping * * Revision 4.0 1998/04/10 19:50:38 kardel * Start 4.0 release version numbering * * Revision 1.1 1998/04/10 19:27:32 kardel * initial NTP VERSION 4 integration of PARSE with GPS166 binary support * * Revision 1.1 1997/10/06 20:55:37 kardel * new parse structure * */ ntpsec-1.1.0+dfsg1/include/isc_interfaceiter.h0000644000175000017500000000725113252364117021110 0ustar rlaagerrlaager/* * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ #ifndef GUARD_ISC_INTERFACEITER_H #define GUARD_ISC_INTERFACEITER_H 1 /***** ***** Module Info *****/ /* isc_interfaceiter.h * Iterates over the list of network interfaces. * * Interfaces whose address family is not supported are ignored and never * returned by the iterator. Interfaces whose netmask, interface flags, * or similar cannot be obtained are also ignored, and the failure is logged. * * Standards: * The API for scanning varies greatly among operating systems. * This module attempts to hide the differences. */ /*** *** Imports ***/ #include "isc_result.h" typedef struct isc_interfaceiter isc_interfaceiter_t; /* Interface Iterator */ typedef struct isc_mem isc_mem_t; /* Memory */ /* Net Address */ typedef struct isc_netaddr { unsigned int family; union { struct in_addr in; struct in6_addr in6; } type; uint32_t zone; } isc_netaddr_t; /*! * \brief Public structure describing a network interface. */ typedef struct isc_interface { char name[32]; /* Interface name, null-terminated. */ unsigned int af; /* Address family. */ isc_netaddr_t address; /* Local address. */ isc_netaddr_t netmask; /* Network mask. */ isc_netaddr_t broadcast; /* Broadcast address. */ isc_netaddr_t dstaddress; /* Destination address (point-to-point only). */ uint32_t flags; /* Flags; see INTERFACE flags. */ unsigned int ifindex; /* Interface index for IP(V6)_MULTICAST_IF. */ } isc_interface_t; /*@{*/ /*! Interface flags. */ #define INTERFACE_F_UP 0x00000001U #define INTERFACE_F_POINTTOPOINT 0x00000002U #define INTERFACE_F_LOOPBACK 0x00000004U #define INTERFACE_F_BROADCAST 0x00000008U #define INTERFACE_F_MULTICAST 0x00000010U #define INTERFACE_F_PRIVACY 0x00000020U /* RFC 4941 */ /*@}*/ /*** *** Functions ***/ bool isc_interfaceiter_create_bool(isc_mem_t *mctx, isc_interfaceiter_t **iterp); isc_result_t isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp); /*!< * \brief Create an iterator for traversing the operating system's list * of network interfaces. * * Returns: *\li #ISC_R_SUCCESS * \li #ISC_R_NOMEMORY *\li Various network-related errors */ bool isc_interfaceiter_first_bool(isc_interfaceiter_t *iter); isc_result_t isc_interfaceiter_first(isc_interfaceiter_t *iter); /*!< * \brief Position the iterator on the first interface. * * Returns: *\li #ISC_R_SUCCESS Success. *\li #ISC_R_NOMORE There are no interfaces. */ bool isc_interfaceiter_current_bool(isc_interfaceiter_t *iter, isc_interface_t *ifdata); isc_result_t isc_interfaceiter_current(isc_interfaceiter_t *iter, isc_interface_t *ifdata); /*!< * \brief Get information about the interface the iterator is currently * positioned at and store it at *ifdata. * * Requires: *\li The iterator has been successfully positioned using * isc_interface_iter_first() / isc_interface_iter_next(). * * Returns: *\li #ISC_R_SUCCESS Success. */ bool isc_interfaceiter_next_bool(isc_interfaceiter_t *iter); isc_result_t isc_interfaceiter_next(isc_interfaceiter_t *iter); /*!< * \brief Position the iterator on the next interface. * * Requires: * \li The iterator has been successfully positioned using * isc_interface_iter_first() / isc_interface_iter_next(). * * Returns: *\li #ISC_R_SUCCESS Success. *\li #ISC_R_NOMORE There are no more interfaces. */ void isc_interfaceiter_destroy(isc_interfaceiter_t **iterp); /*!< * \brief Destroy the iterator. */ #endif /* GUARD_ISC_INTERFACEITER_H */ ntpsec-1.1.0+dfsg1/.gitlab-ci.yml0000644000175000017500000001552213252364117016266 0ustar rlaagerrlaager#pages: # stage: deploy # script: # - apt-get update -qq && apt-get install -y -qq bison python-dev asciidoc # - python ./waf configure --enable-doc --prefix=/tmp/docbot-local --htmldir=`pwd`/public/latest/ build install # - cp www/favicon.ico `pwd`/public # - cp docs/top.html.in `pwd`/public/index.html # - cp docs/top-asciidoc.css `pwd`/public/asciidoc.css # allow_failure: true # artifacts: # paths: # - public ## only: ## - master # tags: # - gitlab-org #codequality: # stage: test # image: docker:latest # variables: # DOCKER_DRIVER: overlay # services: # - docker:dind # script: # - docker pull codeclimate/codeclimate # - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f json > codeclimate.json # allow_failure: true # artifacts: # paths: [codeclimate.json] # tags: # - docker .job_template: &job_definition stage: build artifacts: expire_in: 7d paths: - build/main/attic/sht - build/main/ntpclients/ntpdig - build/main/ntpclients/ntpkeygen - build/main/ntpclients/ntpleapfetch - build/main/ntpclients/ntploggps - build/main/ntpclients/ntplogtemp - build/main/ntpclients/ntpmon - build/main/ntpclients/ntpq - build/main/ntpclients/ntpsweep - build/main/ntpclients/ntptrace - build/main/ntpclients/ntpviz - build/main/ntpclients/ntpwait - build/main/ntpd/ntpd - build/main/ntpfrob/ntpfrob gitlab-basic: <<: *job_definition script: - apt-get update -qq && apt-get install -y -qq bison libcap-dev pps-tools python-dev - python ./waf configure build check tags: - gitlab-org gitlab-refclocks: <<: *job_definition script: - apt-get update -qq && apt-get install -y -qq bison libcap-dev pps-tools python-dev - python ./waf configure --refclock=all build check tags: - gitlab-org debian-wheezy-basic: <<: *job_definition script: - python ./waf configure build check tags: - debian-wheezy only: - branches@NTPsec/ntpsec debian-wheezy-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - debian-wheezy only: - branches@NTPsec/ntpsec debian-jessie-basic: <<: *job_definition script: - python ./waf configure build check tags: - debian-jessie only: - branches@NTPsec/ntpsec debian-jessie-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - debian-jessie only: - branches@NTPsec/ntpsec debian-stretch-basic: <<: *job_definition script: - python ./waf configure build check tags: - debian-stretch only: - branches@NTPsec/ntpsec debian-stretch-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - debian-stretch only: - branches@NTPsec/ntpsec ubuntu-1404-lts-basic: <<: *job_definition script: - python ./waf configure build check tags: - ubuntu-1404-lts only: - branches@NTPsec/ntpsec ubuntu-1404-lts-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - ubuntu-1404-lts only: - branches@NTPsec/ntpsec ubuntu-1604-lts-basic: <<: *job_definition script: - python ./waf configure build check tags: - ubuntu-1604-lts only: - branches@NTPsec/ntpsec ubuntu-1604-lts-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - ubuntu-1604-lts only: - branches@NTPsec/ntpsec ubuntu-1710-basic: <<: *job_definition script: - python ./waf configure build check tags: - ubuntu-1710 only: - branches@NTPsec/ntpsec ubuntu-1710-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - ubuntu-1710 only: - branches@NTPsec/ntpsec gcc-7-basic: <<: *job_definition image: gcc:7 script: - apt-get update -qq && apt-get install -y -qq bison libcap-dev pps-tools python-dev - python ./waf configure build check tags: - gitlab-org gcc-7-refclocks: <<: *job_definition image: gcc:7 script: - apt-get update -qq && apt-get install -y -qq bison libcap-dev pps-tools python-dev - python ./waf configure --refclock=all build check tags: - gitlab-org freebsd-10-basic: <<: *job_definition script: - python ./waf configure build check tags: - freebsd-10 only: - branches@NTPsec/ntpsec freebsd-10-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - freebsd-10 only: - branches@NTPsec/ntpsec freebsd-11-basic: <<: *job_definition script: - python ./waf configure build check tags: - freebsd-11 only: - branches@NTPsec/ntpsec freebsd-11-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - freebsd-11 only: - branches@NTPsec/ntpsec centos-6-basic: <<: *job_definition script: - python ./waf configure build check tags: - centos-6 only: - branches@NTPsec/ntpsec centos-6-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - centos-6 only: - branches@NTPsec/ntpsec centos-7-basic: <<: *job_definition script: - python ./waf configure build check tags: - centos-7 only: - branches@NTPsec/ntpsec centos-7-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - centos-7 only: - branches@NTPsec/ntpsec fedora-26-basic: <<: *job_definition script: - python ./waf configure build check tags: - fedora-26 only: - branches@NTPsec/ntpsec fedora-26-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - fedora-26 only: - branches@NTPsec/ntpsec fedora-27-basic: <<: *job_definition script: - python ./waf configure build check tags: - fedora-27 only: - branches@NTPsec/ntpsec fedora-27-refclocks: <<: *job_definition script: - python ./waf configure --refclock=all build check tags: - fedora-27 only: - branches@NTPsec/ntpsec python3: <<: *job_definition image: python:3 script: - apt-get update -qq && apt-get install -y -qq bison libcap-dev pps-tools - python ./waf configure --refclock=all build check tags: - gitlab-org python-coverage: <<: *job_definition script: - apt-get update -qq && apt-get install -y -qq bison python-dev python-coverage - python ./waf configure build check - for i in build/main/tests/pylib/test_*; do python-coverage run -a --source build/main/pylib $i; done - python-coverage report tags: - gitlab-org ntpsec-1.1.0+dfsg1/README-PYTHON0000644000175000017500000000325313252364117015527 0ustar rlaagerrlaagerIf you are trying to debug something like: ImportError: No module named ntp you have come to the right place. The default location where we install our python libraries is /usr/local/lib/pythonX.Y/site-packages/ where X and Y are the python version numbers. Unfortunately, that's not on the default search path of several OSes/distros, in particular Fedora and NetBSD. Python has a search path that is used to find library modules when you import them. You can see your search path with: python2 -c "import sys; print sys.path" or python3 -c "import sys; print(sys.path)" Info on Python's search path: https://docs.python.org/2/tutorial/modules.html or https://docs.python.org/3/tutorial/modules.html There are several ways to make things work. 1: You can modify the location where waf will install the libraries. For NetBSD, something like this should work: ./waf configure \ --pythondir=/usr/pkg/lib/python2.7/site-packages \ --pythonarchdir=/usr/pkg/lib/python2.7/site-packages \ ... You need to specify it at configure time. Install time is too late. 2: You can setup your PYTHONPATH with something like this: export PYTHONPATH=/usr/local/lib/python2.7/site-packages For bash, you can add that line to your .bashrc or the system /etc/bashrc If you don't put it in the system file, all users will have to do this, including root if root uses any ntp scripts. 3: You can add to the default search path by setting up a .pth file with something like this: echo /usr/local/lib/python2.7/site-packages > \ /usr/lib/python2.7/site-packages/ntpsec.pth This works for all users, including root. Note that the pth file must be on the default Python search path. ntpsec-1.1.0+dfsg1/libjsmn/0000775000175000017500000000000013252650651015266 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/libjsmn/example/0000775000175000017500000000000013252650651016721 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/libjsmn/example/jsondump.c0000644000175000017500000000447213252364117020730 0ustar rlaagerrlaager#include #include #include #include #include "../jsmn.h" /* * An example of reading JSON from stdin and printing its content to stdout. * The output looks like YAML, but I'm not sure if it's really compatible. */ static int dump(const char *js, jsmntok_t *t, size_t count, int indent) { int i, j, k; if (count == 0) { return 0; } if (t->type == JSMN_PRIMITIVE) { printf("%.*s", t->end - t->start, js+t->start); return 1; } else if (t->type == JSMN_STRING) { printf("'%.*s'", t->end - t->start, js+t->start); return 1; } else if (t->type == JSMN_OBJECT) { printf("\n"); j = 0; for (i = 0; i < t->size; i++) { for (k = 0; k < indent; k++) printf(" "); j += dump(js, t+1+j, count-j, indent+1); printf(": "); j += dump(js, t+1+j, count-j, indent+1); printf("\n"); } return j+1; } else if (t->type == JSMN_ARRAY) { j = 0; printf("\n"); for (i = 0; i < t->size; i++) { for (k = 0; k < indent-1; k++) printf(" "); printf(" - "); j += dump(js, t+1+j, count-j, indent+1); printf("\n"); } return j+1; } return 0; } int main() { int r; int eof_expected = 0; char *js = NULL; size_t jslen = 0; char buf[BUFSIZ]; jsmn_parser p; jsmntok_t *tok; size_t tokcount = 2; /* Prepare parser */ jsmn_init(&p); /* Allocate some tokens as a start */ tok = malloc(sizeof(*tok) * tokcount); if (tok == NULL) { fprintf(stderr, "malloc(): errno=%d\n", errno); return 3; } for (;;) { /* Read another chunk */ r = fread(buf, 1, sizeof(buf), stdin); if (r < 0) { fprintf(stderr, "fread(): %d, errno=%d\n", r, errno); return 1; } if (r == 0) { if (eof_expected != 0) { return 0; } else { fprintf(stderr, "fread(): unexpected EOF\n"); return 2; } } js = realloc(js, jslen + r + 1); if (js == NULL) { fprintf(stderr, "realloc(): errno=%d\n", errno); return 3; } strlcpy(js + jslen, buf, r + 1); jslen = jslen + r; again: r = jsmn_parse(&p, js, jslen, tok, tokcount); if (r < 0) { if (r == JSMN_ERROR_NOMEM) { tokcount = tokcount * 2; tok = realloc(tok, sizeof(*tok) * tokcount); if (tok == NULL) { fprintf(stderr, "realloc(): errno=%d\n", errno); return 3; } goto again; } } else { dump(js, tok, p.toknext, 0); eof_expected = 1; } } return 0; } ntpsec-1.1.0+dfsg1/libjsmn/example/simple.c0000644000175000017500000000417613252364117020363 0ustar rlaagerrlaager#include #include #include "../jsmn.h" /* * A small example of jsmn parsing when JSON structure is known and number of * tokens is predictable. */ const char *JSON_STRING = "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}"; static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && strncmp(json + tok->start, s, tok->end - tok->start) == 0) { return 0; } return -1; } int main() { int i; int r; jsmn_parser p; jsmntok_t t[128]; /* We expect no more than 128 tokens */ jsmn_init(&p); r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0])); if (r < 0) { printf("Failed to parse JSON: %d\n", r); return 1; } /* Assume the top-level element is an object */ if (r < 1 || t[0].type != JSMN_OBJECT) { printf("Object expected\n"); return 1; } /* Loop over all keys of the root object */ for (i = 1; i < r; i++) { if (jsoneq(JSON_STRING, &t[i], "user") == 0) { /* We may use strndup() to fetch string value */ printf("- User: %.*s\n", t[i+1].end-t[i+1].start, JSON_STRING + t[i+1].start); i++; } else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) { /* We may additionally check if the value is either "true" or "false" */ printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start, JSON_STRING + t[i+1].start); i++; } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) { /* We may want to do strtol() here to get numeric value */ printf("- UID: %.*s\n", t[i+1].end-t[i+1].start, JSON_STRING + t[i+1].start); i++; } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) { int j; printf("- Groups:\n"); if (t[i+1].type != JSMN_ARRAY) { continue; /* We expect groups to be an array of strings */ } for (j = 0; j < t[i+1].size; j++) { jsmntok_t *g = &t[i+j+2]; printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start); } i += t[i+1].size + 1; } else { printf("Unexpected key: %.*s\n", t[i].end-t[i].start, JSON_STRING + t[i].start); } } return 0; } ntpsec-1.1.0+dfsg1/libjsmn/jsmn.h0000644000175000017500000000323513252364117016406 0ustar rlaagerrlaager/* SPDX-License-Identifier: MIT */ #ifndef GUARD_JSMN_H #define GUARD_JSMN_H #include #ifdef __cplusplus extern "C" { #endif /** * JSON type identifier. Basic types are: * o Object * o Array * o String * o Other primitive: number, boolean (true/false) or null */ typedef enum { JSMN_PRIMITIVE = 0, JSMN_OBJECT = 1, JSMN_ARRAY = 2, JSMN_STRING = 3 } jsmntype_t; typedef enum { /* Not enough tokens were provided */ JSMN_ERROR_NOMEM = -1, /* Invalid character inside JSON string */ JSMN_ERROR_INVAL = -2, /* The string is not a full JSON packet, more bytes expected */ JSMN_ERROR_PART = -3 } jsmnerr_t; /** * JSON token description. * @param type type (object, array, string etc.) * @param start start position in JSON data string * @param end end position in JSON data string */ typedef struct { jsmntype_t type; int start; int end; int size; #ifdef JSMN_PARENT_LINKS int parent; #endif } jsmntok_t; /** * JSON parser. Contains an array of token blocks available. Also stores * the string being parsed now and current position in that string */ typedef struct { unsigned int pos; /* offset in the JSON string */ unsigned int toknext; /* next token to allocate */ int toksuper; /* superior token node, e.g parent object or array */ } jsmn_parser; /** * Create JSON parser over an array of tokens */ void jsmn_init(jsmn_parser *parser); /** * Run JSON parser. It parses a JSON data string into and array of tokens, each describing * a single JSON object. */ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens); #ifdef __cplusplus } #endif #endif /* GUARD_JSMN_H */ ntpsec-1.1.0+dfsg1/libjsmn/README.md0000644000175000017500000001263713252364117016553 0ustar rlaagerrlaager JSMN ==== jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be easily integrated into resource-limited or embedded projects. You can find more information about JSON format at [json.org][1] Library sources are available at [bitbucket.org/zserge/jsmn][2] The web page with some information about jsmn can be found at [http://zserge.com/jsmn.html][3] Philosophy ---------- Most JSON parsers offer you a bunch of functions to load JSON data, parse it and extract any value by its name. jsmn proves that checking the correctness of every JSON packet or allocating temporary objects to store parsed JSON fields often is an overkill. JSON format itself is extremely simple, so why should we complicate it? jsmn is designed to be **robust** (it should work fine even with erroneous data), **fast** (it should parse data on the fly), **portable** (no superfluous dependencies or non-standard C extensions). An of course, **simplicity** is a key feature - simple code style, simple algorithm, simple integration into other projects. Features -------- * compatible with C89 * no dependencies (even libc!) * highly portable (tested on x86/amd64, ARM, AVR) * about 200 lines of code * extremely small code footprint * API contains only 2 functions * no dynamic memory allocation * incremental single-pass parsing * library code is covered with unit-tests Design ------ The rudimentary jsmn object is a **token**. Let's consider a JSON string: '{ "name" : "Jack", "age" : 27 }' It holds the following tokens: * Object: `{ "name" : "Jack", "age" : 27}` (the whole object) * Strings: `"name"`, `"Jack"`, `"age"` (keys and some values) * Number: `27` In jsmn, tokens do not hold any data, but point to token boundaries in JSON string instead. In the example above jsmn will create tokens like: Object [0..31], String [3..7], String [12..16], String [20..23], Number [27..29]. Every jsmn token has a type, which indicates the type of corresponding JSON token. jsmn supports the following token types: * Object - a container of key-value pairs, e.g.: `{ "foo":"bar", "x":0.3 }` * Array - a sequence of values, e.g.: `[ 1, 2, 3 ]` * String - a quoted sequence of chars, e.g.: `"foo"` * Primitive - a number, a boolean (`true`, `false`) or `null` Besides start/end positions, jsmn tokens for complex types (like arrays or objects) also contain a number of child items, so you can easily follow object hierarchy. This approach provides enough information for parsing any JSON data and makes it possible to use zero-copy techniques. Install ------- To clone the repository you should have mercurial installed. Just run: $ hg clone http://bitbucket.org/zserge/jsmn jsmn Repository layout is simple: jsmn.c and jsmn.h are library files, tests are in the jsmn\_test.c, you will also find README, LICENSE and Makefile files inside. To build the library, run `make`. It is also recommended to run `make test`. Let me know, if some tests fail. If build was successful, you should get a `libjsmn.a` library. The header file you should include is called `"jsmn.h"`. API --- Token types are described by `jsmntype_t`: typedef enum { JSMN_PRIMITIVE = 0, JSMN_OBJECT = 1, JSMN_ARRAY = 2, JSMN_STRING = 3 } jsmntype_t; **Note:** Unlike JSON data types, primitive tokens are not divided into numbers, booleans and null, because one can easily tell the type using the first character: * 't', 'f' - boolean * 'n' - null * '-', '0'..'9' - number Token is an object of `jsmntok_t` type: typedef struct { jsmntype_t type; // Token type int start; // Token start position int end; // Token end position int size; // Number of child (nested) tokens } jsmntok_t; **Note:** string tokens point to the first character after the opening quote and the previous symbol before final quote. This was made to simplify string extraction from JSON data. All job is done by `jsmn_parser` object. You can initialize a new parser using: jsmn_parser parser; jsmntok_t tokens[10]; jsmn_init(&parser); // js - pointer to JSON string // tokens - an array of tokens available // 10 - number of tokens available jsmn_parse(&parser, js, tokens, 10); This will create a parser, and then it tries to parse up to 10 JSON tokens from the `js` string. A non-negative reutrn value of `jsmn_parse` is the number of tokens actually used by the parser. Passing NULL instead of the tokens array would not store parsing results, but instead the function will return the value of tokens needed to parse the given string. This can be useful if you don't know yet how many tokens to allocate. If something goes wrong, you will get an error. Error will be one of these: * `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted * `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large * `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data If you get `JSON_ERROR_NOMEM`, you can re-allocate more tokens and call `jsmn_parse` once more. If you read json data from the stream, you can periodically call `jsmn_parse` and check if return value is `JSON_ERROR_PART`. You will get this error until you reach the end of JSON data. Other info ---------- This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php), so feel free to integrate it in your commercial products. [1]: http://www.json.org/ [2]: https://bitbucket.org/zserge/jsmn/wiki/Home [3]: http://zserge.com/jsmn.html ntpsec-1.1.0+dfsg1/libjsmn/jsmn_test.c0000644000175000017500000004000713252364117017436 0ustar rlaagerrlaager/* SPDX-License-Identifier: MIT */ #include #include #include static int test_passed = 0; static int test_failed = 0; /* Terminate current test with error */ #define fail() return __LINE__ /* Successful end of the test case */ #define done() return 0 /* Check single condition */ #define check(cond) do { if (!(cond)) fail(); } while (0) /* Test runner */ static void test(int (*func)(void), const char *name) { int r = func(); if (r == 0) { test_passed++; } else { test_failed++; printf("FAILED: %s (at line %d)\n", name, r); } } #define TOKEN_EQ(t, tok_start, tok_end, tok_type) \ ((t).start == tok_start \ && (t).end == tok_end \ && (t).type == (tok_type)) #define TOKEN_STRING(js, t, s) \ (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \ && strlen(s) == (t).end - (t).start) #define TOKEN_PRINT(t) \ printf("start: %d, end: %d, type: %d, size: %d\n", \ (t).start, (t).end, (t).type, (t).size) #define JSMN_STRICT #include "jsmn.c" int test_empty() { const char *js; int r; jsmn_parser p; jsmntok_t t[10]; js = "{}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), t, 10); check(r >= 0); check(t[0].type == JSMN_OBJECT); check(t[0].start == 0 && t[0].end == 2); js = "[]"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), t, 10); check(r >= 0); check(t[0].type == JSMN_ARRAY); check(t[0].start == 0 && t[0].end == 2); js = "{\"a\":[]}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), t, 10); check(r >= 0); check(t[0].type == JSMN_OBJECT && t[0].start == 0 && t[0].end == 8); check(t[1].type == JSMN_STRING && t[1].start == 2 && t[1].end == 3); check(t[2].type == JSMN_ARRAY && t[2].start == 5 && t[2].end == 7); js = "[{},{}]"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), t, 10); check(r >= 0); check(t[0].type == JSMN_ARRAY && t[0].start == 0 && t[0].end == 7); check(t[1].type == JSMN_OBJECT && t[1].start == 1 && t[1].end == 3); check(t[2].type == JSMN_OBJECT && t[2].start == 4 && t[2].end == 6); return 0; } int test_simple() { const char *js; int r; jsmn_parser p; jsmntok_t tokens[10]; js = "{\"a\": 0}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); check(TOKEN_EQ(tokens[0], 0, 8, JSMN_OBJECT)); check(TOKEN_EQ(tokens[1], 2, 3, JSMN_STRING)); check(TOKEN_EQ(tokens[2], 6, 7, JSMN_PRIMITIVE)); check(TOKEN_STRING(js, tokens[0], js)); check(TOKEN_STRING(js, tokens[1], "a")); check(TOKEN_STRING(js, tokens[2], "0")); jsmn_init(&p); js = "[\"a\":{},\"b\":{}]"; r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); jsmn_init(&p); js = "{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }"; r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); return 0; } int test_primitive() { #ifndef JSMN_STRICT int r; jsmn_parser p; jsmntok_t tok[10]; const char *js; js = "\"boolVar\" : true"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_PRIMITIVE); check(TOKEN_STRING(js, tok[0], "boolVar")); check(TOKEN_STRING(js, tok[1], "true")); js = "\"boolVar\" : false"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_PRIMITIVE); check(TOKEN_STRING(js, tok[0], "boolVar")); check(TOKEN_STRING(js, tok[1], "false")); js = "\"intVar\" : 12345"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_PRIMITIVE); check(TOKEN_STRING(js, tok[0], "intVar")); check(TOKEN_STRING(js, tok[1], "12345")); js = "\"floatVar\" : 12.345"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_PRIMITIVE); check(TOKEN_STRING(js, tok[0], "floatVar")); check(TOKEN_STRING(js, tok[1], "12.345")); js = "\"nullVar\" : null"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_PRIMITIVE); check(TOKEN_STRING(js, tok[0], "nullVar")); check(TOKEN_STRING(js, tok[1], "null")); #endif return 0; } int test_string() { int r; jsmn_parser p; jsmntok_t tok[10]; const char *js; js = "\"strVar\" : \"hello world\""; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_STRING); check(TOKEN_STRING(js, tok[0], "strVar")); check(TOKEN_STRING(js, tok[1], "hello world")); js = "\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\""; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_STRING); check(TOKEN_STRING(js, tok[0], "strVar")); check(TOKEN_STRING(js, tok[1], "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\")); js = "\"strVar\" : \"\""; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_STRING); check(TOKEN_STRING(js, tok[0], "strVar")); check(TOKEN_STRING(js, tok[1], "")); return 0; } int test_partial_string() { int r; jsmn_parser p; jsmntok_t tok[10]; const char *js; jsmn_init(&p); js = "\"x\": \"va"; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING); check(TOKEN_STRING(js, tok[0], "x")); check(p.toknext == 1); jsmn_init(&p); char js_slash[9] = "\"x\": \"va\\"; r = jsmn_parse(&p, js_slash, sizeof(js_slash), tok, 10); check(r == JSMN_ERROR_PART); jsmn_init(&p); char js_unicode[10] = "\"x\": \"va\\u"; r = jsmn_parse(&p, js_unicode, sizeof(js_unicode), tok, 10); check(r == JSMN_ERROR_PART); js = "\"x\": \"valu"; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING); check(TOKEN_STRING(js, tok[0], "x")); check(p.toknext == 1); js = "\"x\": \"value\""; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_STRING); check(TOKEN_STRING(js, tok[0], "x")); check(TOKEN_STRING(js, tok[1], "value")); js = "\"x\": \"value\", \"y\": \"value y\""; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_STRING && tok[1].type == JSMN_STRING && tok[2].type == JSMN_STRING && tok[3].type == JSMN_STRING); check(TOKEN_STRING(js, tok[0], "x")); check(TOKEN_STRING(js, tok[1], "value")); check(TOKEN_STRING(js, tok[2], "y")); check(TOKEN_STRING(js, tok[3], "value y")); return 0; } int test_unquoted_keys() { #ifndef JSMN_STRICT int r; jsmn_parser p; jsmntok_t tok[10]; const char *js; jsmn_init(&p); js = "key1: \"value\"\nkey2 : 123"; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_PRIMITIVE && tok[1].type == JSMN_STRING && tok[2].type == JSMN_PRIMITIVE && tok[3].type == JSMN_PRIMITIVE); check(TOKEN_STRING(js, tok[0], "key1")); check(TOKEN_STRING(js, tok[1], "value")); check(TOKEN_STRING(js, tok[2], "key2")); check(TOKEN_STRING(js, tok[3], "123")); #endif return 0; } int test_partial_array() { int r; jsmn_parser p; jsmntok_t tok[10]; const char *js; jsmn_init(&p); js = " [ 1, true, "; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE); js = " [ 1, true, [123, \"hello"; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE); js = " [ 1, true, [123, \"hello\"]"; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE && tok[5].type == JSMN_STRING); /* check child nodes of the 2nd array */ check(tok[3].size == 2); js = " [ 1, true, [123, \"hello\"]]"; r = jsmn_parse(&p, js, strlen(js), tok, 10); check(r >= 0 && tok[0].type == JSMN_ARRAY && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE && tok[5].type == JSMN_STRING); check(tok[3].size == 2); check(tok[0].size == 3); return 0; } int test_array_nomem() { int i; int r; jsmn_parser p; jsmntok_t toksmall[10], toklarge[10]; const char *js; js = " [ 1, true, [123, \"hello\"]]"; for (i = 0; i < 6; i++) { jsmn_init(&p); memset(toksmall, 0, sizeof(toksmall)); memset(toklarge, 0, sizeof(toklarge)); r = jsmn_parse(&p, js, strlen(js), toksmall, i); check(r == JSMN_ERROR_NOMEM); memcpy(toklarge, toksmall, sizeof(toksmall)); r = jsmn_parse(&p, js, strlen(js), toklarge, 10); check(r >= 0); check(toklarge[0].type == JSMN_ARRAY && toklarge[0].size == 3); check(toklarge[3].type == JSMN_ARRAY && toklarge[3].size == 2); } return 0; } int test_objects_arrays() { int r; jsmn_parser p; jsmntok_t tokens[10]; const char *js; js = "[10}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "[10]"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); js = "{\"a\": 1]"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\": 1}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); return 0; } int test_issue_22() { int r; jsmn_parser p; jsmntok_t tokens[128]; const char *js; js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, " "\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", " "\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], " "\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, " "\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", " "\"imageheight\":64, \"imagewidth\":160, \"margin\":0, \"name\":\"Tiles\", " "\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], " "\"tilewidth\":32, \"version\":1, \"width\":10 }"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 128); check(r >= 0); #if 0 for (i = 1; tokens[i].end < tokens[0].end; i++) { if (tokens[i].type == JSMN_STRING || tokens[i].type == JSMN_PRIMITIVE) { printf("%.*s\n", tokens[i].end - tokens[i].start, js + tokens[i].start); } else if (tokens[i].type == JSMN_ARRAY) { printf("[%d elems]\n", tokens[i].size); } else if (tokens[i].type == JSMN_OBJECT) { printf("{%d elems}\n", tokens[i].size); } else { TOKEN_PRINT(tokens[i]); } } #endif return 0; } int test_unicode_characters() { jsmn_parser p; jsmntok_t tokens[10]; const char *js; int r; js = "{\"a\":\"\\uAbcD\"}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); js = "{\"a\":\"str\\u0000\"}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); js = "{\"a\":\"\\uFFFFstr\"}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); js = "{\"a\":\"str\\uFFGFstr\"}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\":\"str\\u@FfF\"}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\":[\"\\u028\"]}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\":[\"\\u0280\"]}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r >= 0); return 0; } int test_input_length() { const char *js; int r; jsmn_parser p; jsmntok_t tokens[10]; js = "{\"a\": 0}garbage"; jsmn_init(&p); r = jsmn_parse(&p, js, 8, tokens, 10); check(r == 3); check(TOKEN_STRING(js, tokens[0], "{\"a\": 0}")); check(TOKEN_STRING(js, tokens[1], "a")); check(TOKEN_STRING(js, tokens[2], "0")); return 0; } int test_count() { jsmn_parser p; const char *js; js = "{}"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); js = "[]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); js = "[[]]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2); js = "[[], []]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); js = "[[], []]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); js = "[[], [[]], [[], []]]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); js = "[\"a\", [[], []]]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); js = "[[], \"[], [[]]\", [[]]]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); js = "[1, 2, 3]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4); js = "[1, 2, [3, \"a\"], null]"; jsmn_init(&p); check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); return 0; } int test_keyvalue() { const char *js; int r; jsmn_parser p; jsmntok_t tokens[10]; js = "{\"a\": 0, \"b\": \"c\"}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == 5); check(tokens[0].size == 2); /* two keys */ check(tokens[1].size == 1 && tokens[3].size == 1); /* one value per key */ check(tokens[2].size == 0 && tokens[4].size == 0); /* values have zero size */ js = "{\"a\"\n0}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\", 0}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\": {2}}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\": {2: 3}}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); js = "{\"a\": {\"a\": 2 3}}"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == JSMN_ERROR_INVAL); return 0; } /** A huge redefinition of everything to include jsmn in non-script mode */ #define jsmn_init jsmn_init_nonstrict #define jsmn_parse jsmn_parse_nonstrict #define jsmn_parser jsmn_parser_nonstrict #define jsmn_alloc_token jsmn_alloc_token_nonstrict #define jsmn_fill_token jsmn_fill_token_nonstrict #define jsmn_parse_primitive jsmn_parse_primitive_nonstrict #define jsmn_parse_string jsmn_parse_string_nonstrict #define jsmntype_t jsmntype_nonstrict_t #define jsmnerr_t jsmnerr_nonstrict_t #define jsmntok_t jsmntok_nonstrict_t #define JSMN_PRIMITIVE JSMN_PRIMITIVE_NONSTRICT #define JSMN_OBJECT JSMN_OBJECT_NONSTRICT #define JSMN_ARRAY JSMN_ARRAY_NONSTRICT #define JSMN_STRING JSMN_STRING_NONSTRICT #define JSMN_ERROR_NOMEM JSMN_ERROR_NOMEM_NONSTRICT #define JSMN_ERROR_INVAL JSMN_ERROR_INVAL_NONSTRICT #define JSMN_ERROR_PART JSMN_ERROR_PART_NONSTRICT #undef __JSMN_H_ #undef JSMN_STRICT #include "jsmn.c" int test_nonstrict() { const char *js; int r; jsmn_parser p; jsmntok_t tokens[10]; js = "a: 0garbage"; jsmn_init(&p); r = jsmn_parse(&p, js, 4, tokens, 10); check(r == 2); check(TOKEN_STRING(js, tokens[0], "a")); check(TOKEN_STRING(js, tokens[1], "0")); js = "Day : 26\nMonth : Sep\n\nYear: 12"; jsmn_init(&p); r = jsmn_parse(&p, js, strlen(js), tokens, 10); check(r == 6); return 0; } int main() { test(test_empty, "general test for a empty JSON objects/arrays"); test(test_simple, "general test for a simple JSON string"); test(test_primitive, "test primitive JSON data types"); test(test_string, "test string JSON data types"); test(test_partial_string, "test partial JSON string parsing"); test(test_partial_array, "test partial array reading"); test(test_array_nomem, "test array reading with a smaller number of tokens"); test(test_unquoted_keys, "test unquoted keys (like in JavaScript)"); test(test_objects_arrays, "test objects and arrays"); test(test_unicode_characters, "test unicode characters"); test(test_input_length, "test strings that are not null-terminated"); test(test_issue_22, "test issue #22"); test(test_count, "test tokens count estimation"); test(test_nonstrict, "test for non-strict mode"); test(test_keyvalue, "test for keys/values"); printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed); return 0; } ntpsec-1.1.0+dfsg1/libjsmn/jsmn.c0000644000175000017500000001757013252364117016410 0ustar rlaagerrlaager/* SPDX-License-Identifier: MIT */ #include #include "jsmn.h" /** * Allocates a fresh unused token from the token pull. */ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *tok; if (parser->toknext >= num_tokens) { return NULL; } tok = &tokens[parser->toknext++]; tok->start = tok->end = -1; tok->size = 0; #ifdef JSMN_PARENT_LINKS tok->parent = -1; #endif return tok; } /** * Fills token type and boundaries. */ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end) { token->type = type; token->start = start; token->end = end; token->size = 0; } /** * Fills next available token with JSON primitive. */ static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; int start; start = (int)parser->pos; for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { switch (js[parser->pos]) { #ifndef JSMN_STRICT /* In strict mode primitive must be followed by "," or "}" or "]" */ case ':': #endif case '\t' : case '\r' : case '\n' : case ' ' : case ',' : case ']' : case '}' : goto found; default: /* huh? */ break; } if (js[parser->pos] < 32 || js[parser->pos] >= 127) { parser->pos = (unsigned int)start; return JSMN_ERROR_INVAL; } } #ifdef JSMN_STRICT /* In strict mode primitive must be followed by a comma/object/array */ parser->pos = start; return JSMN_ERROR_PART; #endif found: if (tokens == NULL) { parser->pos--; return 0; } token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->pos = (unsigned int)start; return JSMN_ERROR_NOMEM; } jsmn_fill_token(token, JSMN_PRIMITIVE, start, (int)parser->pos); #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif parser->pos--; return 0; } /** * Filsl next token with JSON string. */ static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) { jsmntok_t *token; int start = (int)parser->pos; parser->pos++; /* Skip starting quote */ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { char c = js[parser->pos]; /* Quote: end of string */ if (c == '\"') { if (tokens == NULL) { return 0; } token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) { parser->pos = (unsigned int)start; return JSMN_ERROR_NOMEM; } jsmn_fill_token(token, JSMN_STRING, start+1, (int)parser->pos); #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif return 0; } /* Backslash: Quoted symbol expected */ if (c == '\\' && parser->pos + 1 < len) { int i; parser->pos++; switch (js[parser->pos]) { /* Allowed escaped symbols */ case '\"': case '/' : case '\\' : case 'b' : case 'f' : case 'r' : case 'n' : case 't' : break; /* Allows escaped symbol \uXXXX */ case 'u': parser->pos++; for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { /* If it isn't a hex character we have an error */ if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ parser->pos = (unsigned int)start; return JSMN_ERROR_INVAL; } parser->pos++; } parser->pos--; break; /* Unexpected symbol */ default: parser->pos = (unsigned int)start; return JSMN_ERROR_INVAL; } } } parser->pos = (unsigned int)start; return JSMN_ERROR_PART; } /** * Parse JSON string and fill tokens. */ jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens) { jsmnerr_t r; int i; jsmntok_t *token; int count = 0; for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { char c; jsmntype_t type; c = js[parser->pos]; switch (c) { case '{': case '[': count++; if (tokens == NULL) { break; } token = jsmn_alloc_token(parser, tokens, num_tokens); if (token == NULL) return JSMN_ERROR_NOMEM; if (parser->toksuper != -1) { tokens[parser->toksuper].size++; #ifdef JSMN_PARENT_LINKS token->parent = parser->toksuper; #endif } token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); token->start = (int)parser->pos; parser->toksuper = (int)parser->toknext - 1; break; case '}': case ']': if (tokens == NULL) break; type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); #ifdef JSMN_PARENT_LINKS if (parser->toknext < 1) { return JSMN_ERROR_INVAL; } token = &tokens[parser->toknext - 1]; for (;;) { if (token->start != -1 && token->end == -1) { if (token->type != type) { return JSMN_ERROR_INVAL; } token->end = (int)parser->pos + 1; parser->toksuper = token->parent; break; } if (token->parent == -1) { break; } token = &tokens[token->parent]; } #else for (i = parser->toknext - 1; i >= 0; i--) { token = &tokens[i]; if (token->start != -1 && token->end == -1) { if (token->type != type) { return JSMN_ERROR_INVAL; } parser->toksuper = -1; token->end = parser->pos + 1; break; } } /* Error if unmatched closing bracket */ if (i == -1) return JSMN_ERROR_INVAL; for (; i >= 0; i--) { token = &tokens[i]; if (token->start != -1 && token->end == -1) { parser->toksuper = i; break; } } #endif break; case '\"': r = jsmn_parse_string(parser, js, len, tokens, num_tokens); if (r < 0) return r; count++; if (parser->toksuper != -1 && tokens != NULL) tokens[parser->toksuper].size++; break; case '\t' : case '\r' : case '\n' : case ' ': break; case ':': parser->toksuper = (int)parser->toknext - 1; break; case ',': if (tokens != NULL && tokens[parser->toksuper].type != JSMN_ARRAY && tokens[parser->toksuper].type != JSMN_OBJECT) { #ifdef JSMN_PARENT_LINKS parser->toksuper = tokens[parser->toksuper].parent; #else for (i = parser->toknext - 1; i >= 0; i--) { if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { if (tokens[i].start != -1 && tokens[i].end == -1) { parser->toksuper = i; break; } } } #endif } break; #ifdef JSMN_STRICT /* In strict mode primitives are: numbers and booleans */ case '-': case '0': case '1' : case '2': case '3' : case '4': case '5': case '6': case '7' : case '8': case '9': case 't': case 'f': case 'n' : /* And they must not be keys of the object */ if (tokens != NULL) { jsmntok_t *t = &tokens[parser->toksuper]; if (t->type == JSMN_OBJECT || (t->type == JSMN_STRING && t->size != 0)) { return JSMN_ERROR_INVAL; } } #else /* In non-strict mode every unquoted value is a primitive */ default: #endif r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); if (r < 0) return r; count++; if (parser->toksuper != -1 && tokens != NULL) tokens[parser->toksuper].size++; break; #ifdef JSMN_STRICT /* Unexpected char in strict mode */ default: return JSMN_ERROR_INVAL; #endif } } if (tokens != NULL) { for (i = (int)parser->toknext - 1; i >= 0; i--) { /* Unmatched opened object or array */ if (tokens[i].start != -1 && tokens[i].end == -1) { return JSMN_ERROR_PART; } } } return count; } /** * Creates a new parser based over a given buffer with an array of tokens * available. */ void jsmn_init(jsmn_parser *parser) { parser->pos = 0; parser->toknext = 0; parser->toksuper = -1; } ntpsec-1.1.0+dfsg1/wafhelpers/0000775000175000017500000000000013252650651015770 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/wafhelpers/bin_test.py0000644000175000017500000000355613252364117020157 0ustar rlaagerrlaagerfrom __future__ import print_function import re from os.path import exists from waflib.Utils import subprocess from waflib.Logs import pprint cmd_map = { ("main/ntpd/ntpd", "-invalid"): br'.*must be run as root, not uid.*', ("main/ntpclients/ntpdig", "time.apple.com"): br'.*time.apple.com.*', ("main/ntpfrob/ntpfrob", "-h"): br'.*illegal option.*', ("main/ntpfrob/ntpfrob", "-b 100000"): br".*Bumping clock by 100000 microseconds.*", ("main/ntpclients/ntpkeygen", "-M"): br'.*Generating new md5 file and link.*', ("main/ntpclients/ntpq", "-p"): br'.*remote.*jitter.*', ("main/ntptime/ntptime", None): br'.*ntp_gettime\(\) returns code 0 \(OK\).*', ("main/attic/sht", "2:r"): br'.*reader.*', # Perl library # ("main/ntpclients/ntptrace", ""): br'', # ("main/ntpclients/ntpwait", ""): br'', # ("main/ntpclients/ntpsweep", ""): br'', } # XXX: Needs to run in a thread with a timeout. def run(cmd, reg): check = False if cmd[1] is None: cmd = [cmd[0]] print("running: ", " ".join(cmd), end="") if not exists("build/%s" % cmd[0]): pprint("YELLOW", " SKIPPING (does not exist)") return False p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=None, cwd="build") stdout, stderr = p.communicate() regex = re.compile(reg) if regex.match(stdout) or regex.match(stderr): check = True if check: pprint("GREEN", " OK") return False else: pprint("RED", " FAILED") return True def cmd_bin_test(ctx, config): fail = True for cmd in sorted(cmd_map): fail = run(cmd, cmd_map[cmd]) if fail: pprint("RED", "Tests failed!") # ctx.fatal("Failed") # cmd_bin_test(None, None) ntpsec-1.1.0+dfsg1/wafhelpers/options.py0000644000175000017500000001172513252364117020040 0ustar rlaagerrlaagerdef options_cmd(ctx, config): ctx.load("compiler_c") ctx.load("msvc") ctx.load('waf_unit_test') ctx.load('gnu_dirs') def callback_flags(option, opt, value, parser): config["OPT_STORE"].setdefault(opt, []).append(value) grp = ctx.add_option_group("NTP configure options") grp.add_option('--enable-debug', action='store_true', default=False, help="Enable debugging code") grp.add_option('--enable-debug-gdb', action='store_true', default=False, help="Enable GDB debugging symbols") grp.add_option('--disable-droproot', action='store_true', default=False, help="Disable dropping root.") grp.add_option('--enable-early-droproot', action='store_true', default=False, help="Droproot earlier (breaks SHM and NetBSD).") grp.add_option('--enable-seccomp', action='store_true', default=False, help="Enable seccomp (restricts syscalls).") grp.add_option('--disable-dns-lookup', action='store_true', default=False, help="Disable DNS lookups.") grp.add_option('--disable-mdns-registration', action='store_true', default=False, help="Disable MDNS registration.") grp.add_option( '--enable-classic-mode', action='store_true', default=False, help="Strict configuration and log-format " " compatibility with NTP Classic") grp.add_option('--enable-debug-timing', action='store_true', default=False, help="Collect timing statistics for debugging.") grp = ctx.add_option_group("NTP cross compile options") grp.add_option('--cross-compiler', type='string', help="Path to cross compiler CC. (enables cross-compiling)") grp.add_option('--cross-cflags', type='string', action="callback", callback=callback_flags, help="Cross compiler CFLAGS.") grp.add_option('--cross-ldflags', type='string', action="callback", callback=callback_flags, help="Cross compiler LDFLAGS.") grp = ctx.add_option_group("NTP configure features") grp.add_option('--enable-leap-smear', action='store_true', default=False, help="Enable Leap Smearing.") grp.add_option('--enable-leap-testing', action='store_true', default=False, help="Enable leaps on other than 1st of month.") grp.add_option('--enable-mssntp', action='store_true', default=False, help="Enable Samba MS SNTP support.") grp.add_option('--enable-lockclock', action='store_true', default=False, help="Enable NIST lockclock scheme.") grp = ctx.add_option_group("Refclock configure options") grp.add_option( '--refclock', dest='refclocks', help="Comma-separated list of Refclock IDs to build (or \"all\")", type='string') grp.add_option('--list', action='store_true', default=False, help="List available Refclocks") grp = ctx.add_option_group("NTP developer configure options") grp.add_option('--build-version-tag', type='string', help="Append a tag to the version string.") grp.add_option('--cflags', type='string', action="callback", callback=callback_flags, help="Users should use CFLAGS in their environment.") grp.add_option('--build-epoch', type='int', default=None, help="Force epoch, or use SOURCE_DATE_EPOCH in environment") grp.add_option('--ldflags', type='string', action="callback", callback=callback_flags, help="Users should use LDFLAGS in their environment.") grp.add_option('--check', action='store_true', default=False, help="Run tests") grp.add_option('--enable-rtems-trace', action='store_true', default=False, help="Enable RTEMS Trace.") grp.add_option('--enable-warnings', action='store_true', default=False, help="Enable annoying CC warnings") grp.add_option('--rtems-trace-path', type='string', default="", help="Path to rtems-tld.") grp.add_option( '--define', type='string', action="callback", callback=callback_flags, help="Force definition of symbol, wants value of form SYM=VAL.") grp.add_option('--undefine', type='string', action="callback", callback=callback_flags, help="Force undefinition of symbol.") grp = ctx.add_option_group("NTP documentation configure options") grp.add_option('--enable-doc', action='store_true', default=False, help="Build NTP documentation") grp.add_option('--enable-a2x-xmllint', action='store_true', default=False, help="Build NTP documentation with a2x XML lint") grp.add_option('--disable-manpage', action='store_true', default=False, help="Disable Manpage building.") ntpsec-1.1.0+dfsg1/wafhelpers/check_mdns.py0000644000175000017500000000234513252364117020441 0ustar rlaagerrlaagerMDNS_FRAG = """ # include int main(void) { DNSServiceRef mdns; DNSServiceRefDeallocate(mdns); return 0; } """ def check_mdns_header(ctx): ctx.check_cc(lib="dns_sd", libpath=ctx.env.PLATFORM_LIBPATH, mandatory=False) ctx.check_cc(header_name="dns_sd.h", includes=ctx.env.PLATFORM_INCLUDES, uselib_store="DNS_SD_INCLUDES", mandatory=False) if ctx.get_define("HAVE_DNS_SD_H") and ctx.env.LIB_LIBDNS_SD: ctx.env.DNS_SD_HEADER = True def check_mdns_run(ctx): if ctx.env.ENABLE_CROSS: # XXX Remove when variant builds exist if ctx.env.DNS_SD_HEADER: ctx.define("HAVE_MDNS", 1, comment="multicast dns support") return ctx.check_cc( fragment=MDNS_FRAG, define_name="HAVE_MDNS", features="c", includes=ctx.env.PLATFORM_INCLUDES, libpath=ctx.env.PLATFORM_LIBPATH, export_includes=ctx.env.PLATFORM_INCLUDES, msg="Checking if mDNSResponder works", name="MDNS_INCLUDES", mandatory=False, comment="Multicast DNS support" ) if ctx.get_define("HAVE_MDNS"): ctx.define("ENABLE_MDNS_REGISTRATION", 1, comment="Multicast DNS support") ntpsec-1.1.0+dfsg1/wafhelpers/asciidoc.py0000644000175000017500000000400413252364117020113 0ustar rlaagerrlaagerimport re from waflib import Task from waflib.TaskGen import extension # asciidoc -b html5 -a linkcss -a stylesdir=/mnt/devel/ntp/commit/docs \ # -o asd driver32.txt # Borrowed from waf/docs/book/wscript in the Waf Project. re_xi = re.compile('''^(include|image)::(.*?.(txt|\\{PIC\\}))\[''', re.M) def ascii_doc_scan(self): p = self.inputs[0].parent node_lst = [self.inputs[0]] seen = [] depnodes = [] while node_lst: nd = node_lst.pop(0) if nd in seen: continue seen.append(nd) code = nd.read() for m in re_xi.finditer(code): name = m.group(2) if m.group(3) == '{PIC}': ext = '.eps' if self.generator.rule.rfind('A2X') > 0: ext = '.png' k = p.find_resource(name.replace('{PIC}', ext)) if k: depnodes.append(k) else: k = self.inputs[0].find_resource(name) if k: depnodes.append(k) node_lst.append(k) return [depnodes, ()] # ASCIIDOC_FLAGS are almost always needed and need to be set by the user. class asciidoc(Task.Task): color = "BLUE" run_str = '${BIN_ASCIIDOC} -b html5 -a linkcss ${ASCIIDOC_FLAGS} ' \ '-o ${TGT[0].name} ${SRC[0].abspath()}' ext_out = ".html" @extension('.txt') def run_asciidoc(self, node): out = node.change_ext(".html") tsk = self.create_task("asciidoc", node, [out]) tsk.cwd = node.parent.get_bld().abspath() class a2x(Task.Task): color = "YELLOW" shell = True run_str = '${BIN_A2X} ${A2X_FLAGS} ${SRC[0].abspath()}' scan = ascii_doc_scan @extension('.man-tmp') def run_a2x(self, node): n_file = node.path_from(self.bld.bldnode) out = "%s.%s" % (n_file.replace("-man.txt.man-tmp", ""), self.section) out_n = self.bld.path.find_or_declare(out) self.create_task('a2x', node, out_n) self.bld.install_files("${MANDIR}/man%s/" % self.section, out_n) ntpsec-1.1.0+dfsg1/wafhelpers/check_sockaddr.py0000644000175000017500000000062713252364117021273 0ustar rlaagerrlaagerSA_LEN_FRAG = """ #include #include int main(void) { extern struct sockaddr *ps; return ps->sa_len; } """ def check_sockaddr(ctx): ctx.check_cc( fragment=SA_LEN_FRAG, define_name="ISC_PLATFORM_HAVESALEN", features="c", msg="Checking for sockaddr->sa_len", mandatory=False, comment="Whether sockaddr.sa_len exists" ) ntpsec-1.1.0+dfsg1/wafhelpers/check_vsprintfm.py0000644000175000017500000000235313252364117021527 0ustar rlaagerrlaager# What we really want to do here is test to see if the following program # compiles *and exits with 9 status*. Because we don't know how to check # the return status we must settle for a simpler test. ''' #include #include #include #include int call_vsnprintf(char *dst, size_t sz, const char *fmt,...) { va_list ap; int rc; va_start(ap, fmt); rc = vsnprintf(dst, sz, fmt, ap); va_end(ap); return rc; } int main(void) { char sbuf[512]; char pbuf[512]; int slen; strcpy(sbuf, strerror(ENOENT)); errno = ENOENT; slen = call_vsnprintf(pbuf, sizeof(pbuf), "%m", "wrong"); return strcmp(sbuf, pbuf); } ''' def check_vsprintfm(ctx): "Check for %m expanding to strerror(error) in glibc style." ctx.check_cc( fragment=''' #include int main(void) { #ifndef __GLIBC__ # error __GLIBC__ is not defined #endif } ''', define_name="VSNPRINTF_PERCENT_M", msg="Checking for %m expansion in vsnprintf(3)", mandatory=False, comment="%m expanding to strerror(error) in glibc style") ntpsec-1.1.0+dfsg1/wafhelpers/refclock.py0000644000175000017500000001102013252364117020121 0ustar rlaagerrlaagerfrom waflib.Configure import conf from waflib.Logs import pprint # Note: When you change this list. also check the following files: # doc/refclock.txt # include/ntp_refclock.h # ntpd/refclock_conf.c refclock_map = { "local": { "descr": "Undisciplined Local Clock", "define": "CLOCK_LOCAL", "file": "local" }, "spectracom": { "descr": "Spectracom GPS Receivers", "define": "CLOCK_SPECTRACOM", "file": "spectracom" }, "truetime": { "descr": "TrueTime GPS/GOES/OMEGA Receivers", "define": "CLOCK_TRUETIME", "file": "truetime" }, "generic": { "descr": "Generic Reference Driver (Parse)", "define": "CLOCK_GENERIC", "require": ["parse"], "file": "generic" }, "arbiter": { "descr": "Arbiter 1088A/B GPS Receiver", "define": "CLOCK_ARBITER", "file": "arbiter" }, "modem": { "descr": "NIST/USNO/PTB Modem Time Services", "define": "CLOCK_MODEM", "file": "modem" }, "nmea": { "descr": "Generic NMEA GPS Receiver", "define": "CLOCK_NMEA", "file": "nmea" }, "pps": { "descr": "PPS Clock Discipline", "define": "CLOCK_PPS", "require": ["ppsapi"], "file": "pps" }, "hpgps": { "descr": "Hewlett Packard 58503A GPS Receiver", "define": "CLOCK_HPGPS", "file": "hpgps" }, "shm": { "descr": "Shared Memory Driver", "define": "CLOCK_SHM", "file": "shm" }, "trimble": { "descr": "Trimble Navigation GPSes", "define": "CLOCK_TRIMBLE", "file": "trimble" }, "oncore": { "descr": "Motorola UT Oncore GPS", "define": "CLOCK_ONCORE", "require": ["ppsapi"], "file": "oncore" }, "jjy": { "descr": "JJY Receivers", "define": "CLOCK_JJY", "file": "jjy" }, "zyfer": { "descr": "Zyfer GPStarplus Receiver", "define": "CLOCK_ZYFER", "file": "zyfer" }, "neoclock": { "descr": "NeoClock4X - DCF77 / TDF serial line", "define": "CLOCK_NEOCLOCK", "file": "neoclock" }, "gpsd": { "descr": "GPSD NG client protocol", "define": "CLOCK_GPSDJSON", "file": "gpsd" } } @conf def refclock_config(ctx): from wafhelpers.refclock import refclock_map if ctx.options.refclocks == "all": ids = refclock_map.keys() else: # XXX: better error checking ids = ctx.options.refclocks.split(",") ctx.env.REFCLOCK_SOURCE = [] # Remove duplicate IDs while preserving order. unique_id = [] [unique_id.append(x) for x in ids if x not in unique_id] refclock = False for id in unique_id: if id not in refclock_map: ctx.fatal("'%s' is not a valid Refclock ID" % id) rc = refclock_map[id] if rc['define'] == "CLOCK_GENERIC": parse_clocks = ( "CLOCK_COMPUTIME", "CLOCK_DCF7000", "CLOCK_HOPF6021", "CLOCK_MEINBERG", "CLOCK_RAWDCF", "CLOCK_RCC8000", "CLOCK_SCHMID", "CLOCK_SEL240X", "CLOCK_TRIMTAIP", "CLOCK_TRIMTSIP", "CLOCK_VARITEXT", "CLOCK_WHARTON_400A", ) for subtype in parse_clocks: ctx.define(subtype, 1, comment="Enable individual parse clock") ctx.start_msg("Enabling Refclock %s (%s):" % (rc["descr"], id)) if "require" in rc: if "ppsapi" in rc["require"]: if not ctx.get_define("HAVE_PPSAPI"): ctx.end_msg("No") pprint("RED", "Refclock \"%s\" disabled, PPS API has not " "been detected as working." % rc["descr"]) continue ctx.env.REFCLOCK_SOURCE.append((rc["file"], rc["define"])) ctx.env["REFCLOCK_%s" % rc["file"].upper()] = True ctx.define(rc["define"], 1, comment="Enable '%s' refclock" % rc["descr"]) ctx.env.REFCLOCK_LIST += [str(id)] ctx.end_msg("Yes") refclock = True if refclock: ctx.env.REFCLOCK_ENABLE = True ctx.define("REFCLOCK", 1, comment="Enable refclock support") ntpsec-1.1.0+dfsg1/wafhelpers/check_pthread.py0000644000175000017500000000422713252364117021130 0ustar rlaagerrlaagerfrom waflib.Logs import pprint PTHREAD_FRAG = """ #include int main(void) { pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); pthread_mutex_destroy(&mutex); return 0; } """ def check_pthread_header_lib(ctx): ctx.check(header_name="pthread.h", includes=ctx.env.PLATFORM_INCLUDES, mandatory=False, comment="pthread header") ctx.check(feature="c cshlib", lib="pthread", libpath=ctx.env.PLATFORM_LIBPATH, mandatory=False, comment="pthread library") ctx.check_cc(lib="thr", mandatory=False, comment="thr library, required by some operating systems.") if ((ctx.get_define("HAVE_PTHREAD_H") and (ctx.env.LIB_PTHREAD or ctx.env.LIB_THR))): ctx.env.PTHREAD_HEADER_LIB = True def check_pthread_run(ctx): if ctx.env.ENABLE_CROSS: if ctx.env.PTHREAD_HEADER_LIB: # XXX Remove when variant builds exist ctx.define("HAVE_PTHREAD", 1, comment="pthread support") ctx.env.PTHREAD_ENABLE = True return ctx.check( fragment=PTHREAD_FRAG, define_name="HAVE_PTHREAD", features="c", use="PTHREAD THR", msg="Checking if pthread works", includes=ctx.env.PLATFORM_INCLUDES, export_includes=ctx.env.PLATFORM_INCLUDES, mandatory=False, comment="pthread support" ) check_sanity(ctx, ctx.env.PTHREAD_HEADER_LIB, "pthread") # XXX if pthread is part of 'libc' this will pass # even if the header isn't detected. if not ctx.get_define("HAVE_PTHREAD"): ctx.fatal("Error: POSIX threads are required in order to build.") else: ctx.env.PTHREAD_ENABLE = True ctx.define("HAVE_PTHREAD", 1, comment="pthread support") def check_sanity(ctx, cond, lib): define = "HAVE_%s" % lib.upper() if cond and (not ctx.get_define(define)): pprint("RED", "Warning %s headers detected, binaries do not build/run" % lib) elif (not cond) and ctx.get_define(define): pprint("RED", "Warning %s headers not detected, binaries build/run" % lib) ntpsec-1.1.0+dfsg1/wafhelpers/__init__.py0000644000175000017500000000000013252364117020064 0ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/wafhelpers/test.py0000644000175000017500000000212113252364117017312 0ustar rlaagerrlaagerfrom __future__ import print_function from waflib.Logs import pprint def test_write_log(ctx): file_out = "%s/test.log" % ctx.bldnode.abspath() log = getattr(ctx, 'utest_results', []) if not log: return with open(file_out, "w") as fp: for binary, retval, lines, error in ctx.utest_results: fp.write("BINARY : %s\n" % binary) fp.write("RETURN VALUE: %s\n" % retval) fp.write("\n*** stdout ***\n") fp.write(str(lines)) fp.write("\n*** stderr ***\n") fp.write(str(error)) fp.write("\n\n\n") pprint("BLUE", "Wrote test log to: ", file_out) def test_print_log(ctx): for binary, retval, lines, error in ctx.utest_results: pprint("YELLOW", "BINARY :", binary) pprint("YELLOW", "RETURN VALUE:", retval) print("") if retval or error: pprint("RED", "****** ERROR ******\n") print(error or lines) if (not retval) and (not error): pprint("GREEN", "****** LOG ******\n", lines) print("") ntpsec-1.1.0+dfsg1/wafhelpers/autorevision.sh0000755000175000017500000010252313252364117021056 0ustar rlaagerrlaager#!/bin/sh # Copyright (c) 2012 - 2016 dak180 and contributors. See # https://opensource.org/licenses/mit-license.php or the included # COPYING.md for licence terms. # # autorevision - extracts metadata about the head version from your # repository. # shellcheck disable=SC2154 # Usage message. arUsage() { tee >&2 << EOF usage: autorevision {-t output-type | -s symbol} [-o cache-file [-f] ] [-e name] [-U] [-V] Options include: -t output-type = specify output type -s symbol = specify symbol output -o cache-file = specify cache file location -f = force the use of cache data -e name = set a different output name for VCS_EXTRA -U = check for untracked files in svn -V = emit version and exit -? = help message The following are valid output types: c = C/C++ file clojure = clojure file cmake = CMake script file csharp = CSharp properties file h = Header for use with c/c++ hpp = Alternate C++ header strings with namespace ini = INI file java = Java file javaprop = Java properties file js = javascript file json = JSON file lua = Lua file m4 = m4 file matlab = matlab file octave = octave file php = PHP file pl = Perl file py = Python file rpm = rpm file scheme = scheme file sh = Bash sytax swift = Swift file tex = (La)TeX file xcode = Header useful for populating info.plist files The following are valid symbols: VCS_TYPE VCS_BASENAME VCS_UUID VCS_NUM VCS_DATE VCS_BRANCH VCS_TAG VCS_TICK VCS_EXTRA VCS_FULL_HASH VCS_SHORT_HASH VCS_WC_MODIFIED VCS_ACTION_STAMP EOF exit 1 } # Config ARVERSION="1.19" while getopts ":t:o:s:e:VfU" OPTION; do case "${OPTION}" in t) AFILETYPE="${OPTARG}" ;; o) CACHEFILE="${OPTARG}" ;; f) CACHEFORCE="1" ;; s) VAROUT="${OPTARG}" ;; e) EXTRA_NAME="${OPTARG}" ;; U) UNTRACKEDFILES="1" ;; V) echo "autorevision ${ARVERSION}" exit 0 ;; ?) # If an unknown flag is used (or -?): arUsage ;; esac done if [ ! -z "${VAROUT}" ] && [ ! -z "${AFILETYPE}" ]; then # If both -s and -t are specified: echo "error: Improper argument combination." 1>&2 exit 1 elif [ -z "${VAROUT}" ] && [ -z "${AFILETYPE}" ]; then # If neither -s or -t are specified: arUsage elif [ -z "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then # If -f is specified without -o: arUsage elif [ ! -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then # If we are forced to use the cache but it does not exist. echo "error: Cache forced but no cache found." 1>&2 exit 1 fi # Only use the local keyword if it is there (ksh we are looking at # you). if [ "$(command -v local 2> /dev/null)" = "local" ]; then LOCAL="local" elif command -v typeset > /dev/null 2>&1; then LOCAL="typeset" else LOCAL="" fi # Make sure that the path we are given is one we can source # (dash, we are looking at you). if [ ! -z "${CACHEFILE}" ] && ! echo "${CACHEFILE}" | grep -q '^\.*/'; then CACHEFILE="./${CACHEFILE}" fi GENERATED_HEADER="Generated by autorevision - do not hand-hack!" : "${EXTRA_NAME:="VCS_EXTRA"}" # Functions to extract data from different repo types. # For git repos # shellcheck disable=SC2039,SC2164,SC2155 gitRepo() { ${LOCAL} oldPath="${PWD}" cd "$(git rev-parse --show-toplevel)" VCS_TYPE="git" VCS_BASENAME="$(basename "${PWD}")" ${LOCAL} currentRev="$(git rev-parse HEAD)" VCS_UUID="$(git rev-list --max-parents=0 --date-order --reverse "${currentRev}" 2>/dev/null | sed -n 1p)" if [ -z "${VCS_UUID}" ]; then VCS_UUID="$(git rev-list --topo-order "${currentRev}" | tail -n 1)" fi # Is the working copy clean? test -z "$(git status --untracked-files=normal --porcelain)" VCS_WC_MODIFIED="${?}" # Enumeration of changesets VCS_NUM="$(git rev-list --count "${currentRev}" 2>/dev/null)" if [ -z "${VCS_NUM}" ]; then echo "warning: Counting the number of revisions may be slower due to an outdated git version less than 1.7.2.3. If something breaks, please update it." 1>&2 VCS_NUM="$(git rev-list HEAD | wc -l)" fi # This may be a git-svn remote. If so, report the Subversion revision. if [ -z "$(git config svn-remote.svn.url 2>/dev/null)" ]; then # The full revision hash VCS_FULL_HASH="$(git rev-parse "${currentRev}")" # The short hash VCS_SHORT_HASH="$(git rev-parse --short "${currentRev}")" else # The git-svn revision number VCS_FULL_HASH="$(git svn find-rev "${currentRev}")" VCS_SHORT_HASH="${VCS_FULL_HASH}" fi # Current branch VCS_BRANCH="$(git rev-parse --symbolic-full-name --verify "$(git name-rev --name-only --no-undefined "${currentRev}" 2>/dev/null)" 2>/dev/null | sed -e 's:refs/heads/::' | sed -e 's:refs/::')" # Cache the description ${LOCAL} DESCRIPTION="$(git describe --long --tags "${currentRev}" 2>/dev/null)" # Current or last tag ancestor (empty if no tags) VCS_TAG="$(echo "${DESCRIPTION}" | sed -e "s:-g${VCS_SHORT_HASH}\$::" -e 's:-[0-9]*$::')" # Distance to last tag or an alias of VCS_NUM if there is no tag if [ ! -z "${DESCRIPTION}" ]; then VCS_TICK="$(echo "${DESCRIPTION}" | sed -e "s:${VCS_TAG}-::" -e "s:-g${VCS_SHORT_HASH}::")" else VCS_TICK="${VCS_NUM}" fi # Date of the current commit VCS_DATE="$(TZ=UTC git show -s --date=iso-strict-local --pretty=format:%cd "${currentRev}" 2>/dev/null | sed -e 's|+00:00|Z|')" if [ -z "${VCS_DATE}" ]; then echo "warning: Action stamps require git version 2.7+." 1>&2 VCS_DATE="$(git log -1 --pretty=format:%ci "${currentRev}" | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')" ${LOCAL} ASdis="1" fi # Action Stamp if [ -z "${ASdis}" ]; then VCS_ACTION_STAMP="${VCS_DATE}!$(git show -s --pretty=format:%cE "${currentRev}")" else VCS_ACTION_STAMP="" fi cd "${oldPath}" } # For hg repos # shellcheck disable=SC2039,SC2164 hgRepo() { ${LOCAL} oldPath="${PWD}" cd "$(hg root)" VCS_TYPE="hg" VCS_BASENAME="$(basename "${PWD}")" VCS_UUID="$(hg log -r "0" -l 1 --template '{node}\n')" # Is the working copy clean? test -z "$(hg status -duram)" VCS_WC_MODIFIED="${?}" # Enumeration of changesets VCS_NUM="$(hg id -n | tr -d '+')" # The full revision hash VCS_FULL_HASH="$(hg log -r "${VCS_NUM}" -l 1 --template '{node}\n')" # The short hash VCS_SHORT_HASH="$(hg id -i | tr -d '+')" # Current bookmark (bookmarks are roughly equivalent to git's branches) # or branch if no bookmark VCS_BRANCH="$(hg id -B | cut -d ' ' -f 1)" # Fall back to the branch if there are no bookmarks if [ -z "${VCS_BRANCH}" ]; then VCS_BRANCH="$(hg id -b)" fi # Current or last tag ancestor (excluding auto tags, empty if no tags) VCS_TAG="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttag}\n' 2>/dev/null | sed -e 's:qtip::' -e 's:tip::' -e 's:qbase::' -e 's:qparent::' -e "s:$(hg --config 'extensions.color=' --config 'extensions.mq=' --color never qtop 2>/dev/null)::" | cut -d ' ' -f 1)" # Distance to last tag or an alias of VCS_NUM if there is no tag if [ ! -z "${VCS_TAG}" ]; then VCS_TICK="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttagdistance}\n' 2>/dev/null)" else VCS_TICK="${VCS_NUM}" fi # Date of the current commit VCS_DATE="$(hg log -r "${VCS_NUM}" -l 1 --template '{date|isodatesec}\n' 2>/dev/null | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')" # Action Stamp VCS_ACTION_STAMP="$(TZ=UTC hg log -r "${VCS_NUM}" -l 1 --template '{date|localdate|rfc3339date}\n' 2>/dev/null | sed -e 's|+00:00|Z|')!$(hg log -r "${VCS_NUM}" -l 1 --template '{author|email}\n' 2>/dev/null)" cd "${oldPath}" } # For bzr repos # shellcheck disable=SC2039,SC2164 bzrRepo() { ${LOCAL} oldPath="${PWD}" cd "$(bzr root)" VCS_TYPE="bzr" VCS_BASENAME="$(basename "${PWD}")" # Currently unimplemented because more investigation is needed. VCS_UUID="" # Is the working copy clean? bzr version-info --custom --template='{clean}\n' | grep -q '1' VCS_WC_MODIFIED="${?}" # Enumeration of changesets VCS_NUM="$(bzr revno)" # The full revision hash VCS_FULL_HASH="$(bzr version-info --custom --template='{revision_id}\n')" # The short hash VCS_SHORT_HASH="${VCS_NUM}" # Nick of the current branch VCS_BRANCH="$(bzr nick)" # Current or last tag ancestor (excluding auto tags, empty if no tags) VCS_TAG="$(bzr tags --sort=time | sed '/?$/d' | tail -n1 | cut -d ' ' -f1)" # Distance to last tag or an alias of VCS_NUM if there is no tag if [ ! -z "${VCS_TAG}" ]; then VCS_TICK="$(bzr log --line -r "tag:${VCS_TAG}.." | tail -n +2 | wc -l | sed -e 's:^ *::')" else VCS_TICK="${VCS_NUM}" fi # Date of the current commit VCS_DATE="$(bzr version-info --custom --template='{date}\n' | sed -e 's: :T:' -e 's: ::')" # Action Stamp # Currently unimplemented because more investigation is needed. VCS_ACTION_STAMP="" cd "${oldPath}" } # For svn repos # shellcheck disable=SC2039,SC2164,SC2155 svnRepo() { ${LOCAL} oldPath="${PWD}" VCS_TYPE="svn" case "${PWD}" in /*trunk*|/*branches*|/*tags*) ${LOCAL} fn="${PWD}" while [ "$(basename "${fn}")" != 'trunk' ] && [ "$(basename "${fn}")" != 'branches' ] && [ "$(basename "${fn}")" != 'tags' ] && [ "$(basename "${fn}")" != '/' ]; do ${LOCAL} fn="$(dirname "${fn}")" done ${LOCAL} fn="$(dirname "${fn}")" if [ "${fn}" = '/' ]; then VCS_BASENAME="$(basename "${PWD}")" else VCS_BASENAME="$(basename "${fn}")" fi ;; *) VCS_BASENAME="$(basename "${PWD}")" ;; esac VCS_UUID="$(svn info --xml | sed -n -e 's:::' -e 's:::p')" # Cache svnversion output ${LOCAL} SVNVERSION="$(svnversion)" # Is the working copy clean? echo "${SVNVERSION}" | grep -q "M" case "${?}" in 0) VCS_WC_MODIFIED="1" ;; 1) if [ ! -z "${UNTRACKEDFILES}" ]; then # `svnversion` does not detect untracked files and `svn status` is really slow, so only run it if we really have to. if [ -z "$(svn status)" ]; then VCS_WC_MODIFIED="0" else VCS_WC_MODIFIED="1" fi else VCS_WC_MODIFIED="0" fi ;; esac # Enumeration of changesets VCS_NUM="$(echo "${SVNVERSION}" | cut -d : -f 1 | sed -e 's:M::' -e 's:S::' -e 's:P::')" # The full revision hash VCS_FULL_HASH="${SVNVERSION}" # The short hash VCS_SHORT_HASH="${VCS_NUM}" # Current branch case "${PWD}" in /*trunk*|/*branches*|/*tags*) ${LOCAL} lastbase="" ${LOCAL} fn="${PWD}" while : do base="$(basename "${fn}")" if [ "${base}" = 'trunk' ]; then VCS_BRANCH='trunk' break elif [ "${base}" = 'branches' ] || [ "${base}" = 'tags' ]; then VCS_BRANCH="${lastbase}" break elif [ "${base}" = '/' ]; then VCS_BRANCH="" break fi ${LOCAL} lastbase="${base}" ${LOCAL} fn="$(dirname "${fn}")" done ;; *) VCS_BRANCH="" ;; esac # Current or last tag ancestor (empty if no tags). But "current # tag" can't be extracted reliably because Subversion doesn't # have tags the way other VCSes do. VCS_TAG="" VCS_TICK="" # Date of the current commit VCS_DATE="$(svn info --xml | sed -n -e 's:::' -e 's:::p')" # Action Stamp VCS_ACTION_STAMP="${VCS_DATE}!$(svn log --xml -l 1 -r "${VCS_SHORT_HASH}" | sed -n -e 's:::' -e 's:::p')" cd "${oldPath}" } # Functions to output data in different formats. # For bash output # First in list because it is used by autorevision shOutput() { tee << EOF # ${GENERATED_HEADER} VCS_TYPE="${VCS_TYPE}" VCS_BASENAME="${VCS_BASENAME}" VCS_UUID="${VCS_UUID}" VCS_NUM="${VCS_NUM}" VCS_DATE="${VCS_DATE}" VCS_BRANCH="${VCS_BRANCH}" VCS_TAG="${VCS_TAG}" VCS_TICK="${VCS_TICK}" ${EXTRA_NAME}="${VCS_EXTRA}" VCS_ACTION_STAMP="${VCS_ACTION_STAMP}" VCS_FULL_HASH="${VCS_FULL_HASH}" VCS_SHORT_HASH="${VCS_SHORT_HASH}" VCS_WC_MODIFIED="${VCS_WC_MODIFIED}" # end EOF } # For source C output cOutput() { tee << EOF /* ${GENERATED_HEADER} */ const char *VCS_TYPE = "${VCS_TYPE}"; const char *VCS_BASENAME = "${VCS_BASENAME}"; const char *VCS_UUID = "${VCS_UUID}"; const int VCS_NUM = ${VCS_NUM}; const char *VCS_DATE = "${VCS_DATE}"; const char *VCS_BRANCH = "${VCS_BRANCH}"; const char *VCS_TAG = "${VCS_TAG}"; const int VCS_TICK = ${VCS_TICK}; const char *${EXTRA_NAME} = "${VCS_EXTRA}"; const char *VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"; const char *VCS_FULL_HASH = "${VCS_FULL_HASH}"; const char *VCS_SHORT_HASH = "${VCS_SHORT_HASH}"; const int VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; /* end */ EOF } # For Cmake output cmakeOutput() { tee << EOF # ${GENERATED_HEADER} set(VCS_TYPE ${VCS_TYPE}) set(VCS_BASENAME ${VCS_BASENAME}) set(VCS_UUID ${VCS_UUID}) set(VCS_NUM ${VCS_NUM}) set(VCS_DATE ${VCS_DATE}) set(VCS_BRANCH ${VCS_BRANCH}) set(VCS_TAG ${VCS_TAG}) set(VCS_TICK ${VCS_TICK}) set(${EXTRA_NAME} ${VCS_EXTRA}) set(VCS_ACTION_STAMP ${VCS_ACTION_STAMP}) set(VCS_FULL_HASH ${VCS_FULL_HASH}) set(VCS_SHORT_HASH ${VCS_SHORT_HASH}) set(VCS_WC_MODIFIED ${VCS_WC_MODIFIED}) # end EOF } # For header output hOutput() { tee << EOF /* ${GENERATED_HEADER} */ #ifndef AUTOREVISION_H #define AUTOREVISION_H #define VCS_TYPE "${VCS_TYPE}" #define VCS_BASENAME "${VCS_BASENAME}" #define VCS_UUID "${VCS_UUID}" #define VCS_NUM ${VCS_NUM} #define VCS_DATE "${VCS_DATE}" #define VCS_BRANCH "${VCS_BRANCH}" #define VCS_TAG "${VCS_TAG}" #define VCS_TICK ${VCS_TICK} #define ${EXTRA_NAME} "${VCS_EXTRA}" #define VCS_ACTION_STAMP "${VCS_ACTION_STAMP}" #define VCS_FULL_HASH "${VCS_FULL_HASH}" #define VCS_SHORT_HASH "${VCS_SHORT_HASH}" #define VCS_WC_MODIFIED ${VCS_WC_MODIFIED} #endif /* end */ EOF } # A header output for use with xcode to populate info.plist strings xcodeOutput() { tee << EOF /* ${GENERATED_HEADER} */ #ifndef AUTOREVISION_H #define AUTOREVISION_H #define VCS_TYPE ${VCS_TYPE} #define VCS_BASENAME ${VCS_BASENAME} #define VCS_UUID ${VCS_UUID} #define VCS_NUM ${VCS_NUM} #define VCS_DATE ${VCS_DATE} #define VCS_BRANCH ${VCS_BRANCH} #define VCS_TAG ${VCS_TAG} #define VCS_TICK ${VCS_TICK} #define ${EXTRA_NAME} ${VCS_EXTRA} #define VCS_ACTION_STAMP ${VCS_ACTION_STAMP} #define VCS_FULL_HASH ${VCS_FULL_HASH} #define VCS_SHORT_HASH ${VCS_SHORT_HASH} #define VCS_WC_MODIFIED ${VCS_WC_MODIFIED} #endif /* end */ EOF } # For Swift output swiftOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="false" ;; 1) VCS_WC_MODIFIED="true" ;; esac # For values that may not exist depending on the type of repo we # have read from, set them to `nil` when they are empty. if [ -z "${VCS_UUID}" ]; then VCS_UUID="nil" else VCS_UUID="\"${VCS_UUID}\"" fi if [ -z "${VCS_TAG}" ]; then VCS_TAG="nil" else VCS_TAG="\"${VCS_TAG}\"" fi : "${VCS_TICK:="nil"}" if [ -z "${VCS_EXTRA}" ]; then VCS_EXTRA="nil" else VCS_EXTRA="\"${VCS_EXTRA}\"" fi if [ -z "${VCS_ACTION_STAMP}" ]; then VCS_ACTION_STAMP="nil" else VCS_ACTION_STAMP="\"${VCS_ACTION_STAMP}\"" fi tee << EOF /* ${GENERATED_HEADER} */ let VCS_TYPE = "${VCS_TYPE}" let VCS_BASENAME = "${VCS_BASENAME}" let VCS_UUID: String? = ${VCS_UUID} let VCS_NUM: Int = ${VCS_NUM} let VCS_DATE = "${VCS_DATE}" let VCS_BRANCH: String = "${VCS_BRANCH}" let VCS_TAG: String? = ${VCS_TAG} let VCS_TICK: Int? = ${VCS_TICK} let ${EXTRA_NAME}: String? = ${VCS_EXTRA} let VCS_ACTION_STAMP: String? = ${VCS_ACTION_STAMP} let VCS_FULL_HASH: String = "${VCS_FULL_HASH}" let VCS_SHORT_HASH: String = "${VCS_SHORT_HASH}" let VCS_WC_MODIFIED: Bool = ${VCS_WC_MODIFIED} /* end */ EOF } # For Python output pyOutput() { case "${VCS_WC_MODIFIED}" in 0) PY_VCS_WC_MODIFIED="False" ;; 1) PY_VCS_WC_MODIFIED="True" ;; esac tee << EOF # -*- coding: utf-8 -*- # ${GENERATED_HEADER} VCS_TYPE = "${VCS_TYPE}" VCS_BASENAME = "${VCS_BASENAME}" VCS_UUID = "${VCS_UUID}" VCS_NUM = ${VCS_NUM} VCS_DATE = "${VCS_DATE}" VCS_BRANCH = "${VCS_BRANCH}" VCS_TAG = "${VCS_TAG}" VCS_TICK = ${VCS_TICK} ${EXTRA_NAME} = "${VCS_EXTRA}" VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}" VCS_FULL_HASH = "${VCS_FULL_HASH}" VCS_SHORT_HASH = "${VCS_SHORT_HASH}" VCS_WC_MODIFIED = ${PY_VCS_WC_MODIFIED} # end EOF } # For Perl output plOutput() { tee << EOF # ${GENERATED_HEADER} \$VCS_TYPE = '${VCS_TYPE}'; \$VCS_BASENAME = '${VCS_BASENAME}'; \$VCS_UUID = '${VCS_UUID}'; \$VCS_NUM = ${VCS_NUM}; \$VCS_DATE = '${VCS_DATE}'; \$VCS_BRANCH = '${VCS_BRANCH}'; \$VCS_TAG = '${VCS_TAG}'; \$VCS_TICK = ${VCS_TICK}; \$${EXTRA_NAME} = '${VCS_EXTRA}'; \$VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}'; \$VCS_FULL_HASH = '${VCS_FULL_HASH}'; \$VCS_SHORT_HASH = '${VCS_SHORT_HASH}'; \$VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; # end 1; EOF } # For lua output luaOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="false" ;; 1) VCS_WC_MODIFIED="true" ;; esac tee << EOF -- ${GENERATED_HEADER} VCS_TYPE = "${VCS_TYPE}" VCS_BASENAME = "${VCS_BASENAME}" VCS_UUID = "${VCS_UUID}" VCS_NUM = ${VCS_NUM} VCS_DATE = "${VCS_DATE}" VCS_BRANCH = "${VCS_BRANCH}" VCS_TAG = "${VCS_TAG}" VCS_TICK = ${VCS_TICK} ${EXTRA_NAME} = "${VCS_EXTRA}" VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}" VCS_FULL_HASH = "${VCS_FULL_HASH}" VCS_SHORT_HASH = "${VCS_SHORT_HASH}" VCS_WC_MODIFIED = ${VCS_WC_MODIFIED} -- end EOF } # For php output phpOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="false" ;; 1) VCS_WC_MODIFIED="true" ;; esac tee << EOF "${VCS_TYPE}", "VCS_BASENAME" => "${VCS_BASENAME}", "VCS_UUID" => "${VCS_UUID}", "VCS_NUM" => ${VCS_NUM}, "VCS_DATE" => "${VCS_DATE}", "VCS_BRANCH" => "${VCS_BRANCH}", "VCS_TAG" => "${VCS_TAG}", "VCS_TICK" => ${VCS_TICK}, "${EXTRA_NAME}" => "${VCS_EXTRA}", "VCS_ACTION_STAMP" => "${VCS_ACTION_STAMP}", "VCS_FULL_HASH" => "${VCS_FULL_HASH}", "VCS_SHORT_HASH" => "${VCS_SHORT_HASH}", "VCS_WC_MODIFIED" => ${VCS_WC_MODIFIED} ); # end ?> EOF } # For ini output iniOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="false" ;; 1) VCS_WC_MODIFIED="true" ;; esac tee << EOF ; ${GENERATED_HEADER} [VCS] VCS_TYPE = "${VCS_TYPE}" VCS_BASENAME = "${VCS_BASENAME}" VCS_UUID = "${VCS_UUID}" VCS_NUM = ${VCS_NUM} VCS_DATE = "${VCS_DATE}" VCS_BRANCH = "${VCS_BRANCH}" VCS_TAG = "${VCS_TAG}" VCS_TICK = ${VCS_TICK} ${EXTRA_NAME} = "${VCS_EXTRA}" VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}" VCS_FULL_HASH = "${VCS_FULL_HASH}" VCS_SHORT_HASH = "${VCS_SHORT_HASH}" VCS_WC_MODIFIED = ${VCS_WC_MODIFIED} ; end EOF } # For javascript output jsOutput() { case "${VCS_WC_MODIFIED}" in 1) VCS_WC_MODIFIED="true" ;; 0) VCS_WC_MODIFIED="false" ;; esac tee << EOF /** ${GENERATED_HEADER} */ var autorevision = { VCS_TYPE: "${VCS_TYPE}", VCS_BASENAME: "${VCS_BASENAME}", VCS_UUID: "${VCS_UUID}", VCS_NUM: ${VCS_NUM}, VCS_DATE: "${VCS_DATE}", VCS_BRANCH: "${VCS_BRANCH}", VCS_TAG: "${VCS_TAG}", VCS_TICK: ${VCS_TICK}, ${EXTRA_NAME}: "${VCS_EXTRA}", VCS_ACTION_STAMP: "${VCS_ACTION_STAMP}", VCS_FULL_HASH: "${VCS_FULL_HASH}", VCS_SHORT_HASH: "${VCS_SHORT_HASH}", VCS_WC_MODIFIED: ${VCS_WC_MODIFIED} }; /** Node.js compatibility */ if (typeof module !== 'undefined') { module.exports = autorevision; } /** end */ EOF } # For JSON output jsonOutput() { case "${VCS_WC_MODIFIED}" in 1) VCS_WC_MODIFIED="true" ;; 0) VCS_WC_MODIFIED="false" ;; esac tee << EOF { "_comment": "${GENERATED_HEADER}", "VCS_TYPE": "${VCS_TYPE}", "VCS_BASENAME": "${VCS_BASENAME}", "VCS_UUID": "${VCS_UUID}", "VCS_NUM": ${VCS_NUM}, "VCS_DATE": "${VCS_DATE}", "VCS_BRANCH":"${VCS_BRANCH}", "VCS_TAG": "${VCS_TAG}", "VCS_TICK": ${VCS_TICK}, "${EXTRA_NAME}": "${VCS_EXTRA}", "VCS_ACTION_STAMP": "${VCS_ACTION_STAMP}", "VCS_FULL_HASH": "${VCS_FULL_HASH}", "VCS_SHORT_HASH": "${VCS_SHORT_HASH}", "VCS_WC_MODIFIED": ${VCS_WC_MODIFIED} } EOF } # For Java output javaOutput() { case "${VCS_WC_MODIFIED}" in 1) VCS_WC_MODIFIED="true" ;; 0) VCS_WC_MODIFIED="false" ;; esac tee << EOF /* ${GENERATED_HEADER} */ public class autorevision { public static final String VCS_TYPE = "${VCS_TYPE}"; public static final String VCS_BASENAME = "${VCS_BASENAME}"; public static final String VCS_UUID = "${VCS_UUID}"; public static final long VCS_NUM = ${VCS_NUM}; public static final String VCS_DATE = "${VCS_DATE}"; public static final String VCS_BRANCH = "${VCS_BRANCH}"; public static final String VCS_TAG = "${VCS_TAG}"; public static final long VCS_TICK = ${VCS_TICK}; public static final String ${EXTRA_NAME} = "${VCS_EXTRA}"; public static final String VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"; public static final String VCS_FULL_HASH = "${VCS_FULL_HASH}"; public static final String VCS_SHORT_HASH = "${VCS_SHORT_HASH}"; public static final boolean VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; } EOF } csharpOutput() { case "${VCS_WC_MODIFIED}" in 1) VCS_WC_MODIFIED="true" ;; 0) VCS_WC_MODIFIED="false" ;; esac if [ "${EXTRA_NAME}" = "VCS_EXTRA" ]; then EXTRA_NAME="VcsExtra" fi tee << EOF /* ${GENERATED_HEADER} */ namespace AutoRevision { public class VersionInfo { public static string VcsType = "${VCS_TYPE}"; public static string VcsBasename = "${VCS_BASENAME}"; public static string VcsUuid = "${VCS_UUID}"; public static string VcsNum = "${VCS_NUM}"; public static string VcsDate = "${VCS_DATE}"; public static string VcsBranch = "${VCS_DATE}"; public static string VcsTag = "${VCS_TAG}"; public static string VcsTick = "${VCS_TICK}"; public static string ${EXTRA_NAME} = "${VCS_EXTRA}"; public static string VcsActionStamp = "${VCS_ACTION_STAMP}"; public static string VcsFullHash = "${VCS_FULL_HASH}"; public static string VcsShortHash = "${VCS_SHORT_HASH}"; public static string VcsWcModified = "${VCS_WC_MODIFIED}"; } } EOF } # For Java properties output javapropOutput() { case "${VCS_WC_MODIFIED}" in 1) VCS_WC_MODIFIED="true" ;; 0) VCS_WC_MODIFIED="false" ;; esac tee << EOF # ${GENERATED_HEADER} VCS_TYPE=${VCS_TYPE} VCS_BASENAME=${VCS_BASENAME} VCS_UUID=${VCS_UUID} VCS_NUM=${VCS_NUM} VCS_DATE=${VCS_DATE} VCS_BRANCH=${VCS_BRANCH} VCS_TAG=${VCS_TAG} VCS_TICK=${VCS_TICK} ${EXTRA_NAME}=${VCS_EXTRA} VCS_ACTION_STAMP=${VCS_ACTION_STAMP} VCS_FULL_HASH=${VCS_FULL_HASH} VCS_SHORT_HASH=${VCS_SHORT_HASH} VCS_WC_MODIFIED=${VCS_WC_MODIFIED} EOF } # For m4 output m4Output() { tee << EOF dnl ${GENERATED_HEADER} define(\`VCS_TYPE', \`${VCS_TYPE}')dnl define(\`VCS_BASENAME', \`${VCS_BASENAME}')dnl define(\`VCS_UUID', \`${VCS_UUID}')dnl define(\`VCS_NUM', \`${VCS_NUM}')dnl define(\`VCS_DATE', \`${VCS_DATE}')dnl define(\`VCS_BRANCH', \`${VCS_BRANCH}')dnl define(\`VCS_TAG', \`${VCS_TAG}')dnl define(\`VCS_TICK', \`${VCS_TICK}')dnl define(\`${EXTRA_NAME}', \`${VCS_EXTRA}')dnl define(\`VCS_ACTIONSTAMP', \`${VCS_ACTION_STAMP}')dnl define(\`VCS_FULLHASH', \`${VCS_FULL_HASH}')dnl define(\`VCS_SHORTHASH', \`${VCS_SHORT_HASH}')dnl define(\`VCS_WC_MODIFIED', \`${VCS_WC_MODIFIED}')dnl EOF } # For (La)TeX output texOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="false" ;; 1) VCS_WC_MODIFIED="true" ;; esac if [ "${EXTRA_NAME}" = "VCS_EXTRA" ]; then EXTRA_NAME="vcsExtra" fi tee << EOF % ${GENERATED_HEADER} \def \vcsType {${VCS_TYPE}} \def \vcsBasename {${VCS_BASENAME}} \def \vcsUUID {${VCS_UUID}} \def \vcsNum {${VCS_NUM}} \def \vcsDate {${VCS_DATE}} \def \vcsBranch {${VCS_BRANCH}} \def \vcsTag {${VCS_TAG}} \def \vcsTick {${VCS_TICK}} \def \\${EXTRA_NAME} {${VCS_EXTRA}} \def \vcsACTIONSTAMP {${VCS_ACTION_STAMP}} \def \vcsFullHash {${VCS_FULL_HASH}} \def \vcsShortHash {${VCS_SHORT_HASH}} \def \vcsWCModified {${VCS_WC_MODIFIED}} \endinput EOF } # For scheme output schemeOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="#f" ;; 1) VCS_WC_MODIFIED="#t" ;; esac tee << EOF ;; ${GENERATED_HEADER} (define VCS_TYPE "${VCS_TYPE}") (define VCS_BASENAME "${VCS_BASENAME}") (define VCS_UUID "${VCS_UUID}") (define VCS_NUM ${VCS_NUM}) (define VCS_DATE "${VCS_DATE}") (define VCS_BRANCH "${VCS_BRANCH}") (define VCS_TAG "${VCS_TAG}") (define VCS_TICK ${VCS_TICK}) (define ${EXTRA_NAME} "${VCS_EXTRA}") (define VCS_ACTION_STAMP "${VCS_ACTION_STAMP}") (define VCS_FULL_HASH "${VCS_FULL_HASH}") (define VCS_SHORT_HASH "${VCS_SHORT_HASH}") (define VCS_WC_MODIFIED ${VCS_WC_MODIFIED}) ;; end EOF } # For clojure output clojureOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="false" ;; 1) VCS_WC_MODIFIED="true" ;; esac tee << EOF ;; ${GENERATED_HEADER} (def VCS_TYPE "${VCS_TYPE}") (def VCS_BASENAME "${VCS_BASENAME}") (def VCS_UUID "${VCS_UUID}") (def VCS_NUM ${VCS_NUM}) (def VCS_DATE "${VCS_DATE}") (def VCS_BRANCH "${VCS_BRANCH}") (def VCS_TAG "${VCS_TAG}") (def VCS_TICK ${VCS_TICK}) (def ${EXTRA_NAME} "${VCS_EXTRA}") (def VCS_ACTION_STAMP "${VCS_ACTION_STAMP}") (def VCS_FULL_HASH "${VCS_FULL_HASH}") (def VCS_SHORT_HASH "${VCS_SHORT_HASH}") (def VCS_WC_MODIFIED ${VCS_WC_MODIFIED}) ;; end EOF } # For rpm spec file output rpmOutput() { tee << EOF # ${GENERATED_HEADER} $([ "${VCS_TYPE}" ] && echo "%define vcs_type ${VCS_TYPE}") $([ "${VCS_BASENAME}" ] && echo "%define vcs_basename ${VCS_BASENAME}") $([ "${VCS_UUID}" ] && echo "%define vcs_uuid ${VCS_UUID}") $([ "${VCS_NUM}" ] && echo "%define vcs_num ${VCS_NUM}") $([ "${VCS_DATE}" ] && echo "%define vcs_date ${VCS_DATE}") $([ "${VCS_BRANCH}" ] && echo "%define vcs_branch ${VCS_BRANCH}") $([ "${VCS_TAG}" ] && echo "%define vcs_tag ${VCS_TAG}") $([ "${VCS_TICK}" ] && echo "%define vcs_tick ${VCS_TICK}") $([ "${VCS_EXTRA}" ] && echo "%define ${EXTRA_NAME} ${VCS_EXTRA}") $([ "${VCS_ACTION_STAMP}" ] && echo "%define vcs_action_stamp ${VCS_ACTION_STAMP}") $([ "${VCS_FULL_HASH}" ] && echo "%define vcs_full_hash ${VCS_FULL_HASH}") $([ "${VCS_SHORT_HASH}" ] && echo "%define vcs_short_hash ${VCS_SHORT_HASH}") $([ "${VCS_WC_MODIFIED}" ] && echo "%define vcs_wc_modified ${VCS_WC_MODIFIED}") # end EOF } # For C++ Header output # shellcheck disable=SC2155,SC2039 hppOutput() { ${LOCAL} NAMESPACE="$(echo "${VCS_BASENAME}" | sed -e 's:_::g' | tr '[:lower:]' '[:upper:]')" tee << EOF /* ${GENERATED_HEADER} */ #ifndef ${NAMESPACE}_AUTOREVISION_H #define ${NAMESPACE}_AUTOREVISION_H #include namespace $(echo "${NAMESPACE}" | tr '[:upper:]' '[:lower:]') { const std::string VCS_TYPE = "${VCS_TYPE}"; const std::string VCS_BASENAME = "${VCS_BASENAME}"; const std::string VCS_UUID = "${VCS_UUID}"; const int VCS_NUM = ${VCS_NUM}; const std::string VCS_DATE = "${VCS_DATE}"; const std::string VCS_BRANCH = "${VCS_BRANCH}"; const std::string VCS_TAG = "${VCS_TAG}"; const int VCS_TICK = ${VCS_TICK}; const std::string ${EXTRA_NAME} = "${VCS_EXTRA}"; const std::string VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"; const std::string VCS_FULL_HASH = "${VCS_FULL_HASH}"; const std::string VCS_SHORT_HASH = "${VCS_SHORT_HASH}"; const int VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; } #endif /* end */ EOF } # For Matlab output matlabOutput() { case "${VCS_WC_MODIFIED}" in 0) VCS_WC_MODIFIED="FALSE" ;; 1) VCS_WC_MODIFIED="TRUE" ;; esac tee << EOF % ${GENERATED_HEADER} VCS_TYPE = '${VCS_TYPE}'; VCS_BASENAME = '${VCS_BASENAME}'; VCS_UUID = '${VCS_UUID}'; VCS_NUM = ${VCS_NUM}; VCS_DATE = '${VCS_DATE}'; VCS_BRANCH = '${VCS_BRANCH}'; VCS_TAG = '${VCS_TAG}'; VCS_TICK = ${VCS_TICK}; ${EXTRA_NAME} = '${VCS_EXTRA}'; VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}'; VCS_FULL_HASH = '${VCS_FULL_HASH}'; VCS_SHORT_HASH = '${VCS_SHORT_HASH}'; VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; % end EOF } # For Octave output octaveOutput() { tee << EOF % ${GENERATED_HEADER} VCS_TYPE = '${VCS_TYPE}'; VCS_BASENAME = '${VCS_BASENAME}'; VCS_UUID = '${VCS_UUID}'; VCS_NUM = ${VCS_NUM}; VCS_DATE = '${VCS_DATE}'; VCS_BRANCH = '${VCS_BRANCH}'; VCS_TAG = '${VCS_TAG}'; VCS_TICK = ${VCS_TICK}; ${EXTRA_NAME} = '${VCS_EXTRA}'; VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}'; VCS_FULL_HASH = '${VCS_FULL_HASH}'; VCS_SHORT_HASH = '${VCS_SHORT_HASH}'; VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}; % end EOF } # Helper functions # Count path segments # shellcheck disable=SC2039 pathSegment() { ${LOCAL} pathz="${1}" ${LOCAL} depth="0" if [ ! -z "${pathz}" ]; then # Continue until we are at / or there are no path separators left. while [ ! "${pathz}" = "/" ] && [ ! "${pathz}" = "$(echo "${pathz}" | sed -e 's:/::')" ]; do pathz="$(dirname "${pathz}")" depth="$((depth+1))" done fi echo "${depth}" } # Largest of four numbers # shellcheck disable=SC2039 multiCompare() { ${LOCAL} larger="${1}" ${LOCAL} numA="${2}" ${LOCAL} numB="${3}" ${LOCAL} numC="${4}" [ "${numA}" -gt "${larger}" ] && larger="${numA}" [ "${numB}" -gt "${larger}" ] && larger="${numB}" [ "${numC}" -gt "${larger}" ] && larger="${numC}" echo "${larger}" } # Test for repositories # shellcheck disable=SC2155,SC2039 repoTest() { REPONUM="0" if command -v git > /dev/null 2>&1; then ${LOCAL} gitPath="$(git rev-parse --show-toplevel 2>/dev/null)" ${LOCAL} gitDepth="$(pathSegment "${gitPath}")" if [ ! -z "${gitPath}" ]; then REPONUM="$((REPONUM+1))" fi else ${LOCAL} gitDepth="0" fi if command -v hg > /dev/null 2>&1; then ${LOCAL} hgPath="$(hg root 2>/dev/null)" ${LOCAL} hgDepth="$(pathSegment "${hgPath}")" if [ ! -z "${hgPath}" ]; then REPONUM="$((REPONUM+1))" fi else ${LOCAL} hgDepth="0" fi if command -v bzr > /dev/null 2>&1; then ${LOCAL} bzrPath="$(bzr root 2>/dev/null)" ${LOCAL} bzrDepth="$(pathSegment "${bzrPath}")" if [ ! -z "${bzrPath}" ]; then REPONUM="$((REPONUM+1))" fi else ${LOCAL} bzrDepth="0" fi if command -v svn > /dev/null 2>&1; then ${LOCAL} stringz="" ${LOCAL} stringx="" ${LOCAL} svnPath="$(svn info --xml 2>/dev/null | sed -n -e "s:${stringz}::" -e "s:${stringx}::p")" # An old enough svn will not be able give us a path; default # to 1 for that case. if [ ! -z "${svnPath}" ]; then ${LOCAL} svnDepth="$(pathSegment "${svnPath}")" REPONUM="$((REPONUM+1))" elif [ -z "${svnPath}" ] && [ -d ".svn" ]; then ${LOCAL} svnDepth="1" REPONUM="$((REPONUM+1))" else ${LOCAL} svnDepth="0" fi else ${LOCAL} svnDepth="0" fi # Do not do more work then we have to. if [ "${REPONUM}" = "0" ]; then return 0 fi # Figure out which repo is the deepest and use it. ${LOCAL} wonRepo="$(multiCompare "${gitDepth}" "${hgDepth}" "${bzrDepth}" "${svnDepth}")" if [ "${wonRepo}" = "${gitDepth}" ]; then gitRepo elif [ "${wonRepo}" = "${hgDepth}" ]; then hgRepo elif [ "${wonRepo}" = "${bzrDepth}" ]; then bzrRepo elif [ "${wonRepo}" = "${svnDepth}" ]; then svnRepo fi } # Detect which repos we are in and gather data. # shellcheck source=/dev/null if [ -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then # When requested only read from the cache to populate our symbols. . "${CACHEFILE}" else # If a value is not set through the environment set VCS_EXTRA to nothing. : "${VCS_EXTRA:=""}" repoTest if [ -f "${CACHEFILE}" ] && [ "${REPONUM}" = "0" ]; then # We are not in a repo; try to use a previously generated cache to populate our symbols. . "${CACHEFILE}" # Do not overwrite the cache if we know we are not going to write anything new. CACHEFORCE="1" elif [ "${REPONUM}" = "0" ]; then echo "error: No repo or cache detected." 1>&2 exit 1 fi fi # -s output is handled here. if [ ! -z "${VAROUT}" ]; then if [ "${VAROUT}" = "VCS_TYPE" ]; then echo "${VCS_TYPE}" elif [ "${VAROUT}" = "VCS_BASENAME" ]; then echo "${VCS_BASENAME}" elif [ "${VAROUT}" = "VCS_NUM" ]; then echo "${VCS_NUM}" elif [ "${VAROUT}" = "VCS_DATE" ]; then echo "${VCS_DATE}" elif [ "${VAROUT}" = "VCS_BRANCH" ]; then echo "${VCS_BRANCH}" elif [ "${VAROUT}" = "VCS_TAG" ]; then echo "${VCS_TAG}" elif [ "${VAROUT}" = "VCS_TICK" ]; then echo "${VCS_TICK}" elif [ "${VAROUT}" = "VCS_FULL_HASH" ]; then echo "${VCS_FULL_HASH}" elif [ "${VAROUT}" = "VCS_SHORT_HASH" ]; then echo "${VCS_SHORT_HASH}" elif [ "${VAROUT}" = "VCS_WC_MODIFIED" ]; then echo "${VCS_WC_MODIFIED}" elif [ "${VAROUT}" = "VCS_ACTION_STAMP" ]; then echo "${VCS_ACTION_STAMP}" else echo "error: Not a valid output symbol." 1>&2 exit 1 fi fi # Detect requested output type and use it. if [ ! -z "${AFILETYPE}" ]; then if [ "${AFILETYPE}" = "c" ]; then cOutput elif [ "${AFILETYPE}" = "h" ]; then hOutput elif [ "${AFILETYPE}" = "xcode" ]; then xcodeOutput elif [ "${AFILETYPE}" = "swift" ]; then swiftOutput elif [ "${AFILETYPE}" = "sh" ]; then shOutput elif [ "${AFILETYPE}" = "py" ] || [ "${AFILETYPE}" = "python" ]; then pyOutput elif [ "${AFILETYPE}" = "pl" ] || [ "${AFILETYPE}" = "perl" ]; then plOutput elif [ "${AFILETYPE}" = "lua" ]; then luaOutput elif [ "${AFILETYPE}" = "php" ]; then phpOutput elif [ "${AFILETYPE}" = "ini" ]; then iniOutput elif [ "${AFILETYPE}" = "js" ]; then jsOutput elif [ "${AFILETYPE}" = "json" ]; then jsonOutput elif [ "${AFILETYPE}" = "java" ]; then javaOutput elif [ "${AFILETYPE}" = "javaprop" ]; then javapropOutput elif [ "${AFILETYPE}" = "csharp" ]; then csharpOutput elif [ "${AFILETYPE}" = "tex" ]; then texOutput elif [ "${AFILETYPE}" = "m4" ]; then m4Output elif [ "${AFILETYPE}" = "scheme" ]; then schemeOutput elif [ "${AFILETYPE}" = "clojure" ]; then clojureOutput elif [ "${AFILETYPE}" = "rpm" ]; then rpmOutput elif [ "${AFILETYPE}" = "hpp" ]; then hppOutput elif [ "${AFILETYPE}" = "matlab" ]; then matlabOutput elif [ "${AFILETYPE}" = "octave" ]; then octaveOutput elif [ "${AFILETYPE}" = "cmake" ]; then cmakeOutput else echo "error: Not a valid output type." 1>&2 exit 1 fi fi # If requested, make a cache file. if [ ! -z "${CACHEFILE}" ] && [ ! "${CACHEFORCE}" = "1" ]; then EXTRA_NAME="VCS_EXTRA" shOutput > "${CACHEFILE}.tmp" # Check to see if there have been any actual changes. if [ ! -f "${CACHEFILE}" ]; then mv -f "${CACHEFILE}.tmp" "${CACHEFILE}" elif cmp -s "${CACHEFILE}.tmp" "${CACHEFILE}"; then rm -f "${CACHEFILE}.tmp" else mv -f "${CACHEFILE}.tmp" "${CACHEFILE}" fi fi ntpsec-1.1.0+dfsg1/wafhelpers/rtems_trace.py0000644000175000017500000000042013252364117020643 0ustar rlaagerrlaagerfrom waflib.TaskGen import feature, after_method @feature("rtems_trace") @after_method('apply_link') def rtems_trace(self): if self.env.RTEMS_TEST_ENABLE: self.link_task.env.LINK_CC = self.env.BIN_RTEMS_TLD \ + self.env.RTEMS_TEST_FLAGS + ['--'] ntpsec-1.1.0+dfsg1/wafhelpers/check_sizeof.py0000644000175000017500000000416213252364117020776 0ustar rlaagerrlaagerfrom waflib.Configure import conf from waflib import Errors SIZE_FRAG = """ %s #include int main(void) { printf("%%lu", sizeof(%s)); return 0; } """ def check_sizeof_host(ctx, header, sizeof, mandatory=True): sizeof_ns = sizeof.replace(" ", "_") name = "NTP_SIZEOF_%s" % sizeof_ns.upper() header_snippet = "" if header: ctx.start_msg("Checking sizeof %s (%s)" % (sizeof, header)) header_snippet = "#include <%s>" % header else: ctx.start_msg("Checking sizeof %s" % (sizeof)) ctx.check_cc( fragment=SIZE_FRAG % (header_snippet, sizeof), define_name=name, execute=True, define_ret=True, quote=False, mandatory=mandatory, comment="Size of %s from <%s>" % (sizeof, header) ) ctx.end_msg(ctx.get_define(name)) # Cross compile check. Much slower so we do not run it all the time. SIZE_FRAG_CROSS = """ %s #include int main(void) { static int test_array [1 - 2 * !(((long int) (sizeof (%s))) <= %d)]; test_array [0] = 0; return test_array[0]; } """ def check_sizeof_cross(ctx, header, sizeof, mandatory=True): sizeof_ns = sizeof.replace(" ", "_") name = "NTP_SIZEOF_%s" % sizeof_ns.upper() header_snippet = "" if header: ctx.start_msg("Checking sizeof %s (%s)" % (sizeof, header)) header_snippet = "#include <%s>" % header else: ctx.start_msg("Checking sizeof %s" % (sizeof)) for size in range(2, 13): try: ctx.check_cc( fragment=SIZE_FRAG_CROSS % (header_snippet, sizeof, size), features="c", execute=False, mandatory=mandatory, ) ctx.define(name, size, comment="Size of %s from <%s>" % (sizeof, header)) ctx.end_msg(ctx.get_define(name)) return except Errors.ConfigurationError: pass raise # never reached. @conf def check_sizeof(*kwargs): if kwargs[0].env.ENABLE_CROSS: return check_sizeof_cross(*kwargs) else: return check_sizeof_host(*kwargs) ntpsec-1.1.0+dfsg1/wafhelpers/.autorevision-cache0000644000175000017500000000061513252367157021567 0ustar rlaagerrlaager# Generated by autorevision - do not hand-hack! VCS_TYPE="git" VCS_BASENAME="ntpsec" VCS_UUID="5a60ff124237ba5d23101e4ca60016ebeae7cb50" VCS_NUM="14539" VCS_DATE="2018-03-14T12:03:57-0700" VCS_BRANCH="master" VCS_TAG="NTPsec_1_0_0" VCS_TICK="419" VCS_EXTRA="1.1.0" VCS_ACTION_STAMP="" VCS_FULL_HASH="6d378236a9edf11fb1cf06f9dde149a912b44c8e" VCS_SHORT_HASH="6d37823" VCS_WC_MODIFIED="1" # end ntpsec-1.1.0+dfsg1/wafhelpers/waf.py0000644000175000017500000000404513252364117017117 0ustar rlaagerrlaagerimport re from waflib.Configure import conf from waflib.TaskGen import before_method, feature, re_m4 @before_method('apply_incpaths') @feature('bld_include') def insert_blddir(self): bldnode = self.bld.bldnode.parent.abspath() self.includes += [bldnode] @before_method('apply_incpaths') @feature('src_include') def insert_srcdir(self): srcnode = self.bld.srcnode.abspath() self.includes += ["%s/include" % srcnode] def manpage_subst_fun(self, code): # Since we are providing a custom substitution method, we must implement # the default behavior, since we want that too. global re_m4 # replace all % by %% to prevent errors by % signs code = code.replace('%', '%%') # extract the vars foo into lst and replace @foo@ by %(foo)s lst = [] def repl(match): g = match.group if g(1): lst.append(g(1)) return "%%(%s)s" % g(1) return '' code = getattr(self.generator, 're_m4', re_m4).sub(repl, code) try: d = self.generator.dct except AttributeError: d = {} for x in lst: tmp = getattr(self.generator, x, '') or self.env[x] or self.env[x.upper()] try: tmp = ''.join(tmp) except TypeError: tmp = str(tmp) d[x] = tmp code = code % d return code.replace("include::../docs/", "include::../../../docs/") @conf def manpage(ctx, section, source): # ctx.install_files('${MANDIR}' + "/man%s/" % section, # source.replace("-man.txt", ".%s" % section)) if not ctx.env.ENABLE_DOC or ctx.env.DISABLE_MANPAGE: return ctx(features="subst", source=source, target=source + '.man-tmp', subst_fun=manpage_subst_fun) ctx(source=source + '.man-tmp', section=section) @conf def ntp_test(ctx, **kwargs): bldnode = ctx.bldnode.abspath() tg = ctx(**kwargs) args = ["%s/tests/%s" % (bldnode, tg.target), "-v"] if hasattr(tg, "test_args"): args += tg.test_args tg.ut_exec = args ntpsec-1.1.0+dfsg1/wafhelpers/probes.py0000644000175000017500000000267213252364117017640 0ustar rlaagerrlaager""" This module exists to contain custom probe functions so they don't clutter up the logic in the main configure.py. """ def probe_header_with_prerequisites(ctx, header, prerequisites, use=None): "Check that a header (with its prerequisites) compiles." src = "" for hdr in prerequisites + [header]: src += "#include <%s>\n" % hdr src += "int main(void) { return 0; }\n" have_name = "HAVE_%s" \ % header.replace(".", "_").replace("/", "_").upper() ctx.check_cc( comment="<%s> header" % header, define_name=have_name, fragment=src, includes=ctx.env.PLATFORM_INCLUDES, mandatory=False, msg="Checking for header %s" % header, use=use or [], ) return ctx.get_define(have_name) def probe_function_with_prerequisites(ctx, function, prerequisites, use=None): "Check that a function (with its prerequisites) compiles." src = "" for hdr in prerequisites: src += "#include <%s>\n" % hdr src += """int main(void) { void *p = (void*)(%s); return (int)p; } """ % function have_name = "HAVE_%s" % function.upper() ctx.check_cc( comment="Whether %s() exists" % function, define_name=have_name, fragment=src, includes=ctx.env.PLATFORM_INCLUDES, mandatory=False, msg="Checking for function %s" % function, use=use or [], ) return ctx.get_define(have_name) # end ntpsec-1.1.0+dfsg1/wafhelpers/pythonize-header0000755000175000017500000000540513252364117021176 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- """ Generate a Python module from a C file of macro definitions. Uses simple regexp transformations, so weird headers are likely to confuse it. Skips #include lines, struct and union declarations, single-line typedefs and externs. Transforms comments. Does not yet handle multiline externs or variable declarations. Yes, SWIG can do something like this. But it's heavyweight and would introduce another dependency. """ import re import sys patterns = ( (re.compile(r"/\*(.*)\*/"), r"#\1"), (re.compile(r"^/\*"), r"#"), (re.compile(r"^ *\*/"), r"#"), (re.compile(r"^ \*"), r"#"), (re.compile(r"^#define\s+([A-Za-z0-9_]+[ \t]+)(.*)"), r"\1= \2"), (re.compile(r"^#define\s+([A-Za-z0-9_]+)\(([a-z_, ]*)\)"), r"def \1(\2): return"), ) skipstarters = ("struct", "union", "typedef struct", "typedef union") skipender = re.compile("^}.*;") # Has to allow a structure attribute skippers = ( re.compile(r"^#include"), re.compile(r"^typedef.*;\s*$"), re.compile(r"^extern.*;\d*$"), ) impossibles = ("sizeof", "offsetof") def pythonize(rfp, wfp): "Pythonize a specified C header file." wfp.write("# This file was generated by pythonize-header;\n") wfp.write("# DO NOT HAND-HACK IT!\n") skipto = False ignoring = False for line in rfp: # The ignore logic if "pythonize-header" in line: if "start" in line: ignoring = True elif "stop" in line: ignoring = False continue if ignoring: continue # Omit include lines skipit = False for skipline in skippers: if skipline.match(line): skipit = True break if skipit: continue # Omit structure and union declarations for skipstart in skipstarters: if line.startswith(skipstart): skipto = True break if skipender.match(line): skipto = False continue # Hack remaining content for (regexp, replacement) in patterns: line = regexp.sub(replacement, line) # Omit some expression elements Python cannot evaluate for word in impossibles: if word in line: line = "# " + line # We're done if not skipto: wfp.write(line) if __name__ == '__main__': if len(sys.argv) == 1: pythonize(sys.stdin, sys.stdout) else: for path in sys.argv[1:]: with open(path) as rfp: sys.stdout.write("# -*- coding: utf-8 -*-\n") sys.stdout.write("#\n# Definitions from %s begin\n#\n" % path) pythonize(rfp, sys.stdout) # end ntpsec-1.1.0+dfsg1/.gitignore0000644000175000017500000000011313252364117015610 0ustar rlaagerrlaagerwafhelpers/.autorevision-cache .lock-waf* .waf* *.pyc ntpd/version.h build ntpsec-1.1.0+dfsg1/ntpd/0000775000175000017500000000000013252650651014575 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/ntpd/ntp_control.c0000644000175000017500000031444513252364117017312 0ustar rlaagerrlaager/* * ntp_control.c - respond to mode 6 control messages. * Provides service to ntpq and others. */ #include "config.h" #include #include #include #include #include #include #include /* provides OpenSSL digest API */ #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_control.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include "ntp_config.h" #include "ntp_assert.h" #include "ntp_leapsec.h" #include "lib_strbuf.h" #include "ntp_syscall.h" #include "timespecops.h" /* undefine to suppress random tags and get fixed emission order */ #define USE_RANDOMIZE_RESPONSES /* * Structure to hold request procedure information */ struct ctl_proc { short control_code; /* defined request code */ #define NO_REQUEST (-1) unsigned short flags; /* flags word */ /* Only one flag. Authentication required or not. */ #define NOAUTH 0 #define AUTH 1 void (*handler) (struct recvbuf *, int); /* handle request */ }; /* * Request processing routines */ static void ctl_error (uint8_t); #ifdef REFCLOCK static unsigned short ctlclkstatus (struct refclockstat *); #endif static void ctl_flushpkt (uint8_t); static void ctl_putdata (const char *, unsigned int, bool); static void ctl_putstr (const char *, const char *, size_t); static void ctl_putdblf (const char *, int, int, double); #define ctl_putdbl(tag, d) ctl_putdblf(tag, 1, 3, d) #define ctl_putdbl6(tag, d) ctl_putdblf(tag, 1, 6, d) #define ctl_putsfp(tag, sfp) ctl_putdblf(tag, 0, -1, \ FP_UNSCALE(sfp)) static void ctl_putuint (const char *, uint64_t); static void ctl_puthex (const char *, uint64_t); static void ctl_putint (const char *, long); static void ctl_putts (const char *, l_fp *); static void ctl_putadr (const char *, uint32_t, sockaddr_u *); static void ctl_putrefid (const char *, uint32_t); static void ctl_putarray (const char *, double *, int); static void ctl_putsys (int); static void ctl_putpeer (int, struct peer *); static void ctl_puttime (const char *, time_t); #ifdef REFCLOCK static void ctl_putclock (int, struct refclockstat *, int); #endif /* REFCLOCK */ static const struct ctl_var *ctl_getitem(const struct ctl_var *, char **); static unsigned short ctlsysstatus (void); static unsigned short count_var (const struct ctl_var *); static void control_unspec (struct recvbuf *, int); static void read_status (struct recvbuf *, int); static void read_sysvars (void); static void read_peervars (void); static void read_variables (struct recvbuf *, int); static void write_variables (struct recvbuf *, int); static void read_clockstatus(struct recvbuf *, int); static void write_clockstatus(struct recvbuf *, int); static void configure (struct recvbuf *, int); static void send_mru_entry (mon_entry *, int); #ifdef USE_RANDOMIZE_RESPONSES static void send_random_tag_value(int); #endif /* USE_RANDOMIZE_RESPONSES */ static void read_mru_list (struct recvbuf *, int); static void send_ifstats_entry(endpt *, unsigned int); static void read_ifstats (struct recvbuf *); static void sockaddrs_from_restrict_u(sockaddr_u *, sockaddr_u *, restrict_u *, int); static void send_restrict_entry(restrict_u *, int, unsigned int); static void send_restrict_list(restrict_u *, int, unsigned int *); static void read_addr_restrictions(struct recvbuf *); static void read_ordlist (struct recvbuf *, int); static uint32_t derive_nonce (sockaddr_u *, uint32_t, uint32_t); static void generate_nonce (struct recvbuf *, char *, size_t); static int validate_nonce (const char *, struct recvbuf *); static void req_nonce (struct recvbuf *, int); static const struct ctl_proc control_codes[] = { { CTL_OP_UNSPEC, NOAUTH, control_unspec }, { CTL_OP_READSTAT, NOAUTH, read_status }, { CTL_OP_READVAR, NOAUTH, read_variables }, { CTL_OP_WRITEVAR, AUTH, write_variables }, { CTL_OP_READCLOCK, NOAUTH, read_clockstatus }, { CTL_OP_WRITECLOCK, NOAUTH, write_clockstatus }, { CTL_OP_CONFIGURE, AUTH, configure }, { CTL_OP_READ_MRU, NOAUTH, read_mru_list }, { CTL_OP_READ_ORDLIST_A, AUTH, read_ordlist }, { CTL_OP_REQ_NONCE, NOAUTH, req_nonce }, { NO_REQUEST, 0, NULL } }; /* * System variables we understand */ #define CS_LEAP 1 #define CS_STRATUM 2 #define CS_PRECISION 3 #define CS_ROOTDELAY 4 #define CS_ROOTDISPERSION 5 #define CS_REFID 6 #define CS_REFTIME 7 #define CS_POLL 8 #define CS_PEERID 9 #define CS_OFFSET 10 #define CS_DRIFT 11 #define CS_JITTER 12 #define CS_ERROR 13 #define CS_CLOCK 14 #define CS_PROCESSOR 15 #define CS_SYSTEM 16 #define CS_VERSION 17 #define CS_STABIL 18 #define CS_VARLIST 19 #define CS_TAI 20 #define CS_LEAPTAB 21 #define CS_LEAPEND 22 #define CS_RATE 23 #define CS_MRU_ENABLED 24 #define CS_MRU_DEPTH 25 #define CS_MRU_DEEPEST 26 #define CS_MRU_MINDEPTH 27 #define CS_MRU_MAXAGE 28 #define CS_MRU_MINAGE 29 #define CS_MRU_MAXDEPTH 30 #define CS_MRU_MEM 31 #define CS_MRU_MAXMEM 32 #define CS_SS_UPTIME 33 #define CS_SS_RESET 34 #define CS_SS_RECEIVED 35 #define CS_SS_THISVER 36 #define CS_SS_OLDVER 37 #define CS_SS_BADFORMAT 38 #define CS_SS_BADAUTH 39 #define CS_SS_DECLINED 40 #define CS_SS_RESTRICTED 41 #define CS_SS_LIMITED 42 #define CS_SS_KODSENT 43 #define CS_SS_PROCESSED 44 #define CS_PEERADR 45 #define CS_PEERMODE 46 #define CS_AUTHDELAY 47 #define CS_AUTHKEYS 48 #define CS_AUTHFREEK 49 #define CS_AUTHKLOOKUPS 50 #define CS_AUTHKNOTFOUND 51 #define CS_AUTHKUNCACHED 52 #define CS_AUTHKEXPIRED 53 #define CS_AUTHENCRYPTS 54 #define CS_AUTHDECRYPTS 55 #define CS_AUTHRESET 56 #define CS_K_OFFSET 57 #define CS_K_FREQ 58 #define CS_K_MAXERR 59 #define CS_K_ESTERR 60 #define CS_K_STFLAGS 61 #define CS_K_TIMECONST 62 #define CS_K_PRECISION 63 #define CS_K_FREQTOL 64 #define CS_K_PPS_FREQ 65 #define CS_K_PPS_STABIL 66 #define CS_K_PPS_JITTER 67 #define CS_K_PPS_CALIBDUR 68 #define CS_K_PPS_CALIBS 69 #define CS_K_PPS_CALIBERRS 70 #define CS_K_PPS_JITEXC 71 #define CS_K_PPS_STBEXC 72 #define CS_KERN_FIRST CS_K_OFFSET #define CS_KERN_LAST CS_K_PPS_STBEXC #define CS_IOSTATS_RESET 73 #define CS_TOTAL_RBUF 74 #define CS_FREE_RBUF 75 #define CS_USED_RBUF 76 #define CS_RBUF_LOWATER 77 #define CS_IO_DROPPED 78 #define CS_IO_IGNORED 79 #define CS_IO_RECEIVED 80 #define CS_IO_SENT 81 #define CS_IO_SENDFAILED 82 #define CS_IO_WAKEUPS 83 #define CS_IO_GOODWAKEUPS 84 #define CS_TIMERSTATS_RESET 85 #define CS_TIMER_OVERRUNS 86 #define CS_TIMER_XMTS 87 #define CS_FUZZ 88 #define CS_WANDER_THRESH 89 #define CS_MRU_EXISTS 90 #define CS_MRU_NEW 91 #define CS_MRU_RECYCLEOLD 92 #define CS_MRU_RECYCLEFULL 93 #define CS_MRU_NONE 94 #define CS_MRU_OLDEST_AGE 95 #define CS_LEAPSMEARINTV 96 #define CS_LEAPSMEAROFFS 97 #define CS_TICK 98 #define CS_NUMCTLREQ 99 #define CS_ROOTDISTANCE 100 #define CS_MAXCODE CS_ROOTDISTANCE /* * Peer variables we understand */ #define CP_CONFIG 1 #define CP_AUTHENABLE 2 #define CP_AUTHENTIC 3 #define CP_SRCADR 4 #define CP_SRCPORT 5 #define CP_DSTADR 6 #define CP_DSTPORT 7 #define CP_LEAP 8 #define CP_HMODE 9 #define CP_STRATUM 10 #define CP_PPOLL 11 #define CP_HPOLL 12 #define CP_PRECISION 13 #define CP_ROOTDELAY 14 #define CP_ROOTDISPERSION 15 #define CP_REFID 16 #define CP_REFTIME 17 #define CP_ORG 18 #define CP_REC 19 #define CP_XMT 20 #define CP_REACH 21 #define CP_UNREACH 22 #define CP_TIMER 23 #define CP_DELAY 24 #define CP_OFFSET 25 #define CP_JITTER 26 #define CP_DISPERSION 27 #define CP_KEYID 28 #define CP_FILTDELAY 29 #define CP_FILTOFFSET 30 #define CP_PMODE 31 #define CP_RECEIVED 32 #define CP_SENT 33 #define CP_FILTERROR 34 #define CP_FLASH 35 #define CP_MODE 36 #define CP_VARLIST 37 #define CP_IN 38 #define CP_OUT 39 #define CP_RATE 40 #define CP_BIAS 41 #define CP_SRCHOST 42 #define CP_TIMEREC 43 #define CP_TIMEREACH 44 #define CP_BADAUTH 45 #define CP_BOGUSORG 46 #define CP_OLDPKT 47 #define CP_SELDISP 48 #define CP_SELBROKEN 49 #define CP_CANDIDATE 50 #define CP_MAXCODE CP_CANDIDATE /* * Clock variables we understand */ #define CC_NAME 1 #define CC_TIMECODE 2 #define CC_POLL 3 #define CC_NOREPLY 4 #define CC_BADFORMAT 5 #define CC_BADDATA 6 #define CC_FUDGETIME1 7 #define CC_FUDGETIME2 8 #define CC_FUDGEVAL1 9 #define CC_FUDGEVAL2 10 #define CC_FLAGS 11 #define CC_DEVICE 12 #define CC_VARLIST 13U #define CC_MAXCODE CC_VARLIST /* * System variable values. The array can be indexed by the variable * index to find the textual name. */ static const struct ctl_var sys_var[] = { { 0, PADDING, "" }, /* 0 */ { CS_LEAP, RW, "leap" }, /* 1 */ { CS_STRATUM, RO, "stratum" }, /* 2 */ { CS_PRECISION, RO, "precision" }, /* 3 */ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */ { CS_ROOTDISPERSION, RO, "rootdisp" }, /* 5 */ { CS_REFID, RO, "refid" }, /* 6 */ { CS_REFTIME, RO, "reftime" }, /* 7 */ { CS_POLL, RO, "tc" }, /* 8 */ { CS_PEERID, RO, "peer" }, /* 9 */ { CS_OFFSET, RO, "offset" }, /* 10 */ { CS_DRIFT, RO, "frequency" }, /* 11 */ { CS_JITTER, RO, "sys_jitter" }, /* 12 */ { CS_ERROR, RO, "clk_jitter" }, /* 13 */ { CS_CLOCK, RO, "clock" }, /* 14 */ { CS_PROCESSOR, RO, "processor" }, /* 15 */ { CS_SYSTEM, RO, "system" }, /* 16 */ { CS_VERSION, RO, "version" }, /* 17 */ { CS_STABIL, RO, "clk_wander" }, /* 18 */ { CS_VARLIST, RO, "sys_var_list" }, /* 19 */ { CS_TAI, RO, "tai" }, /* 20 */ { CS_LEAPTAB, RO, "leapsec" }, /* 21 */ { CS_LEAPEND, RO, "expire" }, /* 22 */ { CS_RATE, RO, "mintc" }, /* 23 */ { CS_MRU_ENABLED, RO, "mru_enabled" }, /* 24 */ { CS_MRU_DEPTH, RO, "mru_depth" }, /* 25 */ { CS_MRU_DEEPEST, RO, "mru_deepest" }, /* 26 */ { CS_MRU_MINDEPTH, RO, "mru_mindepth" }, /* 27 */ { CS_MRU_MAXAGE, RO, "mru_maxage" }, /* 28 */ { CS_MRU_MINAGE, RO, "mru_minage" }, /* 29 */ { CS_MRU_MAXDEPTH, RO, "mru_maxdepth" }, /* 30 */ { CS_MRU_MEM, RO, "mru_mem" }, /* 31 */ { CS_MRU_MAXMEM, RO, "mru_maxmem" }, /* 32 */ { CS_SS_UPTIME, RO, "ss_uptime" }, /* 33 */ { CS_SS_RESET, RO, "ss_reset" }, /* 34 */ { CS_SS_RECEIVED, RO, "ss_received" }, /* 35 */ { CS_SS_THISVER, RO, "ss_thisver" }, /* 36 */ { CS_SS_OLDVER, RO, "ss_oldver" }, /* 37 */ { CS_SS_BADFORMAT, RO, "ss_badformat" }, /* 38 */ { CS_SS_BADAUTH, RO, "ss_badauth" }, /* 39 */ { CS_SS_DECLINED, RO, "ss_declined" }, /* 40 */ { CS_SS_RESTRICTED, RO, "ss_restricted" }, /* 41 */ { CS_SS_LIMITED, RO, "ss_limited" }, /* 42 */ { CS_SS_KODSENT, RO, "ss_kodsent" }, /* 43 */ { CS_SS_PROCESSED, RO, "ss_processed" }, /* 44 */ { CS_PEERADR, RO, "peeradr" }, /* 45 */ { CS_PEERMODE, RO, "peermode" }, /* 46 */ { CS_AUTHDELAY, RO, "authdelay" }, /* 47 */ { CS_AUTHKEYS, RO, "authkeys" }, /* 48 */ { CS_AUTHFREEK, RO, "authfreek" }, /* 49 */ { CS_AUTHKLOOKUPS, RO, "authklookups" }, /* 50 */ { CS_AUTHKNOTFOUND, RO, "authknotfound" }, /* 51 */ { CS_AUTHKUNCACHED, RO, "authkuncached" }, /* 52 */ { CS_AUTHKEXPIRED, RO, "authkexpired" }, /* 53 */ { CS_AUTHENCRYPTS, RO, "authencrypts" }, /* 54 */ { CS_AUTHDECRYPTS, RO, "authdecrypts" }, /* 55 */ { CS_AUTHRESET, RO, "authreset" }, /* 56 */ { CS_K_OFFSET, RO, "koffset" }, /* 57 */ { CS_K_FREQ, RO, "kfreq" }, /* 58 */ { CS_K_MAXERR, RO, "kmaxerr" }, /* 59 */ { CS_K_ESTERR, RO, "kesterr" }, /* 60 */ { CS_K_STFLAGS, RO, "kstflags" }, /* 61 */ { CS_K_TIMECONST, RO, "ktimeconst" }, /* 62 */ { CS_K_PRECISION, RO, "kprecis" }, /* 63 */ { CS_K_FREQTOL, RO, "kfreqtol" }, /* 64 */ { CS_K_PPS_FREQ, RO, "kppsfreq" }, /* 65 */ { CS_K_PPS_STABIL, RO, "kppsstab" }, /* 66 */ { CS_K_PPS_JITTER, RO, "kppsjitter" }, /* 67 */ { CS_K_PPS_CALIBDUR, RO, "kppscalibdur" }, /* 68 */ { CS_K_PPS_CALIBS, RO, "kppscalibs" }, /* 69 */ { CS_K_PPS_CALIBERRS, RO, "kppscaliberrs" }, /* 70 */ { CS_K_PPS_JITEXC, RO, "kppsjitexc" }, /* 71 */ { CS_K_PPS_STBEXC, RO, "kppsstbexc" }, /* 72 */ { CS_IOSTATS_RESET, RO, "iostats_reset" }, /* 73 */ { CS_TOTAL_RBUF, RO, "total_rbuf" }, /* 74 */ { CS_FREE_RBUF, RO, "free_rbuf" }, /* 75 */ { CS_USED_RBUF, RO, "used_rbuf" }, /* 76 */ { CS_RBUF_LOWATER, RO, "rbuf_lowater" }, /* 77 */ { CS_IO_DROPPED, RO, "io_dropped" }, /* 78 */ { CS_IO_IGNORED, RO, "io_ignored" }, /* 79 */ { CS_IO_RECEIVED, RO, "io_received" }, /* 80 */ { CS_IO_SENT, RO, "io_sent" }, /* 81 */ { CS_IO_SENDFAILED, RO, "io_sendfailed" }, /* 82 */ { CS_IO_WAKEUPS, RO, "io_wakeups" }, /* 83 */ { CS_IO_GOODWAKEUPS, RO, "io_goodwakeups" }, /* 84 */ { CS_TIMERSTATS_RESET, RO, "timerstats_reset" },/* 85 */ { CS_TIMER_OVERRUNS, RO, "timer_overruns" }, /* 86 */ { CS_TIMER_XMTS, RO, "timer_xmts" }, /* 87 */ { CS_FUZZ, RO, "fuzz" }, /* 88 */ { CS_WANDER_THRESH, RO, "clk_wander_threshold" }, /* 89 */ { CS_MRU_EXISTS, RO, "mru_exists" }, /* 90 */ { CS_MRU_NEW, RO, "mru_new" }, /* 91 */ { CS_MRU_RECYCLEOLD, RO, "mru_recycleold" }, /* 92 */ { CS_MRU_RECYCLEFULL, RO, "mru_recyclefull" },/* 93 */ { CS_MRU_NONE, RO, "mru_none" }, /* 94 */ { CS_MRU_OLDEST_AGE, RO, "mru_oldest_age" }, /* 95 */ { CS_LEAPSMEARINTV, RO, "leapsmearinterval" }, /* 96 */ { CS_LEAPSMEAROFFS, RO, "leapsmearoffset" }, /* 97 */ { CS_TICK, RO, "tick" }, /* 98 */ /* new in NTPsec */ { CS_NUMCTLREQ, RO, "ss_numctlreq" }, /* 99 */ { CS_ROOTDISTANCE, RO, "rootdist" }, /* 100 */ { 0, EOV, "" } }; static struct ctl_var *ext_sys_var = NULL; /* * System variables we print by default (in fuzzball order, * more-or-less) */ static const uint8_t def_sys_var[] = { CS_VERSION, CS_PROCESSOR, CS_SYSTEM, CS_LEAP, CS_STRATUM, CS_PRECISION, CS_ROOTDELAY, CS_ROOTDISPERSION, CS_REFID, CS_REFTIME, CS_CLOCK, CS_PEERID, CS_POLL, CS_RATE, CS_OFFSET, CS_DRIFT, CS_JITTER, CS_ERROR, CS_STABIL, CS_TAI, CS_LEAPTAB, CS_LEAPEND, 0 }; /* * Peer variable list */ static const struct ctl_var peer_var[] = { { 0, PADDING, "" }, /* 0 */ { CP_CONFIG, RO, "config" }, /* 1 */ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */ { CP_SRCADR, RO, "srcadr" }, /* 4 */ { CP_SRCPORT, RO, "srcport" }, /* 5 */ { CP_DSTADR, RO, "dstadr" }, /* 6 */ { CP_DSTPORT, RO, "dstport" }, /* 7 */ { CP_LEAP, RO, "leap" }, /* 8 */ { CP_HMODE, RO, "hmode" }, /* 9 */ { CP_STRATUM, RO, "stratum" }, /* 10 */ { CP_PPOLL, RO, "ppoll" }, /* 11 */ { CP_HPOLL, RO, "hpoll" }, /* 12 */ { CP_PRECISION, RO, "precision" }, /* 13 */ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */ { CP_ROOTDISPERSION, RO, "rootdisp" }, /* 15 */ { CP_REFID, RO, "refid" }, /* 16 */ { CP_REFTIME, RO, "reftime" }, /* 17 */ /* Placeholder. Reporting of this variable is disabled because leaking it creates a vulnerability */ { CP_ORG, RO, "org" }, /* 18 */ { CP_REC, RO, "rec" }, /* 19 */ { CP_XMT, RO, "xmt" }, /* 20 */ { CP_REACH, RO, "reach" }, /* 21 */ { CP_UNREACH, RO, "unreach" }, /* 22 */ { CP_TIMER, RO, "timer" }, /* 23 */ { CP_DELAY, RO, "delay" }, /* 24 */ { CP_OFFSET, RO, "offset" }, /* 25 */ { CP_JITTER, RO, "jitter" }, /* 26 */ { CP_DISPERSION, RO, "dispersion" }, /* 27 */ { CP_KEYID, RO, "keyid" }, /* 28 */ { CP_FILTDELAY, RO, "filtdelay" }, /* 29 */ { CP_FILTOFFSET, RO, "filtoffset" }, /* 30 */ { CP_PMODE, RO, "pmode" }, /* 31 */ { CP_RECEIVED, RO, "received"}, /* 32 */ { CP_SENT, RO, "sent" }, /* 33 */ { CP_FILTERROR, RO, "filtdisp" }, /* 34 */ { CP_FLASH, RO, "flash" }, /* 35 */ { CP_MODE, RO, "mode" }, /* 36 */ { CP_VARLIST, RO, "peer_var_list" }, /* 37 */ { CP_IN, RO, "in" }, /* 38 */ { CP_OUT, RO, "out" }, /* 39 */ { CP_RATE, RO, "headway" }, /* 40 */ { CP_BIAS, RO, "bias" }, /* 41 */ { CP_SRCHOST, RO, "srchost" }, /* 42 */ { CP_TIMEREC, RO, "timerec" }, /* 43 */ { CP_TIMEREACH, RO, "timereach" }, /* 44 */ { CP_BADAUTH, RO, "badauth" }, /* 45 */ { CP_BOGUSORG, RO, "bogusorg" }, /* 46 */ { CP_OLDPKT, RO, "oldpkt" }, /* 47 */ { CP_SELDISP, RO, "seldisp" }, /* 48 */ { CP_SELBROKEN, RO, "selbroken" }, /* 49 */ { CP_CANDIDATE, RO, "candidate" }, /* 50 */ { 0, EOV, "" } /* 50/58 */ }; /* * Peer variables we print by default */ static const uint8_t def_peer_var[] = { CP_SRCADR, CP_SRCPORT, CP_SRCHOST, CP_DSTADR, CP_DSTPORT, CP_OUT, CP_IN, CP_LEAP, CP_STRATUM, CP_PRECISION, CP_ROOTDELAY, CP_ROOTDISPERSION, CP_REFID, CP_REFTIME, CP_REC, CP_REACH, CP_UNREACH, CP_HMODE, CP_PMODE, CP_HPOLL, CP_PPOLL, CP_RATE, CP_FLASH, CP_KEYID, CP_MODE, CP_OFFSET, CP_DELAY, CP_DISPERSION, CP_JITTER, CP_XMT, CP_BIAS, CP_FILTDELAY, CP_FILTOFFSET, CP_FILTERROR, 0 }; #ifdef REFCLOCK /* * Clock variable list */ static const struct ctl_var clock_var[] = { { 0, PADDING, "" }, /* 0 */ { CC_NAME, RO, "name" }, /* 1 */ { CC_TIMECODE, RO, "timecode" }, /* 2 */ { CC_POLL, RO, "poll" }, /* 3 */ { CC_NOREPLY, RO, "noreply" }, /* 4 */ { CC_BADFORMAT, RO, "badformat" }, /* 5 */ { CC_BADDATA, RO, "baddata" }, /* 6 */ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */ { CC_FLAGS, RO, "flags" }, /* 11 */ { CC_DEVICE, RO, "device" }, /* 12 */ { CC_VARLIST, RO, "clock_var_list" }, /* 13 */ { 0, EOV, "" } /* 14 */ }; /* * Clock variables printed by default */ static const uint8_t def_clock_var[] = { CC_DEVICE, CC_NAME, CC_TIMECODE, CC_POLL, CC_NOREPLY, CC_BADFORMAT, CC_BADDATA, CC_FUDGETIME1, CC_FUDGETIME2, CC_FUDGEVAL1, CC_FUDGEVAL2, CC_FLAGS, 0 }; #endif /* * MRU string constants shared by send_mru_entry() and read_mru_list(). */ static const char addr_fmt[] = "addr.%d"; static const char last_fmt[] = "last.%d"; /* * System and processor definitions. */ # include static struct utsname utsnamebuf; /* * Keyid used for authenticating write requests. */ keyid_t ctl_auth_keyid; /* * * A hack. To keep the authentication module clear of ntp-ism's, we * * include a time reset variable for its stats here. * */ static unsigned long auth_timereset; /* * We keep track of the last error reported by the system internally */ static uint8_t ctl_sys_last_event; static uint8_t ctl_sys_num_events; /* * Statistic counters to keep track of requests and responses. */ static unsigned long ctltimereset; /* time stats reset */ static uint64_t numctlreq; /* # of requests we've received */ static uint64_t numctlbadpkts; /* # of bad control packets */ static uint64_t numctlresponses; /* # of resp packets sent with data */ static uint64_t numctlfrags; /* # of fragments sent */ static uint64_t numctlerrors; /* # of error responses sent */ static uint64_t numctltooshort; /* # of too short input packets */ static uint64_t numctlinputresp; /* # of responses on input */ static uint64_t numctlinputfrag; /* # of fragments on input */ static uint64_t numctlinputerr; /* # of input pkts with err bit set */ static uint64_t numctlbadoffset; /* # of input pkts with nonzero offset */ static uint64_t numctlbadversion; /* # of input pkts with unknown version */ static uint64_t numctldatatooshort; /* data too short for count */ static uint64_t numctlbadop; /* bad op code found in packet */ static uint64_t numasyncmsgs; /* # of async messages we've sent */ /* * Response packet used by these routines. Also some state information * so that we can handle packet formatting within a common set of * subroutines. Note we try to enter data in place whenever possible, * but the need to set the more bit correctly means we occasionally * use the extra buffer and copy. */ static struct ntp_control rpkt; static uint8_t res_version; static uint8_t res_opcode; static associd_t res_associd; static unsigned short res_frags; /* datagrams in this response */ static int res_offset; /* offset of payload in response */ static uint8_t * datapt; static int datalinelen; static bool datasent; /* flag to avoid initial ", " */ static bool datanotbinflag; static sockaddr_u *rmt_addr; static endpt *lcl_inter; static bool res_authenticate; static bool res_authokay; static keyid_t res_keyid; #define MAXDATALINELEN (72) /* * Pointers for saving state when decoding request packets */ static char *reqpt; static char *reqend; #ifndef MIN #define MIN(a, b) (((a) <= (b)) ? (a) : (b)) #endif /* * init_control - initialize request data */ void init_control(void) { uname(&utsnamebuf); ctl_clr_stats(); ctl_auth_keyid = 0; /* these may be unused with the old trap facility gone */ ctl_sys_last_event = EVNT_UNSPEC; ctl_sys_num_events = 0; #ifdef ENABLE_CLASSIC_MODE /* a relic from when there were multiple nonstandard ways to set time */ #define PRESET "settimeofday=\"clock_settime\"" set_sys_var(PRESET, sizeof(PRESET), RO); #undef PRESET #endif /* ENABLE_CLASSIC_MODE */ } /* * ctl_error - send an error response for the current request */ static void ctl_error( uint8_t errcode ) { int maclen; numctlerrors++; DPRINT(3, ("sending control error %u\n", errcode)); /* * Fill in the fields. We assume rpkt.sequence and rpkt.associd * have already been filled in. */ rpkt.r_m_e_op = (uint8_t)CTL_RESPONSE | CTL_ERROR | (res_opcode & CTL_OP_MASK); rpkt.status = htons((unsigned short)(errcode << 8) & 0xff00); rpkt.count = 0; /* * send packet and bump counters */ if (res_authenticate) { maclen = authencrypt(res_keyid, (uint32_t *)&rpkt, CTL_HEADER_LEN); sendpkt(rmt_addr, lcl_inter, &rpkt, (int)CTL_HEADER_LEN + maclen); } else sendpkt(rmt_addr, lcl_inter, &rpkt, CTL_HEADER_LEN); } /* * process_control - process an incoming control message */ void process_control( struct recvbuf *rbufp, int restrict_mask ) { struct ntp_control *pkt; int req_count; int req_data; const struct ctl_proc *cc; keyid_t *pkid; int properlen; size_t maclen; DPRINT(3, ("in process_control()\n")); /* * Save the addresses for error responses */ numctlreq++; rmt_addr = &rbufp->recv_srcadr; lcl_inter = rbufp->dstadr; pkt = (struct ntp_control *)&rbufp->recv_pkt; /* * If the length is less than required for the header, or * it is a response or a fragment, ignore this. */ if (rbufp->recv_length < (int)CTL_HEADER_LEN || (CTL_RESPONSE | CTL_MORE | CTL_ERROR) & pkt->r_m_e_op || pkt->offset != 0) { DPRINT(1, ("invalid format in control packet\n")); if (rbufp->recv_length < (int)CTL_HEADER_LEN) numctltooshort++; if (CTL_RESPONSE & pkt->r_m_e_op) numctlinputresp++; if (CTL_MORE & pkt->r_m_e_op) numctlinputfrag++; if (CTL_ERROR & pkt->r_m_e_op) numctlinputerr++; if (pkt->offset != 0) numctlbadoffset++; return; } res_version = PKT_VERSION(pkt->li_vn_mode); if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) { DPRINT(1, ("unknown version %d in control packet\n", res_version)); numctlbadversion++; return; } /* * Pull enough data from the packet to make intelligent * responses */ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version, MODE_CONTROL); res_opcode = pkt->r_m_e_op; rpkt.sequence = pkt->sequence; rpkt.associd = pkt->associd; rpkt.status = 0; res_frags = 1; res_offset = 0; res_associd = ntohs(pkt->associd); res_authenticate = false; res_keyid = 0; res_authokay = false; req_count = (int)ntohs(pkt->count); datanotbinflag = false; datalinelen = 0; datasent = false; datapt = rpkt.data; if ((rbufp->recv_length & 0x3) != 0) DPRINT(3, ("Control packet length %zu unrounded\n", rbufp->recv_length)); /* * We're set up now. Make sure we've got at least enough * incoming data space to match the count. */ req_data = (int)rbufp->recv_length - (int)CTL_HEADER_LEN; if (req_data < req_count || rbufp->recv_length & 0x3) { ctl_error(CERR_BADFMT); numctldatatooshort++; return; } properlen = req_count + (int)CTL_HEADER_LEN; /* round up proper len to a 8 octet boundary */ properlen = (properlen + 7) & ~7; maclen = rbufp->recv_length - (size_t)properlen; if ((rbufp->recv_length & 3) == 0 && maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN) { res_authenticate = true; pkid = (void *)((char *)pkt + properlen); res_keyid = ntohl(*pkid); DPRINT(3, ("recv_len %zu, properlen %d, wants auth with keyid %08x, MAC length=%zu\n", rbufp->recv_length, properlen, res_keyid, maclen)); if (!authistrusted(res_keyid)) DPRINT(3, ("invalid keyid %08x\n", res_keyid)); else if (authdecrypt(res_keyid, (uint32_t *)pkt, (int)rbufp->recv_length - (int)maclen, (int)maclen)) { res_authokay = true; DPRINT(3, ("authenticated okay\n")); } else { res_keyid = 0; DPRINT(3, ("authentication failed\n")); } } /* * Set up translate pointers */ reqpt = (char *)pkt->data; reqend = reqpt + req_count; /* * Look for the opcode processor */ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) { if (cc->control_code == res_opcode) { DPRINT(3, ("opcode %d, found command handler\n", res_opcode)); if (cc->flags == AUTH && (!res_authokay || res_keyid != ctl_auth_keyid)) { ctl_error(CERR_PERMISSION); return; } (cc->handler)(rbufp, restrict_mask); return; } } /* * Can't find this one, return an error. */ numctlbadop++; ctl_error(CERR_BADOP); return; } /* * ctlpeerstatus - return a status word for this peer */ unsigned short ctlpeerstatus( struct peer *p ) { unsigned short status; status = p->status; if (FLAG_CONFIG & p->cfg.flags) status |= CTL_PST_CONFIG; if (p->cfg.peerkey) status |= CTL_PST_AUTHENABLE; if (FLAG_AUTHENTIC & p->cfg.flags) status |= CTL_PST_AUTHENTIC; if (p->reach) status |= CTL_PST_REACH; if (MDF_TXONLY_MASK & p->cast_flags) status |= CTL_PST_BCAST; return CTL_PEER_STATUS(status, p->num_events, p->last_event); } /* * ctlclkstatus - return a status word for this clock */ #ifdef REFCLOCK static unsigned short ctlclkstatus( struct refclockstat *pcs ) { return CTL_PEER_STATUS(0, pcs->lastevent, pcs->currentstatus); } #endif /* * ctlsysstatus - return the system status word */ static unsigned short ctlsysstatus(void) { uint8_t this_clock; this_clock = CTL_SST_TS_UNSPEC; #ifdef REFCLOCK if (sys_peer != NULL) { if (CTL_SST_TS_UNSPEC != sys_peer->sstclktype) this_clock = sys_peer->sstclktype; } #else /* REFCLOCK */ if (sys_peer != 0) this_clock = CTL_SST_TS_NTP; #endif /* REFCLOCK */ return CTL_SYS_STATUS(sys_leap, this_clock, ctl_sys_num_events, ctl_sys_last_event); } /* * ctl_flushpkt - write out the current packet and prepare * another if necessary. */ static void ctl_flushpkt( uint8_t more ) { int dlen; int sendlen; int maclen; int totlen; keyid_t keyid; dlen = datapt - rpkt.data; if (!more && datanotbinflag && dlen + 2 < CTL_MAX_DATA_LEN) { /* * Big hack, output a trailing \r\n */ *datapt++ = '\r'; *datapt++ = '\n'; dlen += 2; } sendlen = dlen + (int)CTL_HEADER_LEN; /* * Zero-fill the unused part of the packet. This wasn't needed * when the clients were all in C, for which the first NUL is * a string terminator. But Python allows NULs in strings, * which means Python mode 6 clients might actually see the trailing * garbage. */ memset(rpkt.data + sendlen, '\0', sizeof(rpkt.data) - (size_t)sendlen); /* * Pad to a multiple of 32 bits */ while (sendlen & 0x3) { sendlen++; } /* * Fill in the packet with the current info */ rpkt.r_m_e_op = CTL_RESPONSE | more | (res_opcode & CTL_OP_MASK); rpkt.count = htons((unsigned short)dlen); rpkt.offset = htons((unsigned short)res_offset); if (res_authenticate) { totlen = sendlen; /* * If we are going to authenticate, then there * is an additional requirement that the MAC * begin on a 64 bit boundary. */ while (totlen & 7) { totlen++; } keyid = htonl(res_keyid); memcpy(datapt, &keyid, sizeof(keyid)); maclen = authencrypt(res_keyid, (uint32_t *)&rpkt, totlen); sendpkt(rmt_addr, lcl_inter, &rpkt, totlen + maclen); } else { sendpkt(rmt_addr, lcl_inter, &rpkt, sendlen); } if (more) numctlfrags++; else numctlresponses++; /* * Set us up for another go around. */ res_frags++; res_offset += dlen; datapt = rpkt.data; } /* * ctl_putdata - write data into the packet, fragmenting and starting * another if this one is full. */ static void ctl_putdata( const char *dp, unsigned int dlen, bool bin /* set to true when data is binary */ ) { unsigned int overhead; unsigned int currentlen; const uint8_t * dataend = &rpkt.data[CTL_MAX_DATA_LEN]; overhead = 0; if (!bin) { datanotbinflag = true; overhead = 3; if (datasent) { *datapt++ = ','; datalinelen++; if ((dlen + (unsigned int)datalinelen + 1) >= MAXDATALINELEN) { *datapt++ = '\r'; *datapt++ = '\n'; datalinelen = 0; } else { *datapt++ = ' '; datalinelen++; } } } /* * Save room for trailing junk */ while (dlen + overhead + datapt > dataend) { /* * Not enough room in this one, flush it out. */ currentlen = MIN(dlen, (unsigned int)(dataend - datapt)); memcpy(datapt, dp, currentlen); datapt += currentlen; dp += currentlen; dlen -= currentlen; datalinelen += (int)currentlen; ctl_flushpkt(CTL_MORE); } memcpy(datapt, dp, dlen); datapt += dlen; datalinelen += (int)dlen; datasent = true; } /* * ctl_putstr - write a tagged string into the response packet * in the form: * * tag="data" * * len is the data length excluding the NUL terminator, * as in ctl_putstr("var", "value", strlen("value")); * The write will be truncated if data contains a NUL, * so don't do that. * * ESR, 2016: Whoever wrote this should be *hurt*. If the string value is * empty, no "=" and no value literal is written, just the bare tag. */ static void ctl_putstr( const char * tag, const char * data, size_t len ) { char buffer[512]; size_t tl = strlen(tag); if (tl >= sizeof(buffer)) return; memcpy(buffer, tag, tl); if (len > 0) snprintf(buffer + tl, sizeof(buffer) - tl, "=\"%s\"", data); ctl_putdata(buffer, (unsigned int)strlen(buffer), false); } /* * ctl_putunqstr - write a tagged string into the response packet * in the form: * * tag=data * * len is the data length excluding the NUL terminator. * data must not contain a comma or whitespace. */ static void ctl_putunqstr( const char * tag, const char * data, size_t len ) { char buffer[512]; char *cp; size_t tl; tl = strlen(tag); if (tl + 1 + len >= sizeof(buffer)) return; memcpy(buffer, tag, tl); cp = buffer + tl; if (len > 0) { *cp++ = '='; memcpy(cp, data, len); cp += len; } ctl_putdata(buffer, (unsigned int)(cp - buffer), false); } /* * ctl_putdblf - write a tagged, signed double into the response packet */ static void ctl_putdblf( const char * tag, int use_f, int precision, double d ) { char *cp; const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; INSIST((size_t)(cp - buffer) < sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), use_f ? "%.*f" : "%.*g", precision, d); cp += strlen(cp); ctl_putdata(buffer, (unsigned)(cp - buffer), false); } /* * ctl_putuint - write a tagged unsigned integer into the response */ static void ctl_putuint( const char *tag, uint64_t uval ) { char *cp; const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; INSIST((cp - buffer) < (int)sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), "%" PRIu64, uval); cp += strlen(cp); ctl_putdata(buffer, (unsigned)( cp - buffer ), false); } /* * ctl_puttime - write a decoded filestamp into the response */ static void ctl_puttime( const char *tag, time_t uval ) { char *cp; const char *cq; char buffer[200]; struct tm tmbuf, *tm = NULL; time_t fstamp = uval; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; tm = gmtime_r(&fstamp, &tmbuf); if (NULL == tm) return; INSIST((cp - buffer) < (int)sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), "%04d-%02d-%02dT%02d:%02dZ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); cp += strlen(cp); ctl_putdata(buffer, (unsigned)( cp - buffer ), false); } /* * ctl_puthex - write a tagged unsigned integer, in hex, into the * response */ static void ctl_puthex( const char *tag, uint64_t uval ) { char *cp; const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; INSIST((cp - buffer) < (int)sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), "0x%" PRIx64, uval); cp += strlen(cp); ctl_putdata(buffer,(unsigned)( cp - buffer ), false); } /* * ctl_putint - write a tagged signed integer into the response */ static void ctl_putint( const char *tag, long ival ) { char *cp; const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; INSIST((cp - buffer) < (int)sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), "%ld", ival); cp += strlen(cp); ctl_putdata(buffer, (unsigned)( cp - buffer ), false); } /* * ctl_putts - write a tagged timestamp, in hex, into the response */ static void ctl_putts( const char *tag, l_fp *ts ) { char *cp; const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; INSIST((size_t)(cp - buffer) < sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), "0x%08x.%08x", (unsigned int)lfpuint(*ts), (unsigned int)lfpfrac(*ts)); cp += strlen(cp); ctl_putdata(buffer, (unsigned)( cp - buffer ), false); } /* * ctl_putadr - write an IP address into the response */ static void ctl_putadr( const char *tag, uint32_t addr32, sockaddr_u *addr ) { char *cp; const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; if (NULL == addr) cq = numtoa(addr32); else cq = socktoa(addr); INSIST((cp - buffer) < (int)sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), "%s", cq); cp += strlen(cp); ctl_putdata(buffer, (unsigned)(cp - buffer), false); } /* * ctl_putrefid - send a uint32_t refid as printable text */ static void ctl_putrefid( const char * tag, uint32_t refid ) { char output[16]; char * optr; char * oplim; char * iptr; char * iplim; char * past_eq = NULL; optr = output; oplim = output + sizeof(output); while (optr < oplim && '\0' != *tag) *optr++ = *tag++; if (optr < oplim) { *optr++ = '='; past_eq = optr; } if (!(optr < oplim)) return; iptr = (char *)&refid; iplim = iptr + sizeof(refid); for ( ; optr < oplim && iptr < iplim && '\0' != *iptr; iptr++, optr++) if (isprint((int)*iptr)) *optr = *iptr; else *optr = '.'; if (!(optr <= oplim)) optr = past_eq; ctl_putdata(output, (unsigned int)(optr - output), false); } /* * ctl_putarray - write a tagged eight element double array into the response */ static void ctl_putarray( const char *tag, double *arr, int start ) { char *cp; const char *cq; char buffer[200]; int i; cp = buffer; cq = tag; while (*cq != '\0' && cp < buffer + sizeof(buffer) - 1) *cp++ = *cq++; *cp++ = '='; i = start; do { if (i == 0) i = NTP_SHIFT; i--; INSIST((cp - buffer) < (int)sizeof(buffer)); snprintf(cp, sizeof(buffer) - (size_t)(cp - buffer), " %.2f", arr[i] * MS_PER_S); cp += strlen(cp); } while (i != start); ctl_putdata(buffer, (unsigned)(cp - buffer), false); } /* * ctl_putsys - output a system variable */ static void ctl_putsys( int varid ) { l_fp tmp; char str[256]; double dtemp; const char *ss; static struct timex ntx; static unsigned long ntp_adjtime_time; /* * CS_K_* variables depend on up-to-date output of ntp_adjtime() */ if (CS_KERN_FIRST <= varid && varid <= CS_KERN_LAST && current_time != ntp_adjtime_time) { ZERO(ntx); if (ntp_adjtime(&ntx) < 0) msyslog(LOG_ERR, "MODE6: ntp_adjtime() for mode 6 query failed: %m"); else ntp_adjtime_time = current_time; } switch (varid) { case CS_LEAP: ctl_putuint(sys_var[CS_LEAP].text, sys_leap); break; case CS_STRATUM: ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum); break; case CS_PRECISION: ctl_putint(sys_var[CS_PRECISION].text, sys_precision); break; case CS_ROOTDELAY: ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay * MS_PER_S); break; case CS_ROOTDISPERSION: ctl_putdbl(sys_var[CS_ROOTDISPERSION].text, sys_rootdisp * MS_PER_S); break; case CS_REFID: if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC) ctl_putadr(sys_var[varid].text, sys_refid, NULL); else ctl_putrefid(sys_var[varid].text, sys_refid); break; case CS_REFTIME: ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime); break; case CS_POLL: ctl_putuint(sys_var[CS_POLL].text, sys_poll); break; case CS_PEERID: if (sys_peer == NULL) ctl_putuint(sys_var[CS_PEERID].text, 0); else ctl_putuint(sys_var[CS_PEERID].text, sys_peer->associd); break; case CS_PEERADR: if (sys_peer != NULL && sys_peer->dstadr != NULL) ss = sockporttoa(&sys_peer->srcadr); else ss = "0.0.0.0:0"; ctl_putunqstr(sys_var[CS_PEERADR].text, ss, strlen(ss)); break; case CS_PEERMODE: { uint64_t u; u = (sys_peer != NULL) ? sys_peer->hmode : MODE_UNSPEC; ctl_putuint(sys_var[CS_PEERMODE].text, u); break; } case CS_OFFSET: ctl_putdbl6(sys_var[CS_OFFSET].text, last_offset * MS_PER_S); break; case CS_DRIFT: /* a.k.a frequency. (s/s), reported as us/s a.k.a. ppm */ ctl_putdbl6(sys_var[CS_DRIFT].text, drift_comp * US_PER_S); break; case CS_JITTER: ctl_putdbl6(sys_var[CS_JITTER].text, sys_jitter * MS_PER_S); break; case CS_ERROR: /* a.k.a clk_jitter (s). output as ms */ ctl_putdbl6(sys_var[CS_ERROR].text, clock_jitter * MS_PER_S); break; case CS_CLOCK: get_systime(&tmp); ctl_putts(sys_var[CS_CLOCK].text, &tmp); break; case CS_PROCESSOR: ctl_putstr(sys_var[CS_PROCESSOR].text, utsnamebuf.machine, strlen(utsnamebuf.machine)); break; case CS_SYSTEM: snprintf(str, sizeof(str), "%s/%s", utsnamebuf.sysname, utsnamebuf.release); ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str)); break; case CS_VERSION: ss = ntpd_version(); ctl_putstr(sys_var[CS_VERSION].text, ss, strlen(ss)); break; case CS_STABIL: /* a.k.a clk_wander (s/s), output as us/s */ ctl_putdbl6(sys_var[CS_STABIL].text, clock_stability * US_PER_S); break; case CS_VARLIST: { char buf[CTL_MAX_DATA_LEN]; //buffPointer, firstElementPointer, buffEndPointer char *buffp, *buffend; bool firstVarName; const char *ss1; size_t len; const struct ctl_var *k; buffp = buf; buffend = buf + sizeof(buf); if (buffp + strlen(sys_var[CS_VARLIST].text) + 4 > buffend) break; /* really long var name */ snprintf(buffp, sizeof(buf), "%s=\"",sys_var[CS_VARLIST].text); buffp += strlen(buffp); firstVarName = true; for (k = sys_var; !(k->flags & EOV); k++) { if (k->flags & PADDING) continue; len = strlen(k->text); if (buffp + len + 1 >= buffend) break; if (!firstVarName) *buffp++ = ','; else firstVarName = false; memcpy(buffp, k->text, len); buffp += len; } for (k = ext_sys_var; k && !(k->flags & EOV); k++) { if (k->flags & PADDING) continue; if (NULL == k->text) continue; ss1 = strchr(k->text, '='); if (NULL == ss1) len = strlen(k->text); else len = (size_t)(ss1 - k->text); if (buffp + len + 1 >= buffend) break; if (firstVarName) { *buffp++ = ','; firstVarName = false; } memcpy(buffp, k->text, len); buffp += len; } if (buffp + 2 >= buffend) break; *buffp++ = '"'; *buffp = '\0'; ctl_putdata(buf, (unsigned)( buffp - buf ), false); break; } case CS_TAI: if (sys_tai > 0) ctl_putuint(sys_var[CS_TAI].text, sys_tai); break; case CS_LEAPTAB: { leap_signature_t lsig; leapsec_getsig(&lsig); if (lsig.ttime > 0) ctl_puttime(sys_var[CS_LEAPTAB].text, lsig.ttime); break; } case CS_LEAPEND: { leap_signature_t lsig; leapsec_getsig(&lsig); if (lsig.etime > 0) ctl_puttime(sys_var[CS_LEAPEND].text, lsig.etime); break; } #ifdef ENABLE_LEAP_SMEAR case CS_LEAPSMEARINTV: if (leap_smear_intv > 0) ctl_putuint(sys_var[CS_LEAPSMEARINTV].text, leap_smear_intv); break; case CS_LEAPSMEAROFFS: if (leap_smear_intv > 0) ctl_putdbl(sys_var[CS_LEAPSMEAROFFS].text, leap_smear.doffset * MS_PER_S); break; #endif /* ENABLE_LEAP_SMEAR */ case CS_RATE: ctl_putuint(sys_var[CS_RATE].text, ntp_minpoll); break; case CS_MRU_ENABLED: ctl_puthex(sys_var[varid].text, mon_enabled); break; case CS_MRU_DEPTH: ctl_putuint(sys_var[varid].text, mru_entries); break; case CS_MRU_MEM: { uint64_t u; u = mru_entries * sizeof(mon_entry); u = (u+512)/1024; ctl_putuint(sys_var[varid].text, u); break; } case CS_MRU_DEEPEST: ctl_putuint(sys_var[varid].text, mru_peakentries); break; case CS_MRU_MINDEPTH: ctl_putuint(sys_var[varid].text, mru_mindepth); break; case CS_MRU_MAXAGE: ctl_putint(sys_var[varid].text, mru_maxage); break; case CS_MRU_MINAGE: ctl_putint(sys_var[varid].text, mru_minage); break; case CS_MRU_MAXDEPTH: ctl_putuint(sys_var[varid].text, mru_maxdepth); break; case CS_MRU_MAXMEM: { uint64_t u; u = mru_maxdepth * sizeof(mon_entry); u = (u+512)/1024; ctl_putuint(sys_var[varid].text, u); break; } case CS_MRU_EXISTS: ctl_putuint(sys_var[varid].text, mru_exists); break; case CS_MRU_NEW: ctl_putuint(sys_var[varid].text, mru_new); break; case CS_MRU_RECYCLEOLD: ctl_putuint(sys_var[varid].text, mru_recycleold); break; case CS_MRU_RECYCLEFULL: ctl_putuint(sys_var[varid].text, mru_recyclefull); break; case CS_MRU_NONE: ctl_putuint(sys_var[varid].text, mru_none); break; case CS_MRU_OLDEST_AGE: { l_fp now; get_systime(&now); ctl_putuint(sys_var[varid].text, mon_get_oldest_age(now)); break; } case CS_SS_UPTIME: ctl_putuint(sys_var[varid].text, current_time); break; case CS_SS_RESET: ctl_putuint(sys_var[varid].text, current_time - sys_stattime); break; case CS_SS_RECEIVED: ctl_putuint(sys_var[varid].text, sys_received); break; case CS_SS_THISVER: ctl_putuint(sys_var[varid].text, sys_newversion); break; case CS_SS_OLDVER: ctl_putuint(sys_var[varid].text, sys_oldversion); break; case CS_SS_BADFORMAT: ctl_putuint(sys_var[varid].text, sys_badlength); break; case CS_SS_BADAUTH: ctl_putuint(sys_var[varid].text, sys_badauth); break; case CS_SS_DECLINED: ctl_putuint(sys_var[varid].text, sys_declined); break; case CS_SS_RESTRICTED: ctl_putuint(sys_var[varid].text, sys_restricted); break; case CS_SS_LIMITED: ctl_putuint(sys_var[varid].text, sys_limitrejected); break; case CS_SS_KODSENT: ctl_putuint(sys_var[varid].text, sys_kodsent); break; case CS_SS_PROCESSED: ctl_putuint(sys_var[varid].text, sys_processed); break; case CS_AUTHDELAY: dtemp = lfptod(sys_authdelay); ctl_putdbl(sys_var[varid].text, dtemp * MS_PER_S); break; case CS_AUTHKEYS: ctl_putuint(sys_var[varid].text, authnumkeys); break; case CS_AUTHFREEK: ctl_putuint(sys_var[varid].text, (unsigned long)authnumfreekeys); break; case CS_AUTHKLOOKUPS: ctl_putuint(sys_var[varid].text, authkeylookups); break; case CS_AUTHKNOTFOUND: ctl_putuint(sys_var[varid].text, authkeynotfound); break; case CS_AUTHKUNCACHED: ctl_putuint(sys_var[varid].text, authkeyuncached); break; case CS_AUTHKEXPIRED: /* historical relic - autokey used to expire keys */ ctl_putuint(sys_var[varid].text, 0); break; case CS_AUTHENCRYPTS: ctl_putuint(sys_var[varid].text, authencryptions); break; case CS_AUTHDECRYPTS: ctl_putuint(sys_var[varid].text, authdecryptions); break; case CS_AUTHRESET: ctl_putuint(sys_var[varid].text, current_time - auth_timereset); break; /* * CTL_IF_KERNPPS() puts a zero if kernel hard PPS is not * active, otherwise calls putfunc with args. */ # define CTL_IF_KERNPPS(putfunc, args) \ if (0 == ntx.shift) \ ctl_putint(sys_var[varid].text, 0); \ else \ putfunc args /* no trailing ; */ case CS_K_OFFSET: ctl_putdblf(sys_var[varid].text, 0, -1, ntp_error_in_seconds(ntx.offset) * MS_PER_S); break; case CS_K_FREQ: ctl_putsfp(sys_var[varid].text, ntx.freq); break; case CS_K_MAXERR: ctl_putdblf(sys_var[varid].text, 0, 6, ntp_error_in_seconds(ntx.maxerror) * MS_PER_S); break; case CS_K_ESTERR: ctl_putdblf(sys_var[varid].text, 0, 6, ntp_error_in_seconds(ntx.esterror) * MS_PER_S); break; case CS_K_STFLAGS: ss = k_st_flags((uint32_t)ntx.status); ctl_putstr(sys_var[varid].text, ss, strlen(ss)); break; case CS_K_TIMECONST: ctl_putint(sys_var[varid].text, ntx.constant); break; case CS_K_PRECISION: ctl_putdblf(sys_var[varid].text, 0, 6, ntp_error_in_seconds(ntx.precision) * MS_PER_S); break; case CS_K_FREQTOL: ctl_putsfp(sys_var[varid].text, ntx.tolerance); break; case CS_K_PPS_FREQ: CTL_IF_KERNPPS( ctl_putsfp, (sys_var[varid].text, ntx.ppsfreq) ); break; case CS_K_PPS_STABIL: CTL_IF_KERNPPS( ctl_putsfp, (sys_var[varid].text, ntx.stabil) ); break; case CS_K_PPS_JITTER: CTL_IF_KERNPPS( ctl_putdbl, (sys_var[varid].text, ntp_error_in_seconds(ntx.jitter) * MS_PER_S) ); break; case CS_K_PPS_CALIBDUR: CTL_IF_KERNPPS( ctl_putint, (sys_var[varid].text, 1 << ntx.shift) ); break; case CS_K_PPS_CALIBS: CTL_IF_KERNPPS( ctl_putint, (sys_var[varid].text, ntx.calcnt) ); break; case CS_K_PPS_CALIBERRS: CTL_IF_KERNPPS( ctl_putint, (sys_var[varid].text, ntx.errcnt) ); break; case CS_K_PPS_JITEXC: CTL_IF_KERNPPS( ctl_putint, (sys_var[varid].text, ntx.jitcnt) ); break; case CS_K_PPS_STBEXC: CTL_IF_KERNPPS( ctl_putint, (sys_var[varid].text, ntx.stbcnt) ); break; case CS_IOSTATS_RESET: ctl_putuint(sys_var[varid].text, current_time - io_timereset); break; case CS_TOTAL_RBUF: ctl_putuint(sys_var[varid].text, total_recvbuffs()); break; case CS_FREE_RBUF: ctl_putuint(sys_var[varid].text, free_recvbuffs()); break; case CS_USED_RBUF: ctl_putuint(sys_var[varid].text, full_recvbuffs()); break; case CS_RBUF_LOWATER: ctl_putuint(sys_var[varid].text, lowater_additions()); break; case CS_IO_DROPPED: ctl_putuint(sys_var[varid].text, packets_dropped); break; case CS_IO_IGNORED: ctl_putuint(sys_var[varid].text, packets_ignored); break; case CS_IO_RECEIVED: ctl_putuint(sys_var[varid].text, packets_received); break; case CS_IO_SENT: ctl_putuint(sys_var[varid].text, packets_sent); break; case CS_IO_SENDFAILED: ctl_putuint(sys_var[varid].text, packets_notsent); break; case CS_IO_WAKEUPS: ctl_putuint(sys_var[varid].text, handler_calls); break; case CS_IO_GOODWAKEUPS: ctl_putuint(sys_var[varid].text, handler_pkts); break; case CS_TIMERSTATS_RESET: ctl_putuint(sys_var[varid].text, current_time - timer_timereset); break; case CS_TIMER_OVERRUNS: ctl_putuint(sys_var[varid].text, alarm_overflow); break; case CS_TIMER_XMTS: ctl_putuint(sys_var[varid].text, timer_xmtcalls); break; case CS_FUZZ: /* a.k.a. fuzz (s), output in ms */ ctl_putdbl6(sys_var[varid].text, sys_fuzz * MS_PER_S); break; case CS_WANDER_THRESH: ctl_putdbl(sys_var[varid].text, wander_threshold * US_PER_S); break; case CS_TICK: /* a.k.a. sys_tick (s), output in ms */ ctl_putdbl6(sys_var[varid].text, sys_tick * MS_PER_S); break; case CS_NUMCTLREQ: ctl_putuint(sys_var[varid].text, numctlreq); break; case CS_ROOTDISTANCE: ctl_putdbl(sys_var[CS_ROOTDISTANCE].text, sys_rootdist * MS_PER_S); break; default: /* huh? */ break; } } /* * ctl_putpeer - output a peer variable */ static void ctl_putpeer( int id, struct peer *p ) { char buf[CTL_MAX_DATA_LEN]; char *s; char *t; char *be; size_t sz; const struct ctl_var *k; switch (id) { case CP_CONFIG: ctl_putuint(peer_var[id].text, !(FLAG_PREEMPT & p->cfg.flags)); break; case CP_AUTHENABLE: ctl_putuint(peer_var[id].text, !(p->cfg.peerkey)); break; case CP_AUTHENTIC: ctl_putuint(peer_var[id].text, !!(FLAG_AUTHENTIC & p->cfg.flags)); break; case CP_SRCADR: ctl_putadr(peer_var[id].text, 0, &p->srcadr); break; case CP_SRCPORT: ctl_putuint(peer_var[id].text, SRCPORT(&p->srcadr)); break; case CP_SRCHOST: if (p->hostname != NULL) ctl_putstr(peer_var[id].text, p->hostname, strlen(p->hostname)); #ifdef REFCLOCK if (p->procptr != NULL) { char buf1[256]; strlcpy(buf1, refclock_name(p), sizeof(buf1)); ctl_putstr(peer_var[id].text, buf1, strlen(buf1)); } #endif /* REFCLOCK */ break; case CP_DSTADR: ctl_putadr(peer_var[id].text, 0, (p->dstadr != NULL) ? &p->dstadr->sin : NULL); break; case CP_DSTPORT: ctl_putuint(peer_var[id].text, (p->dstadr != NULL) ? SRCPORT(&p->dstadr->sin) : 0); break; case CP_IN: if (p->r21 > 0.) ctl_putdbl(peer_var[id].text, p->r21 / MS_PER_S); break; case CP_OUT: if (p->r34 > 0.) ctl_putdbl(peer_var[id].text, p->r34 / MS_PER_S); break; case CP_RATE: ctl_putuint(peer_var[id].text, p->throttle); break; case CP_LEAP: ctl_putuint(peer_var[id].text, p->leap); break; case CP_HMODE: ctl_putuint(peer_var[id].text, p->hmode); break; case CP_STRATUM: ctl_putuint(peer_var[id].text, p->stratum); break; case CP_PPOLL: ctl_putuint(peer_var[id].text, p->ppoll); break; case CP_HPOLL: ctl_putuint(peer_var[id].text, p->hpoll); break; case CP_PRECISION: ctl_putint(peer_var[id].text, p->precision); break; case CP_ROOTDELAY: ctl_putdbl(peer_var[id].text, p->rootdelay * MS_PER_S); break; case CP_ROOTDISPERSION: ctl_putdbl(peer_var[id].text, p->rootdisp * MS_PER_S); break; case CP_REFID: #ifdef REFCLOCK if (p->cfg.flags & FLAG_REFCLOCK) { ctl_putrefid(peer_var[id].text, p->refid); break; } #endif if (p->stratum > 1 && p->stratum < STRATUM_UNSPEC) ctl_putadr(peer_var[id].text, p->refid, NULL); else ctl_putrefid(peer_var[id].text, p->refid); break; case CP_REFTIME: ctl_putts(peer_var[id].text, &p->reftime); break; case CP_REC: ctl_putts(peer_var[id].text, &p->dst); break; case CP_XMT: ctl_putts(peer_var[id].text, &p->xmt); break; case CP_BIAS: if ( !D_ISZERO_NS(p->cfg.bias) ) ctl_putdbl(peer_var[id].text, p->cfg.bias); break; case CP_REACH: ctl_puthex(peer_var[id].text, p->reach); break; case CP_FLASH: ctl_puthex(peer_var[id].text, p->flash); break; case CP_MODE: #ifdef REFCLOCK if (p->cfg.flags & FLAG_REFCLOCK) { ctl_putuint(peer_var[id].text, p->cfg.mode); break; } #endif break; case CP_UNREACH: ctl_putuint(peer_var[id].text, (unsigned long)p->unreach); break; case CP_TIMER: ctl_putuint(peer_var[id].text, p->nextdate - current_time); break; case CP_DELAY: ctl_putdbl6(peer_var[id].text, p->delay * MS_PER_S); break; case CP_OFFSET: ctl_putdbl6(peer_var[id].text, p->offset * MS_PER_S); break; case CP_JITTER: ctl_putdbl6(peer_var[id].text, p->jitter * MS_PER_S); break; case CP_DISPERSION: ctl_putdbl6(peer_var[id].text, p->disp * MS_PER_S); break; case CP_KEYID: if (p->cfg.peerkey > NTP_MAXKEY) ctl_puthex(peer_var[id].text, p->cfg.peerkey); else ctl_putuint(peer_var[id].text, p->cfg.peerkey); break; case CP_FILTDELAY: ctl_putarray(peer_var[id].text, p->filter_delay, p->filter_nextpt); break; case CP_FILTOFFSET: ctl_putarray(peer_var[id].text, p->filter_offset, p->filter_nextpt); break; case CP_FILTERROR: ctl_putarray(peer_var[id].text, p->filter_disp, p->filter_nextpt); break; case CP_PMODE: ctl_putuint(peer_var[id].text, p->pmode); break; case CP_RECEIVED: ctl_putuint(peer_var[id].text, p->received); break; case CP_SENT: ctl_putuint(peer_var[id].text, p->sent); break; case CP_VARLIST: s = buf; be = buf + sizeof(buf); if (strlen(peer_var[id].text) + 4 > sizeof(buf)) break; /* really long var name */ snprintf(s, sizeof(buf), "%s=\"", peer_var[id].text); s += strlen(s); t = s; for (k = peer_var; !(EOV & k->flags); k++) { if (PADDING & k->flags) continue; sz = strlen(k->text); if (s + sz + 1 >= be) break; if (s != t) *s++ = ','; memcpy(s, k->text, sz); s += sz; } if (s + 2 < be) { *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned int)(s - buf), false); } break; case CP_TIMEREC: ctl_putuint(peer_var[id].text, current_time - p->timereceived); break; case CP_TIMEREACH: ctl_putuint(peer_var[id].text, current_time - p->timereachable); break; case CP_BADAUTH: ctl_putuint(peer_var[id].text, p->badauth); break; case CP_BOGUSORG: ctl_putuint(peer_var[id].text, p->bogusorg); break; case CP_OLDPKT: ctl_putuint(peer_var[id].text, p->oldpkt); break; case CP_SELDISP: ctl_putuint(peer_var[id].text, p->seldisptoolarge); break; case CP_SELBROKEN: ctl_putuint(peer_var[id].text, p->selbroken); break; case CP_CANDIDATE: ctl_putuint(peer_var[id].text, p->status); break; default: break; } } #ifdef REFCLOCK /* * ctl_putclock - output clock variables */ static void ctl_putclock( int id, struct refclockstat *pcs, int mustput ) { char buf[CTL_MAX_DATA_LEN]; char *s, *t, *be; const char *ss; size_t sz; const struct ctl_var *k; switch (id) { case CC_NAME: if (pcs->clockname == NULL || *(pcs->clockname) == '\0') { if (mustput) ctl_putstr(clock_var[id].text, "", 0); } else { ctl_putstr(clock_var[id].text, pcs->clockname, strlen(pcs->clockname)); } break; case CC_TIMECODE: ctl_putstr(clock_var[id].text, pcs->p_lastcode, (unsigned)pcs->lencode); break; case CC_POLL: ctl_putuint(clock_var[id].text, pcs->polls); break; case CC_NOREPLY: ctl_putuint(clock_var[id].text, pcs->noresponse); break; case CC_BADFORMAT: ctl_putuint(clock_var[id].text, pcs->badformat); break; case CC_BADDATA: ctl_putuint(clock_var[id].text, pcs->baddata); break; case CC_FUDGETIME1: if (mustput || (pcs->haveflags & CLK_HAVETIME1)) ctl_putdbl(clock_var[id].text, pcs->fudgetime1 * MS_PER_S); break; case CC_FUDGETIME2: if (mustput || (pcs->haveflags & CLK_HAVETIME2)) ctl_putdbl(clock_var[id].text, pcs->fudgetime2 * MS_PER_S); break; case CC_FUDGEVAL1: if (mustput || (pcs->haveflags & CLK_HAVEVAL1)) ctl_putint(clock_var[id].text, pcs->fudgeval1); break; case CC_FUDGEVAL2: if (mustput || (pcs->haveflags & CLK_HAVEVAL2)) { if (pcs->fudgeval1 > 1) ctl_putadr(clock_var[id].text, pcs->fudgeval2, NULL); else ctl_putrefid(clock_var[id].text, pcs->fudgeval2); } break; case CC_FLAGS: ctl_putuint(clock_var[id].text, pcs->flags); break; case CC_DEVICE: if (pcs->clockdesc == NULL || *(pcs->clockdesc) == '\0') { if (mustput) ctl_putstr(clock_var[id].text, "", 0); } else { ctl_putstr(clock_var[id].text, pcs->clockdesc, strlen(pcs->clockdesc)); } break; case CC_VARLIST: s = buf; be = buf + sizeof(buf); if (strlen(clock_var[CC_VARLIST].text) + 4 > sizeof(buf)) break; /* really long var name */ snprintf(s, sizeof(buf), "%s=\"", clock_var[CC_VARLIST].text); s += strlen(s); t = s; for (k = clock_var; !(EOV & k->flags); k++) { if (PADDING & k->flags) continue; sz = strlen(k->text); if (s + sz + 1 >= be) break; if (s != t) *s++ = ','; memcpy(s, k->text, sz); s += sz; } for (k = pcs->kv_list; k && !(EOV & k->flags); k++) { if (PADDING & k->flags) continue; ss = k->text; if (NULL == ss) continue; while (*ss && *ss != '=') ss++; sz = (size_t)(ss - k->text); if (s + sz + 1 >= be) break; if (s != t) *s++ = ','; memcpy(s, k->text, sz); s += sz; *s = '\0'; } if (s + 2 >= be) break; *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned)(s - buf), false); break; default: /* huh? */ break; } } #endif /* * ctl_getitem - get the next data item from the incoming packet */ static const struct ctl_var * ctl_getitem( const struct ctl_var *var_list, char **data ) { /* [Bug 3008] First check the packet data sanity, then search * the key. This improves the consistency of result values: If * the result is NULL once, it will never be EOV again for this * packet; If it's EOV, it will never be NULL again until the * variable is found and processed in a given 'var_list'. (That * is, a result is returned that is neither NULL nor EOV). */ static const struct ctl_var eol = { 0, EOV, NULL }; static char buf[128]; static u_long quiet_until; const struct ctl_var *v; char *cp; char *tp; /* * Part One: Validate the packet state */ /* Delete leading commas and white space */ while (reqpt < reqend && (*reqpt == ',' || isspace((unsigned char)*reqpt))) reqpt++; if (reqpt >= reqend) return NULL; /* Scan the string in the packet until we hit comma or * EoB. Register position of first '=' on the fly. */ for (tp = NULL, cp = reqpt; cp != reqend; ++cp) { if (*cp == '=' && tp == NULL) tp = cp; if (*cp == ',') break; } /* Process payload, if any. */ *data = NULL; if (NULL != tp) { /* eventually strip white space from argument. */ const char *plhead = tp + 1; /* skip the '=' */ const char *pltail = cp; size_t plsize; while (plhead != pltail && isspace((u_char)plhead[0])) ++plhead; while (plhead != pltail && isspace((u_char)pltail[-1])) --pltail; /* check payload size, terminate packet on overflow */ plsize = (size_t)(pltail - plhead); if (plsize >= sizeof(buf)) goto badpacket; /* copy data, NUL terminate, and set result data ptr */ memcpy(buf, plhead, plsize); buf[plsize] = '\0'; *data = buf; } else { /* no payload, current end --> current name termination */ tp = cp; } /* Part Two * * Now we're sure that the packet data itself is sane. Scan the * list now. Make sure a NULL list is properly treated by * returning a synthetic End-Of-Values record. We must not * return NULL pointers after this point, or the behaviour would * become inconsistent if called several times with different * variable lists after an EoV was returned. (Such a behavior * actually caused Bug 3008.) */ if (NULL == var_list) return &eol; for (v = var_list; !(EOV & v->flags); ++v) if (!(PADDING & v->flags)) { /* Check if the var name matches the buffer. The * name is bracketed by [reqpt..tp] and not NUL * terminated, and it contains no '=' char. The * lookup value IS NUL-terminated but might * include a '='... We have to look out for * that! */ const char *sp1 = reqpt; const char *sp2 = v->text; /* [Bug 3412] do not compare past NUL byte in name */ while ( (sp1 != tp) && ('\0' != *sp2) && (*sp1 == *sp2)) { ++sp1; ++sp2; } if (sp1 == tp && (*sp2 == '\0' || *sp2 == '=')) break; } /* See if we have found a valid entry or not. If found, advance * the request pointer for the next round; if not, clear the * data pointer so we have no dangling garbage here. */ if (EOV & v->flags) *data = NULL; else reqpt = cp + (cp != reqend); return v; badpacket: /*TODO? somehow indicate this packet was bad, apart from syslog? */ numctlbadpkts++; NLOG(NLOG_SYSEVENT) if (quiet_until <= current_time) { quiet_until = current_time + 300; msyslog(LOG_WARNING, "Possible 'ntpdx' exploit from %s#%u (possibly spoofed)", socktoa(rmt_addr), SRCPORT(rmt_addr)); } reqpt = reqend; /* never again for this packet! */ return NULL; } /* * control_unspec - response to an unspecified op-code */ /*ARGSUSED*/ static void control_unspec( struct recvbuf *rbufp, int restrict_mask ) { struct peer *peer; UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); /* * What is an appropriate response to an unspecified op-code? * I return no errors and no data, unless a specified association * doesn't exist. */ if (res_associd) { peer = findpeerbyassoc(res_associd); if (NULL == peer) { ctl_error(CERR_BADASSOC); return; } rpkt.status = htons(ctlpeerstatus(peer)); } else rpkt.status = htons(ctlsysstatus()); ctl_flushpkt(0); } /* * read_status - return either a list of associd's, or a particular * peer's status. */ /*ARGSUSED*/ static void read_status( struct recvbuf *rbufp, int restrict_mask ) { struct peer *peer; const uint8_t *cp; size_t n; /* a_st holds association ID, status pairs alternating */ unsigned short a_st[CTL_MAX_DATA_LEN / sizeof(unsigned short)]; UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); DPRINT(3, ("read_status: ID %d\n", res_associd)); /* * Two choices here. If the specified association ID is * zero we return all known association ID's. Otherwise * we return a bunch of stuff about the particular peer. */ if (res_associd) { peer = findpeerbyassoc(res_associd); if (NULL == peer) { ctl_error(CERR_BADASSOC); return; } rpkt.status = htons(ctlpeerstatus(peer)); if (res_authokay) peer->num_events = 0; /* * For now, output everything we know about the * peer. May be more selective later. */ for (cp = def_peer_var; *cp != 0; cp++) ctl_putpeer((int)*cp, peer); ctl_flushpkt(0); return; } n = 0; rpkt.status = htons(ctlsysstatus()); for (peer = peer_list; peer != NULL; peer = peer->p_link) { a_st[n++] = htons(peer->associd); a_st[n++] = htons(ctlpeerstatus(peer)); /* two entries each loop iteration, so n + 1 */ if (n + 1 >= COUNTOF(a_st)) { ctl_putdata((void *)a_st, n * sizeof(a_st[0]), true); n = 0; } } if (n) ctl_putdata((void *)a_st, n * sizeof(a_st[0]), true); ctl_flushpkt(0); } /* * read_peervars - half of read_variables() implementation */ static void read_peervars(void) { const struct ctl_var *v; struct peer *peer; const uint8_t *cp; size_t i; char * valuep; bool wants[CP_MAXCODE + 1]; bool gotvar; /* * Wants info for a particular peer. See if we know * the guy. */ peer = findpeerbyassoc(res_associd); if (NULL == peer) { ctl_error(CERR_BADASSOC); return; } rpkt.status = htons(ctlpeerstatus(peer)); if (res_authokay) peer->num_events = 0; ZERO(wants); gotvar = false; while (NULL != (v = ctl_getitem(peer_var, &valuep))) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); return; } INSIST(v->code < COUNTOF(wants)); wants[v->code] = 1; gotvar = true; } if (gotvar) { for (i = 1; i < COUNTOF(wants); i++) if (wants[i]) ctl_putpeer((int)i, peer); } else for (cp = def_peer_var; *cp != 0; cp++) ctl_putpeer((int)*cp, peer); ctl_flushpkt(0); } /* * read_sysvars - half of read_variables() implementation */ static void read_sysvars(void) { const struct ctl_var *v; struct ctl_var *kv; unsigned int n; bool gotvar; const uint8_t *cs; char * valuep; const char * pch; uint8_t *wants; size_t wants_count; /* * Wants system variables. Figure out which he wants * and give them to him. */ rpkt.status = htons(ctlsysstatus()); if (res_authokay) ctl_sys_num_events = 0; wants_count = CS_MAXCODE + 1 + count_var(ext_sys_var); wants = emalloc_zero(wants_count); gotvar = false; while (NULL != (v = ctl_getitem(sys_var, &valuep))) { if (!(EOV & v->flags)) { INSIST(v->code < wants_count); wants[v->code] = 1; gotvar = true; } else { v = ctl_getitem(ext_sys_var, &valuep); if (NULL == v) { ctl_error(CERR_BADVALUE); free(wants); return; } if (EOV & v->flags) { ctl_error(CERR_UNKNOWNVAR); free(wants); return; } n = v->code + CS_MAXCODE + 1; INSIST(n < wants_count); wants[n] = 1; gotvar = true; } } if (gotvar) { for (n = 1; n <= CS_MAXCODE; n++) if (wants[n]) ctl_putsys((int)n); for (n = 0; n + CS_MAXCODE + 1 < wants_count; n++) if (wants[n + CS_MAXCODE + 1]) { pch = ext_sys_var[n].text; ctl_putdata(pch, strlen(pch), false); } } else { for (cs = def_sys_var; *cs != 0; cs++) ctl_putsys((int)*cs); for (kv = ext_sys_var; kv && !(EOV & kv->flags); kv++) if (DEF & kv->flags) ctl_putdata(kv->text, strlen(kv->text), false); } free(wants); ctl_flushpkt(0); } /* * read_variables - return the variables the caller asks for */ /*ARGSUSED*/ static void read_variables( struct recvbuf *rbufp, int restrict_mask ) { UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); if (res_associd) read_peervars(); else read_sysvars(); } /* * write_variables - write into variables. We only allow leap bit * writing this way. */ /*ARGSUSED*/ static void write_variables( struct recvbuf *rbufp, int restrict_mask ) { const struct ctl_var *v; int ext_var; char *valuep; long val; size_t octets; char *vareqv; const char *t; char *tt; UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); val = 0; /* * If he's trying to write into a peer tell him no way */ if (res_associd != 0) { ctl_error(CERR_PERMISSION); return; } /* * Set status */ rpkt.status = htons(ctlsysstatus()); /* * Look through the variables. Dump out at the first sign of * trouble. */ while ((v = ctl_getitem(sys_var, &valuep)) != 0) { ext_var = 0; if (v->flags & EOV) { if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); return; } ext_var = 1; } else { break; } } if (!(v->flags & CAN_WRITE)) { ctl_error(CERR_PERMISSION); return; } errno = 0; if (!ext_var && (*valuep == '\0' || (val = strtol(valuep, NULL, 10), errno != 0))) { ctl_error(CERR_BADFMT); return; } if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) { ctl_error(CERR_BADVALUE); return; } if (ext_var) { octets = strlen(v->text) + strlen(valuep) + 2; vareqv = emalloc(octets); tt = vareqv; t = v->text; while (*t && *t != '=') *tt++ = *t++; *tt++ = '='; memcpy(tt, valuep, 1 + strlen(valuep)); set_sys_var(vareqv, 1 + strlen(vareqv), v->flags); free(vareqv); } else { ctl_error(CERR_UNSPEC); /* really */ return; } } /* * If we got anything, do it. xxx nothing to do *** */ /* if (leapind != ~0 || leapwarn != ~0) { if (!leap_setleap((int)leapind, (int)leapwarn)) { ctl_error(CERR_PERMISSION); return; } } */ ctl_flushpkt(0); } /* * configure() processes ntpq :config/config-from-file, allowing * generic runtime reconfiguration. */ static void configure( struct recvbuf *rbufp, int restrict_mask ) { size_t data_count; int retval; bool replace_nl; /* I haven't yet implemented changes to an existing association. * Hence check if the association id is 0 */ if (res_associd != 0) { ctl_error(CERR_BADVALUE); return; } if (RES_NOMODIFY & restrict_mask) { snprintf(remote_config.err_msg, sizeof(remote_config.err_msg), "runtime configuration prohibited by restrict ... nomodify"); ctl_putdata(remote_config.err_msg, strlen(remote_config.err_msg), false); ctl_flushpkt(0); NLOG(NLOG_SYSINFO) msyslog(LOG_NOTICE, "MODE6: runtime config from %s rejected due to nomodify restriction", socktoa(&rbufp->recv_srcadr)); sys_restricted++; return; } /* Initialize the remote config buffer */ data_count = (size_t)(reqend - reqpt); if (data_count > sizeof(remote_config.buffer) - 2) { snprintf(remote_config.err_msg, sizeof(remote_config.err_msg), "runtime configuration failed: request too long"); ctl_putdata(remote_config.err_msg, strlen(remote_config.err_msg), false); ctl_flushpkt(0); msyslog(LOG_NOTICE, "MODE6: runtime config from %s rejected: request too long", socktoa(&rbufp->recv_srcadr)); return; } memcpy(remote_config.buffer, reqpt, data_count); if (data_count > 0 && '\n' != remote_config.buffer[data_count - 1]) remote_config.buffer[data_count++] = '\n'; remote_config.buffer[data_count] = '\0'; remote_config.pos = 0; remote_config.err_pos = 0; remote_config.no_errors = 0; /* do not include terminating newline in log */ if (data_count > 0 && '\n' == remote_config.buffer[data_count - 1]) { remote_config.buffer[data_count - 1] = '\0'; replace_nl = true; } else { replace_nl = false; } DPRINT(1, ("Got Remote Configuration Command: %s\n", remote_config.buffer)); msyslog(LOG_NOTICE, "MODE6: %s config: %s", socktoa(&rbufp->recv_srcadr), remote_config.buffer); if (replace_nl) remote_config.buffer[data_count - 1] = '\n'; config_remotely(&rbufp->recv_srcadr); /* * Check if errors were reported. If not, output 'Config * Succeeded'. Else output the error count. It would be nice * to output any parser error messages. */ if (0 == remote_config.no_errors) { retval = snprintf(remote_config.err_msg, sizeof(remote_config.err_msg), "Config Succeeded"); if (retval > 0) remote_config.err_pos += retval; } ctl_putdata(remote_config.err_msg, (unsigned int)remote_config.err_pos, false); ctl_flushpkt(0); DPRINT(1, ("Reply: %s\n", remote_config.err_msg)); if (remote_config.no_errors > 0) msyslog(LOG_NOTICE, "MODE6: %d error in %s config", remote_config.no_errors, socktoa(&rbufp->recv_srcadr)); } /* * derive_nonce - generate client-address-specific nonce value * associated with a given timestamp. */ static uint32_t derive_nonce( sockaddr_u * addr, uint32_t ts_i, uint32_t ts_f ) { static uint32_t salt[4]; static unsigned long last_salt_update; union d_tag { uint8_t digest[EVP_MAX_MD_SIZE]; uint32_t extract; } d; EVP_MD_CTX *ctx; unsigned int len; while (!salt[0] || current_time - last_salt_update >= SECSPERHR) { salt[0] = (uint32_t)ntp_random(); salt[1] = (uint32_t)ntp_random(); salt[2] = (uint32_t)ntp_random(); salt[3] = (uint32_t)ntp_random(); last_salt_update = current_time; } ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, salt, sizeof(salt)); EVP_DigestUpdate(ctx, &ts_i, sizeof(ts_i)); EVP_DigestUpdate(ctx, &ts_f, sizeof(ts_f)); if (IS_IPV4(addr)) EVP_DigestUpdate(ctx, &SOCK_ADDR4(addr), sizeof(SOCK_ADDR4(addr))); else EVP_DigestUpdate(ctx, &SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr))); EVP_DigestUpdate(ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr))); EVP_DigestUpdate(ctx, salt, sizeof(salt)); EVP_DigestFinal_ex(ctx, d.digest, &len); EVP_MD_CTX_destroy(ctx); return d.extract; } /* * generate_nonce - generate client-address-specific nonce string. */ static void generate_nonce( struct recvbuf * rbufp, char * nonce, size_t nonce_octets ) { uint32_t derived; derived = derive_nonce(&rbufp->recv_srcadr, lfpuint(rbufp->recv_time), lfpfrac(rbufp->recv_time)); snprintf(nonce, nonce_octets, "%08x%08x%08x", lfpuint(rbufp->recv_time), lfpfrac(rbufp->recv_time), derived); } /* * validate_nonce - validate client-address-specific nonce string. * * Returns true if the local calculation of the nonce matches the * client-provided value and the timestamp is recent enough. */ static int validate_nonce( const char * pnonce, struct recvbuf * rbufp ) { unsigned int ts_i; unsigned int ts_f; l_fp ts; l_fp now_delta; unsigned int supposed; unsigned int derived; if (3 != sscanf(pnonce, "%08x%08x%08x", &ts_i, &ts_f, &supposed)) return false; ts = lfpinit_u(ts_i, (uint32_t)ts_f); derived = derive_nonce(&rbufp->recv_srcadr, lfpuint(ts), lfpfrac(ts)); get_systime(&now_delta); now_delta -= ts; return (supposed == derived && lfpuint(now_delta) < NONCE_TIMEOUT); } #ifdef USE_RANDOMIZE_RESPONSES /* * send_random_tag_value - send a randomly-generated three character * tag prefix, a '.', an index, a '=' and a * random integer value. * * To try to force clients to ignore unrecognized tags in mrulist, * reslist, and ifstats responses, the first and last rows are spiced * with randomly-generated tag names with correct .# index. Make it * three characters knowing that none of the currently-used subscripted * tags have that length, avoiding the need to test for * tag collision. */ static void send_random_tag_value( int indx ) { int noise; char buf[32]; noise = ntp_random(); buf[0] = 'a' + noise % 26; noise >>= 5; buf[1] = 'a' + noise % 26; noise >>= 5; buf[2] = 'a' + noise % 26; noise >>= 5; buf[3] = '.'; snprintf(&buf[4], sizeof(buf) - 4, "%d", indx); ctl_putuint(buf, (unsigned long)noise); } #endif /* USE_RANDOMIZE_RESPONSE */ /* * Send a MRU list entry in response to a "ntpq -c mrulist" operation. * * To keep clients honest about not depending on the order of values, * and thereby avoid being locked into ugly workarounds to maintain * backward compatibility later as new fields are added to the response, * the order is random. */ static void send_mru_entry( mon_entry * mon, int count ) { const char first_fmt[] = "first.%d"; const char ct_fmt[] = "ct.%d"; const char mv_fmt[] = "mv.%d"; const char rs_fmt[] = "rs.%d"; char tag[32]; bool sent[6]; /* 6 tag=value pairs */ uint32_t noise; unsigned int which = 0; unsigned int remaining; const char * pch; remaining = COUNTOF(sent); ZERO(sent); noise = (uint32_t)ntp_random(); while (remaining > 0) { #ifdef USE_RANDOMIZE_RESPONSES which = (noise & 7) % COUNTOF(sent); #endif /* USE_RANDOMIZE_RESPONSES */ noise >>= 3; while (sent[which]) which = (which + 1) % COUNTOF(sent); switch (which) { case 0: snprintf(tag, sizeof(tag), addr_fmt, count); pch = sockporttoa(&mon->rmtadr); ctl_putunqstr(tag, pch, strlen(pch)); break; case 1: snprintf(tag, sizeof(tag), last_fmt, count); ctl_putts(tag, &mon->last); break; case 2: snprintf(tag, sizeof(tag), first_fmt, count); ctl_putts(tag, &mon->first); break; case 3: snprintf(tag, sizeof(tag), ct_fmt, count); ctl_putint(tag, mon->count); break; case 4: snprintf(tag, sizeof(tag), mv_fmt, count); ctl_putuint(tag, mon->vn_mode); break; case 5: snprintf(tag, sizeof(tag), rs_fmt, count); ctl_puthex(tag, mon->flags); break; default: /* huh? */ break; } sent[which] = true; remaining--; } } /* * read_mru_list - supports ntpq's mrulist command. * * The approach was suggested by Ry Jones. A finite and variable number * of entries are retrieved per request, to avoid having responses with * such large numbers of packets that socket buffers are overflowed and * packets lost. The entries are retrieved oldest-first, taking into * account that the MRU list will be changing between each request. We * can expect to see duplicate entries for addresses updated in the MRU * list during the fetch operation. In the end, the client can assemble * a close approximation of the MRU list at the point in time the last * response was sent by ntpd. The only difference is it may be longer, * containing some number of oldest entries which have since been * reclaimed. If necessary, the protocol could be extended to zap those * from the client snapshot at the end, but so far that doesn't seem * useful. * * To accommodate the changing MRU list, the starting point for requests * after the first request is supplied as a series of last seen * timestamps and associated addresses, the newest ones the client has * received. As long as at least one of those entries hasn't been * bumped to the head of the MRU list, ntpd can pick up at that point. * Otherwise, the request is failed and it is up to ntpq to back up and * provide the next newest entry's timestamps and addresses, conceivably * backing up all the way to the starting point. * * input parameters: * nonce= Regurgitated nonce retrieved by the client * previously using CTL_OP_REQ_NONCE, demonstrating * ability to receive traffic sent to its address. * frags= Limit on datagrams (fragments) in response. Used * by newer ntpq versions instead of limit= when * retrieving multiple entries. * 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. * mincount= (decimal) Return entries with count >= mincount. * laddr= Return entries associated with the server's IP * address given. No port specification is needed, * and any supplied is ignored. * recent= Set the reporting start point to retrieve roughly * a specified number of most recent entries * 'Roughly' because the logic cannot anticipate * update volume. Use this to volume-limit the * response when you are monitoring something like * a pool server with a very long MRU list. * resall= 0x-prefixed hex restrict bits which must all be * lit for an MRU entry to be included. * Has precedence over any resany=. * resany= 0x-prefixed hex restrict bits, at least one of * which must be list for an MRU entry to be * included. * last.0= 0x-prefixed hex l_fp timestamp of newest entry * which client previously received. * addr.0= text of newest entry's IP address and port, * IPv6 addresses in bracketed form: [::]:123 * last.1= timestamp of 2nd newest entry client has. * addr.1= address of 2nd newest entry. * [...] * * ntpq provides as many last/addr pairs as will fit in a single request * packet, except for the first request in a MRU fetch operation. * * The response begins with a new nonce value to be used for any * followup request. Following the nonce is the next newer entry than * referred to by last.0 and addr.0, if the "0" entry has not been * bumped to the front. If it has, the first entry returned will be the * next entry newer than referred to by last.1 and addr.1, and so on. * If none of the referenced entries remain unchanged, the request fails * and ntpq backs up to the next earlier set of entries to resync. * * Except for the first response, the response begins with confirmation * of the entry that precedes the first additional entry provided: * * last.older= hex l_fp timestamp matching one of the input * .last timestamps, which entry now precedes the * response 0. entry in the MRU list. * addr.older= text of address corresponding to older.last. * * And in any case, a successful response contains sets of values * comprising entries, with the oldest numbered 0 and incrementing from * there: * * addr.# text of IPv4 or IPv6 address and port * last.# hex l_fp timestamp of last receipt * first.# hex l_fp timestamp of first receipt * ct.# count of packets received * mv.# mode and version * rs.# restriction mask (RES_* bits) * * Note the code currently assumes there are no valid three letter * tags sent with each row, and needs to be adjusted if that changes. * * The client should accept the values in any order, and ignore .# * values which it does not understand, to allow a smooth path to * future changes without requiring a new opcode. Clients can rely * on all *.0 values preceding any *.1 values, that is all values for * a given index number are together in the response. * * The end of the response list is noted with one or two tag=value * pairs. Unconditionally: * * now= 0x-prefixed l_fp timestamp at the server marking * the end of the operation. * * If any entries were returned, now= is followed by: * * last.newest= hex l_fp identical to last.# of the prior * entry. */ static void read_mru_list( struct recvbuf *rbufp, int restrict_mask ) { static const char nulltxt[1] = { '\0' }; static const char nonce_text[] = "nonce"; static const char frags_text[] = "frags"; static const char limit_text[] = "limit"; static const char mincount_text[] = "mincount"; static const char resall_text[] = "resall"; static const char resany_text[] = "resany"; static const char maxlstint_text[] = "maxlstint"; static const char laddr_text[] = "laddr"; static const char recent_text[] = "recent"; static const char resaxx_fmt[] = "0x%hx"; unsigned int limit; unsigned short frags; unsigned short resall; unsigned short resany; int mincount; unsigned int maxlstint; sockaddr_u laddr; unsigned int recent; endpt * lcladr; unsigned int count; static unsigned int countdown; unsigned int ui; unsigned int uf; l_fp last[16]; sockaddr_u addr[COUNTOF(last)]; char buf[128]; struct ctl_var * in_parms; const struct ctl_var * v; const char * val; const char * pch; char * pnonce; int nonce_valid; size_t i; int priors; unsigned short hash; mon_entry * mon; mon_entry * prior_mon; l_fp now; if (RES_NOMRULIST & restrict_mask) { ctl_error(CERR_PERMISSION); NLOG(NLOG_SYSINFO) msyslog(LOG_NOTICE, "MODE6: mrulist from %s rejected due to nomrulist restriction", socktoa(&rbufp->recv_srcadr)); sys_restricted++; return; } /* * fill in_parms var list with all possible input parameters. */ in_parms = NULL; set_var(&in_parms, nonce_text, sizeof(nonce_text), 0); set_var(&in_parms, frags_text, sizeof(frags_text), 0); set_var(&in_parms, limit_text, sizeof(limit_text), 0); set_var(&in_parms, mincount_text, sizeof(mincount_text), 0); set_var(&in_parms, resall_text, sizeof(resall_text), 0); set_var(&in_parms, resany_text, sizeof(resany_text), 0); set_var(&in_parms, maxlstint_text, sizeof(maxlstint_text), 0); set_var(&in_parms, laddr_text, sizeof(laddr_text), 0); set_var(&in_parms, recent_text, sizeof(recent_text), 0); for (i = 0; i < COUNTOF(last); i++) { snprintf(buf, sizeof(buf), last_fmt, (int)i); set_var(&in_parms, buf, strlen(buf) + 1, 0); snprintf(buf, sizeof(buf), addr_fmt, (int)i); set_var(&in_parms, buf, strlen(buf) + 1, 0); } /* decode input parms */ pnonce = NULL; frags = 0; limit = 0; mincount = 0; resall = 0; resany = 0; maxlstint = 0; recent = 0; lcladr = NULL; priors = 0; ZERO(last); ZERO(addr); /* have to go through '(void*)' to drop 'const' property from pointer. * ctl_getitem()' needs some cleanup, too.... perlinger@ntp.org */ while (NULL != (v = ctl_getitem(in_parms, (void*)&val)) && !(EOV & v->flags)) { int si; if (NULL == val) val = nulltxt; if (!strcmp(nonce_text, v->text)) { free(pnonce); pnonce = (*val) ? estrdup(val) : NULL; } else if (!strcmp(frags_text, v->text)) { if (1 != sscanf(val, "%hu", &frags)) goto blooper; } else if (!strcmp(limit_text, v->text)) { if (1 != sscanf(val, "%u", &limit)) goto blooper; } else if (!strcmp(mincount_text, v->text)) { if (1 != sscanf(val, "%d", &mincount)) goto blooper; if (mincount < 0) mincount = 0; } else if (!strcmp(resall_text, v->text)) { if (1 != sscanf(val, resaxx_fmt, &resall)) goto blooper; } else if (!strcmp(resany_text, v->text)) { if (1 != sscanf(val, resaxx_fmt, &resany)) goto blooper; } else if (!strcmp(maxlstint_text, v->text)) { if (1 != sscanf(val, "%u", &maxlstint)) goto blooper; } else if (!strcmp(laddr_text, v->text)) { if (decodenetnum(val, &laddr)) goto blooper; lcladr = getinterface(&laddr, 0); } else if (!strcmp(recent_text, v->text)) { if (1 != sscanf(val, "%u", &recent)) goto blooper; } else if (1 == sscanf(v->text, last_fmt, &si) && (size_t)si < COUNTOF(last)) { if (2 != sscanf(val, "0x%08x.%08x", &ui, &uf)) goto blooper; last[si] = lfpinit_u(ui, uf); if (!SOCK_UNSPEC(&addr[si]) && si == priors) priors++; } else if (1 == sscanf(v->text, addr_fmt, &si) && (size_t)si < COUNTOF(addr)) { if (decodenetnum(val, &addr[si])) goto blooper; if (lfpuint(last[si]) && lfpfrac(last[si]) && si == priors) priors++; } else { DPRINT(1, ("read_mru_list: invalid key item: '%s' (ignored)\n", v->text)); continue; blooper: DPRINT(1, ("read_mru_list: invalid param for '%s': '%s' (bailing)\n", v->text, val)); free(pnonce); pnonce = NULL; break; } } free_varlist(in_parms); in_parms = NULL; /* return no responses until the nonce is validated */ if (NULL == pnonce) return; nonce_valid = validate_nonce(pnonce, rbufp); free(pnonce); if (!nonce_valid) return; if ((0 == frags && !(0 < limit && limit <= MRU_ROW_LIMIT)) || frags > MRU_FRAGS_LIMIT) { ctl_error(CERR_BADVALUE); return; } /* * If either frags or limit is not given, use the max. */ if (0 != frags && 0 == limit) limit = UINT_MAX; else if (0 != limit && 0 == frags) frags = MRU_FRAGS_LIMIT; /* * Find the starting point if one was provided. */ mon = NULL; for (i = 0; i < (size_t)priors; i++) { hash = MON_HASH(&addr[i]); for (mon = mon_hash[hash]; mon != NULL; mon = mon->hash_next) if (ADDR_PORT_EQ(&mon->rmtadr, &addr[i])) break; if (mon != NULL) { if (mon->last == last[i]) break; mon = NULL; } } /* If a starting point was provided... */ if (priors) { /* and none could be found unmodified... */ if (NULL == mon) { /* tell ntpq to try again with older entries */ ctl_error(CERR_UNKNOWNVAR); return; } /* confirm the prior entry used as starting point */ ctl_putts("last.older", &mon->last); pch = sockporttoa(&mon->rmtadr); ctl_putunqstr("addr.older", pch, strlen(pch)); /* * Move on to the first entry the client doesn't have, * except in the special case of a limit of one. In * that case return the starting point entry. */ if (limit > 1) mon = PREV_DLIST(mon_mru_list, mon, mru); } else { /* start with the oldest */ mon = TAIL_DLIST(mon_mru_list, mru); countdown = mru_entries; } /* * send up to limit= entries in up to frags= datagrams */ get_systime(&now); generate_nonce(rbufp, buf, sizeof(buf)); ctl_putunqstr("nonce", buf, strlen(buf)); prior_mon = NULL; for (count = 0; mon != NULL && res_frags < frags && count < limit; mon = PREV_DLIST(mon_mru_list, mon, mru)) { if (mon->count < mincount) continue; if (resall && resall != (resall & mon->flags)) continue; if (resany && !(resany & mon->flags)) continue; if (maxlstint > 0 && lfpuint(now) - lfpuint(mon->last) > maxlstint) continue; if (lcladr != NULL && mon->lcladr != lcladr) continue; if (recent != 0 && countdown-- > recent) continue; send_mru_entry(mon, (int)count); #ifdef USE_RANDOMIZE_RESPONSES if (!count) send_random_tag_value(0); #endif /* USE_RANDOMIZE_RESPONSES */ count++; prior_mon = mon; } /* * If this batch completes the MRU list, say so explicitly with * a now= l_fp timestamp. */ if (NULL == mon) { #ifdef USE_RANDOMIZE_RESPONSES if (count > 1) send_random_tag_value((int)count - 1); #endif /* USE_RANDOMIZE_RESPONSES */ ctl_putts("now", &now); /* if any entries were returned confirm the last */ if (prior_mon != NULL) ctl_putts("last.newest", &prior_mon->last); } ctl_flushpkt(0); } /* * Send a ifstats entry in response to a "ntpq -c ifstats" request. * * To keep clients honest about not depending on the order of values, * and thereby avoid being locked into ugly workarounds to maintain * backward compatibility later as new fields are added to the response, * the order is random. */ static void send_ifstats_entry( endpt * la, unsigned int ifnum ) { const char addr_fmtu[] = "addr.%u"; const char bcast_fmt[] = "bcast.%u"; const char en_fmt[] = "en.%u"; /* enabled */ const char name_fmt[] = "name.%u"; const char flags_fmt[] = "flags.%u"; const char rx_fmt[] = "rx.%u"; const char tx_fmt[] = "tx.%u"; const char txerr_fmt[] = "txerr.%u"; const char pc_fmt[] = "pc.%u"; /* peer count */ const char up_fmt[] = "up.%u"; /* uptime */ char tag[32]; uint8_t sent[IFSTATS_FIELDS]; /* 12 tag=value pairs */ int noisebits; uint32_t noise; unsigned int which = 0; unsigned int remaining; const char *pch; remaining = COUNTOF(sent); ZERO(sent); noise = 0; noisebits = 0; while (remaining > 0) { if (noisebits < 4) { noise = (uint32_t)ntp_random(); noisebits = 31; } #ifdef USE_RANDOMIZE_RESPONSES which = (noise & 0xf) % COUNTOF(sent); #endif /* USE_RANDOMIZE_RESPONSES */ noise >>= 4; noisebits -= 4; while (sent[which]) which = (which + 1) % COUNTOF(sent); switch (which) { case 0: snprintf(tag, sizeof(tag), addr_fmtu, ifnum); pch = sockporttoa(&la->sin); ctl_putunqstr(tag, pch, strlen(pch)); break; case 1: snprintf(tag, sizeof(tag), bcast_fmt, ifnum); if (INT_BCASTOPEN & la->flags) pch = sockporttoa(&la->bcast); else pch = ""; ctl_putunqstr(tag, pch, strlen(pch)); break; case 2: snprintf(tag, sizeof(tag), en_fmt, ifnum); ctl_putint(tag, !la->ignore_packets); break; case 3: snprintf(tag, sizeof(tag), name_fmt, ifnum); ctl_putstr(tag, la->name, strlen(la->name)); break; case 4: snprintf(tag, sizeof(tag), flags_fmt, ifnum); ctl_puthex(tag, la->flags); break; case 5: snprintf(tag, sizeof(tag), rx_fmt, ifnum); ctl_putint(tag, la->received); break; case 6: snprintf(tag, sizeof(tag), tx_fmt, ifnum); ctl_putint(tag, la->sent); break; case 7: snprintf(tag, sizeof(tag), txerr_fmt, ifnum); ctl_putint(tag, la->notsent); break; case 8: snprintf(tag, sizeof(tag), pc_fmt, ifnum); ctl_putuint(tag, la->peercnt); break; case 9: snprintf(tag, sizeof(tag), up_fmt, ifnum); ctl_putuint(tag, current_time - la->starttime); break; default: /* Get here if IFSTATS_FIELDS is too big. */ break; } sent[which] = true; remaining--; } #ifdef USE_RANDOMIZE_RESPONSES send_random_tag_value((int)ifnum); #endif /* USE_RANDOMIZE_RESPONSES */ } /* * read_ifstats - send statistics for each local address, exposed by * ntpq -c ifstats */ static void read_ifstats( struct recvbuf * rbufp ) { unsigned int ifidx; endpt * la; UNUSED_ARG(rbufp); /* * loop over [0..sys_ifnum] searching ep_list for each * ifnum in turn. */ for (ifidx = 0; ifidx < sys_ifnum; ifidx++) { for (la = ep_list; la != NULL; la = la->elink) if (ifidx == la->ifnum) break; if (NULL == la) continue; /* return stats for one local address */ send_ifstats_entry(la, ifidx); } ctl_flushpkt(0); } static void sockaddrs_from_restrict_u( sockaddr_u * psaA, sockaddr_u * psaM, restrict_u * pres, int ipv6 ) { ZERO(*psaA); ZERO(*psaM); if (!ipv6) { SET_AF(psaA, AF_INET); PSOCK_ADDR4(psaA)->s_addr = htonl(pres->u.v4.addr); SET_AF(psaM, AF_INET); PSOCK_ADDR4(psaM)->s_addr = htonl(pres->u.v4.mask); } else { SET_AF(psaA, AF_INET6); memcpy(&SOCK_ADDR6(psaA), &pres->u.v6.addr, sizeof(SOCK_ADDR6(psaA))); SET_AF(psaM, AF_INET6); memcpy(&SOCK_ADDR6(psaM), &pres->u.v6.mask, sizeof(SOCK_ADDR6(psaA))); } } /* * Send a restrict entry in response to a "ntpq -c reslist" request. * * To keep clients honest about not depending on the order of values, * and thereby avoid being locked into ugly workarounds to maintain * backward compatibility later as new fields are added to the response, * the order is random. */ static void send_restrict_entry( restrict_u * pres, int ipv6, unsigned int idx ) { const char addr_fmtu[] = "addr.%u"; const char mask_fmtu[] = "mask.%u"; const char hits_fmt[] = "hits.%u"; const char flags_fmt[] = "flags.%u"; char tag[32]; uint8_t sent[RESLIST_FIELDS]; /* 4 tag=value pairs */ int noisebits; uint32_t noise; unsigned int which = 0; unsigned int remaining; sockaddr_u addr; sockaddr_u mask; const char * pch; char * buf; const char * match_str; const char * access_str; sockaddrs_from_restrict_u(&addr, &mask, pres, ipv6); remaining = COUNTOF(sent); ZERO(sent); noise = 0; noisebits = 0; while (remaining > 0) { if (noisebits < 2) { noise = (uint32_t)ntp_random(); noisebits = 31; } #ifdef USE_RANDOMIZE_RESPONSES which = (noise & 0x3) % COUNTOF(sent); #endif /* USE_RANDOMIZE_RESPONSES */ noise >>= 2; noisebits -= 2; while (sent[which]) which = (which + 1) % COUNTOF(sent); switch (which) { case 0: snprintf(tag, sizeof(tag), addr_fmtu, idx); pch = socktoa(&addr); ctl_putunqstr(tag, pch, strlen(pch)); break; case 1: snprintf(tag, sizeof(tag), mask_fmtu, idx); pch = socktoa(&mask); ctl_putunqstr(tag, pch, strlen(pch)); break; case 2: snprintf(tag, sizeof(tag), hits_fmt, idx); ctl_putuint(tag, pres->hitcount); break; case 3: snprintf(tag, sizeof(tag), flags_fmt, idx); match_str = res_match_flags(pres->mflags); access_str = res_access_flags(pres->flags); if ('\0' == match_str[0]) { pch = access_str; } else { buf = lib_getbuf(); snprintf(buf, LIB_BUFLENGTH, "%s %s", match_str, access_str); pch = buf; } ctl_putunqstr(tag, pch, strlen(pch)); break; default: /* huh? */ break; } sent[which] = true; remaining--; } #ifdef USE_RANDOMIZE_RESPONSES send_random_tag_value((int)idx); #endif /* USE_RANDOMIZE_RESPONSES */ } static void send_restrict_list( restrict_u * pres, int ipv6, unsigned int * pidx ) { for ( ; pres != NULL; pres = pres->link) { send_restrict_entry(pres, ipv6, *pidx); (*pidx)++; } } /* * read_addr_restrictions - returns IPv4 and IPv6 access control lists */ static void read_addr_restrictions( struct recvbuf * rbufp ) { unsigned int idx; UNUSED_ARG(rbufp); idx = 0; send_restrict_list(restrictlist4, false, &idx); send_restrict_list(restrictlist6, true, &idx); ctl_flushpkt(0); } /* * read_ordlist - CTL_OP_READ_ORDLIST_A for ntpq -c ifstats & reslist */ static void read_ordlist( struct recvbuf * rbufp, int restrict_mask ) { const char ifstats_s[] = "ifstats"; const size_t ifstatint8_ts = COUNTOF(ifstats_s) - 1; const char addr_rst_s[] = "addr_restrictions"; const size_t a_r_chars = COUNTOF(addr_rst_s) - 1; struct ntp_control * cpkt; unsigned short qdata_octets; UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); /* * CTL_OP_READ_ORDLIST_A was first named CTL_OP_READ_IFSTATS and * used only for ntpq -c ifstats. With the addition of reslist * the same opcode was generalized to retrieve ordered lists * which require authentication. The request data is empty or * contains "ifstats" (not null terminated) to retrieve local * addresses and associated stats. It is "addr_restrictions" * to retrieve the IPv4 then IPv6 remote address restrictions, * which are access control lists. Other request data return * CERR_UNKNOWNVAR. */ cpkt = (struct ntp_control *)&rbufp->recv_pkt; qdata_octets = ntohs(cpkt->count); if (0 == qdata_octets || (ifstatint8_ts == qdata_octets && !memcmp(ifstats_s, cpkt->data, ifstatint8_ts))) { read_ifstats(rbufp); return; } if (a_r_chars == qdata_octets && !memcmp(addr_rst_s, cpkt->data, a_r_chars)) { read_addr_restrictions(rbufp); return; } ctl_error(CERR_UNKNOWNVAR); } /* * req_nonce - CTL_OP_REQ_NONCE for ntpq -c mrulist prerequisite. */ static void req_nonce( struct recvbuf * rbufp, int restrict_mask ) { char buf[64]; UNUSED_ARG(restrict_mask); generate_nonce(rbufp, buf, sizeof(buf)); ctl_putunqstr("nonce", buf, strlen(buf)); ctl_flushpkt(0); } /* * read_clockstatus - return clock radio status */ /*ARGSUSED*/ static void read_clockstatus( struct recvbuf *rbufp, int restrict_mask ) { #ifndef REFCLOCK UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); /* * If no refclock support, no data to return */ ctl_error(CERR_BADASSOC); #else const struct ctl_var * v; int i; struct peer * peer; char * valuep; uint8_t * wants; size_t wants_alloc; bool gotvar; const uint8_t * cc; struct ctl_var * kv; struct refclockstat cs; UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); if (res_associd != 0) { peer = findpeerbyassoc(res_associd); } else { /* * Find a clock for this jerk. If the system peer * is a clock use it, else search peer_list for one. */ if (sys_peer != NULL && (FLAG_REFCLOCK & sys_peer->cfg.flags)) peer = sys_peer; else for (peer = peer_list; peer != NULL; peer = peer->p_link) if (FLAG_REFCLOCK & peer->cfg.flags) break; } if (NULL == peer || !(FLAG_REFCLOCK & peer->cfg.flags)) { ctl_error(CERR_BADASSOC); return; } /* * If we got here we have a peer which is a clock. Get his * status. */ cs.kv_list = NULL; refclock_control(&peer->srcadr, NULL, &cs); kv = cs.kv_list; /* * Look for variables in the packet. */ rpkt.status = htons(ctlclkstatus(&cs)); wants_alloc = CC_MAXCODE + 1 + count_var(kv); wants = emalloc_zero(wants_alloc); gotvar = false; while (NULL != (v = ctl_getitem(clock_var, &valuep))) { if (!(EOV & v->flags)) { wants[v->code] = true; gotvar = true; } else { v = ctl_getitem(kv, &valuep); if (NULL == v) { ctl_error(CERR_BADVALUE); free(wants); free_varlist(cs.kv_list); return; } if (EOV & v->flags) { ctl_error(CERR_UNKNOWNVAR); free(wants); free_varlist(cs.kv_list); return; } wants[CC_MAXCODE + 1 + v->code] = true; gotvar = true; } } if (gotvar) { for (i = 1; i <= (int)CC_MAXCODE; i++) if (wants[i]) ctl_putclock(i, &cs, true); if (kv != NULL) for (i = 0; !(EOV & kv[i].flags); i++) if (wants[(unsigned int)i + CC_MAXCODE + 1]) ctl_putdata(kv[i].text, strlen(kv[i].text), false); } else { for (cc = def_clock_var; *cc != 0; cc++) ctl_putclock((int)*cc, &cs, false); for ( ; kv != NULL && !(EOV & kv->flags); kv++) if (DEF & kv->flags) ctl_putdata(kv->text, strlen(kv->text), false); } free(wants); free_varlist(cs.kv_list); ctl_flushpkt(0); #endif } /* * write_clockstatus - we don't do this */ /*ARGSUSED*/ static void write_clockstatus( struct recvbuf *rbufp, int restrict_mask ) { UNUSED_ARG(rbufp); UNUSED_ARG(restrict_mask); ctl_error(CERR_PERMISSION); } /* * report_event - report an event to log files * * Code lives here because in past times it reported through the * obsolete trap facility. */ void report_event( int err, /* error code */ struct peer *peer, /* peer structure pointer */ const char *str /* protostats string */ ) { #define NTP_MAXSTRLEN 256 /* max string length */ char statstr[NTP_MAXSTRLEN]; size_t len; /* * Report the error to the protostats file and system log */ if (peer == NULL) { /* * Discard a system report if the number of reports of * the same type exceeds the maximum. */ if (ctl_sys_last_event != (uint8_t)err) ctl_sys_num_events= 0; if (ctl_sys_num_events >= CTL_SYS_MAXEVENTS) return; ctl_sys_last_event = (uint8_t)err; ctl_sys_num_events++; snprintf(statstr, sizeof(statstr), "0.0.0.0 %04x %02x %s", ctlsysstatus(), (unsigned)err, eventstr(err)); if (str != NULL) { len = strlen(statstr); snprintf(statstr + len, sizeof(statstr) - len, " %s", str); } NLOG(NLOG_SYSEVENT) msyslog(LOG_INFO, "PROTO: %s", statstr); } else { /* * Discard a peer report if the number of reports of * the same type exceeds the maximum for that peer. */ const char * src; uint8_t errlast; errlast = (uint8_t)err & ~PEER_EVENT; if (peer->last_event != errlast) peer->num_events = 0; if (peer->num_events >= CTL_PEER_MAXEVENTS) return; peer->last_event = errlast; peer->num_events++; #ifdef REFCLOCK if (IS_PEER_REFCLOCK(peer)) src = refclock_name(peer); else #endif /* REFCLOCK */ src = socktoa(&peer->srcadr); snprintf(statstr, sizeof(statstr), "%s %04x %02x %s", src, ctlpeerstatus(peer), (unsigned)err, eventstr(err)); if (str != NULL) { len = strlen(statstr); snprintf(statstr + len, sizeof(statstr) - len, " %s", str); } NLOG(NLOG_PEEREVENT) msyslog(LOG_INFO, "PROTO: %s", statstr); } record_proto_stats(statstr); DPRINT(1, ("event at %u %s\n", current_time, statstr)); } /* * mprintf_event - printf-style varargs variant of report_event() */ int mprintf_event( int evcode, /* event code */ struct peer * p, /* may be NULL */ const char * fmt, /* msnprintf format */ ... ) { va_list ap; int rc; char msg[512]; va_start(ap, fmt); rc = mvsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); report_event(evcode, p, msg); return rc; } /* * ctl_clr_stats - clear stat counters */ void ctl_clr_stats(void) { ctltimereset = current_time; numctlreq = 0; numctlbadpkts = 0; numctlresponses = 0; numctlfrags = 0; numctlerrors = 0; numctlfrags = 0; numctltooshort = 0; numctlinputresp = 0; numctlinputfrag = 0; numctlinputerr = 0; numctlbadoffset = 0; numctlbadversion = 0; numctldatatooshort = 0; numctlbadop = 0; numasyncmsgs = 0; } static unsigned short count_var( const struct ctl_var *k ) { unsigned int c; if (NULL == k) return 0; c = 0; while (!(EOV & (k++)->flags)) c++; ENSURE(c <= USHRT_MAX); return (unsigned short)c; } char * add_var( struct ctl_var **kv, unsigned long size, unsigned short def ) { unsigned short c; struct ctl_var *k; char * buf; c = count_var(*kv); *kv = erealloc(*kv, (c + 2U) * sizeof(**kv)); k = *kv; buf = emalloc(size); k[c].code = c; k[c].text = buf; k[c].flags = def; k[c + 1].code = 0; k[c + 1].text = NULL; k[c + 1].flags = EOV; return buf; } void set_var( struct ctl_var **kv, const char *data, unsigned long size, unsigned short def ) { struct ctl_var *k; const char *s; const char *t; char *td; if (NULL == data || !size) return; k = *kv; if (k != NULL) { while (!(EOV & k->flags)) { if (NULL == k->text) { td = emalloc(size); memcpy(td, data, size); k->text = td; k->flags = def; return; } else { s = data; t = k->text; while (*t != '=' && *s == *t) { s++; t++; } if (*s == *t && ((*t == '=') || !*t)) { td = erealloc((void *)(intptr_t)k->text, size); memcpy(td, data, size); k->text = td; k->flags = def; return; } } k++; } } td = add_var(kv, size, def); memcpy(td, data, size); } void set_sys_var( const char *data, unsigned long size, unsigned short def ) { set_var(&ext_sys_var, data, size, def); } /* * get_ext_sys_var() retrieves the value of a user-defined variable or * NULL if the variable has not been setvar'd. */ const char * get_ext_sys_var(const char *tag) { struct ctl_var * v; size_t c; const char * val; val = NULL; c = strlen(tag); for (v = ext_sys_var; !(EOV & v->flags); v++) { if (NULL != v->text && !memcmp(tag, v->text, c)) { if ('=' == v->text[c]) { val = v->text + c + 1; break; } else if ('\0' == v->text[c]) { val = ""; break; } } } return val; } void free_varlist( struct ctl_var *kv ) { struct ctl_var *k; if (kv) { for (k = kv; !(k->flags & EOV); k++) free((void *)(intptr_t)k->text); free((void *)kv); } } /* from ntp_request.c when ntpdc was nuked */ /* * * reset_auth_stats - reset the authentication stat counters. Done here * * to keep ntp-isms out of the authentication module * */ void reset_auth_stats(void) { authkeylookups = 0; authkeynotfound = 0; authencryptions = 0; authdecryptions = 0; authkeyuncached = 0; auth_timereset = current_time; } ntpsec-1.1.0+dfsg1/ntpd/refclock_arbiter.c0000644000175000017500000002764013252364117020247 0ustar rlaagerrlaager/* * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite * Controlled Clock */ #include "config.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "timespecops.h" #include #include /* * This driver supports the Arbiter 1088A/B Satellite Controlled Clock. * The claimed accuracy of this clock is 100 ns relative to the PPS * output when receiving four or more satellites. * * WARNING: This driver depends on the system clock for year disambiguation. * It will thus not be usable for recovery if the system clock is trashed. * * The receiver should be configured before starting the NTP daemon, in * order to establish reliable position and operating conditions. It * does not initiate surveying or hold mode. For use with NTP, the * daylight savings time feature should be disables (D0 command) and the * broadcast mode set to operate in UTC (BU command). * * The timecode format supported by this driver is selected by the poll * sequence "B5", which initiates a line in the following format to be * repeated once per second until turned off by the "B0" poll sequence. * * Format B5 (24 ASCII printing characters): * * i yy ddd hh:mm:ss.000bbb * * on-time = * i = synchronization flag (' ' = locked, '?' = unlocked) * yy = year of century * ddd = day of year * hh:mm:ss = hours, minutes, seconds * .000 = fraction of second (not used) * bbb = tailing spaces for fill * * The alarm condition is indicated by a '?' at i, which indicates the * receiver is not synchronized. In normal operation, a line consisting * of the timecode followed by the time quality character (TQ) followed * by the receiver status string (SR) is written to the clockstats file. * The time quality character is encoded in IEEE P1344 standard: * * Format TQ (IEEE P1344 estimated worst-case time quality) * * 0 clock locked, maximum accuracy * F clock failure, time not reliable * 4 clock unlocked, accuracy < 1 us * 5 clock unlocked, accuracy < 10 us * 6 clock unlocked, accuracy < 100 us * 7 clock unlocked, accuracy < 1 ms * 8 clock unlocked, accuracy < 10 ms * 9 clock unlocked, accuracy < 100 ms * A clock unlocked, accuracy < 1 s * B clock unlocked, accuracy < 10 s * * The status string is encoded as follows: * * Format SR (25 ASCII printing characters) * * V=vv S=ss T=t P=pdop E=ee * * vv = satellites visible * ss = relative signal strength * t = satellites tracked * pdop = position dilution of precision (meters) * ee = hardware errors * * If flag4 is set, an additional line consisting of the receiver * latitude (LA), longitude (LO), elevation (LH) (meters), and data * buffer (DB) is written to this file. If channel B is enabled for * deviation mode and connected to a 1-PPS signal, the last two numbers * on the line are the deviation and standard deviation averaged over * the last 15 seconds. * * PPS calibration fudge time1 .001240 */ /* * Interface definitions */ #define DEVICE "/dev/gps%d" /* device name and unit */ #define SPEED232 B9600 /* uart speed (9600 baud) */ #define PRECISION (-20) /* precision assumed (about 1 us) */ #define REFID "GPS " /* reference ID */ #define NAME "ARBITER" /* shortname */ #define DESCRIPTION "Arbiter 1088A/B GPS Receiver" /* WRU */ #define LENARB 24 /* format B5 timecode length */ #define MAXSTA 40 /* max length of status string */ #define MAXPOS 80 /* max length of position string */ #define COMMAND_HALT_BCAST ( (peer->cfg.mode % 2) ? "O0" : "B0" ) #define COMMAND_START_BCAST ( (peer->cfg.mode % 2) ? "O5" : "B5" ) /* * ARB unit control structure */ struct arbunit { l_fp laststamp; /* last receive timestamp */ int tcswitch; /* timecode switch/counter */ char qualchar; /* IEEE P1344 quality (TQ command) */ char status[MAXSTA]; /* receiver status (SR command) */ char latlon[MAXPOS]; /* receiver position (lat/lon/alt) */ }; /* * Function prototypes */ static bool arb_start (int, struct peer *); static void arb_receive (struct recvbuf *); static void arb_poll (int, struct peer *); /* * Transfer vector */ struct refclock refclock_arbiter = { NAME, /* basename of driver */ arb_start, /* start up driver */ NULL, /* shut down driver in standard way */ arb_poll, /* transmit poll message */ NULL, /* not used (old arb_control) */ NULL, /* initialize driver (not used) */ NULL /* timer - not used */ }; /* * arb_start - open the devices and initialize data for processing */ static bool arb_start( int unit, struct peer *peer ) { struct arbunit *up; struct refclockproc *pp; int fd; char device[20]; /* * Open serial port. Use CLK line discipline, if available. */ snprintf(device, sizeof(device), DEVICE, unit); fd = refclock_open(peer->cfg.path ? peer->cfg.path : device, peer->cfg.baud ? peer->cfg.baud : SPEED232, LDISC_CLK); if (fd <= 0) /* coverity[leaked_handle] */ return false; /* * Allocate and initialize unit structure */ up = emalloc_zero(sizeof(*up)); pp = peer->procptr; pp->io.clock_recv = arb_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { close(fd); pp->io.fd = -1; free(up); return false; } pp->unitptr = up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; if (peer->cfg.mode > 1) { msyslog(LOG_NOTICE, "REFCLOCK ARBITER: Invalid mode %u", peer->cfg.mode); close(fd); pp->io.fd = -1; free(up); return false; } DPRINT(1, ("arbiter: mode = %u.\n", peer->cfg.mode)); IGNORE(write(pp->io.fd, COMMAND_HALT_BCAST, 2)); return true; } /* * arb_receive - receive data from the serial interface */ static void arb_receive( struct recvbuf *rbufp ) { struct arbunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; int temp; uint8_t syncchar; /* synch indicator */ char tbuf[BMAX]; /* temp buffer */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; temp = refclock_gtlin(rbufp, tbuf, sizeof(tbuf), &trtmp); /* * Note we get a buffer and timestamp for both a and , * but only the timestamp is retained. The program first * sends a TQ and expects the echo followed by the time quality * character. It then sends a B5 starting the timecode broadcast * and expects the echo followed some time later by the on-time * character and then the beginning the timecode * itself. Finally, at the beginning the next timecode at * the next second, the program sends a B0 shutting down the * timecode broadcast. * * If flag4 is set, the program snatches the latitude, longitude * and elevation and writes it to the clockstats file. */ if (temp == 0) return; pp->lastrec = up->laststamp; up->laststamp = trtmp; if (temp < 3) return; if (up->tcswitch == 0) { /* * Collect statistics. If nothing is recognized, just * ignore; sometimes the clock doesn't stop spewing * timecodes for awhile after the B0 command. * * If flag4 is not set, send TQ, SR, B5. If flag4 is * sset, send TQ, SR, LA, LO, LH, DB, B5. When the * median filter is full, send B0. */ if (!strncmp(tbuf, "TQ", 2)) { up->qualchar = tbuf[2]; IGNORE(write(pp->io.fd, "SR", 2)); return; } else if (!strncmp(tbuf, "SR", 2)) { strlcpy(up->status, tbuf + 2, sizeof(up->status)); if (pp->sloppyclockflag & CLK_FLAG4) IGNORE(write(pp->io.fd, "LA", 2)); else IGNORE(write(pp->io.fd, COMMAND_START_BCAST, 2)); return; } else if (!strncmp(tbuf, "LA", 2)) { strlcpy(up->latlon, tbuf + 2, sizeof(up->latlon)); IGNORE(write(pp->io.fd, "LO", 2)); return; } else if (!strncmp(tbuf, "LO", 2)) { strlcat(up->latlon, " ", sizeof(up->latlon)); strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); IGNORE(write(pp->io.fd, "LH", 2)); return; } else if (!strncmp(tbuf, "LH", 2)) { strlcat(up->latlon, " ", sizeof(up->latlon)); strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); IGNORE(write(pp->io.fd, "DB", 2)); return; } else if (!strncmp(tbuf, "DB", 2)) { strlcat(up->latlon, " ", sizeof(up->latlon)); strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); record_clock_stats(peer, up->latlon); DPRINT(1, ("arbiter: %s\n", up->latlon)); IGNORE(write(pp->io.fd, COMMAND_START_BCAST, 2)); } } /* * We get down to business, check the timecode format and decode * its contents. If the timecode has valid length, but not in * proper format, we declare bad format and exit. If the * timecode has invalid length, which sometimes occurs when the * B0 amputates the broadcast, we just quietly steal away. Note * that the time quality character and receiver status string is * tacked on the end for clockstats display. */ up->tcswitch++; if (up->tcswitch <= 1 || temp < LENARB) return; /* * Timecode format B5: "i yy ddd hh:mm:ss.000 " */ strlcpy(pp->a_lastcode, tbuf, sizeof(pp->a_lastcode)); pp->a_lastcode[LENARB - 2] = up->qualchar; strlcat(pp->a_lastcode, up->status, sizeof(pp->a_lastcode)); pp->lencode = (int)strlen(pp->a_lastcode); syncchar = ' '; if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d", &syncchar, &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second) != 6) { refclock_report(peer, CEVNT_BADREPLY); IGNORE(write(pp->io.fd, COMMAND_HALT_BCAST, 2)); return; } /* * We decode the clock dispersion from the time quality * character. */ switch (up->qualchar) { case '0': /* locked, max accuracy */ pp->disp = 1e-7; pp->lastref = pp->lastrec; break; case '4': /* unlock accuracy < 1 us */ pp->disp = S_PER_US; break; case '5': /* unlock accuracy < 10 us */ pp->disp = 1e-5; break; case '6': /* unlock accuracy < 100 us */ pp->disp = 1e-4; break; case '7': /* unlock accuracy < 1 ms */ pp->disp = S_PER_MS; break; case '8': /* unlock accuracy < 10 ms */ pp->disp = .01; break; case '9': /* unlock accuracy < 100 ms */ pp->disp = .1; break; case 'A': /* unlock accuracy < 1 s */ pp->disp = 1; break; case 'B': /* unlock accuracy < 10 s */ pp->disp = 10; break; case 'F': /* clock failure */ pp->disp = sys_maxdisp; refclock_report(peer, CEVNT_FAULT); IGNORE(write(pp->io.fd, COMMAND_HALT_BCAST, 2)); return; default: pp->disp = sys_maxdisp; refclock_report(peer, CEVNT_BADREPLY); IGNORE(write(pp->io.fd, COMMAND_HALT_BCAST, 2)); return; } if (syncchar != ' ') pp->leap = LEAP_NOTINSYNC; else pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) refclock_report(peer, CEVNT_BADTIME); else if (peer->disp > MAXDISTANCE) refclock_receive(peer); /* if (up->tcswitch >= MAXSTAGE) { */ IGNORE(write(pp->io.fd, COMMAND_HALT_BCAST, 2)); /* } */ } /* * arb_poll - called by the transmit procedure */ static void arb_poll( int unit, struct peer *peer ) { struct arbunit *up; struct refclockproc *pp; UNUSED_ARG(unit); /* * Time to poll the clock. The Arbiter clock responds to a "B5" * by returning a timecode in the format specified above. * Transmission occurs once per second, unless turned off by a * "B0". Note there is no checking on state, since this may not * be the only customer reading the clock. Only one customer * need poll the clock; all others just listen in. */ pp = peer->procptr; up = pp->unitptr; pp->polls++; up->tcswitch = 0; if (write(pp->io.fd, "TQ", 2) != 2) refclock_report(peer, CEVNT_FAULT); /* * Process median filter samples. If none received, declare a * timeout and keep going. */ if (pp->coderecv == pp->codeproc) { refclock_report(peer, CEVNT_TIMEOUT); return; } refclock_receive(peer); record_clock_stats(peer, pp->a_lastcode); DPRINT(1, ("arbiter: timecode %d %s\n", pp->lencode, pp->a_lastcode)); } ntpsec-1.1.0+dfsg1/ntpd/ntpd-man.txt0000644000175000017500000000070513252364117017053 0ustar rlaagerrlaager= ntpd(8) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntpd - Network Time Protocol service daemon include::../docs/includes/ntpd-body.txt[] == EXIT STATUS == One of the following exit values will be returned: 0 (EXIT_SUCCESS):: Successful program execution. 1 (EXIT_FAILURE):: Execution failed - examine system logfiles. == SEE ALSO == {ntpdconfman}, {ntpqman}, {ntpdigman}. // end ntpsec-1.1.0+dfsg1/ntpd/refclock_pps.c0000644000175000017500000001662613252364117017423 0ustar rlaagerrlaager/* * refclock_pps - clock driver for 1-pps signals */ #include "config.h" #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" /* * This driver requires the PPSAPI interface (RFC 2783) */ #include "ppsapi_timepps.h" #include "refclock_pps.h" /* * This driver furnishes an interface for pulse-per-second (PPS) signals * produced by a cesium clock, timing receiver or related equipment. It * can be used to remove accumulated jitter over a congested link and * retime a server before redistributing the time to clients. It can * also be used as a holdover should all other synchronization sources * become unreachable. * * Before this driver becomes active, the local clock must be set to * within +-0.4 s by another means, such as a radio clock or NTP * itself. There are two ways to connect the PPS signal, normally at TTL * levels, to the computer. One is to shift to EIA levels and connect to * pin 8 (DCD) of a serial port. This requires a level converter and * may require a one-shot flipflop to lengthen the pulse. The other is * to connect the PPS signal directly to pin 10 (ACK) of a PC parallel * port. These methods are architecture dependent. * * This driver requires the Pulse-per-Second API for Unix-like Operating * Systems, Version 1.0, RFC 2783 (PPSAPI). Implementations are * available for FreeBSD, Linux, SunOS, Solaris and Tru64. However, at * present only the Tru64 implementation provides the full generality of * the API with multiple PPS drivers and multiple handles per driver. If * the PPSAPI is normally implemented in the /usr/include/sys/timepps.h * header file and kernel support specific to each operating system. * * This driver normally uses the PLL/FLL clock discipline implemented in * the ntpd code. Ordinarily, this is the most accurate means, as the * median filter in the driver interface is much larger than in the * kernel. However, if the systemic clock frequency error is large (tens * to hundreds of PPM), it's better to used the kernel support, if * available. * * This driver is subject to the mitigation rules described in the * "mitigation rules and the prefer peer" page. However, there is an * important difference. If this driver becomes the PPS driver according * to these rules, it is active only if (a) a prefer peer other than * this driver is among the survivors or (b) there are no survivors and * the minsane option of the tos command is zero. This is intended to * support space missions where updates from other spacecraft are * infrequent, but a reliable PPS signal, such as from an Ultra Stable * Oscillator (USO) is available. * * Driver options * * The PPS timestamp is captured on the rising (assert) edge if flag2 is * dim (default) and on the falling (clear) edge if lit. If flag3 is dim * (default), the kernel PPS support is disabled; if lit it is enabled. * If flag4 is lit, each timesampt is copied to the clockstats file for * later analysis. This can be useful when constructing Allan deviation * plots. The time1 parameter can be used to compensate for * miscellaneous device driver and OS delays. */ /* * Interface definitions */ #define DEVICE "/dev/pps%d" /* device name and unit */ #define PRECISION (-30) /* precision assumed (about 1 ns) */ #define REFID "PPS\0" /* reference ID */ #define NAME "PPS" /* shortname */ #define DESCRIPTION "PPS Clock Discipline" /* WRU */ /* * PPS unit control structure */ struct ppsunit {struct refclock_ppsctl ppsctl; /* PPS context structure pointer */ int fddev; /* file descriptor */ int pcount; /* PPS samples added to FIFO */ int scount; /* PPS not setup */ int kcount; /* PPS error from kernel */ int rcount; /* PPS not ready */ }; /* * Function prototypes */ static bool pps_start (int, struct peer *); static void pps_shutdown (struct refclockproc *); static void pps_poll (int, struct peer *); static void pps_timer (int, struct peer *); /* * Transfer vector */ struct refclock refclock_pps = { NAME, /* basename of driver */ pps_start, /* start up driver */ pps_shutdown, /* shut down driver */ pps_poll, /* transmit poll message */ NULL, /* control (not used) */ NULL, /* initialize driver (not used) */ pps_timer, /* called once per second */ }; /* * pps_start - initialize data for processing */ static bool pps_start( int unit, /* unit number (not used) */ struct peer *peer /* peer structure pointer */ ) { struct refclockproc *pp; struct ppsunit *up; char device[80]; /* * Allocate and initialize unit structure */ pp = peer->procptr; peer->is_pps_driver = true; peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; pp->stratum = STRATUM_UNSPEC; memcpy((char *)&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_ATOM; up = emalloc_zero(sizeof(struct ppsunit)); pp->unitptr = up; /* * Open PPS device. This can be any serial or parallel port and * not necessarily the port used for the associated radio. */ snprintf(device, sizeof(device), DEVICE, unit); up->fddev = open(peer->cfg.ppspath ? peer->cfg.ppspath : device, O_RDWR); if (up->fddev <= 0) { msyslog(LOG_ERR, "REFCLOCK: refclock_pps: %m"); return false; } /* * Light up the PPSAPI interface. */ return (refclock_ppsapi(up->fddev, &up->ppsctl)); } /* * pps_shutdown - shut down the clock */ static void pps_shutdown( struct refclockproc *pp /* refclock structure pointer */ ) { struct ppsunit *up; up = pp->unitptr; if (up->fddev > 0) close(up->fddev); free(up); } /* * pps_timer - called once per second */ void pps_timer( int unit, /* unit pointer (not used) */ struct peer *peer /* peer structure pointer */ ) { struct ppsunit *up; struct refclockproc *pp; pps_status rc; UNUSED_ARG(unit); pp = peer->procptr; up = pp->unitptr; rc = refclock_catcher(peer, &up->ppsctl, pp->sloppyclockflag); switch (rc) { case PPS_OK: up->pcount++; break; default: case PPS_SETUP: up->scount++; break; case PPS_KERNEL: up->kcount++; break; case PPS_NREADY: up->rcount++; break; } if (rc != PPS_OK) return; peer->cfg.flags |= FLAG_PPS; /* * If flag4 is lit, record each second offset to clockstats. * That's so we can make awesome Allan deviation plots. */ if (pp->sloppyclockflag & CLK_FLAG4) { mprintf_clock_stats(peer, "%.9f", pp->filter[pp->coderecv]); } } /* * pps_poll - called by the transmit procedure */ static void pps_poll( int unit, /* unit number (not used) */ struct peer *peer /* peer structure pointer */ ) { struct ppsunit *up; struct refclockproc *pp; UNUSED_ARG(unit); pp = peer->procptr; up = (struct ppsunit *)pp->unitptr; /* * Don't wiggle the clock until some other driver has numbered * the seconds. */ if (sys_leap == LEAP_NOTINSYNC) { pp->codeproc = pp->coderecv; // xxx ?? up->pcount = up->scount = up->kcount = up->rcount = 0; return; } pp->polls++; mprintf_clock_stats(peer, "%lu %d %d %d %d", up->ppsctl.sequence, up->pcount, up->scount, up->kcount, up->rcount); up->pcount = up->scount = up->kcount = up->rcount = 0; if (pp->codeproc == pp->coderecv) { peer->cfg.flags &= ~FLAG_PPS; refclock_report(peer, CEVNT_TIMEOUT); return; } pp->lastref = pp->lastrec; refclock_receive(peer); } ntpsec-1.1.0+dfsg1/ntpd/ntp_timer.c0000644000175000017500000003230713252364117016744 0ustar rlaagerrlaager/* * ntp_timer.c - event timer support routines */ #include "config.h" #include "ntp_machine.h" #include "ntpd.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" #include "ntp_leapsec.h" #include #include #include #include "ntp_syscall.h" #ifdef HAVE_TIMER_CREATE /* TC_ERR represents the timer_create() error return value. */ # define TC_ERR (-1) #endif #define EVENT_TIMEOUT 0 /* one second, that is */ static void check_leapsec(time_t, bool); /* * These routines provide support for the event timer. The timer is * implemented by an interrupt routine which sets a flag once every * second, and a timer routine which is called when the mainline code * gets around to seeing the flag. The timer routine dispatches the * clock adjustment code if its time has come, then searches the timer * queue for expiries which are dispatched to the transmit procedure. * Finally, we call the hourly procedure to do cleanup and print a * message. */ int interface_interval; /* init_io() sets def. 300s */ /* * The counters and timeouts */ static uptime_t interface_timer; /* interface update timer */ static uptime_t adjust_timer; /* second timer */ static uptime_t stats_timer; /* stats timer */ static uptime_t leapf_timer; /* Report leapfile problems once/day */ static uptime_t huffpuff_timer; /* huff-n'-puff timer */ static unsigned long leapsec; /* secs to next leap (proximity class) */ unsigned int leap_smear_intv; /* Duration of smear. Enables smear mode. */ int leapdif; /* TAI difference step at next leap second*/ uptime_t orphwait; /* orphan wait time */ /* * Statistics counter for the interested. */ unsigned long alarm_overflow; uptime_t current_time; /* seconds since startup */ /* * Stats. Time of last reset and number of calls to transmit(). */ uptime_t timer_timereset; unsigned long timer_xmtcalls; static void catchALRM (int); #ifdef HAVE_TIMER_CREATE static timer_t timer_id; typedef struct itimerspec intervaltimer; #define itv_frac tv_nsec #else typedef struct itimerval intervaltimer; #define itv_frac tv_usec #endif static intervaltimer itimer; void set_timer_or_die(const intervaltimer *); void set_timer_or_die( const intervaltimer * ptimer ) { const char * setfunc; int rc; UNUSED_ARG(ptimer); #ifdef HAVE_TIMER_CREATE setfunc = "timer_settime"; rc = timer_settime(timer_id, 0, &itimer, NULL); #else setfunc = "setitimer"; rc = setitimer(ITIMER_REAL, &itimer, NULL); #endif if (-1 == rc) { msyslog(LOG_ERR, "ERR:interval timer %s failed, %m", setfunc); exit(1); } } /* * reinit_timer - reinitialize interval timer after a clock step. */ void reinit_timer(void) { ZERO(itimer); #ifdef HAVE_TIMER_CREATE timer_gettime(timer_id, &itimer); #else getitimer(ITIMER_REAL, &itimer); #endif if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1 << EVENT_TIMEOUT)) itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT); if (itimer.it_value.itv_frac < 0) itimer.it_value.itv_frac = 0; if (0 == itimer.it_value.tv_sec && 0 == itimer.it_value.itv_frac) itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT); itimer.it_interval.tv_sec = (1 << EVENT_TIMEOUT); itimer.it_interval.itv_frac = 0; set_timer_or_die(&itimer); } /* * init_timer - initialize the timer data structures */ void init_timer(void) { /* * Initialize... */ sawALRM = false; alarm_overflow = 0; adjust_timer = 1; stats_timer = SECSPERHR; leapf_timer = SECSPERDAY; huffpuff_timer = 0; interface_timer = 0; current_time = 0; timer_xmtcalls = 0; timer_timereset = 0; /* * Set up the alarm interrupt. The first comes 2**EVENT_TIMEOUT * seconds from now and they continue on every 2**EVENT_TIMEOUT * seconds. */ #ifdef HAVE_TIMER_CREATE if (TC_ERR == timer_create(CLOCK_REALTIME, NULL, &timer_id)) { msyslog(LOG_ERR, "ERR: timer_create failed, %m"); exit(1); } #endif signal_no_reset(SIGALRM, catchALRM); itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT); itimer.it_interval.itv_frac = itimer.it_value.itv_frac = 0; set_timer_or_die(&itimer); } /* * timer - event timer */ void timer(void) { struct peer * p; struct peer * next_peer; time_t now; /* * The basic timerevent is one second. This is used to adjust the * system clock in time and frequency, implement the kiss-o'-death * function and the association polling function. */ current_time++; if (adjust_timer <= current_time) { adjust_timer += 1; adj_host_clock(); #ifdef REFCLOCK for (p = peer_list; p != NULL; p = next_peer) { next_peer = p->p_link; if (FLAG_REFCLOCK & p->cfg.flags) refclock_timer(p); } #endif /* REFCLOCK */ } /* * Now dispatch any peers whose event timer has expired. Be * careful here, since the peer structure might go away as the * result of the call. */ for (p = peer_list; p != NULL; p = next_peer) { next_peer = p->p_link; /* * Restrain the non-burst packet rate not more * than one packet every 16 seconds. This is * usually tripped using iburst and minpoll of * 128 s or less. */ if (p->throttle > 0) p->throttle--; if (p->nextdate <= current_time) { #ifdef REFCLOCK if (FLAG_REFCLOCK & p->cfg.flags) refclock_transmit(p); else #endif /* REFCLOCK */ transmit(p); } } /* * Orphan mode is active when enabled and when no servers less * than the orphan stratum are available. A server with no other * synchronization source is an orphan. It shows offset zero and * reference ID the loopback address. */ if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL && current_time > orphwait) { if (sys_leap == LEAP_NOTINSYNC) { sys_leap = LEAP_NOWARNING; } sys_stratum = (uint8_t)sys_orphan; if (sys_stratum > 1) sys_refid = htonl(LOOPBACKADR); else memcpy(&sys_refid, "LOOP", REFIDLEN); sys_offset = 0; sys_rootdelay = 0; sys_rootdisp = 0; } time(&now); /* * Leapseconds. Get time and defer to worker if either something * is imminent or every 8th second. */ if (leapsec > LSPROX_NOWARN || 0 == (current_time & 7)) check_leapsec(now, (sys_leap == LEAP_NOTINSYNC)); if (sys_leap != LEAP_NOTINSYNC) { if (leapsec >= LSPROX_ANNOUNCE && leapdif) { if (leapdif > 0) sys_leap = LEAP_ADDSECOND; else sys_leap = LEAP_DELSECOND; } else { sys_leap = LEAP_NOWARNING; } } /* * Update huff-n'-puff filter. */ if (huffpuff_timer <= current_time) { huffpuff_timer += HUFFPUFF; huffpuff(); } /* * Interface update timer */ if (interface_interval && interface_timer <= current_time) { timer_interfacetimeout(current_time + interface_interval); DPRINT(2, ("timer: interface update\n")); interface_update(NULL, NULL); } /* * Finally, write hourly stats and do the hourly * and daily leapfile checks. */ if (stats_timer <= current_time) { stats_timer += SECSPERHR; write_stats(); if (leapf_timer <= current_time) { leapf_timer += SECSPERDAY; check_leap_file(true, now); } else { check_leap_file(false, now); } } } /* * catchALRM - tell the world we've been alarmed */ static void catchALRM( int sig ) { UNUSED_ARG(sig); # ifdef DEBUG const char *msg = NULL; # endif if (sawALRM) { alarm_overflow++; # ifdef DEBUG msg = "catchALRM: overflow\n"; # endif } else { sawALRM = true; # ifdef DEBUG msg = "catchALRM: normal\n"; # endif } # ifdef DEBUG if (debug >= 4 && msg != NULL) (void)(-1 == write(1, msg, strlen(msg))); # endif } void timer_interfacetimeout(uptime_t timeout) { interface_timer = timeout; } /* * timer_clr_stats - clear timer module stat counters */ void timer_clr_stats(void) { timer_xmtcalls = 0; timer_timereset = current_time; } static void check_leap_sec_in_progress( const leap_result_t *lsdata ) { bool prv_leap_sec_in_progress = leap_sec_in_progress; leap_sec_in_progress = lsdata->tai_diff && (lsdata->ddist < 3); /* if changed we may have to update the leap status sent to clients */ if (leap_sec_in_progress != prv_leap_sec_in_progress) set_sys_leap(sys_leap); } static void check_leapsec( time_t now, bool reset) { static const char leapmsg_p_step[] = "Positive leap second, stepped backward."; static const char leapmsg_p_slew[] = "Positive leap second, no step correction. " "System clock will be inaccurate for a long time."; static const char leapmsg_n_step[] = "Negative leap second, stepped forward."; static const char leapmsg_n_slew[] = "Negative leap second, no step correction. " "System clock will be inaccurate for a long time."; leap_result_t lsdata; uint32_t lsprox; leapsec_electric((pll_control && kern_enable) ? electric_on : electric_off); #ifdef ENABLE_LEAP_SMEAR leap_smear.enabled = (leap_smear_intv != 0); #endif if (reset) { lsprox = LSPROX_NOWARN; leapsec_reset_frame(); memset(&lsdata, 0, sizeof(lsdata)); } else { int fired = leapsec_query(&lsdata, now); DPRINT(1, ("*** leapsec_query: fired %i, now %lli (0x%llX), " "tai_diff %i, ddist %u\n", fired, (long long)now, (long long unsigned)now, lsdata.tai_diff, lsdata.ddist)); #ifdef ENABLE_LEAP_SMEAR leap_smear.in_progress = false; leap_smear.doffset = 0.0; if (leap_smear.enabled) { if (lsdata.tai_diff) { if (leap_smear.interval == 0) { leap_smear.interval = leap_smear_intv; leap_smear.intv_end = lsdata.ttime; leap_smear.intv_start = leap_smear.intv_end - leap_smear.interval; DPRINT(1, ("*** leapsec_query: setting leap_smear interval %li, begin %.0f, end %.0f\n", leap_smear.interval, leap_smear.intv_start, leap_smear.intv_end)); } } else { if (leap_smear.interval) DPRINT(1, ("*** leapsec_query: clearing leap_smear interval\n")); leap_smear.interval = 0; } if (leap_smear.interval) { double dtemp = now; if (dtemp >= leap_smear.intv_start && dtemp <= leap_smear.intv_end) { double leap_smear_time = dtemp - leap_smear.intv_start; /* * For now we just do a linear interpolation over the smear interval * https://developers.google.com/time/smear */ leap_smear.doffset = -(leap_smear_time * lsdata.tai_diff / leap_smear.interval); /* * TODO see if we're inside an * inserted leap second, so we * need to compute * leap_smear.doffset = 1.0 - * leap_smear.doffset */ leap_smear.in_progress = true; #if 0 && defined( DEBUG ) msyslog(LOG_NOTICE, "CLOCK: *** leapsec_query: [%.0f:%.0f] (%li), now %u (%.0f), smear offset %.6f ms\n", leap_smear.intv_start, leap_smear.intv_end, leap_smear.interval, now, leap_smear_time, leap_smear.doffset); #else DPRINT(1, ("*** leapsec_query: [%.0f:%.0f] (%li), now %lld (%.0f), smear offset %.6f ms\n", leap_smear.intv_start, leap_smear.intv_end, leap_smear.interval, (long long)now, leap_smear_time, leap_smear.doffset)); #endif } } } else leap_smear.interval = 0; /* * Update the current leap smear offset, eventually 0.0 if outside smear interval. */ leap_smear.offset = dtolfp(leap_smear.doffset); #endif /* ENABLE_LEAP_SMEAR */ /* Full hit. Eventually step the clock, but always * announce the leap event has happened. */ if (fired) { const char *leapmsg = NULL; if (lsdata.warped < 0) { if (clock_max_back > 0.0 && clock_max_back < abs(lsdata.warped)) { step_systime(lsdata.warped, ntp_set_tod); leapmsg = leapmsg_p_step; } else { leapmsg = leapmsg_p_slew; } } else if (lsdata.warped > 0) { if (clock_max_fwd > 0.0 && clock_max_fwd < abs(lsdata.warped)) { step_systime(lsdata.warped, ntp_set_tod); leapmsg = leapmsg_n_step; } else { leapmsg = leapmsg_n_slew; } } if (leapmsg) msyslog(LOG_NOTICE, "CLOCK: %s", leapmsg); report_event(EVNT_LEAP, NULL, NULL); lsprox = LSPROX_NOWARN; leapsec = LSPROX_NOWARN; sys_tai = (unsigned int)lsdata.tai_offs; } else { lsprox = lsdata.proximity; sys_tai = (unsigned int)lsdata.tai_offs; } } /* We guard against panic alarming during the red alert phase. * Strange and evil things might happen if we go from stone cold * to piping hot in one step. If things are already that wobbly, * we let the normal clock correction take over, even if a jump * is involved. * Also make sure the alarming events are edge-triggered, that is, * created only when the threshold is crossed. */ if ( (leapsec > 0 || lsprox < LSPROX_ALERT) && leapsec < lsprox ) { if ( leapsec < LSPROX_SCHEDULE && lsprox >= LSPROX_SCHEDULE) { if (lsdata.dynamic) report_event(PEVNT_ARMED, sys_peer, NULL); else report_event(EVNT_ARMED, NULL, NULL); } leapsec = lsprox; } if (leapsec > lsprox) { if ( leapsec >= LSPROX_SCHEDULE && lsprox < LSPROX_SCHEDULE) { report_event(EVNT_DISARMED, NULL, NULL); } leapsec = lsprox; } if (leapsec >= LSPROX_SCHEDULE) leapdif = lsdata.tai_diff; else leapdif = 0; check_leap_sec_in_progress(&lsdata); } ntpsec-1.1.0+dfsg1/ntpd/ntpd.c0000644000175000017500000007564613252364117015725 0ustar rlaagerrlaager/* * ntpd.c - main program for the fixed point NTP daemon */ #include "config.h" #include "ntp_machine.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_stdlib.h" #include "ntp_config.h" #include "ntp_syslog.h" #include "ntp_assert.h" #ifdef ENABLE_DNS_LOOKUP #include "ntp_dns.h" #endif #include "isc_error.h" #include #include #include #include #include #ifdef HAVE_SYS_IOCTL_H # include #endif /* HAVE_SYS_IOCTL_H */ #include #include #include #include "recvbuff.h" #include "version.h" void catchQuit (int sig); static volatile int signo = 0; /* In an ideal world, 'finish_safe()' would declared as noreturn... */ static void finish_safe (int) __attribute__ ((__noreturn__)); #ifdef SIGDANGER # include #endif /* SIGDANGER */ #if defined(HAVE_DNS_SD_H) && defined(ENABLE_MDNS_REGISTRATION) # include static DNSServiceRef mdns; #endif static void check_minsane(void); static bool need_priority = false; static bool config_priority_override = false; static int config_priority; bool listen_to_virtual_ips = true; static char *logfilename; static bool opt_ipv4, opt_ipv6; static const char *explicit_config; static bool explicit_interface; static bool nofork = false; /* Fork by default */ static bool dumpopts; static long wait_sync = -1; static const char *driftfile, *pidfile; #if defined(HAVE_DNS_SD_H) && defined(ENABLE_MDNS_REGISTRATION) /* * mDNS registration flag. If set, we attempt to register with the * mDNS system, but only after we have synched the first time. If the * attempt fails, then try again once per minute for up to 5 * times. After all, we may be starting before mDNS. */ static bool mdnsreg = false; int mdnstries = 5; #endif /* ENABLE_MDNS_REGISTRATION */ static bool droproot = false; static char *user; /* User to switch to */ static char *group; /* group to switch to */ static const char *chrootdir; /* directory to chroot to */ #ifdef HAVE_WORKING_FORK int waitsync_fd_to_close = -1; /* -w/--wait-sync */ #endif const char *progname; #if defined(HAVE_WORKING_FORK) static int wait_child_sync_if (int, long); #endif static void catchHUP (int); #ifdef ENABLE_DNS_LOOKUP static void catchDNS (int); #endif # ifdef DEBUG static void moredebug (int); static void lessdebug (int); # else /* !DEBUG follows */ static void no_debug (int); # endif /* !DEBUG */ static int saved_argc; static char ** saved_argv; static int ntpdmain(int, char **) __attribute__((noreturn)); static void mainloop (void) __attribute__ ((__noreturn__)); static void set_process_priority (void); static void close_all_beyond(int); static void close_all_except(int); #define ALL_OPTIONS "46abc:dD:f:gGhi:I:k:l:LmnNp:Pqr:Rs:t:u:U:Vw:xzZ" static const struct option longoptions[] = { { "ipv4", 0, 0, '4' }, { "ipv6", 0, 0, '6' }, { "assert", 0, 0, 'a' }, { "configfile", 1, 0, 'c' }, { "debug", 0, 0, 'd' }, { "set-debug-level", 1, 0, 'D' }, { "driftile", 1, 0, 'f' }, { "panicgate", 0, 0, 'g' }, { "help", 0, 0, 'h' }, { "jaildir", 1, 0, 'i' }, { "interface", 1, 0, 'I' }, { "keyfile", 1, 0, 'k' }, { "logfile", 1, 0, 'l' }, { "mdns", 0, 0, 'm' }, { "novirtualips", 0, 0, 'L' }, { "nofork", 0, 0, 'n' }, { "nice", 0, 0, 'N' }, { "pidfile", 1, 0, 'p' }, { "priority", 1, 0, 'P' }, { "quit", 0, 0, 'q' }, { "dumpopts", 0, 0, 'R' }, { "statsdir", 1, 0, 's' }, { "trustedkey", 1, 0, 't' }, { "user", 1, 0, 'u' }, { "updateinterval", 1, 0, 'U' }, { "var", 1, 0, 'z' }, { "dvar", 1, 0, 'Z' }, { "slew", 0, 0, 'x' }, { "version", 0, 0, 'V' }, { NULL, 0, 0, '\0'}, }; static void ntpd_usage(void) { #define P(x) fputs(x, stderr) P("USAGE: ntpd [ - [] | --[{=| }] ]...\n"); P(" Flg Arg Option-Name Description\n"); P(" -4 no ipv4 Force IPv4 DNS name resolution\n"); P(" - prohibits the option 'ipv6'\n"); P(" -6 no ipv6 Force IPv6 DNS name resolution\n"); P(" - prohibits the option 'ipv4'\n"); P(" -a no assert REQUIRE(false) to test assert handler\n"); P(" -c Str configfile configuration file name\n"); P(" -d no debug-level Increase output debug message level\n"); P(" - may appear multiple times\n"); P(" -D Str set-debug-level Set the output debug message level\n"); P(" - may appear multiple times\n"); P(" -f Str driftfile frequency drift file name\n"); P(" -g no panicgate Allow the first adjustment to be Big\n"); P(" - may appear multiple times\n"); P(" -h no --help Display usage summary of options and exit.\n"); P(" -i Str jaildir Jail directory\n"); P(" -I Str interface Listen on an interface name or address\n"); P(" - may appear multiple times\n"); P(" -k Str keyfile path to symmetric keys\n"); P(" -l Str logfile path to the log file\n"); P(" -L no novirtualips Do not listen to virtual interfaces\n"); P(" -m no Enable mDNS registration\n"); P(" -n no nofork Do not fork\n"); P(" -N no nice Run at high priority\n"); P(" -p Str pidfile path to the PID file\n"); P(" -P Num priority Process priority\n"); P(" -q no quit Set the time and quit\n"); P(" -r Str propagationdelay Broadcast/propagation delay\n"); P(" -s Str statsdir Statistics file location\n"); P(" -t Str trustedkey Trusted key number\n"); P(" - may appear multiple times\n"); P(" -u Str user Run as userid (or userid:groupid)\n"); P(" -U Num uinterval interval in secs between scans for new or dropped interfaces\n"); P(" Str var make ARG an ntp variable (RW)\n"); P(" - may appear multiple times\n"); P(" Str dvar make ARG an ntp variable (RW|DEF)\n"); P(" - may appear multiple times\n"); P(" -x no slew Slew up to 600 seconds\n"); P(" -V no version Output version information and exit\n"); P(" -h no help Display extended usage information and exit\n"); #ifdef REFCLOCK P("This version was compiled with the following clock drivers:\n"); int ct = 0; for (int dtype = 1; dtype < (int)num_refclock_conf; dtype++) if (refclock_conf[dtype]->basename != NULL) { fprintf(stderr, "%12s", refclock_conf[dtype]->basename); if (((++ct % 5) == 0)) fputc('\n', stderr); } if (ct % 5) fputc('\n', stderr); #endif /* REFCLOCK */ #undef P } static void parse_cmdline_opts( int argc, char **argv ) { static bool parsed = false; if (parsed) return; int op; while ((op = ntp_getopt_long(argc, argv, ALL_OPTIONS, longoptions, NULL)) != -1) { switch (op) { case '4': opt_ipv4 = true; break; case '6': opt_ipv6 = true; break; case 'a': fputs("Testing assert failure.\n", stderr); REQUIRE(false); break; case 'b': fputs("ERROR: Obsolete and unsupported broadcast option -b\n", stderr); ntpd_usage(); exit(1); break; case 'c': if (ntp_optarg != NULL) explicit_config = ntp_optarg; break; case 'd': #ifdef DEBUG ++debug; #endif nofork = true; break; case 'D': #ifdef DEBUG if (ntp_optarg != NULL) debug = atoi(ntp_optarg); #endif break; case 'f': if (ntp_optarg != NULL) driftfile = ntp_optarg; break; case 'g': allow_panic = true; break; case 'G': force_step_once = true; break; case 'h': ntpd_usage(); exit(0); case 'i': #ifdef ENABLE_DROPROOT droproot = true; if (ntp_optarg != NULL) chrootdir = ntp_optarg; #endif break; case 'I': explicit_interface = true; /* processing deferred */ break; case 'k': /* defer */ break; case 'l': if (ntp_optarg != NULL) logfilename = ntp_optarg; break; case 'L': listen_to_virtual_ips = false; break; case 'm': #if defined(HAVE_DNS_SD_H) && defined(ENABLE_MDNS_REGISTRATION) mdnsreg = true; #endif /* ENABLE_MDNS_REGISTRATION */ break; case 'M': /* defer */ break; case 'n': nofork = true; break; case 'N': need_priority = true; break; case 'p': if (ntp_optarg != NULL) pidfile = ntp_optarg; break; case 'P': if (ntp_optarg != NULL) { config_priority = atoi(ntp_optarg); config_priority_override = true; need_priority = true; } break; case 'q': mode_ntpdate = true; nofork = true; break; case 'r': fputs("ERROR: Obsolete and unsupported broadcast option -r\n", stderr); ntpd_usage(); exit(1); break; case 'R': /* undocumented -- dump CLI options for testing */ dumpopts = true; nofork = true; break; case 's': if (ntp_optarg != NULL) strlcpy(statsdir, ntp_optarg, sizeof(statsdir)); break; case 't': /* defer */ break; case 'u': #ifdef ENABLE_DROPROOT if (ntp_optarg != NULL) { char *gp; droproot = true; if ( user ) { fputs("ERROR: more than one -u given.\n", stderr); ntpd_usage(); exit(1); } user = estrdup(ntp_optarg); gp = strrchr(user, ':'); if (gp) { *gp++ = '\0'; /* get rid of the ':' */ group = estrdup(gp); } else { group = NULL; } } #endif break; case 'U': if (ntp_optarg != NULL) { long val = atol(ntp_optarg); if (val >= 0) interface_interval = (int)val; else { fprintf(stderr, "command line interface update interval %ld must not be negative\n", val); msyslog(LOG_ERR, "CONFIG: command line interface update interval %ld must not be negative", val); exit(0); } } break; case 'V': printf("%s\n", ntpd_version()); exit(0); case 'w': if (ntp_optarg != NULL) wait_sync = (long)strtod(ntp_optarg, NULL); break; case 'x': /* defer */ break; case 'z': /* defer */ break; case 'Z': /* defer */ break; default: fputs("Unknown command line switch or missing argument.\n", stderr); ntpd_usage(); exit(1); } /*switch*/ } /* * Sanity checks and derived options */ /* save list of servers from cmd line for config_peers() use */ if (ntp_optind < argc) { cmdline_server_count = argc - ntp_optind; cmdline_servers = argv + ntp_optind; } } #ifdef NO_MAIN_ALLOWED CALL(ntpd,"ntpd",ntpdmain); #else /* !NO_MAIN_ALLOWED follows */ int main( int argc, char *argv[] ) { return ntpdmain(argc, argv); } #endif /* !NO_MAIN_ALLOWED */ #ifdef SIGDANGER /* * Signal handler for SIGDANGER. (AIX) */ static void catch_danger(int signo) { msyslog(LOG_INFO, "ERR: setpgid(): %m"); /* Make the system believe we'll free something, but don't do it! */ return; } #endif /* SIGDANGER */ /* * Set the process priority */ static void set_process_priority(void) { # ifdef DEBUG if (debug > 1) /* SPECIAL DEBUG */ msyslog(LOG_DEBUG, "INIT: set_process_priority: %s", ((!need_priority) ? "Leave priority alone" : "Attempt to set priority" )); # endif /* DEBUG */ #ifdef HAVE_SCHED_SETSCHEDULER if (need_priority) { int pmax, pmin; struct sched_param sched; pmax = sched_get_priority_max(SCHED_FIFO); sched.sched_priority = pmax; if ( config_priority_override ) { pmin = sched_get_priority_min(SCHED_FIFO); if ( config_priority > pmax ) sched.sched_priority = pmax; else if ( config_priority < pmin ) sched.sched_priority = pmin; else sched.sched_priority = config_priority; } if ( sched_setscheduler(0, SCHED_FIFO, &sched) == -1 ) msyslog(LOG_ERR, "INIT: sched_setscheduler(): %m"); else need_priority = false; } #endif if (need_priority) msyslog(LOG_ERR, "INIT: set_process_priority: No way found to improve our priority"); } const char *ntpd_version(void) { static char versionbuf[64]; snprintf(versionbuf, sizeof(versionbuf), "ntpd ntpsec-%s+%d %s", VERSION, VCS_TICK, VCS_DATE); return versionbuf; } /* * Main program. Initialize us, disconnect us from the tty if necessary, * and loop waiting for I/O and/or timer expiries. */ static int ntpdmain( int argc, char *argv[] ) { mode_t uv; uid_t uid; # if defined(HAVE_WORKING_FORK) int pipe_fds[2]; int rc; int exit_code; # ifdef SIGDANGER struct sigaction sa; # endif # endif /* HAVE_WORKING_FORK*/ int op; uv = umask(0); if (uv) umask(uv); else umask(022); saved_argc = argc; saved_argv = argv; progname = argv[0]; parse_cmdline_opts(argc, argv); # ifdef DEBUG setvbuf(stdout, NULL, _IOLBF, 0); # endif init_logging(progname, NLOG_SYNCMASK, true); /* honor -l/--logfile option to log to a file */ if (logfilename != NULL) { syslogit = false; termlogit = false; change_logfile(logfilename, false); } else { if (nofork) termlogit = true; if (dumpopts) syslogit = false; } if (!dumpopts) { char buf[1024]; /* Secret knowledge of msyslog buf length */ char *cp = buf; msyslog(LOG_NOTICE, "INIT: %s: Starting", ntpd_version()); /* Note that every arg has an initial space character */ snprintf(cp, sizeof(buf), "Command line:"); cp += strlen(cp); for (int i = 0; i < saved_argc ; ++i) { snprintf(cp, sizeof(buf) - (size_t)(cp - buf), " %s", saved_argv[i]); cp += strlen(cp); } msyslog(LOG_INFO, "INIT: %s", buf); } uid = getuid(); if (uid && !dumpopts) { termlogit = true; msyslog(LOG_ERR, "INIT: must be run as root, not uid %ld", (long)uid); exit(1); } set_prettydate_pivot(time(NULL)); # ifdef HAVE_WORKING_FORK /* make sure the FDs are initialised */ pipe_fds[0] = -1; pipe_fds[1] = -1; if (wait_sync <= 0) wait_sync = 0; else { /* -w requires a fork() even with debug > 0 */ nofork = false; if (pipe(pipe_fds)) { termlogit = true; exit_code = (errno) ? errno : -1; msyslog(LOG_ERR, "INIT: Pipe creation failed for --wait-sync: %m"); exit(exit_code); } waitsync_fd_to_close = pipe_fds[1]; } # endif /* HAVE_WORKING_FORK */ init_network(); /* * Detach us from the terminal. May need an #ifndef GIZMO. */ if (!nofork) { # ifdef HAVE_WORKING_FORK rc = fork(); if (-1 == rc) { exit_code = (errno) ? errno : -1; msyslog(LOG_ERR, "INIT: fork: %m"); exit(exit_code); } if (rc > 0) { /* parent */ exit_code = wait_child_sync_if(pipe_fds[0], wait_sync); exit(exit_code); } /* * child/daemon * close all open files excepting waitsync_fd_to_close. * msyslog() unreliable until after init_logging(). */ termlogit = false; /* do not use stderr after fork */ closelog(); close_all_except(waitsync_fd_to_close); INSIST(0 == open("/dev/null", 0) && 1 == dup2(0, 1) \ && 2 == dup2(0, 2)); init_logging(progname, 0, true); /* we lost our logfile (if any) daemonizing */ setup_logfile(logfilename); if (setsid() == (pid_t)-1) msyslog(LOG_ERR, "INIT: setsid(): %m"); # ifdef SIGDANGER /* Don't get killed by low-on-memory signal. */ sa.sa_handler = catch_danger; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGDANGER, &sa, NULL); # endif /* SIGDANGER */ # endif /* HAVE_WORKING_FORK */ } /* * Set up signals we pay attention to locally. */ signal_no_reset(SIGINT, catchQuit); signal_no_reset(SIGQUIT, catchQuit); signal_no_reset(SIGTERM, catchQuit); signal_no_reset(SIGHUP, catchHUP); signal_no_reset(SIGBUS, catchQuit); /* FIXME: It's broken, can't continue. */ #ifdef ENABLE_DNS_LOOKUP signal_no_reset(SIGDNS, catchDNS); #endif # ifdef DEBUG signal_no_reset(MOREDEBUGSIG, moredebug); signal_no_reset(LESSDEBUGSIG, lessdebug); # else signal_no_reset(MOREDEBUGSIG, no_debug); signal_no_reset(LESSDEBUGSIG, no_debug); # endif /* DEBUG */ /* * Set up signals we should never pay attention to. */ signal_no_reset(SIGPIPE, SIG_IGN); /* * Call the init_ routines to initialize the data structures. * * Exactly what command-line options are we expecting here? */ ssl_init(); init_auth(); init_util(); init_restrict(); init_mon(); init_control(); init_peer(); # ifdef REFCLOCK init_refclock(); # endif set_process_priority(); init_proto(!dumpopts); /* Call at high priority */ init_io(); init_loopfilter(); init_readconfig(); /* see readconfig() */ mon_start(MON_ON); /* monitor on by default now */ /* turn off in config if unwanted */ /* * Some option settings have to be deferred until after * the library initialization sequence. */ ntp_optind = 1; while ((op = ntp_getopt_long(argc, argv, ALL_OPTIONS, longoptions, NULL)) != -1) { switch (op) { case '4': case '6': /* handled elsewhere */ break; case 'b': fputs("ERROR: Obsolete and unsupported broadcast option -b\n", stderr); ntpd_usage(); exit(1); break; case 'c': case 'd': case 'D': /* handled elsewhere */ break; case 'f': stats_config(STATS_FREQ_FILE, driftfile); break; case 'g': case 'h': case 'i': /* handled elsewhere */ break; case 'I': if (ntp_optarg != NULL) { sockaddr_u addr; add_nic_rule( is_ip_address(ntp_optarg, AF_UNSPEC, &addr) ? MATCH_IFADDR : MATCH_IFNAME, ntp_optarg, -1, ACTION_LISTEN); } break; case 'k': if (ntp_optarg != NULL) set_keys_file(ntp_optarg); break; case 'l': case 'L': case 'm': case 'n': case 'N': /* handled elsewhere */ break; case 'p': stats_config(STATS_PID_FILE, pidfile); break; case 'P': case 'q': /* handled elsewhere */ break; case 'r': fputs("ERROR: Obsolete and unsupported broadcast option -r\n", stderr); ntpd_usage(); exit(1); break; case 'R': /* handled elsewhere */ break; case 's': stats_config(STATS_STATSDIR, statsdir); break; case 't': if (ntp_optarg != NULL) { unsigned long tkey = (unsigned long)atol(ntp_optarg); if (tkey == 0 || tkey > NTP_MAXKEY) { msyslog(LOG_ERR, "INIT: command line trusted key %s is invalid", ntp_optarg); exit(1); } else { set_trustedkey((keyid_t)tkey); } } break; case 'u': case 'U': case 'v': case 'w': /* handled elsewhere */ break; case 'x': loop_config(LOOP_MAX, 600); break; case 'z': if (ntp_optarg != NULL) set_sys_var(ntp_optarg, strlen(ntp_optarg) + 1, RW); break; case 'Z': if (ntp_optarg != NULL) set_sys_var(ntp_optarg, strlen(ntp_optarg) + 1, (unsigned short) (RW | DEF)); break; default: msyslog(LOG_ERR, "INIT: Unknown option: %c", (char)op); exit(1); } } /* * ntpd's working set is never going to be large relative to memory * availability on modern machines. Do what chrony does and indulge it; * we get some latency improvement that way. * Need to do this before droproot. */ { struct rlimit rlim; rlim.rlim_max = rlim.rlim_cur = RLIM_INFINITY; #ifdef RLIMIT_MEMLOCK if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0) msyslog(LOG_WARNING, "INIT: setrlimit() failed: not locking into RAM"); else #endif if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) msyslog(LOG_WARNING, "INIT: mlockall() failed: not locking into RAM"); else msyslog(LOG_INFO, "INIT: successfully locked into RAM"); } #ifdef ENABLE_EARLY_DROPROOT /* drop root privileges */ /* This doesn't work on NetBSD or with SHM */ if (sandbox(droproot, user, group, chrootdir, interface_interval!=0)) { interface_interval = 0; /* * for now assume that the privilege to bind to privileged ports * is associated with running with uid 0 - should be refined on * ports that allow binding to NTP_PORT with uid != 0 */ disable_dynamic_updates = true; msyslog(LOG_INFO, "INIT: running as non-root disables dynamic interface tracking"); } #endif /* use this to test if option setting gives expected results */ if (dumpopts) { if (explicit_config) fprintf(stdout, "conffile \"%s\";\n", explicit_config); #ifdef DEBUG fprintf(stdout, "#debug = %d\n", debug); #endif /* DEBUG */ if (driftfile) fprintf(stdout, "driftfile \"%s\";\n", driftfile); fprintf(stdout, "#allow_panic = %s\n", allow_panic ? "true" : "false"); fprintf(stdout, "#force_step_once = %s\n", force_step_once ? "true" : "false"); #ifdef ENABLE_DROPROOT if (chrootdir) fprintf(stdout, "#chrootdir = \"%s\";\n", chrootdir); if (user) fprintf(stdout, "#user = %s\n", user); if (group) fprintf(stdout, "#group = %s\n", group); #endif /* FIXME: dump interfaces */ /* FIXME: dump authkeys */ if (logfilename) fprintf(stdout, "logfile \"%s\";\n", logfilename); fprintf(stdout, "#listen_to_virtual_ips = %s\n", listen_to_virtual_ips ? "true" : "false"); #if defined(HAVE_DNS_SD_H) && defined(ENABLE_MDNS_REGISTRATION) fprintf(stdout, "#mdnsreg = %s\n", mdnsreg ? "true" : "false"); #endif /* ENABLE_MDNS_REGISTRATION */ if (pidfile) fprintf(stdout, "pidfile \"%s\";\n", pidfile); /* FIXME: dump priority */ fprintf(stdout, "#mode_ntpdate = %s\n", mode_ntpdate ? "true" : "false"); if (statsdir[0]) fprintf(stdout, "statsdir \"%s\";\n", statsdir); fprintf(stdout, "#interface_interval = %d\n", interface_interval); /* FIXME: dump variable settings */ exit(0); } if (ipv4_works && ipv6_works) { if (opt_ipv4) ipv6_works = false; else if (opt_ipv6) ipv4_works = false; } else if (!ipv4_works && !ipv6_works) { msyslog(LOG_ERR, "INIT: Neither IPv4 nor IPv6 networking detected, fatal."); exit(1); } else if (opt_ipv4 && !ipv4_works) msyslog(LOG_WARNING, "INIT: -4/--ipv4 ignored, IPv4 networking not found."); else if (opt_ipv6 && !ipv6_works) msyslog(LOG_WARNING, "INIT: -6/--ipv6 ignored, IPv6 networking not found."); /* * Get the configuration. */ have_interface_option = (!listen_to_virtual_ips || explicit_interface); readconfig(getconfig(explicit_config)); check_minsane(); if ( 8 > sizeof(time_t) ) { msyslog(LOG_ERR, "INIT: This system has a 32-bit time_t."); msyslog(LOG_ERR, "INIT: This ntpd will fail on 2038-01-19T03:14:07Z."); } loop_config(LOOP_DRIFTINIT, 0); report_event(EVNT_SYSRESTART, NULL, NULL); #ifndef ENABLE_EARLY_DROPROOT /* drop root privileges */ if (sandbox(droproot, user, group, chrootdir, interface_interval!=0) && interface_interval) { interface_interval = 0; msyslog(LOG_INFO, "INIT: running as non-root disables dynamic interface tracking"); } #endif mainloop(); /* unreachable, mainloop() never returns */ } /* * Process incoming packets until exit or interrupted. */ static void mainloop(void) { struct recvbuf *rbuf; init_timer(); for (;;) { if (sawQuit) finish_safe(signo); if (!sawALRM && !has_full_recv_buffer()) { /* * Nothing to do. Wait for something. */ io_handler(); } if (sawALRM) { /* * Out here, signals are unblocked. Call timer routine * to process expiry. */ sawALRM = false; timer(); } #ifdef ENABLE_DNS_LOOKUP if (sawDNS) { sawDNS = false; dns_check(); } #endif # ifdef ENABLE_DEBUG_TIMING { l_fp pts; l_fp tsa, tsb; int bufcount = 0; get_systime(&pts); tsa = pts; # endif rbuf = get_full_recv_buffer(); while (rbuf != NULL) { if (sawALRM) { /* avoid timer starvation during lengthy I/O handling */ timer(); sawALRM = false; } /* * Call the data procedure to handle each received * packet. */ if (rbuf->receiver != NULL) { # ifdef ENABLE_DEBUG_TIMING l_fp dts = pts; dts -= rbuf->recv_time; DPRINT(2, ("processing timestamp delta %s (with prec. fuzz)\n", lfptoa(dts, 9))); collect_timing(rbuf, "buffer processing delay", 1, dts); bufcount++; # endif (*rbuf->receiver)(rbuf); } else { msyslog(LOG_ERR, "ERR: fatal: receive buffer callback NULL"); abort(); } freerecvbuf(rbuf); rbuf = get_full_recv_buffer(); } # ifdef ENABLE_DEBUG_TIMING get_systime(&tsb); tsb -= tsa; if (bufcount) { collect_timing(NULL, "processing", bufcount, tsb); DPRINT(2, ("processing time for %d buffers %s\n", bufcount, lfptoa(tsb, 9))); } } # endif /* * Check files */ if (sawHUP) { sawHUP = false; msyslog(LOG_INFO, "LOG: Saw SIGHUP"); reopen_logfile(); { time_t tnow; time(&tnow); check_leap_file(false, tnow); } } /* * Go around again */ # if defined(HAVE_DNS_SD_H) && defined(ENABLE_MDNS_REGISTRATION) if (mdnsreg && (current_time - mdnsreg ) > 60 && mdnstries && sys_leap != LEAP_NOTINSYNC) { mdnsreg = current_time; msyslog(LOG_INFO, "INIT: Attempting to register mDNS"); if ( DNSServiceRegister (&mdns, 0, 0, NULL, "_ntp._udp", NULL, NULL, htons(NTP_PORT), 0, NULL, NULL, NULL) != kDNSServiceErr_NoError ) { if (!--mdnstries) { msyslog(LOG_ERR, "INIT: Unable to register mDNS, giving up."); } else { msyslog(LOG_INFO, "INIT: Unable to register mDNS, will try later."); } } else { msyslog(LOG_INFO, "INIT: mDNS service registered."); mdnsreg = false; } } # endif /* ENABLE_MDNS_REGISTRATION */ } } /* * finish_safe - exit gracefully */ static void finish_safe( int sig ) { const char *sig_desc; sig_desc = strsignal(sig); if (sig_desc == NULL) sig_desc = ""; msyslog(LOG_NOTICE, "ERR: %s exiting on signal %d (%s)", progname, sig, sig_desc); /* See Classic Bugs 2513 and Bug 2522 re the unlink of PIDFILE */ # if defined(HAVE_DNS_SD_H) && defined(ENABLE_MDNS_REGISTRATION) if (mdns != NULL) DNSServiceRefDeallocate(mdns); # endif peer_cleanup(); exit(0); } void catchQuit( int sig ) { sawQuit = true; signo = sig; } /* * catchHUP - set flag to check files */ static void catchHUP(int sig) { UNUSED_ARG(sig); sawHUP = true; } #ifdef ENABLE_DNS_LOOKUP /* * catchDNS - set flag to process answer DNS lookup */ static void catchDNS(int sig) { UNUSED_ARG(sig); sawDNS = true; } #endif /* * wait_child_sync_if - implements parent side of -w/--wait-sync */ # ifdef HAVE_WORKING_FORK static int wait_child_sync_if( int pipe_read_fd, long wait_sync1 ) { int rc; int exit_code; time_t wait_end_time; time_t cur_time; time_t wait_rem; fd_set readset; struct timespec wtimeout; if (0 == wait_sync1) return 0; /* waitsync_fd_to_close used solely by child */ close(waitsync_fd_to_close); wait_end_time = time(NULL) + wait_sync1; do { cur_time = time(NULL); wait_rem = (wait_end_time > cur_time) ? (wait_end_time - cur_time) : 0; wtimeout.tv_sec = wait_rem; wtimeout.tv_nsec = 0; FD_ZERO(&readset); FD_SET(pipe_read_fd, &readset); rc = pselect(pipe_read_fd + 1, &readset, NULL, NULL, &wtimeout, NULL); if (-1 == rc) { if (EINTR == errno) continue; exit_code = (errno) ? errno : -1; msyslog(LOG_ERR, "ERR: --wait-sync select failed: %m"); return exit_code; } if (0 == rc) { /* * select() indicated a timeout, but in case * its timeouts are affected by a step of the * system clock, select() again with a zero * timeout to confirm. */ FD_ZERO(&readset); FD_SET(pipe_read_fd, &readset); wtimeout.tv_sec = 0; wtimeout.tv_nsec = 0; rc = pselect(pipe_read_fd + 1, &readset, NULL, NULL, &wtimeout, NULL); if (0 == rc) /* select() timeout */ break; else /* readable */ return 0; } else /* readable */ return 0; } while (wait_rem > 0); fprintf(stderr, "%s: -w/--wait-sync %ld timed out.\n", progname, wait_sync1); return ETIMEDOUT; } # endif /* HAVE_WORKING_FORK */ /* * check_minsane - check peers to see if minsane should be bigger * * This is just a first cut. It should probably fixup things automagically. * We also need to do similar for maxclock when running a pool command. * * With 2 working servers: * if they don't agree, you can't tell which one is correct * With 3 working servers, 2 can outvote a falseticker * With 4 servers, you still have 3 if one is down. */ static void check_minsane() { struct peer *peer; int servers = 0; if (sys_minsane > 1) return; /* already adjusted, assume reasonable */ for (peer = peer_list; peer != NULL; peer = peer->p_link) { if (peer->cfg.flags & FLAG_NOSELECT) continue; servers++; if (peer->cast_flags & MDF_POOL) { /* pool server */ servers = sys_maxclock; break; } /* ?? multicast and such */ } if (servers >= 5) msyslog(LOG_ERR, "SYNC: Found %d servers, suggest minsane at least 3", servers); else if (servers == 4) msyslog(LOG_ERR, "SYNC: Found 4 servers, suggest minsane of 2"); } # ifdef DEBUG /* * moredebug - increase debugging verbosity */ static void moredebug( int sig ) { int saved_errno = errno; UNUSED_ARG(sig); if (debug < 255) /* SPECIAL DEBUG */ { debug++; msyslog(LOG_DEBUG, "LOG: debug raised to %d", debug); } errno = saved_errno; } /* * lessdebug - decrease debugging verbosity */ static void lessdebug( int sig ) { int saved_errno = errno; UNUSED_ARG(sig); if (debug > 0) /* SPECIAL DEBUG */ { debug--; msyslog(LOG_DEBUG, "LOG: debug lowered to %d", debug); } errno = saved_errno; } # else /* !DEBUG follows */ /* * no_debug - We don't do the debug here. */ static void no_debug( int sig ) { int saved_errno = errno; msyslog(LOG_DEBUG, "LOG: ntpd not compiled for debugging (signal %d)", sig); errno = saved_errno; } # endif /* !DEBUG */ /* * close_all_except() * * Close all file descriptors except the given keep_fd. */ static void close_all_except( int keep_fd ) { int fd; for (fd = 0; fd < keep_fd; fd++) close(fd); close_all_beyond(keep_fd); } /* * close_all_beyond() * * Close all file descriptors after the given keep_fd, which is the * highest fd to keep open. See * * http://stackoverflow.com/questions/899038/getting-the-highest-allocated-file-descriptor */ static void close_all_beyond( int keep_fd ) { # ifdef HAVE_CLOSEFROM closefrom(keep_fd + 1); # elif defined(F_CLOSEM) /* * From 'Writing Reliable AIX Daemons,' SG24-4946-00, * by Eric Agar (saves us from doing 32767 system * calls) */ if (fcntl(keep_fd + 1, F_CLOSEM, 0) == -1) msyslog(LOG_ERR, "INIT: F_CLOSEM(%d): %m", keep_fd + 1); # else /* !HAVE_CLOSEFROM && !F_CLOSEM follows */ int fd; int max_fd; /* includes POSIX case */ max_fd = sysconf(_SC_OPEN_MAX); if (10000 < max_fd) msyslog(LOG_ERR, "INIT: close_all_beyond: closing %d files", max_fd); for (fd = keep_fd + 1; fd < max_fd; fd++) close(fd); # endif /* !HAVE_CLOSEFROM && !F_CLOSEM */ } ntpsec-1.1.0+dfsg1/ntpd/refclock_modem.c0000644000175000017500000005707513252364117017725 0ustar rlaagerrlaager/* * refclock_modem - clock driver for the NIST/USNO/PTB/NPL Computer Time * Services */ #include "config.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include #include #ifdef HAVE_SYS_IOCTL_H # include #endif /* HAVE_SYS_IOCTL_H */ /* * This driver supports the US (NIST, USNO) and European (PTB, NPL, * etc.) modem time services, as well as Spectracom GPS * receivers connected via a modem. The driver periodically dials a * number from a telephone list, receives the timecode data and * calculates the local clock correction. It is designed primarily for * use as backup when neither a radio clock nor connectivity to Internet * time servers is available. * * WARNING: The ACTS mode of this driver depends on the system clock * for year disambiguation. It will thus not be usable for recovery * if the system clock is trashed. * * This driver requires a modem with a Hayes-compatible command set * and control over the modem data terminal ready (DTR) control * line. The default modem setup string is hard-coded in the driver * and may require changes for nonstandard modems or special * circumstances. It can be overridden by setting the extended system * variable "modem_setup" via ntpq. * * When enabled, the calling program dials the first number in the * phones file. If that call fails, it dials the second number and * so on. The phone number is specified by the Hayes ATDT prefix * followed by the number itself, including the long-distance prefix * and delay code, if necessary. The calling program is enabled * when (a) option flag1 is set by ntpq, (b) at each poll interval * when no other synchronization sources are present, and (c) at each * poll interval whether or not other synchronization sources are * present. The calling program disconnects if (a) the called party * is busy or does not answer, (b) the called party disconnects * before a sufficient number of timecodes have been received. * * The driver is transparent to each of the modem time services and * Spectracom receivers. It selects the parsing algorithm depending on the * message length. There is some hazard should the message be corrupted. * However, the data format is checked carefully and only if all checks * succeed is the message accepted. Corrupted lines are discarded * without complaint. * * Options: * * flag1 force a call in manual mode * flag2 enable port locking (not verified) * flag3 not used * flag4 not used * * time1 offset adjustment (s) * * Ordinarily, the serial port is connected to a modem and the phones * list is defined. If no phones list is defined, the port can be * connected directly to a device or another computer. In this case the * driver will send a single character 'T' at each poll event. If * option flag2 is enabled, port locking allows the modem to be shared * when not in use by this driver. */ /* * National Institute of Science and Technology (NIST) * * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii) * * Data Format * * National Institute of Standards and Technology * Telephone Time Service, Generator 3B * Enter question mark "?" for HELP * D L D * MJD YR MO DA H M S ST S UT1 msADV * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) * * ... * * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is * the on-time markers echoed by the driver and used by NIST to measure * and correct for the propagation delay. Note: the ACTS timecode has * recently been changed to eliminate the * on-time indicator. The * reason for this and the long term implications are not clear. * * US Naval Observatory (USNO) * * Phone: (202) 762-1594 (Washington, DC); (719) 567-6743 (Colorado Springs, CO) * * Data Format (two lines, repeating at one-second intervals) * * jjjjj nnn hhmmss UTC * * * * jjjjj modified Julian day number (not used) * nnn day of year * hhmmss second of day * * on-time marker for previous timecode * ... * * USNO does not correct for the propagation delay. A fudge time1 of * about .06 s is advisable. * * European Services (PTB, NPL, etc.) * * PTB: +49 531 512038 (Germany) * NPL: 0906 851 6333 (UK only) * * Data format (see the documentation for phone numbers and formats.) * * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500 * * Spectracom GPS Receivers * * If a modem is connected to a Spectracom receiver, this driver will * call it up and retrieve the time in one of two formats. As this * driver does not send anything, the radio will have to either be * configured in continuous mode or be polled by another local driver. */ /* * Interface definitions */ #ifndef ENABLE_CLASSIC_MODE #define DEVICE "/dev/modem%d" /* device name and unit */ #else #define DEVICE "/dev/acts%d" /* device name and unit */ #endif #define SPEED232 B19200 /* uart speed (19200 bps) */ #define PRECISION (-10) /* precision assumed (about 1 ms) */ #define LOCKFILE "/var/spool/lock/LCK..cua%d" #define NAME "MODEM" /* shortname */ #define DESCRIPTION "Automated Computer Time Service" /* WRU */ #define REFID "NONE" /* default reference ID */ /* #define MSGCNT 20 * max message count UNUSED */ /* #define MAXPHONE 10 * max number of phone numbers UNUSED */ /* * Calling program modes (mode) */ #define MODE_BACKUP 0 /* backup mode */ #define MODE_AUTO 1 /* automatic mode */ #define MODE_MANUAL 2 /* manual mode */ /* * Service identifiers (message length) */ #define REFACTS "NIST" /* NIST reference ID */ #define LENACTS 50 /* NIST format A */ #define REFUSNO "USNO" /* USNO reference ID */ #define LENUSNO 20 /* USNO */ #define REFPTB "PTB\0" /* PTB/NPL reference ID */ #define LENPTB 78 /* PTB/NPL format */ #define REFTYPE "GPS\0" /* Spectracom reference ID */ #define LENTYPE0 22 /* SPECTRACOM format 0 */ #define LENTYPE2 24 /* SPECTRACOM format 2 */ #define LF 0x0a /* ASCII LF */ /* * Modem setup strings. These may have to be changed for * some modems. * * AT command prefix * B1 US answer tone * &C0 disable carrier detect * &D2 hang up and return to command mode on DTR transition * E0 modem command echo disabled * L1 set modem speaker volume to low level * M1 speaker enabled until carrier detect * Q0 return result codes * V1 return result codes as English words * Y1 enable long-space disconnect */ static const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1"; static const char *modem_setup = def_modem_setup; /* * Timeouts (all in seconds) */ #define SETUP 3 /* setup timeout */ #define REDIAL 30 /* redial timeout */ #define ANSWER 60 /* answer timeout */ #define TIMECODE 60 /* message timeout */ #define MAXCODE 20 /* max timecodes */ /* * State machine codes */ typedef enum { S_IDLE, /* wait for poll */ S_SETUP, /* send modem setup */ S_CONNECT, /* wait for answer */ S_MSG /* wait for timecode */ } teModemState; /* * Unit control structure */ struct modemunit { int unit; /* unit number */ int state; /* the first one was Delaware */ int timer; /* timeout counter */ int retry; /* retry index */ int msgcnt; /* count of messages received */ l_fp tstamp; /* on-time timestamp */ char *bufptr; /* next incoming char stored here */ char buf[BMAX]; /* bufptr roams within buf[] */ }; /* * Function prototypes */ static bool modem_start (int, struct peer *); static void modem_shutdown (struct refclockproc *); static void modem_receive (struct recvbuf *); static void modem_message (struct peer *, const char *); static void modem_timecode (struct peer *, const char *); static void modem_poll (int, struct peer *); static void modem_timeout (struct peer *, teModemState); static void modem_timer (int, struct peer *); static void modem_close (struct refclockproc *); /* * Transfer vector (conditional structure name) */ struct refclock refclock_modem = { NAME, /* basename of driver */ modem_start, /* start up driver */ modem_shutdown, /* shut down driver */ modem_poll, /* transmit poll message */ NULL, /* control - not used */ NULL, /* init - not used */ modem_timer /* housekeeping timer */ }; /* * Initialize data for processing */ static bool modem_start( int unit, struct peer *peer ) { struct modemunit *up; struct refclockproc *pp; const char *setup; /* * Allocate and initialize unit structure */ up = emalloc_zero(sizeof(struct modemunit)); up->unit = unit; pp = peer->procptr; pp->unitptr = up; pp->io.clock_recv = modem_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = -1; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy(&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_TELEPHONE; up->bufptr = up->buf; if (def_modem_setup == modem_setup) { setup = get_ext_sys_var("modemsetup"); if (setup != NULL) modem_setup = estrdup(setup); } return true; } /* * modem_shutdown - shut down the clock */ static void modem_shutdown( struct refclockproc *pp ) { /* * Warning: do this only when a call is not in progress. */ modem_close(pp); free(pp->unitptr); } /* * modem_receive - receive data from the serial interface */ static void modem_receive( struct recvbuf *rbufp ) { struct modemunit *up; struct refclockproc *pp; struct peer *peer; char tbuf[sizeof(up->buf)]; char * tptr; size_t octets; /* * Initialize pointers and read the timecode and timestamp. Note * we are in raw mode and victim of whatever the terminal * interface kicks up; so, we have to reassemble messages from * arbitrary fragments. Capture the timecode at the beginning of * the message and at the '*' and '#' on-time characters. */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; octets = sizeof(up->buf) - (size_t)(up->bufptr - up->buf); refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec); for (tptr = tbuf; *tptr != '\0'; tptr++) { if (*tptr == LF) { if (up->bufptr == up->buf) { up->tstamp = pp->lastrec; continue; } else { *up->bufptr = '\0'; up->bufptr = up->buf; modem_message(peer, up->buf); } } else if (!iscntrl((unsigned char)*tptr)) { *up->bufptr++ = *tptr; if (*tptr == '*' || *tptr == '#') { up->tstamp = pp->lastrec; if (write(pp->io.fd, tptr, 1) < 0) msyslog(LOG_ERR, "REFCLOCK: modem: write echo fails %m"); } } } } /* * modem_message - process message */ void modem_message( struct peer *peer, const char *msg ) { struct modemunit *up; struct refclockproc *pp; char tbuf[BMAX], *cp; int dtr = TIOCM_DTR; DPRINT(1, ("modem: %d %s\n", (int)strlen(msg), msg)); /* * What to do depends on the state and the first token in the * message. */ pp = peer->procptr; up = pp->unitptr; /* * Extract the first token in the line. */ strlcpy(tbuf, msg, sizeof(tbuf)); for (cp = tbuf; cp < tbuf + sizeof(tbuf); cp++) if (isspace((int)*cp)) *cp = '\0'; switch (up->state) { /* * We are waiting for the OK response to the modem setup * command. When this happens, dial the number followed. * If anything other than OK is received, just ignore it * and wait for timeoue. */ case S_SETUP: if (strcmp(tbuf, "OK") != 0) { /* * We disable echo with MODEM_SETUP's E0 but * if the modem was previously E1, we will * see MODEM_SETUP echoed before the OK/ERROR. * Ignore it. */ if (!strcmp(tbuf, modem_setup)) return; break; } mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s", up->retry, sys_phone[up->retry]); if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0) msyslog(LOG_ERR, "REFCLOCK: modem: ioctl(TIOCMBIS) failed: %m"); if (write(pp->io.fd, sys_phone[up->retry], strlen(sys_phone[up->retry])) < 0) msyslog(LOG_ERR, "REFCLOCK: modem: write DIAL fails %m"); IGNORE(write(pp->io.fd, "\r", 1)); up->retry++; up->state = S_CONNECT; up->timer = ANSWER; return; /* * We are waiting for the CONNECT response to the dial * command. When this happens, listen for timecodes. If * something other than CONNECT is received, like BUSY * or NO CARRIER, abort the call. */ case S_CONNECT: if (strcmp(tbuf, "CONNECT") != 0) break; report_event(PEVNT_CLOCK, peer, msg); up->state = S_MSG; up->timer = TIMECODE; return; /* * We are waiting for a timecode response. Pass it to * the parser. If NO CARRIER is received, save the * messages and abort the call. */ case S_MSG: if (strcmp(tbuf, "NO") == 0) report_event(PEVNT_CLOCK, peer, msg); if (up->msgcnt < MAXCODE) modem_timecode(peer, msg); else modem_timeout(peer, S_MSG); return; default: /* huh? */ break; } /* * Other response. Tell us about it. */ report_event(PEVNT_CLOCK, peer, msg); modem_close(peer->procptr); } /* * modem_timeout - called on timeout */ static void modem_timeout( struct peer *peer, teModemState dstate ) { struct modemunit *up; struct refclockproc *pp; int fd; ssize_t rc; char device[20]; char lockfile[128], pidbuf[8]; /* * The state machine is driven by messages from the modem, * when first started and at timeout. */ pp = peer->procptr; up = pp->unitptr; switch (dstate) { /* * System poll event. Lock the modem port, open the device * and send the setup command. */ case S_IDLE: if (-1 != pp->io.fd) return; /* port is already open */ /* * Lock the modem port. If busy, retry later. Note: if * something fails between here and the close, the lock * file may not be removed. */ if (pp->sloppyclockflag & CLK_FLAG2) { snprintf(lockfile, sizeof(lockfile), LOCKFILE, up->unit); fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0644); if (fd < 0) { report_event(PEVNT_CLOCK, peer, "modem: port busy"); return; } snprintf(pidbuf, sizeof(pidbuf), "%u\n", (unsigned int)getpid()); if (write(fd, pidbuf, strlen(pidbuf)) < 0) msyslog(LOG_ERR, "REFCLOCK: modem: write lock fails %m"); close(fd); } /* * Open the device in raw mode and link the I/O. */ snprintf(device, sizeof(device), DEVICE, up->unit); fd = refclock_open(peer->cfg.path ? peer->cfg.path : device, peer->cfg.baud ? peer->cfg.baud : SPEED232, LDISC_ACTS | LDISC_RAW | LDISC_REMOTE); if (fd < 0) { msyslog(LOG_ERR, "REFCLOCK: modem: open fails %m"); return; } pp->io.fd = fd; if (!io_addclock(&pp->io)) { msyslog(LOG_ERR, "REFCLOCK: modem: addclock fails"); close(fd); pp->io.fd = -1; return; } up->msgcnt = 0; up->bufptr = up->buf; /* * If the port is directly connected to the device, skip * the modem business and send 'T' for Spectrabum. */ if (sys_phone[up->retry] == NULL) { if (write(pp->io.fd, "T", 1) < 0) msyslog(LOG_ERR, "REFCLOCK: modem: write T fails %m"); up->state = S_MSG; up->timer = TIMECODE; return; } /* * Initialize the modem. This works with Hayes- * compatible modems. */ mprintf_event(PEVNT_CLOCK, peer, "SETUP %s", modem_setup); rc = write(pp->io.fd, modem_setup, strlen(modem_setup)); if (rc < 0) msyslog(LOG_ERR, "REFCLOCK: modem: write SETUP fails %m"); IGNORE(write(pp->io.fd, "\r", 1)); up->state = S_SETUP; up->timer = SETUP; return; /* * In SETUP state the modem did not respond OK to setup string. */ case S_SETUP: report_event(PEVNT_CLOCK, peer, "no modem"); break; /* * In CONNECT state the call did not complete. Abort the call. */ case S_CONNECT: report_event(PEVNT_CLOCK, peer, "no answer"); break; /* * In MSG states no further timecodes are expected. If any * timecodes have arrived, update the clock. In any case, * terminate the call. */ case S_MSG: if (up->msgcnt == 0) { report_event(PEVNT_CLOCK, peer, "no timecodes"); } else { pp->lastref = pp->lastrec; record_clock_stats(peer, pp->a_lastcode); refclock_receive(peer); } break; default: /* huh? */ break; } modem_close(peer->procptr); } /* * modem_close - close and prepare for next call. * * In ClOSE state no further protocol actions are required * other than to close and release the device and prepare to * dial the next number if necessary. */ void modem_close( struct refclockproc *pp ) { struct modemunit *up; char lockfile[128]; int dtr; up = pp->unitptr; if (pp->io.fd != -1) { //report_event(PEVNT_CLOCK, peer, "close"); dtr = TIOCM_DTR; if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0) msyslog(LOG_ERR, "REFCLOCK: modem: ioctl(TIOCMBIC) failed: %m"); io_closeclock(&pp->io); pp->io.fd = -1; } if (pp->sloppyclockflag & CLK_FLAG2) { snprintf(lockfile, sizeof(lockfile), LOCKFILE, up->unit); unlink(lockfile); } if (up->msgcnt == 0 && up->retry > 0) { if (sys_phone[up->retry] != NULL) { up->state = S_IDLE; up->timer = REDIAL; return; } } up->state = S_IDLE; up->timer = 0; } /* * modem_poll - called by the transmit routine */ static void modem_poll( int unit, struct peer *peer ) { struct modemunit *up; struct refclockproc *pp; UNUSED_ARG(unit); /* * This routine is called at every system poll. All it does is * set flag1 under certain conditions. The real work is done by * the timeout routine and state machine. */ pp = peer->procptr; up = pp->unitptr; switch (peer->cfg.mode) { /* * In manual mode the calling program is activated by the ntpq * program using the enable flag (flag1 option), either manually * or by a cron job. */ case MODE_MANUAL: return; /* * In automatic mode the calling program runs continuously at * intervals determined by the poll event or specified timeout. */ case MODE_AUTO: break; /* * In backup mode the calling program runs continuously as long * as either no peers are available or this peer is selected. */ case MODE_BACKUP: if (!(sys_peer == NULL || sys_peer == peer)) return; break; default: /* huh? */ break; } pp->polls++; if (S_IDLE == up->state) { up->retry = 0; modem_timeout(peer, S_IDLE); } } /* * modem_timer - called at one-second intervals */ static void modem_timer( int unit, struct peer *peer ) { struct modemunit *up; struct refclockproc *pp; UNUSED_ARG(unit); /* * This routine implements a timeout which runs for a programmed * interval. The counter is initialized by the state machine and * counts down to zero. Upon reaching zero, the state machine is * called. If flag1 is set while timer is zero, force a call. */ pp = peer->procptr; up = pp->unitptr; if (up->timer == 0) { if (pp->sloppyclockflag & CLK_FLAG1) { pp->sloppyclockflag &= ~CLK_FLAG1; modem_timeout(peer, S_IDLE); } } else { up->timer--; if (up->timer == 0) modem_timeout(peer, (teModemState)up->state); } } /* * modem_timecode - identify the service and parse the timecode message */ void modem_timecode( struct peer * peer, /* peer structure pointer */ const char * str /* timecode string */ ) { struct modemunit *up; struct refclockproc *pp; int day; /* day of the month */ int month; /* month of the year */ unsigned long mjd; /* Modified Julian Day */ double dut1; /* DUT adjustment */ unsigned int dst; /* ACTS daylight/standard time */ unsigned int leap; /* ACTS leap indicator */ double msADV; /* ACTS transmit advance (ms) */ char utc[10]; /* ACTS timescale */ char flag; /* ACTS on-time character (* or #) */ char synchar; /* Spectracom synchronized indicator */ char qualchar; /* Spectracom quality indicator */ char leapchar; /* Spectracom leap indicator */ char dstchar; /* Spectracom daylight/savings indicator */ int tz; /* Spectracom timezone */ int leapmonth; /* PTB/NPL month of leap */ char leapdir; /* PTB/NPL leap direction */ /* * The parser selects the modem format based on the message * length. Since the data are checked carefully, occasional * errors due noise are forgivable. */ pp = peer->procptr; up = pp->unitptr; pp->nsec = 0; switch (strlen(str)) { /* * For USNO format on-time character '*', which is on a line by * itself. Be sure a timecode has been received. */ case 1: if (*str == '*' && up->msgcnt > 0) break; return; /* * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa * UTC(NIST) *". */ case LENACTS: if (sscanf(str, "%5lu %2d-%2d-%2d %2d:%2d:%2d %2u %1u %3lf %5lf %9s %c", &mjd, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, &dst, &leap, &dut1, &msADV, utc, &flag) != 13) { refclock_report(peer, CEVNT_BADREPLY); return; } pp->day = ymd2yd(pp->year, month, day); pp->leap = LEAP_NOWARNING; if (leap == 1) pp->leap = LEAP_ADDSECOND; else if (leap == 2) pp->leap = LEAP_DELSECOND; memcpy(&pp->refid, REFACTS, REFIDLEN); peer->sstclktype = CTL_SST_TS_TELEPHONE; up->msgcnt++; if (flag != '#' && up->msgcnt < 10) return; break; /* * USNO format: "jjjjj nnn hhmmss UTC" */ case LENUSNO: if (sscanf(str, "%5lu %3d %2d%2d%2d %3s", &mjd, &pp->day, &pp->hour, &pp->minute, &pp->second, utc) != 6) { refclock_report(peer, CEVNT_BADREPLY); return; } /* * Wait for the on-time character, which follows in a * separate message. There is no provision for leap * warning. */ pp->leap = LEAP_NOWARNING; memcpy(&pp->refid, REFUSNO, REFIDLEN); peer->sstclktype = CTL_SST_TS_TELEPHONE; up->msgcnt++; break; /* * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" */ case LENPTB: if (sscanf(str, "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5lu%2lf%c%2d%3lf%*15c%c", &pp->second, &pp->year, &month, &day, &pp->hour, &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, &msADV, &flag) != 12) { refclock_report(peer, CEVNT_BADREPLY); return; } pp->leap = LEAP_NOWARNING; if (leapmonth == month) { if (leapdir == '+') pp->leap = LEAP_ADDSECOND; else if (leapdir == '-') pp->leap = LEAP_DELSECOND; } pp->day = ymd2yd(pp->year, month, day); memcpy(&pp->refid, REFPTB, REFIDLEN); peer->sstclktype = CTL_SST_TS_TELEPHONE; up->msgcnt++; break; /* * Spectracom format 0: "I ddd hh:mm:ss DTZ=nn" */ case LENTYPE0: if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d", &synchar, &pp->day, &pp->hour, &pp->minute, &pp->second, &dstchar, &tz) != 7) { refclock_report(peer, CEVNT_BADREPLY); return; } pp->leap = LEAP_NOWARNING; if (synchar != ' ') pp->leap = LEAP_NOTINSYNC; memcpy(&pp->refid, REFTYPE, REFIDLEN); peer->sstclktype = CTL_SST_TS_TELEPHONE; up->msgcnt++; break; /* * Spectracom format 2: "IQyy ddd hh:mm:ss.mmm LD" */ case LENTYPE2: if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c", &synchar, &qualchar, &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec, &dstchar, &leapchar, &dstchar) != 11) { refclock_report(peer, CEVNT_BADREPLY); return; } pp->nsec *= 1000000; pp->leap = LEAP_NOWARNING; if (synchar != ' ') pp->leap = LEAP_NOTINSYNC; else if (leapchar == 'L') pp->leap = LEAP_ADDSECOND; memcpy(&pp->refid, REFTYPE, REFIDLEN); peer->sstclktype = CTL_SST_TS_TELEPHONE; up->msgcnt++; break; /* * None of the above. Just forget about it and wait for the next * message or timeout. */ default: return; } /* * We have a valid timecode. The fudge time1 value is added to * each sample by the main line routines. Note that in current * telephone networks the propatation time can be different for * each call and can reach 200 ms for some calls. */ peer->refid = pp->refid; pp->lastrec = up->tstamp; if (up->msgcnt == 0) return; strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode)); pp->lencode = (int)strlen(pp->a_lastcode); if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } pp->lastref = pp->lastrec; } ntpsec-1.1.0+dfsg1/ntpd/ntp_sandbox.c0000644000175000017500000003123213252364117017256 0ustar rlaagerrlaager/* * ntp_sandbox.c - privilege containment for the NTP daemon * * SPDX-License-Identifier: BSD-2-clause */ #include "config.h" #include #include #include #include #include #include #include "ntp_assert.h" #ifdef ENABLE_DROPROOT # include # include # include # ifdef HAVE_LINUX_CAPABILITY # include # include # endif /* HAVE_LINUX_CAPABILITY */ # if defined(HAVE_PRIV_H) && defined(HAVE_SOLARIS_PRIVS) # include # endif /* HAVE_PRIV_H */ #endif /* ENABLE_DROPROOT */ #ifdef HAVE_SOLARIS_PRIVS # define LOWPRIVS "basic,sys_time,net_privaddr,proc_setid,!proc_info,!proc_session,!proc_exec" static priv_set_t *lowprivs = NULL; static priv_set_t *highprivs = NULL; #endif /* HAVE_SOLARIS_PRIVS */ #ifdef HAVE_SECCOMP_H # include static void catchTrap(int sig, siginfo_t *, void *); #endif #ifdef ENABLE_DROPROOT static uid_t sw_uid; static gid_t sw_gid; static char *endp; static struct group *gr; static struct passwd *pw; #endif /* ENABLE_DROPROOT */ #include "ntp_syslog.h" #include "ntp_stdlib.h" bool sandbox(const bool droproot, char *user, const char *group, const char *chrootdir, bool want_dynamic_interface_tracking) { bool nonroot = false; #ifndef ENABLE_DROPROOT UNUSED_ARG(droproot); UNUSED_ARG(user); UNUSED_ARG(group); UNUSED_ARG(chrootdir); UNUSED_ARG(want_dynamic_interface_tracking); #else #ifndef HAVE_LINUX_CAPABILITY UNUSED_ARG(want_dynamic_interface_tracking); #endif /* HAVE_LINUX_CAPABILITY */ #if !defined(HAVE_LINUX_CAPABILITY) && !defined(HAVE_SOLARIS_PRIVS) && !defined(HAVE_SYS_CLOCKCTL_H) if (droproot) { msyslog(LOG_ERR, "INIT: root can't be dropped due to missing capabilities."); exit(-1); } #endif /* !defined(HAVE_LINUX_CAPABILITY) && !defined(HAVE_SOLARIS_PRIVS) && !defined(HAVE_SYS_CLOCKCTL_H) */ if (droproot) { /* Drop super-user privileges and chroot now if the OS supports this */ # ifdef HAVE_LINUX_CAPABILITY /* set flag: keep privileges across setuid() call. */ if (prctl( PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L ) == -1) { msyslog( LOG_ERR, "INIT: prctl( PR_SET_KEEPCAPS, 1L ) failed: %m" ); exit(-1); } # elif defined(HAVE_SOLARIS_PRIVS) /* Nothing to do here */ # else /* we need a user to switch to */ if (user == NULL) { msyslog(LOG_ERR, "INIT: Need user name to drop root privileges (see -u flag!)" ); exit(-1); } # endif /* HAVE_LINUX_CAPABILITY || HAVE_SOLARIS_PRIVS */ if (user != NULL) { if (isdigit((unsigned char)*user)) { sw_uid = (uid_t)strtoul(user, &endp, 0); if (*endp != '\0') goto getuser; if ((pw = getpwuid(sw_uid)) != NULL) { free((void *)user); user = estrdup(pw->pw_name); sw_gid = pw->pw_gid; } else { errno = 0; msyslog(LOG_ERR, "INIT: Cannot find user ID %s", user); exit (-1); } } else { getuser: errno = 0; if ((pw = getpwnam(user)) != NULL) { sw_uid = pw->pw_uid; sw_gid = pw->pw_gid; } else { if (errno) msyslog(LOG_ERR, "INIT: getpwnam(%s) failed: %m", user); else msyslog(LOG_ERR, "INIT: Cannot find user `%s'", user); exit (-1); } } } if (group != NULL) { if (isdigit((unsigned char)*group)) { sw_gid = (gid_t)strtoul(group, &endp, 0); if (*endp != '\0') goto getgroup; } else { getgroup: if ((gr = getgrnam(group)) != NULL) { sw_gid = gr->gr_gid; } else { errno = 0; msyslog(LOG_ERR, "INIT: Cannot find group `%s'", group); exit (-1); } } } if (chrootdir ) { /* make sure cwd is inside the jail: */ if (chdir(chrootdir)) { msyslog(LOG_ERR, "INIT: Cannot chdir() to `%s': %m", chrootdir); exit (-1); } if (chroot(chrootdir)) { msyslog(LOG_ERR, "INIT: Cannot chroot() to `%s': %m", chrootdir); exit (-1); } if (chdir("/")) { msyslog(LOG_ERR, "INIT: Cannot chdir() to root after chroot(): %m"); exit (-1); } } # ifdef HAVE_SOLARIS_PRIVS if ((lowprivs = priv_str_to_set(LOWPRIVS, ",", NULL)) == NULL) { msyslog(LOG_ERR, "INIT: priv_str_to_set() failed:%m"); exit(-1); } if ((highprivs = priv_allocset()) == NULL) { msyslog(LOG_ERR, "INIT: priv_allocset() failed:%m"); exit(-1); } (void) getppriv(PRIV_PERMITTED, highprivs); (void) priv_intersect(highprivs, lowprivs); if (setppriv(PRIV_SET, PRIV_PERMITTED, lowprivs) == -1) { msyslog(LOG_ERR, "INIT: setppriv() failed:%m"); exit(-1); } # endif /* HAVE_SOLARIS_PRIVS */ /* FIXME? Apple takes an int as 2nd argument */ if (user && initgroups(user, (gid_t)sw_gid)) { msyslog(LOG_ERR, "INIT: Cannot initgroups() to user `%s': %m", user); exit (-1); } if (group && setgid(sw_gid)) { msyslog(LOG_ERR, "INIT: Cannot setgid() to group `%s': %m", group); exit (-1); } if (group && setegid(sw_gid)) { msyslog(LOG_ERR, "INIT: Cannot setegid() to group `%s': %m", group); exit (-1); } if (group) { if (0 != setgroups(1, &sw_gid)) { msyslog(LOG_ERR, "INIT: setgroups(1, %u) failed: %m", sw_gid); exit (-1); } } else if (pw) if (0 != initgroups(pw->pw_name, (gid_t)pw->pw_gid)) { msyslog(LOG_ERR, "INIT: initgroups(<%s>, %u) filed: %m", pw->pw_name, pw->pw_gid); exit (-1); } if (user && setuid(sw_uid)) { msyslog(LOG_ERR, "INIT: Cannot setuid() to user `%s': %m", user); exit (-1); } if (user && seteuid(sw_uid)) { msyslog(LOG_ERR, "INIT: Cannot seteuid() to user `%s': %m", user); exit (-1); } # if !defined(HAVE_LINUX_CAPABILITY) && !defined(HAVE_SOLARIS_PRIVS) /* * for now assume that the privilege to bind to privileged ports * is associated with running with uid 0 - should be refined on * ports that allow binding to NTP_PORT with uid != 0 */ nonroot |= (sw_uid != 0); /* also notifies routing message listener */ # endif /* !HAVE_LINUX_CAPABILITY && !HAVE_SOLARIS_PRIVS */ # ifdef HAVE_LINUX_CAPABILITY { /* * We may be running under non-root uid now, * but we still hold full root privileges! * We drop all of them, except for the * crucial few: cap_sys_nice, cap_sys_time and * cap_net_bind_service for doing dynamic * interface tracking. */ cap_t caps; const char *captext; captext = want_dynamic_interface_tracking ? "cap_sys_nice,cap_sys_time,cap_net_bind_service=pe" : "cap_sys_nice,cap_sys_time=pe"; caps = cap_from_text(captext); if (!caps) { msyslog(LOG_ERR, "INIT: cap_from_text(%s) failed: %m", captext); exit(-1); } if (-1 == cap_set_proc(caps)) { msyslog(LOG_ERR, "INIT: cap_set_proc() failed to drop root privs: %m"); exit(-1); } cap_free(caps); } # endif /* HAVE_LINUX_CAPABILITY */ # ifdef HAVE_SOLARIS_PRIVS if (priv_delset(lowprivs, "proc_setid") == -1) { msyslog(LOG_ERR, "INIT: priv_delset() failed:%m"); exit(-1); } if (setppriv(PRIV_SET, PRIV_PERMITTED, lowprivs) == -1) { msyslog(LOG_ERR, "INIT: setppriv() failed:%m"); exit(-1); } priv_freeset(lowprivs); priv_freeset(highprivs); # endif /* HAVE_SOLARIS_PRIVS */ } /* if (droproot) */ # endif /* ENABLE_DROPROOT */ /* libseccomp sandboxing */ #if defined(HAVE_SECCOMP_H) #ifdef ENABLE_KILL_ON_TRAP #define MY_SCMP_ACT SCMP_ACT_KILL #else #define MY_SCMP_ACT SCMP_ACT_TRAP #endif scmp_filter_ctx ctx = seccomp_init(MY_SCMP_ACT); signal_no_reset1(SIGSYS, catchTrap); if (NULL == ctx) { msyslog(LOG_ERR, "INIT: sandbox: seccomp_init() failed: %m"); exit (1); } int scmp_sc[] = { #ifdef ENABLE_EARLY_DROPROOT /* Initialization uses a few syscalls that are not otherwise used. * Collect them here. * There are probably a few below that were added before we * understood the need for this section. * We could make a second pass after initialization to remove * these from the list. */ #ifndef ENABLE_DNS_LOOKUP /* libcrypto uses pthread_once() */ /* We could avoid this by calling ssl_init() first. */ SCMP_SYS(futex), /* sem_xxx, used by threads */ #endif SCMP_SYS(getdents), /* Scanning /etc/ntp.d/ */ #endif SCMP_SYS(adjtimex), SCMP_SYS(bind), SCMP_SYS(brk), SCMP_SYS(chdir), SCMP_SYS(clock_gettime), SCMP_SYS(clock_settime), SCMP_SYS(close), SCMP_SYS(connect), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(fcntl), SCMP_SYS(fstat), SCMP_SYS(fsync), #ifdef __NR_getrandom SCMP_SYS(getrandom), /* Added in 3.17 kernel */ #endif SCMP_SYS(getitimer), #ifdef __NR_ugetrlimit SCMP_SYS(ugetrlimit), /* sysconf */ #endif #ifdef __NR_getrlimit SCMP_SYS(getrlimit), /* sysconf */ SCMP_SYS(setrlimit), #endif #ifdef __NR_prlimit64 SCMP_SYS(prlimit64), /* 64 bit Fedora 26 with early_droproot*/ #endif SCMP_SYS(getrusage), SCMP_SYS(getsockname), SCMP_SYS(getsockopt), SCMP_SYS(gettimeofday), /* mkstemp */ SCMP_SYS(ioctl), SCMP_SYS(link), SCMP_SYS(lseek), SCMP_SYS(munmap), SCMP_SYS(open), #ifdef __NR_openat SCMP_SYS(openat), /* SUSE */ #endif SCMP_SYS(poll), SCMP_SYS(pselect6), SCMP_SYS(read), SCMP_SYS(recvfrom), /* Comment this out for testing. * It will die on the first reply. * (Or maybe sooner if a request arrives.) */ SCMP_SYS(recvmsg), SCMP_SYS(rename), SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigprocmask), SCMP_SYS(rt_sigreturn), SCMP_SYS(sigaction), SCMP_SYS(sigprocmask), SCMP_SYS(sigreturn), #ifdef __NR_select SCMP_SYS(select), /* not in ARM */ #endif SCMP_SYS(sendto), SCMP_SYS(setitimer), SCMP_SYS(setsid), #ifdef __NR_setsockopt SCMP_SYS(setsockopt), /* not in old kernels */ #endif SCMP_SYS(socket), SCMP_SYS(socketcall), /* old kernels */ SCMP_SYS(stat), SCMP_SYS(statfs64), /* from getaddrinfo after lid open */ #ifdef __NR_time SCMP_SYS(time), /* not in ARM */ #endif SCMP_SYS(write), SCMP_SYS(unlink), #ifdef ENABLE_DNS_LOOKUP /* Don't comment out this block for testing. * pthread_create blocks signals so it will crash * rather than generate a trap. */ SCMP_SYS(clone), /* threads */ SCMP_SYS(futex), /* sem_xxx, used by threads */ SCMP_SYS(kill), /* generate signal */ SCMP_SYS(madvise), SCMP_SYS(mprotect), SCMP_SYS(set_robust_list), SCMP_SYS(sendmmsg), /* DNS lookup */ SCMP_SYS(socketpair), SCMP_SYS(statfs), SCMP_SYS(uname), #endif SCMP_SYS(access), #ifdef REFCLOCK SCMP_SYS(nanosleep), #endif #ifdef CLOCK_SHM SCMP_SYS(shmget), SCMP_SYS(shmat), SCMP_SYS(shmdt), #endif SCMP_SYS(fcntl64), SCMP_SYS(fstat64), /* Arch Linux */ SCMP_SYS(getpid), SCMP_SYS(gettid), SCMP_SYS(geteuid), SCMP_SYS(ppoll), SCMP_SYS(sendmsg), #ifdef __NR_mmap /* gentoo 64-bit and 32-bit, Intel and Arm use mmap */ SCMP_SYS(mmap), #endif #if defined(__i386__) || defined(__arm__) SCMP_SYS(_newselect), SCMP_SYS(_llseek), SCMP_SYS(mmap2), SCMP_SYS(send), SCMP_SYS(stat64), #endif }; { unsigned int i; for (i = 0; i < COUNTOF(scmp_sc); i++) { if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, scmp_sc[i], 0) < 0) { msyslog(LOG_ERR, "INIT: sandbox: seccomp_rule_add() failed: %m"); exit(1); } } } if (seccomp_load(ctx) < 0) { msyslog(LOG_ERR, "INIT: sandbox: seccomp_load() failed: %m"); exit(1); } else { msyslog(LOG_NOTICE, "INIT: sandbox: seccomp enabled."); } #endif /* HAVE_SECCOMP_H */ return nonroot; } #ifdef HAVE_SECCOMP_H /* * catchTrap - get here if something missing from list above * (or a bad guy finds a way in) * * The list above is a moving target. Most syscalls will be * obvious but libc (and friends) can remap things and * getaddrinfo does all sorts of syscalls. * * To track down a missing call: * * Option one: * The code below should print out the syscall number. * grep _NR_ /usr/include/ -r | grep -w * You will get several hits for various architures/modes. * You can probably guess the right one. * * Option two: * use strace * sudo strace -t -f -o * When it crashes, the last syscall will be near the end of the log file * just before the line with "--- SIGSYS", a page or two from the end * depending on the stack trace. * * Option three: * use gdb, break on catchTrap, get a trace. * If you have the symbols, you can probably guess the syscall. * You may have to get the sources for libc. * */ static void catchTrap(int sig, siginfo_t *si, void *u) { UNUSED_ARG(sig); /* signal number */ UNUSED_ARG(u); /* unused ucontext_t */ msyslog(LOG_ERR, "ERR: SIGSYS: got a trap.\n"); if ( si->si_syscall ) { msyslog(LOG_ERR, "ERR: SIGSYS/seccomp bad syscall %d/%#x\n", si->si_syscall, si->si_arch); } #ifndef BACKTRACE_DISABLED backtrace_log(); #endif exit(1); } #endif /* HAVE_SECCOMP_H */ /* end */ ntpsec-1.1.0+dfsg1/ntpd/ntp.keys-man.txt0000644000175000017500000000045013252364117017656 0ustar rlaagerrlaager= ntp.keys(5) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntp.keys - NTP symmetric key file format include::../docs/includes/ntp.keys-body.txt[] == SEE ALSO == {ntpdconfman}, {ntpdman}, {ntpqman}, {ntpkeygenman}, {ntpdigman}. // end ntpsec-1.1.0+dfsg1/ntpd/refclock_nmea.c0000644000175000017500000015753713252364117017550 0ustar rlaagerrlaager/* * refclock_nmea.c - clock driver for an NMEA GPS CLOCK * Michael Petry Jun 20, 1994 * based on refclock_heathn.c * * Updated to add support for Accord GPS Clock * Venu Gopal Dec 05, 2007 * neo.venu@gmail.com, venugopal_d@pgad.gov.in * * Updated to process 'time1' fudge factor * Venu Gopal May 05, 2008 * * Converted to common PPSAPI code, separate PPS fudge time1 * from serial timecode fudge time2. * Dave Hart July 1, 2009 * hart@ntp.org, davehart@davehart.com */ #define NMEA_WRITE_SUPPORT 0 /* no write support at the moment */ #include "config.h" #include "ntp_types.h" #include #include #include #include #include "ntp.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" #include "timespecops.h" #ifdef HAVE_PPSAPI # include "ppsapi_timepps.h" # include "refclock_pps.h" #endif /* HAVE_PPSAPI */ /* * This driver supports NMEA-compatible GPS receivers * * Prototype was refclock_trak.c, Thanks a lot. * * The receiver used spits out the NMEA sentences for boat navigation. * And you thought it was an information superhighway. Try a raging river * filled with rapids and whirlpools that rip away your data and warp time. * * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in. * On startup if initialization of the PPSAPI fails, it will fall back * to the "normal" timestamps. * * The PPSAPI part of the driver understands options flag2 and flag3. If * flag2 is set, it will use the clear edge of the pulse. If flag3 is * set, kernel hardpps is enabled. * * GPS sentences other than RMC (the default) may be enabled by setting * the relevant bits of 'mode' in the server configuration line * refclock u mode X * * bit 0 - enables RMC (1) * bit 1 - enables GGA (2) * bit 2 - enables GLL (4) * bit 3 - enables ZDA (8) - Standard Time & Date * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time * very close to standard ZDA * * Multiple sentences may be selected except when ZDG/ZDA is selected. * * bit 4/5/6 - selects the baudrate for serial port : * 0 for 4800 (default) * 1 for 9600 * 2 for 19200 * 3 for 38400 * 4 for 57600 * 5 for 115200 */ #define NMEA_MESSAGE_MASK 0x0000FF0FU #define NMEA_BAUDRATE_MASK 0x00000070U #define NMEA_BAUDRATE_SHIFT 4 #define NMEA_DELAYMEAS_MASK 0x80 #define NMEA_EXTLOG_MASK 0x00010000U #define NMEA_DATETRUST_MASK 0x02000000U #define NMEA_PROTO_IDLEN 5 /* tag name must be at least 5 chars */ /* #define NMEA_PROTO_MINLEN 6 * min chars in sentence, excluding CS UNUSED */ #define NMEA_PROTO_MAXLEN 80 /* max chars in sentence, excluding CS */ #define NMEA_PROTO_FIELDS 32 /* not official; limit on fields per record */ /* * We check the timecode format and decode its contents. We only care * about a few of them, the most important being the $GPRMC format: * * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC * * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21 * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77 * * Defining GPZDA to support Standard Time & Date * sentence. The sentence has the following format * * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS * * Apart from the familiar fields, * 'TH' Time zone Hours * 'TM' Time zone Minutes * * Defining GPZDG to support Accord GPS Clock's custom NMEA * sentence. The sentence has the following format * * $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS * * It contains the GPS timestamp valid for next PPS pulse. * Apart from the familiar fields, * 'AA.BB' denotes the signal strength( should be < 05.00 ) * 'V' denotes the GPS sync status : * '0' indicates INVALID time, * '1' indicates accuracy of +/-20 ms * '2' indicates accuracy of +/-100 ns * * Defining PGRMF for Garmin GPS Fix Data * $PGRMF,WN,WS,DATE,TIME,LS,LAT,LAT_DIR,LON,LON_DIR,MODE,FIX,SPD,DIR,PDOP,TDOP * WN -- GPS week number (weeks since 1980-01-06, mod 1024) * WS -- GPS seconds in week * LS -- GPS leap seconds, accumulated ( UTC + LS == GPS ) * FIX -- Fix type: 0=nofix, 1=2D, 2=3D * DATE/TIME are standard date/time strings in UTC time scale * * The GPS time can be used to get the full century for the truncated * date spec. */ /* * Definitions */ #define DEVICE "/dev/gps%d" /* GPS serial device */ #ifdef HAVE_PPSAPI # define PPSDEV "/dev/gpspps%d" /* PPSAPI device override */ # define PPS_PRECISION (-20) /* precision assumed (~ 1 us) */ #endif #ifdef ENABLE_CLASSIC_MODE #define SPEED232 B4800 /* uart speed (4800 bps) */ #else #define SPEED232 B9600 /* uart speed (9600 bps) */ #endif #define PRECISION (-9) /* precision assumed (about 2 ms) */ #define REFID "GPS\0" /* reference id */ #define NAME "NMEA" /* shortname */ #define DESCRIPTION "NMEA GPS Clock" /* who we are */ /* NMEA sentence array indexes for those we use */ #define NMEA_GPRMC 0 /* recommended min. nav. */ #define NMEA_GPGGA 1 /* fix and quality */ #define NMEA_GPGLL 2 /* geo. lat/long */ #define NMEA_GPZDA 3 /* date/time */ /* * $GPZDG is a proprietary sentence that violates the spec, by not * using $P and an assigned company identifier to prefix the sentence * identifier. When used with this driver, the system needs to be * isolated from other NTP networks, as it operates in GPS time, not * UTC as is much more common. GPS time is >15 seconds different from * UTC due to not respecting leap seconds since 1970 or so. Other * than the different timebase, $GPZDG is similar to $GPZDA. */ #define NMEA_GPZDG 4 #define NMEA_PGRMF 5 #define NMEA_ARRAY_SIZE (NMEA_PGRMF + 1) /* * Sentence selection mode bits */ #define USE_GPRMC 0x00000001u #define USE_GPGGA 0x00000002u #define USE_GPGLL 0x00000004u #define USE_GPZDA 0x00000008u #define USE_PGRMF 0x00000100u /* mapping from sentence index to controlling mode bit */ static const uint32_t sentence_mode[NMEA_ARRAY_SIZE] = { USE_GPRMC, USE_GPGGA, USE_GPGLL, USE_GPZDA, USE_GPZDA, USE_PGRMF }; /* date formats we support */ enum date_fmt { DATE_1_DDMMYY, /* use 1 field with 2-digit year */ DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */ }; /* results for 'field_init()' * * Note: If a checksum is present, the checksum test must pass OK or the * sentence is tagged invalid. */ #define CHECK_EMPTY -1 /* no data */ #define CHECK_INVALID 0 /* not a valid NMEA sentence */ #define CHECK_VALID 1 /* valid but without checksum */ #define CHECK_CSVALID 2 /* valid with checksum OK */ /* * Unit control structure */ typedef struct { #ifdef HAVE_PPSAPI struct refclock_ppsctl ppsctl; /* PPSAPI structure */ int ppsapi_fd; /* fd used with PPSAPI */ bool ppsapi_tried; /* attempt PPSAPI once */ bool ppsapi_lit; /* time_pps_create() worked */ bool ppsapi_gate; /* system is on PPS */ #endif /* HAVE_PPSAPI */ bool gps_time; /* use GPS time, not UTC */ unsigned short century_cache; /* cached current century */ l_fp last_reftime; /* last processed reference stamp */ short epoch_warp; /* last epoch warp, for logging */ /* tally stats, reset each poll cycle */ struct { unsigned int total; unsigned int accepted; unsigned int rejected; /* GPS said not enough signal */ unsigned int malformed; /* Bad checksum, invalid date or time */ unsigned int filtered; /* mode bits, not GPZDG, same second */ unsigned int pps_used; } tally; /* per sentence checksum seen flag */ uint8_t cksum_type[NMEA_ARRAY_SIZE]; } nmea_unit; /* * helper for faster field access */ typedef struct { char *base; /* buffer base */ char *cptr; /* current field ptr */ int blen; /* buffer length */ int cidx; /* current field index */ } nmea_data; /* * NMEA gps week/time information * This record contains the number of weeks since 1980-01-06 modulo * 1024, the seconds elapsed since start of the week, and the number of * leap seconds that are the difference between GPS and UTC time scale. */ typedef struct { uint32_t wt_time; /* seconds since weekstart */ unsigned short wt_week; /* week number */ short wt_leap; /* leap seconds */ } gps_weektm; /* * The GPS week time scale starts on Sunday, 1980-01-06. We need the * rata die number of this day. */ #ifndef DAY_GPS_STARTS #define DAY_GPS_STARTS 722820 #endif /* * Function prototypes */ static void nmea_init (void); static bool nmea_start (int, struct peer *); static void nmea_shutdown (struct refclockproc *); static void nmea_receive (struct recvbuf *); static void nmea_poll (int, struct peer *); #ifdef HAVE_PPSAPI static void nmea_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); #define NMEA_CONTROL nmea_control #else #define NMEA_CONTROL NULL #endif /* HAVE_PPSAPI */ static void nmea_timer (int, struct peer *); /* parsing helpers */ static int field_init (nmea_data * data, char * cp, int len); static char * field_parse (nmea_data * data, int fn); static void field_wipe (nmea_data * data, ...); static uint8_t parse_qual (nmea_data * data, int idx, char tag, int inv); static bool parse_time (struct calendar * jd, long * nsec, nmea_data *, int idx); static bool parse_date (struct calendar *jd, nmea_data*, int idx, enum date_fmt fmt); static bool parse_weekdata (gps_weektm *, nmea_data *, int weekidx, int timeidx, int leapidx); /* calendar / date helpers */ static bool unfold_day (struct calendar * jd, uint32_t rec_ui); static bool unfold_century (struct calendar * jd, uint32_t rec_ui); static bool gpsfix_century (struct calendar * jd, const gps_weektm * wd, unsigned short * ccentury); static l_fp eval_gps_time (struct peer * peer, const struct calendar * gpst, const struct timespec * gpso, const l_fp * xrecv); static void save_ltc (struct refclockproc * const, const char * const, size_t); /* * If we want the driver to output sentences, too: re-enable the send * support functions by defining NMEA_WRITE_SUPPORT to non-zero... */ #if NMEA_WRITE_SUPPORT static void gps_send(int, const char *, struct peer *); #endif /* NMEA_WRITE_SUPPORT */ static int32_t g_gpsMinBase; static int32_t g_gpsMinYear; /* * ------------------------------------------------------------------- * Transfer vector * ------------------------------------------------------------------- */ struct refclock refclock_nmea = { NAME, /* basename of driver */ nmea_start, /* start up driver */ nmea_shutdown, /* shut down driver */ nmea_poll, /* transmit poll message */ NMEA_CONTROL, /* fudge control */ nmea_init, /* initialize driver */ nmea_timer /* called once per second */ }; /* * ------------------------------------------------------------------- * nmea_init - initialise data * * calculates a few runtime constants that cannot be made compile time * constants. * ------------------------------------------------------------------- */ static void nmea_init(void) { struct calendar date; /* - calculate min. base value for GPS epoch & century unfolding * This assumes that the build system was roughly in sync with * the world, and that really synchronising to a time before the * program was created would be unsafe or insane. If the build * date cannot be stablished, at least use the start of GPS * (1980-01-06) as minimum, because GPS can surely NOT * synchronise beyond it's own big bang. We add a little safety * margin for the fuzziness of the build date, which is in an * undefined time zone. */ if (ntpcal_get_build_date(&date)) g_gpsMinBase = ntpcal_date_to_rd(&date) - 2; else g_gpsMinBase = 0; if (g_gpsMinBase < DAY_GPS_STARTS) g_gpsMinBase = DAY_GPS_STARTS; ntpcal_rd_to_date(&date, g_gpsMinBase); g_gpsMinYear = date.year; g_gpsMinBase -= DAY_NTP_STARTS; } /* * ------------------------------------------------------------------- * nmea_start - open the GPS devices and initialize data for processing * * return false on error, true on success. Even on error the peer structures * must be in a state that permits 'nmea_shutdown()' to clean up all * resources, because it will be called immediately to do so. * ------------------------------------------------------------------- */ static bool nmea_start( int unit, struct peer * peer ) { struct refclockproc * const pp = peer->procptr; nmea_unit * const up = emalloc_zero(sizeof(*up)); char device[20], *path; uint32_t rate; unsigned int baudrate; const char * baudtext; int rcode; /* Old style: get baudrate choice from mode byte bits 4/5/6 */ rate = (peer->cfg.mode & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT; /* New style: get baudrate from baud option */ if (peer->cfg.baud) rate = peer->cfg.baud; switch (rate) { case 0: case 4800: baudrate = B4800; baudtext = "4800"; break; case 1: case 9600: baudrate = B9600; baudtext = "9600"; break; case 2: case 19200: baudrate = B19200; baudtext = "19200"; break; case 3: case 38400: baudrate = B38400; baudtext = "38400"; break; #ifdef B57600 case 4: case 57600: baudrate = B57600; baudtext = "57600"; break; #endif #ifdef B115200 case 5: case 115200: baudrate = B115200; baudtext = "115200"; break; #endif default: baudrate = SPEED232; #ifdef ENABLE_CLASSIC_MODE baudtext = "4800 (fallback)"; #else baudtext = "9600 (fallback)"; #endif break; } /* Allocate and initialize unit structure */ pp->unitptr = (void *)up; pp->io.fd = -1; pp->io.clock_recv = nmea_receive; pp->io.srcclock = peer; pp->io.datalen = 0; /* force change detection on first valid message */ memset(&up->last_reftime, 0xFF, sizeof(up->last_reftime)); /* force checksum on GPRMC, see below */ up->cksum_type[NMEA_GPRMC] = CHECK_CSVALID; #ifdef HAVE_PPSAPI up->ppsapi_fd = -1; #endif ZERO(up->tally); /* Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy(&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; if (peer->cfg.path) path = peer->cfg.path; else { /* build a path */ rcode = snprintf(device, sizeof(device), DEVICE, unit); if ( 0 > rcode ) { /* failed, set to NUL */ device[0] = '\0'; } path = device; } /* Open serial port. Use CLK line discipline, if available. */ pp->io.fd = refclock_open(path, baudrate, LDISC_CLK); if (0 > pp->io.fd) { msyslog(LOG_ERR, "REFCLOCK: %s NMEA device open(%s) failed", refclock_name(peer), path); return false; } LOGIF(CLOCKINFO, (LOG_NOTICE, "%s serial %s open at %s bps", refclock_name(peer), path, baudtext)); /* succeed if this clock can be added */ return io_addclock(&pp->io) != 0; } /* * ------------------------------------------------------------------- * nmea_shutdown - shut down a GPS clock * * NOTE this routine is called after nmea_start() returns failure, * as well as during a normal shutdown due to ntpq :config unpeer. * ------------------------------------------------------------------- */ static void nmea_shutdown( struct refclockproc * pp ) { nmea_unit * const up = (nmea_unit *)pp->unitptr; if (up != NULL) { #ifdef HAVE_PPSAPI if (up->ppsapi_lit) time_pps_destroy(up->ppsctl.handle); if (up->ppsapi_tried && up->ppsapi_fd != pp->io.fd) close(up->ppsapi_fd); #endif free(up); } pp->unitptr = (void *)NULL; if (-1 != pp->io.fd) io_closeclock(&pp->io); pp->io.fd = -1; } /* * ------------------------------------------------------------------- * nmea_control - configure fudge params * ------------------------------------------------------------------- */ #ifdef HAVE_PPSAPI static void nmea_control( int unit, const struct refclockstat * in_st, struct refclockstat * out_st, struct peer * peer ) { struct refclockproc * const pp = peer->procptr; nmea_unit * const up = (nmea_unit *)pp->unitptr; char device[32]; UNUSED_ARG(in_st); UNUSED_ARG(out_st); /* * PPS control * * If /dev/gpspps$UNIT can be opened that will be used for * PPSAPI. Otherwise, the GPS serial device /dev/gps$UNIT * already opened is used for PPSAPI as well. (This might not * work, in which case the PPS API remains unavailable...) */ /* Light up the PPSAPI interface if not yet attempted. */ if ((CLK_FLAG1 & pp->sloppyclockflag) && !up->ppsapi_tried) { up->ppsapi_tried = true; if ( !peer->cfg.ppspath ) { int rcode; /* build a path */ rcode = snprintf(device, sizeof(device), PPSDEV, unit); if ( 0 > rcode ) { /* failed, set to NUL */ device[0] = '\0'; } peer->cfg.ppspath = estrdup( device ); } if ( peer->cfg.ppspath ) { up->ppsapi_fd = open(peer->cfg.ppspath, O_RDWR | O_NOCTTY | O_NONBLOCK); } else { up->ppsapi_fd = -1; } if ( 0 <= up->ppsapi_fd) { LOGIF(CLOCKINFO, (LOG_NOTICE, "%s PPS %s opened", refclock_name(peer), peer->cfg.ppspath)); } else { /* fall back to primary device */ up->ppsapi_fd = pp->io.fd; msyslog(LOG_ERR, "REFCLOCK: %s PPS device open(%s) failed", refclock_name(peer), peer->cfg.ppspath); } if (refclock_ppsapi(up->ppsapi_fd, &up->ppsctl)) { /* use the PPS API for our own purposes now. */ up->ppsapi_lit = refclock_params( pp->sloppyclockflag, &up->ppsctl); if (!up->ppsapi_lit) { /* failed to configure, drop PPS unit */ time_pps_destroy(up->ppsctl.handle); msyslog(LOG_WARNING, "REFCLOCK: %s set PPSAPI params fails", refclock_name(peer)); } /* note: the PPS I/O handle remains valid until * flag1 is cleared or the clock is shut down. */ } else { msyslog(LOG_WARNING, "REFCLOCK: %s flag1 1 but PPSAPI fails", refclock_name(peer)); } } /* shut down PPS API if activated */ if (!(CLK_FLAG1 & pp->sloppyclockflag) && up->ppsapi_tried) { /* shutdown PPS API */ if (up->ppsapi_lit) time_pps_destroy(up->ppsctl.handle); up->ppsctl.handle = 0; /* close/drop PPS fd */ if (up->ppsapi_fd != pp->io.fd) close(up->ppsapi_fd); up->ppsapi_fd = -1; /* clear markers and peer items */ up->ppsapi_gate = false; up->ppsapi_lit = false; up->ppsapi_tried = false; peer->cfg.flags &= ~FLAG_PPS; peer->precision = PRECISION; } } #endif /* HAVE_PPSAPI */ /* * ------------------------------------------------------------------- * nmea_timer - called once per second * this only polls (older?) Oncore devices now * * Usually 'nmea_receive()' can get a timestamp every second, but at * least one Motorola unit needs prompting each time. Doing so in * 'nmea_poll()' gives only one sample per poll cycle, which actually * defeats the purpose of the median filter. Polling once per second * seems a much better idea. * ------------------------------------------------------------------- */ static void nmea_timer( int unit, struct peer * peer ) { #if NMEA_WRITE_SUPPORT struct refclockproc * const pp = peer->procptr; UNUSED_ARG(unit); if (-1 != pp->io.fd) /* any mode bits to evaluate here? */ gps_send(pp->io.fd, "$PMOTG,RMC,0000*1D\r\n", peer); #else UNUSED_ARG(unit); UNUSED_ARG(peer); #endif /* NMEA_WRITE_SUPPORT */ } #ifdef HAVE_PPSAPI /* * ------------------------------------------------------------------- * refclock_ppsrelate(...) -- correlate with PPS edge * * This function is used to correlate a receive time stamp and a * reference time with a PPS edge time stamp. It applies the necessary * fudges (fudge1 for PPS, fudge2 for receive time) and then tries to * move the receive time stamp to the corresponding edge. This can warp * into future, if a transmission delay of more than 500ms is not * compensated with a corresponding fudge time2 value, because then the * next PPS edge is nearer than the last. (Similar to what the PPS * driver does, but we deal with full time stamps here, not just phase * shift information.) Likewise, a negative fudge time2 value must be * used if the reference time stamp correlates with the *following* PPS * pulse. * * Note that the receive time fudge value only needs to move the receive * stamp near a PPS edge but that close proximity is not required; * +/-100ms precision should be enough. But since the fudge value will * probably also be used to compensate the transmission delay when no * PPS edge can be related to the time stamp, it's best to get it as * close as possible. * * It should also be noted that the typical use case is matching to the * preceding edge, as most units relate their sentences to the current * second. * * The function returns PPS_RELATE_NONE (0) if no PPS edge correlation * can be fixed; PPS_RELATE_EDGE (1) when a PPS edge could be fixed, but * the distance to the reference time stamp is too big (exceeds * +/-400ms) and the PPS driver PLL cannot be used to fix the phase; * and PPS_RELATE_PHASE (2) when the ATOM driver PLL code can be used. * * On output, the receive time stamp is replaced with the corresponding * PPS edge time if a fix could be made; the PPS fudge is updated to * reflect the proper fudge time to apply. (This implies that * 'refclock_process_offset()' must be used!) * ------------------------------------------------------------------- */ #define PPS_RELATE_NONE 0 /* no pps correlation possible */ #define PPS_RELATE_EDGE 1 /* recv time fixed, no phase lock */ #define PPS_RELATE_PHASE 2 /* recv time fixed, phase lock ok */ static int refclock_ppsrelate( const struct refclockproc * pp , /* for sanity */ const struct refclock_ppsctl * ap , /* for PPS io */ const l_fp * reftime , l_fp * rd_stamp, /* i/o read stamp */ double pp_fudge, /* pps fudge */ double * rd_fudge /* i/o read fudge */ ) { pps_info_t pps_info; struct timespec timeout; l_fp pp_stamp, pp_delta; double delta, idelta; if (pp->leap == LEAP_NOTINSYNC) return PPS_RELATE_NONE; /* clock is insane, no chance */ ZERO(timeout); ZERO(pps_info); if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC, &pps_info, &timeout) < 0) return PPS_RELATE_NONE; /* can't get time stamps */ /* get last active PPS edge before receive */ if (ap->pps_params.mode & PPS_CAPTUREASSERT) timeout = pps_info.assert_timestamp; else if (ap->pps_params.mode & PPS_CAPTURECLEAR) timeout = pps_info.clear_timestamp; else return PPS_RELATE_NONE; /* WHICH edge, please?!? */ /* get delta between receive time and PPS time */ pp_stamp = tspec_stamp_to_lfp(timeout); pp_delta = *rd_stamp; pp_delta -= pp_stamp; delta = lfptod(pp_delta); delta += pp_fudge - *rd_fudge; if (fabs(delta) > 1.5) return PPS_RELATE_NONE; /* PPS timeout control */ /* eventually warp edges, check phase */ idelta = floor(delta + 0.5); pp_fudge -= idelta; delta -= idelta; if (fabs(delta) > 0.45) return PPS_RELATE_NONE; /* dead band control */ /* we actually have a PPS edge to relate with! */ *rd_stamp = pp_stamp; *rd_fudge = pp_fudge; /* if whole system out-of-sync, do not try to PLL */ if (sys_leap == LEAP_NOTINSYNC) return PPS_RELATE_EDGE; /* cannot PLL with pps code */ /* check against reftime if PPS PLL can be used */ pp_delta = *reftime; pp_delta-= pp_stamp; delta = lfptod(pp_delta); delta += pp_fudge; if (fabs(delta) > 0.45) return PPS_RELATE_EDGE; /* cannot PLL with PPS code */ /* all checks passed, gets an AAA rating here! */ return PPS_RELATE_PHASE; /* can PLL with PPS code */ } #endif /* HAVE_PPSAPI */ /* * ------------------------------------------------------------------- * nmea_receive - receive data from the serial interface * * This is the workhorse for NMEA data evaluation: * * + it checks all NMEA data, and rejects sentences that are not valid * NMEA sentences * + it checks whether a sentence is known and to be used * + it parses the time and date data from the NMEA data string and * augments the missing bits. (century in dat, whole date, ...) * + it rejects data that is not from the first accepted sentence in a * burst * + it eventually replaces the receive time with the PPS edge time. * + it feeds the data to the internal processing stages. * ------------------------------------------------------------------- */ static void nmea_receive( struct recvbuf * rbufp ) { /* declare & init control structure ptrs */ struct peer * const peer = rbufp->recv_peer; struct refclockproc * const pp = peer->procptr; nmea_unit * const up = (nmea_unit*)pp->unitptr; /* Use these variables to hold data until we decide its worth keeping */ nmea_data rdata; char rd_lastcode[BMAX]; l_fp rd_timestamp, rd_reftime; int rd_lencode; double rd_fudge; /* working stuff */ struct calendar date; /* to keep & convert the time stamp */ struct timespec tofs; /* offset to full-second reftime */ gps_weektm gpsw; /* week time storage */ /* results of sentence/date/time parsing */ uint8_t sentence; /* sentence tag */ int checkres; char * cp; bool rc_date; bool rc_time; /* make sure data has defined pristine state */ ZERO(tofs); ZERO(date); ZERO(gpsw); sentence = 0; rc_date = false; rc_time = false; /* * Read the timecode and timestamp, then initialise field * processing. The at the NMEA line end is translated * to by the terminal input routines on most systems, * and this gives us one spurious empty read per record which we * better ignore silently. */ rd_lencode = refclock_gtlin(rbufp, rd_lastcode, sizeof(rd_lastcode), &rd_timestamp); checkres = field_init(&rdata, rd_lastcode, rd_lencode); switch (checkres) { case CHECK_INVALID: DPRINT(1, ("%s invalid data: '%s'\n", refclock_name(peer), rd_lastcode)); refclock_report(peer, CEVNT_BADREPLY); return; case CHECK_EMPTY: return; default: DPRINT(1, ("%s gpsread: %d '%s'\n", refclock_name(peer), rd_lencode, rd_lastcode)); break; } up->tally.total++; /* * --> below this point we have a valid NMEA sentence <-- * * Check sentence name. Skip first 2 chars (talker ID) in most * cases, to allow for $GLGGA and $GPGGA etc. Since the name * field has at least 5 chars we can simply shift the field * start. */ cp = field_parse(&rdata, 0); if (strncmp(cp + 2, "RMC,", 4) == 0) sentence = NMEA_GPRMC; else if (strncmp(cp + 2, "GGA,", 4) == 0) sentence = NMEA_GPGGA; else if (strncmp(cp + 2, "GLL,", 4) == 0) sentence = NMEA_GPGLL; else if (strncmp(cp + 2, "ZDA,", 4) == 0) sentence = NMEA_GPZDA; else if (strncmp(cp + 2, "ZDG,", 4) == 0) sentence = NMEA_GPZDG; else if (strncmp(cp, "PGRMF,", 6) == 0) sentence = NMEA_PGRMF; else return; /* not something we know about */ /* Eventually output delay measurement now. */ if (peer->cfg.mode & NMEA_DELAYMEAS_MASK) { mprintf_clock_stats(peer, "delay %0.6f %.*s", ldexp(lfpfrac(rd_timestamp), -32), (int)(strchr(rd_lastcode, ',') - rd_lastcode), rd_lastcode); } /* See if I want to process this message type */ if ((peer->cfg.mode & NMEA_MESSAGE_MASK) && !(peer->cfg.mode & sentence_mode[sentence])) { up->tally.filtered++; return; } /* * make sure it came in clean * * Apparently, older NMEA specifications (which are expensive) * did not require the checksum for all sentences. $GPRMC is * the only one so far identified which has always been required * to include a checksum. * * Today, most NMEA GPS receivers checksum every sentence. To * preserve its error-detection capabilities with modern GPSes * while allowing operation without checksums on all but $GPRMC, * we keep track of whether we've ever seen a valid checksum on * a given sentence, and if so, reject future instances without * checksum. ('up->cksum_type[NMEA_GPRMC]' is set in * 'nmea_start()' to enforce checksums for $GPRMC right from the * start.) */ if (up->cksum_type[sentence] <= (uint8_t)checkres) { up->cksum_type[sentence] = (uint8_t)checkres; } else { DPRINT(1, ("%s checksum missing: '%s'\n", refclock_name(peer), rd_lastcode)); refclock_report(peer, CEVNT_BADREPLY); up->tally.malformed++; return; } /* * $GPZDG provides GPS time not UTC, and the two mix poorly. * Once have processed a $GPZDG, do not process any further UTC * sentences (all but $GPZDG currently). */ if (up->gps_time && NMEA_GPZDG != sentence) { up->tally.filtered++; return; } DPRINT(1, ("%s processing %d bytes, timecode '%s'\n", refclock_name(peer), rd_lencode, rd_lastcode)); /* * Grab fields depending on clock string type and possibly wipe * sensitive data from the last timecode. */ switch (sentence) { case NMEA_GPRMC: /* Check quality byte, fetch data & time */ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); pp->leap = parse_qual(&rdata, 2, 'A', 0); rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY) && unfold_century(&date, lfpuint(rd_timestamp)); if (CLK_FLAG4 & pp->sloppyclockflag) field_wipe(&rdata, 3, 4, 5, 6, -1); break; case NMEA_GPGGA: /* Check quality byte, fetch time only */ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); pp->leap = parse_qual(&rdata, 6, '0', 1); rc_date = unfold_day(&date, lfpuint(rd_timestamp)); if (CLK_FLAG4 & pp->sloppyclockflag) field_wipe(&rdata, 2, 4, -1); break; case NMEA_GPGLL: /* Check quality byte, fetch time only */ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 5); pp->leap = parse_qual(&rdata, 6, 'A', 0); rc_date = unfold_day(&date, lfpuint(rd_timestamp)); if (CLK_FLAG4 & pp->sloppyclockflag) field_wipe(&rdata, 1, 3, -1); break; case NMEA_GPZDA: /* No quality. Assume best, fetch time & full date */ pp->leap = LEAP_NOWARNING; rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); break; case NMEA_GPZDG: /* Check quality byte, fetch time & full date */ rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 1); rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); pp->leap = parse_qual(&rdata, 4, '0', 1); tofs.tv_sec = -1; /* GPZDG is following second */ break; case NMEA_PGRMF: /* get date, time, qualifier and GPS weektime. We need * date and time-of-day for the century fix, so we read * them first. */ rc_date = parse_weekdata(&gpsw, &rdata, 1, 2, 5) && parse_date(&date, &rdata, 3, DATE_1_DDMMYY); rc_time = parse_time(&date, &tofs.tv_nsec, &rdata, 4); pp->leap = parse_qual(&rdata, 11, '0', 1); rc_date = rc_date && gpsfix_century(&date, &gpsw, &up->century_cache); if (CLK_FLAG4 & pp->sloppyclockflag) field_wipe(&rdata, 6, 8, -1); break; default: INVARIANT(0); /* Coverity 97123 */ return; } /* Check sanity of time-of-day. */ if (!rc_time) { /* no time or conversion error? */ checkres = CEVNT_BADTIME; up->tally.malformed++; } /* Check sanity of date. */ else if (!rc_date) {/* no date or conversion error? */ checkres = CEVNT_BADDATE; up->tally.malformed++; } /* check clock sanity; [bug 2143] */ else if (pp->leap == LEAP_NOTINSYNC) { /* no good status? */ checkres = CEVNT_BADREPLY; up->tally.rejected++; } else checkres = -1; if (checkres != -1) { save_ltc(pp, rd_lastcode, (size_t)rd_lencode); refclock_report(peer, checkres); return; } DPRINT(1, ("%s effective timecode: %04u-%02u-%02u %02d:%02d:%02d\n", refclock_name(peer), date.year, date.month, date.monthday, date.hour, date.minute, date.second)); /* Check if we must enter GPS time mode; log so if we do */ if (!up->gps_time && (sentence == NMEA_GPZDG)) { msyslog(LOG_INFO, "REFCLOCK: %s using GPS time as if it were UTC", refclock_name(peer)); up->gps_time = true; } /* * Get the reference time stamp from the calendar buffer. * Process the new sample in the median filter and determine the * timecode timestamp, but only if the PPS is not in control. * Discard sentence if reference time did not change. */ rd_reftime = eval_gps_time(peer, &date, &tofs, &rd_timestamp); if (up->last_reftime == rd_reftime) { /* Do not touch pp->a_lastcode on purpose! */ up->tally.filtered++; return; } up->last_reftime = rd_reftime; rd_fudge = pp->fudgetime2; DPRINT(1, ("%s using '%s'\n", refclock_name(peer), rd_lastcode)); /* Data will be accepted. Update stats & log data. */ up->tally.accepted++; save_ltc(pp, rd_lastcode, (size_t)rd_lencode); pp->lastrec = rd_timestamp; #ifdef HAVE_PPSAPI /* * If we have PPS running, we try to associate the sentence * with the last active edge of the PPS signal. */ if (up->ppsapi_lit) switch (refclock_ppsrelate( pp, &up->ppsctl, &rd_reftime, &rd_timestamp, pp->fudgetime1, &rd_fudge)) { case PPS_RELATE_PHASE: up->ppsapi_gate = true; peer->precision = PPS_PRECISION; peer->cfg.flags |= FLAG_PPS; DPRINT(2, ("%s PPS_RELATE_PHASE\n", refclock_name(peer))); up->tally.pps_used++; break; case PPS_RELATE_EDGE: up->ppsapi_gate = true; peer->precision = PPS_PRECISION; DPRINT(2, ("%s PPS_RELATE_EDGE\n", refclock_name(peer))); break; case PPS_RELATE_NONE: default: /* * Resetting precision and PPS flag is done in * 'nmea_poll', since it might be a glitch. But * at the end of the poll cycle we know... */ DPRINT(2, ("%s PPS_RELATE_NONE\n", refclock_name(peer))); break; } #endif /* HAVE_PPSAPI */ refclock_process_offset(pp, rd_reftime, rd_timestamp, rd_fudge); } /* * ------------------------------------------------------------------- * nmea_poll - called by the transmit procedure * * Does the necessary bookkeeping stuff to keep the reported state of * the clock in sync with reality. * * We go to great pains to avoid changing state here, since there may * be more than one eavesdropper receiving the same timecode. * ------------------------------------------------------------------- */ static void nmea_poll( int unit, struct peer * peer ) { struct refclockproc * const pp = peer->procptr; nmea_unit * const up = (nmea_unit *)pp->unitptr; UNUSED_ARG(unit); /* * Process median filter samples. If none received, declare a * timeout and keep going. */ #ifdef HAVE_PPSAPI /* * If we don't have PPS pulses and time stamps, turn PPS down * for now. */ if (!up->ppsapi_gate) { if ( FLAG_PPS & peer->cfg.flags ) { /* * PPS just turned off, reset jitter to prevent * thinking the NMEA is PPS precise and thus * being wrongly seelcted when better time is * available elsewhere */ peer->jitter = LOGTOD(PRECISION); } peer->cfg.flags &= ~FLAG_PPS; peer->precision = PRECISION; } else { up->ppsapi_gate = false; } #endif /* HAVE_PPSAPI */ /* * If the median filter is empty, claim a timeout. Else process * the input data and keep the stats going. */ if (pp->coderecv == pp->codeproc) { refclock_report(peer, CEVNT_TIMEOUT); /* reset the jitter, to avoid bad time on recovery */ peer->jitter = LOGTOD(PRECISION); } else { pp->polls++; pp->lastref = pp->lastrec; refclock_receive(peer); } /* * If extended logging is required, write the tally stats to the * clockstats file; otherwise just do a normal clock stats * record. Clear the tally stats anyway. */ if (peer->cfg.mode & NMEA_EXTLOG_MASK) { /* Log & reset counters with extended logging */ const char *nmea = pp->a_lastcode; if (*nmea == '\0') nmea = "(none)"; mprintf_clock_stats( peer, "%s %u %u %u %u %u %u", nmea, up->tally.total, up->tally.accepted, up->tally.rejected, up->tally.malformed, up->tally.filtered, up->tally.pps_used); } else { record_clock_stats(peer, pp->a_lastcode); } ZERO(up->tally); } /* * ------------------------------------------------------------------- * Save the last timecode string, making sure it's properly truncated * if necessary and NUL terminated in any case. */ static void save_ltc( struct refclockproc * const pp, const char * const tc, size_t len ) { if (len >= sizeof(pp->a_lastcode)) len = sizeof(pp->a_lastcode) - 1; pp->lencode = (unsigned short)len; memcpy(pp->a_lastcode, tc, len); pp->a_lastcode[len] = '\0'; } #if NMEA_WRITE_SUPPORT /* * ------------------------------------------------------------------- * gps_send(fd, cmd, peer) Sends a command to the GPS receiver. * as in gps_send(fd, "rqts,u", peer); * * If 'cmd' starts with a '$' it is assumed that this command is in raw * format, that is, starts with '$', ends with '' and that any * checksum is correctly provided; the command will be send 'as is' in * that case. Otherwise the function will create the necessary frame * (start char, chksum, final CRLF) on the fly. * * We don't currently send any data, but would like to send RTCM SC104 * messages for differential positioning. It should also give us better * time. Without a PPS output, we're Just fooling ourselves because of * the serial code paths * ------------------------------------------------------------------- */ static void gps_send( int fd, const char * cmd, struct peer * peer ) { /* $...*xy add 7 */ char buf[NMEA_PROTO_MAXLEN + 7]; int len; uint8_t dcs; const uint8_t *beg, *end; if (*cmd != '$') { /* get checksum and length */ beg = end = (const uint8_t*)cmd; dcs = 0; while (*end >= ' ' && *end != '*') dcs ^= *end++; len = end - beg; /* format into output buffer with overflow check */ len = snprintf(buf, sizeof(buf), "$%.*s*%02X\r\n", len, beg, dcs); if ( ( 0 > len) || ((size_t)len >= sizeof(buf))) { DPRINT(1, ("%s gps_send: buffer overflow for command '%s'\n", refclock_name(peer), cmd)); return; /* game over player 1 */ } cmd = buf; } else { len = strlen(cmd); } DPRINT(1, ("%s gps_send: '%.*s'\n", refclock_name(peer), len - 2, cmd)); /* send out the whole stuff */ if (write(fd, cmd, len) == -1) refclock_report(peer, CEVNT_FAULT); } #endif /* NMEA_WRITE_SUPPORT */ /* * ------------------------------------------------------------------- * helpers for faster field splitting * ------------------------------------------------------------------- * * set up a field record, check syntax and verify checksum * * format is $XXXXX,1,2,3,4*ML * * 8-bit XOR of characters between $ and * noninclusive is transmitted * in last two chars M and L holding most and least significant nibbles * in hex representation such as: * * $GPGLL,5057.970,N,00146.110,E,142451,A*27 * $GPVTG,089.0,T,,,15.2,N,,*7F * * Some other constraints: * + The field name must at least 5 upcase characters or digits and must * start with a character. * + The checksum (if present) must be uppercase hex digits. * + The length of a sentence is limited to 80 characters (not including * the final CR/LF nor the checksum, but including the leading '$') * * Return values: * + CHECK_INVALID * The data does not form a valid NMEA sentence or a checksum error * occurred. * + CHECK_VALID * The data is a valid NMEA sentence but contains no checksum. * + CHECK_CSVALID * The data is a valid NMEA sentence and passed the checksum test. * ------------------------------------------------------------------- */ static int field_init( nmea_data * data, /* context structure */ char * cptr, /* start of raw data */ int dlen /* data len, not counting trailing NUL */ ) { uint8_t cs_l; /* checksum local computed */ uint8_t cs_r; /* checksum remote given */ char * eptr; /* buffer end end pointer */ char tmp; /* char buffer */ cs_l = 0; cs_r = 0; /* some basic input constraints */ if (dlen < 0) dlen = 0; eptr = cptr + dlen; *eptr = '\0'; /* load data context */ data->base = cptr; data->cptr = cptr; data->cidx = 0; data->blen = dlen; /* syntax check follows here. check allowed character * sequences, updating the local computed checksum as we go. * * regex equiv: '^\$[A-Z][A-Z0-9]{4,}[^*]*(\*[0-9A-F]{2})?$' */ /* -*- start character: '^\$' */ if (*cptr == '\0') return CHECK_EMPTY; if (*cptr++ != '$') return CHECK_INVALID; /* -*- advance context beyond start character */ data->base++; data->cptr++; data->blen--; /* -*- field name: '[A-Z][A-Z0-9]{4,},' */ if (*cptr < 'A' || *cptr > 'Z') return CHECK_INVALID; cs_l ^= *cptr++; while ((*cptr >= 'A' && *cptr <= 'Z') || (*cptr >= '0' && *cptr <= '9') ) cs_l ^= *cptr++; if (*cptr != ',' || (cptr - data->base) < NMEA_PROTO_IDLEN) return CHECK_INVALID; cs_l ^= *cptr++; /* -*- data: '[^*]*' */ while (*cptr && *cptr != '*') cs_l ^= *cptr++; /* -*- checksum field: (\*[0-9A-F]{2})?$ */ if (*cptr == '\0') return CHECK_VALID; if (*cptr != '*' || cptr != eptr - 3 || (cptr - data->base) >= NMEA_PROTO_MAXLEN) return CHECK_INVALID; for (cptr++; (tmp = *cptr) != '\0'; cptr++) { if (tmp >= '0' && tmp <= '9') cs_r = (cs_r << 4) + (tmp - '0'); else if (tmp >= 'A' && tmp <= 'F') cs_r = (cs_r << 4) + (tmp - 'A' + 10); else break; } /* -*- make sure we are at end of string and csum matches */ if (cptr != eptr || cs_l != cs_r) return CHECK_INVALID; return CHECK_CSVALID; } /* * ------------------------------------------------------------------- * fetch a data field by index, zero being the name field. If this * function is called repeatedly with increasing indices, the total load * is O(n), n being the length of the string; if it is called with * decreasing indices, the total load is O(n^2). Try not to go backwards * too often. * ------------------------------------------------------------------- */ static char * field_parse( nmea_data * data, int fn ) { char tmp; if (fn < data->cidx) { data->cidx = 0; data->cptr = data->base; } while ((fn > data->cidx) && (tmp = *data->cptr) != '\0') { data->cidx += (tmp == ','); data->cptr++; } return data->cptr; } /* * ------------------------------------------------------------------- * Wipe (that is, overwrite with '_') data fields and the checksum in * the last timecode. The list of field indices is given as integers * in a varargs list, preferably in ascending order, in any case * terminated by a negative field index. * * A maximum number of 8 fields can be overwritten at once to guard * against runaway (that is, unterminated) argument lists. * * This function affects what a remote user can see with * * ntpq -c clockvar * * Note that this also removes the wiped fields from any clockstats * log. Some NTP operators monitor their NMEA GPS using the change in * location in clockstats over time as as a proxy for the quality of * GPS reception and thereby time reported. * ------------------------------------------------------------------- */ static void field_wipe( nmea_data * data, ... ) { va_list va; /* vararg index list */ int fcnt; /* safeguard against runaway arglist */ int fidx; /* field to nuke, or -1 for checksum */ char * cp; /* overwrite destination */ fcnt = 8; cp = NULL; va_start(va, data); do { fidx = va_arg(va, int); if (fidx >= 0 && fidx <= NMEA_PROTO_FIELDS) { cp = field_parse(data, fidx); } else { cp = data->base + data->blen; if (data->blen >= 3 && cp[-3] == '*') cp -= 2; } for ( ; '\0' != *cp && '*' != *cp && ',' != *cp; cp++) if ('.' != *cp) *cp = '_'; } while (fcnt-- && fidx >= 0); va_end(va); } /* * ------------------------------------------------------------------- * PARSING HELPERS * ------------------------------------------------------------------- * * Check sync status * * If the character at the data field start matches the tag value, * return LEAP_NOWARNING and LEAP_NOTINSYNC otherwise. If the 'inverted' * flag is given, just the opposite value is returned. If there is no * data field (*cp points to the NUL byte) the result is LEAP_NOTINSYNC. * ------------------------------------------------------------------- */ static uint8_t parse_qual( nmea_data * rd, int idx, char tag, int inv ) { static const uint8_t table[2] = { LEAP_NOTINSYNC, LEAP_NOWARNING }; char * dp; dp = field_parse(rd, idx); return table[ *dp && ((*dp == tag) == !inv) ]; } /* * ------------------------------------------------------------------- * Parse a time stamp in HHMMSS[.sss] format with error checking. * * returns true on success, false on failure * ------------------------------------------------------------------- */ static bool parse_time( struct calendar * jd, /* result calendar pointer */ long * ns, /* storage for nsec fraction */ nmea_data * rd, int idx ) { static const unsigned long weight[4] = { 0, 100000000, 10000000, 1000000 }; int rc; unsigned int h; unsigned int m; unsigned int s; int p1; int p2; unsigned long f; char * dp; dp = field_parse(rd, idx); rc = sscanf(dp, "%2u%2u%2u%n.%3lu%n", &h, &m, &s, &p1, &f, &p2); if (rc < 3 || p1 != 6) { DPRINT(1, ("nmea: invalid time code: '%.6s'\n", dp)); return false; } /* value sanity check */ if (h > 23 || m > 59 || s > 60) { DPRINT(1, ("nmea: invalid time spec %02u:%02u:%02u\n", h, m, s)); return false; } jd->hour = (uint8_t)h; jd->minute = (uint8_t)m; jd->second = (uint8_t)s; /* if we have a fraction, scale it up to nanoseconds. */ if (rc == 4) *ns = (long)(f * weight[p2 - p1 - 1]); else *ns = 0; return true; } /* * ------------------------------------------------------------------- * Parse a date string from an NMEA sentence. This could either be a * partial date in DDMMYY format in one field, or DD,MM,YYYY full date * spec spanning three fields. This function does some extensive error * checking to make sure the date string was consistent. * * returns true on success, false on failure * ------------------------------------------------------------------- */ static bool parse_date( struct calendar * jd, /* result pointer */ nmea_data * rd, int idx, enum date_fmt fmt ) { int rc; unsigned int y; unsigned int m; unsigned int d; int p; char * dp; dp = field_parse(rd, idx); switch (fmt) { case DATE_1_DDMMYY: rc = sscanf(dp, "%2u%2u%2u%n", &d, &m, &y, &p); if (rc != 3 || p != 6) { DPRINT(1, ("nmea: invalid date code: '%.6s'\n", dp)); return false; } break; case DATE_3_DDMMYYYY: rc = sscanf(dp, "%2u,%2u,%4u%n", &d, &m, &y, &p); if (rc != 3 || p != 10) { DPRINT(1, ("nmea: invalid date code: '%.10s'\n", dp)); return false; } break; default: DPRINT(1, ("nmea: invalid parse format: %u\n", fmt)); return false; } /* value sanity check */ if (d < 1 || d > 31 || m < 1 || m > 12) { DPRINT(1, ("nmea: invalid date spec (YMD) %04u:%02u:%02u\n", y, m, d)); return false; } /* store results */ jd->monthday = (uint8_t)d; jd->month = (uint8_t)m; jd->year = (unsigned short)y; return true; } /* * ------------------------------------------------------------------- * Parse GPS week time info from an NMEA sentence. This info contains * the GPS week number, the GPS time-of-week and the leap seconds GPS * to UTC. * * returns true on success, false on failure * ------------------------------------------------------------------- */ static bool parse_weekdata( gps_weektm * wd, nmea_data * rd, int weekidx, int timeidx, int leapidx ) { unsigned long secs; int fcnt; /* parse fields and count success */ fcnt = sscanf(field_parse(rd, weekidx), "%hu", &wd->wt_week); fcnt += sscanf(field_parse(rd, timeidx), "%lu", &secs); fcnt += sscanf(field_parse(rd, leapidx), "%hd", &wd->wt_leap); if (fcnt != 3 || wd->wt_week >= 1024 || secs >= 7*SECSPERDAY) { DPRINT(1, ("nmea: parse_weekdata: invalid weektime spec\n")); return false; } wd->wt_time = (uint32_t)secs; return true; } /* * ------------------------------------------------------------------- * funny calendar-oriented stuff -- perhaps a bit hard to grok. * ------------------------------------------------------------------- * * Unfold a time-of-day (seconds since midnight) around the current * system time in a manner that guarantees an absolute difference of * less than 12hrs. * * This function is used for NMEA sentences that contain no date * information. This requires the system clock to be in +/-12hrs * around the true time, or the clock will synchronize the system 1day * off if not augmented with a time sources that also provide the * necessary date information. * * The function updates the calendar structure it also uses as * input to fetch the time from. * * returns true on success, false on failure * ------------------------------------------------------------------- */ static bool unfold_day( struct calendar * jd, uint32_t rec_ui ) { time64_t rec_qw; ntpcal_split rec_ds; /* * basically this is the peridiodic extension of the receive * time - 12hrs to the time-of-day with a period of 1 day. * But we would have to execute this in 64bit arithmetic, and we * cannot assume we can do this; therefore this is done * in split representation. */ rec_qw = ntpcal_ntp_to_ntp(rec_ui - SECSPERDAY/2, time(NULL)); rec_ds = ntpcal_daysplit(rec_qw); rec_ds.lo = ntpcal_periodic_extend(rec_ds.lo, ntpcal_date_to_daysec(jd), SECSPERDAY); rec_ds.hi += ntpcal_daysec_to_date(jd, rec_ds.lo); /* -1 return means calculation overflowed */ return (ntpcal_rd_to_date(jd, rec_ds.hi + DAY_NTP_STARTS) >= 0); } /* * ------------------------------------------------------------------- * A 2-digit year is expanded into full year spec around the year found * in 'jd->year'. This should be in +79/-19 years around the system time, * or the result will be off by 100 years. The asymmetric behaviour was * chosen to enable initial sync for systems that do not have a * battery-backup clock and start with a date that is typically years in * the past. * * Since the GPS epoch starts at 1980-01-06, the resulting year will be * not be before 1980 in any case. * * returns true on success, false on failure * ------------------------------------------------------------------- */ static bool unfold_century( struct calendar * jd, uint32_t rec_ui ) { struct calendar rec; int32_t baseyear; ntpcal_ntp_to_date(&rec, rec_ui, time(NULL)); baseyear = rec.year - 20; if (baseyear < g_gpsMinYear) baseyear = g_gpsMinYear; jd->year = (unsigned short)ntpcal_periodic_extend(baseyear, jd->year, 100); return ((baseyear <= jd->year) && (baseyear + 100 > jd->year)); } /* * ------------------------------------------------------------------- * A 2-digit year is expanded into a full year spec by correlation with * a GPS week number and the current leap second count. * * The GPS week time scale counts weeks since Sunday, 1980-01-06, modulo * 1024 and seconds since start of the week. The GPS time scale is based * on international atomic time (TAI), so the leap second difference to * UTC is also needed for a proper conversion. * * A brute-force analysis (that is, test for every date) shows that a * wrong assignment of the century can not happen between the years 1900 * to 2399 when comparing the week signatures for different * centuries. (I *think* that will not happen for 400*1024 years, but I * have no valid proof. -*-perlinger@ntp.org-*-) * * This function is bound to to work between years 1980 and 2399 * (inclusive), which should suffice for now ;-) * * Note: This function needs a full date&time spec on input due to the * necessary leap second corrections! * * returns true on success, false on failure * ------------------------------------------------------------------- */ static bool gpsfix_century( struct calendar * jd, const gps_weektm * wd, unsigned short * century ) { int32_t days; int32_t doff; unsigned short week; unsigned short year; int loop; /* Get day offset. Assumes that the input time is in range and * that the leap seconds do not shift more than +/-1 day. */ doff = ntpcal_date_to_daysec(jd) + wd->wt_leap; doff = (doff >= SECSPERDAY) - (doff < 0); /* * Loop over centuries to get a match, starting with the last * successful one. (Or with the 19th century if the cached value * is out of range...) */ year = jd->year % 100; for (loop = 5; loop > 0; loop--,(*century)++) { if (*century < 19 || *century >= 24) *century = 19; /* Get days and week in GPS epoch */ jd->year = year + *century * 100; days = ntpcal_date_to_rd(jd) - DAY_GPS_STARTS + doff; week = (days / 7) % 1024; if (days >= 0 && wd->wt_week == week) return true; /* matched... */ } jd->year = year; return false; /* match failed... */ } /* * ------------------------------------------------------------------- * And now the final execise: Considering the fact that many (most?) * GPS receivers cannot handle a GPS epoch wrap well, we try to * compensate for that problem by unwrapping a GPS epoch around the * receive stamp. Another execise in periodic unfolding, of course, * but with enough points to take care of. * * Note: The integral part of 'tofs' is intended to handle small(!) * systematic offsets, as -1 for handling $GPZDG, which gives the * following second. (sigh...) The absolute value shall be less than a * day (86400 seconds). * ------------------------------------------------------------------- */ static l_fp eval_gps_time( struct peer * peer, /* for logging etc */ const struct calendar * gpst, /* GPS time stamp */ const struct timespec * tofs, /* GPS frac second & offset */ const l_fp * xrecv /* receive time stamp */ ) { struct refclockproc * const pp = peer->procptr; nmea_unit * const up = (nmea_unit *)pp->unitptr; l_fp retv; /* components of calculation */ int32_t rcv_sec, rcv_day; /* receive ToD and day */ int32_t gps_sec, gps_day; /* GPS ToD and day in NTP epoch */ int32_t adj_day, weeks; /* adjusted GPS day and week shift */ /* some temporaries to shuffle data */ time64_t vi64; ntpcal_split rs64; /* evaluate time stamp from receiver. */ gps_sec = ntpcal_date_to_daysec(gpst); gps_day = ntpcal_date_to_rd(gpst) - DAY_NTP_STARTS; /* merge in fractional offset */ retv = tspec_intv_to_lfp(*tofs); gps_sec += lfpsint(retv); /* If we fully trust the GPS receiver, just combine days and * seconds and be done. */ if (peer->cfg.mode & NMEA_DATETRUST_MASK) { setlfpuint(retv, time64lo(ntpcal_dayjoin(gps_day, gps_sec))); return retv; } /* So we do not trust the GPS receiver to deliver a correct date * due to the GPS epoch changes. We map the date from the * receiver into the +/-512 week interval around the receive * time in that case. This would be a tad easier with 64bit * calculations, but again, we restrict the code to 32bit ops * when possible. */ /* - make sure the GPS fractional day is normalised * Applying the offset value might have put us slightly over the * edge of the allowed range for seconds-of-day. Doing a full * division with floor correction is overkill here; a simple * addition or subtraction step is sufficient. Using WHILE loops * gives the right result even if the offset exceeds one day, * which is NOT what it's intended for! */ while (gps_sec >= SECSPERDAY) { gps_sec -= SECSPERDAY; gps_day += 1; } while (gps_sec < 0) { gps_sec += SECSPERDAY; gps_day -= 1; } /* - get unfold base: day of full recv time - 512 weeks */ vi64 = ntpcal_ntp_to_ntp(lfpuint(*xrecv), time(NULL)); rs64 = ntpcal_daysplit(vi64); rcv_sec = rs64.lo; rcv_day = rs64.hi - 512 * 7; /* - take the fractional days into account * If the fractional day of the GPS time is smaller than the * fractional day of the receive time, we shift the base day for * the unfold by 1. */ if ( gps_sec < rcv_sec || (gps_sec == rcv_sec && lfpfrac(retv) < lfpfrac(*xrecv))) rcv_day += 1; /* - don't warp ahead of GPS invention! */ if (rcv_day < g_gpsMinBase) rcv_day = g_gpsMinBase; /* - let the magic happen: */ adj_day = ntpcal_periodic_extend(rcv_day, gps_day, 1024*7); /* - check if we should log a GPS epoch warp */ weeks = (adj_day - gps_day) / 7; if (weeks != up->epoch_warp) { up->epoch_warp = (short)weeks; LOGIF(CLOCKINFO, (LOG_INFO, "%s Changed GPS epoch warp to %d weeks", refclock_name(peer), weeks)); } /* - build result and be done */ setlfpuint(retv, time64lo(ntpcal_dayjoin(adj_day, gps_sec))); return retv; } // end ntpsec-1.1.0+dfsg1/ntpd/ntp_leapsec.c0000644000175000017500000006134313252364117017242 0ustar rlaagerrlaager/* * ntp_leapsec.c - leap second processing for NTPD * * Written by Juergen Perlinger for the NTP project. * * ---------------------------------------------------------------------- * This is an attempt to get the leap second handling into a dedicated * module to make the somewhat convoluted logic testable. * * Note: This code assumes that the unsigned long long return value of * strtoull(3) is large enough to parse any integer literal found in these * files, and that C will promote such values to uint64_t properly. * * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: NTP */ #include "config.h" #include #include #include #include "ntp_types.h" #include "ntp_fp.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" #include "ntp_leapsec.h" #include "ntp.h" #include "ntpd.h" /* for rfc3339time() only */ #include "lib_strbuf.h" #include #define ISC_SHA1_DIGESTLENGTH 20U static const char * const logPrefix = "leapsecond file"; /* --------------------------------------------------------------------- * Our internal data structure */ #define MAX_HIST 10 /* history of leap seconds */ struct leap_info { time_t ttime; /* transition time (after the step, POSIX epoch) */ time_t stime; /* schedule limit (a month before transition) */ int16_t taiof; /* TAI offset on and after the transition */ bool dynls; /* dynamic: inserted on peer/clock request */ }; typedef struct leap_info leap_info_t; struct leap_head { time_t update; /* time of information update */ time_t expire; /* table expiration time */ uint16_t size; /* number of infos in table */ int16_t base_tai; /* total leaps before first entry */ int16_t this_tai; /* current TAI offset */ int16_t next_tai; /* TAI offset after 'when' */ time_t dtime; /* due time (current era end) */ time_t ttime; /* nominal transition time (next era start) */ time_t stime; /* schedule time (when we take notice) */ time_t ebase; /* base time of this leap era */ bool dynls; /* next leap is dynamic (by peer request) */ }; typedef struct leap_head leap_head_t; struct leap_table { leap_signature_t lsig; leap_head_t head; leap_info_t info[MAX_HIST]; }; /* Where we store our tables */ static leap_table_t _ltab[2], *_lptr; static bool _electric; /* Forward decls of local helpers */ static bool add_range(leap_table_t*, const leap_info_t*); static char * get_line(leapsec_reader, void*, char*, size_t); static char * skipws(char*) __attribute__((pure)); static bool parsefail(const char * cp, const char * ep); static void reload_limits(leap_table_t*, time_t); static void reset_times(leap_table_t*); static bool leapsec_add(leap_table_t*, time_t, int); static bool leapsec_raw(leap_table_t*, time_t, int, bool); /* time_t is unsigned. This is used for infinity in tables */ #if NTP_SIZEOF_TIME_T == 8 # define LAST_time_t 0x7fffffffffffffff #elif NTP_SIZEOF_TIME_T == 4 # define LAST_time_t 0x7fffffff #endif /* ===================================================================== * Get & Set the current leap table */ /* ------------------------------------------------------------------ */ leap_table_t * leapsec_get_table( bool alternate) { leap_table_t *p1, *p2; p1 = _lptr; if (p1 == &_ltab[0]) { p2 = &_ltab[1]; } else if (p1 == &_ltab[1]) { p2 = &_ltab[0]; } else { p1 = &_ltab[0]; p2 = &_ltab[1]; reset_times(p1); reset_times(p2); _lptr = p1; } if (alternate) { memcpy(p2, p1, sizeof(leap_table_t)); p1 = p2; } return p1; } /* ------------------------------------------------------------------ */ bool leapsec_set_table( leap_table_t * pt) { if (pt == &_ltab[0] || pt == &_ltab[1]) _lptr = pt; return _lptr == pt; } /* ------------------------------------------------------------------ */ bool leapsec_electric( electric_mode el) { int res = _electric; if (el == electric_query) return res; _electric = (el == electric_on); if (_electric == res) return res; if (_lptr == &_ltab[0] || _lptr == &_ltab[1]) reset_times(_lptr); return res; } /* ===================================================================== * API functions that operate on tables */ /* --------------------------------------------------------------------- * Clear all leap second data. Use it for init & cleanup */ void leapsec_clear( leap_table_t * pt) { memset(&pt->lsig, 0, sizeof(pt->lsig)); memset(&pt->head, 0, sizeof(pt->head)); reset_times(pt); } /* --------------------------------------------------------------------- * Load a leap second file and check expiration on the go */ bool leapsec_load( leap_table_t * pt , leapsec_reader func, void * farg) { char *cp, *ep, linebuf[50]; time_t ttime; uint64_t ntp; /* Time from file with NTP epoch of 1900 */ long taiof; leapsec_clear(pt); while (get_line(func, farg, linebuf, sizeof(linebuf))) { cp = linebuf; if (*cp == '#') { cp++; if (*cp == '@') { cp = skipws(cp+1); ntp = strtoull(cp, &ep, 10); if (parsefail(cp, ep)) goto fail_read; pt->head.expire = (time_t)(ntp - JAN_1970); pt->lsig.etime = pt->head.expire; } else if (*cp == '$') { cp = skipws(cp+1); ntp = strtoull(cp, &ep, 10); if (parsefail(cp, ep)) goto fail_read; pt->head.update = (time_t)(ntp - JAN_1970); } } else if (isdigit((uint8_t)*cp)) { ntp = strtoull(cp, &ep, 10); if (parsefail(cp, ep)) goto fail_read; ttime = (time_t)(ntp - JAN_1970); cp = skipws(ep); taiof = strtol(cp, &ep, 10); if ( parsefail(cp, ep) || taiof > SHRT_MAX || taiof < SHRT_MIN) goto fail_read; if (!leapsec_raw(pt, ttime, taiof, false)) goto fail_insn; pt->lsig.ttime = ttime; pt->lsig.taiof = (int16_t)taiof; } } return true; fail_read: errno = EILSEQ; fail_insn: leapsec_clear(pt); return false; } /* --------------------------------------------------------------------- * Dump a table in human-readable format. Use 'fprintf' and a FILE * pointer if you want to get it printed into a stream. */ void leapsec_dump( const leap_table_t * pt , leapsec_dumper func, void * farg) { int idx; time_t ts; struct tm atb, ttb; ts = pt->head.expire; gmtime_r(&ts, &ttb); (*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n", pt->head.size, ttb.tm_year+1900, ttb.tm_mon+1, ttb.tm_mday); idx = pt->head.size; while (idx-- != 0) { ts = pt->info[idx].ttime; gmtime_r(&ts, &ttb); ts = ts - pt->info[idx].stime; // FIXME ??? gmtime_r(&ts, &atb); (*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n", ttb.tm_year+1900, ttb.tm_mon+1, ttb.tm_mday, "-*"[pt->info[idx].dynls], atb.tm_year+1900, atb.tm_mon+1, atb.tm_mday, pt->info[idx].taiof); } } /* ===================================================================== * usecase driven API functions */ bool leapsec_query( leap_result_t * qr, time_t when) { leap_table_t * pt; time_t last, next; bool fired; /* preset things we use later on... */ fired = false; pt = leapsec_get_table(false); memset(qr, 0, sizeof(leap_result_t)); if (when < pt->head.ebase) { /* Most likely after leap frame reset. Could also be a * backstep of the system clock. Anyway, get the new * leap era frame. */ reload_limits(pt, when); } else if (when >= pt->head.dtime) { /* Boundary crossed in forward direction. This might * indicate a leap transition, so we prepare for that * case. * * Some operations below are actually NOPs in electric * mode, but having only one code path that works for * both modes is easier to maintain. */ last = pt->head.ttime; // FIXME warped is only 16 bits. ???? qr->warped = (int16_t)(last - pt->head.dtime); next = when + qr->warped; reload_limits(pt, next); fired = (pt->head.ebase == last); if (fired) { when = next; } else { qr->warped = 0; } } qr->tai_offs = pt->head.this_tai; /* If before the next scheduling alert, we're done. */ if (when < pt->head.stime) return fired; /* now start to collect the remaining data */ qr->tai_diff = pt->head.next_tai - pt->head.this_tai; qr->ttime = pt->head.ttime; qr->ddist = (uint32_t)(pt->head.dtime - when); qr->dynamic = pt->head.dynls; qr->proximity = LSPROX_SCHEDULE; /* if not in the last day before transition, we're done. */ if (when >= pt->head.dtime) return fired; /* Future */ if (when < pt->head.dtime - SECSPERDAY) return fired; /* Before last day */ qr->proximity = LSPROX_ANNOUNCE; if (when < pt->head.dtime - 10) return fired; /* Before last 10 seconds */ /* The last 10s before the transition. Prepare for action! */ qr->proximity = LSPROX_ALERT; return fired; } /* ------------------------------------------------------------------ */ bool leapsec_frame( leap_result_t *qr) { const leap_table_t * pt; memset(qr, 0, sizeof(leap_result_t)); pt = leapsec_get_table(false); if (pt->head.ttime <= pt->head.stime) return false; qr->tai_offs = pt->head.this_tai; qr->tai_diff = pt->head.next_tai - pt->head.this_tai; qr->ttime = pt->head.ttime; qr->dynamic = pt->head.dynls; return true; } /* ------------------------------------------------------------------ */ /* Reset the current leap frame */ void leapsec_reset_frame(void) { reset_times(leapsec_get_table(false)); } /* ------------------------------------------------------------------ */ /* load a file from a FILE pointer. Note: If hcheck is true, load * only after successful signature check. The stream must be seekable * or this will fail. */ bool leapsec_load_stream( FILE * ifp , const char * fname, bool logall) { leap_table_t *pt; int rcheck; if (NULL == fname) fname = ""; rcheck = leapsec_validate((leapsec_reader)getc, ifp); if (logall) switch (rcheck) { case LSVALID_GOODHASH: msyslog(LOG_NOTICE, "CLOCK: %s ('%s'): good hash signature", logPrefix, fname); break; case LSVALID_NOHASH: msyslog(LOG_ERR, "CLOCK: %s ('%s'): no hash signature", logPrefix, fname); break; case LSVALID_BADHASH: msyslog(LOG_ERR, "CLOCK: %s ('%s'): signature mismatch", logPrefix, fname); break; case LSVALID_BADFORMAT: msyslog(LOG_ERR, "CLOCK: %s ('%s'): malformed hash signature", logPrefix, fname); break; default: msyslog(LOG_ERR, "CLOCK: %s ('%s'): unknown error code %d", logPrefix, fname, rcheck); break; } if (rcheck < 0) return false; rewind(ifp); pt = leapsec_get_table(true); if (!leapsec_load(pt, (leapsec_reader)getc, ifp)) { switch (errno) { case EINVAL: msyslog(LOG_ERR, "CLOCK: %s ('%s'): bad transition time", logPrefix, fname); break; case ERANGE: msyslog(LOG_ERR, "CLOCK: %s ('%s'): times not ascending", logPrefix, fname); break; default: msyslog(LOG_ERR, "CLOCK: %s ('%s'): parsing error", logPrefix, fname); break; } return false; } if (pt->head.size) msyslog(LOG_NOTICE, "CLOCK: %s ('%s'): loaded, expire=%s last=%s ofs=%d", logPrefix, fname, rfc3339time(pt->head.expire), rfc3339time(pt->info[0].ttime), pt->info[0].taiof); else msyslog(LOG_NOTICE, "CLOCK: %s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)", logPrefix, fname, rfc3339time(pt->head.expire), pt->head.base_tai); return leapsec_set_table(pt); } /* ------------------------------------------------------------------ */ bool leapsec_load_file( const char * fname, struct stat * sb_old, bool force, bool logall) { FILE * fp; struct stat sb_new; int rc; /* just do nothing if there is no leap file */ if ( !(fname && *fname) ) return false; /* try to stat the leapfile */ /* coverity[toctou] */ if (0 != stat(fname, &sb_new)) { if (logall) msyslog(LOG_ERR, "CLOCK: %s ('%s'): stat failed: %m", logPrefix, fname); return false; } /* silently skip to postcheck if no new file found */ if (NULL != sb_old) { if (!force && sb_old->st_mtime == sb_new.st_mtime && sb_old->st_ctime == sb_new.st_ctime ) return false; *sb_old = sb_new; } /* try to open the leap file, complain if that fails * * [perlinger@ntp.org] * coverity raises a TOCTOU (time-of-check/time-of-use) issue * here, which is not entirely helpful: While there is indeed a * possible race condition between the 'stat()' call above and * the 'fopen)' call below, I intentionally want to omit the * overhead of opening the file and calling 'fstat()', because * in most cases the file would have be to closed anyway without * reading the contents. I chose to disable the coverity * warning instead. * * So unless someone comes up with a reasonable argument why * this could be a real issue, I'll just try to silence coverity * on that topic. */ /* coverity[toctou] */ if ((fp = fopen(fname, "r")) == NULL) { if (logall) msyslog(LOG_ERR, "CLOCK: %s ('%s'): open failed: %m", logPrefix, fname); return false; } rc = leapsec_load_stream(fp, fname, logall); fclose(fp); return rc; } /* ------------------------------------------------------------------ */ void leapsec_getsig( leap_signature_t * psig) { const leap_table_t * pt; pt = leapsec_get_table(false); memcpy(psig, &pt->lsig, sizeof(leap_signature_t)); } /* ------------------------------------------------------------------ */ bool leapsec_expired( time_t when) { const leap_table_t * pt; pt = leapsec_get_table(false); return (when >= pt->head.expire); } /* ------------------------------------------------------------------ */ int32_t leapsec_daystolive( time_t limit) { const leap_table_t * pt; long secs_left; pt = leapsec_get_table(false); secs_left = pt->head.expire - limit; if (secs_left < 0) secs_left -= (SECSPERDAY-1); return secs_left/SECSPERDAY; } /* ------------------------------------------------------------------ */ bool leapsec_add_fix( int tai_offset, time_t ttime, time_t etime) { leap_table_t * pt; pt = leapsec_get_table(true); if ((etime <= pt->head.expire) || !leapsec_raw(pt, ttime, tai_offset, false) ) return false; pt->lsig.etime = etime; pt->lsig.ttime = ttime; pt->lsig.taiof = (int16_t)tai_offset; pt->head.expire = etime; return leapsec_set_table(pt); } /* ------------------------------------------------------------------ */ bool leapsec_add_dyn( bool insert, time_t when) { leap_table_t * pt; pt = leapsec_get_table(true); return (leapsec_add(pt, when, insert) && leapsec_set_table(pt)); } /* ===================================================================== * internal helpers */ /* [internal] Reset / init the time window in the leap processor to * force reload on next query. Since a leap transition cannot take place * at an odd second, the value chosen avoids spurious leap transition * triggers. Making all three times equal forces a reload. Using the * maximum value for unsigned 64 bits makes finding the next leap frame * a bit easier. */ static void reset_times( leap_table_t * pt) { pt->head.ebase = LAST_time_t; pt->head.stime = pt->head.ebase; pt->head.ttime = pt->head.ebase; pt->head.dtime = pt->head.ebase; } /* [internal] Add raw data to the table, removing old entries on the * fly. This cannot fail currently. */ static bool add_range( leap_table_t * pt, const leap_info_t * pi) { /* If the table is full, make room by throwing out the oldest * entry. But remember the accumulated leap seconds! */ if (pt->head.size >= MAX_HIST) { pt->head.size = MAX_HIST - 1; pt->head.base_tai = pt->info[pt->head.size].taiof; } /* make room in lower end and insert item */ memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info)); pt->info[0] = *pi; pt->head.size++; /* invalidate the cached limit data -- we might have news ;-) * * This blocks a spurious transition detection. OTOH, if you add * a value after the last query before a leap transition was * expected to occur, this transition trigger is lost. But we * can probably live with that. */ reset_times(pt); return true; } /* [internal] given a reader function, read characters into a buffer * until either EOL or EOF is reached. Makes sure that the buffer is * always NUL terminated, but silently truncates excessive data. The * EOL-marker ('\n') is *not* stored in the buffer. * * Returns the pointer to the buffer, unless EOF was reached when trying * to read the first character of a line. */ static char * get_line( leapsec_reader func, void * farg, char * buff, size_t size) { int ch; char *ptr; /* if we cannot even store the delimiter, declare failure */ if (buff == NULL || size == 0) return NULL; ptr = buff; while (EOF != (ch = (*func)(farg)) && '\n' != ch) if (size > 1) { size--; *ptr++ = (char)ch; } /* discard trailing whitespace */ while (ptr != buff && isspace((uint8_t)ptr[-1])) ptr--; *ptr = '\0'; return (ptr == buff && ch == EOF) ? NULL : buff; } /* [internal] skips whitespace characters from a character buffer. */ static char * skipws( char *ptr) { while (isspace((uint8_t)*ptr)) ptr++; return ptr; } /* [internal] check if a strtoXYZ ended at EOL or whitespace and * converted something at all. Return true if something went wrong. */ static bool parsefail( const char * cp, const char * ep) { return (cp == ep) || (*ep && *ep != '#' && !isspace((uint8_t)*ep)); } /* [internal] reload the table limits around the given time stamp. This * is where the real work is done when it comes to table lookup and * evaluation. Some care has been taken to have correct code for dealing * with boundary conditions and empty tables. * * In electric mode, transition and trip time are the same. In dumb * mode, the difference of the TAI offsets must be taken into account * and trip time and transition time become different. The difference * becomes the warping distance when the trip time is reached. */ static void reload_limits( leap_table_t * pt, time_t ts) { int idx; /* Get full time and search the true lower bound. Use a * simple loop here, since the number of entries does * not warrant a binary search. This also works for an empty * table, so there is no shortcut for that case. */ for (idx = 0; idx != pt->head.size; idx++) if (ts >= pt->info[idx].ttime) break; /* get time limits with proper bound conditions. Note that the * bounds of the table will be observed even if the table is * empty -- no undefined condition must arise from this code. */ if (idx >= pt->head.size) { pt->head.ebase = 0; pt->head.this_tai = pt->head.base_tai; } else { pt->head.ebase = pt->info[idx].ttime; pt->head.this_tai = pt->info[idx].taiof; } if (--idx >= 0) { pt->head.next_tai = pt->info[idx].taiof; pt->head.dynls = pt->info[idx].dynls; pt->head.ttime = pt->info[idx].ttime; if (_electric) pt->head.dtime = pt->head.ttime; else pt->head.dtime = pt->head.ttime + pt->head.next_tai - pt->head.this_tai; pt->head.stime = pt->head.ttime - pt->info[idx].stime; } else { pt->head.ttime = LAST_time_t; pt->head.stime = pt->head.ttime; pt->head.dtime = pt->head.ttime; pt->head.next_tai = pt->head.this_tai; pt->head.dynls = false; } } /* [internal] Take a time stamp and create a leap second frame for * it. This will schedule a leap second for the beginning of the next * month, midnight UTC. The 'insert' argument tells if a leap second is * added (!=0) or removed (==0). We do not handle multiple inserts * (yet?) * * Returns 1 if the insert worked, 0 otherwise. (It's not possible to * insert a leap second into the current history -- only appending * towards the future is allowed!) */ static bool leapsec_add( leap_table_t* pt, const time_t now, int insert) { time_t ttime, starttime; struct tm fts; leap_info_t li; /* Check against the table expiration and the latest available * leap entry. Do not permit inserts, only appends, and only if * the extend the table beyond the expiration! */ if ((now < pt->head.expire) || (pt->head.size && (now <= pt->info[0].ttime))) { errno = ERANGE; return false; } gmtime_r(&now, &fts); /* To guard against dangling leap flags: do not accept leap * second request on the 1st hour of the 1st day of the month. */ #ifndef ENABLE_LEAP_TESTING if (fts.tm_mday == 1 && fts.tm_hour == 0) { errno = EINVAL; return false; } #endif /* Ok, do the remaining calculations */ fts.tm_hour = 0; fts.tm_min = 0; fts.tm_sec = 0; starttime = mktime(&fts); fts.tm_mon++; if (fts.tm_mon == 12) { fts.tm_mon = 0; fts.tm_year++; } ttime = mktime(&fts); li.ttime = ttime; li.stime = ttime - starttime; li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai) + (insert ? 1 : -1); li.dynls = 1; return add_range(pt, &li); } /* [internal] Given a time stamp for a leap insertion (the exact begin * of the new leap era), create new leap frame and put it into the * table. This is the work horse for reading a leap file and getting a * leap second update via authenticated network packet. */ bool leapsec_raw( leap_table_t * pt, const time_t ttime, int taiof, bool dynls) { time_t starttime; struct tm fts; leap_info_t li; /* Check that we only extend the table. Paranoia rulez! */ if (pt->head.size && (ttime <= pt->info[0].ttime)) { errno = ERANGE; return false; } gmtime_r(&ttime, &fts); #ifndef ENABLE_LEAP_TESTING /* If this does not match the exact month start, bail out. */ if (fts.tm_mday != 1 || fts.tm_hour || fts.tm_min || fts.tm_sec) { errno = EINVAL; return false; } #endif /* Start 28 days earler. Avoids month arithmetic. */ starttime = ttime - 28*SECSPERDAY; li.ttime = ttime; li.stime = ttime - starttime; li.taiof = (int16_t)taiof; li.dynls = dynls; return add_range(pt, &li); } /* ===================================================================== * validation stuff */ typedef struct { unsigned char hv[ISC_SHA1_DIGESTLENGTH]; } sha1_digest; /* [internal] parse a digest line to get the hash signature * The NIST code creating the hash writes them out as 5 hex integers * without leading zeros. This makes reading them back as hex-encoded * BLOB impossible, because there might be less than 40 hex digits. * * The solution is to read the values back as integers, and then do the * byte twiddle necessary to get it into an array of 20 chars. The * drawback is that it permits any acceptable number syntax provided by * 'scanf()' and 'strtoul()', including optional signs and '0x' * prefixes. */ static bool do_leap_hash( sha1_digest * mac, char const * cp ) { int wi, di, num, len; unsigned long tmp[5]; memset(mac, 0, sizeof(*mac)); num = sscanf(cp, " %lx %lx %lx %lx %lx%n", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &len); if (num != 5 || cp[len] > ' ') return false; /* now do the byte twiddle */ for (wi=0; wi < 5; ++wi) for (di=3; di >= 0; --di) { mac->hv[wi*4 + di] = (unsigned char)(tmp[wi] & 0x0FF); tmp[wi] >>= 8; } return true; } /* [internal] add the digits of a data line to the hash, stopping at the * next hash ('#') character. */ static void do_hash_data( EVP_MD_CTX * mdctx, char const * cp ) { unsigned char text[32]; // must be power of two! unsigned int tlen = 0; unsigned char ch; while ('\0' != (ch = (unsigned char)(*cp++)) && '#' != ch) if (isdigit(ch)) { text[tlen++] = ch; tlen &= (sizeof(text)-1); if (0 == tlen) EVP_DigestUpdate( mdctx, (const void *)text, sizeof(text)); } if (0 < tlen) EVP_DigestUpdate(mdctx, (const void *)text, (size_t)tlen); } /* given a reader and a reader arg, calculate and validate the hash * signature of a NIST leap second file. */ int leapsec_validate( leapsec_reader func, void * farg) { EVP_MD_CTX *mdctx; sha1_digest rdig, ldig; /* remote / local digests */ char line[50]; int hlseen = -1; mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, EVP_sha1(), NULL); while (get_line(func, farg, line, sizeof(line))) { if (!strncmp(line, "#h", 2)) hlseen = do_leap_hash(&rdig, line+2); else if (!strncmp(line, "#@", 2)) do_hash_data(mdctx, line+2); else if (!strncmp(line, "#$", 2)) do_hash_data(mdctx, line+2); else if (isdigit((unsigned char)line[0])) do_hash_data(mdctx, line); } EVP_DigestFinal_ex(mdctx, ldig.hv, NULL); EVP_MD_CTX_destroy(mdctx); if (0 > hlseen) return LSVALID_NOHASH; if (0 == hlseen) return LSVALID_BADFORMAT; if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest))) return LSVALID_BADHASH; return LSVALID_GOODHASH; } /* reset the global state for unit tests */ void leapsec_ut_pristine(void) { memset(_ltab, 0, sizeof(_ltab)); _lptr = NULL; _electric = 0; } /* -*- that's all folks! -*- */ ntpsec-1.1.0+dfsg1/ntpd/ntp_config.c0000644000175000017500000022145713252364117017077 0ustar rlaagerrlaager/* ntp_config.c * * This file contains the ntpd configuration code. * * Written By: Sachin Kamboj * University of Delaware * Newark, DE 19711 * Some parts borrowed from the older ntp_config.c * Copyright (c) 2006 * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-clause */ #include "config.h" #ifdef HAVE_NETINFO_NI_H # include #endif #include #include #include #include #include "isc_netaddr.h" #include "ntp.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_filegen.h" #include "ntp_stdlib.h" #include "lib_strbuf.h" #include "ntp_assert.h" #include "ntp_dns.h" /* * [Classic Bug 467]: Some linux headers collide with CONFIG_PHONE and * CONFIG_KEYS so #include these later. */ #include "ntp_config.h" #include "ntp_scanner.h" #include "ntp_parser.tab.h" /* Hack to dance around bug in older Bison. See Classic Issue 287 */ /* Similar for yydebug below */ #ifndef yyparse int yyparse (void); #endif /* NetInfo configuration locations */ #ifdef HAVE_NETINFO_NI_H #define NETINFO_CONFIG_DIR "/config/ntp" #endif /* * Selection algorithm tuning parameters */ /* * In the past, we told reference clocks from network peers by giving * the reference clocks magic address of a particular special form * ntpd itself, the filtering that used to be done based on this magic * address prefix is now done using a flag set on incoming packets. * In ntpq, the filtering is replaced by asking the server how a * peer's name should be displayed. * * Address filtering is still done in this file so we can tell which old-style * config declarations refer to clocks. When that syntax is retired, drop these. */ #define REFCLOCK_ADDR 0x7f7f0000 /* 127.127.0.0 */ #define REFCLOCK_MASK 0xffff0000 /* 255.255.0.0 */ #define ISREFCLOCKADR(srcadr) \ (IS_IPV4(srcadr) && \ (SRCADR(srcadr) & REFCLOCK_MASK) == REFCLOCK_ADDR) /* * Macros to determine the clock type and unit numbers from a * refclock magic address */ #define REFCLOCKTYPE(srcadr) ((SRCADR(srcadr) >> 8) & 0xff) #define REFCLOCKUNIT(srcadr) (SRCADR(srcadr) & 0xff) /* list of servers from command line for config_peers() */ int cmdline_server_count = 0; char ** cmdline_servers; /* * FIXME: ugly globals, only created to avoid wiring in option-parsing cruft. * These are symptoms of deeper factoring issues; the things they're * controlling, deep down inside that configuration parsing, should * not be happening where they are. */ bool have_interface_option; /* * "logconfig" building blocks */ struct masks { const char * const name; const uint32_t mask; }; static struct masks logcfg_class[] = { { "clock", NLOG_OCLOCK }, { "peer", NLOG_OPEER }, { "sync", NLOG_OSYNC }, { "sys", NLOG_OSYS }, { NULL, 0 } }; /* logcfg_noclass_items[] masks are complete and must not be shifted */ static struct masks logcfg_noclass_items[] = { { "allall", NLOG_SYSMASK | NLOG_PEERMASK | NLOG_CLOCKMASK | NLOG_SYNCMASK }, { "allinfo", NLOG_SYSINFO | NLOG_PEERINFO | NLOG_CLOCKINFO | NLOG_SYNCINFO }, { "allevents", NLOG_SYSEVENT | NLOG_PEEREVENT | NLOG_CLOCKEVENT | NLOG_SYNCEVENT }, { "allstatus", NLOG_SYSSTATUS | NLOG_PEERSTATUS | NLOG_CLOCKSTATUS | NLOG_SYNCSTATUS }, { "allstatistics", NLOG_SYSSTATIST | NLOG_PEERSTATIST | NLOG_CLOCKSTATIST | NLOG_SYNCSTATIST }, /* the remainder are misspellings of clockall, peerall, sysall, and syncall. */ { "allclock", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OCLOCK }, { "allpeer", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OPEER }, { "allsys", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYS }, { "allsync", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYNC }, { NULL, 0 } }; /* logcfg_class_items[] masks are shiftable by NLOG_O* counts */ static struct masks logcfg_class_items[] = { { "all", NLOG_INFO | NLOG_EVENT | NLOG_STATUS | NLOG_STATIST }, { "info", NLOG_INFO }, { "events", NLOG_EVENT }, { "status", NLOG_STATUS }, { "statistics", NLOG_STATIST }, { NULL, 0 } }; /* Limits */ #define MAXPHONE 10 /* maximum number of phone strings */ /* #define MAXPPS 20 * maximum length of PPS device string UNUSED */ /* * Miscellaneous macros */ #ifdef HAVE_NETINFO_NI_H # define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0') # define ISSPACE(c) ((c) == ' ' || (c) == '\t') #endif #define _UC(str) ((char *)(intptr_t)(str)) /* * Definitions of things either imported from or exported to outside */ config_tree cfgt; /* Parser output stored here */ static struct config_tree_tag *cfg_tree_history; /* History of configs */ char *sys_phone[MAXPHONE] = {NULL}; /* ACTS phone numbers */ static char default_ntp_signd_socket[] = #ifdef MSSNTP_PATH MSSNTP_PATH; #else ""; #endif char *ntp_signd_socket = default_ntp_signd_socket; #ifdef HAVE_NETINFO_NI_H static struct netinfo_config_state *config_netinfo = NULL; static bool check_netinfo = true; #endif /* HAVE_NETINFO_NI_H */ #ifdef HAVE_NETINFO_NI_H /* * NetInfo configuration state */ struct netinfo_config_state { void *domain; /* domain with config */ ni_id config_dir; /* ID config dir */ int prop_index; /* current property */ int val_index; /* current value */ char **val_list; /* value list */ }; #endif struct REMOTE_CONFIG_INFO remote_config; /* Remote configuration buffer and pointer info */ /* FUNCTION PROTOTYPES */ static void init_syntax_tree(config_tree *); static void apply_enable_disable(attr_val_fifo *q, int enable); static void free_auth_node(config_tree *); static void free_all_config_trees(void); static struct peer *peer_config(sockaddr_u *, const char *, endpt *, int, struct peer_ctl *); static void free_config_access(config_tree *); static void free_config_auth(config_tree *); static void free_config_fudge(config_tree *); static void free_config_logconfig(config_tree *); static void free_config_monitor(config_tree *); static void free_config_nic_rules(config_tree *); static void free_config_peers(config_tree *); static void free_config_phone(config_tree *); static void free_config_reset_counters(config_tree *); static void free_config_rlimit(config_tree *); static void free_config_setvar(config_tree *); static void free_config_system_opts(config_tree *); static void free_config_tinker(config_tree *); static void free_config_tos(config_tree *); static void free_config_unpeers(config_tree *); static void free_config_vars(config_tree *); static void free_config_tree(config_tree *ptree); static void destroy_restrict_node(restrict_node *my_node); static bool is_sane_resolved_address(sockaddr_u *peeraddr, int hmode); static void save_and_apply_config_tree(bool from_file); static void destroy_int_fifo(int_fifo *); #define FREE_INT_FIFO(pf) \ do { \ destroy_int_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_string_fifo(string_fifo *); #define FREE_STRING_FIFO(pf) \ do { \ destroy_string_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_attr_val_fifo(attr_val_fifo *); #define FREE_ATTR_VAL_FIFO(pf) \ do { \ destroy_attr_val_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_filegen_fifo(filegen_fifo *); #define FREE_FILEGEN_FIFO(pf) \ do { \ destroy_filegen_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_restrict_fifo(restrict_fifo *); #define FREE_RESTRICT_FIFO(pf) \ do { \ destroy_restrict_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_setvar_fifo(setvar_fifo *); #define FREE_SETVAR_FIFO(pf) \ do { \ destroy_setvar_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_addr_opts_fifo(addr_opts_fifo *); #define FREE_ADDR_OPTS_FIFO(pf) \ do { \ destroy_addr_opts_fifo(pf); \ (pf) = NULL; \ } while (0) static void config_logconfig(config_tree *); static void config_monitor(config_tree *); static void config_rlimit(config_tree *); static void config_system_opts(config_tree *); static void config_tinker(config_tree *); static void config_tos(config_tree *); static void config_logfile(config_tree *); static void config_vars(config_tree *); static void config_ntpd(config_tree *, bool input_from_file); static void config_auth(config_tree *); static void config_access(config_tree *); static void config_mdnstries(config_tree *); static void config_phone(config_tree *); static void config_setvar(config_tree *); static void config_fudge(config_tree *); static void config_peers(config_tree *); static void config_unpeers(config_tree *); static void config_nic_rules(config_tree *, bool input_from_file); static void config_reset_counters(config_tree *); enum gnn_type { t_UNK, /* Unknown */ t_REF, /* Refclock */ t_MSK /* Network Mask */ }; static uint32_t get_pfxmatch(const char **, struct masks *); static uint32_t get_match(const char *, struct masks *); static uint32_t get_logmask(const char *); static int getnetnum(const char *num, sockaddr_u *addr); static void fix_node_cidr(restrict_node *my_node); /* FUNCTIONS FOR INITIALIZATION * ---------------------------- */ static void free_auth_node( config_tree *ptree ) { if (ptree->auth.keys) { free(ptree->auth.keys); ptree->auth.keys = NULL; } if (ptree->auth.ntp_signd_socket) { free(ptree->auth.ntp_signd_socket); ptree->auth.ntp_signd_socket = NULL; } } static void init_syntax_tree( config_tree *ptree ) { ZERO(*ptree); ptree->mdnstries = 5; } static void free_all_config_trees(void) { config_tree *ptree; config_tree *pnext; ptree = cfg_tree_history; while (ptree != NULL) { pnext = ptree->link; free_config_tree(ptree); ptree = pnext; } } static void free_config_tree( config_tree *ptree ) { if (ptree->source.value.s != NULL) free(ptree->source.value.s); free_config_auth(ptree); free_config_tos(ptree); free_config_monitor(ptree); free_config_access(ptree); free_config_tinker(ptree); free_config_rlimit(ptree); free_config_system_opts(ptree); free_config_logconfig(ptree); free_config_phone(ptree); free_config_setvar(ptree); free_config_fudge(ptree); free_config_vars(ptree); free_config_peers(ptree); free_config_unpeers(ptree); free_config_nic_rules(ptree); free_config_reset_counters(ptree); free_auth_node(ptree); free(ptree); } /* generic fifo routines for structs linked by 1st member */ void * append_gen_fifo( void *fifo, void *entry ) { gen_fifo *pf; gen_node *pe; pf = fifo; pe = entry; if (NULL == pf) pf = emalloc_zero(sizeof(*pf)); else CHECK_FIFO_CONSISTENCY(*pf); if (pe != NULL) LINK_FIFO(*pf, pe, link); CHECK_FIFO_CONSISTENCY(*pf); return pf; } void * concat_gen_fifos( void *first, void *second ) { gen_fifo *pf1; gen_fifo *pf2; pf1 = first; pf2 = second; if (NULL == pf1) return pf2; if (NULL == pf2) return pf1; CONCAT_FIFO(*pf1, *pf2, link); free(pf2); return pf1; } /* FUNCTIONS FOR CREATING NODES ON THE SYNTAX TREE * ----------------------------------------------- */ attr_val * create_attr_dval( int attr, double value ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.d = value; my_val->type = T_Double; return my_val; } attr_val * create_attr_ival( int attr, int value ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.i = value; my_val->type = T_Integer; return my_val; } attr_val * create_attr_uval( int attr, unsigned int value ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.u = value; my_val->type = T_U_int; return my_val; } attr_val * create_attr_rangeval( int attr, int first, int last ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.r.first = first; my_val->value.r.last = last; my_val->type = T_Intrange; return my_val; } attr_val * create_attr_sval( int attr, const char *s ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; if (NULL == s) /* free() hates NULL */ s = estrdup(""); my_val->value.s = _UC(s); my_val->type = T_String; return my_val; } int_node * create_int_node( int val ) { int_node *i_n; i_n = emalloc_zero(sizeof(*i_n)); i_n->i = val; return i_n; } string_node * create_string_node( char *str ) { string_node *sn; sn = emalloc_zero(sizeof(*sn)); sn->s = str; return sn; } address_node * create_address_node( char * addr, int type ) { address_node *my_node; REQUIRE(NULL != addr); REQUIRE(AF_INET == type || AF_INET6 == type || AF_UNSPEC == type); my_node = emalloc_zero(sizeof(*my_node)); my_node->address = addr; my_node->type = (unsigned short)type; return my_node; } void destroy_address_node( address_node *my_node ) { if (NULL == my_node) return; REQUIRE(NULL != my_node->address); free(my_node->address); free(my_node); } peer_node * create_peer_node( int hmode, address_node * addr, attr_val_fifo * options ) { peer_node *my_node; attr_val *option; int errflag = false; my_node = emalloc_zero(sizeof(*my_node)); /* Initialize node values to default */ my_node->ctl.version = NTP_VERSION; my_node->ctl.minpoll = NTP_MINDPOLL; /* can not set maxpoll default yet, it may be NTP_MAXDPOLL or minpoll */ my_node->ctl.maxpoll = NTP_MAXPOLL_UNK; my_node->ctl.bias = 0; /* Now set the node to the read values */ my_node->host_mode = hmode; my_node->addr = addr; /* The options FIFO is consumed and reclaimed here */ if (options != NULL) CHECK_FIFO_CONSISTENCY(*options); while (options != NULL) { UNLINK_FIFO(option, *options, link); if (NULL == option) { free(options); break; } /* Check the kind of option being set */ switch (option->attr) { case T_Flag: switch (option->value.i) { default: INSIST(0); break; case T_Burst: my_node->ctl.flags |= FLAG_BURST; break; case T_Iburst: my_node->ctl.flags |= FLAG_IBURST; break; case T_Noselect: my_node->ctl.flags |= FLAG_NOSELECT; break; case T_Prefer: my_node->ctl.flags |= FLAG_PREFER; break; case T_True: my_node->ctl.flags |= FLAG_TRUE; break; } break; case T_Minpoll: if (option->value.i < NTP_MINPOLL ) { msyslog(LOG_INFO, "CONFIG: minpoll: provided value (%d) is too small [%d-%d])", option->value.i, NTP_MINPOLL, NTP_MAXPOLL); my_node->ctl.minpoll = NTP_MINPOLL; } else if (option->value.i > NTP_MAXPOLL) { msyslog(LOG_INFO, "CONFIG: minpoll: provided value (%d) is too large [%d-%d])", option->value.i, NTP_MINPOLL, NTP_MAXPOLL); my_node->ctl.minpoll = NTP_MAXPOLL; } else { my_node->ctl.minpoll = (uint8_t)option->value.u; } break; case T_Maxpoll: if (option->value.i < NTP_MINPOLL ) { msyslog(LOG_INFO, "CONFIG: maxpoll: value (%d) is too small [%d-%d])", option->value.i, NTP_MINPOLL, NTP_MAXPOLL); my_node->ctl.maxpoll = NTP_MINPOLL; } else if ( option->value.i > NTP_MAXPOLL) { msyslog(LOG_INFO, "CONFIG: maxpoll: value (%d) is too large [%d-%d])", option->value.i, NTP_MINPOLL, NTP_MAXPOLL); my_node->ctl.maxpoll = NTP_MAXPOLL; } else { my_node->ctl.maxpoll = (uint8_t)option->value.u; } break; case T_Subtype: case T_Mode: my_node->ctl.mode = option->value.u; break; case T_Key: if (option->value.u >= NTP_MAXKEY) { msyslog(LOG_ERR, "CONFIG: key: invalid argument"); errflag = true; } else { my_node->ctl.peerkey = (keyid_t)option->value.u; } break; case T_Version: if (option->value.u >= UCHAR_MAX) { msyslog(LOG_ERR, "CONFIG: version: invalid argument"); errflag = true; } else { my_node->ctl.version = (uint8_t)option->value.u; } break; case T_Bias: my_node->ctl.bias = option->value.d; break; #ifdef REFCLOCK case T_Path: my_node->ctl.path = estrdup(option->value.s); break; case T_Ppspath: my_node->ctl.ppspath = estrdup(option->value.s); break; case T_Baud: my_node->ctl.baud = option->value.u; break; /* * Past this point are options the old syntax * handled in fudge processing. They're parsed * here to avoid ordering constraints. */ case T_Time1: my_node->clock_stat.haveflags |= CLK_HAVETIME1; my_node->clock_stat.fudgetime1 = option->value.d; break; case T_Time2: my_node->clock_stat.haveflags |= CLK_HAVETIME2; my_node->clock_stat.fudgetime2 = option->value.d; break; case T_Stratum: my_node->clock_stat.haveflags |= CLK_HAVEVAL1; my_node->clock_stat.fudgeval1 = option->value.i; break; case T_Refid: my_node->clock_stat.haveflags |= CLK_HAVEVAL2; my_node->clock_stat.fudgeval2 = 0; memcpy(&my_node->clock_stat.fudgeval2, option->value.s, min(strlen(option->value.s), REFIDLEN)); break; case T_Flag1: my_node->clock_stat.haveflags |= CLK_HAVEFLAG1; if (option->value.i) my_node->clock_stat.flags |= CLK_FLAG1; else my_node->clock_stat.flags &= ~CLK_FLAG1; break; case T_Flag2: my_node->clock_stat.haveflags |= CLK_HAVEFLAG2; if (option->value.i) my_node->clock_stat.flags |= CLK_FLAG2; else my_node->clock_stat.flags &= ~CLK_FLAG2; break; case T_Flag3: my_node->clock_stat.haveflags |= CLK_HAVEFLAG3; if (option->value.i) my_node->clock_stat.flags |= CLK_FLAG3; else my_node->clock_stat.flags &= ~CLK_FLAG3; break; case T_Flag4: my_node->clock_stat.haveflags |= CLK_HAVEFLAG4; if (option->value.i) my_node->clock_stat.flags |= CLK_FLAG4; else my_node->clock_stat.flags &= ~CLK_FLAG4; break; case T_Holdover: my_node->clock_stat.flags |= CLK_HOLDOVER; break; #endif /* REFCLOCK */ default: msyslog(LOG_ERR, "CONFIG: Unknown peer/server option token %s", token_name(option->attr)); errflag = true; } free(option); } /* Check if errors were reported. If yes, ignore the node */ if (errflag) { free(my_node); my_node = NULL; } return my_node; } unpeer_node * create_unpeer_node( address_node *addr ) { unpeer_node * my_node; unsigned int u; char * pch; my_node = emalloc_zero(sizeof(*my_node)); /* * From the parser's perspective an association ID fits into * its generic T_String definition of a name/address "address". * We treat all valid 16-bit numbers as association IDs. */ pch = addr->address; while (*pch && isdigit((unsigned char)*pch)) pch++; if (!*pch && 1 == sscanf(addr->address, "%u", &u) && u <= ASSOCID_MAX) { my_node->assocID = (associd_t)u; destroy_address_node(addr); my_node->addr = NULL; } else { my_node->assocID = 0; my_node->addr = addr; } return my_node; } filegen_node * create_filegen_node( int filegen_token, attr_val_fifo * options ) { filegen_node *my_node; my_node = emalloc_zero(sizeof(*my_node)); my_node->filegen_token = filegen_token; my_node->options = options; return my_node; } restrict_node * create_restrict_node( const int mode, address_node * addr, address_node * mask, int_fifo * flags, int line_no ) { restrict_node *my_node; my_node = emalloc_zero(sizeof(*my_node)); my_node->mode = mode; my_node->addr = addr; my_node->mask = mask; my_node->flags = flags; my_node->line_no = line_no; return my_node; } static void destroy_restrict_node( restrict_node *my_node ) { /* With great care, free all the memory occupied by * the restrict node */ destroy_address_node(my_node->addr); destroy_address_node(my_node->mask); destroy_int_fifo(my_node->flags); free(my_node); } static void destroy_int_fifo( int_fifo * fifo ) { int_node * i_n; if (fifo != NULL) { for (;;) { UNLINK_FIFO(i_n, *fifo, link); if (i_n == NULL) break; free(i_n); } free(fifo); } } static void destroy_string_fifo( string_fifo * fifo ) { string_node * sn; if (fifo != NULL) { for (;;) { UNLINK_FIFO(sn, *fifo, link); if (sn == NULL) break; free(sn->s); free(sn); } free(fifo); } } static void destroy_attr_val_fifo( attr_val_fifo * av_fifo ) { attr_val * av; if (av_fifo != NULL) { for (;;) { UNLINK_FIFO(av, *av_fifo, link); if (av == NULL) break; if (T_String == av->type) free(av->value.s); free(av); } free(av_fifo); } } static void destroy_filegen_fifo( filegen_fifo * fifo ) { filegen_node * fg; if (fifo != NULL) { for (;;) { UNLINK_FIFO(fg, *fifo, link); if (fg == NULL) break; destroy_attr_val_fifo(fg->options); free(fg); } free(fifo); } } static void destroy_restrict_fifo( restrict_fifo * fifo ) { restrict_node * rn; if (fifo != NULL) { for (;;) { UNLINK_FIFO(rn, *fifo, link); if (rn == NULL) break; destroy_restrict_node(rn); } free(fifo); } } static void destroy_setvar_fifo( setvar_fifo * fifo ) { setvar_node * sv; if (fifo != NULL) { for (;;) { UNLINK_FIFO(sv, *fifo, link); if (sv == NULL) break; free(sv->var); free(sv->val); free(sv); } free(fifo); } } static void destroy_addr_opts_fifo( addr_opts_fifo * fifo ) { addr_opts_node * aon; if (fifo != NULL) { for (;;) { UNLINK_FIFO(aon, *fifo, link); if (aon == NULL) break; destroy_address_node(aon->addr); destroy_attr_val_fifo(aon->options); free(aon); } free(fifo); } } setvar_node * create_setvar_node( char * var, char * val, int isdefault ) { setvar_node * my_node; char * pch; /* do not allow = in the variable name */ pch = strchr(var, '='); if (NULL != pch) *pch = '\0'; /* Now store the string into a setvar_node */ my_node = emalloc_zero(sizeof(*my_node)); my_node->var = var; my_node->val = val; my_node->isdefault = isdefault; return my_node; } nic_rule_node * create_nic_rule_node( int match_class, char *if_name, /* interface name or numeric address */ int action ) { nic_rule_node *my_node; REQUIRE(match_class != 0 || if_name != NULL); my_node = emalloc_zero(sizeof(*my_node)); my_node->match_class = match_class; my_node->if_name = if_name; my_node->action = action; return my_node; } addr_opts_node * create_addr_opts_node( address_node * addr, attr_val_fifo * options ) { addr_opts_node *my_node; my_node = emalloc_zero(sizeof(*my_node)); my_node->addr = addr; my_node->options = options; return my_node; } /* FUNCTIONS FOR PERFORMING THE CONFIGURATION * ------------------------------------------ */ static void config_auth( config_tree *ptree ) { attr_val * my_val; int first; int last; int i; int count; /* ntp_signd_socket Command */ if (ptree->auth.ntp_signd_socket) { if (ntp_signd_socket != default_ntp_signd_socket) free(ntp_signd_socket); ntp_signd_socket = estrdup(ptree->auth.ntp_signd_socket); } /* * Count the number of trusted keys to preallocate storage and * size the hash table. */ count = 0; my_val = HEAD_PFIFO(ptree->auth.trusted_key_list); for (; my_val != NULL; my_val = my_val->link) { if (T_Integer == my_val->type) { first = my_val->value.i; if (first > 1 && first <= NTP_MAXKEY) count++; } else { REQUIRE(T_Intrange == my_val->type); first = my_val->value.r.first; last = my_val->value.r.last; if (!(first > last || first < 1 || last > NTP_MAXKEY)) { count += 1 + last - first; } } } if (0 < count) msyslog(LOG_INFO, "Found %d trusted keys.", count); auth_prealloc_symkeys(count); /* Keys Command */ if (ptree->auth.keys) getauthkeys(ptree->auth.keys); /* Control Key Command */ if (ptree->auth.control_key) ctl_auth_keyid = (keyid_t)ptree->auth.control_key; /* Trusted Key Command */ my_val = HEAD_PFIFO(ptree->auth.trusted_key_list); for (; my_val != NULL; my_val = my_val->link) { if (T_Integer == my_val->type) { first = my_val->value.i; if (first >= 1 && first <= NTP_MAXKEY) { authtrust((keyid_t)first, true); } else { msyslog(LOG_NOTICE, "CONFIG: Ignoring invalid trustedkey %d, min 1 max %d.", first, NTP_MAXKEY); } } else { first = my_val->value.r.first; last = my_val->value.r.last; if (first > last || first < 1 || last > NTP_MAXKEY) { msyslog(LOG_NOTICE, "CONFIG: Ignoring invalid trustedkey range %d ... %d, min 1 max %d.", first, last, NTP_MAXKEY); } else { for (i = first; i <= last; i++) { authtrust((keyid_t)i, true); } } } } } static void free_config_auth( config_tree *ptree ) { destroy_attr_val_fifo(ptree->auth.trusted_key_list); ptree->auth.trusted_key_list = NULL; } static void config_tos( config_tree *ptree ) { attr_val * tos; int item; double val; item = -1; /* quiet warning */ tos = HEAD_PFIFO(ptree->orphan_cmds); for (; tos != NULL; tos = tos->link) { val = tos->value.d; switch(tos->attr) { default: INSIST(0); break; case T_Ceiling: if (val > STRATUM_UNSPEC - 1) { msyslog(LOG_WARNING, "CONFIG: Using maximum tos ceiling %d, %g requested", STRATUM_UNSPEC - 1, val); val = STRATUM_UNSPEC - 1; } item = PROTO_CEILING; break; case T_Floor: item = PROTO_FLOOR; break; case T_Orphan: item = PROTO_ORPHAN; break; case T_Orphanwait: item = PROTO_ORPHWAIT; break; case T_Mindist: item = PROTO_MINDISP; break; case T_Maxdisp: item = PROTO_MAXDISP; break; case T_Maxdist: item = PROTO_MAXDIST; break; case T_Minclock: item = PROTO_MINCLOCK; break; case T_Maxclock: item = PROTO_MAXCLOCK; break; case T_Minsane: item = PROTO_MINSANE; break; } proto_config(item, 0, val); } } static void free_config_tos( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->orphan_cmds); } static void config_monitor( config_tree *ptree ) { int_node *pfilegen_token; const char *filegen_string; const char *filegen_file; FILEGEN *filegen; filegen_node *my_node; attr_val *my_opts; int filegen_type; int filegen_flag; /* Set the statistics directory */ if (ptree->stats_dir) stats_config(STATS_STATSDIR, ptree->stats_dir); /* NOTE: * Calling filegen_get is brain dead. Doing a string * comparison to find the relavant filegen structure is * expensive. * * Through the parser, we already know which filegen is * being specified. Hence, we should either store a * pointer to the specified structure in the syntax tree * or an index into a filegen array. * * Need to change the filegen code to reflect the above. */ /* Turn on the specified statistics */ pfilegen_token = HEAD_PFIFO(ptree->stats_list); for (; pfilegen_token != NULL; pfilegen_token = pfilegen_token->link) { filegen_string = keyword(pfilegen_token->i); filegen = filegen_get(filegen_string); if (NULL == filegen) { msyslog(LOG_ERR, "CONFIG: stats %s unrecognized", filegen_string); continue; } DPRINT(4, ("enabling filegen for %s statistics '%s%s'\n", filegen_string, filegen->dir, filegen->fname)); filegen_flag = filegen->flag; filegen_flag |= FGEN_FLAG_ENABLED; filegen_config(filegen, statsdir, filegen_string, filegen->type, (unsigned int)filegen_flag); } /* Configure the statistics with the options */ my_node = HEAD_PFIFO(ptree->filegen_opts); for (; my_node != NULL; my_node = my_node->link) { filegen_string = keyword(my_node->filegen_token); filegen = filegen_get(filegen_string); if (NULL == filegen) { msyslog(LOG_ERR, "CONFIG: filegen category '%s' unrecognized", filegen_string); continue; } filegen_file = filegen_string; /* Initialize the filegen variables to their pre-configuration states */ filegen_flag = filegen->flag; filegen_type = filegen->type; /* "filegen ... enabled" is the default (when filegen is used) */ filegen_flag |= FGEN_FLAG_ENABLED; my_opts = HEAD_PFIFO(my_node->options); for (; my_opts != NULL; my_opts = my_opts->link) { switch (my_opts->attr) { case T_File: filegen_file = my_opts->value.s; break; case T_Type: switch (my_opts->value.i) { default: INSIST(0); break; case T_None: filegen_type = FILEGEN_NONE; break; case T_Pid: filegen_type = FILEGEN_PID; break; case T_Day: filegen_type = FILEGEN_DAY; break; case T_Week: filegen_type = FILEGEN_WEEK; break; case T_Month: filegen_type = FILEGEN_MONTH; break; case T_Year: filegen_type = FILEGEN_YEAR; break; case T_Age: filegen_type = FILEGEN_AGE; break; } break; case T_Flag: switch (my_opts->value.i) { case T_Link: filegen_flag |= FGEN_FLAG_LINK; break; case T_Nolink: filegen_flag &= ~FGEN_FLAG_LINK; break; case T_Enable: filegen_flag |= FGEN_FLAG_ENABLED; break; case T_Disable: filegen_flag &= ~FGEN_FLAG_ENABLED; break; default: msyslog(LOG_ERR, "CONFIG: Unknown filegen flag token %d", my_opts->value.i); exit(1); } break; default: msyslog(LOG_ERR, "CONFIG: Unknown filegen option token %d", my_opts->attr); exit(1); } } filegen_config(filegen, statsdir, filegen_file, (unsigned int)filegen_type, (unsigned int)filegen_flag); } } static void free_config_monitor( config_tree *ptree ) { if (ptree->stats_dir) { free(ptree->stats_dir); ptree->stats_dir = NULL; } FREE_INT_FIFO(ptree->stats_list); FREE_FILEGEN_FIFO(ptree->filegen_opts); } static void config_access( config_tree *ptree ) { static bool warned_signd; attr_val * my_opt; restrict_node * my_node; int_node * curr_flag; sockaddr_u addr; sockaddr_u mask; struct addrinfo hints; struct addrinfo * ai_list = NULL; struct addrinfo * pai; int rc; bool restrict_default; unsigned short flags; unsigned short mflags; bool range_err; const char * signd_warning = #ifdef ENABLE_MSSNTP "MS-SNTP signd operations currently block ntpd degrading service to all clients."; #else "mssntp restrict bit ignored, this ntpd was configured without --enable-mssntp."; #endif /* Configure the mru options */ my_opt = HEAD_PFIFO(ptree->mru_opts); for (; my_opt != NULL; my_opt = my_opt->link) { range_err = false; switch (my_opt->attr) { case T_Incalloc: if (0 <= my_opt->value.i) mru_incalloc = my_opt->value.u; else range_err = true; break; case T_Incmem: if (0 <= my_opt->value.i) mru_incalloc = (my_opt->value.u * 1024U) / sizeof(mon_entry); else range_err = true; break; case T_Initalloc: if (0 <= my_opt->value.i) mru_initalloc = my_opt->value.u; else range_err = true; break; case T_Initmem: if (0 <= my_opt->value.i) mru_initalloc = (my_opt->value.u * 1024U) / sizeof(mon_entry); else range_err = true; break; case T_Mindepth: if (0 <= my_opt->value.i) mru_mindepth = my_opt->value.u; else range_err = true; break; case T_Maxage: mru_maxage = my_opt->value.i; break; case T_Minage: mru_minage = my_opt->value.i; break; case T_Maxdepth: if (0 <= my_opt->value.i) mru_maxdepth = my_opt->value.u; else mru_maxdepth = UINT_MAX; break; case T_Maxmem: if (0 <= my_opt->value.i) mru_maxdepth = (my_opt->value.u * 1024U) / sizeof(mon_entry); else mru_maxdepth = UINT_MAX; break; default: msyslog(LOG_ERR, "CONFIG: Unknown mru option %s (%d)", keyword(my_opt->attr), my_opt->attr); exit(1); } if (range_err) msyslog(LOG_ERR, "CONFIG: mru %s %d out of range, ignored.", keyword(my_opt->attr), my_opt->value.i); } /* Configure the discard options */ my_opt = HEAD_PFIFO(ptree->discard_opts); for (; my_opt != NULL; my_opt = my_opt->link) { switch (my_opt->attr) { case T_Average: if (0 <= my_opt->value.i && my_opt->value.i <= UCHAR_MAX) ntp_minpoll = (uint8_t)my_opt->value.u; else msyslog(LOG_ERR, "CONFIG: discard average %d out of range, ignored.", my_opt->value.i); break; case T_Minimum: ntp_minpkt = my_opt->value.i; break; case T_Monitor: mon_age = my_opt->value.i; break; default: msyslog(LOG_ERR, "CONFIG: Unknown discard option %s (%d)", keyword(my_opt->attr), my_opt->attr); exit(1); } } /* Configure the restrict options */ my_node = HEAD_PFIFO(ptree->restrict_opts); for (; my_node != NULL; my_node = my_node->link) { if (ai_list != NULL) { /* we do this here, to not need at every continue */ freeaddrinfo(ai_list); ai_list = NULL; } /* Parse the flags */ flags = 0; mflags = 0; curr_flag = HEAD_PFIFO(my_node->flags); for (; curr_flag != NULL; curr_flag = curr_flag->link) { switch (curr_flag->i) { default: INSIST(0); break; case T_Ntpport: mflags |= RESM_NTPONLY; break; case T_Source: mflags |= RESM_SOURCE; break; case T_Flake: flags |= RES_FLAKE; break; case T_Ignore: flags |= RES_IGNORE; break; case T_Kod: flags |= RES_KOD; break; case T_Mssntp: flags |= RES_MSSNTP; break; case T_Limited: flags |= RES_LIMITED; break; case T_Nomodify: flags |= RES_NOMODIFY; break; case T_Nomrulist: flags |= RES_NOMRULIST; break; case T_Nopeer: flags |= RES_NOPEER; break; case T_Noquery: flags |= RES_NOQUERY; break; case T_Noserve: flags |= RES_DONTSERVE; break; case T_Notrap: /* * No-op - included for backward compatibility * with all the world's boilerplate ntp.conf * files. */ flags |= RES_NOTRAP; break; case T_Notrust: flags |= RES_DONTTRUST; break; case T_Version: flags |= RES_VERSION; break; } } if ((RES_MSSNTP & flags) && !warned_signd) { warned_signd = true; fprintf(stderr, "%s\n", signd_warning); msyslog(LOG_WARNING, "CONFIG: %s", signd_warning); } /* It would be swell if we could identify the line number */ if ((RES_KOD & flags) && !(RES_LIMITED & flags)) { const char *kod_where = (my_node->addr) ? my_node->addr->address : (mflags & RESM_SOURCE) ? "source" : "default"; const char *kod_warn = "KOD does nothing without LIMITED."; fprintf(stderr, "restrict %s: %s\n", kod_where, kod_warn); msyslog(LOG_WARNING, "CONFIG: restrict %s: %s", kod_where, kod_warn); } if (RES_NOTRAP & flags) { const char *notrap_where = (my_node->addr) ? my_node->addr->address : (mflags & RESM_SOURCE) ? "source" : "default"; msyslog(LOG_WARNING, "CONFIG: restrict %s: notrap keyword is ignored.", notrap_where); } ZERO_SOCK(&addr); pai = NULL; restrict_default = false; if (NULL == my_node->addr) { ZERO_SOCK(&mask); if (!(RESM_SOURCE & mflags)) { /* * The user specified a default rule * without a -4 / -6 qualifier, add to * both lists */ restrict_default = true; } else { /* apply "restrict source ..." */ DPRINT(1, ("restrict source template mflags %x flags %x\n", mflags, flags)); hack_restrict(RESTRICT_FLAGS, NULL, NULL, mflags, flags, 0); continue; } } else { /* Resolve the specified address */ /* CIDR notation? */ /* will overwrite my_node->mask-> address with CIDR */ fix_node_cidr(my_node); /* type is always zero, AF_INET */ AF(&addr) = (unsigned short)my_node->addr->type; if (getnetnum(my_node->addr->address, &addr) != 1) { /* * Attempt a blocking lookup. This * is in violation of the nonblocking * design of ntpd's mainline code. The * alternative of running without the * restriction until the name resolved * seems worse. * Ideally some scheme could be used for * restrict directives in the startup * ntp.conf to delay starting up the * protocol machinery until after all * restrict hosts have been resolved. */ ZERO(hints); hints.ai_protocol = IPPROTO_UDP; hints.ai_socktype = SOCK_DGRAM; hints.ai_family = my_node->addr->type; rc = getaddrinfo(my_node->addr->address, "ntp", &hints, &ai_list); if (rc) { msyslog(LOG_ERR, "CONFIG: restrict: ignoring line %d, address/host '%s' unusable.", my_node->line_no, my_node->addr->address); continue; } INSIST(ai_list != NULL); pai = ai_list; INSIST(pai->ai_addr != NULL); INSIST(sizeof(addr) >= pai->ai_addrlen); memcpy(&addr, pai->ai_addr, pai->ai_addrlen); INSIST(AF_INET == AF(&addr) || AF_INET6 == AF(&addr)); } SET_HOSTMASK(&mask, AF(&addr)); /* Resolve the mask */ if (my_node->mask) { ZERO_SOCK(&mask); AF(&mask) = my_node->mask->type; if (getnetnum(my_node->mask->address, &mask) != 1) { msyslog(LOG_ERR, "CONFIG: restrict: ignoring line %d, mask '%s' unusable.", my_node->line_no, my_node->mask->address); continue; } } } /* Set the flags */ if (restrict_default) { AF(&addr) = AF_INET; AF(&mask) = AF_INET; hack_restrict(RESTRICT_FLAGS, &addr, &mask, mflags, flags, 0); AF(&addr) = AF_INET6; AF(&mask) = AF_INET6; } do { int op; if (my_node->mode == T_Restrict) op = RESTRICT_FLAGS; else if (my_node->mode == T_Unrestrict && flags == 0 && mflags == 0) op = RESTRICT_REMOVE; else if (my_node->mode == T_Unrestrict) op = RESTRICT_UNFLAG; else continue; /* should never happen */ hack_restrict(op, &addr, &mask, mflags, flags, 0); if (pai != NULL && NULL != (pai = pai->ai_next)) { INSIST(pai->ai_addr != NULL); INSIST(sizeof(addr) >= pai->ai_addrlen); ZERO_SOCK(&addr); memcpy(&addr, pai->ai_addr, pai->ai_addrlen); INSIST(AF_INET == AF(&addr) || AF_INET6 == AF(&addr)); SET_HOSTMASK(&mask, AF(&addr)); } } while (pai != NULL); } if (ai_list != NULL) { /* coverity thinks this can happen, so just in case */ freeaddrinfo(ai_list); ai_list = NULL; } /* coverity[leaked_storage] */ } static void free_config_access( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->mru_opts); FREE_ATTR_VAL_FIFO(ptree->discard_opts); FREE_RESTRICT_FIFO(ptree->restrict_opts); } static void config_rlimit( config_tree *ptree ) { attr_val * rlimit_av; rlimit_av = HEAD_PFIFO(ptree->rlimit); for (; rlimit_av != NULL; rlimit_av = rlimit_av->link) { switch (rlimit_av->attr) { default: INSIST(0); break; case T_Memlock: /* ignore, for backward compatibility */ break; case T_Stacksize: #if defined(RLIMIT_STACK) ntp_rlimit(RLIMIT_STACK, (rlim_t)(rlimit_av->value.i * 4096), 4096, "4k"); #else /* STDERR as well would be fine... */ msyslog(LOG_WARNING, "CONFIG: 'rlimit stacksize' specified but is not available on this system."); #endif /* RLIMIT_STACK */ break; case T_Filenum: #if defined(RLIMIT_NOFILE) ntp_rlimit(RLIMIT_NOFILE, (rlim_t)(rlimit_av->value.i), 1, ""); #else /* STDERR as well would be fine... */ msyslog(LOG_WARNING, "CONFIG: 'rlimit filenum' specified but is not available on this system."); #endif /* RLIMIT_NOFILE */ break; } } } static void config_tinker( config_tree *ptree ) { attr_val * tinker; int item; item = -1; /* quiet warning */ tinker = HEAD_PFIFO(ptree->tinker); for (; tinker != NULL; tinker = tinker->link) { switch (tinker->attr) { default: INSIST(0); break; case T_Allan: item = LOOP_ALLAN; break; case T_Dispersion: item = LOOP_PHI; break; case T_Freq: item = LOOP_FREQ; break; case T_Huffpuff: item = LOOP_HUFFPUFF; break; case T_Panic: item = LOOP_PANIC; break; case T_Step: item = LOOP_MAX; break; case T_Stepback: item = LOOP_MAX_BACK; break; case T_Stepfwd: item = LOOP_MAX_FWD; break; case T_Stepout: item = LOOP_MINSTEP; break; case T_Tick: item = LOOP_TICK; break; } loop_config(item, tinker->value.d); } } static void free_config_rlimit( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->rlimit); } static void free_config_tinker( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->tinker); } /* * config_nic_rules - apply interface listen/ignore/drop items */ static void config_nic_rules( config_tree *ptree, bool input_from_file ) { nic_rule_node * curr_node; sockaddr_u addr; nic_rule_match match_type; nic_rule_action action; char * if_name; char * pchSlash; int prefixlen; int addrbits; curr_node = HEAD_PFIFO(ptree->nic_rules); if (curr_node != NULL && have_interface_option) { msyslog(LOG_ERR, "CONFIG: interface/nic rules are not allowed with --interface (-I) or --novirtualips (-L)%s", (input_from_file) ? ", exiting" : ""); if (input_from_file) exit(1); else return; } for (; curr_node != NULL; curr_node = curr_node->link) { prefixlen = -1; if_name = curr_node->if_name; if (if_name != NULL) if_name = estrdup(if_name); switch (curr_node->match_class) { default: INSIST(false); break; case 0: /* * 0 is out of range for valid token T_... * and in a nic_rules_node indicates the * interface descriptor is either a name or * address, stored in if_name in either case. */ INSIST(if_name != NULL); pchSlash = strchr(if_name, '/'); if (pchSlash != NULL) *pchSlash = '\0'; if (is_ip_address(if_name, AF_UNSPEC, &addr)) { match_type = MATCH_IFADDR; if (pchSlash != NULL && 1 == sscanf(pchSlash + 1, "%d", &prefixlen)) { addrbits = 8 * SIZEOF_INADDR(AF(&addr)); prefixlen = max(-1, prefixlen); prefixlen = min(prefixlen, addrbits); } } else { match_type = MATCH_IFNAME; if (pchSlash != NULL) *pchSlash = '/'; } break; case T_All: match_type = MATCH_ALL; break; case T_Ipv4: match_type = MATCH_IPV4; break; case T_Ipv6: match_type = MATCH_IPV6; break; case T_Wildcard: match_type = MATCH_WILDCARD; break; } switch (curr_node->action) { default: /* * this assignment quiets a gcc "may be used * uninitialized" warning and is here for no * other reason. */ action = ACTION_LISTEN; INSIST(false); break; case T_Listen: action = ACTION_LISTEN; break; case T_Ignore: action = ACTION_IGNORE; break; case T_Drop: action = ACTION_DROP; break; } add_nic_rule(match_type, if_name, prefixlen, action); timer_interfacetimeout(current_time + 2); if (if_name != NULL) free(if_name); } } static void free_config_nic_rules( config_tree *ptree ) { nic_rule_node *curr_node; if (ptree->nic_rules != NULL) { for (;;) { UNLINK_FIFO(curr_node, *ptree->nic_rules, link); if (NULL == curr_node) break; free(curr_node->if_name); free(curr_node); } free(ptree->nic_rules); ptree->nic_rules = NULL; } } static void apply_enable_disable( attr_val_fifo * fifo, int enable ) { attr_val *curr_flag; int option; for (curr_flag = HEAD_PFIFO(fifo); curr_flag != NULL; curr_flag = curr_flag->link) { option = curr_flag->value.i; switch (option) { default: msyslog(LOG_ERR, "CONFIG: can not apply enable/disable token %d, unknown", option); break; case T_Calibrate: proto_config(PROTO_CAL, (unsigned long)enable, 0.); break; case T_Kernel: proto_config(PROTO_KERNEL, (unsigned long)enable, 0.); break; case T_Monitor: proto_config(PROTO_MONITOR, (unsigned long)enable, 0.); break; case T_Ntp: proto_config(PROTO_NTP, (unsigned long)enable, 0.); break; case T_Stats: proto_config(PROTO_FILEGEN, (unsigned long)enable, 0.); break; } } } static void config_system_opts( config_tree *ptree ) { apply_enable_disable(ptree->enable_opts, 1); apply_enable_disable(ptree->disable_opts, 0); } static void free_config_system_opts( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->enable_opts); FREE_ATTR_VAL_FIFO(ptree->disable_opts); } static void config_logconfig( config_tree *ptree ) { attr_val * my_lc; my_lc = HEAD_PFIFO(ptree->logconfig); for (; my_lc != NULL; my_lc = my_lc->link) { switch (my_lc->attr) { case '+': ntp_syslogmask |= get_logmask(my_lc->value.s); break; case '-': ntp_syslogmask &= ~get_logmask(my_lc->value.s); break; case '=': ntp_syslogmask = get_logmask(my_lc->value.s); break; default: INSIST(0); break; } } } static void free_config_logconfig( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->logconfig); } static void config_phone( config_tree *ptree ) { size_t i; string_node * sn; i = 0; sn = HEAD_PFIFO(ptree->phone); for (; sn != NULL; sn = sn->link) { /* need to leave array entry for NULL terminator */ if (i < COUNTOF(sys_phone) - 1) { sys_phone[i++] = estrdup(sn->s); sys_phone[i] = NULL; } else { msyslog(LOG_INFO, "CONFIG: Number of phone entries exceeds %zu. Ignoring phone %s...", (COUNTOF(sys_phone) - 1), sn->s); } } } static void config_mdnstries( config_tree *ptree ) { #if defined(HAVE_DNS_SD_H) && defined(ENABLE_MDNS_REGISTRATION) mdnstries = ptree->mdnstries; #else UNUSED_ARG(ptree); #endif /* ENABLE_MDNS_REGISTRATION */ } static void free_config_phone( config_tree *ptree ) { FREE_STRING_FIFO(ptree->phone); } static void config_setvar( config_tree *ptree ) { setvar_node *my_node; size_t varlen, vallen, octets; char * str; str = NULL; my_node = HEAD_PFIFO(ptree->setvar); for (; my_node != NULL; my_node = my_node->link) { varlen = strlen(my_node->var); vallen = strlen(my_node->val); octets = varlen + vallen + 1 + 1; str = erealloc(str, octets); snprintf(str, octets, "%s=%s", my_node->var, my_node->val); set_sys_var(str, octets, (my_node->isdefault) ? DEF : 0); } if (str != NULL) free(str); } static void free_config_setvar( config_tree *ptree ) { FREE_SETVAR_FIFO(ptree->setvar); } static void config_fudge( config_tree *ptree ) { addr_opts_node *curr_fudge; # ifdef REFCLOCK attr_val *curr_opt; sockaddr_u addr_sock; address_node *addr_node; struct refclockstat clock_stat; bool err_flag; curr_fudge = HEAD_PFIFO(ptree->fudge); for (; curr_fudge != NULL; curr_fudge = curr_fudge->link) { err_flag = false; /* Get the reference clock address and * ensure that it is sane */ addr_node = curr_fudge->addr; ZERO_SOCK(&addr_sock); if (getnetnum(addr_node->address, &addr_sock) != 1) { err_flag = true; msyslog(LOG_ERR, "CONFIG: unrecognized fudge reference clock address %s, line ignored", socktoa(&addr_sock)); } if (!ISREFCLOCKADR(&addr_sock)) { err_flag = true; msyslog(LOG_ERR, "CONFIG: inappropriate address %s for the fudge command, line ignored", socktoa(&addr_sock)); } /* Parse all the options to the fudge command */ ZERO(clock_stat); curr_opt = HEAD_PFIFO(curr_fudge->options); for (; curr_opt != NULL; curr_opt = curr_opt->link) { switch (curr_opt->attr) { case T_Time1: clock_stat.haveflags |= CLK_HAVETIME1; clock_stat.fudgetime1 = curr_opt->value.d; break; case T_Time2: clock_stat.haveflags |= CLK_HAVETIME2; clock_stat.fudgetime2 = curr_opt->value.d; break; case T_Stratum: clock_stat.haveflags |= CLK_HAVEVAL1; clock_stat.fudgeval1 = curr_opt->value.i; break; case T_Refid: clock_stat.haveflags |= CLK_HAVEVAL2; clock_stat.fudgeval2 = 0; memcpy(&clock_stat.fudgeval2, curr_opt->value.s, min(strlen(curr_opt->value.s), REFIDLEN)); break; case T_Flag1: clock_stat.haveflags |= CLK_HAVEFLAG1; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG1; else clock_stat.flags &= ~CLK_FLAG1; break; case T_Flag2: clock_stat.haveflags |= CLK_HAVEFLAG2; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG2; else clock_stat.flags &= ~CLK_FLAG2; break; case T_Flag3: clock_stat.haveflags |= CLK_HAVEFLAG3; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG3; else clock_stat.flags &= ~CLK_FLAG3; break; case T_Flag4: clock_stat.haveflags |= CLK_HAVEFLAG4; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG4; else clock_stat.flags &= ~CLK_FLAG4; break; default: msyslog(LOG_ERR, "CONFIG: Unexpected fudge flag %s (%d) for %s", token_name(curr_opt->attr), curr_opt->attr, socktoa(&addr_sock)); exit(curr_opt->attr ? curr_opt->attr : 1); } } if (!err_flag) refclock_control(&addr_sock, &clock_stat, NULL); } # else curr_fudge = HEAD_PFIFO(ptree->fudge); if (curr_fudge != NULL) msyslog(LOG_ERR, "CONFIG: Fudge commands not supported: built without refclocks"); # endif } static void free_config_fudge( config_tree *ptree ) { FREE_ADDR_OPTS_FIFO(ptree->fudge); } /* Clone of config_vars that only does log file. */ static void config_logfile( config_tree *ptree ) { attr_val *curr_var; curr_var = HEAD_PFIFO(ptree->vars); for (; curr_var != NULL; curr_var = curr_var->link) { /* Determine which variable to set and set it */ switch (curr_var->attr) { case T_Logfile: if (-1 == change_logfile(curr_var->value.s, true)) msyslog(LOG_ERR, "CONFIG: Cannot open logfile %s: %m", curr_var->value.s); break; default: break; } } } static void config_vars( config_tree *ptree ) { attr_val *curr_var; curr_var = HEAD_PFIFO(ptree->vars); for (; curr_var != NULL; curr_var = curr_var->link) { /* Determine which variable to set and set it */ switch (curr_var->attr) { case T_Tick: loop_config(LOOP_TICK, curr_var->value.d); break; case T_Driftfile: if ('\0' == curr_var->value.s[0]) { stats_drift_file = 0; msyslog(LOG_INFO, "CONFIG: driftfile disabled"); } else stats_config(STATS_FREQ_FILE, curr_var->value.s); break; case T_Dscp: /* DSCP is in the upper 6 bits of the IP TOS/DS field */ qos = curr_var->value.i << 2; break; case T_WanderThreshold: /* FALLTHROUGH */ case T_Nonvolatile: wander_threshold = curr_var->value.d; break; case T_Leapfile: stats_config(STATS_LEAP_FILE, curr_var->value.s); break; #ifdef ENABLE_LEAP_SMEAR case T_Leapsmearinterval: if (curr_var->value.i < 0) msyslog(LOG_ERR, "CONFIG: negative leap smear interval ignored: %i", curr_var->value.i); break; leap_smear_intv = curr_var->value.u; msyslog(LOG_INFO, "CONFIG: leap smear interval %u sec", leap_smear_intv); break; #endif case T_Pidfile: stats_config(STATS_PID_FILE, curr_var->value.s); break; case T_Logfile: /* processed in config_logfile */ break; default: msyslog(LOG_ERR, "CONFIG: config_vars(): unexpected token %d", curr_var->attr); } } } static void free_config_vars( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->vars); } /* Define a function to check if a resolved address is sane. * If yes, return true, else return false; */ static bool is_sane_resolved_address( sockaddr_u * peeraddr, int hmode ) { if (!ISREFCLOCKADR(peeraddr) && ISBADADR(peeraddr)) { msyslog(LOG_ERR, "CONFIG: attempt to configure invalid address %s", socktoa(peeraddr)); return false; } /* * Shouldn't be able to specify multicast * address for server/peer! */ if ((T_Server == hmode || T_Peer == hmode || T_Pool == hmode) && IS_MCAST(peeraddr)) { msyslog(LOG_ERR, "CONFIG: attempt to configure invalid address %s", socktoa(peeraddr)); return false; } if (IS_IPV6(peeraddr) && !ipv6_works) return false; /* Ok, all tests succeeded, now we can return true */ return true; } /* * peer_config - configure a new association * * RETURN: a pointer to the new peer structure * NULL if this would duplicate an existing peer */ struct peer * peer_config( sockaddr_u * srcadr, const char * hostname, endpt * dstadr, int htype, struct peer_ctl *ctl) { uint8_t cast_flags; uint8_t hmode; #ifndef ENABLE_DNS_LOOKUP if (NULL != hostname) { msyslog(LOG_ERR, "CONFIG: hostnames need DNS lookup: %s", hostname); return NULL; } #endif /* * We do a dirty little jig to figure the cast flags. This is * probably not the best place to do this, at least until the * configure code is rebuilt. Note only one flag can be set. */ switch (htype) { case T_Pool: cast_flags = MDF_POOL; hmode = MODE_CLIENT; ctl->flags &= ~FLAG_PREEMPT; break; case T_Peer: msyslog(LOG_ERR, "CONFIG: peer deprecated, treated as server: %s", NULL != hostname? hostname : socktoa(srcadr) ); FALLTHRU case T_Server: cast_flags = MDF_UCAST; hmode = MODE_CLIENT; if (NULL != hostname) ctl->flags |= FLAG_DNS; break; default: msyslog(LOG_ERR, "CONFIG: peer_config, strange htype: %d", htype); cast_flags = MDF_UCAST; hmode = MODE_CLIENT; } /* * Mobilize the association and initialize its variables. If * emulating ntpdate, force iburst. For pool, * strip FLAG_PREEMPT as the prototype associations are not * themselves preemptible, though the resulting associations * are. */ ctl->flags |= FLAG_CONFIG; if (mode_ntpdate) ctl->flags |= FLAG_IBURST; return newpeer(srcadr, hostname, dstadr, hmode, ctl, cast_flags, true); } static void config_peers( config_tree *ptree ) { sockaddr_u peeraddr; peer_node * curr_peer; /* add servers named on the command line with iburst implied */ for ( ; cmdline_server_count > 0; cmdline_server_count--, cmdline_servers++) { struct peer_ctl client_ctl; /* so any new members we introduce will be zeroed */ memset(&client_ctl, '\0', sizeof(struct peer_ctl)); client_ctl.version = NTP_VERSION; client_ctl.minpoll = NTP_MINDPOLL; client_ctl.maxpoll = NTP_MAXPOLL_UNK; client_ctl.flags = FLAG_IBURST; client_ctl.mode = 0; client_ctl.peerkey = 0; client_ctl.bias = 0; ZERO_SOCK(&peeraddr); /* * If we have a numeric address, we can safely * proceed in the mainline with it. Otherwise, hand * the hostname off to the blocking child. */ if (is_ip_address(*cmdline_servers, AF_UNSPEC, &peeraddr)) { SET_PORT(&peeraddr, NTP_PORT); if (is_sane_resolved_address(&peeraddr, T_Server)) peer_config( &peeraddr, NULL, NULL, T_Server, &client_ctl); } else { /* we have a hostname to resolve */ peer_config( &peeraddr, *cmdline_servers, NULL, T_Server, &client_ctl); } } /* add associations from the configuration file */ curr_peer = HEAD_PFIFO(ptree->peers); for (; curr_peer != NULL; curr_peer = curr_peer->link) { ZERO_SOCK(&peeraddr); if (T_Pool == curr_peer->host_mode) { AF(&peeraddr) = curr_peer->addr->type; peer_config( &peeraddr, curr_peer->addr->address, NULL, curr_peer->host_mode, &curr_peer->ctl); /* * If we have a numeric address, we can safely * proceed in the mainline with it. */ } else if (is_ip_address(curr_peer->addr->address, curr_peer->addr->type, &peeraddr)) { SET_PORT(&peeraddr, NTP_PORT); if (is_sane_resolved_address(&peeraddr, curr_peer->host_mode)) { #ifdef REFCLOCK /* save maxpoll from config line * newpeer smashes it */ uint8_t maxpoll = curr_peer->ctl.maxpoll; #endif struct peer *peer = peer_config( &peeraddr, NULL, NULL, curr_peer->host_mode, &curr_peer->ctl); if ( NULL == peer ) { /* duplicate peer !?, ignore */ msyslog(LOG_INFO, "CONFIG: configpeers: Ignoring duplicate '%s'", socktoa(&peeraddr)); continue; } if (ISREFCLOCKADR(&peeraddr)) { #ifdef REFCLOCK uint8_t clktype; int unit; /* * We let the reference clock * support do clock dependent * initialization. This * includes setting the peer * timer, since the clock may * have requirements for this. */ if (NTP_MAXPOLL_UNK == maxpoll) /* default maxpoll for * refclocks is minpoll */ peer->cfg.maxpoll = peer->cfg.minpoll; clktype = (uint8_t)REFCLOCKTYPE(&peer->srcadr); unit = REFCLOCKUNIT(&peer->srcadr); peer->cfg.path = curr_peer->ctl.path; peer->cfg.ppspath = curr_peer->ctl.ppspath; peer->cfg.baud = curr_peer->ctl.baud; if (refclock_newpeer(clktype, unit, peer)) refclock_control(&peeraddr, &curr_peer->clock_stat, NULL); else /* * Dump it, something screwed up */ unpeer(peer); #else /* REFCLOCK */ msyslog(LOG_ERR, "INIT: ntpd was compiled without refclock support."); unpeer(peer); #endif /* REFCLOCK */ } } /* DNS lookup */ } else { AF(&peeraddr) = curr_peer->addr->type; peer_config( &peeraddr, curr_peer->addr->address, NULL, curr_peer->host_mode, &curr_peer->ctl); } } } static void free_config_peers( config_tree *ptree ) { peer_node *curr_peer; if (ptree->peers != NULL) { for (;;) { UNLINK_FIFO(curr_peer, *ptree->peers, link); if (NULL == curr_peer) break; destroy_address_node(curr_peer->addr); free(curr_peer); } free(ptree->peers); ptree->peers = NULL; } } static void config_unpeers( config_tree *ptree ) { sockaddr_u peeraddr; unpeer_node * curr_unpeer; struct peer * p; const char * name; int rc; curr_unpeer = HEAD_PFIFO(ptree->unpeers); for (; curr_unpeer != NULL; curr_unpeer = curr_unpeer->link) { /* * Either AssocID will be zero, and we unpeer by name/ * address addr, or it is nonzero and addr NULL. */ if (curr_unpeer->assocID) { p = findpeerbyassoc(curr_unpeer->assocID); if (p != NULL) { msyslog(LOG_NOTICE, "CONFIG: unpeered %s", socktoa(&p->srcadr)); peer_clear(p, "GONE", true); unpeer(p); } continue; } if (curr_unpeer->addr == NULL) { msyslog(LOG_ERR, "CONFIG: invalid address in unpeer command"); continue; } ZERO(peeraddr); AF(&peeraddr) = curr_unpeer->addr->type; name = curr_unpeer->addr->address; rc = getnetnum(name, &peeraddr); /* Do we have a numeric address? */ if (rc > 0) { DPRINT(1, ("unpeer: searching for %s\n", socktoa(&peeraddr))); p = findexistingpeer(&peeraddr, NULL, NULL, -1); if (p != NULL) { msyslog(LOG_NOTICE, "CONFIG: unpeered %s", socktoa(&peeraddr)); peer_clear(p, "GONE", true); unpeer(p); } continue; } /* It's not a numeric IP address... */ /* It's a hostname. */ p = findexistingpeer(NULL, name, NULL, -1); if (p != NULL) { msyslog(LOG_NOTICE, "CONFIG: unpeered %s", name); peer_clear(p, "GONE", true); unpeer(p); } } } static void free_config_unpeers( config_tree *ptree ) { unpeer_node *curr_unpeer; if (ptree->unpeers != NULL) { for (;;) { UNLINK_FIFO(curr_unpeer, *ptree->unpeers, link); if (NULL == curr_unpeer) break; destroy_address_node(curr_unpeer->addr); free(curr_unpeer); } free(ptree->unpeers); } } static void config_reset_counters( config_tree *ptree ) { int_node *counter_set; for (counter_set = HEAD_PFIFO(ptree->reset_counters); counter_set != NULL; counter_set = counter_set->link) { switch (counter_set->i) { default: DPRINT(1, ("config_reset_counters %s (%d) invalid\n", keyword(counter_set->i), counter_set->i)); break; case T_Allpeers: peer_all_reset(); break; case T_Auth: reset_auth_stats(); break; case T_Ctl: ctl_clr_stats(); break; case T_Io: io_clr_stats(); break; case T_Mem: peer_clr_stats(); break; case T_Sys: proto_clr_stats(); break; case T_Timer: timer_clr_stats(); break; } } } static void free_config_reset_counters( config_tree *ptree ) { FREE_INT_FIFO(ptree->reset_counters); } static void config_ntpd( config_tree *ptree, bool input_from_files ) { /* Do this early so most errors go to new log file */ /* Command line arg is earlier. */ config_logfile(ptree); config_nic_rules(ptree, input_from_files); config_monitor(ptree); config_auth(ptree); config_tos(ptree); config_access(ptree); config_tinker(ptree); config_rlimit(ptree); config_system_opts(ptree); config_logconfig(ptree); config_phone(ptree); config_mdnstries(ptree); config_setvar(ptree); config_vars(ptree); io_open_sockets(); config_peers(ptree); config_unpeers(ptree); config_fudge(ptree); config_reset_counters(ptree); } /* * config_remotely() - implements ntpd side of ntpq :config */ void config_remotely( sockaddr_u * remote_addr ) { char origin[128]; snprintf(origin, sizeof(origin), "remote config from %s", socktoa(remote_addr)); lex_init_stack(origin, NULL); /* no checking needed... */ init_syntax_tree(&cfgt); yyparse(); lex_drop_stack(); cfgt.source.attr = CONF_SOURCE_NTPQ; cfgt.timestamp = time(NULL); cfgt.source.value.s = estrdup(socktoa(remote_addr)); DPRINT(1, ("Finished Parsing!!\n")); save_and_apply_config_tree(false); } /* * getconfig() - return name of configuration file e.g /etc/ntp.conf */ const char * getconfig(const char *explicit_config) { const char *config_file; config_file = CONFIG_FILE; if (explicit_config) { #ifdef HAVE_NETINFO_NI_H check_netinfo = false; #endif config_file = explicit_config; } return config_file; } /* * init_readconfig() - init for readconfig */ void init_readconfig(void) { init_syntax_tree(&cfgt); } /* * readconfig() - process startup configuration file */ void readconfig(const char *config_file) { char line[256]; char dirpath[PATH_MAX]; int srccount; /* * install a non default variable with this daemon version */ snprintf(line, sizeof(line), "daemon_version=\"%s\"", ntpd_version()); set_sys_var(line, strlen(line) + 1, RO); atexit(free_all_config_trees); #ifdef DEBUG #ifndef yydebug /* See comment above for yyparse */ extern int yydebug; #endif yydebug = !!(debug >= 5); #endif /* Moved to init_readconfig so command lines can contribute info * init_syntax_tree(&cfgt); */ srccount = 0; /* parse the plain config file if it exists */ if (lex_init_stack(config_file, "r")) { msyslog(LOG_INFO, "CONFIG: readconfig: parsing file: %s", config_file); yyparse(); ++srccount; //cfgt.source.value.s = estrdup(config_file); } /* parse configs in parallel subdirectory if that exists */ reparent(dirpath, sizeof(dirpath), config_file, CONFIG_DIR); if (is_directory(dirpath) && lex_push_file(dirpath)) { msyslog(LOG_INFO, "CONFIG: readconfig: parsing directory: %s", config_file); yyparse(); ++srccount; } if (srccount == 0) { #ifdef HAVE_NETINFO_NI_H /* If there is no config_file, try NetInfo. */ if (check_netinfo) config_netinfo = get_netinfo_config(); #endif /* HAVE_NETINFO_NI_H */ io_open_sockets(); } lex_drop_stack(); DPRINT(1, ("Finished Parsing!!\n")); //cfgt.source.attr = CONF_SOURCE_FILE; cfgt.timestamp = time(NULL); save_and_apply_config_tree(true); #ifdef HAVE_NETINFO_NI_H if (config_netinfo) free_netinfo_config(config_netinfo); #endif /* HAVE_NETINFO_NI_H */ } /* hooks for ntpd.c */ void set_keys_file(char* keys) { cfgt.auth.keys = estrdup(keys); }; void set_trustedkey(keyid_t tkey) { attr_val *val = create_attr_ival('i', tkey); attr_val *val2 = NULL; APPEND_G_FIFO(val2, val); CONCAT_G_FIFOS(cfgt.auth.trusted_key_list, val2); }; void save_and_apply_config_tree(bool input_from_file) { config_tree *ptree; config_tree *punlinked; /* * Keep all the configuration trees applied since startup in * a list that can be used to dump the configuration back to * a text file. */ ptree = emalloc(sizeof(*ptree)); memcpy(ptree, &cfgt, sizeof(*ptree)); ZERO(cfgt); LINK_TAIL_SLIST(cfg_tree_history, ptree, link, config_tree); /* The actual configuration done depends on whether we are * configuring the simulator or the daemon. Perform a check * and call the appropriate function as needed. */ config_ntpd(ptree, input_from_file); UNLINK_SLIST(punlinked, cfg_tree_history, ptree, link, config_tree); INSIST(punlinked == ptree); free_config_tree(ptree); } /* FUNCTIONS COPIED FROM THE OLDER ntp_config.c * -------------------------------------------- */ /* * get_pfxmatch - find value for prefixmatch * and update char * accordingly */ static uint32_t get_pfxmatch( const char ** pstr, struct masks * m ) { while (m->name != NULL) { if (strncmp(*pstr, m->name, strlen(m->name)) == 0) { *pstr += strlen(m->name); return m->mask; } else { m++; } } return 0; } /* * get_match - find logmask value */ static uint32_t get_match( const char * str, struct masks * m ) { while (m->name != NULL) { if (strcmp(str, m->name) == 0) return m->mask; else m++; } return 0; } /* * get_logmask - build bitmask for ntp_syslogmask */ static uint32_t get_logmask( const char * str ) { const char * t; uint32_t offset; uint32_t mask; mask = get_match(str, logcfg_noclass_items); if (mask != 0) return mask; t = str; offset = get_pfxmatch(&t, logcfg_class); mask = get_match(t, logcfg_class_items); if (mask) return mask << offset; else msyslog(LOG_ERR, "CONFIG: logconfig: '%s' not recognized - ignored", str); return 0; } #ifdef HAVE_NETINFO_NI_H /* * get_netinfo_config - find the nearest NetInfo domain with an ntp * configuration and initialize the configuration state. */ static struct netinfo_config_state * get_netinfo_config(void) { ni_status status; void *domain; ni_id config_dir; struct netinfo_config_state *config; if (ni_open(NULL, ".", &domain) != NI_OK) return NULL; while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) { void *next_domain; if (ni_open(domain, "..", &next_domain) != NI_OK) { ni_free(next_domain); break; } ni_free(domain); domain = next_domain; } if (status != NI_OK) { ni_free(domain); return NULL; } config = emalloc(sizeof(*config)); config->domain = domain; config->config_dir = config_dir; config->prop_index = 0; config->val_index = 0; config->val_list = NULL; return config; } /* * free_netinfo_config - release NetInfo configuration state */ static void free_netinfo_config( struct netinfo_config_state *config ) { ni_free(config->domain); free(config); } /* * gettokens_netinfo - return tokens from NetInfo */ static int gettokens_netinfo ( struct netinfo_config_state *config, char **tokenlist, int *ntokens ) { int prop_index = config->prop_index; int val_index = config->val_index; char **val_list = config->val_list; /* * Iterate through each keyword and look for a property that matches it. */ again: if (!val_list) { for (; prop_index < COUNTOF(keywords); prop_index++) { ni_namelist namelist; struct keyword current_prop = keywords[prop_index]; ni_index index; /* * For each value associated in the property, we're going to return * a separate line. We squirrel away the values in the config state * so the next time through, we don't need to do this lookup. */ NI_INIT(&namelist); if (NI_OK == ni_lookupprop(config->domain, &config->config_dir, current_prop.text, &namelist)) { /* Found the property, but it has no values */ if (namelist.ni_namelist_len == 0) continue; config->val_list = eallocarray( (namelist.ni_namelist_len + 1), sizeof(char*)); val_list = config->val_list; for (index = 0; index < namelist.ni_namelist_len; index++) { char *value; value = namelist.ni_namelist_val[index]; val_list[index] = estrdup(value); } val_list[index] = NULL; break; } ni_namelist_free(&namelist); } config->prop_index = prop_index; } /* No list; we're done here. */ if (!val_list) return CONFIG_UNKNOWN; /* * We have a list of values for the current property. * Iterate through them and return each in order. */ if (val_list[val_index]) { int ntok = 1; int quoted = 0; char *tokens = val_list[val_index]; msyslog(LOG_INFO, "CONFIG: %s %s", keywords[prop_index].text, val_list[val_index]); (const char*)tokenlist[0] = keywords[prop_index].text; for (ntok = 1; ntok < MAXTOKENS; ntok++) { tokenlist[ntok] = tokens; while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted)) quoted ^= (*tokens++ == '"'); if (ISEOL(*tokens)) { *tokens = '\0'; break; } else { /* must be space */ *tokens++ = '\0'; while (ISSPACE(*tokens)) tokens++; if (ISEOL(*tokens)) break; } } if (ntok == MAXTOKENS) { /* HMS: chomp it to lose the EOL? */ msyslog(LOG_ERR, "CONFIG: gettokens_netinfo: too many tokens. Ignoring: %s", tokens); } else { *ntokens = ntok + 1; } config->val_index++; /* HMS: Should this be in the 'else'? */ return keywords[prop_index].keytype; } /* We're done with the current property. */ prop_index = ++config->prop_index; /* Free val_list and reset counters. */ for (val_index = 0; val_list[val_index]; val_index++) free(val_list[val_index]); free(val_list); val_list = config->val_list = NULL; val_index = config->val_index = 0; goto again; } #endif /* HAVE_NETINFO_NI_H */ /* * check my_node->addr for CIDR notation * if so, convert to old addr/mask notation and override mask */ static void fix_node_cidr( restrict_node *my_node) { address_node *addr; char mask_s[40], *mask_p; char *cidr_p; char *colon_p; char *endptr; long cidr_len; int i; unsigned a[8]; REQUIRE(my_node); addr = my_node->addr; REQUIRE(addr); cidr_p = strrchr(addr->address, '/'); if (!cidr_p) { /* not CIDR, leave silently */ return; } *cidr_p++ = '\0'; /* remove the '/' and beyond from address */ /* get CIDR as int */ errno = 0; cidr_len = strtol(cidr_p, &endptr, 10); if ( errno || (endptr == cidr_p) ) { /* conversion fail, leave silently */ return; } if ( 0 >= cidr_len ) { /* negative or zero? leave silently */ /* exiting on 0 avoids a bad shift warning from Coverity */ return; } /* sadly, addr->type not previously set, look for colon */ colon_p = strrchr(addr->address, ':'); if (colon_p) { /* IPv6 */ uint64_t mask_top = 0xFFFFFFFFFFFFFFFFU; uint64_t mask_bot = 0xFFFFFFFFFFFFFFFFU; if ( 128 < cidr_len ) { /* out of range, leave silently */ return; } if ( 64 >= cidr_len ) { mask_bot = 0; mask_top <<= 64 - cidr_len ; } else { mask_bot <<= 128 - cidr_len ; } for (i = 0; i < 4; i++) a[i] = mask_top >> (16 * (3 - i)) & 0xffffU; for (i = 0; i < 4; i++) a[i + 4] = mask_bot >> (16 * (3 - i)) & 0xffffU; snprintf(mask_s, sizeof(mask_s), "%x:%x:%x:%x:%x:%x:%x:%x", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); } else { /* must be IPv4 */ uint32_t mask_n = 0xFFFFFFFFU; if ( 32 < cidr_len ) { /* out of range, leave silently */ return; } mask_n <<= 32 - cidr_len ; for (i = 0; i < 4; i++) a[i] = mask_n >> (8 * (3 - i)) & 0xff; snprintf(mask_s, sizeof(mask_s), "%u.%u.%u.%u", a[0], a[1], a[2], a[3]); } /* lose old mask */ destroy_address_node(my_node->mask); /* create mask node, yes AF_UNSPEC is weird... */ mask_p = estrdup(mask_s); my_node->mask = create_address_node(mask_p, AF_UNSPEC); } /* * getnetnum - return a net number (this is crude, but careful) * * returns 1 for success, and mysteriously, 0 for most failures, and * -1 if the address found is IPv6 and we believe IPv6 isn't working. */ static int getnetnum( const char *num, sockaddr_u *addr ) { REQUIRE(AF_UNSPEC == AF(addr) || AF_INET == AF(addr) || AF_INET6 == AF(addr)); if (!is_ip_address(num, AF(addr), addr)) return 0; if (IS_IPV6(addr) && !ipv6_works) return -1; # ifdef ISC_PLATFORM_HAVESALEN addr->sa.sa_len = SIZEOF_SOCKADDR(AF(addr)); # endif SET_PORT(addr, NTP_PORT); DPRINT(2, ("getnetnum given %s, got %s\n", num, socktoa(addr))); return 1; } void ntp_rlimit( int rl_what, rlim_t rl_value, int rl_scale, const char * rl_sstr ) { struct rlimit rl; #ifndef DEBUG UNUSED_ARG(rl_scale); UNUSED_ARG(rl_sstr); #endif switch (rl_what) { #ifdef RLIMIT_MEMLOCK case RLIMIT_MEMLOCK: /* ignore - for backward compatibility only */ break; #endif /* RLIMIT_MEMLOCK */ #ifdef RLIMIT_NOFILE case RLIMIT_NOFILE: /* * For large systems the default file descriptor limit may * not be enough. */ DPRINT(2, ("ntp_rlimit: NOFILE: %d %s\n", (int)rl_value / rl_scale, rl_sstr)); rl.rlim_cur = rl.rlim_max = rl_value; if (setrlimit(RLIMIT_NOFILE, &rl) == -1) msyslog(LOG_ERR, "CONFIG: Cannot set RLIMIT_NOFILE: %m"); break; #endif /* RLIMIT_NOFILE */ #ifdef RLIMIT_STACK case RLIMIT_STACK: /* * Provide a way to set the stack limit to something * smaller, so that we don't lock a lot of unused * stack memory. */ DPRINT(2, ("ntp_rlimit: STACK: %d %s pages\n", (int)rl_value / rl_scale, rl_sstr)); if (-1 == getrlimit(RLIMIT_STACK, &rl)) { msyslog(LOG_ERR, "CONFIG: getrlimit(RLIMIT_STACK) failed: %m"); } else { if (rl_value > rl.rlim_max) { msyslog(LOG_WARNING, "CONFIG: ntp_rlimit: using maximum allowed stack limit %lu instead of %lu.", (unsigned long)rl.rlim_max, (unsigned long)rl_value); rl_value = rl.rlim_max; } rl.rlim_cur = rl_value; if (-1 == setrlimit(RLIMIT_STACK, &rl)) { msyslog(LOG_ERR, "CONFIG: ntp_rlimit: Cannot set RLIMIT_STACK: %m"); } } break; #endif /* RLIMIT_STACK */ default: INSIST(!"Unexpected setrlimit() case!"); break; } } ntpsec-1.1.0+dfsg1/ntpd/ntp_loopfilter.c0000644000175000017500000011701313252364117020001 0ustar rlaagerrlaager/* * ntp_loopfilter.c - implements the NTP loop filter algorithm * * ATTENTION: Get approval from Dave Mills on all changes to this file! * */ #include "config.h" #include #include #include #include #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include "ntp_syscall.h" #include "timespecops.h" #define NTP_MAXFREQ 500e-6 # define FREQTOD(x) ((x) / 65536e6) /* NTP to double */ # define DTOFREQ(x) ((int32_t)((x) * 65536e6)) /* double to NTP */ /* * This is an implementation of the clock discipline algorithm described * in UDel TR 97-4-3, as amended. It operates as an adaptive parameter, * hybrid phase/frequency-lock loop. A number of sanity checks are * included to protect against timewarps, timespikes and general mayhem. * All units are in s and s/s, unless noted otherwise. */ #define CLOCK_MAX .128 /* default step threshold (s) */ #define CLOCK_MINSTEP 300. /* default stepout threshold (s) */ #define CLOCK_PANIC 1000. /* default panic threshold (s) */ #define CLOCK_PHI 15e-6 /* max frequency error (s/s) */ #define CLOCK_PLL 16. /* PLL loop gain (log2) */ #define CLOCK_AVG 8. /* parameter averaging constant */ #define CLOCK_FLL .25 /* FLL loop gain */ #define CLOCK_FLOOR .0005 /* startup offset floor (s) */ #define CLOCK_ALLAN 11 /* Allan intercept (log2 s) */ #define CLOCK_LIMIT 30 /* poll-adjust threshold */ #define CLOCK_PGATE 4. /* poll-adjust gate */ /* #define PPS_MAXAGE 120 * kernel pps signal timeout (s) UNUSED */ /* * Clock discipline state machine. This is used to control the * synchronization behavior during initialization and following a * timewarp. * * State < step > step Comments * ======================================================== * NSET FREQ step, FREQ freq not set * * FSET SYNC step, SYNC freq set * * FREQ if (mu < 900) if (mu < 900) set freq direct * ignore ignore * else else * freq, SYNC freq, step, SYNC * * SYNC SYNC SPIK, ignore adjust phase/freq * * SPIK SYNC if (mu < 900) adjust phase/freq * ignore * step, SYNC */ /* * Kernel PLL/PPS state machine. This is used with the kernel PLL * modifications described in the documentation. * * If kernel support for the ntp_adjtime() system call is available, the * ntp_control flag is set. The ntp_enable and kern_enable flags can be * set at configuration time or run time using ntpq. If ntp_enable is * false, the discipline loop is unlocked and no corrections of any kind * are made. If both ntp_control and kern_enable are set, the kernel * support is used as described above; if false, the kernel is bypassed * entirely and the daemon discipline used instead. * * There have been three versions of the kernel discipline code. The * first (microkernel) now in Solaris disciplines the microseconds. The * second and third (nanokernel) disciplines the clock in nanoseconds. * These versions are identifed if the symbol STA_PLL is present in the * header file /usr/include/sys/timex.h. The third and current version * includes TAI offset and is identified by the symbol NTP_API with * value 4. * * Each PPS time/frequency discipline can be enabled by the atom driver * or another driver. If enabled, the STA_PPSTIME and STA_FREQ bits are * set in the kernel status word; otherwise, these bits are cleared. * These bits are also cleard if the kernel reports an error. * * If an external clock is present, the clock driver sets STA_CLK in the * status word. When the local clock driver sees this bit, it updates * via this routine, which then calls ntp_adjtime() with the STA_PLL bit * set to zero, in which case the system clock is not adjusted. This is * also a signal for the external clock driver to discipline the system * clock. Unless specified otherwise, all times are in seconds. */ /* * Program variables that can be tinkered. */ double clock_max_back = CLOCK_MAX; /* step threshold */ double clock_max_fwd = CLOCK_MAX; /* step threshold */ static double clock_minstep = CLOCK_MINSTEP; /* stepout threshold */ static double clock_panic = CLOCK_PANIC; /* panic threshold */ double clock_phi = CLOCK_PHI; /* dispersion rate (s/s) */ uint8_t allan_xpt = CLOCK_ALLAN; /* Allan intercept (log2 s) */ /* * Program variables */ #ifndef ENABLE_LOCKCLOCK static double clock_offset; /* offset */ static uptime_t clock_epoch; /* last update */ #endif /* ENABLE_LOCKCLOCK */ double clock_jitter; /* offset jitter */ double drift_comp; /* frequency (s/s) */ static double init_drift_comp; /* initial frequency (PPM) */ double clock_stability; /* frequency stability (wander) (s/s) */ unsigned int sys_tai; /* TAI offset from UTC */ #ifndef ENABLE_LOCKCLOCK static bool loop_started; /* true after LOOP_DRIFTINIT */ static void rstclock (int, double); /* transition function */ static double direct_freq(double); /* direct set frequency */ static void set_freq(double); /* set frequency */ #endif /* ENABLE_LOCKCLOCK */ #ifndef PATH_MAX # define PATH_MAX MAX_PATH #endif static char relative_path[PATH_MAX + 1]; /* relative path per recursive make */ static char *this_file = NULL; static struct timex ntv; /* ntp_adjtime() parameters */ static int pll_status; /* last kernel status bits */ #ifndef ENABLE_LOCKCLOCK #if defined(STA_NANO) && defined(NTP_API) && NTP_API == 4 static unsigned int loop_tai; /* last TAI offset */ #endif /* STA_NANO */ #endif /* ENABLE_LOCKCLOCK */ static void start_kern_loop(void); static void stop_kern_loop(void); /* * Clock state machine control flags */ bool ntp_enable = true; /* clock discipline enabled */ bool pll_control = false; /* kernel support available */ bool kern_enable = true; /* kernel support enabled */ bool hardpps_enable; /* kernel PPS discipline enabled */ bool allow_panic = false; /* allow panic correction (-g) */ bool force_step_once = false; /* always step time once at startup (-G) */ bool mode_ntpdate = false; /* exit on first clock set (-q) */ int freq_cnt; /* initial frequency clamp */ static int freq_set; /* initial set frequency switch */ static bool ext_enable; /* external clock enabled */ /* * Clock state machine variables */ #ifndef ENABLE_LOCKCLOCK static int state = 0; /* clock discipline state */ #endif /* ENABLE_LOCKCLOCK */ uint8_t sys_poll; /* time constant/poll (log2 s) */ int tc_counter; /* jiggle counter */ double last_offset; /* last offset (s) */ /* * Huff-n'-puff filter variables */ static double *sys_huffpuff; /* huff-n'-puff filter */ static int sys_hufflen; /* huff-n'-puff filter stages */ static int sys_huffptr; /* huff-n'-puff filter pointer */ static double sys_mindly; /* huff-n'-puff filter min delay */ /* Emacs cc-mode goes nuts if we split the next line... */ #define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \ MOD_STATUS | MOD_TIMECONST) #ifdef SIGSYS static void pll_trap (int); /* configuration trap */ static struct sigaction sigsys; /* current sigaction status */ static struct sigaction newsigsys; /* new sigaction status */ static sigjmp_buf env; /* environment var. for pll_trap() */ #endif /* SIGSYS */ #ifndef ENABLE_LOCKCLOCK static void sync_status(const char *what, int ostatus, int nstatus) { char obuf[256], nbuf[256], tbuf[1024]; snprintf(obuf, sizeof(obuf), "%04x", (unsigned)ostatus); snprintf(nbuf, sizeof(nbuf), "%04x", (unsigned)nstatus); snprintf(tbuf, sizeof(tbuf), "%s status: %s -> %s", what, obuf, nbuf); report_event(EVNT_KERN, NULL, tbuf); } #endif /* ENABLE_LOCKCLOCK */ /* * file_name - return pointer to non-relative portion of this C file pathname */ static char *file_name(void) { if (this_file == NULL) { (void)strlcpy(relative_path, __FILE__, PATH_MAX); for (this_file=relative_path; *this_file && ! isalnum((unsigned char)*this_file); this_file++) ; } return this_file; } /* * init_loopfilter - initialize loop filter data */ void init_loopfilter(void) { /* * Initialize state variables. */ sys_poll = ntp_minpoll; clock_jitter = LOGTOD(sys_precision); freq_cnt = (int)clock_minstep; } /* * ntp_adjtime_error_handler - process errors from ntp_adjtime */ static void ntp_adjtime_error_handler( const char *caller, /* name of calling function */ struct timex *ptimex, /* pointer to struct timex */ int ret, /* return value from ntp_adjtime */ int saved_errno, /* value of errno when ntp_adjtime returned */ bool pps_call, /* ntp_adjtime call was PPS-related */ bool tai_call, /* ntp_adjtime call was TAI-related */ int line /* line number of ntp_adjtime call */ ) { char des[1024] = ""; /* Decoded Error Status */ switch (ret) { case -1: switch (saved_errno) { case EFAULT: msyslog(LOG_ERR, "CLOCK: %s: %s line %d: invalid " "struct timex pointer: 0x%lx", caller, file_name(), line, (long unsigned)((void *)ptimex) ); break; case EINVAL: msyslog(LOG_ERR, "CLOCK: %s: %s line %d: invalid struct timex \"constant\" element value: %ld", caller, file_name(), line, (long)(ptimex->constant) ); break; case EPERM: if (tai_call) { errno = saved_errno; msyslog(LOG_ERR, "CLOCK: %s: ntp_adjtime(TAI) failed: %m", caller); } errno = saved_errno; msyslog(LOG_ERR, "CLOCK: %s: %s line %d: ntp_adjtime: %m", caller, file_name(), line ); break; default: msyslog(LOG_NOTICE, "CLOCK: %s: %s line %d: unhandled errno value %d after failed ntp_adjtime call", caller, file_name(), line, saved_errno ); break; } break; #ifdef TIME_OK case TIME_OK: /* 0: synchronized, no leap second warning */ /* msyslog(LOG_INFO, "CLOCK: kernel reports time is synchronized normally"); */ break; #else # warning TIME_OK is not defined #endif #ifdef TIME_INS case TIME_INS: /* 1: positive leap second warning */ msyslog(LOG_INFO, "CLOCK: kernel reports leap second insertion scheduled"); break; #else # warning TIME_INS is not defined #endif #ifdef TIME_DEL case TIME_DEL: /* 2: negative leap second warning */ msyslog(LOG_INFO, "CLOCK: kernel reports leap second deletion scheduled"); break; #else # warning TIME_DEL is not defined #endif #ifdef TIME_OOP case TIME_OOP: /* 3: leap second in progress */ msyslog(LOG_INFO, "CLOCK: kernel reports leap second in progress"); break; #else # warning TIME_OOP is not defined #endif #ifdef TIME_WAIT case TIME_WAIT: /* 4: leap second has occurred */ msyslog(LOG_INFO, "CLOCK: kernel reports leap second has occurred"); break; #else # warning TIME_WAIT is not defined #endif #ifdef TIME_ERROR #if 0 from the reference implementation of ntp_gettime(): // Hardware or software error if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) /* * PPS signal lost when either time or frequency synchronization * requested */ || (time_status & (STA_PPSFREQ | STA_PPSTIME) && !(time_status & STA_PPSSIGNAL)) /* * PPS jitter exceeded when time synchronization requested */ || (time_status & STA_PPSTIME && time_status & STA_PPSJITTER) /* * PPS wander exceeded or calibration error when frequency * synchronization requested */ || (time_status & STA_PPSFREQ && time_status & (STA_PPSWANDER | STA_PPSERROR))) return (TIME_ERROR); or, from ntp_adjtime(): if ( (time_status & (STA_UNSYNC | STA_CLOCKERR)) || (time_status & (STA_PPSFREQ | STA_PPSTIME) && !(time_status & STA_PPSSIGNAL)) || (time_status & STA_PPSTIME && time_status & STA_PPSJITTER) || (time_status & STA_PPSFREQ && time_status & (STA_PPSWANDER | STA_PPSERROR)) ) return (TIME_ERROR); #endif case TIME_ERROR: /* 5: unsynchronized, or loss of synchronization */ /* error (see status word) */ if (ptimex->status & STA_UNSYNC) snprintf(des, sizeof(des), "%s%sClock Unsynchronized", des, (*des) ? "; " : ""); if (ptimex->status & STA_CLOCKERR) snprintf(des, sizeof(des), "%s%sClock Error", des, (*des) ? "; " : ""); if (!(ptimex->status & STA_PPSSIGNAL) && ptimex->status & STA_PPSFREQ) snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but no PPS", des, (*des) ? "; " : ""); if (!(ptimex->status & STA_PPSSIGNAL) && ptimex->status & STA_PPSTIME) snprintf(des, sizeof(des), "%s%sPPS Time Sync wanted but no PPS signal", des, (*des) ? "; " : ""); if ( ptimex->status & STA_PPSTIME && ptimex->status & STA_PPSJITTER) snprintf(des, sizeof(des), "%s%sPPS Time Sync wanted but PPS Jitter exceeded", des, (*des) ? "; " : ""); if ( ptimex->status & STA_PPSFREQ && ptimex->status & STA_PPSWANDER) snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but PPS Wander exceeded", des, (*des) ? "; " : ""); if ( ptimex->status & STA_PPSFREQ && ptimex->status & STA_PPSERROR) snprintf(des, sizeof(des), "%s%sPPS Frequency Sync wanted but Calibration error detected", des, (*des) ? "; " : ""); if (pps_call && !(ptimex->status & STA_PPSSIGNAL)) report_event(EVNT_KERN, NULL, "no PPS signal"); DPRINT(1, ("kernel loop status %#x (%s)\n", (unsigned)ptimex->status, des)); /* * This code may be returned when ntp_adjtime() has just * been called for the first time, quite a while after * startup, when ntpd just starts to discipline the kernel * time. In this case the occurrence of this message * can be pretty confusing. * * HMS: How about a message when we begin kernel processing: * Determining kernel clock state... * so an initial TIME_ERROR message is less confising, * or skipping the first message (ugh), * or ??? * msyslog(LOG_INFO, "CLOCK: kernel reports time synchronization lost"); */ msyslog(LOG_INFO, "CLOCK: kernel reports TIME_ERROR: %#x: %s", (unsigned)ptimex->status, des); break; #else # warning TIME_ERROR is not defined #endif default: msyslog(LOG_NOTICE, "CLOCK: %s: %s line %d: unhandled return value %d from ntp_adjtime() in %s at line %d", caller, file_name(), line, ret, __func__, __LINE__ ); break; } return; } /* * local_clock - the NTP logical clock loop filter. * * Return codes: * -1 update ignored: exceeds panic threshold * 0 update ignored: popcorn or exceeds step threshold * 1 clock was slewed * 2 clock was stepped * * ENABLE_LOCKCLOCK: The only thing this routine does is set the * sys_rootdisp variable equal to the peer dispersion. */ int local_clock( struct peer *peer, /* synch source peer structure */ double fp_offset /* clock offset (s) */ ) { #ifdef ENABLE_LOCKCLOCK UNUSED_ARG(peer); UNUSED_ARG(fp_offset); #else int rval; /* return code */ int osys_poll; /* old system poll */ int ntp_adj_ret; /* returned by ntp_adjtime */ double mu; /* interval since last update */ double clock_frequency; /* clock frequency */ double dtemp, etemp; /* double temps */ char tbuf[80]; /* report buffer */ #endif /* ENABLE_LOCKCLOCK */ /* * If the loop is opened or the NIST lockclock scheme is in use, * monitor and record the offsets anyway in order to determine * the open-loop response and then go home. */ #ifdef ENABLE_LOCKCLOCK { #else if (!ntp_enable) { #endif /* ENABLE_LOCKCLOCK */ record_loop_stats(fp_offset, drift_comp, clock_jitter, clock_stability, sys_poll); return (0); } #ifndef ENABLE_LOCKCLOCK /* * If the clock is way off, panic is declared. The clock_panic * defaults to 1000 s; if set to zero, the panic will never * occur. The allow_panic defaults to false, so the first panic * will exit. It can be set true by a command line option, in * which case the clock will be set anyway and time marches on. * But, allow_panic will be set false when the update is less * than the step threshold; so, subsequent panics will exit. */ if (fabs(fp_offset) > clock_panic && clock_panic > 0 && !allow_panic) { snprintf(tbuf, sizeof(tbuf), "%+.0f s; set clock manually within %.0f s.", fp_offset, clock_panic); report_event(EVNT_SYSFAULT, NULL, tbuf); return (-1); } /* * This section simulates ntpdate. If the offset exceeds the * step threshold (128 ms), step the clock to that time and * exit. Otherwise, slew the clock to that time and exit. Note * that the slew will persist and eventually complete beyond the * life of this program. Note that while ntpdate is active, the * terminal does not detach, so the termination message prints * directly to the terminal. */ if (mode_ntpdate) { if ( ( fp_offset > clock_max_fwd && clock_max_fwd > 0) || (-fp_offset > clock_max_back && clock_max_back > 0)) { step_systime(fp_offset, ntp_set_tod); msyslog(LOG_NOTICE, "CLOCK: time set %+.6f s", fp_offset); printf("ntpd: time set %+.6fs\n", fp_offset); } else { adj_systime(fp_offset, adjtime); msyslog(LOG_NOTICE, "CLOCK: time slew %+.6f s", fp_offset); printf("ntpd: time slew %+.6fs\n", fp_offset); } record_loop_stats(fp_offset, drift_comp, clock_jitter, clock_stability, sys_poll); exit(0); } /* * The huff-n'-puff filter finds the lowest delay in the recent * interval. This is used to correct the offset by one-half the * difference between the sample delay and minimum delay. This * is most effective if the delays are highly asymmetric and * clockhopping is avoided and the clock frequency wander is * relatively small. */ if (sys_huffpuff != NULL) { if (peer->delay < sys_huffpuff[sys_huffptr]) sys_huffpuff[sys_huffptr] = peer->delay; if (peer->delay < sys_mindly) sys_mindly = peer->delay; if (fp_offset > 0) dtemp = -(peer->delay - sys_mindly) / 2; else dtemp = (peer->delay - sys_mindly) / 2; fp_offset += dtemp; DPRINT(1, ("local_clock: size %d mindly %.6f huffpuff %.6f\n", sys_hufflen, sys_mindly, dtemp)); } /* * Clock state machine transition function which defines how the * system reacts to large phase and frequency excursion. There * are two main regimes: when the offset exceeds the step * threshold (128 ms) and when it does not. Under certain * conditions updates are suspended until the stepout threshold * (900 s) is exceeded. See the documentation on how these * thresholds interact with commands and command line options. * * Note the kernel is disabled if step is disabled or greater * than 0.5 s or in ntpdate mode. */ osys_poll = sys_poll; if (sys_poll < peer->cfg.minpoll) sys_poll = peer->cfg.minpoll; if (sys_poll > peer->cfg.maxpoll) sys_poll = peer->cfg.maxpoll; mu = current_time - clock_epoch; clock_frequency = drift_comp; rval = 1; if ( ( fp_offset > clock_max_fwd && clock_max_fwd > 0) || (-fp_offset > clock_max_back && clock_max_back > 0) || force_step_once ) { if (force_step_once) { force_step_once = false; /* we want this only once after startup */ msyslog(LOG_NOTICE, "CLOCK: Doing intital time step" ); } switch (state) { /* * In SYNC state we ignore the first outlier and switch * to SPIK state. */ case EVNT_SYNC: snprintf(tbuf, sizeof(tbuf), "%+.6f s", fp_offset); report_event(EVNT_SPIK, NULL, tbuf); state = EVNT_SPIK; return (0); /* * In FREQ state we ignore outliers and inliers. At the * first outlier after the stepout threshold, compute * the apparent frequency correction and step the phase. */ case EVNT_FREQ: if (mu < clock_minstep) return (0); clock_frequency = direct_freq(fp_offset); /* * In SPIK state we ignore succeeding outliers until * either an inlier is found or the stepout threshold is * exceeded. */ /* FALLTHRU to EVNT_SPIK */ FALLTHRU case EVNT_SPIK: if (mu < clock_minstep) return (0); /* * We get here by default in NSET and FSET states and * from above in FREQ or SPIK states. * * In NSET state an initial frequency correction is not * available, usually because the frequency file has not * yet been written. Since the time is outside the step * threshold, the clock is stepped. The frequency will * be set directly following the stepout interval. * * In FSET state the initial frequency has been set from * the frequency file. Since the time is outside the * step threshold, the clock is stepped immediately, * rather than after the stepout interval. Guys get * nervous if it takes 15 minutes to set the clock for * the first time. * * In FREQ and SPIK states the stepout threshold has * expired and the phase is still above the step * threshold. Note that a single spike greater than the * step threshold is always suppressed, even with a * long time constant. */ /* FALLTHRU to default */ FALLTHRU default: snprintf(tbuf, sizeof(tbuf), "%+.6f s", fp_offset); report_event(EVNT_CLOCKRESET, NULL, tbuf); step_systime(fp_offset, ntp_set_tod); reinit_timer(); tc_counter = 0; clock_jitter = LOGTOD(sys_precision); rval = 2; if (state == EVNT_NSET) { rstclock(EVNT_FREQ, 0); return (rval); } break; } rstclock(EVNT_SYNC, 0); } else { /* * The offset is less than the step threshold. Calculate * the jitter as the exponentially weighted offset * differences. */ etemp = SQUARE(clock_jitter); dtemp = SQUARE(fp_offset - last_offset); clock_jitter = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG); switch (state) { /* * In NSET state this is the first update received and * the frequency has not been initialized. Adjust the * phase, but do not adjust the frequency until after * the stepout threshold. */ case EVNT_NSET: adj_systime(fp_offset, adjtime); rstclock(EVNT_FREQ, fp_offset); break; /* * In FREQ state ignore updates until the stepout * threshold. After that, compute the new frequency, but * do not adjust the frequency until the holdoff counter * decrements to zero. */ case EVNT_FREQ: if (mu < clock_minstep) return (0); clock_frequency = direct_freq(fp_offset); /* fall through */ /* * We get here by default in FSET, SPIK and SYNC states. * Here compute the frequency update due to PLL and FLL * contributions. Note, we avoid frequency discipline at * startup until the initial transient has subsided. */ default: allow_panic = false; if (freq_cnt == 0) { /* * The FLL and PLL frequency gain constants * depend on the time constant and Allan * intercept. The PLL is always used, but * becomes ineffective above the Allan intercept * where the FLL becomes effective. */ if (sys_poll >= allan_xpt) clock_frequency += (fp_offset - clock_offset) / (max(ULOGTOD(sys_poll), mu) * CLOCK_FLL); /* * The PLL frequency gain (numerator) depends on * the minimum of the update interval and Allan * intercept. This reduces the PLL gain when the * FLL becomes effective. */ etemp = min(ULOGTOD(allan_xpt), mu); dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll); clock_frequency += fp_offset * etemp / (dtemp * dtemp); } rstclock(EVNT_SYNC, fp_offset); if (fabs(fp_offset) < CLOCK_FLOOR) freq_cnt = 0; break; } } /* * This code segment works when clock adjustments are made using * precision time kernel support and the ntp_adjtime() system * call. This support is available in Solaris 2.6 and later, * Digital Unix 4.0 and later, FreeBSD, Linux and specially * modified kernels for HP-UX 9 and Ultrix 4. In the case of the * DECstation 5000/240 and Alpha AXP, additional kernel * modifications provide a true microsecond clock and nanosecond * clock, respectively. * * Important note: The kernel discipline is used only if the * step threshold is less than 0.5 s, as anything higher can * lead to overflow problems. This might occur if some misguided * lad set the step threshold to something ridiculous. */ if (pll_control && kern_enable && freq_cnt == 0) { static int kernel_status; /* from ntp_adjtime */ /* * We initialize the structure for the ntp_adjtime() * system call. We have to convert everything to * microseconds or nanoseconds first. Do not update the * system variables if the ext_enable flag is set. In * this case, the external clock driver will update the * variables, which will be read later by the local * clock driver. Afterwards, remember the time and * frequency offsets for jitter and stability values and * to update the frequency file. */ ZERO(ntv); if (ext_enable) { ntv.modes = MOD_STATUS; } else { #ifdef STA_NANO ntv.modes = MOD_BITS | MOD_NANO; #else /* STA_NANO */ ntv.modes = MOD_BITS; #endif /* STA_NANO */ if (clock_offset < 0) dtemp = -.5; else dtemp = .5; ntv.offset = (long)(clock_offset * NS_PER_S + dtemp); #ifdef STA_NANO ntv.constant = sys_poll; #else /* STA_NANO */ ntv.constant = sys_poll - 4; #endif /* STA_NANO */ if (ntv.constant < 0) ntv.constant = 0; ntv.esterror = (long)(clock_jitter * US_PER_S); ntv.maxerror = (long)((sys_rootdelay / 2 + sys_rootdisp) * US_PER_S); ntv.status = STA_PLL; /* * Enable/disable the PPS if requested. */ if (hardpps_enable) { ntv.status |= (STA_PPSTIME | STA_PPSFREQ); if (!(pll_status & STA_PPSTIME)) sync_status("PPS enabled", pll_status, ntv.status); } else { ntv.status &= ~(STA_PPSTIME | STA_PPSFREQ); if (pll_status & STA_PPSTIME) sync_status("PPS disabled", pll_status, ntv.status); } if (sys_leap == LEAP_ADDSECOND) ntv.status |= STA_INS; else if (sys_leap == LEAP_DELSECOND) ntv.status |= STA_DEL; } /* * Pass the stuff to the kernel. If it squeals, turn off * the pps. In any case, fetch the kernel offset, * frequency and jitter. */ ntp_adj_ret = ntp_adjtime_ns(&ntv); /* * A squeal is a return status < 0, or a state change. */ if ((0 > ntp_adj_ret) || (ntp_adj_ret != kernel_status)) { kernel_status = ntp_adj_ret; ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, hardpps_enable, false, __LINE__ - 1); } pll_status = ntv.status; clock_offset = ntv.offset * S_PER_NS; clock_frequency = FREQTOD(ntv.freq); /* * If the kernel PPS is lit, monitor its performance. */ if (ntv.status & STA_PPSTIME) { clock_jitter = ntv.jitter * S_PER_NS; } #if defined(STA_NANO) && defined(NTP_API) && NTP_API == 4 /* * If the TAI changes, update the kernel TAI. */ if (loop_tai != sys_tai) { loop_tai = sys_tai; ntv.modes = MOD_TAI; ntv.constant = (long)sys_tai; if ((ntp_adj_ret = ntp_adjtime_ns(&ntv)) != 0) { ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, false, true, __LINE__ - 1); } } #endif /* STA_NANO */ } /* * Clamp the frequency within the tolerance range and calculate * the frequency difference since the last update. */ if (fabs(clock_frequency) > NTP_MAXFREQ) msyslog(LOG_NOTICE, "CLOCK: frequency error %.0f PPM exceeds tolerance %.0f PPM", clock_frequency * US_PER_S, NTP_MAXFREQ * US_PER_S); dtemp = SQUARE(clock_frequency - drift_comp); if (clock_frequency > NTP_MAXFREQ) drift_comp = NTP_MAXFREQ; else if (clock_frequency < -NTP_MAXFREQ) drift_comp = -NTP_MAXFREQ; else drift_comp = clock_frequency; /* * Calculate the wander as the exponentially weighted RMS * frequency differences. Record the change for the frequency * file update. */ etemp = SQUARE(clock_stability); clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG); /* * Here we adjust the time constant by comparing the current * offset with the clock jitter. If the offset is less than the * clock jitter times a constant, then the averaging interval is * increased, otherwise it is decreased. A bit of hysteresis * helps calm the dance. Works best using burst mode. Don't * fiddle with the poll during the startup clamp period. */ if (freq_cnt > 0) { tc_counter = 0; } else if (fabs(clock_offset) < CLOCK_PGATE * clock_jitter) { tc_counter += sys_poll; if (tc_counter > CLOCK_LIMIT) { tc_counter = CLOCK_LIMIT; if (sys_poll < peer->cfg.maxpoll) { tc_counter = 0; sys_poll++; } } } else { tc_counter -= sys_poll << 1; if (tc_counter < -CLOCK_LIMIT) { tc_counter = -CLOCK_LIMIT; if (sys_poll > peer->cfg.minpoll) { tc_counter = 0; sys_poll--; } } } /* * If the time constant has changed, update the poll variables. */ if (osys_poll != sys_poll) poll_update(peer, sys_poll); /* * Yibbidy, yibbbidy, yibbidy; that'h all folks. */ record_loop_stats(clock_offset, drift_comp, clock_jitter, clock_stability, sys_poll); DPRINT(1, ("local_clock: offset %.9f jit %.9f freq %.6f stab %.3f poll %d\n", clock_offset, clock_jitter, drift_comp * US_PER_S, clock_stability * US_PER_S, sys_poll)); return (rval); #endif /* ENABLE_LOCKCLOCK */ } /* * adj_host_clock - Called once every second to update the local clock. * * ENABLE_LOCKCLOCK: The only thing this routine does is increment the * sys_rootdisp variable. */ void adj_host_clock( void ) { #ifndef ENABLE_LOCKCLOCK double offset_adj; double freq_adj; #endif /* ENABLE_LOCKCLOCK */ /* * Update the dispersion since the last update. In contrast to * NTPv3, NTPv4 does not declare unsynchronized after one day, * since the dispersion check serves this function. Also, * since the poll interval can exceed one day, the old test * would be counterproductive. During the startup clamp period, the * time constant is clamped at 2. */ sys_rootdisp += clock_phi; #ifndef ENABLE_LOCKCLOCK if (!ntp_enable || mode_ntpdate) return; /* * Determine the phase adjustment. The gain factor (denominator) * increases with poll interval, so is dominated by the FLL * above the Allan intercept. Note the reduced time constant at * startup. */ if (state != EVNT_SYNC) { offset_adj = 0.; } else if (freq_cnt > 0) { offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(1)); freq_cnt--; } else if (pll_control && kern_enable) { offset_adj = 0.; } else { offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll)); } /* * If the kernel discipline is enabled the frequency correction * drift_comp has already been engaged via ntp_adjtime() in * set_freq(). Otherwise it is a component of the adj_systime() * offset. */ if (pll_control && kern_enable) freq_adj = 0.; else freq_adj = drift_comp; /* Bound absolute value of total adjustment to NTP_MAXFREQ. */ if (offset_adj + freq_adj > NTP_MAXFREQ) offset_adj = NTP_MAXFREQ - freq_adj; else if (offset_adj + freq_adj < -NTP_MAXFREQ) offset_adj = -NTP_MAXFREQ - freq_adj; clock_offset -= offset_adj; /* * FIXME: With the Windows legacy port officially gone, this * could be called much less often. However, leave some * version of the comment below in place in case of a re-port. * See also the related FIXME comment in libntp/systime.c * * Windows port adj_systime() must be called each second, * even if the argument is zero, to ease emulation of * adjtime() using Windows' slew API which controls the rate * but does not automatically stop slewing when an offset * has decayed to zero. */ adj_systime(offset_adj + freq_adj, adjtime); #endif /* ENABLE_LOCKCLOCK */ } #ifndef ENABLE_LOCKCLOCK /* * Clock state machine. Enter new state and set state variables. */ static void rstclock( int trans, /* new state */ double offset /* new offset */ ) { DPRINT(2, ("local_clock: mu %" PRIu32 " state %d poll %d count %d\n", current_time - clock_epoch, trans, sys_poll, tc_counter)); if (trans != state && trans != EVNT_FSET) report_event(trans, NULL, NULL); state = trans; last_offset = clock_offset = offset; clock_epoch = current_time; } /* * calc_freq - calculate frequency directly * * This is very carefully done. When the offset is first computed at the * first update, a residual frequency component results. Subsequently, * updates are suppresed until the end of the measurement interval while * the offset is amortized. At the end of the interval the frequency is * calculated from the current offset, residual offset, length of the * interval and residual frequency component. At the same time the * frequenchy file is armed for update at the next hourly stats. */ static double direct_freq( double fp_offset ) { set_freq(fp_offset / (current_time - clock_epoch)); return drift_comp; } #endif /* ENABLE_LOCKCLOCK */ #ifndef ENABLE_LOCKCLOCK /* * set_freq - set clock frequency correction * * Used to step the frequency correction at startup, possibly again once * the frequency is measured (that is, transitioning from EVNT_NSET to * EVNT_FSET), and finally to switch between daemon and kernel loop * discipline at runtime. * * When the kernel loop discipline is available but the daemon loop is * in use, the kernel frequency correction is disabled (set to 0) to * ensure drift_comp is applied by only one of the loops. */ static void set_freq( double freq /* frequency update */ ) { const char * loop_desc; drift_comp = freq; loop_desc = "ntpd"; if (pll_control) { int ntp_adj_ret; ZERO(ntv); ntv.modes = MOD_FREQUENCY; if (kern_enable) { loop_desc = "kernel"; ntv.freq = DTOFREQ(drift_comp); } if ((ntp_adj_ret = ntp_adjtime_ns(&ntv)) != 0) { ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, false, false, __LINE__ - 1); } } mprintf_event(EVNT_FSET, NULL, "%s %.6f PPM", loop_desc, drift_comp * US_PER_S); } #endif /* HAVE_LOCKCLOCK */ static void start_kern_loop(void) { static bool atexit_done; int ntp_adj_ret; pll_control = true; ZERO(ntv); ntv.modes = MOD_BITS; ntv.status = STA_PLL; ntv.maxerror = sys_maxdisp; ntv.esterror = sys_maxdisp; ntv.constant = sys_poll; /* why is it that here constant is unconditionally set to sys_poll, whereas elsewhere is is modified depending on nanosecond vs. microsecond kernel? */ #ifdef SIGSYS /* * Use sigsetjmp() to save state and then call ntp_adjtime(); if * it fails, then pll_trap() will set pll_control false before * returning control using siglogjmp(). */ newsigsys.sa_handler = pll_trap; newsigsys.sa_flags = 0; if (sigaction(SIGSYS, &newsigsys, &sigsys)) { msyslog(LOG_ERR, "ERR: sigaction() trap SIGSYS: %m"); pll_control = false; } else { if (sigsetjmp(env, 1) == 0) { if ((ntp_adj_ret = ntp_adjtime_ns(&ntv)) != 0) { ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, false, false, __LINE__ - 1); } } if (sigaction(SIGSYS, &sigsys, NULL)) { msyslog(LOG_ERR, "ERR: sigaction() restore SIGSYS: %m"); pll_control = false; } } #else /* SIGSYS */ if ((ntp_adj_ret = ntp_adjtime_ns(&ntv)) != 0) { ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, false, false, __LINE__ - 1); } #endif /* SIGSYS */ /* * Save the result status and light up an external clock * if available. */ pll_status = ntv.status; if (pll_control) { if (!atexit_done) { atexit_done = true; atexit(&stop_kern_loop); } #ifdef STA_NANO if (pll_status & STA_CLK) ext_enable = true; #endif /* STA_NANO */ report_event(EVNT_KERN, NULL, "kernel time sync enabled"); } } static void stop_kern_loop(void) { if (pll_control && kern_enable) report_event(EVNT_KERN, NULL, "kernel time sync disabled"); } /* * select_loop() - choose kernel or daemon loop discipline. */ void select_loop( int use_kern_loop ) { if (kern_enable == use_kern_loop) return; if (pll_control && !use_kern_loop) stop_kern_loop(); kern_enable = use_kern_loop; if (pll_control && use_kern_loop) start_kern_loop(); /* * If this loop selection change occurs after initial startup, * call set_freq() to switch the frequency compensation to or * from the kernel loop. */ #if !defined(ENABLE_LOCKCLOCK) if (pll_control && loop_started) set_freq(drift_comp); #endif } /* * huff-n'-puff filter */ void huffpuff(void) { int i; if (sys_huffpuff == NULL) return; sys_huffptr = (sys_huffptr + 1) % sys_hufflen; sys_huffpuff[sys_huffptr] = 1e9; sys_mindly = 1e9; for (i = 0; i < sys_hufflen; i++) { if (sys_huffpuff[i] < sys_mindly) sys_mindly = sys_huffpuff[i]; } } /* * loop_config - configure the loop filter * * ENABLE_LOCKCLOCK: The LOOP_DRIFTINIT and LOOP_DRIFTCOMP cases are no-ops. */ void loop_config( int item, double freq ) { int i; DPRINT(2, ("loop_config: item %d freq %f\n", item, freq)); switch (item) { /* * We first assume the kernel supports the ntp_adjtime() * syscall. If that syscall works, initialize the kernel time * variables. Otherwise, continue leaving no harm behind. */ case LOOP_DRIFTINIT: #ifndef ENABLE_LOCKCLOCK if (mode_ntpdate) break; start_kern_loop(); /* * Initialize frequency if given; otherwise, begin frequency * calibration phase. */ { double ftemp = init_drift_comp / US_PER_S; if (ftemp > NTP_MAXFREQ) ftemp = NTP_MAXFREQ; else if (ftemp < -NTP_MAXFREQ) ftemp = -NTP_MAXFREQ; set_freq(ftemp); } if (freq_set) rstclock(EVNT_FSET, 0); else rstclock(EVNT_NSET, 0); loop_started = true; #endif /* !ENABLE_LOCKCLOCK */ break; case LOOP_KERN_CLEAR: #if 0 /* XXX: needs more review, and how can we get here? */ #ifndef ENABLE_LOCKCLOCK if (pll_control && kern_enable) { memset((char *)&ntv, 0, sizeof(ntv)); ntv.modes = MOD_STATUS; ntv.status = STA_UNSYNC; ntp_adjtime_ns(&ntv); sync_status("kernel time sync disabled", pll_status, ntv.status); } #endif /* ENABLE_LOCKCLOCK */ #endif break; /* * Tinker command variables for Ulrich Windl. Very dangerous. */ case LOOP_ALLAN: /* Allan intercept (log2) (allan) */ allan_xpt = (uint8_t)freq; break; case LOOP_PHI: /* dispersion threshold (dispersion) */ clock_phi = freq / US_PER_S; break; case LOOP_FREQ: /* initial frequency (freq) */ init_drift_comp = freq; freq_set++; break; case LOOP_HUFFPUFF: /* huff-n'-puff length (huffpuff) */ if (freq < HUFFPUFF) freq = HUFFPUFF; sys_hufflen = (int)(freq / HUFFPUFF); sys_huffpuff = emalloc(sizeof(sys_huffpuff[0]) * (unsigned long)sys_hufflen); for (i = 0; i < sys_hufflen; i++) sys_huffpuff[i] = 1e9; sys_mindly = 1e9; break; case LOOP_PANIC: /* panic threshold (panic) */ clock_panic = freq; break; case LOOP_MAX: /* step threshold (step) */ clock_max_fwd = clock_max_back = freq; if ( D_ISZERO_NS(freq) || freq > 0.5) select_loop(false); break; case LOOP_MAX_BACK: /* step threshold (step) */ clock_max_back = freq; /* * Leave using the kernel discipline code unless both * limits are massive. This assumes the reason to stop * using it is that it's pointless, not that it goes wrong. */ if ( D_ISZERO_NS(clock_max_back) || (clock_max_back > 0.5) || D_ISZERO_NS(clock_max_fwd) || (clock_max_fwd > 0.5)) select_loop(false); break; case LOOP_MAX_FWD: /* step threshold (step) */ clock_max_fwd = freq; if ( D_ISZERO_NS(clock_max_back) || (clock_max_back > 0.5) || D_ISZERO_NS(clock_max_fwd) || (clock_max_fwd > 0.5)) select_loop(false); break; case LOOP_MINSTEP: /* stepout threshold (stepout) */ if (freq < CLOCK_MINSTEP) clock_minstep = CLOCK_MINSTEP; else clock_minstep = freq; break; case LOOP_TICK: /* tick increment (tick) */ set_sys_tick_precision(freq); break; case LOOP_LEAP: /* not used, fall through */ default: msyslog(LOG_NOTICE, "CONFIG: loop_config: unsupported option %d", item); } } #if defined(SIGSYS) /* * _trap - trap processor for undefined syscalls * * This nugget is called by the kernel when the SYS_ntp_adjtime() * syscall bombs because the silly thing has not been implemented in * the kernel. In this case the phase-lock loop is emulated by * the stock adjtime() syscall and a lot of indelicate abuse. */ static void pll_trap( int arg ) { UNUSED_ARG(arg); pll_control = false; siglongjmp(env, 1); } #endif /* SIGSYS */ ntpsec-1.1.0+dfsg1/ntpd/refclock_local.c0000644000175000017500000001244213252364117017703 0ustar rlaagerrlaager /* * refclock_local - local pseudo-clock driver */ #include "config.h" #include "ntp.h" #include "ntpd.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "ntp_syscall.h" #include #include /* * This is a hack to allow a machine to use its own system clock as a * reference clock, i.e., to free-run using no outside clock discipline * source. Note that the clock selection algorithm will not select this * driver unless all other sources of synchronization have been lost. * This is useful if you want to use NTP in an isolated environment * with no radio clock or NIST modem available. Pick a machine that you * figure has a good clock oscillator and configure it with this * driver. Set the clock using the best means available, like * eyeball-and-wristwatch. Then, point all the other machines at this * one or use broadcast (not multicast) mode to distribute time. * * Another application for this driver is if you want to use a * particular server's clock as the clock of last resort when all other * normal synchronization sources have gone away. This is especially * useful if that server has an ovenized oscillator. However, the * preferred was to do this is using orphan mode. See the documentation. * * A third application for this driver is when an external discipline * source is available, such as the NIST "lockclock" program, which * synchronizes the local clock via a telephone modem and the NIST * Automated Computer Time Service (ACTS), or the Digital Time * Synchronization Service (DTSS), which runs on DCE machines. In this * case the stratum should be set at zero, indicating a bona fide * stratum-1 source. Exercise some caution with this, since there is no * easy way to telegraph via NTP that something might be wrong in the * discipline source itself. In the case of DTSS, the local clock can * have a rather large jitter, depending on the interval between * corrections and the intrinsic frequency error of the clock * oscillator. In extreme cases, this can cause clients to exceed the * 128-ms slew window and drop off the NTP subnet. * * Fudge Factors * * None currently supported. */ /* * Local interface definitions */ /* #define PRECISION (-7) * about 10 ms precision UNUSED */ #define NAME "LOCAL" /* shortname */ #define DESCRIPTION "Undisciplined local clock" /* WRU */ #define STRATUM 5 /* default stratum */ #define DISPERSION .01 /* default dispersion (10 ms) */ /* * Function prototypes */ static bool local_start (int, struct peer *); static void local_poll (int, struct peer *); /* * Local variables */ static unsigned long poll_time; /* last time polled */ /* * Transfer vector */ struct refclock refclock_local = { NAME, /* basename of driver */ local_start, /* start up driver */ NULL, /* shut down driver (not used) */ local_poll, /* transmit poll message */ NULL, /* not used (old lcl_control) */ NULL, /* initialize driver (not used) */ NULL /* timer - not used */ }; /* * local_start - start up the clock */ static bool local_start( int unit, struct peer *peer ) { struct refclockproc *pp; UNUSED_ARG(unit); pp = peer->procptr; /* * Initialize miscellaneous variables */ peer->precision = sys_precision; pp->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM; pp->stratum = STRATUM; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy(&pp->refid, "LOCL", REFIDLEN); peer->sstclktype = CTL_SST_TS_LOCAL; poll_time = current_time; return true; } /* * local_poll - called by the transmit procedure * * ENABLE_LOCKCLOCK: If the kernel supports the nanokernel or microkernel * system calls, the leap bits are extracted from the kernel. If there * is a kernel error or the kernel leap bits are set to 11, the NTP leap * bits are set to 11 and the stratum is set to infinity. Otherwise, the * NTP leap bits are set to the kernel leap bits and the stratum is set * as fudged. This behavior does not faithfully follow the * specification, but is probably more appropriate in a multiple-server * national laboratory network. */ static void local_poll( int unit, struct peer *peer ) { struct refclockproc *pp; UNUSED_ARG(unit); /* * Do no evil unless the house is dark or lit with our own lamp. */ if (!(sys_peer == NULL || sys_peer == peer)) return; pp = peer->procptr; pp->polls++; /* * Ramble through the usual filtering and grooming code, which * is essentially a no-op and included mostly for pretty * billboards. */ poll_time = current_time; refclock_process_offset(pp, pp->lastrec, pp->lastrec, 0); /* * If another process is disciplining the system clock, we set * the leap bits and quality indicators from the kernel. */ #if defined(ENABLE_LOCKCLOCK) struct timex ntv; memset(&ntv, 0, sizeof ntv); switch (ntp_adjtime(&ntv)) { case TIME_OK: pp->leap = LEAP_NOWARNING; peer->stratum = pp->stratum; break; case TIME_INS: pp->leap = LEAP_ADDSECOND; peer->stratum = pp->stratum; break; case TIME_DEL: pp->leap = LEAP_DELSECOND; peer->stratum = pp->stratum; break; default: pp->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_UNSPEC; } pp->disp = 0; pp->jitter = 0; #else /* ENABLE_LOCKCLOCK */ pp->leap = LEAP_NOWARNING; pp->disp = DISPERSION; pp->jitter = 0; #endif /* ENABLE_LOCKCLOCK */ pp->lastref = pp->lastrec; refclock_receive(peer); } ntpsec-1.1.0+dfsg1/ntpd/ntp_scanner.h0000644000175000017500000001107513252364117017261 0ustar rlaagerrlaager/* ntp_scanner.h * * The header file for a simple lexical analyzer. * * Written By: Sachin Kamboj * University of Delaware * Newark, DE 19711 * Copyright (c) 2006 * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-clause */ #ifndef GUARD_NTP_SCANNER_H #define GUARD_NTP_SCANNER_H #include "ntp_config.h" /* * ntp.conf syntax is slightly irregular in that some tokens such as * hostnames do not require quoting even if they might otherwise be * recognized as T_ terminal tokens. This hand-crafted lexical scanner * uses a "followed by" value associated with each keyword to indicate * normal scanning of the next token, forced scanning of the next token * alone as a T_String, or forced scanning of all tokens to the end of * the command as T_String. * In the past the identifiers for this functionality ended in _ARG: * * NO_ARG -> FOLLBY_TOKEN * SINGLE_ARG -> FOLLBY_STRING * MULTIPLE_ARG -> FOLLBY_STRINGS_TO_EOC * * Note that some tokens use FOLLBY_TOKEN even though they sometimes * are followed by strings. FOLLBY_STRING is used only when needed to * avoid the keyword scanner matching a token where a string is needed. * * FOLLBY_NON_ACCEPT is an overloading of this field to distinguish * non-accepting states (where the state number does not match a T_ * value). */ typedef enum { FOLLBY_TOKEN = 0, FOLLBY_STRING, FOLLBY_STRINGS_TO_EOC, FOLLBY_NON_ACCEPTING } follby; #define MAXINCLUDELEVEL 5 /* maximum include file levels */ /* STRUCTURES * ---------- */ /* * Define a structure to hold the FSA for the keywords. * The structure is actually a tree. * * To save space, a single uint32_t encodes four fields, and a fifth * (the token completed for terminal states) is implied by the index of * the rule within the scan state array, taking advantage of the fact * there are more scan states than the highest T_ token number. * * The lowest 8 bits hold the character the state matches on. * Bits 8 and 9 hold the followedby value (0 - 3). For non-accepting * states (which do not match a completed token) the followedby * value 3 (FOLLBY_NONACCEPTING) denotes that fact. For accepting * states, values 0 - 2 control whether the scanner forces the * following token(s) to strings. * Bits 10 through 20 hold the next state to check not matching * this state's character. * Bits 21 through 31 hold the next state to check matching the char. */ #define S_ST(ch, fb, match_n, other_n) ( \ (uint8_t)((ch) & 0xff) | \ ((uint32_t)(fb) << 8) | \ ((uint32_t)(match_n) << 10) | \ ((uint32_t)(other_n) << 21) \ ) #define SS_CH(ss) ((char)(uint8_t)((ss) & 0xff)) #define SS_FB(ss) (((unsigned int)(ss) >> 8) & 0x3) #define SS_MATCH_N(ss) (((unsigned int)(ss) >> 10) & 0x7ff) #define SS_OTHER_N(ss) (((unsigned int)(ss) >> 21) & 0x7ff) typedef uint32_t scan_state; struct LCPOS { int nline; int ncol; }; /* Structure to hold a filename, file pointer and positional info. * Instances are dynamically allocated, and the file name is copied by * value into a dynamic extension of the 'fname' array. (Which *must* be * the last field for that reason!) */ struct FILE_INFO { struct FILE_INFO * st_next; /* next on stack */ FILE * fpi; /* File Descriptor */ bool force_eof; /* locked or not */ int backch; /* ungetch buffer */ struct LCPOS curpos; /* current scan position */ struct LCPOS bakpos; /* last line end for ungetc */ struct LCPOS tokpos; /* current token position */ struct LCPOS errpos; /* error position */ char fname[1]; /* (formal only) buffered name */ }; /* SCANNER GLOBAL VARIABLES * ------------------------ */ extern config_tree cfgt; /* Parser output stored here */ /* VARIOUS SUBROUTINE DECLARATIONS * ------------------------------- */ extern const char *keyword(int token); extern char *quote_if_needed(char *str); int yylex(void); /* managing the input source stack itself */ extern bool lex_init_stack(const char * path, const char * mode); extern void lex_drop_stack(void); extern bool lex_flush_stack(void); /* path management for config directories */ extern void reparent(char *, size_t, const char *, const char *); extern bool is_directory(const char *); /* add/remove a nested input source */ extern bool lex_push_file(const char * path); extern bool lex_pop_file(void); /* input stack state query functions */ extern size_t lex_level(void); extern bool lex_from_file(void); extern struct FILE_INFO * lex_current(void); extern const char * const keyword_text[]; #endif /* GUARD_NTP_SCANNER_H */ ntpsec-1.1.0+dfsg1/ntpd/ppsapi_timepps.h0000644000175000017500000000034013252364117017775 0ustar rlaagerrlaager/* ppsapi_timepps.h */ /* * This logic tries to get the timepps.h file from standard * location. */ #ifdef HAVE_TIMEPPS_H # include #else # ifdef HAVE_SYS_TIMEPPS_H # include # endif #endif ntpsec-1.1.0+dfsg1/ntpd/keyword-gen.c0000644000175000017500000004722713252364117017205 0ustar rlaagerrlaager/* * keyword-gen.c -- generate keyword scanner finite state machine and * keyword_text array. * * This program is run to generate ntp_keyword.h */ #include "config.h" #include #include #include #include "ntp_scanner.h" #include "ntp_parser.tab.h" /* Define a structure to hold a (keyword, token) pair */ struct key_tok { const char * key; /* Keyword */ unsigned short token; /* Associated Token */ follby followedby; /* nonzero indicates the next token(s) forced to be string(s) */ }; struct key_tok ntp_keywords[] = { { "...", T_Ellipsis, FOLLBY_TOKEN }, { "allpeers", T_Allpeers, FOLLBY_TOKEN }, { "bias", T_Bias, FOLLBY_TOKEN }, { "baud", T_Baud, FOLLBY_TOKEN }, { "clock", T_Clock, FOLLBY_STRING }, { "ctl", T_Ctl, FOLLBY_TOKEN }, { "disable", T_Disable, FOLLBY_TOKEN }, { "driftfile", T_Driftfile, FOLLBY_STRING }, { "dscp", T_Dscp, FOLLBY_TOKEN }, { "enable", T_Enable, FOLLBY_TOKEN }, { "end", T_End, FOLLBY_TOKEN }, { "filegen", T_Filegen, FOLLBY_TOKEN }, { "fudge", T_Fudge, FOLLBY_STRING }, { "holdover", T_Holdover, FOLLBY_TOKEN }, { "io", T_Io, FOLLBY_TOKEN }, { "includefile", T_Includefile, FOLLBY_STRING }, { "leapfile", T_Leapfile, FOLLBY_STRING }, { "leapsmearinterval", T_Leapsmearinterval, FOLLBY_TOKEN }, { "logconfig", T_Logconfig, FOLLBY_STRINGS_TO_EOC }, { "logfile", T_Logfile, FOLLBY_STRING }, { "mem", T_Mem, FOLLBY_TOKEN }, { "path", T_Path, FOLLBY_STRING }, { "peer", T_Peer, FOLLBY_STRING }, { "phone", T_Phone, FOLLBY_STRINGS_TO_EOC }, { "pidfile", T_Pidfile, FOLLBY_STRING }, { "pool", T_Pool, FOLLBY_STRING }, { "ppspath", T_Ppspath, FOLLBY_STRING }, { "discard", T_Discard, FOLLBY_TOKEN }, { "reset", T_Reset, FOLLBY_TOKEN }, { "restrict", T_Restrict, FOLLBY_TOKEN }, { "refclock", T_Refclock, FOLLBY_STRING }, { "rlimit", T_Rlimit, FOLLBY_TOKEN }, { "server", T_Server, FOLLBY_STRING }, { "setvar", T_Setvar, FOLLBY_STRING }, { "statistics", T_Statistics, FOLLBY_TOKEN }, { "statsdir", T_Statsdir, FOLLBY_STRING }, { "sys", T_Sys, FOLLBY_TOKEN }, { "tick", T_Tick, FOLLBY_TOKEN }, { "timer", T_Timer, FOLLBY_TOKEN }, { "tinker", T_Tinker, FOLLBY_TOKEN }, { "tos", T_Tos, FOLLBY_TOKEN }, { "unconfig", T_Unconfig, FOLLBY_STRING }, { "unit", T_Unit, FOLLBY_TOKEN }, { "unpeer", T_Unpeer, FOLLBY_STRING }, { "unrestrict", T_Unrestrict, FOLLBY_TOKEN }, /* authentication_command */ { "controlkey", T_ControlKey, FOLLBY_TOKEN }, { "requestkey", T_Requestkey, FOLLBY_TOKEN }, /* dummy */ { "keys", T_Keys, FOLLBY_STRING }, { "ntpsigndsocket", T_NtpSignDsocket, FOLLBY_STRING }, { "trustedkey", T_Trustedkey, FOLLBY_TOKEN }, /* IPv4/IPv6 protocol override flag */ { "-4", T_Ipv4_flag, FOLLBY_TOKEN }, { "-6", T_Ipv6_flag, FOLLBY_TOKEN }, /* option */ { "burst", T_Burst, FOLLBY_TOKEN }, { "iburst", T_Iburst, FOLLBY_TOKEN }, { "key", T_Key, FOLLBY_TOKEN }, { "maxpoll", T_Maxpoll, FOLLBY_TOKEN }, { "mdnstries", T_Mdnstries, FOLLBY_TOKEN }, { "minpoll", T_Minpoll, FOLLBY_TOKEN }, { "mode", T_Mode, FOLLBY_TOKEN }, { "noselect", T_Noselect, FOLLBY_TOKEN }, { "true", T_True, FOLLBY_TOKEN }, { "prefer", T_Prefer, FOLLBY_TOKEN }, { "subtype", T_Subtype, FOLLBY_TOKEN }, { "version", T_Version, FOLLBY_TOKEN }, /*** MONITORING COMMANDS ***/ /* stat */ { "clockstats", T_Clockstats, FOLLBY_TOKEN }, { "loopstats", T_Loopstats, FOLLBY_TOKEN }, { "peerstats", T_Peerstats, FOLLBY_TOKEN }, { "protostats", T_Protostats, FOLLBY_TOKEN }, { "rawstats", T_Rawstats, FOLLBY_TOKEN }, { "sysstats", T_Sysstats, FOLLBY_TOKEN }, { "timingstats", T_Timingstats, FOLLBY_TOKEN }, { "usestats", T_Usestats, FOLLBY_TOKEN }, /* filegen_option */ { "file", T_File, FOLLBY_STRING }, { "link", T_Link, FOLLBY_TOKEN }, { "nolink", T_Nolink, FOLLBY_TOKEN }, { "type", T_Type, FOLLBY_TOKEN }, /* filegen_type */ { "age", T_Age, FOLLBY_TOKEN }, { "day", T_Day, FOLLBY_TOKEN }, { "month", T_Month, FOLLBY_TOKEN }, { "none", T_None, FOLLBY_TOKEN }, { "pid", T_Pid, FOLLBY_TOKEN }, { "week", T_Week, FOLLBY_TOKEN }, { "year", T_Year, FOLLBY_TOKEN }, /*** ORPHAN MODE COMMANDS ***/ /* tos_option */ { "minclock", T_Minclock, FOLLBY_TOKEN }, { "maxclock", T_Maxclock, FOLLBY_TOKEN }, { "minsane", T_Minsane, FOLLBY_TOKEN }, { "floor", T_Floor, FOLLBY_TOKEN }, { "ceiling", T_Ceiling, FOLLBY_TOKEN }, { "mindist", T_Mindist, FOLLBY_TOKEN }, { "maxdisp", T_Maxdisp, FOLLBY_TOKEN }, { "maxdist", T_Maxdist, FOLLBY_TOKEN }, { "orphan", T_Orphan, FOLLBY_TOKEN }, { "orphanwait", T_Orphanwait, FOLLBY_TOKEN }, { "nonvolatile", T_Nonvolatile, FOLLBY_TOKEN }, /* access_control_flag */ { "default", T_Default, FOLLBY_TOKEN }, { "source", T_Source, FOLLBY_TOKEN }, { "flake", T_Flake, FOLLBY_TOKEN }, { "ignore", T_Ignore, FOLLBY_TOKEN }, { "limited", T_Limited, FOLLBY_TOKEN }, { "mssntp", T_Mssntp, FOLLBY_TOKEN }, { "kod", T_Kod, FOLLBY_TOKEN }, { "mask", T_Mask, FOLLBY_TOKEN }, { "nomodify", T_Nomodify, FOLLBY_TOKEN }, { "nomrulist", T_Nomrulist, FOLLBY_TOKEN }, { "nopeer", T_Nopeer, FOLLBY_TOKEN }, { "noquery", T_Noquery, FOLLBY_TOKEN }, { "notrap", T_Notrap, FOLLBY_TOKEN }, { "noserve", T_Noserve, FOLLBY_TOKEN }, { "notrust", T_Notrust, FOLLBY_TOKEN }, { "ntpport", T_Ntpport, FOLLBY_TOKEN }, /* discard_option */ { "average", T_Average, FOLLBY_TOKEN }, { "minimum", T_Minimum, FOLLBY_TOKEN }, { "monitor", T_Monitor, FOLLBY_TOKEN }, /* mru_option */ { "incalloc", T_Incalloc, FOLLBY_TOKEN }, { "incmem", T_Incmem, FOLLBY_TOKEN }, { "initalloc", T_Initalloc, FOLLBY_TOKEN }, { "initmem", T_Initmem, FOLLBY_TOKEN }, { "mindepth", T_Mindepth, FOLLBY_TOKEN }, { "maxdepth", T_Maxdepth, FOLLBY_TOKEN }, { "maxage", T_Maxage, FOLLBY_TOKEN }, { "minage", T_Minage, FOLLBY_TOKEN }, { "maxmem", T_Maxmem, FOLLBY_TOKEN }, { "mru", T_Mru, FOLLBY_TOKEN }, /* fudge_factor */ { "flag1", T_Flag1, FOLLBY_TOKEN }, { "flag2", T_Flag2, FOLLBY_TOKEN }, { "flag3", T_Flag3, FOLLBY_TOKEN }, { "flag4", T_Flag4, FOLLBY_TOKEN }, { "refid", T_Refid, FOLLBY_STRING }, { "stratum", T_Stratum, FOLLBY_TOKEN }, { "time1", T_Time1, FOLLBY_TOKEN }, { "time2", T_Time2, FOLLBY_TOKEN }, /* system_option */ { "auth", T_Auth, FOLLBY_TOKEN }, { "calibrate", T_Calibrate, FOLLBY_TOKEN }, { "kernel", T_Kernel, FOLLBY_TOKEN }, { "ntp", T_Ntp, FOLLBY_TOKEN }, { "stats", T_Stats, FOLLBY_TOKEN }, /* rlimit_option */ { "memlock", T_Memlock, FOLLBY_TOKEN }, { "stacksize", T_Stacksize, FOLLBY_TOKEN }, { "filenum", T_Filenum, FOLLBY_TOKEN }, /* tinker_option */ { "step", T_Step, FOLLBY_TOKEN }, { "stepback", T_Stepback, FOLLBY_TOKEN }, { "stepfwd", T_Stepfwd, FOLLBY_TOKEN }, { "panic", T_Panic, FOLLBY_TOKEN }, { "dispersion", T_Dispersion, FOLLBY_TOKEN }, { "stepout", T_Stepout, FOLLBY_TOKEN }, { "allan", T_Allan, FOLLBY_TOKEN }, { "huffpuff", T_Huffpuff, FOLLBY_TOKEN }, { "freq", T_Freq, FOLLBY_TOKEN }, /* miscellaneous_command */ { "port", T_Port, FOLLBY_TOKEN }, { "interface", T_Interface, FOLLBY_TOKEN }, /* interface_command (ignore and interface already defined) */ { "nic", T_Nic, FOLLBY_TOKEN }, { "all", T_All, FOLLBY_TOKEN }, { "ipv4", T_Ipv4, FOLLBY_TOKEN }, { "ipv6", T_Ipv6, FOLLBY_TOKEN }, { "wildcard", T_Wildcard, FOLLBY_TOKEN }, { "listen", T_Listen, FOLLBY_TOKEN }, { "drop", T_Drop, FOLLBY_TOKEN }, }; typedef struct big_scan_state_tag { char ch; /* Character this state matches on */ char followedby; /* Forces next token(s) to T_String */ unsigned short finishes_token; /* nonzero ID if last keyword char */ unsigned short match_next_s; /* next state to check matching ch */ unsigned short other_next_s; /* next state to check if not ch */ } big_scan_state; /* * Note: to increase MAXSTATES beyond 2048, be aware it is currently * crammed into 11 bits in scan_state form. Raising to 4096 would be * relatively easy by storing the followedby value in a separate * array with one entry per token, and shrinking the char value to * 7 bits to free a bit for accepting/non-accepting. More than 4096 * states will require expanding scan_state beyond 32 bits each. */ #define MAXSTATES 2048 #define MAX_TOK_LEN 63 const char * current_keyword;/* for error reporting */ static big_scan_state sst[MAXSTATES]; /* scanner FSM state entries */ static unsigned short sst_highwater; /* next entry index to consider */ char * symb[1024]; /* map token ID to symbolic name */ /* for libntp */ const char * progname = "keyword-gen"; int main (int, char **); static void generate_preamble (void); static void generate_fsm (void); static void generate_token_text (void); static unsigned short create_keyword_scanner (void); static unsigned short create_scan_states (const char *, unsigned short, follby, unsigned short); static int compare_key_tok_id (const void *, const void *); static int compare_key_tok_text (const void *, const void *); static void populate_symb (char *); static const char* symbname (unsigned short); int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "Usage:\n%s t_header.h\n", argv[0]); exit(1); } populate_symb(argv[1]); generate_preamble(); generate_token_text(); generate_fsm(); return 0; } static void generate_preamble(void) { time_t now; char timestamp[128]; struct tm tmbuf; char preamble[] = "/*\n" " * ntp_keyword.h\n" " * \n" " * NOTE: edit this file with caution, it is generated by keyword-gen.c\n" " *\t Generated %s UTC diff_ignore_line\n" " *\n" " */\n" "#include \"ntp_scanner.h\"\n" "#include \"ntp_parser.tab.h\"\n" "\n"; time(&now); if (!strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", gmtime_r(&now, &tmbuf))) timestamp[0] = '\0'; printf(preamble, timestamp); } static void generate_fsm(void) { char rprefix[MAX_TOK_LEN + 1]; char prefix[MAX_TOK_LEN + 1]; char token_id_comment[16 + MAX_TOK_LEN + 1]; size_t prefix_len; char *p; char *r; unsigned short initial_state; unsigned short this_state; unsigned short state; unsigned short i; unsigned short token; /* * Sort ntp_keywords in alphabetical keyword order. This is * not necessary, but minimizes nonfunctional changes in the * generated finite state machine when keywords are modified. */ qsort(ntp_keywords, COUNTOF(ntp_keywords), sizeof(ntp_keywords[0]), compare_key_tok_text); /* * To save space, reserve the state array entry matching each * token number for its terminal state, so the token identifier * does not need to be stored in each state, but can be * recovered trivially. To mark the entry reserved, * finishes_token is nonzero. */ for (i = 0; i < COUNTOF(ntp_keywords); i++) { token = ntp_keywords[i].token; if (1 > token || token >= COUNTOF(sst)) { fprintf(stderr, "keyword-gen sst[%u] too small " "for keyword '%s' id %d\n", (int)COUNTOF(sst), ntp_keywords[i].key, token); exit(4); } sst[token].finishes_token = token; } initial_state = create_keyword_scanner(); fprintf(stderr, "%d keywords consumed %d states of %d max.\n", (int)COUNTOF(ntp_keywords), sst_highwater - 1, (int)COUNTOF(sst) - 1); printf("#define SCANNER_INIT_S %d\n\n", initial_state); printf("const scan_state sst[%d] = {\n" "/*SS_T( ch,\tf-by, match, other ),\t\t\t\t */\n" " 0,\t\t\t\t /* %5d %-17s */\n", sst_highwater, 0, ""); for (i = 1; i < sst_highwater; i++) { /* verify fields will fit */ if (sst[i].followedby & ~0x3) { fprintf(stderr, "keyword-gen internal error " "sst[%d].followedby %d too big\n", i, sst[i].followedby); exit(7); } if (sst_highwater <= sst[i].match_next_s || sst[i].match_next_s & ~0x7ff) { fprintf(stderr, "keyword-gen internal error " "sst[%d].match_next_s %d too big\n", i, sst[i].match_next_s); exit(8); } if (sst_highwater <= sst[i].other_next_s || sst[i].other_next_s & ~0x7ff) { fprintf(stderr, "keyword-gen internal error " "sst[%d].other_next_s %d too big\n", i, sst[i].other_next_s); exit(9); } if (sst[i].finishes_token) { snprintf(token_id_comment, sizeof(token_id_comment), "%5d %-17s", i, symbname(sst[i].finishes_token)); if (i != sst[i].finishes_token) { fprintf(stderr, "keyword-gen internal error " "entry %d finishes token %d\n", i, sst[i].finishes_token); exit(5); } } else { /* * Determine the keyword prefix that leads to this * state. This is expensive but keyword-gen is run * only when it changes. * * Scan the state array iteratively looking for a state * which leads to the current one, collecting matching * characters along the way. There is only one such * path back to the starting state given the way our * scanner state machine is built and the practice of * using the spelling of the keyword as its T_* token * identifier, which results in never having two * spellings result in the same T_* value. */ prefix_len = 0; this_state = i; do { for (state = 1; state < sst_highwater; state++) if (sst[state].other_next_s == this_state) { this_state = state; break; } else if (sst[state].match_next_s == this_state) { this_state = state; rprefix[prefix_len] = sst[state].ch; prefix_len++; break; } } while (this_state != initial_state); if (prefix_len) { /* reverse rprefix into prefix */ p = prefix + prefix_len; r = rprefix; while (r < rprefix + prefix_len) *--p = *r++; } prefix[prefix_len] = '\0'; snprintf(token_id_comment, sizeof(token_id_comment), "%5d %-17s", i, (initial_state == i) ? "[initial state]" : prefix); } printf(" S_ST( '%c',\t%d, %5u, %5u )%s /* %s */\n", sst[i].ch, sst[i].followedby, sst[i].match_next_s, sst[i].other_next_s, (i + 1 < sst_highwater) ? "," : " ", token_id_comment); } printf("};\n\n"); } /* Define a function to create the states of the scanner. This function * is used by the create_keyword_scanner function below. * * This function takes a suffix of a keyword, the token to be returned on * recognizing the complete keyword, and any pre-existing state that exists * for some other keyword that has the same prefix as the current one. */ static unsigned short create_scan_states( const char * text, unsigned short token, follby followedby, unsigned short prev_state ) { unsigned short my_state; unsigned short return_state; unsigned short prev_char_s; unsigned short curr_char_s; return_state = prev_state; curr_char_s = prev_state; prev_char_s = 0; /* Find the correct position to insert the state. * All states should be in alphabetical order */ while (curr_char_s && (text[0] < sst[curr_char_s].ch)) { prev_char_s = curr_char_s; curr_char_s = sst[curr_char_s].other_next_s; } /* * Check if a previously seen keyword has the same prefix as * the current keyword. If so, simply use the state for that * keyword as my_state, otherwise, allocate a new state. */ if (curr_char_s && (text[0] == sst[curr_char_s].ch)) { my_state = curr_char_s; if ('\0' == text[1]) { fprintf(stderr, "Duplicate entries for keyword '%s' in" " keyword_gen.c ntp_keywords[].\n", current_keyword); exit(2); } } else { do my_state = sst_highwater++; while (my_state < COUNTOF(sst) && sst[my_state].finishes_token); if (my_state >= COUNTOF(sst)) { fprintf(stderr, "fatal, keyword scanner state array " "sst[%d] is too small, modify\n" "keyword-gen.c to increase.\n", (int)COUNTOF(sst)); exit(3); } /* Store the next character of the keyword */ sst[my_state].ch = text[0]; sst[my_state].other_next_s = curr_char_s; sst[my_state].followedby = FOLLBY_NON_ACCEPTING; if (prev_char_s) sst[prev_char_s].other_next_s = my_state; else return_state = my_state; } /* Check if the next character is '\0'. * If yes, we are done with the recognition and this is an accepting * state. * If not, we need to continue scanning */ if ('\0' == text[1]) { sst[my_state].finishes_token = (unsigned short)token; sst[my_state].followedby = (char)followedby; if (sst[token].finishes_token != (unsigned short)token) { fprintf(stderr, "fatal, sst[%d] not reserved for %s.\n", token, symbname(token)); exit(6); } /* relocate so token id is sst[] index */ if (my_state != token) { sst[token] = sst[my_state]; ZERO(sst[my_state]); do sst_highwater--; while (sst[sst_highwater].finishes_token); my_state = token; if (prev_char_s) sst[prev_char_s].other_next_s = my_state; else return_state = my_state; } } else sst[my_state].match_next_s = create_scan_states( &text[1], token, followedby, sst[my_state].match_next_s); return return_state; } /* Define a function that takes a list of (keyword, token) values and * creates a keywords scanner out of it. */ static unsigned short create_keyword_scanner(void) { unsigned short scanner; unsigned short i; sst_highwater = 1; /* index 0 invalid, unused */ scanner = 0; for (i = 0; i < COUNTOF(ntp_keywords); i++) { current_keyword = ntp_keywords[i].key; scanner = create_scan_states( ntp_keywords[i].key, ntp_keywords[i].token, ntp_keywords[i].followedby, scanner); } return scanner; } static void generate_token_text(void) { unsigned short lowest_id; unsigned short highest_id; unsigned short id_count; unsigned short id; unsigned short i; /* sort ntp_keywords in token ID order */ qsort(ntp_keywords, COUNTOF(ntp_keywords), sizeof(ntp_keywords[0]), compare_key_tok_id); lowest_id = ntp_keywords[0].token; highest_id = ntp_keywords[COUNTOF(ntp_keywords) - 1].token; id_count = highest_id - lowest_id + 1; printf("#define LOWEST_KEYWORD_ID %d\n\n", lowest_id); printf("const char * const keyword_text[%d] = {", id_count); id = lowest_id; i = 0; while (i < COUNTOF(ntp_keywords)) { while (id < ntp_keywords[i].token) { printf(",\n\t/* %-5d %5d %20s */\tNULL", id - lowest_id, id, symbname(id)); id++; } if (i > 0) printf(","); printf("\n\t/* %-5d %5d %20s */\t\"%s\"", id - lowest_id, id, symbname(id), ntp_keywords[i].key); i++; id++; } printf("\n};\n\n"); } static int compare_key_tok_id( const void *a1, const void *a2 ) { const struct key_tok *p1 = a1; const struct key_tok *p2 = a2; if (p1->token == p2->token) return COMPARE_EQUAL; if (p1->token < p2->token) return COMPARE_LESSTHAN; else return COMPARE_GREATERTHAN; } static int compare_key_tok_text( const void *a1, const void *a2 ) { const struct key_tok *p1 = a1; const struct key_tok *p2 = a2; return strcmp(p1->key, p2->key); } /* * populate_symb() - populate symb[] lookup array with symbolic token * names such that symb[T_Age] == "T_Age", etc. */ static void populate_symb( char *header_file ) { FILE * yh; char line[2 * MAX_TOK_LEN]; char name[2 * MAX_TOK_LEN]; int token; yh = fopen(header_file, "r"); if (NULL == yh) { perror("unable to open yacc/bison header file"); exit(4); } while (NULL != fgets(line, sizeof(line), yh)) if (2 == sscanf(line, "#define %s %d", name, &token) && 'T' == name[0] && '_' == name[1] && token >= 0 && token < (int)COUNTOF(symb)) { symb[token] = strdup(name); if (strlen(name) > MAX_TOK_LEN) { fprintf(stderr, "MAX_TOK_LEN %d too small for '%s'\n" "Edit keyword-gen.c to raise.\n", MAX_TOK_LEN, name); exit(10); } } fclose(yh); } static const char * symbname( unsigned short token ) { static char buf[20]; char *name; if (token < COUNTOF(symb) && symb[token] != NULL) { name = symb[token]; } else { snprintf(buf, sizeof(buf), "%d", token); name = buf; } return name; } ntpsec-1.1.0+dfsg1/ntpd/ntp_refclock.c0000644000175000017500000006611713252364117017422 0ustar rlaagerrlaager/* * ntp_refclock - processing support for reference clocks */ #include "config.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_tty.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "ntp_assert.h" #include "lib_strbuf.h" #include "ntp_calendar.h" #include "timespecops.h" #include #ifdef HAVE_SYS_IOCTL_H # include #endif /* HAVE_SYS_IOCTL_H */ #ifdef REFCLOCK #ifdef HAVE_PPSAPI #include "ppsapi_timepps.h" #include "refclock_pps.h" #endif /* HAVE_PPSAPI */ #define SAMPLE(x) pp->coderecv = (pp->coderecv + 1) % MAXSTAGE; \ pp->filter[pp->coderecv] = (x); \ if (pp->coderecv == pp->codeproc) \ pp->codeproc = (pp->codeproc + 1) % MAXSTAGE; #define TTY struct termios /* * Reference clock support is provided here by maintaining the fiction * that the clock is actually a peer. As no packets are exchanged with * a reference clock, however, we replace the transmit, receive and * packet procedures with separate code to simulate them. Routines * refclock_transmit() and refclock_receive() maintain the peer * variables in a state analogous to an actual peer and pass reference * clock data on through the filters. Routines refclock_peer() and * refclock_unpeer() are called to initialize and terminate reference * clock associations. A set of utility routines is included to open * serial devices, process sample data, and to perform various debugging * functions. * * The main interface used by these routines is the refclockproc * structure, which contains for most drivers the decimal equivalants * of the year, day, month, hour, second and millisecond/microsecond * decoded from the ASCII timecode. Additional information includes * the receive timestamp, exception report, statistics tallies, etc. * In addition, there may be a driver-specific unit structure used for * local control of the device. * * The support routines are passed a pointer to the peer structure, * which is used for all peer-specific processing and contains a * pointer to the refclockproc structure, which in turn contains a * pointer to the unit structure, if used. The peer structure is * identified as a refclock by having a non-NULL procptr member. */ #define FUDGEFAC .1 /* fudge correction factor */ /* #define LF 0x0a * ASCII LF UNUSED */ bool cal_enable; /* enable refclock calibrate */ /* * Forward declarations */ static int refclock_cmpl_fp (const void *, const void *); static int refclock_sample (struct refclockproc *); static bool refclock_setup (int, unsigned int, unsigned int); /* * refclock_report - note the occurrence of an event * * This routine presently just remembers the report and logs it. It * tries to be a good citizen and bothers the system log only if * things change. */ void refclock_report( struct peer *peer, int code ) { struct refclockproc *pp; pp = peer->procptr; if (pp == NULL) return; switch (code) { case CEVNT_TIMEOUT: pp->noreply++; break; case CEVNT_BADREPLY: pp->badformat++; break; case CEVNT_FAULT: break; case CEVNT_BADDATE: case CEVNT_BADTIME: pp->baddata++; break; default: /* ignore others */ break; } if (pp->lastevent < 15) pp->lastevent++; if (pp->currentstatus != code) { pp->currentstatus = (uint8_t)code; report_event(PEVNT_CLOCK, peer, ceventstr(code)); } } char * refclock_name( const struct peer *peer ) { char *buf; buf = lib_getbuf(); snprintf(buf, LIB_BUFLENGTH, "%s(%d)", peer->procptr->clockname, peer->procptr->refclkunit); return buf; } /* * init_refclock - initialize the reference clock drivers * * This routine calls each of the drivers in turn to initialize internal * variables, if necessary. Most drivers have nothing to say at this * point. */ void init_refclock(void) { int i; for (i = 0; i < (int)num_refclock_conf; i++) if (refclock_conf[i]->clock_init) (refclock_conf[i]->clock_init)(); } /* * refclock_newpeer - initialize and start a reference clock * * This routine allocates and initializes the interface structure which * supports a reference clock in the form of an ordinary NTP peer. A * driver-specific support routine completes the initialization, if * used. Default peer variables which identify the clock and establish * its reference ID and stratum are set here. It returns true if success * and false if the clock already running, insufficient resources are * available or the driver declares a bum rap. */ bool refclock_newpeer( uint8_t clktype, int unit, struct peer *peer /* peer structure pointer */ ) { struct refclockproc *pp; if (clktype >= num_refclock_conf || !refclock_conf[clktype]->clock_start) { msyslog(LOG_ERR, "REFCLOCK: refclock_newpeer: clock type %d invalid\n", clktype); return false; } /* * Allocate and initialize interface structure */ pp = emalloc_zero(sizeof(*pp)); peer->procptr = pp; /* * Initialize structures */ peer->cfg.flags |= FLAG_REFCLOCK; peer->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_REFCLOCK; peer->ppoll = peer->cfg.maxpoll; pp->refclkunit = (uint8_t)unit; pp->conf = refclock_conf[clktype]; pp->timestarted = current_time; pp->io.fd = -1; /* * Set peer.pmode based on the hmode. For appearances only. */ switch (peer->hmode) { case MODE_ACTIVE: peer->pmode = MODE_PASSIVE; break; default: peer->pmode = MODE_SERVER; break; } /* * Do driver dependent initialization. The above defaults * can be wiggled, then finish up for consistency. */ if (!((pp->conf->clock_start)(unit, peer))) { refclock_unpeer(peer); return false; } peer->refid = pp->refid; return true; } /* * refclock_unpeer - shut down a clock */ void refclock_unpeer( struct peer *peer /* peer structure pointer */ ) { /* * Wiggle the driver to release its resources, then give back * the interface structure. */ if (NULL == peer->procptr) return; /* There's a standard shutdown sequence if user didn't declare one */ if (peer->procptr->conf->clock_shutdown) (peer->procptr->conf->clock_shutdown)(peer->procptr); else { if (NULL != peer->procptr->unitptr) free(peer->procptr->unitptr); if (-1 != peer->procptr->io.fd) io_closeclock(&peer->procptr->io); } free(peer->procptr); peer->procptr = NULL; } /* * refclock_timer - called once per second for housekeeping. */ void refclock_timer( struct peer *p ) { struct refclockproc * pp; int unit; pp = p->procptr; unit = pp->refclkunit; if (pp->conf->clock_timer) (*pp->conf->clock_timer)(unit, p); if (pp->action != NULL && pp->nextaction <= current_time) (*pp->action)(p); } /* * refclock_transmit - simulate the transmit procedure * * This routine implements the NTP transmit procedure for a reference * clock. This provides a mechanism to call the driver at the NTP poll * interval, as well as provides a reachability mechanism to detect a * broken radio or other madness. */ void refclock_transmit( struct peer *peer /* peer structure pointer */ ) { int unit; unit = peer->procptr->refclkunit; peer->sent++; get_systime(&peer->xmt); /* * This is a ripoff of the peer transmit routine, but * specialized for reference clocks. We do a little less * protocol here and call the driver-specific transmit routine. */ if (peer->burst == 0) { uint8_t oreach; DPRINT(1, ("refclock_transmit: at %u %s\n", current_time, socktoa(&(peer->srcadr)))); /* * Update reachability and poll variables like the * network code. */ oreach = peer->reach & 0xfe; peer->reach <<= 1; if (!(peer->reach & 0x0f)) clock_filter(peer, 0., 0., sys_maxdisp); peer->outdate = current_time; if (!peer->reach) { if (oreach) { report_event(PEVNT_UNREACH, peer, NULL); peer->timereachable = current_time; } } else { if (peer->cfg.flags & FLAG_BURST) peer->burst = NSTAGE; } } else { peer->burst--; } if (peer->procptr->conf->clock_poll) (peer->procptr->conf->clock_poll)(unit, peer); poll_update(peer, peer->hpoll); } /* * Compare two doubles - used with qsort() */ static int refclock_cmpl_fp( const void *p1, const void *p2 ) { const double *dp1 = (const double *)p1; const double *dp2 = (const double *)p2; if (*dp1 < *dp2) return COMPARE_LESSTHAN; if (*dp1 > *dp2) return COMPARE_GREATERTHAN; return COMPARE_EQUAL; } /* * refclock_process_offset - update median filter * * This routine uses the given offset and timestamps to construct a new * entry in the median filter circular buffer. Samples that overflow the * filter are quietly discarded. */ void refclock_process_offset( struct refclockproc *pp, /* refclock structure pointer */ l_fp lasttim, /* last timecode timestamp */ l_fp lastrec, /* last receive timestamp */ double fudge ) { l_fp lftemp; double doffset; pp->lastrec = lastrec; lftemp = lasttim; lftemp -= lastrec; doffset = lfptod(lftemp); SAMPLE(doffset + fudge); } /* * refclock_process - process a sample from the clock * refclock_process_f - refclock_process with other than time1 fudge * * This routine converts the timecode in the form days, hours, minutes, * seconds and milliseconds/microseconds to internal timestamp format, * then constructs a new entry in the median filter circular buffer. * Return success (1) if the data are correct and consistent with the * converntional calendar. * * Important for PPS users: Normally, the pp->lastrec is set to the * system time when the on-time character is received and the pp->year, * ..., pp->second decoded and the seconds fraction pp->nsec in * nanoseconds). When a PPS offset is available, pp->nsec is forced to * zero and the fraction for pp->lastrec is set to the PPS offset. */ bool refclock_process_f( struct refclockproc *pp, /* refclock structure pointer */ double fudge ) { l_fp offset = 0, ltemp = 0; uint32_t sec; /* * Compute the timecode timestamp from the days, hours, minutes, * seconds and milliseconds/microseconds of the timecode. Use * clocktime() for the aggregate seconds and the msec/usec for * the fraction, when present. Note that this code will fall back * to deducing the year from the receipt time of the sample if * it finds only a 2-digit year in the timecode. */ if (!clocktime(pp->year, pp->day, pp->hour, pp->minute, pp->second, time(NULL), lfpuint(pp->lastrec), &pp->yearstart, &sec)) return false; setlfpuint(offset, sec); setlfpfrac(offset, 0); ltemp = dtolfp(pp->nsec * S_PER_NS); offset += ltemp; refclock_process_offset(pp, offset, pp->lastrec, fudge); return true; } bool refclock_process( struct refclockproc *pp /* refclock structure pointer */ ) { return refclock_process_f(pp, pp->fudgetime1); } /* * refclock_sample - process a pile of samples from the clock * * This routine implements a recursive median filter to suppress spikes * in the data, as well as determine a performance statistic. It * calculates the mean offset and RMS jitter. A time adjustment * fudgetime1 can be added to the final offset to compensate for various * systematic errors. The routine returns the number of samples * processed, which could be zero. */ static int refclock_sample( struct refclockproc *pp /* refclock structure pointer */ ) { size_t i, j, k, m, n; double off[MAXSTAGE]; double offset; /* * Copy the raw offsets and sort into ascending order. Don't do * anything if the buffer is empty. */ n = 0; while (pp->codeproc != pp->coderecv) { pp->codeproc = (pp->codeproc + 1) % MAXSTAGE; off[n] = pp->filter[pp->codeproc]; n++; } if (n == 0) return (0); if (n > 1) qsort(off, n, sizeof(off[0]), refclock_cmpl_fp); /* * Reject the furthest from the median of the samples until * approximately 60 percent of the samples remain. */ i = 0; j = n; m = n - (n * 4) / 10; while ((j - i) > m) { offset = off[(j + i) / 2]; if (off[j - 1] - offset < offset - off[i]) i++; /* reject low end */ else j--; /* reject high end */ } /* * Determine the offset and jitter. */ pp->offset = 0; pp->jitter = 0; for (k = i; k < j; k++) { pp->offset += off[k]; if (k > i) pp->jitter += SQUARE(off[k] - off[k - 1]); } pp->offset /= m; pp->jitter = SQRT(pp->jitter / m); DPRINT(1, ("refclock_sample: n %d offset %.6f disp %.6f jitter %.6f\n", (int)n, pp->offset, pp->disp, pp->jitter)); return (int)n; } /* * refclock_receive - simulate the receive and packet procedures * * This routine simulates the NTP receive and packet procedures for a * reference clock. This provides a mechanism in which the ordinary NTP * filter, selection and combining algorithms can be used to suppress * misbehaving time sources and to mitigate between them when more than one is * available for backup. */ void refclock_receive( struct peer *peer /* peer structure pointer */ ) { struct refclockproc *pp; DPRINT(1, ("refclock_receive: at %u %s\n", current_time, socktoa(&peer->srcadr))); /* * Do a little sanity dance and update the peer structure. Groom * the median filter samples and give the data to the clock * filter. */ pp = peer->procptr; peer->leap = pp->leap; if (peer->leap == LEAP_NOTINSYNC) return; peer->received++; peer->timereceived = current_time; if (!peer->reach) { report_event(PEVNT_REACH, peer, NULL); peer->timereachable = current_time; } peer->reach |= 1; peer->reftime = pp->lastref; peer->org = pp->lastrec; peer->rootdisp = pp->disp; get_systime(&peer->dst); if (!refclock_sample(pp)) return; clock_filter(peer, pp->offset, 0., pp->jitter); if (cal_enable && fabs(last_offset) < sys_mindisp && sys_peer != NULL) { if (sys_peer->is_pps_driver && !peer->is_pps_driver) pp->fudgetime1 -= pp->offset * FUDGEFAC; } } /* * refclock_gtlin - groom next input line and extract timestamp * * This routine processes the timecode received from the clock and * strips the parity bit and control characters. It returns the number * of characters in the line followed by a NULL character ('\0'), which * is not included in the count. In case of an empty line, the previous * line is preserved. */ int refclock_gtlin( struct recvbuf *rbufp, /* receive buffer pointer */ char *lineptr, /* current line pointer */ int bmax, /* remaining characters in line */ l_fp *tsptr /* pointer to timestamp returned */ ) { const char *sp, *spend; char *dp, *dpend; int dlen; if (bmax <= 0) return (0); dp = lineptr; dpend = dp + bmax - 1; /* leave room for NUL pad */ sp = (const char *)rbufp->recv_buffer; spend = sp + rbufp->recv_length; while (sp != spend && dp != dpend) { char c; c = *sp++ & 0x7f; if (c >= 0x20 && c < 0x7f) *dp++ = c; } /* Get length of data written to the destination buffer. If * zero, do *not* place a NUL byte to preserve the previous * buffer content. */ dlen = dp - lineptr; if (dlen) *dp = '\0'; *tsptr = rbufp->recv_time; DPRINT(2, ("refclock_gtlin: fd %d time %s timecode %d %s\n", rbufp->fd, ulfptoa(rbufp->recv_time, 6), dlen, (dlen != 0) ? lineptr : "")); return (dlen); } /* * refclock_gtraw - get next line/chunk of data * * This routine returns the raw data received from the clock in both * canonical or raw modes. The terminal interface routines map CR to LF. * In canonical mode this results in two lines, one containing data * followed by LF and another containing only LF. In raw mode the * interface routines can deliver arbitraty chunks of data from one * character to a maximum specified by the calling routine. In either * mode the routine returns the number of characters in the line * followed by a NULL character ('\0'), which is not included in the * count. * * *tsptr receives a copy of the buffer timestamp. */ size_t refclock_gtraw( struct recvbuf *rbufp, /* receive buffer pointer */ char *lineptr, /* current line pointer */ size_t bmax, /* remaining characters in line */ l_fp *tsptr /* pointer to timestamp returned */ ) { if (bmax <= 0) return (0); bmax -= 1; /* leave room for trailing NUL */ if (bmax > rbufp->recv_length) bmax = rbufp->recv_length; memcpy(lineptr, rbufp->recv_buffer, bmax); lineptr[bmax] = '\0'; *tsptr = rbufp->recv_time; DPRINT(2, ("refclock_gtraw: fd %d time %s timecode %zu %s\n", rbufp->fd, ulfptoa(rbufp->recv_time, 6), bmax, lineptr)); return (bmax); } /* * indicate_refclock_packet() * * Passes a fragment of refclock input read from the device to the * driver direct input routine, which may consume it (batch it for * queuing once a logical unit is assembled). If it is not so * consumed, queue it for the driver's receive entrypoint. * * The return value is true if the data has been consumed as a fragment * and should not be counted as a received packet. */ bool indicate_refclock_packet( struct refclockio * rio, struct recvbuf * rb ) { /* Does this refclock use direct input routine? */ if (rio->io_input != NULL && (*rio->io_input)(rb) == 0) { /* * data was consumed - nothing to pass up * into block input machine */ freerecvbuf(rb); return true; } add_full_recv_buffer(rb); return false; } /* * refclock_open - open serial port for reference clock * * This routine opens a serial port for I/O and sets default options. It * returns the file descriptor if successful, or logs an error and * returns -1. */ int refclock_open( char *dev, /* device name pointer */ unsigned int speed, /* serial port speed (code) */ unsigned int lflags /* line discipline flags */ ) { int fd; char trash[128]; /* litter bin for old input data */ /* * Open serial port and set default options */ fd = open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY); /* refclock_open() long returned 0 on failure, avoid it. */ if (0 == fd) { fd = dup(0); close(0); } if (fd < 0) { msyslog(LOG_ERR, "REFCLOCK: refclock_open %s: %m", dev); return -1; } if (!refclock_setup(fd, speed, lflags)) { close(fd); return -1; } /* * We want to make sure there is no pending trash in the input * buffer. Since we have non-blocking IO available, this is a * good moment to read and dump all available outdated stuff * that might have become toxic for the driver. */ while (read(fd, trash, sizeof(trash)) > 0 || errno == EINTR) /*NOP*/; return fd; } /* * refclock_setup - initialize terminal interface structure */ static bool refclock_setup( int fd, /* file descriptor */ unsigned int speed, /* serial port speed (code) */ unsigned int lflags /* line discipline flags */ ) { int i; TTY ttyb, *ttyp; /* * By default, the serial line port is initialized in canonical * (line-oriented) mode at specified line speed, 8 bits and no * parity. LF ends the line and CR is mapped to LF. The break, * erase and kill functions are disabled. There is a different * section for each terminal interface, as selected at compile * time. The flag bits can be used to set raw mode and echo. */ ttyp = &ttyb; /* * POSIX serial line parameters (termios interface) */ if (tcgetattr(fd, ttyp) < 0) { msyslog(LOG_ERR, "REFCLOCK: refclock_setup fd %d tcgetattr: %m", fd); return false; } /* * Set canonical mode and local connection; set specified speed, * 8 bits and no parity; map CR to NL; ignore break. */ if (speed) { unsigned int ltemp = 0; ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL; ttyp->c_oflag = 0; ttyp->c_cflag = CS8 | CLOCAL | CREAD; if (lflags & LDISC_7O1) { /* HP Z3801A needs 7-bit, odd parity */ ttyp->c_cflag = CS7 | PARENB | PARODD | CLOCAL | CREAD; } cfsetispeed(&ttyb, speed); cfsetospeed(&ttyb, speed); for (i = 0; i < NCCS; ++i) ttyp->c_cc[i] = '\0'; #ifdef TIOCMGET /* * If we have modem control, check to see if modem leads * are active; if so, set remote connection. This is * necessary for the kernel pps mods to work. */ if (ioctl(fd, TIOCMGET, (char *)<emp) < 0) msyslog(LOG_ERR, "REFCLOCK: refclock_setup fd %d TIOCMGET: %m", fd); DPRINT(1, ("REFCLOCK: refclock_setup fd %d modem status: 0x%x\n", fd, ltemp)); if (ltemp & TIOCM_DSR && lflags & LDISC_REMOTE) ttyp->c_cflag &= (unsigned int)~CLOCAL; #endif /* TIOCMGET */ } /* * Set raw and echo modes. These can be changed on-fly. */ ttyp->c_lflag = ICANON; if (lflags & LDISC_RAW) { ttyp->c_lflag = 0; ttyp->c_iflag = 0; ttyp->c_cc[VMIN] = 1; } if (tcsetattr(fd, TCSANOW, ttyp) < 0) { msyslog(LOG_ERR, "REFCLOCK: refclock_setup fd %d TCSANOW: %m", fd); return false; } /* * flush input and output buffers to discard any outdated stuff * that might have become toxic for the driver. Failing to do so * is logged, but we keep our fingers crossed otherwise. */ if (tcflush(fd, TCIOFLUSH) < 0) msyslog(LOG_ERR, "REFCLOCK: refclock_setup fd %d tcflush(): %m", fd); return true; } /* * refclock_control - set and/or return clock values * * This routine is used mainly for debugging. It returns designated * values from the interface structure that can be displayed using * ntpq and the clockstat command. It can also be used to initialize * configuration variables, such as fudgetimes, fudgevalues, reference * ID and stratum. */ void refclock_control( sockaddr_u *srcadr, const struct refclockstat *in, struct refclockstat *out ) { struct peer *peer; struct refclockproc *pp; int unit; /* * Check for valid address and running peer */ peer = findexistingpeer(srcadr, NULL, NULL, -1); if (NULL == peer) return; if (!IS_PEER_REFCLOCK(peer)) return; pp = peer->procptr; unit = peer->procptr->refclkunit; /* * Initialize requested data */ if (in != NULL) { if (in->haveflags & CLK_HAVETIME1) pp->fudgetime1 = in->fudgetime1; if (in->haveflags & CLK_HAVETIME2) pp->fudgetime2 = in->fudgetime2; if (in->haveflags & CLK_HAVEVAL1) peer->stratum = pp->stratum = (uint8_t)in->fudgeval1; if (in->haveflags & CLK_HAVEVAL2) peer->refid = pp->refid = in->fudgeval2; if (in->haveflags & CLK_HAVEFLAG1) { pp->sloppyclockflag &= ~CLK_FLAG1; pp->sloppyclockflag |= in->flags & CLK_FLAG1; } if (in->haveflags & CLK_HAVEFLAG2) { pp->sloppyclockflag &= ~CLK_FLAG2; pp->sloppyclockflag |= in->flags & CLK_FLAG2; } if (in->haveflags & CLK_HAVEFLAG3) { pp->sloppyclockflag &= ~CLK_FLAG3; pp->sloppyclockflag |= in->flags & CLK_FLAG3; } if (in->haveflags & CLK_HAVEFLAG4) { pp->sloppyclockflag &= ~CLK_FLAG4; pp->sloppyclockflag |= in->flags & CLK_FLAG4; } } /* * Readback requested data */ if (out != NULL) { out->fudgeval1 = pp->stratum; out->fudgeval2 = pp->refid; out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2; out->fudgetime1 = pp->fudgetime1; if (!D_ISZERO_NS(out->fudgetime1)) out->haveflags |= CLK_HAVETIME1; out->fudgetime2 = pp->fudgetime2; if (!D_ISZERO_NS(out->fudgetime2)) out->haveflags |= CLK_HAVETIME2; out->flags = (uint8_t) pp->sloppyclockflag; if (CLK_FLAG1 & out->flags) out->haveflags |= CLK_HAVEFLAG1; if (CLK_FLAG2 & out->flags) out->haveflags |= CLK_HAVEFLAG2; if (CLK_FLAG3 & out->flags) out->haveflags |= CLK_HAVEFLAG3; if (CLK_FLAG4 & out->flags) out->haveflags |= CLK_HAVEFLAG4; out->timereset = current_time - pp->timestarted; out->polls = pp->polls; out->noresponse = pp->noreply; out->badformat = pp->badformat; out->baddata = pp->baddata; out->lastevent = pp->lastevent; out->currentstatus = pp->currentstatus; out->clockname = pp->clockname; out->clockdesc = pp->clockdesc; out->lencode = (unsigned short)pp->lencode; out->p_lastcode = pp->a_lastcode; } /* * Give the stuff to the clock */ if (peer->procptr->conf->clock_control) (peer->procptr->conf->clock_control)(unit, in, out, peer); } #ifdef HAVE_PPSAPI /* * refclock_ppsapi - initialize/update ppsapi * * This routine is called after the refclock command to open the PPSAPI * interface for later parameter setting after the refclock command. */ bool refclock_ppsapi( int fddev, /* fd device */ struct refclock_ppsctl *ap /* PPS context structure pointer */ ) { if (ap->handle == 0) { if (time_pps_create(fddev, &ap->handle) < 0) { msyslog(LOG_ERR, "REFCLOCK: refclock_ppsapi: time_pps_create: %m"); return false; } } return true; } /* * refclock_params - set ppsapi parameters * * This routine is called to set the PPSAPI parameters after the fudge * command. */ bool refclock_params( int mode, /* mode bits */ struct refclock_ppsctl *ap /* PPS context structure pointer */ ) { ZERO(ap->pps_params); ap->pps_params.api_version = PPS_API_VERS_1; /* * If flag2 is lit, capture on clear edge if we can. Not all * PPSAPI implementations let you choose; if in doubt, check * the documentation of your serial driver. */ if (mode & CLK_FLAG2) ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTURECLEAR; else ap->pps_params.mode = PPS_TSFMT_TSPEC | PPS_CAPTUREASSERT; if (time_pps_setparams(ap->handle, &ap->pps_params) < 0) { msyslog(LOG_ERR, "REFCLOCK: refclock_params: time_pps_setparams: %m"); return false; } /* * If flag3 is lit, select the kernel PPS if we can. */ if (mode & CLK_FLAG3) { if (time_pps_kcbind(ap->handle, PPS_KC_HARDPPS, ap->pps_params.mode & ~PPS_TSFMT_TSPEC, PPS_TSFMT_TSPEC) < 0) { if (errno == EOPNOTSUPP) msyslog(LOG_ERR, "REFCLOCK: refclock_params: kernel PLL (hardpps, RFC 1589) not implemented"); else msyslog(LOG_ERR, "REFCLOCK: refclock_params: time_pps_kcbind: %m"); return false; } hardpps_enable = true; } return true; } /* * refclock_catcher - called once per second * * This routine is called once per second. It snatches the PPS * timestamp from the kernel and saves the sign-extended fraction in * a circular buffer for processing at the next poll event. */ pps_status refclock_catcher( struct peer *peer, /* peer structure pointer */ struct refclock_ppsctl *ap, /* PPS context structure pointer */ int mode /* mode bits */ ) { struct refclockproc *pp; pps_info_t pps_info; struct timespec timeout; double dtemp; UNUSED_ARG(mode); /* * We require the clock to be synchronized before setting the * parameters. When the parameters have been set, fetch the * most recent PPS timestamp. */ pp = peer->procptr; if (ap->handle == 0) return PPS_SETUP; if (ap->pps_params.mode == 0 && sys_leap != LEAP_NOTINSYNC) { if (refclock_params(pp->sloppyclockflag, ap) < 1) return PPS_SETUP; } timeout.tv_sec = 0; timeout.tv_nsec = 0; ZERO(pps_info); if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC, &pps_info, &timeout) < 0) { refclock_report(peer, CEVNT_FAULT); return PPS_KERNEL; } timeout = ap->ts; if (ap->pps_params.mode & PPS_CAPTUREASSERT) { ap->ts = pps_info.assert_timestamp; ap->sequence = pps_info.assert_sequence; } else if (ap->pps_params.mode & PPS_CAPTURECLEAR) { ap->ts = pps_info.clear_timestamp; ap->sequence = pps_info.clear_sequence; } else return PPS_NREADY; /* Check for duplicates. * Sequence number might not be implemented. * saved (above) for debugging. */ if (0 == memcmp(&timeout, &ap->ts, sizeof(timeout))) return PPS_NREADY; /* * Convert to signed fraction offset and stuff in median filter. */ setlfpuint(pp->lastrec, (uint32_t)ap->ts.tv_sec + JAN_1970); dtemp = ap->ts.tv_nsec * S_PER_NS; setlfpfrac(pp->lastrec, (uint32_t)(dtemp * FRAC)); if (dtemp > .5) dtemp -= 1.; SAMPLE(-dtemp + pp->fudgetime1); DPRINT(2, ("refclock_pps: %u %f %f\n", current_time, dtemp, pp->fudgetime1)); return PPS_OK; } #endif /* HAVE_PPSAPI */ #endif /* REFCLOCK */ ntpsec-1.1.0+dfsg1/ntpd/refclock_oncore.c0000644000175000017500000036100213252364117020075 0ustar rlaagerrlaager/* * SPDX-License-Identifier: Beerware * * refclock_oncore.c * * Driver for some of the various the Motorola Oncore GPS receivers. * should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T * The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate * than the others. * The receivers without position hold (GT, GT+) will be less accurate. * * Tested with: * * (UT) (VP) * COPYRIGHT 1991-1997 MOTOROLA INC. COPYRIGHT 1991-1996 MOTOROLA INC. * SFTW P/N # 98-P36848P SFTW P/N # 98-P36830P * SOFTWARE VER # 2 SOFTWARE VER # 8 * SOFTWARE REV # 2 SOFTWARE REV # 8 * SOFTWARE DATE APR 24 1998 SOFTWARE DATE 06 Aug 1996 * MODEL # R1121N1114 MODEL # B4121P1155 * HWDR P/N # 1 HDWR P/N # _ * SERIAL # R0010A SERIAL # SSG0226478 * MANUFACTUR DATE 6H07 MANUFACTUR DATE 7E02 * OPTIONS LIST IB * * (Basic) (M12) * COPYRIGHT 1991-1994 MOTOROLA INC. COPYRIGHT 1991-2000 MOTOROLA INC. * SFTW P/N # 98-P39949M SFTW P/N # 61-G10002A * SOFTWARE VER # 5 SOFTWARE VER # 1 * SOFTWARE REV # 0 SOFTWARE REV # 3 * SOFTWARE DATE 20 JAN 1994 SOFTWARE DATE Mar 13 2000 * MODEL # A11121P116 MODEL # P143T12NR1 * HDWR P/N # _ HWDR P/N # 1 * SERIAL # SSG0049809 SERIAL # P003UD * MANUFACTUR DATE 417AMA199 MANUFACTUR DATE 0C27 * OPTIONS LIST AB * * (M12+T) (M12+T later version) * COPYRIGHT 1991-2002 MOTOROLA INC. COPYRIGHT 1991-2003 MOTOROLA INC. * SFTW P/N # 61-G10268A SFTW P/N # 61-G10268A * SOFTWARE VER # 2 SOFTWARE VER # 2 * SOFTWARE REV # 0 SOFTWARE REV # 1 * SOFTWARE DATE AUG 14 2002 SOFTWARE DATE APR 16 2003 * MODEL # P283T12T11 MODEL # P273T12T12 * HWDR P/N # 2 HWDR P/N # 2 * SERIAL # P04DC2 SERIAL # P05Z7Z * MANUFACTUR DATE 2J17 MANUFACTUR DATE 3G15 * * -------------------------------------------------------------------------- * Reg Clemens (June 2009) * BUG[1220] OK, big patch, but mostly done mechanically. Change direct calls to write * to clockstats to a call to oncore_log, which now calls the old routine plus msyslog. * Have to set the LOG_LEVELS of the calls for msyslog, and this was done by hand. New * routine oncore_log. * -------------------------------------------------------------------------- * Reg Clemens (June 2009) * BUG[1218] The comment on where the oncore driver gets its input file does not * agree with the code. Change the comment. * -------------------------------------------------------------------------- * Reg Clemens (June 2009) * change exit statements to return(0) in main program. I had assumed that if the * PPS driver did not start for some reason, we shuould stop NTPD itelf. Others * disagree. We now give an ERR log message and stop this driver. * -------------------------------------------------------------------------- * Reg Clemens (June 2009) * A bytes available message for the input subsystem (Debug message). * -------------------------------------------------------------------------- * Reg Clemens (Nov 2008) * This code adds a message for TRAIM messages. Users often worry about the * driver not starting up, and it is often because of signal strength being low. * Low signal strength will give TRAIM messages. * -------------------------------------------------------------------------- * Reg Clemens (Nov 2008) * Add waiting on Almanac Message. * -------------------------------------------------------------------------- * Reg Clemens (Nov 2008) * Add back in @@Bl code to do the @@Bj/@@Gj that is in later ONCOREs * LEAP SECONDS: All of the ONCORE receivers, VP -> M12T have the @@Bj command * that says 'Leap Pending'. As documented it only becomes true in the month * before the leap second is to be applied, but in practice at least some of * the receivers turn this indicator on as soon as the message is posted, which * can be 6months early. As such, we use the Bj command to turn on the * instance->pp->leap indicator but only run this test in December and June for * updates on 1Jan and 1July. * * The @@Gj command exists in later ONCOREs, and it gives the exact date * and size of the Leap Update. It can be emulated in the VP using the @@Bl * command which reads the raw Satellite Broadcast Messages. * We use these two commands to print informative messages in the clockstats * file once per day as soon as the message appears on the satellites. * -------------------------------------------------------------------------- * Reg Clemens (Feb 2006) * Fix some gcc4 compiler complaints * Fix possible segfault in oncore_init_shmem * change all (possible) fprintf(stderr, to record_clock_stats * Apply patch from Russell J. Yount Fixed (new) MT12+T UTC not correct * immediately after new Almanac Read. * Apply patch for new PPS implementation by Rodolfo Giometti * now code can use old Ulrich Windl or * the new one. Compiles depending on timepps.h seen. * -------------------------------------------------------------------------- * Luis Batanero Guerrero (Dec 2005) Patch for leap seconds * (the oncore driver was setting the wrong ntpd variable) * -------------------------------------------------------------------------- * Reg.Clemens (Mar 2004) * Support for interfaces other than PPSAPI removed, for Solaris, SunOS, * SCO, you now need to use one of the timepps.h files in the root dir. * this driver will 'grab' it for you if you don't have one in /usr/include * -------------------------------------------------------------------------- * This code uses the two devices * /dev/oncore.serial.n * /dev/oncore.pps.n * which may be linked to the same device. * and can read initialization data from the file * /etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where * n or N are the unit number * -------------------------------------------------------------------------- * Reg.Clemens Sep98. * Original code written for FreeBSD. * With these mods it works on FreeBSD, SunOS, Solaris and Linux * (SunOS 4.1.3 + ppsclock) * (Solaris7 + MU4) * (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later). * * Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the * state machine state) are printed to CLOCKSTATS if that file is enabled * in /etc/ntp.conf. * * -------------------------------------------------------------------------- * * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13) * doing an average of 10000 valid 2D and 3D fixes is what the automatic * site survey mode does. Looking at the output from the receiver * it seems like it is only using 3D fixes. * When we do it ourselves, take 10000 3D fixes. */ #define POS_HOLD_AVERAGE 10000 /* nb, 10000s ~= 2h45m */ /* * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a * "STATUS" line in the oncore config file, which contains the most recent * copy of all types of messages we recognize. This file can be mmap(2)'ed * by monitoring and statistics programs. * * See separate HTML documentation for this option. */ #include "config.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include "timespecops.h" #include #include #include #include #ifdef ENABLE_ONCORE_SHMEM # include # ifndef MAP_FAILED # define MAP_FAILED ((uint8_t *) -1) # endif /* MAP_FAILED */ #endif /* ENABLE_ONCORE_SHMEM */ #ifdef HAVE_PPSAPI # include "ppsapi_timepps.h" #endif #define NAME "ONCORE" #define DESCRIPTION "Motorola Oncore GPS Receiver" struct Bl { int dt_ls; int dt_lsf; int WN; int DN; int WN_lsf; int DN_lsf; int wn_flg; int lsf_flg; int Bl_day; } Bl; enum receive_state { ONCORE_NO_IDEA, ONCORE_CHECK_ID, ONCORE_CHECK_CHAN, ONCORE_HAVE_CHAN, ONCORE_RESET_SENT, ONCORE_TEST_SENT, ONCORE_INIT, ONCORE_ALMANAC, ONCORE_RUN }; enum site_survey_state { ONCORE_SS_UNKNOWN, ONCORE_SS_TESTING, ONCORE_SS_HW, ONCORE_SS_SW, ONCORE_SS_DONE }; enum antenna_state { ONCORE_ANTENNA_UNKNOWN = -1, ONCORE_ANTENNA_OK = 0, ONCORE_ANTENNA_OC = 1, ONCORE_ANTENNA_UC = 2, ONCORE_ANTENNA_NV = 3 }; /* Model Name, derived from the @@Cj message. * Used to initialize some variables. */ enum oncore_model { ONCORE_BASIC, ONCORE_PVT6, ONCORE_VP, ONCORE_UT, ONCORE_UTPLUS, ONCORE_GT, ONCORE_GTPLUS, ONCORE_SL, ONCORE_M12, ONCORE_UNKNOWN }; /* the bits that describe these properties are in the same place * on the VP/UT, but have moved on the M12. As such we extract * them, and use them from this struct. * */ struct RSM { uint8_t posn0D; uint8_t posn2D; uint8_t posn3D; uint8_t bad_almanac; uint8_t bad_fix; }; /* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to * see what mode it is in. The bits on the M12 are multiplexed with * other messages, so we have to 'keep' the last known mode here. */ enum posn_mode { MODE_UNKNOWN, MODE_0D, MODE_2D, MODE_3D }; struct instance { int unit; struct refclockproc *pp; struct peer *peer; int ttyfd; /* TTY file descriptor */ int ppsfd; /* PPS file descriptor */ int shmemfd; /* Status shm descriptor */ pps_handle_t pps_h; pps_params_t pps_p; enum receive_state o_state; /* Receive state */ enum posn_mode mode; /* 0D, 2D, 3D */ enum site_survey_state site_survey; /* Site Survey state */ enum antenna_state ant_state; /* antenna state */ int Bj_day; unsigned long delay; /* ns */ long offset; /* ns */ uint8_t *shmem; char *shmem_fname; unsigned int shmem_Cb; unsigned int shmem_Ba; unsigned int shmem_Ea; unsigned int shmem_Ha; uint8_t shmem_reset; uint8_t shmem_Posn; uint8_t shmem_bad_Ea; uint8_t almanac_from_shmem; double ss_lat; double ss_long; double ss_ht; double dH; int ss_count; uint8_t posn_set; enum oncore_model model; unsigned int version; unsigned int revision; uint8_t chan; /* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */ int8_t traim; /* do we have traim? yes UT/VP, M12+T, no BASIC, GT, M12, -1 unknown, 0 no, +1 yes */ /* the following 7 are all timing counters */ uint8_t traim_delay; /* seconds counter, waiting for reply */ uint8_t count; /* cycles through Ea before starting */ uint8_t count1; /* cycles through Ea after SS_TESTING, waiting for SS_HW */ uint8_t count2; /* cycles through Ea after count, to check for @@Ea */ uint8_t count3; /* cycles through Ea checking for # channels */ uint8_t count4; /* cycles through leap after Gj to issue Bj */ uint8_t count5; /* cycles through get_timestamp waiting for valid UTC correction */ uint8_t count5_set; /* only set count5 once */ uint8_t counta; /* count for waiting on almanac message */ uint8_t pollcnt; uint8_t timeout; /* count to retry Cj after Fa self-test */ uint8_t max_len; /* max length message seen by oncore_log, for debugging */ uint8_t max_count; /* count for message statistics */ struct RSM rsm; /* bits extracted from Receiver Status Msg in @@Ea */ struct Bl Bl; /* Satellite Broadcast Data Message */ uint8_t printed; uint8_t polled; unsigned long ev_serial; unsigned Rcvptr; uint8_t Rcvbuf[500]; uint8_t BEHa[160]; /* Ba, Ea or Ha */ uint8_t BEHn[80]; /* Bn , En , or Hn */ uint8_t Cj[300]; uint8_t Ag; /* Satellite mask angle */ uint8_t saw_At; uint8_t saw_Ay; uint8_t saw_Az; int8_t saw_Bj; int8_t saw_Gj; uint8_t have_dH; uint8_t init_type; int8_t saw_tooth; int8_t chan_in; /* chan number from INPUT, will always use it */ uint8_t chan_id; /* chan number determined from part number */ uint8_t chan_ck; /* chan number determined by sending commands to hardware */ int8_t traim_in; /* TRAIM from INPUT, will always use ON/OFF specified */ int8_t traim_id; /* TRAIM determined from part number */ uint8_t traim_ck; /* TRAIM determined by sending commands to hardware */ uint8_t once; /* one pass code at top of BaEaHa */ int8_t assert; uint8_t hardpps; int8_t pps_control; /* PPS control, M12 only */ int8_t pps_control_msg_seen; }; #define rcvbuf instance->Rcvbuf #define rcvptr instance->Rcvptr static bool oncore_start (int, struct peer *); static void oncore_poll (int, struct peer *); static void oncore_shutdown (struct refclockproc *); static void oncore_consume (struct instance *); static void oncore_read_config (struct instance *); static void oncore_receive (struct recvbuf *); static bool oncore_ppsapi (struct instance *); static void oncore_get_timestamp (struct instance *, long, long); #ifdef ENABLE_ONCORE_SHMEM static void oncore_init_shmem (struct instance *); #endif static void oncore_antenna_report (struct instance *, enum antenna_state); static void oncore_chan_test (struct instance *); static void oncore_check_almanac (struct instance *); static void oncore_check_antenna (struct instance *); static void oncore_check_leap_sec (struct instance *); static int oncore_checksum_ok (uint8_t *, int); static void oncore_compute_dH (struct instance *); static void oncore_load_almanac (struct instance *); static void oncore_log (struct instance *, int, const char *); static int oncore_log_f (struct instance *, int, const char *, ...) NTP_PRINTF(3, 4); static void oncore_print_Cb (struct instance *, uint8_t *); /* static void oncore_print_array (uint8_t *, int); */ static void oncore_print_posn (struct instance *); static void oncore_sendmsg (struct instance *, uint8_t *, size_t); static void oncore_set_posn (struct instance *); static void oncore_set_traim (struct instance *); static void oncore_shmem_get_3D (struct instance *); static void oncore_ss (struct instance *); static bool oncore_wait_almanac (struct instance *); static void oncore_msg_any (struct instance *, uint8_t *, size_t, int); static void oncore_msg_Adef (struct instance *, uint8_t *, size_t); static void oncore_msg_Ag (struct instance *, uint8_t *, size_t); static void oncore_msg_As (struct instance *, uint8_t *, size_t); static void oncore_msg_At (struct instance *, uint8_t *, size_t); static void oncore_msg_Ay (struct instance *, uint8_t *, size_t); static void oncore_msg_Az (struct instance *, uint8_t *, size_t); static void oncore_msg_BaEaHa (struct instance *, uint8_t *, size_t); static void oncore_msg_Bd (struct instance *, uint8_t *, size_t); static void oncore_msg_Bj (struct instance *, uint8_t *, size_t); static void oncore_msg_Bl (struct instance *, uint8_t *, size_t); static void oncore_msg_BnEnHn (struct instance *, uint8_t *, size_t); static void oncore_msg_CaFaIa (struct instance *, uint8_t *, size_t); static void oncore_msg_Cb (struct instance *, uint8_t *, size_t); static void oncore_msg_Cf (struct instance *, uint8_t *, size_t); static void oncore_msg_Cj (struct instance *, uint8_t *, size_t); static void oncore_msg_Cj_id (struct instance *, uint8_t *, size_t); static void oncore_msg_Cj_init (struct instance *, uint8_t *, size_t); static void oncore_msg_Ga (struct instance *, uint8_t *, size_t); static void oncore_msg_Gb (struct instance *, uint8_t *, size_t); static void oncore_msg_Gc (struct instance *, uint8_t *, size_t); static void oncore_msg_Gj (struct instance *, uint8_t *, size_t); static void oncore_msg_Sz (struct instance *, uint8_t *, size_t); struct refclock refclock_oncore = { NAME, /* basename of driver */ oncore_start, /* start up driver */ oncore_shutdown, /* shut down driver */ oncore_poll, /* transmit poll message */ NULL, /* control - not used */ NULL, /* init - not used */ NULL /* timer - not used */ }; /* * Understanding the next bit here is not easy unless you have a manual * for the various Oncore Models. */ static struct msg_desc { const char flag[3]; const int len; void (*handler) (struct instance *, uint8_t *, size_t); const char *fmt; int shmem; } oncore_messages[] = { /* Ea and En first since they're most common */ { "Ea", 76, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC", 0 }, { "Ba", 68, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC", 0 }, { "Ha", 154, oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC", 0 }, { "Bn", 59, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC", 0 }, { "En", 69, oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC", 0 }, { "Hn", 78, oncore_msg_BnEnHn, "", 0 }, { "Ab", 10, 0, "", 0 }, { "Ac", 11, 0, "", 0 }, { "Ad", 11, oncore_msg_Adef, "", 0 }, { "Ae", 11, oncore_msg_Adef, "", 0 }, { "Af", 15, oncore_msg_Adef, "", 0 }, { "Ag", 8, oncore_msg_Ag, "", 0 }, /* Satellite mask angle */ { "As", 20, oncore_msg_As, "", 0 }, { "At", 8, oncore_msg_At, "", 0 }, { "Au", 12, 0, "", 0 }, { "Av", 8, 0, "", 0 }, { "Aw", 8, 0, "", 0 }, { "Ay", 11, oncore_msg_Ay, "", 0 }, { "Az", 11, oncore_msg_Az, "", 0 }, { "AB", 8, 0, "", 0 }, { "Bb", 92, 0, "", 0 }, { "Bd", 23, oncore_msg_Bd, "", 0 }, { "Bj", 8, oncore_msg_Bj, "", 0 }, { "Bl", 41, oncore_msg_Bl, "", 0 }, { "Ca", 9, oncore_msg_CaFaIa, "", 0 }, { "Cb", 33, oncore_msg_Cb, "", 0 }, { "Cf", 7, oncore_msg_Cf, "", 0 }, { "Cg", 8, 0, "", 0 }, { "Ch", 9, 0, "", 0 }, { "Cj", 294, oncore_msg_Cj, "", 0 }, { "Ek", 71, 0, "", 0 }, { "Fa", 9, oncore_msg_CaFaIa, "", 0 }, { "Ga", 20, oncore_msg_Ga, "", 0 }, { "Gb", 17, oncore_msg_Gb, "", 0 }, { "Gc", 8, oncore_msg_Gc, "", 0 }, { "Gd", 8, 0, "", 0 }, { "Ge", 8, 0, "", 0 }, { "Gj", 21, oncore_msg_Gj, "", 0 }, { "Ia", 10, oncore_msg_CaFaIa, "", 0 }, { "Sz", 8, oncore_msg_Sz, "", 0 }, { {0}, 7, 0, "", 0 } }; static uint8_t oncore_cmd_Aa[] = { 'A', 'a', 0, 0, 0 }; /* 6/8 Time of Day */ static uint8_t oncore_cmd_Ab[] = { 'A', 'b', 0, 0, 0 }; /* 6/8 GMT Correction */ static uint8_t oncore_cmd_AB[] = { 'A', 'B', 4 }; /* VP Application Type: Static */ static uint8_t oncore_cmd_Ac[] = { 'A', 'c', 0, 0, 0, 0 }; /* 6/8 Date */ static uint8_t oncore_cmd_Ad[] = { 'A', 'd', 0,0,0,0 }; /* 6/8 Latitude */ static uint8_t oncore_cmd_Ae[] = { 'A', 'e', 0,0,0,0 }; /* 6/8 Longitude */ static uint8_t oncore_cmd_Af[] = { 'A', 'f', 0,0,0,0, 0 }; /* 6/8 Height */ static uint8_t oncore_cmd_Ag[] = { 'A', 'g', 0 }; /* 6/8/12 Satellite Mask Angle */ static uint8_t oncore_cmd_Agx[] = { 'A', 'g', 0xff }; /* 6/8/12 Satellite Mask Angle: read */ static uint8_t oncore_cmd_As[] = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 6/8/12 Posn Hold Parameters */ static uint8_t oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff, /* 6/8/12 Posn Hold Readback */ 0x7f,0xff,0xff,0xff, /* on UT+ this doesn't work with 0xff */ 0x7f,0xff,0xff,0xff, 0xff }; /* but does work with 0x7f (sigh). */ static uint8_t oncore_cmd_At0[] = { 'A', 't', 0 }; /* 6/8 Posn Hold: off */ static uint8_t oncore_cmd_At1[] = { 'A', 't', 1 }; /* 6/8 Posn Hold: on */ static uint8_t oncore_cmd_At2[] = { 'A', 't', 2 }; /* 6/8 Posn Hold: Start Site Survey */ static uint8_t oncore_cmd_Atx[] = { 'A', 't', 0xff }; /* 6/8 Posn Hold: Read Back */ static uint8_t oncore_cmd_Au[] = { 'A', 'u', 0,0,0,0, 0 }; /* GT/M12 Altitude Hold Ht. */ static uint8_t oncore_cmd_Av0[] = { 'A', 'v', 0 }; /* VP/GT Altitude Hold: off */ static uint8_t oncore_cmd_Av1[] = { 'A', 'v', 1 }; /* VP/GT Altitude Hold: on */ static uint8_t oncore_cmd_Aw[] = { 'A', 'w', 1 }; /* 6/8/12 UTC/GPS time selection */ static uint8_t oncore_cmd_Ay[] = { 'A', 'y', 0, 0, 0, 0 }; /* Timing 1PPS time offset: set */ static uint8_t oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff }; /* Timing 1PPS time offset: Read */ static uint8_t oncore_cmd_Az[] = { 'A', 'z', 0, 0, 0, 0 }; /* 6/8UT/12 1PPS Cable Delay: set */ static uint8_t oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff }; /* 6/8UT/12 1PPS Cable Delay: Read */ static uint8_t oncore_cmd_Ba0[] = { 'B', 'a', 0 }; /* 6 Position/Data/Status: off */ static uint8_t oncore_cmd_Ba[] = { 'B', 'a', 1 }; /* 6 Position/Data/Status: on */ static uint8_t oncore_cmd_Bb[] = { 'B', 'b', 1 }; /* 6/8/12 Visible Satellites */ static uint8_t oncore_cmd_Bd[] = { 'B', 'd', 1 }; /* 6/8/12? Almanac Status Msg. */ static uint8_t oncore_cmd_Be[] = { 'B', 'e', 1 }; /* 6/8/12 Request Almanac Data */ static uint8_t oncore_cmd_Bj[] = { 'B', 'j', 0 }; /* 6/8 Leap Second Pending */ static uint8_t oncore_cmd_Bl[] = { 'B', 'l', 1 }; /* VP Satellite Broadcast Data Msg */ static uint8_t oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim on */ static uint8_t oncore_cmd_Bn[] = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg on, traim on */ static uint8_t oncore_cmd_Bnx[] = { 'B', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6 TRAIM setup/status: msg off, traim off */ static uint8_t oncore_cmd_Ca[] = { 'C', 'a' }; /* 6 Self Test */ static uint8_t oncore_cmd_Cf[] = { 'C', 'f' }; /* 6/8/12 Set to Defaults */ static uint8_t oncore_cmd_Cg[] = { 'C', 'g', 1 }; /* VP Posn Fix/Idle Mode */ static uint8_t oncore_cmd_Cj[] = { 'C', 'j' }; /* 6/8/12 Receiver ID */ static uint8_t oncore_cmd_Ea0[] = { 'E', 'a', 0 }; /* 8 Position/Data/Status: off */ static uint8_t oncore_cmd_Ea[] = { 'E', 'a', 1 }; /* 8 Position/Data/Status: on */ static uint8_t oncore_cmd_Ek[] = { 'E', 'k', 0 }; /* just turn off */ /* 8 Posn/Status/Data - extension */ static uint8_t oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim on */ static uint8_t oncore_cmd_En[] = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg on, traim on */ static uint8_t oncore_cmd_Enx[] = { 'E', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT TRAIM setup/status: msg off, traim off */ static uint8_t oncore_cmd_Fa[] = { 'F', 'a' }; /* 8 Self Test */ static uint8_t oncore_cmd_Ga[] = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 }; /* 12 Position Set */ static uint8_t oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff, /* 12 Position Set: Read */ 0xff, 0xff, 0xff, 0xff, /* */ 0xff, 0xff, 0xff, 0xff, 0xff }; /* */ static uint8_t oncore_cmd_Gb[] = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* 12 set Date/Time */ static uint8_t oncore_cmd_Gc[] = { 'G', 'c', 0 }; /* 12 PPS Control: Off, On, 1+satellite,TRAIM */ static uint8_t oncore_cmd_Gd0[] = { 'G', 'd', 0 }; /* 12 Position Control: 3D (no hold) */ static uint8_t oncore_cmd_Gd1[] = { 'G', 'd', 1 }; /* 12 Position Control: 0D (3D hold) */ static uint8_t oncore_cmd_Gd2[] = { 'G', 'd', 2 }; /* 12 Position Control: 2D (Alt Hold) */ static uint8_t oncore_cmd_Gd3[] = { 'G', 'd', 3 }; /* 12 Position Coltrol: Start Site Survey */ static uint8_t oncore_cmd_Ge0[] = { 'G', 'e', 0 }; /* M12+T TRAIM: off */ static uint8_t oncore_cmd_Ge[] = { 'G', 'e', 1 }; /* M12+T TRAIM: on */ static uint8_t oncore_cmd_Gj[] = { 'G', 'j' }; /* 8?/12 Leap Second Pending */ static uint8_t oncore_cmd_Ha0[] = { 'H', 'a', 0 }; /* 12 Position/Data/Status: off */ static uint8_t oncore_cmd_Ha[] = { 'H', 'a', 1 }; /* 12 Position/Data/Status: on */ static uint8_t oncore_cmd_Hn0[] = { 'H', 'n', 0 }; /* 12 TRAIM Status: off */ static uint8_t oncore_cmd_Hn[] = { 'H', 'n', 1 }; /* 12 TRAIM Status: on */ static uint8_t oncore_cmd_Ia[] = { 'I', 'a' }; /* 12 Self Test */ /* it appears that as of 1997/1998, the UT had As,At, but not Au,Av * the GT had Au,Av, but not As,At * This was as of v2.0 of both firmware sets. possibly 1.3 for UT. * Bj in UT at v1.3 * don't see Bd in UT/GT through 1999 * Gj in UT as of 3.0, 1999 , Bj as of 1.3 */ #define DEVICE1 "/dev/oncore.serial.%d" /* name of serial device */ #define DEVICE2 "/dev/oncore.pps.%d" /* name of pps device */ #define SPEED B9600 /* Oncore Binary speed (9600 bps) */ /* * Assemble and disassemble 32bit signed quantities from a buffer. * */ /* to buffer, int w, uint8_t *buf */ #define w32_buf(buf,w) { unsigned int i_tmp; \ i_tmp = (unsigned int)((w<0) ? (~(-w)+1) : (w)); \ (buf)[0] = (i_tmp >> 24) & 0xff; \ (buf)[1] = (i_tmp >> 16) & 0xff; \ (buf)[2] = (i_tmp >> 8) & 0xff; \ (buf)[3] = (i_tmp ) & 0xff; \ } #define w32(buf) (((buf)[0]&0xff) << 24 | \ ((buf)[1]&0xff) << 16 | \ ((buf)[2]&0xff) << 8 | \ ((buf)[3]&0xff) ) /* from buffer, char *buf, result to an int */ #define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf)) /* * oncore_start - initialize data for processing */ static bool oncore_start( int unit, struct peer *peer ) { #define STRING_LEN 32 struct instance *instance; struct refclockproc *pp; int fd1, fd2; char device1[STRING_LEN], device2[STRING_LEN]; struct stat stat1, stat2; /* create instance structure for this unit */ instance = emalloc_zero(sizeof(*instance)); /* initialize miscellaneous variables */ pp = peer->procptr; instance->pp = pp; instance->unit = unit; instance->peer = peer; instance->assert = 1; instance->once = 1; instance->Bj_day = -1; instance->traim = -1; instance->traim_in = -1; instance->chan_in = -1; instance->pps_control = -1; /* PPS control, M12 only */ instance->pps_control_msg_seen = -1; /* Have seen response to Gc msg */ instance->model = ONCORE_UNKNOWN; instance->mode = MODE_UNKNOWN; instance->site_survey = ONCORE_SS_UNKNOWN; instance->Ag = 0xff; /* Satellite mask angle, unset by user */ instance->ant_state = ONCORE_ANTENNA_UNKNOWN; peer->cfg.flags &= ~FLAG_PPS; /* PPS not active yet */ peer->precision = -26; peer->cfg.minpoll = 4; peer->cfg.maxpoll = 4; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, "GPS\0", (size_t) 4); oncore_log(instance, LOG_NOTICE, "ONCORE DRIVER -- CONFIGURING"); instance->o_state = ONCORE_NO_IDEA; oncore_log(instance, LOG_NOTICE, "state = ONCORE_NO_IDEA"); /* Now open files. * This is a bit complicated, and we don't want to open the same file twice * (it's a problem on some OS), and device2 may not exist for the new PPS */ (void)snprintf(device1, sizeof(device1), DEVICE1, unit); (void)snprintf(device2, sizeof(device2), DEVICE2, unit); /* OPEN DEVICES */ /* opening different devices for fd1 and fd2 presents no problems */ /* opening the SAME device twice, seems to be OS dependent. (a) on Linux (no streams) no problem (b) on SunOS (and possibly Solaris, untested), (streams) never see the line discipline. Since things ALWAYS work if we only open the device once, we check to see if the two devices are in fact the same, then proceed to do one open or two. For use with linuxPPS we assume that the N_TTY file has been opened and that the line discipline has been changed to N_PPS by another program (say ppsldisc) so that the two files expected by the oncore driver can be opened. Note that the linuxPPS N_PPS file is just like a N_TTY, so we can do the stat below without error even though the file has already had its line discipline changed by another process. */ if (stat(device1, &stat1)) { oncore_log_f(instance, LOG_ERR, "Can't stat fd1 (%s)", device1); free(instance); return false; /* exit, no file, can't start driver */ } fd1 = refclock_open(device1, SPEED, LDISC_RAW); if (fd1 <= 0) { oncore_log_f(instance, LOG_ERR, "Can't open fd1 (%s)", device1); free(instance); return false; /* exit, can't open file, can't start driver */ } /* coverity[toctou] */ if (stat(device2, &stat2)) { stat2.st_dev = stat2.st_ino = (ino_t)-2; oncore_log_f(instance, LOG_ERR, "Can't stat fd2 (%s) %d %m", device2, errno); } /* for LINUX the PPS device is the result of a line discipline. It seems simplest to let an external program create the appropriate /dev/pps file, and only check (carefully) for its existence here */ if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) /* same device here */ fd2 = fd1; else { /* different devices here */ /* coverity[toctou] */ if ((fd2=open(device2, O_RDWR, 0777)) < 0) { oncore_log_f(instance, LOG_ERR, "Can't open fd2 (%s)", device2); close(fd1); free(instance); return false; /* exit, can't open PPS file, can't start driver */ } } /* open ppsapi source */ if (time_pps_create(fd2, &instance->pps_h) < 0) { oncore_log(instance, LOG_ERR, "exit, PPSAPI not found in kernel"); free(instance); return false; /* exit, don't find PPSAPI in kernel */ } /* continue initialization */ instance->ttyfd = fd1; instance->ppsfd = fd2; /* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */ oncore_read_config(instance); if (!oncore_ppsapi(instance)) { free(instance); return false; } pp->io.clock_recv = oncore_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd1; if (!io_addclock(&pp->io)) { oncore_log(instance, LOG_ERR, "can't do io_addclock"); close(fd1); pp->io.fd = -1; free(instance); return false; } pp->unitptr = instance; /* can not trivially free instance now */ #ifdef ENABLE_ONCORE_SHMEM /* * Before starting ONCORE, lets setup SHMEM * This will include merging an old SHMEM into the new one if * an old one is found. */ oncore_init_shmem(instance); #endif /* * This will return the Model of the Oncore receiver. * and start the Initialization loop in oncore_msg_Cj. */ instance->o_state = ONCORE_CHECK_ID; oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_ID"); instance->timeout = 4; oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */ oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); instance->pollcnt = 2; return true; } /* * oncore_shutdown - shut down the clock */ static void oncore_shutdown( struct refclockproc *pp ) { struct instance *instance = pp->unitptr; if (pp->io.fd != -1) io_closeclock(&pp->io); if (instance != NULL) { time_pps_destroy (instance->pps_h); close(instance->ttyfd); if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd)) close(instance->ppsfd); if (instance->shmemfd) close(instance->shmemfd); free(instance); } } /* * oncore_poll - called by the transmit procedure */ static void oncore_poll( int unit, struct peer *peer ) { struct instance *instance; UNUSED_ARG(unit); instance = peer->procptr->unitptr; if (instance->timeout) { instance->timeout--; if (instance->timeout == 0) { oncore_log(instance, LOG_ERR, "Oncore: No response from @@Cj, shutting down driver"); oncore_shutdown(peer->procptr); } else { oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); oncore_log(instance, LOG_WARNING, "Oncore: Resend @@Cj"); } return; } if (!instance->pollcnt) refclock_report(peer, CEVNT_TIMEOUT); else instance->pollcnt--; peer->procptr->polls++; instance->polled = 1; } /* * Initialize PPSAPI */ static bool oncore_ppsapi( struct instance *instance ) { int cap, mode, mode1; const char *cp; if (time_pps_getcap(instance->pps_h, &cap) < 0) { oncore_log_f(instance, LOG_ERR, "time_pps_getcap failed: %m"); return false; } if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) { oncore_log_f(instance, LOG_ERR, "time_pps_getparams failed: %m"); return false; } /* nb. only turn things on, if someone else has turned something * on before we get here, leave it alone! */ if (instance->assert) { cp = "Assert"; mode = PPS_CAPTUREASSERT; mode1 = PPS_OFFSETASSERT; } else { cp = "Clear"; mode = PPS_CAPTURECLEAR; mode1 = PPS_OFFSETCLEAR; } oncore_log_f(instance, LOG_INFO, "Initializing timing to %s.", cp); if (!(mode & cap)) { oncore_log_f(instance, LOG_ERR, "Can't set timing to %s, exiting...", cp); return false; } if (!(mode1 & cap)) { oncore_log_f(instance, LOG_NOTICE, "Can't set %s, this will increase jitter.", cp); mode1 = 0; } /* only set what is legal */ instance->pps_p.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap; if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) { oncore_log_f(instance, LOG_ERR, "ONCORE: time_pps_setparams fails %m"); return false; /* exit, can't do time_pps_setparans on PPS file */ } /* If HARDPPS is on, we tell kernel */ if (instance->hardpps) { int i; oncore_log(instance, LOG_INFO, "HARDPPS Set."); if (instance->assert) i = PPS_CAPTUREASSERT; else i = PPS_CAPTURECLEAR; /* we know that 'i' is legal from above */ if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i, PPS_TSFMT_TSPEC) < 0) { oncore_log_f(instance, LOG_ERR, "time_pps_kcbind failed: %m"); oncore_log(instance, LOG_ERR, "HARDPPS failed, abort..."); return false; } hardpps_enable = true; } return true; } #ifdef ENABLE_ONCORE_SHMEM static void oncore_init_shmem( struct instance *instance ) { int l, fd; uint8_t *cp, *cp1, *buf, *shmem_old; struct msg_desc *mp; struct stat sbuf; size_t i, n, n1, shmem_length, shmem_old_size; /* * The first thing we do is see if there is an instance->shmem_fname file (still) * out there from a previous run. If so, we copy it in and use it to initialize * shmem (so we won't lose our almanac if we need it). */ shmem_old = 0; shmem_old_size = 0; if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0) oncore_log(instance, LOG_WARNING, "ONCORE: Can't open SHMEM file"); else { fstat(fd, &sbuf); shmem_old_size = sbuf.st_size; if (shmem_old_size != 0) { shmem_old = emalloc((unsigned) sbuf.st_size); read(fd, shmem_old, shmem_old_size); } close(fd); } /* OK, we now create the NEW SHMEM. */ if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { oncore_log(instance, LOG_WARNING, "ONCORE: Can't open shmem"); if (shmem_old) free(shmem_old); return; } /* see how big it needs to be */ n = 1; for (mp=oncore_messages; mp->flag[0]; mp++) { mp->shmem = n; /* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */ if (!strcmp(mp->flag, "Cb")) { instance->shmem_Cb = n; n += (mp->len + 3) * 34; } if (!strcmp(mp->flag, "Ba")) { instance->shmem_Ba = n; n += (mp->len + 3) * 3; } if (!strcmp(mp->flag, "Ea")) { instance->shmem_Ea = n; n += (mp->len + 3) * 3; } if (!strcmp(mp->flag, "Ha")) { instance->shmem_Ha = n; n += (mp->len + 3) * 3; } n += (mp->len + 3); } shmem_length = n + 2; buf = emalloc_zero(shmem_length); /* next build the new SHMEM buffer in memory */ for (mp=oncore_messages; mp->flag[0]; mp++) { l = mp->shmem; buf[l + 0] = mp->len >> 8; buf[l + 1] = mp->len & 0xff; buf[l + 2] = 0; buf[l + 3] = '@'; buf[l + 4] = '@'; buf[l + 5] = mp->flag[0]; buf[l + 6] = mp->flag[1]; if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) { if (!strcmp(mp->flag, "Cb")) n = 35; else n = 4; for (i=1; ilen+3) + 0] = mp->len >> 8; buf[l + i * (mp->len+3) + 1] = mp->len & 0xff; buf[l + i * (mp->len+3) + 2] = 0; buf[l + i * (mp->len+3) + 3] = '@'; buf[l + i * (mp->len+3) + 4] = '@'; buf[l + i * (mp->len+3) + 5] = mp->flag[0]; buf[l + i * (mp->len+3) + 6] = mp->flag[1]; } } } /* we now walk through the two buffers (shmem_old and buf, soon to become shmem) * copying the data in shmem_old to buf. * When we are done we write it out and free both buffers. * If the structure sizes don't agree, I will not copy. * This could be due to an addition/deletion or a problem with the disk file. */ if (shmem_old) { if (shmem_old_size == shmem_length) { for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3), cp1+=(n+3)) { n1 = 256*(*(cp1-3)) + *(cp1-2); if (n == 0 || n1 != n || strncmp((char *) cp, (char *) cp1, 4)) break; memcpy(cp, cp1, (size_t) n); } } free(shmem_old); } i = write(instance->shmemfd, buf, shmem_length); free(buf); if (i != shmem_length) { oncore_log(instance, LOG_ERR, "ONCORE: error writing shmem"); close(instance->shmemfd); return; } instance->shmem = (uint8_t *) mmap(0, shmem_length, PROT_READ | PROT_WRITE, #ifdef MAP_HASSEMAPHORE MAP_HASSEMAPHORE | #endif MAP_SHARED, instance->shmemfd, (off_t)0); if (instance->shmem == (uint8_t *)MAP_FAILED) { instance->shmem = 0; close(instance->shmemfd); return; } oncore_log_f(instance, LOG_NOTICE, "SHMEM (size = %ld) is CONFIGURED and available as %s", (unsigned long) shmem_length, instance->shmem_fname); } #endif /* ENABLE_ONCORE_SHMEM */ /* * Read Input file if it exists. */ static void oncore_read_config( struct instance *instance ) { /* * First we try to open the configuration file * /etc/ntp.oncore.N * where N is the unit number * If we don't find it we try * /etc/ntp.oncoreN * and then * /etc/ntp.oncore * * If we don't find any then we don't have the cable delay or PPS offset * and we choose MODE (4) below. * * Five Choices for MODE * (0) ONCORE is preinitialized, don't do anything to change it. * nb, DON'T set 0D mode, DON'T set Delay, position... * (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode. * (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position, * lock this in, go to 0D mode. * (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode. * (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position, * lock this in, go to 0D mode. * NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY] * then this position is set as the INITIAL position of the ONCORE. * This can reduce the time to first fix. * ------------------------------------------------------------------------------- * Note that an Oncore UT without a battery backup retains NO information if it is * power cycled, with a Battery Backup it remembers the almanac, etc. * For an Oncore VP, there is an eeprom that will contain this data, along with the * option of Battery Backup. * So a UT without Battery Backup is equivalent to doing a HARD RESET on each * power cycle, since there is nowhere to store the data. * ------------------------------------------------------------------------------- * * If we open one or the other of the files, we read it looking for * MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS, * STATUS, POSN3D, POSN2D, CHAN, TRAIM * then initialize using method MODE. For Mode = (1,3) all of (LAT, LON, HT) must * be present or mode reverts to (2,4). * * Read input file. * * # is comment to end of line * = allowed between 1st and 2nd fields. * * Expect to see one line with 'MODE' as first field, followed by an integer * in the range 0-4 (default = 4). * * Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields. * All numbers are floating point. * DDD.ddd * DDD MMM.mmm * DDD MMM SSS.sss * * Expect to see one line with 'HT' as first field, * followed by 1-2 fields. First is a number, the second is 'FT' or 'M' * for feet or meters. HT is the height above the GPS ellipsoid. * If the receiver reports height in both GPS and MSL, then we will report * the difference GPS-MSL on the clockstats file. * * There is an optional line, starting with DELAY, followed * by 1 or two fields. The first is a number (a time) the second is * 'MS', 'US' or 'NS' for milliseconds, microseconds or nanoseconds. * DELAY is cable delay, typically a few tens of ns. * * There is an optional line, starting with OFFSET, followed * by 1 or two fields. The first is a number (a time) the second is * 'MS', 'US' or 'NS' for milliseconds, microseconds or nanoseconds. * OFFSET is the offset of the PPS pulse from 0. (only fully implemented * with the PPSAPI, we need to be able to tell the Kernel about this * offset if the Kernel PLL is in use, but can only do this presently * when using the PPSAPI interface. If not using the Kernel PLL, * then there is no problem. * * There is an optional line, with either ASSERT or CLEAR on it, which * determine which transition of the PPS signal is used for timing by the * PPSAPI. If neither is present, then ASSERT is assumed. * ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input. * For Flag2, ASSERT=0, and hence is default. * * There is an optional line, with HARDPPS on it. Including this line causes * the PPS signal to control the kernel PLL. * HARDPPS can also be set with FLAG3 of the ntp.conf input. * For Flag3, 0 is disabled, and the default. * * There are three options that have to do with using the shared memory option. * First, to enable the option there must be a SHMEM line with a file name. * The file name is the file associated with the shared memory. * * In shared memory, there is one 'record' for each returned variable. * For the @@Ea data there are three 'records' containing position data. * There will always be data in the record corresponding to the '0D' @@Ea record, * and the user has a choice of filling the '3D' record by specifying POSN3D, * or the '2D' record by specifying POSN2D. In either case the '2D' or '3D' * record is filled once every 15s. * * Two additional variables that can be set are CHAN and TRAIM. These should be * set correctly by the code examining the @@Cj record, but we bring them out here * to allow the user to override either the # of channels, or the existence of TRAIM. * CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be * followed by YES or NO. * * There is an optional line with MASK on it followed by one integer field in the * range 0 to 89. This sets the satellite mask angle and will determine the minimum * elevation angle for satellites to be tracked by the receiver. The default value * is 10 deg for the VP and 0 deg for all other receivers. * * There is an optional line with PPSCONTROL on it (only valid for M12 or M12+T * receivers, the option is read, but ignored for all others) * and it is followed by: * ON Turn PPS on. This is the default and the default for other * oncore receivers. The PPS is on even if not tracking * any satellites. * SATELLITE Turns PPS on if tracking at least 1 satellite, else off. * TRAIM Turns PPS on or off controlled by TRAIM. * The OFF option is NOT implemented, since the Oncore driver will not work * without the PPS signal. * * So acceptable input would be * # these are my coordinates (RWC) * LON -106 34.610 * LAT 35 08.999 * HT 1589 # could equally well say HT 5215 FT * DELAY 60 ns */ FILE *fd; char *cc, *ca, line[100], units[2], device[64]; const char *dirs[] = { "/etc/ntp", "/etc", 0 }; const char *cp, **cpp; int i, sign, lat_flg, long_flg, ht_flg, mode, mask; double f1, f2, f3; fd = NULL; /* just to shutup gcc complaint */ for (cpp=dirs; *cpp; cpp++) { cp = *cpp; snprintf(device, sizeof(device), "%s/ntp.oncore.%d", cp, instance->unit); /* try "ntp.oncore.0 */ if ((fd=fopen(device, "r"))) break; snprintf(device, sizeof(device), "%s/ntp.oncore%d", cp, instance->unit); /* try "ntp.oncore0" */ if ((fd=fopen(device, "r"))) break; snprintf(device, sizeof(device), "%s/ntp.oncore", cp); if ((fd=fopen(device, "r"))) /* last try "ntp.oncore" */ break; } if (!fd) { /* no inputfile, default to the works ... */ instance->init_type = 4; return; } mode = mask = 0; lat_flg = long_flg = ht_flg = 0; while (fgets(line, 100, fd)) { char *cpw; /* Remove comments */ if ((cpw = strchr(line, '#'))) *cpw = '\0'; /* Remove trailing space */ for (i = (int)strlen(line); i > 0 && isascii((unsigned char)line[i - 1]) && isspace((unsigned char)line[i - 1]); ) line[--i] = '\0'; /* Remove leading space */ for (cc = line; *cc && isascii((unsigned char)*cc) && isspace((unsigned char)*cc); cc++) continue; /* Stop if nothing left */ if (!*cc) continue; /* Uppercase the command and find the arg */ for (ca = cc; *ca; ca++) { if (isascii((unsigned char)*ca)) { if (islower((unsigned char)*ca)) { *ca = toupper((unsigned char)*ca); } else if (isspace((unsigned char)*ca) || (*ca == '=')) break; } } /* Remove space (and possible =) leading the arg */ for (; *ca && isascii((unsigned char)*ca) && (isspace((unsigned char)*ca) || (*ca == '=')); ca++) continue; if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) { instance->shmem_fname = estrdup(ca); continue; } /* Uppercase argument as well */ for (cpw = ca; *cpw; cpw++) if (isascii((unsigned char)*cpw) && islower((unsigned char)*cpw)) *cpw = toupper((unsigned char)*cpw); if (!strncmp(cc, "LAT", (size_t) 3)) { f1 = f2 = f3 = 0; if ( 3 != sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3)) continue; sign = 1; if (f1 < 0) { f1 = -f1; sign = -1; } instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*milliseconds*/ lat_flg++; } else if (!strncmp(cc, "LON", (size_t) 3)) { f1 = f2 = f3 = 0; if ( 3 != sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3)) continue; sign = 1; if (f1 < 0) { f1 = -f1; sign = -1; } instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*milliseconds*/ long_flg++; } else if (!strncmp(cc, "HT", (size_t) 2)) { f1 = 0; units[0] = '\0'; if ( 2 != sscanf(ca, "%lf %1s", &f1, units)) continue; if (units[0] == 'F') f1 = 0.3048 * f1; instance->ss_ht = 100 * f1; /* cm */ ht_flg++; } else if (!strncmp(cc, "DELAY", (size_t) 5)) { f1 = 0; units[0] = '\0'; if ( 2 != sscanf(ca, "%lf %1s", &f1, units)) continue; if (units[0] == 'N') ; else if (units[0] == 'U') f1 = 1000 * f1; else if (units[0] == 'M') f1 = 1000000 * f1; else f1 = 1000000000 * f1; if (f1 < 0 || f1 > 1.e9) f1 = 0; if (f1 < 0 || f1 > 999999) oncore_log_f(instance, LOG_WARNING, "PPS Cable delay of %fns out of Range, ignored", f1); else instance->delay = f1; /* delay in ns */ } else if (!strncmp(cc, "OFFSET", (size_t) 6)) { f1 = 0; units[0] = '\0'; if ( 2 != sscanf(ca, "%lf %1s", &f1, units)) continue; if (units[0] == 'N') ; else if (units[0] == 'U') f1 = 1000 * f1; else if (units[0] == 'M') f1 = 1000000 * f1; else f1 = 1000000000 * f1; if (f1 < 0 || f1 > 1.e9) f1 = 0; if (f1 < 0 || f1 > 999999999.) oncore_log_f(instance, LOG_WARNING, "PPS Offset of %fns out of Range, ignored", f1); else instance->offset = f1; /* offset in ns */ } else if (!strncmp(cc, "MODE", (size_t) 4)) { if ( 1 != sscanf(ca, "%d", &mode)) continue; if (mode < 0 || mode > 4) mode = 4; } else if (!strncmp(cc, "ASSERT", (size_t) 6)) { instance->assert = 1; } else if (!strncmp(cc, "CLEAR", (size_t) 5)) { instance->assert = 0; } else if (!strncmp(cc, "HARDPPS", (size_t) 7)) { instance->hardpps = 1; } else if (!strncmp(cc, "POSN2D", (size_t) 6)) { instance->shmem_Posn = 2; } else if (!strncmp(cc, "POSN3D", (size_t) 6)) { instance->shmem_Posn = 3; } else if (!strncmp(cc, "CHAN", (size_t) 4)) { if ( 1 != sscanf(ca, "%d", &i)) continue; if ((i == 6) || (i == 8) || (i == 12)) instance->chan_in = i; } else if (!strncmp(cc, "TRAIM", (size_t) 5)) { instance->traim_in = 1; /* so TRAIM alone is YES */ if (!strcmp(ca, "NO") || !strcmp(ca, "OFF")) /* Yes/No, On/Off */ instance->traim_in = 0; } else if (!strncmp(cc, "MASK", (size_t) 4)) { if ( 1 != sscanf(ca, "%d", &mask) ) continue; if (mask > -1 && mask < 90) instance->Ag = mask; /* Satellite mask angle */ } else if (!strncmp(cc,"PPSCONTROL",10)) { /* pps control M12 only */ if (!strcmp(ca,"ON") || !strcmp(ca, "CONTINUOUS")) { instance->pps_control = 1; /* PPS always on */ } else if (!strcmp(ca,"SATELLITE")) { instance->pps_control = 2; /* PPS on when satellite is available */ } else if (!strcmp(ca,"TRAIM")) { instance->pps_control = 3; /* PPS on when TRAIM status is OK */ } else { oncore_log_f(instance, LOG_WARNING, "Unknown value \"%s\" for PPSCONTROL, ignored", cc); } } } fclose(fd); /* * OK, have read all of data file, and extracted the good stuff. * If lat/long/ht specified they ALL must be specified for mode = (1,3). */ instance->posn_set = 1; if (!( lat_flg && long_flg && ht_flg )) { oncore_log_f(instance, LOG_WARNING, "ONCORE: incomplete data on %s", device); instance->posn_set = 0; if (mode == 1 || mode == 3) { oncore_log_f(instance, LOG_WARNING, "Input Mode = %d, but no/incomplete position, mode set to %d", mode, mode+1); mode++; } } instance->init_type = mode; oncore_log_f(instance, LOG_INFO, "Input mode = %d", mode); } /* * move data from NTP to buffer (toss the extra in the unlikely case it won't fit) */ static void oncore_receive( struct recvbuf *rbufp ) { size_t i; uint8_t *p; struct peer *peer; struct instance *instance; peer = rbufp->recv_peer; instance = peer->procptr->unitptr; p = (uint8_t *) &rbufp->recv_space; #ifdef ONCORE_VERBOSE_RECEIVE if (debug > 4) { /* SPECIAL DEBUG */ int i; char Msg[120], Msg2[10]; oncore_log_f(instance, LOG_DEBUG, ">>> %d bytes available", rbufp->recv_length); strlcpy(Msg, ">>>", sizeof(Msg)); for (i = 0; i < rbufp->recv_length; i++) { snprintf(Msg2, sizeof(Msg2), "%02x ", p[i]); strlcat(Msg, Msg2, sizeof(Msg)); } oncore_log(instance, LOG_DEBUG, Msg); strlcpy(Msg, ">>>", sizeof(Msg)); for (i = 0; i < rbufp->recv_length; i++) { snprintf(Msg2, sizeof(Msg2), "%03o ", p[i]); strlcat(Msg, Msg2, sizeof(Msg)); } oncore_log(instance, LOG_DEBUG, Msg); } #endif i = rbufp->recv_length; if (rcvbuf+rcvptr+i > &rcvbuf[sizeof(rcvbuf)-1]) i = sizeof(rcvbuf) - rcvptr; /* and some char will be lost */ memcpy(rcvbuf+rcvptr, p, i); rcvptr += i; oncore_consume(instance); } /* * Deal with any complete messages */ static void oncore_consume( struct instance *instance ) { unsigned i, m, l; while (rcvptr >= 7) { if (rcvbuf[0] != '@' || rcvbuf[1] != '@') { /* We're not in sync, lets try to get there */ for (i=1; i < rcvptr-1; i++) if (rcvbuf[i] == '@' && rcvbuf[i+1] == '@') break; #ifdef ONCORE_VERBOSE_CONSUME if (debug > 4) /* SPECIAL DEBUG */ oncore_log_f(instance, LOG_DEBUG, ">>> skipping %d chars", i); #endif if (i != rcvptr) memcpy(rcvbuf, rcvbuf+i, (size_t)(rcvptr-i)); rcvptr -= i; continue; } /* Ok, we have a header now */ l = sizeof(oncore_messages)/sizeof(oncore_messages[0]) -1; for(m = 0; m < l; m++) if (!strncmp(oncore_messages[m].flag, (char *)(rcvbuf+2), (size_t) 2)) break; if (m == l) { #ifdef ONCORE_VERBOSE_CONSUME if (debug > 4) /* SPECIAL DEBUG */ oncore_log_f(instance, LOG_DEBUG, ">>> Unknown MSG, skipping 4 (%c%c)", rcvbuf[2], rcvbuf[3]); #endif memcpy(rcvbuf, rcvbuf+4, (size_t) 4); rcvptr -= 4; continue; } l = (unsigned int)oncore_messages[m].len; #ifdef ONCORE_VERBOSE_CONSUME if (debug > 3) /* SPECIAL DEBUG */ oncore_log_f(instance, LOG_DEBUG, "GOT: %c%c %d of %d entry %d", instance->unit, rcvbuf[2], rcvbuf[3], rcvptr, l, m); #endif /* Got the entire message ? */ if (rcvptr < l) return; /* are we at the end of message? should be */ if (rcvbuf[l-2] != '\r' || rcvbuf[l-1] != '\n') { #ifdef ONCORE_VERBOSE_CONSUME if (debug) /* SPECIAL DEBUG */ oncore_log(instance, LOG_DEBUG, "NO at end of message"); #endif } else { /* check the CheckSum */ if (oncore_checksum_ok(rcvbuf, (int)l)) { if (instance->shmem != NULL) { instance->shmem[oncore_messages[m].shmem + 2]++; memcpy(instance->shmem + oncore_messages[m].shmem + 3, rcvbuf, (size_t) l); } oncore_msg_any(instance, rcvbuf, (size_t)(l-3), (int)m); if (oncore_messages[m].handler) oncore_messages[m].handler(instance, rcvbuf, (size_t) (l-3)); } #ifdef ONCORE_VERBOSE_CONSUME else if (debug) { /* SPECIAL DEBUG */ char Msg[120], Msg2[10]; oncore_log(instance, LOG_ERR, "Checksum mismatch!"); snprintf(Msg, sizeof(Msg), "@@%c%c ", rcvbuf[2], rcvbuf[3]); for (i = 4; i < l; i++) { snprintf(Msg2, sizeof(Msg2), "%03o ", rcvbuf[i]); strlcat(Msg, Msg2, sizeof(Msg)); } oncore_log(instance, LOG_DEBUG, Msg); } #endif } if (l != rcvptr) memcpy(rcvbuf, rcvbuf+l, (size_t) (rcvptr-l)); rcvptr -= l; } } static void oncore_get_timestamp( struct instance *instance, long dt1, /* tick offset THIS time step */ long dt2 /* tick offset NEXT time step */ ) { int Rsm; unsigned long j; l_fp ts, ts_tmp; double dmy; struct timespec *tsp = 0; UNUSED_ARG(dt1); int current_mode; pps_params_t current_params; struct timespec timeout; struct peer *peer; pps_info_t pps_i; char Msg[160]; peer = instance->peer; #if 1 /* If we are in SiteSurvey mode, then we are in 3D mode, and we fall through. * If we have Finished the SiteSurvey, then we fall through for the 14/15 * times we get here in 0D mode (the 1/15 is in 3D for SHMEM). * This gives good time, which gets better when the SS is done. */ if ((instance->site_survey == ONCORE_SS_DONE) && (instance->mode != MODE_0D)) { #else /* old check, only fall through for SS_DONE and 0D mode, 2h45m wait for ticks */ if ((instance->site_survey != ONCORE_SS_DONE) || (instance->mode != MODE_0D)) { #endif peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } /* Don't do anything without an almanac to define the GPS->UTC delta */ if (instance->rsm.bad_almanac) { peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } /* Once the Almanac is valid, the M12+T does not produce valid UTC * immediately. * Wait for UTC offset decode valid, then wait one message more * so we are not off by 13 seconds after reset. */ if (instance->count5) { instance->count5--; peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } j = instance->ev_serial; timeout.tv_sec = 0; timeout.tv_nsec = 0; if (time_pps_fetch(instance->pps_h, PPS_TSFMT_TSPEC, &pps_i, &timeout) < 0) { oncore_log_f(instance, LOG_ERR, "time_pps_fetch failed %m"); peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } if (instance->assert) { tsp = &pps_i.assert_timestamp; #ifdef ONCORE_VERBOSE_GET_TIMESTAMP if (debug > 2) { /* SPECIAL DEBUG */ unsigned long i; i = (unsigned long) pps_i.assert_sequence; oncore_log_f(instance, LOG_DEBUG, "serial/j (%lu, %lu) %ld.%09ld", i, j, (long)tsp->tv_sec, tsp->tv_nsec); } #endif if (pps_i.assert_sequence == j) { oncore_log(instance, LOG_NOTICE, "ONCORE: oncore_get_timestamp, error serial pps"); peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } instance->ev_serial = pps_i.assert_sequence; } else { tsp = &pps_i.clear_timestamp; #if 0 if (debug > 2) { /* SPECIAL DEBUG */ unsigned long i; i = (unsigned long) pps_i.clear_sequence; oncore_log_f(instance, LOG_DEBUG, "serial/j (%lu, %lu) %ld.%09ld", i, j, (long)tsp->tv_sec, tsp->tv_nsec); } #endif if (pps_i.clear_sequence == j) { oncore_log(instance, LOG_ERR, "oncore_get_timestamp, error serial pps"); peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } instance->ev_serial = pps_i.clear_sequence; } /* add in saw_tooth and offset, these will be ZERO if no TRAIM */ /* they will be IGNORED if the PPSAPI can't do PPS_OFFSET/ASSERT/CLEAR */ /* we just try to add them in and don't test for that here */ /* saw_tooth not really necessary if using TIMEVAL */ /* since its only precise to us, but do it anyway. */ /* offset in ns, and is positive (late), we subtract */ /* to put the PPS time transition back where it belongs */ /* must hand the offset for the NEXT sec off to the Kernel to do */ /* the addition, so that the Kernel PLL sees the offset too */ if (instance->assert) instance->pps_p.assert_offset.tv_nsec = -dt2; else instance->pps_p.clear_offset.tv_nsec = -dt2; /* The following code is necessary, and not just a time_pps_setparams, * using the saved instance->pps_p, since some other process on the * machine may have diddled with the mode bits (say adding something * that it needs). We take what is there and ADD what we need. * [[ The results from the time_pps_getcap is unlikely to change so * we could probably just save it, but I choose to do the call ]] * Unfortunately, there is only ONE set of mode bits in the kernel per * interface, and not one set for each open handle. * * There is still a race condition here where we might mess up someone * elses mode, but if he is being careful too, he should survive. */ if (time_pps_getcap(instance->pps_h, ¤t_mode) < 0) { oncore_log_f(instance, LOG_ERR, "time_pps_getcap failed: %m"); peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } if (time_pps_getparams(instance->pps_h, ¤t_params) < 0) { oncore_log_f(instance, LOG_ERR, "time_pps_getparams failed: %m"); peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } /* or current and mine */ current_params.mode |= instance->pps_p.mode; /* but only set whats legal */ current_params.mode &= current_mode; current_params.assert_offset.tv_sec = 0; current_params.assert_offset.tv_nsec = -dt2; current_params.clear_offset.tv_sec = 0; current_params.clear_offset.tv_nsec = -dt2; if (time_pps_setparams(instance->pps_h, ¤t_params)) oncore_log(instance, LOG_ERR, "ONCORE: Error doing time_pps_setparams"); /* have time from UNIX origin, convert to NTP origin. */ ts = tspec_stamp_to_lfp(*tsp); instance->pp->lastrec = ts; /* print out information about this timestamp (long line) */ ts_tmp = ts; setlfpuint(ts_tmp, 0); /* zero integer part */ dmy = lfptod(ts_tmp); /* convert fractional part to a double */ j = 1.0e9*dmy; /* then to integer ns */ Rsm = 0; if (instance->chan == 6) Rsm = instance->BEHa[64]; else if (instance->chan == 8) Rsm = instance->BEHa[72]; else if (instance->chan == 12) Rsm = ((instance->BEHa[129]<<8) | instance->BEHa[130]); if (instance->chan == 6 || instance->chan == 8) { char f1[5], f2[5], f3[5], f4[5]; if (instance->traim) { snprintf(f1, sizeof(f1), "%d", instance->BEHn[21]); snprintf(f2, sizeof(f2), "%d", instance->BEHn[22]); snprintf(f3, sizeof(f3), "%2d", instance->BEHn[23] * 256 + instance->BEHn[24]); snprintf(f4, sizeof(f4), "%3d", (int8_t)instance->BEHn[25]); } else { strlcpy(f1, "x", sizeof(f1)); strlcpy(f2, "x", sizeof(f2)); strlcpy(f3, "xx", sizeof(f3)); strlcpy(f4, "xxx", sizeof(f4)); } snprintf(Msg, sizeof(Msg), /* MAX length 128, currently at 127 */ "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d", lfpuint(ts), j, instance->pp->year, instance->pp->day, instance->pp->hour, instance->pp->minute, instance->pp->second, (long) tsp->tv_sec % 60, (unsigned)Rsm, 0.1*(256*instance->BEHa[35]+instance->BEHa[36]), /*rsat dop */ instance->BEHa[38], instance->BEHa[39], instance->traim, f1, f2, /* nsat visible, nsat tracked, traim,traim,traim */ f3, f4, /* sigma neg-sawtooth */ /*sat*/ instance->BEHa[41], instance->BEHa[45], instance->BEHa[49], instance->BEHa[53], instance->BEHa[57], instance->BEHa[61], instance->BEHa[65], instance->BEHa[69] ); /* will be 0 for 6 chan */ } else if (instance->chan == 12) { char f1[5], f2[5], f3[5], f4[5]; if (instance->traim) { snprintf(f1, sizeof(f1), "%d", instance->BEHn[6]); snprintf(f2, sizeof(f2), "%d", instance->BEHn[7]); snprintf(f3, sizeof(f3), "%d", instance->BEHn[12] * 256 + instance->BEHn[13]); snprintf(f4, sizeof(f4), "%3d", (int8_t)instance->BEHn[14]); } else { strlcpy(f1, "x", sizeof(f1)); strlcpy(f2, "x", sizeof(f2)); strlcpy(f3, "xx", sizeof(f3)); strlcpy(f4, "xxx", sizeof(f4)); } snprintf(Msg, sizeof(Msg), "%u.%09lu %d %d %2d %2d %2d %2ld rstat %02x dop %4.1f nsat %2d,%d traim %d,%s,%s sigma %s neg-sawtooth %s sat %d%d%d%d%d%d%d%d%d%d%d%d", lfpuint(ts), j, instance->pp->year, instance->pp->day, instance->pp->hour, instance->pp->minute, instance->pp->second, (long) tsp->tv_sec % 60, (unsigned)Rsm, 0.1*(256*instance->BEHa[53]+instance->BEHa[54]), /*rsat dop */ instance->BEHa[55], instance->BEHa[56], instance->traim, f1, f2, /* nsat visible, nsat tracked traim,traim,traim */ f3, f4, /* sigma neg-sawtooth */ /*sat*/ instance->BEHa[58], instance->BEHa[64], instance->BEHa[70], instance->BEHa[76], instance->BEHa[82], instance->BEHa[88], instance->BEHa[94], instance->BEHa[100], instance->BEHa[106], instance->BEHa[112], instance->BEHa[118], instance->BEHa[124] ); } /* and some things I don't understand (magic ntp things) */ if (!refclock_process(instance->pp)) { refclock_report(instance->peer, CEVNT_BADTIME); peer->cfg.flags &= ~FLAG_PPS; /* problem - clear PPS FLAG */ return; } oncore_log(instance, LOG_INFO, Msg); /* this is long message above */ instance->pollcnt = 2; if (instance->polled) { instance->polled = 0; /* instance->pp->dispersion = instance->pp->skew = 0; */ instance->pp->lastref = instance->pp->lastrec; refclock_receive(instance->peer); } peer->cfg.flags |= FLAG_PPS; } /*************** oncore_msg_XX routines start here *******************/ /* * print Oncore response message. */ static void oncore_msg_any( struct instance *instance, uint8_t *buf, size_t len, int idx ) { #ifndef ONCORE_VERBOSE_MSG_ANY UNUSED_ARG(instance); UNUSED_ARG(buf); UNUSED_ARG(len); UNUSED_ARG(idx); #else const char *fmt = oncore_messages[idx].fmt; const char *p; char *q; char *qlim; struct timespec ts; char Msg[120], Msg2[10]; if (debug > 3) { /* SPECIAL DEBUG */ (void) clock_gettime(CLOCK_REALTIME, &ts); oncore_log(instance, LOG_DEBUG, "%ld.%09ld", (long)tv.tv_sec, tv.tv_nsec); if (!*fmt) { snprintf(Msg, sizeof(Msg), ">>@@%c%c ", buf[2], buf[3]); for(int i = 2; i < len && i < 2400 ; i++) { snprintf(Msg2, sizeof(Msg2), "%02x", buf[i]); strlcat(Msg, Msg2, sizeof(Msg)); } oncore_log(instance, LOG_DEBUG, Msg); return; } else { strlcpy(Msg, "##", sizeof(Msg)); qlim = Msg + sizeof(Msg) - 3; for (p = fmt, q = Msg + 2; q < qlim && *p; ) { *q++ = *p++; *q++ = '_'; } *q = '\0'; oncore_log(instance, LOG_DEBUG, Msg); snprintf(Msg, sizeof(Msg), "%c%c", buf[2], buf[3]); i = 4; for (p = fmt; *p; p++) { snprintf(Msg2, "%02x", buf[i++]); strlcat(Msg, Msg2, sizeof(Msg)); } oncore_log(instance, LOG_DEBUG, Msg); } } #endif } /* Latitude, Longitude, Height */ static void oncore_msg_Adef( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(instance); UNUSED_ARG(buf); UNUSED_ARG(len); } /* Mask Angle */ static void oncore_msg_Ag( struct instance *instance, uint8_t *buf, size_t len ) { const char *cp; UNUSED_ARG(len); cp = "set to"; if (instance->o_state == ONCORE_RUN) cp = "is"; instance->Ag = buf[4]; oncore_log_f(instance, LOG_INFO, "Satellite mask angle %s %d degrees", cp, (int)instance->Ag); } /* * get Position hold position */ static void oncore_msg_As( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(len); instance->ss_lat = buf_w32(&buf[4]); instance->ss_long = buf_w32(&buf[8]); instance->ss_ht = buf_w32(&buf[12]); /* Print out Position */ oncore_print_posn(instance); } /* * Try to use Oncore UT+ Auto Survey Feature * If its not there (VP), set flag to do it ourselves. */ static void oncore_msg_At( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(len); instance->saw_At = 1; if (instance->site_survey == ONCORE_SS_TESTING) { if (buf[4] == 2) { oncore_log(instance, LOG_NOTICE, "Initiating hardware 3D site survey"); oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW"); instance->site_survey = ONCORE_SS_HW; } } } /* * get PPS Offset * Nb. @@Ay is not supported for early UT (no plus) model */ static void oncore_msg_Ay( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(len); if (instance->saw_Ay) return; instance->saw_Ay = 1; /* @@Ay is between 0 and 999999999 */ instance->offset = buf_w32(&buf[4]); oncore_log_f(instance, LOG_INFO, "PPS Offset is set to %ld ns", instance->offset); } /* * get Cable Delay */ static void oncore_msg_Az( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(len); if (instance->saw_Az) return; instance->saw_Az = 1; instance->delay = (unsigned long)buf_w32(&buf[4]); oncore_log_f(instance, LOG_INFO, "Cable delay is set to %lu ns", instance->delay); } /* Ba, Ea and Ha come here, these contain Position */ static void oncore_msg_BaEaHa( struct instance *instance, uint8_t *buf, size_t len ) { const char *cp; int mode; /* OK, we are close to the RUN state now. * But we have a few more items to initialize first. * * At the beginning of this routine there are several 'timers'. * We enter this routine 1/sec, and since the upper levels of NTP have usurped * the use of timers, we use the 1/sec entry to do things that * we would normally do with timers... */ if (instance->o_state == ONCORE_CHECK_CHAN) { /* here while checking for the # chan */ if (buf[2] == 'B') { /* 6chan */ if (instance->chan_ck < 6) instance->chan_ck = 6; } else if (buf[2] == 'E') { /* 8chan */ if (instance->chan_ck < 8) instance->chan_ck = 8; } else if (buf[2] == 'H') { /* 12chan */ if (instance->chan_ck < 12) instance->chan_ck = 12; } if (instance->count3++ < 5) return; instance->count3 = 0; if (instance->chan_in != -1) /* set in Input */ instance->chan = (uint8_t)instance->chan_in; else /* set from test */ instance->chan = instance->chan_ck; oncore_log_f(instance, LOG_INFO, "Input says chan = %d", instance->chan_in); oncore_log_f(instance, LOG_INFO, "Model # says chan = %d", instance->chan_id); oncore_log_f(instance, LOG_INFO, "Testing says chan = %d", instance->chan_ck); oncore_log_f(instance, LOG_INFO, "Using chan = %d", instance->chan); instance->o_state = ONCORE_HAVE_CHAN; oncore_log(instance, LOG_NOTICE, "state = ONCORE_HAVE_CHAN"); instance->timeout = 4; oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); return; } if (instance->o_state != ONCORE_ALMANAC && instance->o_state != ONCORE_RUN) return; /* PAUSE 5sec - make sure results are stable, before using position */ if (instance->count) { if (instance->count++ < 5) return; instance->count = 0; } memcpy(instance->BEHa, buf, (size_t) (len+3)); /* Ba, Ea or Ha */ /* check if we saw a response to Gc (M12 or M12+T */ if (instance->pps_control_msg_seen != -2) { if ((instance->pps_control_msg_seen == -1) && (instance->pps_control != -1)) { oncore_log(instance, LOG_INFO, "PPSCONTROL set, but not implemented (not M12)"); } instance->pps_control_msg_seen = -2; } /* check the antenna (did it get unplugged) and almanac (is it ready) for changes. */ oncore_check_almanac(instance); oncore_check_antenna(instance); /* If we are in Almanac mode, waiting for Almanac, we can't do anything till we have it */ /* When we have an almanac, we will start the Bn/En/@@Hn messages */ if (instance->o_state == ONCORE_ALMANAC) if (oncore_wait_almanac(instance)) return; /* do some things once when we get this far in BaEaHa */ if (instance->once) { instance->once = 0; instance->count2 = 1; /* Have we seen an @@At (position hold) command response */ /* if not, message out */ if (instance->chan != 12 && !instance->saw_At) { oncore_log(instance, LOG_NOTICE, "Not Good, no @@At command (no Position Hold), must be a GT/GT+"); oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); } /* have an Almanac, can start the SiteSurvey * (actually only need to get past the almanac_load where we diddle with At * command,- we can't change it after we start the HW_SS below */ mode = instance->init_type; switch (mode) { case 0: /* NO initialization, don't change anything */ case 1: /* Use given Position */ case 3: instance->site_survey = ONCORE_SS_DONE; oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE"); break; case 2: case 4: /* Site Survey */ oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_TESTING"); instance->site_survey = ONCORE_SS_TESTING; instance->count1 = 1; if (instance->chan == 12) oncore_sendmsg(instance, oncore_cmd_Gd3, sizeof(oncore_cmd_Gd3)); /* M12+T */ else oncore_sendmsg(instance, oncore_cmd_At2, sizeof(oncore_cmd_At2)); /* not GT, arg not VP */ break; default: /* huh? */ break; } /* Read back PPS Offset for Output */ /* Nb. This will fail silently for early UT (no plus) and M12 models */ oncore_sendmsg(instance, oncore_cmd_Ayx, sizeof(oncore_cmd_Ayx)); /* Read back Cable Delay for Output */ oncore_sendmsg(instance, oncore_cmd_Azx, sizeof(oncore_cmd_Azx)); /* Read back Satellite Mask Angle for Output */ oncore_sendmsg(instance, oncore_cmd_Agx, sizeof(oncore_cmd_Agx)); } /* Unfortunately, the Gd3 command returns '3' for the M12 v1.3 firmware where it is * out-of-range and it should return 0-2. (v1.3 can't do a HW Site Survey) * We must do the Gd3, and then wait a cycle or two for things to settle, * then check Ha[130]&0x10 to see if a SS is in progress. * We will set SW if HW has not been set after an appropriate delay. */ if (instance->site_survey == ONCORE_SS_TESTING) { if (instance->chan == 12) { if (instance->count1) { if (instance->count1++ > 5 || instance->BEHa[130]&0x10) { instance->count1 = 0; if (instance->BEHa[130]&0x10) { oncore_log(instance, LOG_NOTICE, "Initiating hardware 3D site survey"); oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_HW"); instance->site_survey = ONCORE_SS_HW; } else { oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW"); instance->site_survey = ONCORE_SS_SW; } } } } else { if (instance->count1) { if (instance->count1++ > 5) { instance->count1 = 0; /* * For instance->site_survey to still be ONCORE_SS_TESTING, then after a 5sec * wait after the @@At2/@@Gd3 command we have not changed the state to * ONCORE_SS_HW. If the Hardware is capable of doing a Site Survey, then * the variable would have been changed by now. * There are three possibilities: * 6/8chan * (a) We did not get a response to the @@At0 or @@At2 commands, * and it must be a GT/GT+/SL with no position hold mode. * We will have to do it ourselves. * (b) We saw the @@At0, @@At2 commands, but @@At2 failed, * must be a VP or older UT which doesn't have Site Survey mode. * We will have to do it ourselves. * 12chan * (c) We saw the @@Gd command, and saw H[13]*0x10 * We will have to do it ourselves (done above) */ oncore_log_f(instance, LOG_INFO, "Initiating software 3D site survey (%d samples)", POS_HOLD_AVERAGE); oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_SW"); instance->site_survey = ONCORE_SS_SW; instance->ss_lat = instance->ss_long = instance->ss_ht = 0; if (instance->chan == 12) oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* disable */ else { oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* disable */ oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* disable */ } } } } } /* check the mode we are in 0/2/3D */ if (instance->chan == 6) { if (instance->BEHa[64]&0x8) instance->mode = MODE_0D; else if (instance->BEHa[64]&0x10) instance->mode = MODE_2D; else if (instance->BEHa[64]&0x20) instance->mode = MODE_3D; } else if (instance->chan == 8) { if (instance->BEHa[72]&0x8) instance->mode = MODE_0D; else if (instance->BEHa[72]&0x10) instance->mode = MODE_2D; else if (instance->BEHa[72]&0x20) instance->mode = MODE_3D; } else if (instance->chan == 12) { int bits; bits = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */ if (bits == 0x4) instance->mode = MODE_0D; else if (bits == 0x6) instance->mode = MODE_2D; else if (bits == 0x7) instance->mode = MODE_3D; } /* copy the record to the (extra) location in SHMEM */ if (instance->shmem) { size_t i; uint8_t *smp; /* pointer to start of shared mem for Ba/Ea/Ha */ switch(instance->chan) { case 6: smp = &instance->shmem[instance->shmem_Ba]; break; case 8: smp = &instance->shmem[instance->shmem_Ea]; break; case 12: smp = &instance->shmem[instance->shmem_Ha]; break; default: smp = (uint8_t *) NULL; break; } switch (instance->mode) { case MODE_0D: i = 1; break; /* 0D, Position Hold */ case MODE_2D: i = 2; break; /* 2D, Altitude Hold */ case MODE_3D: i = 3; break; /* 3D fix */ default: i = 0; break; } if (i && smp != NULL) { i *= (len+6); smp[i + 2]++; memcpy(&smp[i+3], buf, (size_t) (len+3)); } } /* * check if traim timer active * if it hasn't been cleared, then @@Bn/@@En/@@Hn did not respond */ if (instance->traim_delay) { if (instance->traim_delay++ > 5) { instance->traim = 0; instance->traim_delay = 0; cp = "ONCORE: Did not detect TRAIM response, TRAIM = OFF"; oncore_log(instance, LOG_INFO, cp); oncore_set_traim(instance); } else return; } /* by now should have a @@Ba/@@Ea/@@Ha with good data in it */ if (!instance->have_dH && !instance->traim_delay) oncore_compute_dH(instance); /* * must be ONCORE_RUN if we are here. * Have # chan and TRAIM by now. */ instance->pp->year = buf[6]*256+buf[7]; instance->pp->day = ymd2yd(buf[6]*256+buf[7], buf[4], buf[5]); instance->pp->hour = buf[8]; instance->pp->minute = buf[9]; instance->pp->second = buf[10]; /* * Are we doing a Hardware or Software Site Survey? */ if (instance->site_survey == ONCORE_SS_HW || instance->site_survey == ONCORE_SS_SW) oncore_ss(instance); /* see if we ever saw a response from the @@Ayx above */ if (instance->count2) { if (instance->count2++ > 5) { /* this delay to check on @@Ay command */ instance->count2 = 0; /* Have we seen an Ay (1PPS time offset) command response */ /* if not, and non-zero offset, zero the offset, and send message */ if (!instance->saw_Ay && instance->offset) { oncore_log(instance, LOG_INFO, "No @@Ay command, PPS OFFSET ignored"); instance->offset = 0; } } } /* * Check the leap second status once per day. */ oncore_check_leap_sec(instance); /* * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn. */ if (instance->shmem && !instance->shmem_bad_Ea && instance->shmem_Posn && (instance->site_survey == ONCORE_SS_DONE)) oncore_shmem_get_3D(instance); if (!instance->traim) /* NO traim, no BnEnHn, go get tick */ oncore_get_timestamp(instance, instance->offset, instance->offset); } /* Almanac Status */ static void oncore_msg_Bd( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(len); oncore_log_f(instance, LOG_NOTICE, "Bd: Almanac %s, week = %d, t = %d, %d SVs: %d", ((buf[4]) ? "LOADED" : "(NONE)"), buf[5], buf[6], buf[7], w32(&buf[8])); } /* get leap-second warning message */ /* * @@Bj does NOT behave as documented in current Oncore firmware. * It turns on the LEAP indicator when the data is set, and does not, * as documented, wait until the beginning of the month when the * leap second will occur. * Since this firmware bug will never be fixed in all the outstanding Oncore receivers * @@Bj is only called in June/December. */ static void oncore_msg_Bj( struct instance *instance, uint8_t *buf, size_t len ) { const char *cp; UNUSED_ARG(len); instance->saw_Bj = 1; switch(buf[4]) { case 1: instance->pp->leap = LEAP_ADDSECOND; cp = "Set pp.leap to LEAP_ADDSECOND"; break; case 2: instance->pp->leap = LEAP_DELSECOND; cp = "Set pp.leap to LEAP_DELSECOND"; break; case 0: default: instance->pp->leap = LEAP_NOWARNING; cp = "Set pp.leap to LEAP_NOWARNING"; break; } oncore_log(instance, LOG_NOTICE, cp); } static void oncore_msg_Bl( struct instance *instance, uint8_t *buf, size_t len ) { int subframe, valid, page, i, j, tow; int day_now, day_lsf; const char *cp; enum { WARN_NOT_YET, WARN_0, WARN_PLUS, WARN_MINUS } warn; UNUSED_ARG(len); day_now = day_lsf = 0; cp = NULL; /* keep gcc happy */ subframe = buf[6] & 017; valid = (buf[6] >> 4) & 017; page = buf[7]; if ((!instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 4 && page == 18 && valid == 10)) { instance->Bl.dt_ls = buf[32]; instance->Bl.WN_lsf = buf[33]; instance->Bl.DN_lsf = buf[34]; instance->Bl.dt_lsf = buf[35]; instance->Bl.lsf_flg++; } if ((instance->Bl.lsf_flg && !instance->Bl.wn_flg) && (subframe == 1 && valid == 10)) { i = (buf[7+7]<<8) + buf[7+8]; instance->Bl.WN = i >> 6; tow = (buf[7+4]<<16) + (buf[7+5]<<8) + buf[7+6]; tow >>= 7; tow = tow & 0377777; tow <<= 2; instance->Bl.DN = tow/57600L + 1; instance->Bl.wn_flg++; } if (instance->Bl.wn_flg && instance->Bl.lsf_flg) { instance->Bl.wn_flg = instance->Bl.lsf_flg = 0; oncore_cmd_Bl[2] = 0; oncore_sendmsg(instance, oncore_cmd_Bl, sizeof oncore_cmd_Bl); oncore_cmd_Bl[2] = 1; i = instance->Bl.WN&01400; instance->Bl.WN_lsf |= i; /* have everything I need, doit */ i = (instance->Bl.WN_lsf - instance->Bl.WN); if (i < 0) i += 1024; day_now = instance->Bl.DN; day_lsf = 7*i + instance->Bl.DN_lsf; /* ignore if in past or more than a month in future */ warn = WARN_NOT_YET; if (day_lsf >= day_now && day_lsf - day_now < 32) { /* if < 28d, doit, if 28-31, ck day-of-month < 20 (not at end of prev month) */ if (day_lsf - day_now < 28 || instance->BEHa[5] < 20) { i = instance->Bl.dt_lsf - instance->Bl.dt_ls; switch (i) { case -1: warn = WARN_MINUS; break; case 0: warn = WARN_0; break; case 1: warn = WARN_PLUS; break; default: /* huh? */ break; } } } switch (warn) { case WARN_0: case WARN_NOT_YET: instance->peer->leap = LEAP_NOWARNING; cp = "Set peer.leap to LEAP_NOWARNING"; break; case WARN_MINUS: instance->peer->leap = LEAP_DELSECOND; cp = "Set peer.leap to LEAP_DELSECOND"; break; case WARN_PLUS: instance->peer->leap = LEAP_ADDSECOND; cp = "Set peer.leap to LEAP_ADDSECOND"; break; default: /* huh? */ break; } oncore_log(instance, LOG_NOTICE, cp); i = instance->Bl.dt_lsf-instance->Bl.dt_ls; if (i) { j = (i >= 0) ? i : -i; /* abs(i) */ oncore_log_f(instance, LOG_NOTICE, "see Leap_Second (%c%d) in %d days", ((i >= 0) ? '+' : '-'), j, day_lsf-day_now); } } /* * Reg only wants the following output for "deeper" driver debugging. * See Classic Bugs 2142 and Bug 1866 */ #if 0 oncore_log_f(instance, LOG_DEBUG, "dt_ls = %d dt_lsf = %d WN = %d DN = %d WN_lsf = %d DNlsf = %d wn_flg = %d lsf_flg = %d Bl_day = %d", instance->Bl.dt_ls, instance->Bl.dt_lsf, instance->Bl.WN, instance->Bl.DN, instance->Bl.WN_lsf, instance->Bl.DN_lsf, instance->Bl.wn_flg, instance->Bl.lsf_flg, instance->Bl.Bl_day); #endif } static void oncore_msg_BnEnHn( struct instance *instance, uint8_t *buf, size_t len ) { long dt1, dt2; if (instance->o_state != ONCORE_RUN) return; if (instance->traim_delay) { /* flag that @@Bn/@@En/Hn returned */ instance->traim_ck = 1; instance->traim_delay = 0; oncore_log(instance, LOG_NOTICE, "ONCORE: Detected TRAIM, TRAIM = ON"); oncore_set_traim(instance); } memcpy(instance->BEHn, buf, (size_t) len); /* Bn or En or Hn */ if (!instance->traim) /* BnEnHn will be turned off in any case */ return; /* If Time RAIM doesn't like it, don't trust it */ if (buf[2] == 'H') { if (instance->BEHn[6]) { /* bad TRAIM */ oncore_log(instance, LOG_WARNING, "BAD TRAIM"); return; } dt1 = instance->saw_tooth + instance->offset; /* dt this time step */ instance->saw_tooth = (int8_t) instance->BEHn[14]; /* update for next time Hn[14] */ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */ } else { if (instance->BEHn[21]) /* bad TRAIM */ return; dt1 = instance->saw_tooth + instance->offset; /* dt this time step */ instance->saw_tooth = (int8_t) instance->BEHn[25]; /* update for next time Bn[25], En[25] */ dt2 = instance->saw_tooth + instance->offset; /* dt next time step */ } oncore_get_timestamp(instance, dt1, dt2); } /* Here for @@Ca, @@Fa and @@Ia messages */ /* These are Self test Commands for 6, 8, and 12 chan receivers. * There are good reasons NOT to do a @@Ca, @@Fa or @@Ia command with the ONCORE. * It was found that under some circumstances the following * command would fail if issued immediately after the return from the * @@Fa, but a 2sec delay seemed to fix things. Since simply calling * sleep(2) is wasteful, and may cause trouble for some OS's, repeating * itimer, we set a flag, and test it at the next POLL. If it hasn't * been cleared, we reissue the @@Cj that is issued below. * Note that we do a @@Cj at the beginning, and again here. * The first is to get the info, the 2nd is just used as a safe command * after the @@Fa for all Oncores (and it was in this posn in the * original code). */ static void oncore_msg_CaFaIa( struct instance *instance, uint8_t *buf, size_t len ) { int i; UNUSED_ARG(len); if (instance->o_state == ONCORE_TEST_SENT) { enum antenna_state antenna; instance->timeout = 0; #ifdef ONCORE_VERBOSE_SELF_TEST if (debug > 2) { /* SPECIAL DEBUG */ if (buf[2] == 'I') oncore_log_f(instance, LOG_DEBUG, ">>@@%ca %x %x %x", buf[2], buf[4], buf[5], buf[6]); else oncore_log_f(instance, LOG_DEBUG, ">>@@%ca %x %x", buf[2], buf[4], buf[5]); } #endif antenna = (buf[4] & 0xc0) >> 6; buf[4] &= ~0xc0; i = buf[4] || buf[5]; if (buf[2] == 'I') i = i || buf[6]; if (i) { if (buf[2] == 'I') oncore_log_f(instance, LOG_ERR, "self test failed: result %02x %02x %02x", buf[4], buf[5], buf[6]); else oncore_log_f(instance, LOG_ERR, "self test failed: result %02x %02x", buf[4], buf[5]); oncore_log(instance, LOG_ERR, "ONCORE: self test failed, shutting down driver"); refclock_report(instance->peer, CEVNT_FAULT); oncore_shutdown(instance->peer->procptr); return; } /* report the current antenna state */ oncore_antenna_report(instance, antenna); instance->o_state = ONCORE_INIT; oncore_log(instance, LOG_NOTICE, "state = ONCORE_INIT"); instance->timeout = 4; oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); } } /* * Demultiplex the almanac into shmem */ static void oncore_msg_Cb( struct instance *instance, uint8_t *buf, size_t len ) { int i; if (instance->shmem == NULL) return; if (buf[4] == 5 && buf[5] > 0 && buf[5] < 26) i = buf[5]; else if (buf[4] == 4 && buf[5] <= 5) i = buf[5] + 24; else if (buf[4] == 4 && buf[5] <= 10) i = buf[5] + 23; else if (buf[4] == 4 && buf[5] == 25) i = 34; else { oncore_log(instance, LOG_NOTICE, "Cb: Response is NO ALMANAC"); return; } i *= 36; instance->shmem[(int)instance->shmem_Cb + i + 2]++; memcpy(instance->shmem + instance->shmem_Cb + i + 3, buf, (size_t)(len + 3)); #ifdef ONCORE_VERBOSE_MSG_CB oncore_log_f(instance, LOG_DEBUG, "See Cb [%d,%d]", buf[4], buf[5]); #endif } /* * Set to Factory Defaults (Reasonable for UT w/ no Battery Backup * not so for VP (eeprom) or any unit with a battery */ static void oncore_msg_Cf( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(buf); UNUSED_ARG(len); if (instance->o_state == ONCORE_RESET_SENT) { oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */ /* Reset set VP to IDLE */ instance->o_state = ONCORE_TEST_SENT; oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT"); oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj)); } } /* * This is the Grand Central Station for the Preliminary Initialization. * Once done here we move on to oncore_msg_BaEaHa for final Initialization and Running. * * We do an @@Cj whenever we need a safe command for all Oncores. * The @@Cj gets us back here where we can switch to the next phase of setup. * * o Once at the very beginning (in start) to get the Model number. * This info is printed, but no longer used. * o Again after we have determined the number of Channels in the receiver. * o And once later after we have done a reset and test, (which may hang), * as we are about to initialize the Oncore and start it running. * o We have one routine below for each case. */ static void oncore_msg_Cj( struct instance *instance, uint8_t *buf, size_t len ) { int mode; memcpy(instance->Cj, buf, len); instance->timeout = 0; if (instance->o_state == ONCORE_CHECK_ID) { oncore_msg_Cj_id(instance, buf, len); oncore_chan_test(instance); } else if (instance->o_state == ONCORE_HAVE_CHAN) { mode = instance->init_type; if (mode == 3 || mode == 4) { /* Cf will return here to check for TEST */ instance->o_state = ONCORE_RESET_SENT; oncore_log(instance, LOG_NOTICE, "state = ONCORE_RESET_SENT"); oncore_sendmsg(instance, oncore_cmd_Cf, sizeof(oncore_cmd_Cf)); } else { instance->o_state = ONCORE_TEST_SENT; oncore_log(instance, LOG_NOTICE, "state = ONCORE_TEST_SENT"); } } if (instance->o_state == ONCORE_TEST_SENT) { if (instance->chan == 6) oncore_sendmsg(instance, oncore_cmd_Ca, sizeof(oncore_cmd_Ca)); else if (instance->chan == 8) oncore_sendmsg(instance, oncore_cmd_Fa, sizeof(oncore_cmd_Fa)); else if (instance->chan == 12) oncore_sendmsg(instance, oncore_cmd_Ia, sizeof(oncore_cmd_Ia)); } else if (instance->o_state == ONCORE_INIT) oncore_msg_Cj_init(instance, buf, len); } /* The information on determining a Oncore 'Model', viz VP, UT, etc, from * the Model Number comes from "Richard M. Hambly" * and from Motorola. Until recently Rick was the only source of * this information as Motorola didn't give the information out. * * Determine the Type from the Model #, this determines #chan and if TRAIM is * available. * * The Information from this routine is NO LONGER USED. * The RESULTS are PRINTED, BUT NOT USED, and the routine COULD BE DELETED */ static void oncore_msg_Cj_id( struct instance *instance, uint8_t *buf, size_t len ) { char *cp2, Model[21]; const char *cp, *cp1; UNUSED_ARG(buf); UNUSED_ARG(len); /* Write Receiver ID message to clockstats file */ instance->Cj[294] = '\0'; for (cp= (char *)instance->Cj; cp< (char *) &instance->Cj[294]; ) { char *cpw = strchr(cp, '\r'); if (!cpw) cpw = (char *)&instance->Cj[294]; *cpw = '\0'; oncore_log(instance, LOG_NOTICE, cp); *cpw = '\r'; cp = cpw+2; } /* next, the Firmware Version and Revision numbers */ instance->version = (unsigned int)atoi((char *) &instance->Cj[83]); instance->revision = (unsigned int)atoi((char *) &instance->Cj[111]); /* from model number decide which Oncore this is, and then the number of channels */ for (cp= (char *) &instance->Cj[160]; *cp == ' '; cp++) /* start right after 'Model #' */ ; cp1 = cp; cp2 = Model; for (; !isspace((unsigned char)*cp) && cp-cp1 < 20; cp++, cp2++) *cp2 = *cp; *cp2 = '\0'; cp = 0; if (!strncmp(Model, "PVT6", (size_t) 4)) { cp = "PVT6"; instance->model = ONCORE_PVT6; } else if (Model[0] == 'A') { cp = "Basic"; instance->model = ONCORE_BASIC; } else if (Model[0] == 'B' || !strncmp(Model, "T8", (size_t) 2)) { cp = "VP"; instance->model = ONCORE_VP; } else if (Model[0] == 'P') { cp = "M12"; instance->model = ONCORE_M12; } else if (Model[0] == 'R' || Model[0] == 'D' || Model[0] == 'S') { if (Model[5] == 'N') { cp = "GT"; instance->model = ONCORE_GT; } else if ((Model[1] == '3' || Model[1] == '4') && Model[5] == 'G') { cp = "GT+"; instance->model = ONCORE_GTPLUS; } else if ((Model[1] == '5' && Model[5] == 'U') || (Model[1] == '1' && Model[5] == 'A')) { cp = "UT"; instance->model = ONCORE_UT; } else if (Model[1] == '5' && Model[5] == 'G') { cp = "UT+"; instance->model = ONCORE_UTPLUS; } else if (Model[1] == '6' && Model[5] == 'G') { cp = "SL"; instance->model = ONCORE_SL; } else { cp = "Unknown"; instance->model = ONCORE_UNKNOWN; } } else { cp = "Unknown"; instance->model = ONCORE_UNKNOWN; } /* use MODEL to set CHAN and TRAIM and possibly zero SHMEM */ oncore_log_f(instance, LOG_INFO, "This looks like an Oncore %s with version %u.%u firmware.", cp, instance->version, instance->revision); instance->chan_id = 8; /* default */ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6) instance->chan_id = 6; else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS) instance->chan_id = 8; else if (instance->model == ONCORE_M12) instance->chan_id = 12; instance->traim_id = 0; /* default */ if (instance->model == ONCORE_BASIC || instance->model == ONCORE_PVT6) instance->traim_id = 0; else if (instance->model == ONCORE_VP || instance->model == ONCORE_UT || instance->model == ONCORE_UTPLUS) instance->traim_id = 1; else if (instance->model == ONCORE_M12) instance->traim_id = -1; oncore_log_f(instance, LOG_INFO, "Channels = %d, TRAIM = %s", instance->chan_id, ((instance->traim_id < 0) ? "UNKNOWN" : (instance->traim_id > 0) ? "ON" : "OFF")); } /* OK, know type of Oncore, have possibly reset it, and have tested it. * We know the number of channels. * We will determine whether we have TRAIM before we actually start. * Now initialize. */ static void oncore_msg_Cj_init( struct instance *instance, uint8_t *buf, size_t len ) { uint8_t Cmd[20]; int mode; UNUSED_ARG(buf); UNUSED_ARG(len); /* The M12 with 1.3 or 2.0 Firmware, loses track of all Satellites and has to * start again if we go from 0D -> 3D, then loses them again when we * go from 3D -> 0D. We do this to get a @@Ea message for SHMEM. * For NOW we will turn this aspect of filling SHMEM off for the M12 */ if (instance->chan == 12) { instance->shmem_bad_Ea = 1; oncore_log_f(instance, LOG_NOTICE, "*** SHMEM partially enabled for ONCORE M12 s/w v%u.%u ***", instance->version, instance->revision); } oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Return to Posn Fix mode */ oncore_sendmsg(instance, oncore_cmd_Bb, sizeof(oncore_cmd_Bb)); /* turn on for shmem (6/8/12) */ oncore_sendmsg(instance, oncore_cmd_Ek, sizeof(oncore_cmd_Ek)); /* turn off (VP) */ oncore_sendmsg(instance, oncore_cmd_Aw, sizeof(oncore_cmd_Aw)); /* UTC time (6/8/12) */ oncore_sendmsg(instance, oncore_cmd_AB, sizeof(oncore_cmd_AB)); /* Appl type static (VP) */ oncore_sendmsg(instance, oncore_cmd_Be, sizeof(oncore_cmd_Be)); /* Tell us the Almanac for shmem (6/8/12) */ oncore_sendmsg(instance, oncore_cmd_Bd, sizeof(oncore_cmd_Bd)); /* Tell us when Almanac changes */ mode = instance->init_type; /* If there is Position input in the Config file * and mode = (1,3) set it as posn hold posn, goto 0D mode. * or mode = (2,4) set it as INITIAL position, and do Site Survey. */ if (instance->posn_set) { oncore_log(instance, LOG_INFO, "Setting Posn from input data"); oncore_set_posn(instance); /* this should print posn indirectly through the As cmd */ } else /* must issue an @@At here to check on 6/8 Position Hold, set_posn would have */ if (instance->chan != 12) oncore_sendmsg(instance, oncore_cmd_Atx, sizeof(oncore_cmd_Atx)); if (mode != 0) { /* cable delay in ns */ memcpy(Cmd, oncore_cmd_Az, (size_t) sizeof(oncore_cmd_Az)); w32_buf(&Cmd[-2+4], (int)instance->delay); oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Az)); /* 6,8,12 */ /* PPS offset in ns */ memcpy(Cmd, oncore_cmd_Ay, (size_t) sizeof(oncore_cmd_Ay)); /* some have it, some don't */ w32_buf(&Cmd[-2+4], instance->offset); /* will check for hw response */ oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ay)); /* Satellite mask angle */ if (instance->Ag != 0xff) { /* will have 0xff in it if not set by user */ memcpy(Cmd, oncore_cmd_Ag, (size_t) sizeof(oncore_cmd_Ag)); Cmd[-2+4] = instance->Ag; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ag)); } } /* 6, 8 12 chan - Position/Status/Data Output Message, 1/s * now we're really running * these were ALL started in the chan test, * However, if we had mode=3,4 then commands got turned off, so we turn * them on again here just in case */ if (instance->chan == 6) { /* start 6chan, kill 8,12chan commands, possibly testing VP in 6chan mode */ oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0)); oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0)); oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0)); oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba )); } else if (instance->chan == 8) { /* start 8chan, kill 6,12chan commands */ oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0)); oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0)); oncore_sendmsg(instance, oncore_cmd_Ha0, sizeof(oncore_cmd_Ha0)); oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea )); } else if (instance->chan == 12){ /* start 12chan, kill 6,12chan commands */ oncore_sendmsg(instance, oncore_cmd_Ba0, sizeof(oncore_cmd_Ba0)); oncore_sendmsg(instance, oncore_cmd_Bn0, sizeof(oncore_cmd_Bn0)); oncore_sendmsg(instance, oncore_cmd_Ea0, sizeof(oncore_cmd_Ea0)); oncore_sendmsg(instance, oncore_cmd_En0, sizeof(oncore_cmd_En0)); oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha )); oncore_cmd_Gc[2] = (uint8_t)((instance->pps_control < 0) ? 1 : instance->pps_control); oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* PPS off/continuous/Tracking 1+sat/TRAIM */ } instance->count = 1; instance->o_state = ONCORE_ALMANAC; oncore_log(instance, LOG_NOTICE, "state = ONCORE_ALMANAC"); } /* 12chan position */ static void oncore_msg_Ga( struct instance *instance, uint8_t *buf, size_t len ) { long lat, lon, ht; double Lat, Lon, Ht; UNUSED_ARG(len); lat = buf_w32(&buf[4]); lon = buf_w32(&buf[8]); ht = buf_w32(&buf[12]); /* GPS ellipsoid */ Lat = lat; Lon = lon; Ht = ht; Lat /= 3600000; Lon /= 3600000; Ht /= 100; oncore_log_f(instance, LOG_NOTICE, "Ga Posn Lat = %.7f, Lon = %.7f, Ht = %.2f", Lat, Lon, Ht); instance->ss_lat = lat; instance->ss_long = lon; instance->ss_ht = ht; oncore_print_posn(instance); } /* 12 chan time/date */ static void oncore_msg_Gb( struct instance *instance, uint8_t *buf, size_t len ) { const char * gmts; int mo, d, y, h, m, s, gmth, gmtm; UNUSED_ARG(len); mo = buf[4]; d = buf[5]; y = 256*buf[6]+buf[7]; h = buf[8]; m = buf[9]; s = buf[10]; gmts = ((buf[11] == 0) ? "+" : "-"); gmth = buf[12]; gmtm = buf[13]; oncore_log_f(instance, LOG_NOTICE, "Date/Time set to: %02d-%02d-%02dT%2d:%02d:%02d%s%02d%02d", y, mo, d, h, m, s, gmts, gmth, gmtm); } /* Response to PPS Control message (M12 and M12+T only ) */ static void oncore_msg_Gc( struct instance *instance, uint8_t *buf, size_t len ) { const char *tbl[] = {"OFF", "ON", "SATELLITE", "TRAIM" }; UNUSED_ARG(len); instance->pps_control_msg_seen = 1; oncore_log_f(instance, LOG_INFO, "PPS Control set to %s", tbl[buf[4]]); } /* Leap Second for M12, gives all info from satellite message */ /* also in UT v3.0 */ static void oncore_msg_Gj( struct instance *instance, uint8_t *buf, size_t len ) { static const char * insrem[2] = { "removed", "inserted" }; int dt; const char *cp; UNUSED_ARG(len); instance->saw_Gj = 1; /* flag, saw_Gj, don't need to try Bj in check_leap */ /* print the message to verify whats there */ dt = buf[5] - buf[4]; oncore_log_f(instance, LOG_INFO, "Leap Sec Msg: %d %d %d %d %d %d %d %d %d %d", buf[4], buf[5], 256 * buf[6] + buf[7], buf[8], buf[9], buf[10], (buf[14] + 256 * (buf[13] + 256 * (buf[12] + 256 * buf[11]))), buf[15], buf[16], buf[17]); /* There seems to be eternal confusion about when a leap second * takes place. It's the second *before* the new TAI offset * becomes effective. But since the ONCORE receiver tells us * just that, we would have to do some time/date calculations to * get the actual leap second -- that is, the one that is * deleted or inserted. * * Going through all this for a simple log is probably overkill, * so for fixing bug#1050 the message output is changed to * reflect the fact that it tells the second after the leap * second. */ if (dt) oncore_log_f(instance, LOG_NOTICE, "Leap second %s (%d) before %04u-%02u-%02u/%02u:%02u:%02u", insrem[(dt > 0)], dt, 256u * buf[6] + buf[7], buf[8], buf[9], buf[15], buf[16], buf[17]); /* Only raise warning within a month of the leap second */ instance->pp->leap = LEAP_NOWARNING; cp = "Set pp.leap to LEAP_NOWARNING"; if (buf[6] == instance->BEHa[6] && buf[7] == instance->BEHa[7] && /* year */ buf[8] == instance->BEHa[4]) { /* month */ if (dt) { if (dt < 0) { instance->pp->leap = LEAP_DELSECOND; cp = "Set pp.leap to LEAP_DELSECOND"; } else { instance->pp->leap = LEAP_ADDSECOND; cp = "Set pp.leap to LEAP_ADDSECOND"; } } } oncore_log(instance, LOG_INFO, cp); } /* Power on failure */ static void oncore_msg_Sz( struct instance *instance, uint8_t *buf, size_t len ) { UNUSED_ARG(buf); UNUSED_ARG(len); if (instance && instance->peer) { oncore_log(instance, LOG_ERR, "Oncore: System Failure at Power On"); oncore_shutdown(instance->peer->procptr); } } /************** Small Subroutines ***************/ static void oncore_antenna_report( struct instance *instance, enum antenna_state new_state) { const char *cp; if (instance->ant_state == new_state) return; switch (new_state) { case ONCORE_ANTENNA_OK: cp = "GPS antenna: OK"; break; case ONCORE_ANTENNA_OC: cp = "GPS antenna: short (overcurrent)"; break; case ONCORE_ANTENNA_UC: cp = "GPS antenna: open (not connected)"; break; case ONCORE_ANTENNA_NV: cp = "GPS antenna: short (no voltage)"; break; default: cp = "GPS antenna: ?"; break; } instance->ant_state = new_state; oncore_log(instance, LOG_NOTICE, cp); } static void oncore_chan_test( struct instance *instance ) { /* subroutine oncore_Cj_id has determined the number of channels from the * model number of the attached oncore. This is not always correct since * the oncore could have non-standard firmware. Here we check (independently) by * trying a 6, 8, and 12 chan command, and see which responds. * Caution: more than one CAN respond. * * This #chan is used by the code rather than that calculated from the model number. */ instance->o_state = ONCORE_CHECK_CHAN; oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_CHAN"); instance->count3 = 1; oncore_sendmsg(instance, oncore_cmd_Ba, sizeof(oncore_cmd_Ba)); oncore_sendmsg(instance, oncore_cmd_Ea, sizeof(oncore_cmd_Ea)); oncore_sendmsg(instance, oncore_cmd_Ha, sizeof(oncore_cmd_Ha)); } /* check for a GOOD Almanac, have we got one yet? */ static void oncore_check_almanac( struct instance *instance ) { if (instance->chan == 6) { instance->rsm.bad_almanac = instance->BEHa[64]&0x1; instance->rsm.bad_fix = instance->BEHa[64]&0x52; } else if (instance->chan == 8) { instance->rsm.bad_almanac = instance->BEHa[72]&0x1; instance->rsm.bad_fix = instance->BEHa[72]&0x52; } else if (instance->chan == 12) { int bits1, bits2, bits3; bits1 = (instance->BEHa[129]>>5) & 0x7; /* actually Ha */ bits2 = instance->BEHa[130]; instance->rsm.bad_almanac = (bits2 & 0x80); instance->rsm.bad_fix = (bits2 & 0x8) || (bits1 == 0x2); /* too few sat Bad Geom */ bits3 = instance->BEHa[141]; /* UTC parameters */ if (!instance->count5_set && (bits3 & 0xC0)) { instance->count5 = 4; /* was 2 [Classic Bug 1766] */ instance->count5_set = 1; } #ifdef ONCORE_VERBOSE_CHECK_ALMANAC oncore_log_f(instance, LOG_DEBUG, "DEBUG BITS: (%x %x), (%x %x %x), %x %x %x %x %x", instance->BEHa[129], instance->BEHa[130], bits1, bits2, bits3, instance->mode == MODE_0D, instance->mode == MODE_2D, instance->mode == MODE_3D, instance->rsm.bad_almanac, instance->rsm.bad_fix); #endif } } /* check the antenna for changes (did it get unplugged?) */ static void oncore_check_antenna( struct instance *instance ) { enum antenna_state antenna; /* antenna state */ antenna = instance->ant_state; if (instance->chan == 12) antenna = (instance->BEHa[130] & 0x6 ) >> 1; else antenna = (instance->BEHa[37] & 0xc0) >> 6; /* prob unset 6, set GT, UT unset VP */ oncore_antenna_report (instance, antenna); } /* * Check the leap second status once per day. * * Note that the ONCORE firmware for the Bj command is wrong at * least in the VP. * It starts advertising a LEAP SECOND as soon as the GPS satellite * data message (page 18, subframe 4) is updated to a date in the * future, and does not wait for the month that it will occur. * The event will usually be advertised several months in advance. * Since there is a one bit flag, there is no way to tell if it is * this month, or when... * * As such, we have the workaround below, of only checking for leap * seconds with the Bj command in June/December. * * The Gj command gives more information, and we can tell in which * month to apply the correction. * * Note that with the VP we COULD read the raw data message, and * interpret it ourselves, but since this is specific to this receiver * only, and the above workaround is adequate, we don't bother. */ static void oncore_check_leap_sec( struct instance *instance ) { oncore_cmd_Bl[2] = 1; /* just to be sure */ if (instance->Bj_day != instance->BEHa[5]) { /* do this 1/day */ instance->Bj_day = instance->BEHa[5]; if (instance->saw_Gj < 0) { /* -1 DON'T have Gj use Bj */ if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj)); oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl)); return; } if (instance->saw_Gj == 0) /* 0 is don't know if we have Gj */ instance->count4 = 1; oncore_sendmsg(instance, oncore_cmd_Gj, sizeof(oncore_cmd_Gj)); return; } /* Gj works for some 6/8 chan UT and the M12 */ /* if no response from Gj in 5 sec, we try Bj */ /* which isn't implemented in all the GT/UT either */ if (instance->count4) { /* delay, waiting for Gj response */ if (instance->saw_Gj == 1) instance->count4 = 0; else if (instance->count4++ > 5) { /* delay, waiting for Gj response */ instance->saw_Gj = -1; /* didn't see it, will use Bj */ instance->count4 = 0; if ((instance->BEHa[4] == 6) || (instance->BEHa[4] == 12)) { oncore_sendmsg(instance, oncore_cmd_Bj, sizeof(oncore_cmd_Bj)); oncore_sendmsg(instance, oncore_cmd_Bl, sizeof(oncore_cmd_Bl)); } } } } /* check the message checksum, * buf points to START of message ( @@ ) * len is length WITH CR/LF. */ static int oncore_checksum_ok( uint8_t *buf, int len ) { int i, j; j = 0; for (i = 2; i < len-3; i++) j ^= buf[i]; return(j == buf[len-3]); } static void oncore_compute_dH( struct instance *instance ) { int GPS, MSL; /* Here calculate dH = GPS - MSL for output message */ /* also set Altitude Hold mode if GT */ instance->have_dH = 1; if (instance->chan == 12) { GPS = buf_w32(&instance->BEHa[39]); MSL = buf_w32(&instance->BEHa[43]); } else { GPS = buf_w32(&instance->BEHa[23]); MSL = buf_w32(&instance->BEHa[27]); } instance->dH = GPS - MSL; instance->dH /= 100.; /* if MSL is not set, the calculation is meaningless */ if (MSL) /* not set ! */ oncore_log_f(instance, LOG_INFO, "dH = (GPS - MSL) = %.2fm", instance->dH); } /* * try loading Almanac from shmem (where it was copied from shmem_old */ static void oncore_load_almanac( struct instance *instance ) { uint8_t *cp, Cmd[20]; int n; struct timespec ts; struct tm tmbuf, *tm; if (!instance->shmem) return; #ifndef ONCORE_VERBOSE_LOAD_ALMANAC for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2)); cp += (n + 3)) { if (!strncmp((char *) cp, "@@Cb", 4) && oncore_checksum_ok(cp, 33) && (*(cp+4) == 4 || *(cp+4) == 5)) { IGNORE(write(instance->ttyfd, cp, (size_t)n)); oncore_print_Cb(instance, cp); } } #else /* ONCORE_VERBOSE_LOAD_ALMANAC follows */ for (cp = instance->shmem + 4; (n = 256 * (*(cp-3)) + *(cp-2)); cp += (n+3)) { oncore_log_f(instance, LOG_DEBUG, "See %c%c%c%c %d", *(cp), *(cp+1), *(cp+2), *(cp+3), *(cp+4)); if (!strncmp(cp, "@@Cb", 4)) { oncore_print_Cb(instance, cp); if (oncore_checksum_ok(cp, 33)) { if (*(cp+4) == 4 || *(cp+4) == 5) { oncore_log(instance, LOG_DEBUG, "GOOD SF"); write(instance->ttyfd, cp, n); } else oncore_log(instance, LOG_DEBUG, "BAD SF"); } else oncore_log(instance, LOG_DEBUG, "BAD CHECKSUM"); } } #endif /* Must load position and time or the Almanac doesn't do us any good */ if (!instance->posn_set) { /* if we input a posn use it, else from SHMEM */ oncore_log(instance, LOG_NOTICE, "Loading Posn from SHMEM"); for (cp=instance->shmem+4; (n = 256*(*(cp-3)) + *(cp-2)); cp+=(n+3)) { if ((instance->chan == 6 && (!strncmp((char *) cp, "@@Ba", 4) && oncore_checksum_ok(cp, 68))) || (instance->chan == 8 && (!strncmp((char *) cp, "@@Ea", 4) && oncore_checksum_ok(cp, 76))) || (instance->chan == 12 && (!strncmp((char *) cp, "@@Ha", 4) && oncore_checksum_ok(cp, 154)))) { int ii, jj, kk; instance->posn_set = 1; ii = buf_w32(cp + 15); jj = buf_w32(cp + 19); kk = buf_w32(cp + 23); #ifdef ONCORE_VERBOSE_LOAD_ALMANAC oncore_log_f(instance, LOG_DEBUG, "SHMEM posn = %ld (%d, %d, %d)", (long)(cp-instance->shmem), ii, jj, kk); #endif if (ii != 0 || jj != 0 || kk != 0) { /* phk asked for this test */ instance->ss_lat = ii; instance->ss_long = jj; instance->ss_ht = kk; } } } } oncore_set_posn(instance); /* and set time to time from Computer clock */ clock_gettime(CLOCK_REALTIME, &ts); tm = gmtime_r((const time_t *) &ts.tv_sec, &tmbuf); #ifdef ONCORE_VERBOSE_LOAD_ALMANAC oncore_log_f(instance, LOG_DEBUG, "DATE %d %d %d, %d %d %d", 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); #endif if (instance->chan == 12) { memcpy(Cmd, oncore_cmd_Gb, (size_t) sizeof(oncore_cmd_Gb)); Cmd[-2+4] = tm->tm_mon + 1; Cmd[-2+5] = tm->tm_mday; Cmd[-2+6] = (1900+tm->tm_year)/256; Cmd[-2+7] = (1900+tm->tm_year)%256; Cmd[-2+8] = tm->tm_hour; Cmd[-2+9] = tm->tm_min; Cmd[-2+10] = tm->tm_sec; Cmd[-2+11] = 0; Cmd[-2+12] = 0; Cmd[-2+13] = 0; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Gb)); } else { /* First set GMT offset to zero */ oncore_sendmsg(instance, oncore_cmd_Ab, sizeof(oncore_cmd_Ab)); memcpy(Cmd, oncore_cmd_Ac, (size_t) sizeof(oncore_cmd_Ac)); Cmd[-2+4] = tm->tm_mon + 1; Cmd[-2+5] = tm->tm_mday; Cmd[-2+6] = (1900+tm->tm_year)/256; Cmd[-2+7] = (1900+tm->tm_year)%256; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ac)); memcpy(Cmd, oncore_cmd_Aa, (size_t) sizeof(oncore_cmd_Aa)); Cmd[-2+4] = tm->tm_hour; Cmd[-2+5] = tm->tm_min; Cmd[-2+6] = tm->tm_sec; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Aa)); } oncore_log(instance, LOG_INFO, "Setting Posn and Time after Loading Almanac"); } /* Almanac data input */ static void oncore_print_Cb( struct instance *instance, uint8_t *cp ) { #ifndef ONCORE_VERBOSE_CB UNUSED_ARG(instance); UNUSED_ARG(cp); #else int ii; char Msg[160], Msg2[10]; oncore_log_f(instance, LOG_DEBUG, "DEBUG: See: %c%c%c%c", *(cp), *(cp+1), *(cp+2), *(cp+3)); snprintf(Msg, sizeof(Msg), "DEBUG: Cb: [%d,%d]", *(cp+4), *(cp+5)); for (ii = 0; ii < 33; ii++) { snprintf(Msg2, sizeof(Msg2), " %d", *(cp+ii)); strlcat(Msg, Msg2, sizeof(Msg)); } oncore_log(instance, LOG_DEBUG, Msg); oncore_log_f(instance, LOG_DEBUG, "Debug: Cb: [%d,%d]", *(cp+4), *(cp+5)); #endif } #if 0 static void oncore_print_array( uint8_t *cp, int n ) { int jj, i, j, nn; nn = 0; printf("\nTOP\n"); jj = n/16; for (j=0; jss_long; if (lon < 0) { ew = 'W'; lon = -lon; } ns = 'N'; lat = instance->ss_lat; if (lat < 0) { ns = 'S'; lat = -lat; } hm = instance->ss_ht/100.; hft= hm/0.3048; xd = lat/3600000.; /* lat, lon in int msec arc, ht in cm. */ yd = lon/3600000.; oncore_log_f(instance, LOG_INFO, "Lat = %c %11.7fdeg, Long = %c %11.7fdeg, Alt = %5.2fm (%5.2fft) GPS", ns, xd, ew, yd, hm, hft); idx = xd; idy = yd; imx = lat%3600000; imy = lon%3600000; xm = imx/60000.; ym = imy/60000.; oncore_log_f(instance, LOG_INFO, "Lat = %c %3ddeg %7.4fm, Long = %c %3ddeg %8.5fm, Alt = %7.2fm (%7.2fft) GPS", ns, idx, xm, ew, idy, ym, hm, hft); imx = xm; imy = ym; is = lat%60000; xs = is/1000.; is = lon%60000; ys = is/1000.; oncore_log_f(instance, LOG_INFO, "Lat = %c %3ddeg %2dm %5.2fs, Long = %c %3ddeg %2dm %5.2fs, Alt = %7.2fm (%7.2fft) GPS", ns, idx, imx, xs, ew, idy, imy, ys, hm, hft); } /* * write message to Oncore. */ static void oncore_sendmsg( struct instance *instance, uint8_t *ptr, size_t len ) { int fd; uint8_t cs = 0; fd = instance->ttyfd; #ifdef ONCORE_VERBOSE_SENDMSG if (debug > 4) { /* SPECIAL DEBUG */ oncore_log_f(instance, LOG_DEBUG, "ONCORE: Send @@%c%c %d", ptr[0], ptr[1], (int)len); } #endif IGNORE(write(fd, "@@", (size_t) 2)); IGNORE(write(fd, ptr, len)); while (len--) cs ^= *ptr++; IGNORE(write(fd, &cs, (size_t) 1)); IGNORE(write(fd, "\r\n", (size_t) 2)); } static void oncore_set_posn( struct instance *instance ) { int mode; uint8_t Cmd[20]; /* Turn OFF position hold, it needs to be off to set position (for some units), will get set ON in @@Ea later */ if (instance->chan == 12) oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* (12) */ else { oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); /* (6/8) */ oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); /* (6/8) */ } mode = instance->init_type; if (mode != 0) { /* first set posn hold position */ memcpy(Cmd, oncore_cmd_As, (size_t) sizeof(oncore_cmd_As)); /* don't modify static variables */ w32_buf(&Cmd[-2+4], (int) instance->ss_lat); w32_buf(&Cmd[-2+8], (int) instance->ss_long); w32_buf(&Cmd[-2+12], (int) instance->ss_ht); Cmd[-2+16] = 0; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_As)); /* posn hold 3D posn (6/8/12) */ memcpy(Cmd, oncore_cmd_Au, (size_t) sizeof(oncore_cmd_Au)); w32_buf(&Cmd[-2+4], (int) instance->ss_ht); Cmd[-2+8] = 0; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Au)); /* altitude hold (6/8/12 not UT, M12T) */ /* next set current position */ if (instance->chan == 12) { memcpy(Cmd, oncore_cmd_Ga, (size_t) sizeof(oncore_cmd_Ga)); w32_buf(&Cmd[-2+4], (int) instance->ss_lat); w32_buf(&Cmd[-2+8], (int) instance->ss_long); w32_buf(&Cmd[-2+12],(int) instance->ss_ht); Cmd[-2+16] = 0; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ga)); /* 3d posn (12) */ } else { memcpy(Cmd, oncore_cmd_Ad, (size_t) sizeof(oncore_cmd_Ad)); w32_buf(&Cmd[-2+4], (int) instance->ss_lat); oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ad)); /* lat (6/8) */ memcpy(Cmd, oncore_cmd_Ae, (size_t) sizeof(oncore_cmd_Ae)); w32_buf(&Cmd[-2+4], (int) instance->ss_long); oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Ae)); /* long (6/8) */ memcpy(Cmd, oncore_cmd_Af, (size_t) sizeof(oncore_cmd_Af)); w32_buf(&Cmd[-2+4], (int) instance->ss_ht); Cmd[-2+8] = 0; oncore_sendmsg(instance, Cmd, sizeof(oncore_cmd_Af)); /* ht (6/8) */ } /* Finally, turn on position hold */ if (instance->chan == 12) oncore_sendmsg(instance, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); else oncore_sendmsg(instance, oncore_cmd_At1, sizeof(oncore_cmd_At1)); } } static void oncore_set_traim( struct instance *instance ) { if (instance->traim_in != -1) /* set in Input */ instance->traim = instance->traim_in; else instance->traim = (int8_t)instance->traim_ck; oncore_log_f(instance, LOG_INFO, "Input says TRAIM = %d", instance->traim_in); oncore_log_f(instance, LOG_INFO, "Model # says TRAIM = %d", instance->traim_id); oncore_log_f(instance, LOG_INFO, "Testing says TRAIM = %d", instance->traim_ck); oncore_log_f(instance, LOG_INFO, "Using TRAIM = %d", instance->traim); if (instance->traim_ck == 1 && instance->traim == 0) { /* if it should be off, and I turned it on during testing, then turn it off again */ if (instance->chan == 6) oncore_sendmsg(instance, oncore_cmd_Bnx, sizeof(oncore_cmd_Bnx)); else if (instance->chan == 8) oncore_sendmsg(instance, oncore_cmd_Enx, sizeof(oncore_cmd_Enx)); else { /* chan == 12 */ oncore_sendmsg(instance, oncore_cmd_Ge0, sizeof(oncore_cmd_Ge0)); oncore_sendmsg(instance, oncore_cmd_Hn0, sizeof(oncore_cmd_Hn0)); } } } /* * if SHMEM active, every 15s, steal one 'tick' to get 2D or 3D posn. */ static void oncore_shmem_get_3D( struct instance *instance ) { if (instance->pp->second%15 == 3) { /* start the sequence */ /* by changing mode */ instance->shmem_reset = 1; if (instance->chan == 12) { if (instance->shmem_Posn == 2) oncore_sendmsg(instance, oncore_cmd_Gd2, sizeof(oncore_cmd_Gd2)); /* 2D */ else oncore_sendmsg(instance, oncore_cmd_Gd0, sizeof(oncore_cmd_Gd0)); /* 3D */ } else { if (instance->saw_At) { /* out of 0D -> 3D mode */ oncore_sendmsg(instance, oncore_cmd_At0, sizeof(oncore_cmd_At0)); if (instance->shmem_Posn == 2) /* 3D -> 2D mode */ oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); } else oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); } } else if (instance->shmem_reset || (instance->mode != MODE_0D)) { instance->shmem_reset = 0; if (instance->chan == 12) oncore_sendmsg(instance, oncore_cmd_Gd1, sizeof(oncore_cmd_Gd1)); /* 0D */ else { if (instance->saw_At) { if (instance->mode == MODE_2D) /* 2D -> 3D or 0D mode */ oncore_sendmsg(instance, oncore_cmd_Av0, sizeof(oncore_cmd_Av0)); oncore_sendmsg(instance, oncore_cmd_At1, sizeof(oncore_cmd_At1)); /* to 0D mode */ } else oncore_sendmsg(instance, oncore_cmd_Av1, sizeof(oncore_cmd_Av1)); } } } /* * Here we do the Software SiteSurvey. * We have to average our own position for the Position Hold Mode * We use Heights from the GPS ellipsoid. * We check for the END of either HW or SW SiteSurvey. */ static void oncore_ss( struct instance *instance ) { double lat, lon, ht; if (instance->site_survey == ONCORE_SS_HW) { /* * Check to see if Hardware SiteSurvey has Finished. */ if ((instance->chan == 8 && !(instance->BEHa[37] & 0x20)) || (instance->chan == 12 && !(instance->BEHa[130] & 0x10))) { oncore_log(instance, LOG_INFO, "Now in 0D mode"); if (instance->chan == 12) oncore_sendmsg(instance, oncore_cmd_Gax, sizeof(oncore_cmd_Gax)); else oncore_sendmsg(instance, oncore_cmd_Asx, sizeof(oncore_cmd_Asx)); oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE"); instance->site_survey = ONCORE_SS_DONE; } } else { /* * Must be a Software Site Survey. */ if (instance->rsm.bad_fix) /* Not if poor geometry or less than 3 sats */ return; if (instance->mode != MODE_3D) /* Use only 3D Fixes */ return; instance->ss_lat += buf_w32(&instance->BEHa[15]); instance->ss_long += buf_w32(&instance->BEHa[19]); instance->ss_ht += buf_w32(&instance->BEHa[23]); /* GPS ellipsoid */ instance->ss_count++; if (instance->ss_count != POS_HOLD_AVERAGE) return; instance->ss_lat /= POS_HOLD_AVERAGE; instance->ss_long /= POS_HOLD_AVERAGE; instance->ss_ht /= POS_HOLD_AVERAGE; oncore_log_f(instance, LOG_NOTICE, "Surveyed posn: lat %.3f (mas) long %.3f (mas) ht %.3f (cm)", instance->ss_lat, instance->ss_long, instance->ss_ht); lat = instance->ss_lat/3600000.; lon = instance->ss_long/3600000.; ht = instance->ss_ht/100; oncore_log_f(instance, LOG_NOTICE, "Surveyed posn: lat %.7f (deg) long %.7f (deg) ht %.2f (m)", lat, lon, ht); oncore_set_posn(instance); oncore_log(instance, LOG_INFO, "Now in 0D mode"); oncore_log(instance, LOG_NOTICE, "SSstate = ONCORE_SS_DONE"); instance->site_survey = ONCORE_SS_DONE; } } static bool oncore_wait_almanac( struct instance *instance ) { if (instance->rsm.bad_almanac) { instance->counta++; if (instance->counta%5 == 0) oncore_log(instance, LOG_INFO, "Waiting for Almanac"); /* * If we get here (first time) then we don't have an almanac in memory. * Check if we have a SHMEM, and if so try to load whatever is there. */ if (!instance->almanac_from_shmem) { instance->almanac_from_shmem = 1; oncore_load_almanac(instance); } return true; } else { /* Here we have the Almanac, we will be starting the @@Bn/@@En/@@Hn commands, and can finally check for TRAIM. Again, we set a delay (5sec) and wait for things to settle down */ if (instance->chan == 6) oncore_sendmsg(instance, oncore_cmd_Bn, sizeof(oncore_cmd_Bn)); else if (instance->chan == 8) oncore_sendmsg(instance, oncore_cmd_En, sizeof(oncore_cmd_En)); else if (instance->chan == 12) { oncore_sendmsg(instance, oncore_cmd_Gc, sizeof(oncore_cmd_Gc)); /* 1PPS on, continuous */ oncore_sendmsg(instance, oncore_cmd_Ge, sizeof(oncore_cmd_Ge)); /* TRAIM on */ oncore_sendmsg(instance, oncore_cmd_Hn, sizeof(oncore_cmd_Hn)); /* TRAIM status 1/s */ } instance->traim_delay = 1; oncore_log(instance, LOG_NOTICE, "Have now loaded an ALMANAC"); instance->o_state = ONCORE_RUN; oncore_log(instance, LOG_NOTICE, "state = ONCORE_RUN"); } return false; } static void oncore_log ( struct instance *instance, int log_level, const char *msg ) { msyslog(log_level, "REFCLOCK: ONCORE[%d]: %s", instance->unit, msg); mprintf_clock_stats(instance->peer, "REFCLOCK: ONCORE[%d]: %s", instance->unit, msg); } static int oncore_log_f( struct instance * instance, int log_level, const char * fmt, ... ) { va_list ap; int rc; char msg[512]; va_start(ap, fmt); rc = mvsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); oncore_log(instance, log_level, msg); #ifdef ONCORE_VERBOSE_ONCORE_LOG instance->max_len = max(strlen(msg), instance->max_len); instance->max_count++; if (instance->max_count % 100 == 0) oncore_log_f(instance, LOG_INFO, "Max Message Length so far is %d", instance->max_len); #endif return rc; } ntpsec-1.1.0+dfsg1/ntpd/ntp_peer.c0000644000175000017500000005556213252364117016567 0ustar rlaagerrlaager/* * ntp_peer.c - management of data maintained for peer associations */ #include "config.h" #include #include #include "ntpd.h" #include "ntp_lists.h" #include "ntp_stdlib.h" /* * Table of valid association combinations * --------------------------------------- * * packet->mode * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST * ---------- | --------------------------------------------- * NO_PEER | e 1 0 1 1 1 * ACTIVE | e 1 1 0 0 0 * PASSIVE | e 1 e 0 0 0 * CLIENT | e 0 0 0 1 0 * SERVER | e 0 0 0 0 0 * BCAST | e 0 0 0 0 0 * BCLIENT | e 0 0 0 e 1 * * One point to note here: a packet in BCAST mode can potentially match * a peer in CLIENT mode, but we that is a special case and we check for * that early in the decision process. This avoids having to keep track * of what kind of associations are possible etc... We actually * circumvent that problem by requiring that the first b(m)roadcast * received after the change back to BCLIENT mode sets the clock. */ #define AM_MODES 7 /* number of rows and columns */ #define NO_PEER 0 /* action when no peer is found */ static int AM[AM_MODES][AM_MODES] = { /* packet->mode */ /* peer { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */ /* mode */ /*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL}, /*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_NOMATCH}, /*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, /*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT}, }; #define MATCH_ASSOC(x, y) AM[(x)][(y)] /* * These routines manage the allocation of memory to peer structures * and the maintenance of three data structures involving all peers: * * - peer_list is a single list with all peers, suitable for scanning * operations over all peers. * - peer_adr_hash is an array of lists indexed by hashed peer address. * - peer_aid_hash is an array of lists indexed by hashed associd. * * They also maintain a free list of peer structures, peer_free. * * The three main entry points are findpeer(), which looks for matching * peer structures in the peer list, newpeer(), which allocates a new * peer structure and adds it to the list, and unpeer(), which * demobilizes the association and deallocates the structure. */ /* * Peer hash tables */ static struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */ static int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */ static struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */ static int assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */ struct peer *peer_list; /* peer structures list */ static struct peer *peer_free; /* peer structures free list */ static int peer_free_count; /* count of free structures */ /* * Association ID. We initialize this value randomly, then assign a new * value every time an association is mobilized. */ static associd_t current_association_ID; /* association ID */ static associd_t initial_association_ID; /* association ID */ /* * Memory allocation watermarks. */ #define INIT_PEER_ALLOC 8 /* static preallocation */ #define INC_PEER_ALLOC 4 /* add N more when empty */ /* * Miscellaneous statistic counters which may be queried. */ static unsigned long peer_timereset; /* time stat counters zeroed */ static unsigned long findpeer_calls; /* calls to findpeer */ static unsigned long assocpeer_calls; /* calls to findpeerbyassoc */ static unsigned long peer_allocations; /* allocations from free list */ static unsigned long peer_demobilizations; /* structs freed to free list */ static int total_peer_structs; /* peer structs */ int peer_associations; /* mobilized associations */ static int peer_preempt; /* preemptable associations */ static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */ static struct peer * findexistingpeer_name(const char *, unsigned short, struct peer *, int); static struct peer * findexistingpeer_addr(sockaddr_u *, struct peer *, int); static void free_peer(struct peer *); static void getmorepeermem(void); static void peer_reset (struct peer *); static int score(struct peer *); static inline l_fp ntohl_fp(l_fp_w lfpw) { return lfpinit_u(ntohl(lfpw.l_ui), ntohl(lfpw.l_uf)); } /* * init_peer - initialize peer data structures and counters * * N.B. We use the random number routine in here. It had better be * initialized prior to getting here. */ void init_peer(void) { int i; /* * Initialize peer free list from static allocation. */ for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--) LINK_SLIST(peer_free, &init_peer_alloc[i], p_link); total_peer_structs = COUNTOF(init_peer_alloc); peer_free_count = COUNTOF(init_peer_alloc); /* * Initialize our first association ID */ do current_association_ID = ntp_random() & ASSOCID_MAX; while (!current_association_ID); initial_association_ID = current_association_ID; } /* * getmorepeermem - add more peer structures to the free list */ static void getmorepeermem(void) { int i; struct peer *peers; peers = emalloc_zero(INC_PEER_ALLOC * sizeof(*peers)); for (i = INC_PEER_ALLOC - 1; i >= 0; i--) LINK_SLIST(peer_free, &peers[i], p_link); total_peer_structs += INC_PEER_ALLOC; peer_free_count += INC_PEER_ALLOC; } static struct peer * findexistingpeer_name( const char * hostname, unsigned short hname_fam, struct peer * start_peer, int mode ) { struct peer *p; if (NULL == start_peer) p = peer_list; else p = start_peer->p_link; for (; p != NULL; p = p->p_link) if (p->hostname != NULL && (-1 == mode || p->hmode == mode) && (AF_UNSPEC == hname_fam || AF_UNSPEC == AF(&p->srcadr) || hname_fam == AF(&p->srcadr)) && !strcasecmp(p->hostname, hostname)) break; return p; } static struct peer * findexistingpeer_addr( sockaddr_u * addr, struct peer * start_peer, int mode ) { struct peer *peer; DPRINT(2, ("findexistingpeer_addr(%s, %s, %d)\n", sockporttoa(addr), (start_peer) ? sockporttoa(&start_peer->srcadr) : "NULL", mode)); /* * start_peer is included so we can locate instances of the * same peer through different interfaces in the hash table. * A match requires the same mode and remote * address. */ if (NULL == start_peer) peer = peer_hash[NTP_HASH_ADDR(addr)]; else peer = start_peer->adr_link; while (peer != NULL) { DPRINT(3, ("%s %s %d %d 0x%x ", sockporttoa(addr), sockporttoa(&peer->srcadr), mode, peer->hmode, (unsigned int)peer->cast_flags)); if ((-1 == mode || peer->hmode == mode) && ADDR_PORT_EQ(addr, &peer->srcadr)) { DPRINT(3, ("found.\n")); break; } DPRINT(3, ("\n")); peer = peer->adr_link; } return peer; } /* * findexistingpeer - search by address and return a pointer to a peer. */ struct peer * findexistingpeer( sockaddr_u * addr, const char * hostname, struct peer * start_peer, int mode) { if (hostname != NULL) return findexistingpeer_name(hostname, (addr == NULL) ? AF_UNSPEC : AF(addr), start_peer, mode); else return findexistingpeer_addr(addr, start_peer, mode); } /* * findpeer - find and return a peer match for a received datagram in * the peer_hash table. */ struct peer * findpeer( struct recvbuf *rbufp, int pkt_mode, int * action ) { struct peer * p; sockaddr_u * srcadr; unsigned int hash; struct pkt * pkt; l_fp pkt_org; findpeer_calls++; srcadr = &rbufp->recv_srcadr; hash = NTP_HASH_ADDR(srcadr); for (p = peer_hash[hash]; p != NULL; p = p->adr_link) { /* [Classic Bug 3072] ensure interface of peer matches */ if (p->dstadr != rbufp->dstadr) continue; /* ensure peer source address matches */ if (!ADDR_PORT_EQ(srcadr, &p->srcadr)) continue; /* If the association matching rules determine that this * is not a valid combination, then look for the next * valid peer association. */ *action = MATCH_ASSOC(p->hmode, pkt_mode); /* A response to our manycastclient solicitation might * be misassociated with an ephemeral peer already spun * for the server. If the packet's org timestamp * doesn't match the peer's, check if it matches the * ACST prototype peer's. If so it is a redundant * solicitation response, return AM_ERR to discard it. * [Classic Bug 1762] */ if (MODE_SERVER == pkt_mode && AM_PROCPKT == *action) { pkt = &rbufp->recv_pkt; pkt_org = ntohl_fp(pkt->org); if (p->org != pkt_org && findmanycastpeer(rbufp)) *action = AM_ERR; } /* If an error was returned, exit back right here. */ if (*action == AM_ERR) return NULL; /* If a match is found, we stop our search. */ if (*action != AM_NOMATCH) break; } /* If no matching association is found */ if (NULL == p) *action = MATCH_ASSOC(NO_PEER, pkt_mode); return p; } /* * findpeerbyassoc - find and return a peer using his association ID */ struct peer * findpeerbyassoc( associd_t assoc ) { struct peer *p; unsigned int hash; assocpeer_calls++; hash = assoc & NTP_HASH_MASK; for (p = assoc_hash[hash]; p != NULL; p = p->aid_link) if (assoc == p->associd) break; return p; } /* * clear_all - flush all time values for all associations */ void clear_all(void) { struct peer *p; /* * This routine is called when the clock is stepped, and so all * previously saved time values are untrusted. */ for (p = peer_list; p != NULL; p = p->p_link) if (!(MDF_TXONLY_MASK & p->cast_flags)) peer_clear(p, "STEP", false); DPRINT(1, ("clear_all: at %u\n", current_time)); } /* * score_all() - determine if an association can be demobilized */ int score_all( struct peer *peer /* peer structure pointer */ ) { struct peer *speer; int temp, tamp; int x; /* * This routine finds the minimum score for all preemptible * associations and returns > 0 if the association can be * demobilized. */ tamp = score(peer); temp = 100; for (speer = peer_list; speer != NULL; speer = speer->p_link) if (speer->cfg.flags & FLAG_PREEMPT) { x = score(speer); if (x < temp) temp = x; } DPRINT(1, ("score_all: at %u score %d min %d\n", current_time, tamp, temp)); if (tamp != temp) temp = 0; return temp; } /* * score() - calculate preemption score */ static int score( struct peer *peer /* peer structure pointer */ ) { int temp; /* * This routine calculates the premption score from the peer * error bits and status. Increasing values are more cherished. */ temp = 0; if (!(peer->flash & BOGON10)) temp++; /* 1 good synch and stratum */ if (!(peer->flash & BOGON13)) temp++; /* 2 reachable */ if (!(peer->flash & BOGON12)) temp++; /* 3 no loop */ if (!(peer->flash & BOGON11)) temp++; /* 4 good distance */ if (peer->status >= CTL_PST_SEL_SELCAND) temp++; /* 5 in the hunt */ if (peer->status != CTL_PST_SEL_EXCESS) temp++; /* 6 not spare tire */ return (temp); /* selection status */ } /* * free_peer - internal routine to free memory referred to by a struct * peer and return it to the peer free list. If unlink is * nonzero, unlink from the various lists. */ static void free_peer( struct peer * p ) { struct peer * unlinked; int hash; if ((MDF_UCAST & p->cast_flags) && !(FLAG_DNS & p->cfg.flags)) { hash = NTP_HASH_ADDR(&p->srcadr); peer_hash_count[hash]--; UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link, struct peer); if (NULL == unlinked) { peer_hash_count[hash]++; msyslog(LOG_ERR, "ERR: peer %s not in address table!", socktoa(&p->srcadr)); } } /* Remove him from the association hash as well. */ hash = p->associd & NTP_HASH_MASK; assoc_hash_count[hash]--; UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link, struct peer); if (NULL == unlinked) { assoc_hash_count[hash]++; msyslog(LOG_ERR, "ERR: peer %s not in association ID table!", socktoa(&p->srcadr)); } /* Remove him from the overall list. */ UNLINK_SLIST(unlinked, peer_list, p, p_link, struct peer); if (NULL == unlinked) msyslog(LOG_ERR, "ERR: %s not in peer list!", socktoa(&p->srcadr)); if (p->hostname != NULL) free(p->hostname); /* Add his corporeal form to peer free list */ ZERO(*p); LINK_SLIST(peer_free, p, p_link); peer_free_count++; } /* * unpeer - remove peer structure from hash table and free structure */ void unpeer( struct peer *peer ) { mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd); restrict_source(&peer->srcadr, true, 0); set_peerdstadr(peer, NULL); peer_demobilizations++; peer_associations--; if (FLAG_PREEMPT & peer->cfg.flags) peer_preempt--; #ifdef REFCLOCK /* * If this peer is actually a clock, shut it down first */ if (FLAG_REFCLOCK & peer->cfg.flags) refclock_unpeer(peer); #endif free_peer(peer); } /* * setup peer dstadr field keeping it in sync with the interface * structures */ void set_peerdstadr( struct peer * p, endpt * dstadr ) { struct peer * unlinked; if (p == NULL || p->dstadr == dstadr) return; /* * Don't accept updates to a separate multicast receive-only * endpt while a BCLNT peer is running its unicast protocol. */ if (dstadr != NULL && (FLAG_BC_VOL & p->cfg.flags) && (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) { return; } if (p->dstadr != NULL) { p->dstadr->peercnt--; UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink, struct peer); msyslog(LOG_INFO, "PROTO: %s unlink local addr %s -> %s", socktoa(&p->srcadr), latoa(p->dstadr), latoa(dstadr)); } p->dstadr = dstadr; if (dstadr != NULL) { LINK_SLIST(dstadr->peers, p, ilink); dstadr->peercnt++; } } /* * attempt to re-rebind interface if necessary */ void peer_refresh_interface( struct peer *p ) { endpt * niface; niface = select_peerinterface(p, &p->srcadr, NULL); DPRINT(4, ( "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x mode %u key %08x: new interface: ", p->dstadr == NULL ? "" : socktoa(&p->dstadr->sin), socktoa(&p->srcadr), p->hmode, p->cfg.version, p->cfg.minpoll, p->cfg.maxpoll, p->cfg.flags, p->cast_flags, p->cfg.mode, p->cfg.peerkey)); if (niface != NULL) { DPRINT(4, ( "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s", niface->fd, niface->bfd, niface->name, niface->flags, niface->ifindex, socktoa(&niface->sin))); if (niface->flags & INT_BROADCAST) DPRINT(4, (", bcast=%s", socktoa(&niface->bcast))); DPRINT(4, (", mask=%s\n", socktoa(&niface->mask))); } else { DPRINT(4, ("\n")); } set_peerdstadr(p, niface); } /* * refresh_all_peerinterfaces - see that all interface bindings are up * to date */ void refresh_all_peerinterfaces(void) { struct peer *p; /* * this is called when the interface list has changed * give all peers a chance to find a better interface * but only if either they don't have an address already * or if the one they have hasn't worked for a while. */ for (p = peer_list; p != NULL; p = p->p_link) { if ((p->dstadr) && (p->reach & 0x3)) // Classic Bug 2849 XOR 2043 /* either of last 2 tries with this dstadr worked */ continue; if (MDF_POOL & p->cast_flags) continue; /* Pool slots don't get interfaces. */ if (FLAG_DNS & p->cfg.flags) continue; /* Still doing DNS lookup. */ peer_refresh_interface(p); } } /* * newpeer - initialize a new peer association * * RETURN: a pointer to the new peer structure * NULL if this would duplicate an existing peer */ struct peer * newpeer( sockaddr_u * srcadr, const char * hostname, endpt * dstadr, uint8_t hmode, struct peer_ctl *ctl, uint8_t cast_flags, bool initializing1 ) { struct peer * peer; unsigned int hash; /* * First search from the beginning for an association with given * remote address and mode. If an interface is given, search * from there to find the association which matches that * destination. If the given interface is "any", track down the * actual interface, because that's what gets put into the peer * structure. */ if (dstadr != NULL) { peer = findexistingpeer(srcadr, hostname, NULL, hmode); while (peer != NULL) { if (peer->dstadr == dstadr || ((MDF_BCLNT & cast_flags) && (MDF_BCLNT & peer->cast_flags))) break; if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) && peer->dstadr == findinterface(srcadr)) break; peer = findexistingpeer(srcadr, hostname, peer, hmode); } } else { /* no endpt address given */ peer = findexistingpeer(srcadr, hostname, NULL, hmode); } /* * If a peer is found, this would be a duplicate and we don't * allow that. This avoids duplicate ephemeral (broadcast/ * multicast) and preemptible (manycast and pool) client * associations. */ if (peer != NULL) { DPRINT(2, ("newpeer(%s) found existing association\n", (hostname) ? hostname : socktoa(srcadr))); return NULL; } /* * Allocate a new peer structure. Some dirt here, since some of * the initialization requires knowledge of our system state. */ if (peer_free_count == 0) getmorepeermem(); UNLINK_HEAD_SLIST(peer, peer_free, p_link); peer_free_count--; peer_associations++; if (FLAG_PREEMPT & ctl->flags) peer_preempt++; /* * Assign an association ID and increment the system variable. */ peer->associd = current_association_ID; if (++current_association_ID == 0) ++current_association_ID; peer->srcadr = *srcadr; if (hostname != NULL) peer->hostname = estrdup(hostname); peer->hmode = hmode; /* * Copy in the peer configuration block. */ memcpy(&peer->cfg, ctl, sizeof(peer->cfg)); peer->cast_flags = cast_flags; set_peerdstadr(peer, select_peerinterface(peer, srcadr, dstadr)); if (NTP_MAXPOLL_UNK == peer->cfg.maxpoll) /* not set yet, set to default */ peer->cfg.maxpoll = NTP_MAXDPOLL; /* * minpoll is clamped not greater than NTP_MAXPOLL * maxpoll is clamped not less than NTP_MINPOLL * minpoll is clamped not greater than maxpoll. */ peer->cfg.minpoll = min(peer->cfg.minpoll, NTP_MAXPOLL); peer->cfg.maxpoll = max(peer->cfg.maxpoll, NTP_MINPOLL); if (peer->cfg.minpoll > peer->cfg.maxpoll) peer->cfg.minpoll = peer->cfg.maxpoll; if (peer->dstadr != NULL) DPRINT(3, ("newpeer(%s): using fd %d and our addr %s\n", socktoa(srcadr), peer->dstadr->fd, socktoa(&peer->dstadr->sin))); else DPRINT(3, ("newpeer(%s): local interface currently not bound\n", socktoa(srcadr))); /* * Broadcast needs the socket enabled for broadcast */ if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL) enable_broadcast(peer->dstadr, srcadr); peer->precision = sys_precision; peer->hpoll = peer->cfg.minpoll; if (cast_flags & MDF_POOL) peer_clear(peer, "POOL", initializing1); else if (cast_flags & MDF_BCAST) peer_clear(peer, "BCST", initializing1); else peer_clear(peer, "INIT", initializing1); if (mode_ntpdate) peer_ntpdate++; /* * Note time on statistics timers. */ peer->timereset = current_time; peer->timereachable = current_time; peer->timereceived = current_time; /* * Put the new peer in the hash tables. */ if ((MDF_UCAST & cast_flags) && !(FLAG_DNS & ctl->flags)) peer_update_hash(peer); hash = peer->associd & NTP_HASH_MASK; LINK_SLIST(assoc_hash[hash], peer, aid_link); assoc_hash_count[hash]++; LINK_SLIST(peer_list, peer, p_link); restrict_source(&peer->srcadr, false, 0); mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd); DPRINT(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x mode %u key %08x\n", latoa(peer->dstadr), socktoa(&peer->srcadr), peer->hmode, peer->cfg.version, peer->cfg.minpoll, peer->cfg.maxpoll, peer->cfg.flags, peer->cast_flags, peer->cfg.mode, peer->cfg.peerkey)); return peer; } void peer_update_hash (struct peer *peer) { unsigned int hash; hash = NTP_HASH_ADDR(&peer->srcadr); LINK_SLIST(peer_hash[hash], peer, adr_link); peer_hash_count[hash]++; } /* * peer_clr_stats - clear peer module statistics counters */ void peer_clr_stats(void) { findpeer_calls = 0; assocpeer_calls = 0; peer_allocations = 0; peer_demobilizations = 0; peer_timereset = current_time; } /* * peer_reset - reset statistics counters */ static void peer_reset( struct peer *peer ) { if (peer == NULL) return; peer->timereset = current_time; peer->sent = 0; peer->received = 0; peer->processed = 0; peer->badauth = 0; peer->bogusorg = 0; peer->oldpkt = 0; peer->seldisptoolarge = 0; peer->selbroken = 0; } /* * peer_all_reset - reset all peer statistics counters */ void peer_all_reset(void) { struct peer *peer; for (peer = peer_list; peer != NULL; peer = peer->p_link) peer_reset(peer); } /* * findmanycastpeer - find and return a manycastclient or pool * association matching a received response. */ struct peer * findmanycastpeer( struct recvbuf *rbufp /* receive buffer pointer */ ) { struct peer *peer; struct pkt *pkt; l_fp p_org; /* * This routine is called upon arrival of a server-mode response * to a manycastclient multicast solicitation, or to a pool * server unicast solicitation. Search the peer list for a * manycastclient association where the last transmit timestamp * matches the response packet's originate timestamp. There can * be multiple manycastclient associations, or multiple pool * solicitation assocations, so this assumes the transmit * timestamps are unique for such. */ pkt = &rbufp->recv_pkt; for (peer = peer_list; peer != NULL; peer = peer->p_link) if (MDF_SOLICIT_MASK & peer->cast_flags) { p_org = ntohl_fp(pkt->org); if (p_org == peer->org) break; } return peer; } /* peer_cleanup - clean peer list prior to shutdown */ void peer_cleanup(void) { struct peer *peer; associd_t assoc; for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) { if (assoc != 0U) { peer = findpeerbyassoc(assoc); if (peer != NULL) unpeer(peer); } } peer = findpeerbyassoc(current_association_ID); if (peer != NULL) unpeer(peer); } ntpsec-1.1.0+dfsg1/ntpd/refclock_zyfer.c0000644000175000017500000001467213252364117017757 0ustar rlaagerrlaager/* * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock * * Harlan Stenn, Jan 2002 */ #include "config.h" #include "ntp.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include #include #include /* * This driver provides support for the TOD serial port of a Zyfer GPStarplus. * This clock also provides PPS as well as IRIG outputs. * Precision is limited by the serial driver, etc. * * If I was really brave I'd hack/generalize the serial driver to deal * with arbitrary on-time characters. This clock *begins* the stream with * `!`, the on-time character, and the string is *not* EOL-terminated. * * Configure the beast for 9600, 8N1. While I see leap-second stuff * in the documentation, the published specs on the TOD format only show * the seconds going to '59'. I see no leap warning in the TOD format. * * The clock sends the following message once per second: * * !TIME,2002,017,07,59,32,2,4,1 * YYYY DDD HH MM SS m T O * * ! On-time character * YYYY Year * DDD 001-366 Day of Year * HH 00-23 Hour * MM 00-59 Minute * SS 00-59 Second (probably 00-60) * m 1-5 Time Mode: * 1 = GPS time * 2 = UTC time * 3 = LGPS time (Local GPS) * 4 = LUTC time (Local UTC) * 5 = Manual time * T 4-9 Time Figure Of Merit: * 4 x <= 1us * 5 1us < x <= 10 us * 6 10us < x <= 100us * 7 100us < x <= 1ms * 8 1ms < x <= 10ms * 9 10ms < x * O 0-4 Operation Mode: * 0 Warm-up * 1 Time Locked * 2 Coasting * 3 Recovering * 4 Manual * */ /* * Interface definitions */ #define DEVICE "/dev/zyfer%d" /* device name and unit */ #define SPEED232 B9600 /* uart speed (9600 baud) */ #define PRECISION (-20) /* precision assumed (about 1 us) */ #define REFID "GPS\0" /* reference ID */ #define NAME "ZYFER" /* shortname */ #define DESCRIPTION "Zyfer GPStarplus" /* WRU */ #define LENZYFER 29 /* timecode length */ /* * Unit control structure */ struct zyferunit { uint8_t Rcvbuf[LENZYFER + 1]; uint8_t polled; /* poll message flag */ int pollcnt; int Rcvptr; }; /* * Function prototypes */ static bool zyfer_start (int, struct peer *); static void zyfer_receive (struct recvbuf *); static void zyfer_poll (int, struct peer *); /* * Transfer vector */ struct refclock refclock_zyfer = { NAME, /* basename of driver */ zyfer_start, /* start up driver */ NULL, /* shut down driver in the standard way */ zyfer_poll, /* transmit poll message */ NULL, /* not used (old zyfer_control) */ NULL, /* initialize driver (not used) */ NULL /* timer - not used */ }; /* * zyfer_start - open the devices and initialize data for processing */ static bool zyfer_start( int unit, struct peer *peer ) { struct zyferunit *up; struct refclockproc *pp; int fd; char device[20]; /* * Open serial port. * Something like LDISC_ACTS that looked for ! would be nice... */ snprintf(device, sizeof(device), DEVICE, unit); fd = refclock_open(peer->cfg.path ? peer->cfg.path : device, peer->cfg.baud ? peer->cfg.baud : SPEED232, LDISC_RAW); if (fd <= 0) /* coverity[leaked_handle] */ return false; msyslog(LOG_NOTICE, "REFCLOCK: zyfer(%d) fd: %d", unit, fd); /* * Allocate and initialize unit structure */ up = emalloc_zero(sizeof(struct zyferunit)); pp = peer->procptr; pp->io.clock_recv = zyfer_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { close(fd); pp->io.fd = -1; free(up); return false; } pp->unitptr = up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; up->pollcnt = 2; up->polled = 0; /* May not be needed... */ return true; } /* * zyfer_receive - receive data from the serial interface */ static void zyfer_receive( struct recvbuf *rbufp ) { struct zyferunit *up; struct refclockproc *pp; struct peer *peer; int tmode; /* Time mode */ int tfom; /* Time Figure Of Merit */ int omode; /* Operation mode */ uint8_t *p; peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; p = (uint8_t *) &rbufp->recv_space; /* * If lencode is 0: * - if *rbufp->recv_space is ! * - - call refclock_gtlin to get things going * - else flush * else stuff it on the end of lastcode * If we don't have LENZYFER bytes * - wait for more data * Crack the beast, and if it's OK, process it. * * We use refclock_gtlin() because we might use LDISC_CLK. * * Under FreeBSD, we get the ! followed by two 14-byte packets. */ if (pp->lencode >= LENZYFER) pp->lencode = 0; if (!pp->lencode) { if (*p == '!') pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); else return; } else { memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length); pp->lencode += (int)rbufp->recv_length; pp->a_lastcode[pp->lencode] = '\0'; } if (pp->lencode < LENZYFER) return; record_clock_stats(peer, pp->a_lastcode); /* * We get down to business, check the timecode format and decode * its contents. If the timecode has invalid length or is not in * proper format, we declare bad format and exit. */ if (pp->lencode != LENZYFER) { refclock_report(peer, CEVNT_BADTIME); return; } /* * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1" */ if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d", &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, &tmode, &tfom, &omode) != 8) { refclock_report(peer, CEVNT_BADREPLY); return; } if (tmode != 2) { refclock_report(peer, CEVNT_BADTIME); return; } /* Should we make sure tfom is 4? */ if (omode != 1) { pp->leap = LEAP_NOTINSYNC; return; } if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } /* * Good place for record_clock_stats() */ up->pollcnt = 2; if (up->polled) { up->polled = 0; refclock_receive(peer); } } /* * zyfer_poll - called by the transmit procedure */ static void zyfer_poll( int unit, struct peer *peer ) { struct zyferunit *up; struct refclockproc *pp; UNUSED_ARG(unit); /* * We don't really do anything here, except arm the receiving * side to capture a sample and check for timeouts. */ pp = peer->procptr; up = pp->unitptr; if (!up->pollcnt) refclock_report(peer, CEVNT_TIMEOUT); else up->pollcnt--; pp->polls++; up->polled = 1; } ntpsec-1.1.0+dfsg1/ntpd/refclock_shm.c0000644000175000017500000003621413252364117017403 0ustar rlaagerrlaager/* * refclock_shm - clock driver for utc via shared memory * - under construction - * To add new modes: Extend or union the shmTime-struct. Do not * extend/shrink size, because otherwise existing implementations * will specify wrong size of shared memory-segment * PB 18.3.97 */ #include "config.h" #include "ntp_types.h" #include "ntp.h" #include "ntpd.h" #undef fileno #include "ntp_io.h" #undef fileno #include "ntp_refclock.h" #undef fileno #include "timespecops.h" #include "ntp_calendar.h" /* for SECSPERHR */ #undef fileno #include "ntp_stdlib.h" #include "ntp_assert.h" #undef fileno #include #undef fileno #include #include #include #include #include #if defined(HAVE_STDATOMIC_H) && !defined(__COVERITY__) # include #endif /* HAVE_STDATOMIC_H */ /* * This driver supports a reference clock attached through shared memory */ /* * SHM interface definitions */ #define PRECISION (-30) /* precision assumed 1 ns */ #define REFID "SHM" /* reference ID */ #define NAME "SHM" /* shortname */ #define DESCRIPTION "SHM/Shared memory interface" #define NSAMPLES 3 /* stages of median filter */ /* * Mode flags */ #define SHM_MODE_PRIVATE 0x0001 /* * Function prototypes */ static bool shm_start (int unit, struct peer *peer); static void shm_shutdown (struct refclockproc *peer); static void shm_poll (int unit, struct peer *peer); static void shm_timer (int unit, struct peer *peer); static void shm_clockstats (int unit, struct peer *peer); static void shm_control (int unit, const struct refclockstat * in_st, struct refclockstat * out_st, struct peer *peer); /* * Transfer vector */ struct refclock refclock_shm = { NAME, /* basename of driver */ shm_start, /* start up driver */ shm_shutdown, /* shut down driver */ shm_poll, /* transmit poll message */ shm_control, /* control settings */ NULL, /* not used: init */ shm_timer, /* once per second */ }; struct shmTime { int mode; /* 0 - if valid is set: * use values, * clear valid * 1 - if valid is set: * if count before and after read of values is equal, * use values * clear valid */ volatile int count; time_t clockTimeStampSec; int clockTimeStampUSec; time_t receiveTimeStampSec; int receiveTimeStampUSec; int leap; int precision; int nsamples; volatile int valid; unsigned clockTimeStampNSec; /* Unsigned ns timestamps */ unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */ int dummy[8]; }; struct shmunit { struct shmTime *shm; /* pointer to shared memory segment */ int forall; /* access for all UIDs? */ /* debugging/monitoring counters - reset when printed */ int ticks; /* number of attempts to read data*/ int good; /* number of valid samples */ int notready; /* number of peeks without data ready */ int bad; /* number of invalid samples */ int clash; /* number of access clashes while reading */ time_t max_delta; /* difference limit */ time_t max_delay; /* age/stale limit */ }; static struct shmTime* getShmTime( int unit, bool forall ) { struct shmTime *p = NULL; int shmid; /* 0x4e545030 is NTP0. * Big units will give non-ascii but that's OK * as long as everybody does it the same way. */ shmid=shmget(0x4e545030 + unit, sizeof (struct shmTime), IPC_CREAT | (forall ? 0666 : 0600)); if (shmid == -1) { /* error */ msyslog(LOG_ERR, "REFCLOCK: SHM shmget (unit %d): %m", unit); return NULL; } p = (struct shmTime *)shmat (shmid, 0, 0); if (p == (struct shmTime *)-1) { /* error */ msyslog(LOG_ERR, "REFCLOCK: SHM shmat (unit %d): %m", unit); return NULL; } return p; } /* * shm_start - attach to shared memory */ static bool shm_start( int unit, struct peer *peer ) { struct refclockproc * const pp = peer->procptr; struct shmunit * const up = emalloc_zero(sizeof(*up)); pp->io.clock_recv = NULL; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = -1; up->forall = (unit >= 2) && !(peer->cfg.mode & SHM_MODE_PRIVATE); up->shm = getShmTime(unit, up->forall); /* * Initialize miscellaneous peer variables */ memcpy((char *)&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; if (up->shm != 0) { pp->unitptr = up; up->shm->precision = PRECISION; peer->precision = (int8_t)up->shm->precision; up->shm->valid = 0; up->shm->nsamples = NSAMPLES; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; /* items to be changed later in 'shm_control()': */ up->max_delay = 5; up->max_delta = 4 * SECSPERHR; return true; } else { free(up); pp->unitptr = NULL; return false; } } /* * shm_control - configure flag1/time2 params * * These are not yet available during 'shm_start', so we have to do any * pre-computations we want to avoid during regular poll/timer callbacks * in this callback. */ static void shm_control( int unit, const struct refclockstat * in_st, struct refclockstat * out_st, struct peer * peer ) { struct refclockproc * const pp = peer->procptr; struct shmunit * const up = pp->unitptr; UNUSED_ARG(unit); UNUSED_ARG(in_st); UNUSED_ARG(out_st); if (NULL == up) return; if (!(pp->sloppyclockflag & CLK_FLAG1)) up->max_delta = 0; else if (pp->fudgetime2 < 1. || pp->fudgetime2 > SECSPERDAY) up->max_delta = 4 * SECSPERHR; else up->max_delta = (time_t)floor(pp->fudgetime2 + 0.5); } /* * shm_shutdown - shut down the clock */ static void shm_shutdown( struct refclockproc * pp ) { struct shmunit * const up = pp->unitptr; if (NULL == up) return; (void)shmdt((char *)up->shm); free(up); } /* * shm_poll - called by the transmit procedure */ static void shm_poll( int unit, struct peer *peer ) { struct refclockproc * const pp = peer->procptr; struct shmunit * const up = pp->unitptr; int major_error; pp->polls++; /* get dominant reason if we have no samples at all */ major_error = max(up->notready, up->bad); major_error = max(major_error, up->clash); /* * Process median filter samples. If none received, see what * happened, tell the core and keep going. */ if (pp->coderecv != pp->codeproc) { /* have some samples, everything OK */ pp->lastref = pp->lastrec; refclock_receive(peer); } else if (NULL == up->shm) { /* is this possible at all? */ /* we're out of business without SHM access */ refclock_report(peer, CEVNT_FAULT); } else if (major_error == up->clash) { /* too many collisions is like a bad signal */ refclock_report(peer, CEVNT_PROP); } else if (major_error == up->bad) { /* too much stale/bad/garbled data */ refclock_report(peer, CEVNT_BADREPLY); } else { /* in any other case assume it's just a timeout */ refclock_report(peer, CEVNT_TIMEOUT); } /* shm_clockstats() clears the tallies, so it must be last... */ shm_clockstats(unit, peer); } enum segstat_t { OK, NO_SEGMENT, NOT_READY, BAD_MODE, CLASH }; struct shm_stat_t { int status; int mode; struct timespec tvc, tvr, tvt; int precision; int leap; }; static inline void memory_barrier(void) { #if defined(HAVE_STDATOMIC_H) && !defined(__COVERITY__) atomic_thread_fence(memory_order_seq_cst); #endif /* HAVE_STDATOMIC_H */ } static enum segstat_t shm_query(volatile struct shmTime *shm_in, struct shm_stat_t *shm_stat) /* try to grab a sample from the specified SHM segment */ { volatile struct shmTime shmcopy, *shm = shm_in; volatile int cnt; unsigned int cns_new, rns_new; /* * This is the main routine. It snatches the time from the shm * board and tacks on a local timestamp. */ if (shm == NULL) { shm_stat->status = NO_SEGMENT; return NO_SEGMENT; } /*@-type@*//* splint is confused about struct timespec */ shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0; { time_t now; time(&now); shm_stat->tvc.tv_sec = now; } /* relying on word access to be atomic here */ if (shm->valid == 0) { shm_stat->status = NOT_READY; return NOT_READY; } cnt = shm->count; /* * This is proof against concurrency issues if either * (a) the memory_barrier() call works on this host, or * (b) memset compiles to an uninterruptible single-instruction bitblt. */ memory_barrier(); /* structure copy, to preserve volatile */ shmcopy = *shm; shm->valid = 0; memory_barrier(); /* * Clash detection in case neither (a) nor (b) was true. * Not supported in mode 0, and word access to the count field * must be atomic for this to work. */ if (shmcopy.mode > 0 && cnt != shm->count) { shm_stat->status = CLASH; return (enum segstat_t)shm_stat->status; } shm_stat->status = OK; shm_stat->mode = shmcopy.mode; switch (shmcopy.mode) { case 0: shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; rns_new = shmcopy.receiveTimeStampNSec; shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; cns_new = shmcopy.clockTimeStampNSec; /* Since the following comparisons are between unsigned ** variables they are always well defined, and any ** (signed) underflow will turn into very large unsigned ** values, well above the 1000 cutoff. ** ** Note: The usecs *must* be a *truncated* ** representation of the nsecs. This code will fail for ** *rounded* usecs, and the logic to deal with ** wrap-arounds in the presence of rounded values is ** much more convoluted. */ if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { shm_stat->tvt.tv_nsec = (long)cns_new; shm_stat->tvr.tv_nsec = (long)rns_new; } /* At this point shm_stat->tvr and shm_stat->tvt contain valid ns-level ** timestamps, possibly generated by extending the old ** us-level timestamps */ break; case 1: shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; rns_new = shmcopy.receiveTimeStampNSec; shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; cns_new = shmcopy.clockTimeStampNSec; /* See the case above for an explanation of the ** following test. */ if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { shm_stat->tvt.tv_nsec = (long)cns_new; shm_stat->tvr.tv_nsec = (long)rns_new; } /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level ** timestamps, possibly generated by extending the old ** us-level timestamps */ break; default: shm_stat->status = BAD_MODE; break; } /*@-type@*/ /* * leap field is not a leap offset but a leap notification code. * The values are magic numbers used by NTP and set by GPSD, if at all, in * the subframe code. */ shm_stat->leap = shmcopy.leap; shm_stat->precision = shmcopy.precision; return (enum segstat_t)shm_stat->status; } /* * shm_timer - called once every second. * * This tries to grab a sample from the SHM segment, filtering bad ones */ static void shm_timer( int unit, struct peer *peer ) { struct refclockproc * const pp = peer->procptr; struct shmunit * const up = pp->unitptr; volatile struct shmTime *shm; l_fp tsrcv; l_fp tsref; int c; time_t tt; enum segstat_t status; struct shm_stat_t shm_stat; up->ticks++; if ((shm = up->shm) == NULL) { /* try to map again - this may succeed if meanwhile some- body has ipcrm'ed the old (unaccessible) shared mem segment */ shm = up->shm = getShmTime(unit, up->forall); if (shm == NULL) { DPRINT(1, ("%s: no SHM segment\n",refclock_name(peer))); return; } } /* query the segment, atomically */ status = shm_query(shm, &shm_stat); switch (status) { case OK: DPRINT(2, ("%s: SHM(%d) type %d sample\n", refclock_name(peer), unit, shm_stat.mode)); break; case NO_SEGMENT: /* should never happen, but is harmless */ return; case NOT_READY: DPRINT(1, ("%s: SHM(%d) not ready\n",refclock_name(peer), unit)); up->notready++; return; case BAD_MODE: DPRINT(1, ("%s: SHM(%d) type blooper, mode=%d\n", refclock_name(peer), unit, shm->mode)); up->bad++; msyslog (LOG_ERR, "SHM(%d): bad mode found in shared memory: %d", unit, shm->mode); return; case CLASH: DPRINT(1, ("%s: type 1 access clash\n", refclock_name(peer))); msyslog (LOG_NOTICE, "SHM(%d): access clash in shared memory", unit); up->clash++; return; default: DPRINT(1, ("%s: internal error, unknown SHM(%d) fetch status\n", refclock_name(peer), unit)); msyslog (LOG_NOTICE, "internal error, unknown SHM(%d) fetch status", unit); up->bad++; return; } /* * Add POSIX UTC seconds and fractional seconds as a timecode. * We used to unpack this to calendar time, but it is bad * practice for the driver to pretend to know calendar time; * that interpretation is best left to higher levels. */ /* a_lastcode is seen as timecode with: ntpq -c cv [associd] */ c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), "%ld.%09ld", (long)shm_stat.tvt.tv_sec, (long)shm_stat.tvt.tv_nsec); pp->lencode = (c < (int)sizeof(pp->a_lastcode)) ? c : 0; /* check 1: age control of local time stamp */ tt = shm_stat.tvc.tv_sec - shm_stat.tvr.tv_sec; if (tt < 0 || tt > up->max_delay) { DPRINT(1, ("%s:SHM(%d) stale/bad receive time, delay=%llds\n", refclock_name(peer), unit, (long long)tt)); up->bad++; msyslog (LOG_ERR, "SHM(%d): stale/bad receive time, delay=%llds", unit, (long long)tt); return; } /* check 2: delta check */ tt = shm_stat.tvr.tv_sec - shm_stat.tvt.tv_sec - (shm_stat.tvr.tv_nsec < shm_stat.tvt.tv_nsec); if (tt < 0) tt = -tt; if (up->max_delta > 0 && tt > up->max_delta) { DPRINT(1, ("%s: SHM(%d) diff limit exceeded, delta=%llds\n", refclock_name(peer), unit, (long long)tt)); up->bad++; msyslog (LOG_ERR, "SHM(%d): difference limit exceeded, delta=%llds\n", unit, (long long)tt); return; } /* if we really made it to this point... we're winners! */ DPRINT(2, ("%s: SHM(%d) feeding data\n", refclock_name(peer), unit)); tsrcv = tspec_stamp_to_lfp(shm_stat.tvr); tsref = tspec_stamp_to_lfp(shm_stat.tvt); pp->leap = (uint8_t)shm_stat.leap; peer->precision = (int8_t)shm_stat.precision; refclock_process_offset(pp, tsref, tsrcv, pp->fudgetime1); up->good++; } /* * shm_clockstats - dump and reset counters */ static void shm_clockstats( int unit, struct peer *peer ) { struct refclockproc * const pp = peer->procptr; struct shmunit * const up = pp->unitptr; UNUSED_ARG(unit); if (pp->sloppyclockflag & CLK_FLAG4) { mprintf_clock_stats( peer, "%3d %3d %3d %3d %3d", up->ticks, up->good, up->notready, up->bad, up->clash); } up->ticks = up->good = up->notready = up->bad = up->clash = 0; } ntpsec-1.1.0+dfsg1/ntpd/refclock_truetime.c0000644000175000017500000004431313252364117020451 0ustar rlaagerrlaager/* * refclock_true - clock driver for the Kinemetrics/TrueTime receivers * Receiver Version 3.0C - tested plain, with CLKLDISC */ #include "config.h" #include #include #include "ntp.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" /* This should be an atom clock but those are very hard to build. * * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a * bunch of TTL input and output pins, all brought out to the back * panel. If you wire a PPS signal (such as the TTL PPS coming out of * a Kinemetrics/Truetime clock) to the 8253's GATE0, and then also * wire the 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read * CTR0 to get the number of uSecs since the last PPS upward swing, * mediated by reading OUT0 to find out if the counter has wrapped * around (this happens if more than 65535us (65ms) elapses between * the PPS event and our being called.) */ #ifdef ENABLE_PPS720 # undef min /* XXX */ # undef max /* XXX */ # include # include # include # define PCL720_IOB 0x2a0 /* XXX */ # define PCL720_CTR 0 /* XXX */ #endif /* * Support for Kinemetrics Truetime Receivers * GPS/TM-TMD: * XL-DC: (a 151-602-210, reported by the driver as a GPS/TM-TMD) * GPS-800 TCU: (an 805-957 with the RS232 Talker/Listener module) * * WARNING: This driver depends on the system clock for year disambiguation. * It will thus not be usable for recovery if the system clock is trashed. * * Most of this code is originally from refclock_wwvb.c (now * refclock_spectracom.c) with thanks. * It has been so mangled that wwvb is not a recognizable ancestor. * * Timcode format: ADDD:HH:MM:SSQCL * A - control A (this is stripped before we see it) * Q - Quality indication (see below) * C - Carriage return * L - Line feed * * Quality codes indicate possible error of * GPS-TM/TMD Receiver: (default quality codes for XL-DC) * ? +/- 1 milliseconds # +/- 100 microseconds * * +/- 10 microseconds . +/- 1 microsecond * space less than 1 microsecond * TL-3 Receiver: (default quality codes for TL-3) * ? unknown quality (receiver is unlocked) * space +/- 5 milliseconds * * The carriage return start bit begins on 0 seconds and extends to 1 bit time. * * This driver used to support the 468-DC receiver, but * http://www.ebay.com/gds/468-DC-SATELLITE-CLOCK-/10000000006640775/g.html * tells us that the GOES sats were shut down in 2005. */ /* * Definitions */ #define DEVICE "/dev/true%d" #define SPEED232 B9600 /* 9600 baud */ /* * Radio interface parameters */ #define PRECISION (-10) /* precision assumed (about 1 ms) */ #define REFID "TRUE" /* reference id */ #define NAME "TRUETIME" /* shortname */ #define DESCRIPTION "Kinemetrics/TrueTime Receiver" /* * used by the state machine */ enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Location, e_TS, e_Max}; static const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Location", "TS"}; #define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?") enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqGOES, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max}; static const char *states[] = {"Base", "InqTM", "InqTCU", "InqGOES", "Init", "F18", "F50", "Start", "Auto"}; #define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?") enum true_type {t_unknown, t_tm, t_tcu, t_tl3, t_Max}; static const char *types[] = {"unknown", "tm", "tcu", "tl3"}; #define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?") /* * unit control structure */ struct true_unit { unsigned int pollcnt; /* poll message counter */ unsigned int station; /* which station we are on */ bool polled; /* Hand in a time sample? */ enum true_state state; /* state machine */ enum true_type type; /* what kind of clock is it? */ int unit; /* save an extra copy of this */ FILE *debug; /* debug logging file */ #ifdef ENABLE_PPS720 int pcl720init; /* init flag for PCL 720 */ #endif }; /* * Function prototypes */ static bool true_start (int, struct peer *); static void true_receive (struct recvbuf *); static void true_poll (int, struct peer *); static void true_send (struct peer *, const char *); static void true_doevent (struct peer *, enum true_event); #ifdef ENABLE_PPS720 static unsigned long true_sample720 (void); #endif /* * Transfer vector */ struct refclock refclock_true = { NAME, /* basename of driver */ true_start, /* start up driver */ NULL, /* shut down driver in the stabdard way */ true_poll, /* transmit poll message */ NULL, /* not used (old true_control) */ NULL, /* initialize driver (not used) */ NULL /* timer - not used */ }; NTP_PRINTF(2, 3) static void true_debug(struct peer *peer, const char *fmt, ...) { va_list ap; int want_debugging, now_debugging; struct refclockproc *pp; struct true_unit *up; va_start(ap, fmt); pp = peer->procptr; up = pp->unitptr; want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0; now_debugging = (up->debug != NULL); if (want_debugging != now_debugging) { if (want_debugging) { char filename[40]; int fd; snprintf(filename, sizeof(filename), "/tmp/true%d.debug", up->unit); fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0600); if (fd >= 0 && (up->debug = fdopen(fd, "w"))) { static char buf[BUFSIZ]; setvbuf(up->debug, buf, _IOLBF, BUFSIZ); } } else { fclose(up->debug); up->debug = NULL; } } if (up->debug) { fprintf(up->debug, "true%d: ", up->unit); vfprintf(up->debug, fmt, ap); } va_end(ap); } /* * true_start - open the devices and initialize data for processing */ static bool true_start( int unit, struct peer *peer ) { struct true_unit *up; struct refclockproc *pp; char device[40]; int fd; /* * Open serial port */ snprintf(device, sizeof(device), DEVICE, unit); fd = refclock_open(peer->cfg.path ? peer->cfg.path : device, peer->cfg.baud ? peer->cfg.baud : SPEED232, LDISC_CLK); if (fd <= 0) /* coverity[leaked_handle] */ return false; /* * Allocate and initialize unit structure */ up = emalloc_zero(sizeof(*up)); pp = peer->procptr; pp->io.clock_recv = true_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { close(fd); pp->io.fd = -1; free(up); return false; } pp->unitptr = up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy(&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; up->pollcnt = 2; up->type = t_unknown; up->state = s_Base; /* * Send a CTRL-C character at the start, * just in case the clock is already * sending timecodes */ true_send(peer, "\03\r"); true_doevent(peer, e_Init); return true; } /* * true_receive - receive data from the serial interface on a clock */ static void true_receive( struct recvbuf *rbufp ) { struct true_unit *up; struct refclockproc *pp; struct peer *peer; char synced; int i; /* These variables hold data until we decide to keep it */ char rd_lastcode[BMAX]; l_fp rd_tmp; unsigned short rd_lencode; /* * Get the clock this applies to and pointers to the data. */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; /* * Read clock output. Automatically handles CLKLDISC. */ rd_lencode = (unsigned short)refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); rd_lastcode[rd_lencode] = '\0'; /* * There is a case where generates 2 timestamps. */ if (rd_lencode == 0) return; pp->lencode = rd_lencode; strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode)); pp->lastrec = rd_tmp; true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode, pp->lencode); up->pollcnt = 2; record_clock_stats(peer, pp->a_lastcode); /* * We get down to business, check the timecode format and decode * its contents. This code decodes a multitude of different * clock messages. Timecodes are processed if needed. All replies * will be run through the state machine to tweak driver options * and program the clock. */ /* * Clock misunderstood our last command? */ if (pp->a_lastcode[0] == '?' || strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) { true_doevent(peer, e_Huh); return; } /* * Timecode: "Fnn" * (from TM/TMD clock when it wants to tell us what it's up to.) */ if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) { switch (i) { case 50: true_doevent(peer, e_F50); break; case 51: true_doevent(peer, e_F51); break; default: true_debug(peer, "got F%02d - ignoring\n", i); break; } return; } /* * Timecode: " TRUETIME Mk III" or " TRUETIME XL" * (from a TM/TMD/XL clock during initialization.) */ if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 || strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { true_doevent(peer, e_F18); NLOG(NLOG_CLOCKSTATUS) { msyslog(LOG_INFO, "REFCLOCK: TM/TMD/XL: %s", pp->a_lastcode); } return; } /* * Timecode: "N03726428W12209421+000033" * 1 2 * index 0123456789012345678901234 * (from a TCU during initialization) */ if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') && (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') && pp->a_lastcode[18] == '+') { true_doevent(peer, e_Location); NLOG(NLOG_CLOCKSTATUS) { msyslog(LOG_INFO, "REFCLOCK: TCU-800: %s", pp->a_lastcode); } return; } /* * Timecode: "ddd:hh:mm:ssQ" * 1 2 * index 0123456789012345678901234 * (from all clocks supported by this driver.) */ if (pp->a_lastcode[3] == ':' && pp->a_lastcode[6] == ':' && pp->a_lastcode[9] == ':' && sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c", &pp->day, &pp->hour, &pp->minute, &pp->second, &synced) == 5) { /* * Adjust the synchronize indicator according to timecode * say were OK, and then say not if we really are not OK */ if (synced == '>' || synced == '#' || synced == '?' || synced == 'X') pp->leap = LEAP_NOTINSYNC; else pp->leap = LEAP_NOWARNING; true_doevent(peer, e_TS); #ifdef ENABLE_PPS720 /* If it's taken more than 65ms to get here, we'll lose. */ if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) { l_fp off; #ifdef CLOCK_PPS uint32_t sec; /* * find out what time it really is. Include * the count from the PCL720 */ if (!clocktime(pp->year, pp->day, pp->hour, pp->minute, pp->second, lfpuint(pp->lastrec), &pp->yearstart, &sec)) { refclock_report(peer, CEVNT_BADTIME); return; } off = lfpinut(sec, 0); #endif pp->usec = true_sample720(); #ifdef CLOCK_PPS TVUTOTSF(pp->usec, lfpfrac(off)); #endif /* * Stomp all over the timestamp that was pulled out * of the input stream. It's irrelevant since we've * adjusted the input time to reflect now (via pp->usec) * rather than when the data was collected. */ get_systime(&pp->lastrec); #ifdef CLOCK_PPS /* * Create a true offset for feeding to pps_sample() */ off -= pp->lastrec; pps_sample(peer, &off); #endif true_debug(peer, "true_sample720: %luus\n", pp->usec); } #endif /* * The clock will blurt a timecode every second but we only * want one when polled. If we havn't been polled, bail out. */ if (!up->polled) return; if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } /* * If clock is good we send a NOMINAL message so that * any previous BAD messages are nullified */ pp->lastref = pp->lastrec; refclock_receive(peer); refclock_report(peer, CEVNT_NOMINAL); /* * We have succeeded in answering the poll. * Turn off the flag and return */ up->polled = false; return; } /* * No match to known timecodes, report failure and return */ refclock_report(peer, CEVNT_BADREPLY); return; } /* * true_send - time to send the clock a signal to cough up a time sample */ static void true_send( struct peer *peer, const char *cmd ) { struct refclockproc *pp; pp = peer->procptr; if (!(pp->sloppyclockflag & CLK_FLAG1)) { ssize_t ret; size_t len = strlen(cmd); true_debug(peer, "Send '%s'\n", cmd); ret = write(pp->io.fd, cmd, len); if (ret < 0 || (size_t)ret != len) refclock_report(peer, CEVNT_FAULT); else pp->polls++; } } /* * state machine for initializing and controlling a clock */ static void true_doevent( struct peer *peer, enum true_event event ) { struct true_unit *up; struct refclockproc *pp; pp = peer->procptr; up = pp->unitptr; if (event != e_TS) { NLOG(NLOG_CLOCKSTATUS) { msyslog(LOG_INFO, "REFCLOCK: TRUETIME: clock %s, state %s, event %s", typeStr(up->type), stateStr(up->state), eventStr(event)); } } true_debug(peer, "clock %s, state %s, event %s\n", typeStr(up->type), stateStr(up->state), eventStr(event)); switch (up->type) { case t_tm: switch (event) { case e_Init: true_send(peer, "F18\r"); up->state = s_Init; break; case e_F18: true_send(peer, "F50\r"); /* * Timecode: " TRUETIME Mk III" or " TRUETIME XL" * (from a TM/TMD/XL clock during initialization.) */ if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 || strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) { true_doevent(peer, e_F18); NLOG(NLOG_CLOCKSTATUS) { msyslog(LOG_INFO, "REFCLOCK: TM/TMD/XL: %s", pp->a_lastcode); } return; } up->state = s_F18; break; case e_F50: true_send(peer, "F51\r"); up->state = s_F50; break; case e_F51: true_send(peer, "F08\r"); up->state = s_Start; break; case e_TS: if (up->state != s_Start && up->state != s_Auto) { true_send(peer, "\03\r"); break; } up->state = s_Auto; break; default: break; } break; case t_tcu: switch (event) { case e_Init: true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */ true_send(peer, "TSU\r"); /* UTC, not GPS. */ true_send(peer, "AU\r"); /* Auto Timestamps. */ up->state = s_Start; break; case e_TS: if (up->state != s_Start && up->state != s_Auto) { true_send(peer, "\03\r"); break; } up->state = s_Auto; break; default: break; } break; case t_tl3: switch (event) { case e_Init: true_send(peer, "ST1"); /* Turn on continuous stream */ break; case e_TS: up->state = s_Auto; break; default: break; } break; case t_unknown: switch (up->state) { case s_Base: if (event != e_Init) abort(); true_send(peer, "P\r"); up->state = s_InqGOES; break; case s_InqGOES: switch (event) { case e_Init: /*FALLTHROUGH*/ case e_Huh: sleep(1); /* wait for it */ break; default: abort(); } break; case s_InqTM: switch (event) { case e_F18: up->type = t_tm; true_doevent(peer, e_Init); break; case e_Init: /*FALLTHROUGH*/ case e_Huh: true_send(peer, "PO\r"); up->state = s_InqTCU; break; default: msyslog(LOG_INFO, "REFCLOCK: TRUETIME: TM/TMD init fellthrough!"); break; } break; case s_InqTCU: switch (event) { case e_Location: up->type = t_tcu; true_doevent(peer, e_Init); break; case e_Init: /*FALLTHROUGH*/ case e_Huh: up->state = s_Base; sleep(1); /* XXX */ break; default: msyslog(LOG_INFO, "REFCLOCK: TRUETIME: TCU init fellthrough!"); break; } break; /* * An expedient hack to prevent lint complaints, * these don't actually need to be used here... */ case s_Init: case s_F18: case s_F50: case s_Start: case s_Auto: case s_Max: msyslog(LOG_INFO, "REFCLOCK: TRUETIME: state %s is unexpected!", stateStr(up->state)); default: /* huh? */ break; } break; default: msyslog(LOG_INFO, "REFCLOCK: TRUETIME: cannot identify refclock!"); abort(); /* NOTREACHED */ } #ifdef ENABLE_PPS720 if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) { /* Make counter trigger on gate0, count down from 65535. */ pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535); /* * (These constants are OK since * they represent hardware maximums.) */ NLOG(NLOG_CLOCKINFO) { msyslog(LOG_NOTICE, "REFCLOCK: PCL-720 initialized"); } up->pcl720init++; } #endif } /* * true_poll - called by the transmit procedure */ static void true_poll( int unit, struct peer *peer ) { struct true_unit *up; struct refclockproc *pp; UNUSED_ARG(unit); /* * You don't need to poll this clock. It puts out timecodes * once per second. If asked for a timestamp, take note. * The next time a timecode comes in, it will be fed back. */ pp = peer->procptr; up = pp->unitptr; if (up->pollcnt > 0) { up->pollcnt--; } else { true_doevent(peer, e_Init); refclock_report(peer, CEVNT_TIMEOUT); } /* * polled every 64 seconds. Ask true_receive to hand in a * timestamp. */ up->polled = true; pp->polls++; } #ifdef ENABLE_PPS720 /* * true_sample720 - sample the PCL-720 */ static unsigned long true_sample720(void) { unsigned long f; /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3. * If it is not being held low now, we did not get called * within 65535us. */ if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) { NLOG(NLOG_CLOCKINFO) { msyslog(LOG_NOTICE, "REFCLOCK: PCL-720 out of synch"); } return (0); } f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR)); #ifdef DEBUG_PPS720 msyslog(LOG_DEBUG, "REFCLOCK: PCL-720: %luus", f); #endif return (f); } #endif ntpsec-1.1.0+dfsg1/ntpd/ntp_util.c0000644000175000017500000004377713252364117016616 0ustar rlaagerrlaager/* * ntp_util.c - stuff I didn't have any other place for */ #include "config.h" #include "lib_strbuf.h" #include "ntp_assert.h" #include "ntp_calendar.h" #include "ntp_config.h" #include "ntp_filegen.h" #include "ntp_leapsec.h" #include "ntp_stdlib.h" #include "ntp_stdlib.h" #include "ntpd.h" #include "timespecops.h" #include #include #include #include #include #include #include #include #include /* * Defines used by file logging */ #define MJD_1970 40587 /* MJD for 1 Jan 1970 */ /* * This contains odds and ends, including the hourly stats, various * configuration items, leapseconds stuff, etc. */ /* * File names */ static char *key_file_name; /* keys file name */ static char *leapfile_name; /* leapseconds file name */ static struct stat leapfile_stat; /* leapseconds file stat() buffer */ static bool have_leapfile = false; char *stats_drift_file; /* frequency file name */ static double wander_resid; /* last frequency update */ double wander_threshold = 1e-7; /* initial frequency threshold */ static char *timespec_to_MJDtime(const struct timespec *); /* * Statistics file stuff */ #ifndef NTP_VAR # define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */ #endif char statsdir[MAXFILENAME] = NTP_VAR; static FILEGEN clockstats; static FILEGEN loopstats; static FILEGEN peerstats; static FILEGEN protostats; static FILEGEN rawstats; static FILEGEN sysstats; static FILEGEN timingstats; static FILEGEN usestats; /* * This controls whether stats are written to the fileset. Provided * so that ntpq can turn off stats when the file system fills up. */ bool stats_control; /* * Last frequency written to file. */ static double prev_drift_comp; /* last frequency update */ /* * Function prototypes */ static void record_sys_stats(void); static void record_use_stats(void); void ntpd_time_stepped(void); static void check_leap_expiration(bool, time_t); /* * Prototypes */ #ifdef DEBUG void uninit_util(void); #endif /* * uninit_util - free memory allocated by init_util */ #ifdef DEBUG void uninit_util(void) { if (stats_drift_file) { free(stats_drift_file); stats_drift_file = NULL; } if (key_file_name) { free(key_file_name); key_file_name = NULL; } filegen_unregister("clockstats"); filegen_unregister("loopstats"); filegen_unregister("rawstats"); filegen_unregister("sysstats"); filegen_unregister("peerstats"); filegen_unregister("protostats"); filegen_unregister("timingstats"); filegen_unregister("usestats"); } #endif /* DEBUG */ /* * init_util - initialize the util module of ntpd */ void init_util(void) { filegen_register(statsdir, "clockstats", &clockstats); filegen_register(statsdir, "loopstats", &loopstats); filegen_register(statsdir, "rawstats", &rawstats); filegen_register(statsdir, "sysstats", &sysstats); filegen_register(statsdir, "peerstats", &peerstats); filegen_register(statsdir, "protostats", &protostats); filegen_register(statsdir, "timingstats", &timingstats); filegen_register(statsdir, "usestats", &usestats); /* * register with libntp ntp_set_tod() to call us back * when time is stepped. */ step_callback = &ntpd_time_stepped; #ifdef DEBUG atexit(&uninit_util); #endif /* DEBUG */ } /* * drift_write - write drift to file, speeds up restart * drift can be off significantly if the system has cooled off * * This used to use mkstemp, but that uses mode 600, user ntp * apparmor keeps root from reading that during initialization * See Issue #409 * We don't need a unique filename. No other job is writing driftfile. * There is no security problem with reading the drift file since * you can get the data via ntp_adjtime(2) or ntptime(8). */ static void drift_write(char *driftfile, double drift) { FILE *new; char tempfile[PATH_MAX]; strlcpy(tempfile, driftfile, sizeof(tempfile)); strlcat(tempfile, "-tmp", sizeof(tempfile)); if ((new = fopen(tempfile, "w")) == NULL) { msyslog(LOG_ERR, "LOG: frequency file %s: %m", tempfile); return; } fprintf(new, "%.6f\n", drift); (void)fclose(new); /* atomic */ if (rename(tempfile, driftfile)) msyslog(LOG_WARNING, "LOG: Unable to rename temp drift file %s to %s, %m", tempfile, driftfile); } /* * write_stats - hourly: sysstats, usestats, and maybe drift */ void write_stats(void) { record_sys_stats(); record_use_stats(); if (stats_drift_file != 0) { /* * When the frequency file is written, initialize the * prev_drift_comp and wander_resid. Thereafter, * reduce the wander_resid by half each hour. When * the difference between the prev_drift_comp and * drift_comp is less than the wander_resid, update * the frequncy file. This minimizes the file writes to * nonvolaile storage. */ DPRINT(1, ("write_stats: frequency %.6lf thresh %.6lf, freq %.6lf\n", (prev_drift_comp - drift_comp) * US_PER_S, wander_resid * US_PER_S, drift_comp * US_PER_S)); if (fabs(prev_drift_comp - drift_comp) < wander_resid) { wander_resid *= 0.5; return; } prev_drift_comp = drift_comp; wander_resid = wander_threshold; drift_write(stats_drift_file, drift_comp * US_PER_S); } } static bool drift_read(const char *drift_file, double *drift) { FILE *fp; if ((fp = fopen(drift_file, "r")) == NULL) { return false; } if (fscanf(fp, "%lf", drift) != 1) { msyslog(LOG_ERR, "LOG: format error frequency file %s", drift_file); fclose(fp); return false; } fclose(fp); return true; } /* * stats_config - configure the stats operation */ void stats_config( int item, const char *invalue /* only one type so far */ ) { FILE *fp; const char *value; size_t len; double new_drift = 0; time_t ttnow; value = invalue; switch (item) { /* * Open and read frequency file. */ case STATS_FREQ_FILE: if (!value || (len = strlen(value)) == 0) break; stats_drift_file = erealloc(stats_drift_file, len + 1); memcpy(stats_drift_file, value, len+1); /* * Open drift file and read frequency. If the file is * missing or contains errors, tell the loop to reset. */ if (drift_read(stats_drift_file, &new_drift)) { loop_config(LOOP_FREQ, new_drift); prev_drift_comp = drift_comp; } break; /* * Specify statistics directory. */ case STATS_STATSDIR: /* - 1 since value may be missing the DIR_SEP. */ if (strlen(value) >= sizeof(statsdir) - 1) { msyslog(LOG_ERR, "LOG: statsdir too long (>%d, sigh)", (int)sizeof(statsdir) - 2); } else { bool add_dir_sep; size_t value_l; /* Add a DIR_SEP unless we already have one. */ value_l = strlen(value); if (0 == value_l) add_dir_sep = false; else add_dir_sep = (DIR_SEP != value[value_l - 1]); if (add_dir_sep) snprintf(statsdir, sizeof(statsdir), "%s%c", value, DIR_SEP); else snprintf(statsdir, sizeof(statsdir), "%s", value); filegen_statsdir(); } break; /* * Open pid file. */ case STATS_PID_FILE: if ((fp = fopen(value, "w")) == NULL) { msyslog(LOG_ERR, "LOG: pid file %s: %m", value); break; } fprintf(fp, "%d", (int)getpid()); fclose(fp); break; /* * Read leapseconds file. * * Note: Currently a leap file without SHA1 signature is * accepted, but if there is a signature line, the signature * must be valid or the file is rejected. */ case STATS_LEAP_FILE: if (!value || (len = strlen(value)) == 0) break; leapfile_name = erealloc(leapfile_name, len + 1); memcpy(leapfile_name, value, len + 1); if (leapsec_load_file( leapfile_name, &leapfile_stat, true, true)) { leap_signature_t lsig; time(&ttnow); leapsec_getsig(&lsig); mprintf_event(EVNT_TAI, NULL, "%d leap %s %s %s", lsig.taiof, rfc3339time(lsig.ttime), leapsec_expired(ttnow) ? "expired" : "expires", rfc3339time(lsig.etime)); have_leapfile = true; /* force an immediate daily expiration check of * the leap seconds table */ check_leap_expiration(true, ttnow); } break; default: /* oh well */ break; } } /* timespec_to_MJDtime */ static char * timespec_to_MJDtime(const struct timespec *ts) { char *buf; unsigned long day, sec, msec; buf = lib_getbuf(); day = (unsigned long)ts->tv_sec / SECSPERDAY + MJD_1970; sec = (unsigned long)ts->tv_sec % SECSPERDAY; msec = (unsigned long)ts->tv_nsec / NS_PER_MS; /* nano secs to milli sec */ snprintf(buf, LIB_BUFLENGTH, "%lu %lu.%03lu", day, sec, msec); return buf; } static const char * peerlabel(const struct peer *peer) { #if defined(REFCLOCK) && !defined(ENABLE_CLASSIC_MODE) if (peer->procptr != NULL) return refclock_name(peer); else #endif /* defined(REFCLOCK) && !defined(ENABLE_CLASSIC_MODE)*/ return socktoa(&peer->srcadr); } /* * record_peer_stats - write peer statistics to file * * file format: * day (MJD) * time (s past UTC midnight) * IP address (old format) or drivername(unit) (new format) * status word (hex) * offset * delay * dispersion * jitter */ void record_peer_stats( struct peer *peer, int status ) { struct timespec now; if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&peerstats, now.tv_sec); if (peerstats.fp != NULL) { fprintf(peerstats.fp, "%s %s %x %.9f %.9f %.9f %.9f\n", timespec_to_MJDtime(&now), peerlabel(peer), (unsigned int)status, peer->offset, peer->delay, peer->disp, peer->jitter); fflush(peerstats.fp); } } /* * record_loop_stats - write loop filter statistics to file * * file format: * day (MJD) * time (s past midnight) * offset * frequency (PPM) * jitter * wnder (PPM) * time constant (log2) */ void record_loop_stats( double offset, /* offset */ double freq, /* frequency (PPM) */ double jitter, /* jitter */ double wander, /* wander (PPM) */ int spoll ) { struct timespec now; if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&loopstats, now.tv_sec); if (loopstats.fp != NULL) { fprintf(loopstats.fp, "%s %.9f %.6f %.9f %.6f %d\n", timespec_to_MJDtime(&now), offset, freq * US_PER_S, jitter, wander * US_PER_S, spoll); fflush(loopstats.fp); } } /* * record_clock_stats - write clock statistics to file * * file format: * day (MJD) * time (s past midnight) * IP address (old format) or drivername(unit) new format * text message */ void record_clock_stats( struct peer *peer, const char *text /* timecode string */ ) { struct timespec now; if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&clockstats, now.tv_sec); if (clockstats.fp != NULL) { fprintf(clockstats.fp, "%s %s %s\n", timespec_to_MJDtime(&now), peerlabel(peer), text); fflush(clockstats.fp); } } /* * mprintf_clock_stats - write clock statistics to file with * msnprintf-style formatting. */ int mprintf_clock_stats( struct peer *peer, const char *fmt, ... ) { va_list ap; int rc; char msg[512]; va_start(ap, fmt); rc = mvsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); if (stats_control) record_clock_stats(peer, msg); return rc; } /* * record_raw_stats - write raw timestamps to file * * file format * day (MJD) * time (s past midnight) * source IP address old format) or drivername(unit) (new format) * destination peer address * t1 t2 t3 t4 timestamps * various other local statistics */ void record_raw_stats( struct peer *peer, int leap, int version, int mode, int stratum, int ppoll, int precision, double root_delay, /* seconds */ double root_dispersion,/* seconds */ uint32_t refid, unsigned int outcount ) { struct timespec now; const sockaddr_u *dstaddr = peer->dstadr ? &peer->dstadr->sin : NULL; l_fp t1 = peer->org; /* originate timestamp */ l_fp t2 = peer->rec; /* receive timestamp */ l_fp t3 = peer->xmt; /* transmit timestamp */ l_fp t4 = peer->dst; /* destination timestamp */ if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&rawstats, now.tv_sec); if (rawstats.fp != NULL) { fprintf(rawstats.fp, "%s %s %s %s %s %s %s %d %d %d %d %d %d %.6f %.6f %s %u\n", timespec_to_MJDtime(&now), peerlabel(peer), dstaddr ? socktoa(dstaddr) : "-", ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9), ulfptoa(t4, 9), leap, version, mode, stratum, ppoll, precision, root_delay, root_dispersion, refid_str(refid, stratum), outcount); fflush(rawstats.fp); } } /* * record_sys_stats - write system statistics to file * * file format * day (MJD) * time (s past midnight) * time since reset * packets received * packets for this host * current version * old version * access denied * bad length or format * bad authentication * declined * rate exceeded * KoD sent */ void record_sys_stats(void) { struct timespec now; if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&sysstats, now.tv_sec); if (sysstats.fp != NULL) { fprintf(sysstats.fp, "%s %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", timespec_to_MJDtime(&now), current_time - sys_stattime, sys_received, sys_processed, sys_newversion, sys_oldversion, sys_restricted, sys_badlength, sys_badauth, sys_declined, sys_limitrejected, sys_kodsent); fflush(sysstats.fp); proto_clr_stats(); } } /* * record_use_stats - write usage statistics to file * * file format * day (MJD) * time (s past midnight) * time since reset */ void record_use_stats(void) { struct timespec now; struct rusage usage; static struct rusage oldusage; /* Descriptions in NetBSD and FreeBSD are better than Linux * man getrusage */ if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&usestats, now.tv_sec); if (usestats.fp != NULL) { double utime, stimex; getrusage(RUSAGE_SELF, &usage); utime = usage.ru_utime.tv_usec - oldusage.ru_utime.tv_usec; utime /= 1E6; utime += usage.ru_utime.tv_sec - oldusage.ru_utime.tv_sec; stimex = usage.ru_stime.tv_usec - oldusage.ru_stime.tv_usec; stimex /= 1E6; stimex += usage.ru_stime.tv_sec - oldusage.ru_stime.tv_sec; fprintf(usestats.fp, "%s %u %.3f %.3f %ld %ld %ld %ld %ld %ld %ld %ld %ld\n", timespec_to_MJDtime(&now), current_time - use_stattime, utime, stimex, usage.ru_minflt - oldusage.ru_minflt, usage.ru_majflt - oldusage.ru_majflt, usage.ru_nswap - oldusage.ru_nswap, usage.ru_inblock - oldusage.ru_inblock, usage.ru_oublock - oldusage.ru_oublock, usage.ru_nvcsw - oldusage.ru_nvcsw, usage.ru_nivcsw - oldusage.ru_nivcsw, usage.ru_nsignals - oldusage.ru_nsignals, usage.ru_maxrss ); fflush(usestats.fp); oldusage = usage; use_stattime = current_time; } } /* * record_proto_stats - write system statistics to file * * file format * day (MJD) * time (s past midnight) * text message */ void record_proto_stats( char *str /* text string */ ) { struct timespec now; if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&protostats, now.tv_sec); if (protostats.fp != NULL) { fprintf(protostats.fp, "%s %s\n", timespec_to_MJDtime(&now), str); fflush(protostats.fp); } } #ifdef ENABLE_DEBUG_TIMING /* * record_timing_stats - write timing statistics to file * * file format: * day (mjd) * time (s past midnight) * text message */ void record_timing_stats( const char *text /* text message */ ) { static unsigned int flshcnt; struct timespec now; if (!stats_control) return; clock_gettime(CLOCK_REALTIME, &now); filegen_setup(&timingstats, now.tv_sec); if (timingstats.fp != NULL) { fprintf(timingstats.fp, "%s %s\n", timespec_to_MJDtime(&now), text); if (++flshcnt % 100 == 0) fflush(timingstats.fp); } } #endif /* * check_leap_file - See if the leapseconds file has been updated. * * Returns: n/a * * Note: This loads a new leapfile on the fly. Currently a leap file * without SHA1 signature is accepted, but if there is a signature line, * the signature must be valid or the file is rejected. */ void check_leap_file( bool is_daily_check, time_t systime ) { /* just do nothing if there is no leap file */ if ( ! (leapfile_name && *leapfile_name)) return; /* try to load leapfile, force it if no leapfile loaded yet */ if (leapsec_load_file( leapfile_name, &leapfile_stat, !have_leapfile, is_daily_check)) have_leapfile = true; else if (!have_leapfile) return; check_leap_expiration(is_daily_check, systime); } /* * check expiration of a loaded leap table */ static void check_leap_expiration( bool is_daily_check, time_t systime ) { static const char * const logPrefix = "leapsecond file"; int rc; /* test the expiration of the leap data and log with proper * level and frequency (once/hour or once/day, depending on the * state. */ rc = leapsec_daystolive(systime); if (rc == 0) { msyslog(LOG_WARNING, "CLOCK: %s ('%s'): will expire in less than one day", logPrefix, leapfile_name); } else if (is_daily_check && rc < 28) { if (rc < 0) msyslog(LOG_ERR, "CLOCK: %s ('%s'): expired less than %d day%s ago", logPrefix, leapfile_name, -rc, (rc == -1 ? "" : "s")); else msyslog(LOG_WARNING, "CLOCK: %s ('%s'): will expire in less than %d days", logPrefix, leapfile_name, 1+rc); } } /* * getauthkeys - read the authentication keys from the specified file */ void getauthkeys( const char *keyfile ) { size_t len; len = strlen(keyfile); if (!len) return; key_file_name = erealloc(key_file_name, len + 1); memcpy(key_file_name, keyfile, len + 1); authreadkeys(key_file_name); } /* * ntpd_time_stepped is called back by step_systime(), allowing ntpd * to do any one-time processing necessitated by the step. */ void ntpd_time_stepped(void) { unsigned int saved_mon_enabled; /* * flush the monitor MRU list which contains l_fp timestamps * which should not be compared across the step. */ if (MON_OFF != mon_enabled) { saved_mon_enabled = mon_enabled; mon_stop(MON_OFF); mon_start((int)saved_mon_enabled); } } ntpsec-1.1.0+dfsg1/ntpd/ntp_leapsec.h0000644000175000017500000002245113252364117017244 0ustar rlaagerrlaager/* * ntp_leapsec.h - leap second processing for NTPD * * Written by Juergen Perlinger for the NTP project. * * ---------------------------------------------------------------------- * This is an attempt to get the leap second handling into a dedicated * module to make the somewhat convoluted logic testable. * * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: NTP */ #ifndef GUARD_NTP_LEAPSEC_H #define GUARD_NTP_LEAPSEC_H #include /* for struct stat */ /* function pointer types. Note that 'fprintf' and 'getc' can be casted * to the dumper resp. reader type, provided the auxiliary argument is a * valid FILE pointer in hat case. */ typedef void (*leapsec_dumper)(void*, const char *fmt, ...); typedef int (*leapsec_reader)(void*); struct leap_table; typedef struct leap_table leap_table_t; /* Validate a stream containing a leap second file in the NIST / NTPD * format that can also be loaded via 'leapsec_load()'. This uses * the SHA1 hash and preprocessing as described in the NIST leapsecond * file. */ #define LSVALID_GOODHASH 1 /* valid signature */ #define LSVALID_NOHASH 0 /* no signature in file */ #define LSVALID_BADHASH -1 /* signature mismatch */ #define LSVALID_BADFORMAT -2 /* signature not parseable */ extern int leapsec_validate(leapsec_reader, void*); /* Set/get electric mode * Electric mode is defined as the operation mode where the system clock * automagically manages the leap second, so we don't have to care about * stepping the clock. (This should be the case with most systems, * including the current implementation of the Win32 timekeeping.) * * The consequence of electric mode is that we do not 'see' the leap * second, and no client actions are needed when crossing the leap era * boundary. In manual (aka non-electric) mode the clock will simply * step forward until *we* (that is, this module) tells the client app * to step at the right time. This needs a slightly different type of * processing, so switching between those two modes should not be done * too close to a leap second. The transition might be lost in that * case. (The limit is actual 2 sec before transition.) * * OTOH, this is a system characteristic, so it's expected to be set * properly somewhere after system start and retain the value. * * Simply querying the state or setting it to the same value as before * does not have any unwanted side effects. You can query by giving a * the electric_query value for the argument. */ typedef enum {electric_query=-1, electric_off=0, electric_on=1} electric_mode; extern bool leapsec_electric(electric_mode el); /* Query result for a leap second schedule * 'ttime' is the transition point in full time scale, but only if * 'tai_diff' is not zero. Nominal UTC time when the next leap * era starts. * 'ddist' is the distance to the transition, in clock seconds. * This is the distance to the due time, which is different * from the transition time if the mode is non-electric. * Only valid if 'tai_diff' is not zero. * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always valid. * 'tai_diff' is the change in TAI offset after the next leap * transition. Zero if nothing is pending or too far ahead. * 'warped' is set only once, when the leap second occurred between * two queries. Always zero in electric mode. If non-zero, * immediately step the clock. * 'proximity' is a proximity warning. See definitions below. This is * more useful than an absolute difference to the leap second. * 'dynamic' != 0 if entry was requested by clock/peer */ struct leap_result { time_t ttime; uint32_t ddist; int16_t tai_offs; int16_t tai_diff; int16_t warped; uint8_t proximity; uint8_t dynamic; }; typedef struct leap_result leap_result_t; struct leap_signature { time_t etime; /* expiration time */ time_t ttime; /* transition time */ int16_t taiof; /* total offset to TAI */ }; typedef struct leap_signature leap_signature_t; #ifdef ENABLE_LEAP_SMEAR struct leap_smear_info { bool enabled; /* true if smearing is generally enabled */ bool in_progress; /* true if smearing is in progress, i.e. the offset has been computed */ double doffset; /* the current smear offset as double */ l_fp offset; /* the current smear offset */ uint32_t t_offset; /* the current time for which a smear offset has been computed */ long interval; /* smear interval, in [s], should be at least some hours */ double intv_start; /* start time of the smear interval */ double intv_end; /* end time of the smear interval */ }; typedef struct leap_smear_info leap_smear_info_t; #endif /* ENABLE_LEAP_SMEAR */ #define LSPROX_NOWARN 0 /* clear radar screen */ #define LSPROX_SCHEDULE 1 /* less than 1 month to target*/ #define LSPROX_ANNOUNCE 2 /* less than 1 day to target */ #define LSPROX_ALERT 3 /* less than 10 sec to target */ /* Get the current or alternate table pointer. Getting the alternate * pointer will automatically copy the primary table, so it can be * subsequently modified. */ extern leap_table_t *leapsec_get_table(bool alternate); /* Set the current leap table. Accepts only return values from * 'leapsec_get_table()', so it's hard to do something wrong. Returns * true if the current table is the requested one. */ extern bool leapsec_set_table(leap_table_t *); /* Clear all leap second data. Use it for init & cleanup */ extern void leapsec_clear(leap_table_t*); /* Load a leap second file. If 'blimit' is set, do not store (but * register with their TAI offset) leap entries before the build date. * Update the leap signature data on the fly. */ extern bool leapsec_load(leap_table_t*, leapsec_reader, void*); /* Dump the current leap table in readable format, using the provided * dump formatter function. */ extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg); /* Read a leap second file from stream. This is a convenience wrapper * around the generic load function, 'leapsec_load()'. */ extern bool leapsec_load_stream(FILE * fp, const char * fname, bool logall); /* Read a leap second file from file. It checks that the file exists and * (if 'force' is not applied) the ctime/mtime has changed since the * last load. If the file has to be loaded, either due to 'force' or * changed time stamps, the 'stat()' results of the file are stored in * '*sb' for the next cycle. Returns true on successful load, false * otherwise. Uses 'leapsec_load_stream()' internally. */ extern bool leapsec_load_file(const char * fname, struct stat * sb, bool force, bool logall); /* Get the current leap data signature. This consists of the last * ransition, the table expiration, and the total TAI difference at the * last transition. This is valid even if the leap transition itself was * culled due to the build date limit. */ extern void leapsec_getsig(leap_signature_t * psig); /* Check if the leap table is expired at the given time. */ extern bool leapsec_expired(time_t when); /* Get the distance to expiration in days. * Returns negative values if expired, zero if there are less than 24hrs * left, and positive numbers otherwise. */ extern int32_t leapsec_daystolive(time_t limit); /* Reset the current leap frame, so the next query will do proper table * lookup from fresh. Suppresses a possible leap era transition detection * for the next query. */ extern void leapsec_reset_frame(void); /* Given a transition time, the TAI offset valid after that and an * expiration time, try to establish a system leap transition. Only * works if the existing table is extended. On success, updates the * signature data. */ extern bool leapsec_add_fix(int offset, time_t ttime, time_t etime); /* Take a time stamp and create a leap second frame for it. This will * schedule a leap second for the beginning of the next month, midnight * UTC. The 'insert' argument tells if a leap second is added (!=0) or * removed (==0). We do not handle multiple inserts (yet?) * * Returns 1 if the insert worked, 0 otherwise. (It's not possible to * insert a leap second into the current history -- only appending * towards the future is allowed!) * * 'ntp_now' is subject to era unfolding. The entry is marked * dynamic. The leap signature is NOT updated. */ extern bool leapsec_add_dyn(bool insert, time_t ntp_now); /* Take a time stamp and get the associated leap information. The time * stamp is subject to era unfolding around the pivot or the current * system time if pivot is NULL. Sets the information in '*qr' and * returns true if a leap second era boundary was crossed between the * last and the current query. In that case, qr->warped contains the * required clock stepping, which is always zero in electric mode. */ extern bool leapsec_query(leap_result_t *qr, time_t when); /* Get the current leap frame info. Returns true if the result contains * useable data, false if there is currently no leap second frame. * This merely replicates some results from a previous query, but since * it does not check the current time, only the following entries are * meaningful: * qr->ttime; * qr->tai_offs; * qr->tai_diff; * qr->dynamic; */ extern bool leapsec_frame(leap_result_t *qr); /* reset global state for unit tests */ extern void leapsec_ut_pristine(void); #endif /* GUARD_NTP_LEAPSEC_H */ ntpsec-1.1.0+dfsg1/ntpd/ntp_io.c0000644000175000017500000022766613252364117016251 0ustar rlaagerrlaager/* * ntp_io.c - input/output routines for ntpd. */ #include "config.h" #include #include #include #if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE) # define FNM_CASEFOLD FNM_IGNORECASE #endif #include #include "ntp_machine.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_lists.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "ntp_assert.h" #include "ntp_dns.h" #include "timespecops.h" #include "isc_interfaceiter.h" #include "isc_netaddr.h" #ifdef HAVE_NET_ROUTE_H # define USE_ROUTING_SOCKET # include # ifdef HAVE_LINUX_RTNETLINK_H # include # endif #endif /* From ntp_request.h - after nuking ntpdc */ #define IFS_EXISTS 1 /* just exists */ #define IFS_CREATED 2 /* was just created */ #define IFS_DELETED 3 /* was just delete */ /* * setsockopt does not always have the same arg declaration * across all platforms. If it's not defined we make it empty * UNUSED * * #ifndef SETSOCKOPT_ARG_CAST * #define SETSOCKOPT_ARG_CAST * #endif */ #ifndef IPTOS_DSCP_EF #define IPTOS_DSCP_EF 0xb8 #endif int qos = IPTOS_DSCP_EF; /* QoS RFC 3246 */ /* * NIC rule entry */ typedef struct nic_rule_tag nic_rule; struct nic_rule_tag { nic_rule * next; nic_rule_action action; nic_rule_match match_type; char * if_name; sockaddr_u addr; int prefixlen; }; /* * NIC rule listhead. Entries are added at the head so that the first * match in the list is the last matching rule specified. */ static nic_rule *nic_rule_list; /* * This code is a remnant from when ntpd did asynchronous input using * the SIGIO facility. Most of that complexity is gone now. * * recvbuf buffers are preallocated for input. In the signal * handler we poll to see which sockets are ready and read the * packets from them into the recvbuf's along with a time stamp and * an indication of the source host and the interface it was received * through. This allows us to get as accurate receive time stamps * as possible independent of other processing going on. * * We watch the number of recvbufs available to the signal handler * and allocate more when this number drops below the low water * mark. If the signal handler should run out of buffers in the * interim it will drop incoming frames, the idea being that it is * better to drop a packet than to be inaccurate. */ /* * Other statistics of possible interest */ uint64_t packets_dropped; /* total # of packets dropped on reception */ uint64_t packets_ignored; /* packets received on wild card interface */ uint64_t packets_received; /* total # of packets received */ uint64_t packets_sent; /* total # of packets sent */ uint64_t packets_notsent; /* total # of packets which couldn't be sent */ uint64_t handler_calls; /* # of calls to interrupt handler */ uint64_t handler_pkts; /* number of pkts received by handler */ uptime_t io_timereset; /* time counters were reset */ /* * Interface stuff */ endpt * any_interface; /* wildcard ipv4 interface */ endpt * any6_interface; /* wildcard ipv6 interface */ endpt * loopback_interface; /* loopback ipv4 interface */ unsigned int sys_ifnum; /* next .ifnum to assign */ static int ninterfaces; /* total # of interfaces */ bool disable_dynamic_updates; /* if true, scan interfaces once only */ static bool netaddr_eqprefix(const isc_netaddr_t *, const isc_netaddr_t *, unsigned int) __attribute__((pure)); /* Socket Address */ typedef struct isc_sockaddr { union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; } type; unsigned int length; /* XXXRTH beginning? */ struct { struct isc_sockaddr *prev, *next; } link; } isc_sockaddr_t; static void netaddr_fromsockaddr(isc_netaddr_t *netaddr, const isc_sockaddr_t *source); #ifdef REFCLOCK /* * Refclock stuff. We keep a chain of structures with data concerning * the guys we are doing I/O for. */ static struct refclockio *refio; #endif /* REFCLOCK */ /* * File descriptor masks etc. for call to select * Not needed for I/O Completion Ports or anything outside this file */ static fd_set activefds; static int maxactivefd; /* * bit alternating value to detect verified interfaces during an update cycle */ static unsigned short sys_interphase = 0; static void add_interface(endpt *); static bool update_interfaces(unsigned short, interface_receiver_t, void *); static void remove_interface(endpt *); static endpt * create_interface(unsigned short, endpt *); static bool is_wildcard_addr (const sockaddr_u *); /* * Multicast functions */ static bool is_anycast (sockaddr_u *, const char *); #ifdef DEBUG static void interface_dump (const endpt *); static void sockaddr_dump (const sockaddr_u *); static void print_interface (const endpt *, const char *, const char *); #define DPRINT_INTERFACE(level, args) do { if (debug >= (level)) { print_interface args; } } while (0) #else #define DPRINT_INTERFACE(level, args) do {} while (0) #endif typedef struct vsock vsock_t; enum desc_type { FD_TYPE_SOCKET, FD_TYPE_FILE }; struct vsock { vsock_t * link; SOCKET fd; enum desc_type type; }; static vsock_t *fd_list; #if defined(USE_ROUTING_SOCKET) /* * async notification processing (e. g. routing sockets) */ /* * support for receiving data on fd that is not a refclock or a socket * like e. g. routing sockets */ struct asyncio_reader { struct asyncio_reader *link; /* the list this is being kept in */ SOCKET fd; /* fd to be read */ void *data; /* possibly local data */ void (*receiver)(struct asyncio_reader *); /* input handler */ }; static struct asyncio_reader *asyncio_reader_list; static void delete_asyncio_reader (struct asyncio_reader *); static struct asyncio_reader *new_asyncio_reader (void); static void add_asyncio_reader (struct asyncio_reader *, enum desc_type); static void remove_asyncio_reader (struct asyncio_reader *); #endif /* defined(USE_ROUTING_SOCKET) */ static void init_async_notifications (void); static bool addr_eqprefix (const sockaddr_u *, const sockaddr_u *, int); static int create_sockets (unsigned short); static void set_reuseaddr (int); static bool socket_broadcast_enable (endpt *, SOCKET, sockaddr_u *); #ifdef OS_MISSES_SPECIFIC_ROUTE_UPDATES static bool socket_broadcast_disable (endpt *, sockaddr_u *); #endif typedef struct remaddr remaddr_t; struct remaddr { remaddr_t * link; sockaddr_u addr; endpt * ep; }; static remaddr_t * remoteaddr_list; endpt * ep_list; /* complete endpt list */ static endpt * wildipv4; static endpt * wildipv6; static const int accept_wildcard_if_for_winnt = false; static void add_fd_to_list (SOCKET, enum desc_type); static endpt * find_addr_in_list (sockaddr_u *); static void delete_interface_from_list(endpt *); static void close_and_delete_fd_from_list(SOCKET); static void add_addr_to_list (sockaddr_u *, endpt *); static void create_wildcards (unsigned short); static endpt * findlocalinterface (sockaddr_u *, int, int); static endpt * findclosestinterface (sockaddr_u *, int); static endpt * findbcastinter (sockaddr_u *); #ifdef DEBUG static const char * action_text (nic_rule_action); #endif static nic_rule_action interface_action(char *, sockaddr_u *, uint32_t); static void convert_isc_if (isc_interface_t *, endpt *, unsigned short); static void calc_addr_distance(sockaddr_u *, const sockaddr_u *, const sockaddr_u *); static int cmp_addr_distance(const sockaddr_u *, const sockaddr_u *); static void maintain_activefds(int fd, bool closing); /* * Routines to read the ntp packets */ static int read_network_packet (SOCKET, endpt *, l_fp); static void input_handler (fd_set *, l_fp *); #ifdef REFCLOCK static int read_refclock_packet (SOCKET, struct refclockio *, l_fp); #endif /* * Flags from signal handlers */ volatile bool sawALRM = false; volatile bool sawHUP = false; #ifdef ENABLE_DNS_LOOKUP volatile bool sawDNS = false; #else # define sawDNS false #endif volatile bool sawQuit = false; /* SIGQUIT, SIGINT, SIGTERM */ static sigset_t blockMask; void maintain_activefds( int fd, bool closing ) { int i; if (fd < 0 || fd >= (int)FD_SETSIZE) { msyslog(LOG_ERR, "IO: Too many sockets in use, FD_SETSIZE %d exceeded by fd %d", FD_SETSIZE, fd); exit(1); } if (!closing) { FD_SET(fd, &activefds); maxactivefd = max(fd, maxactivefd); } else { FD_CLR(fd, &activefds); if (maxactivefd && fd == maxactivefd) { for (i = maxactivefd - 1; i >= 0; i--) if (FD_ISSET(i, &activefds)) { maxactivefd = i; break; } INSIST(fd != maxactivefd); } } } #ifdef ENABLE_DEBUG_TIMING /* * collect timing information for various processing * paths. currently we only pass them on to the file * for later processing. this could also do histogram * based analysis in order to reduce the load (and skew) * due to the file output */ void collect_timing(struct recvbuf *rb, const char *tag, int count, l_fp dts) { char buf[256]; snprintf(buf, sizeof(buf), "%s %d %s %s", (rb != NULL) ? ((rb->dstadr != NULL) ? socktoa(&rb->recv_srcadr) : "-REFCLOCK-") : "-", count, lfptoa(dts, 9), tag); record_timing_stats(buf); } #endif /* * About dynamic interfaces, sockets, reception and more... * * the code solves following tasks: * * - keep a current list of active interfaces in order * to bind to to the interface address on NTP_PORT so that * all wild and specific bindings for NTP_PORT are taken by ntpd * to avoid other daemons messing with the time or sockets. * - all interfaces keep a list of peers that are referencing * the interface in order to quickly re-assign the peers to * new interface in case an interface is deleted (=> gone from system or * down) * - have a preconfigured socket ready with the right local address * for transmission and reception * - have an address list for all destination addresses used within ntpd * to find the "right" preconfigured socket. * - facilitate updating the internal interface list with respect to * the current kernel state * * special issues: * * - on some systems it is perfectly legal to assign the same * address to multiple interfaces. Therefore this code does not * keep a list of interfaces but a list of interfaces that * represent a unique address as determined by the kernel by the * procedure in findlocalinterface. Thus it is perfectly legal to * see only one representative of a group of real interfaces if * they share the same address. * * Frank Kardel 20050910 */ /* * init_io - initialize I/O module. */ void init_io(void) { /* Init buffer free list and stat counters */ init_recvbuff(RECV_INIT); /* update interface every 5 minutes as default */ interface_interval = 300; sigemptyset(&blockMask); sigaddset(&blockMask, SIGALRM); sigaddset(&blockMask, MOREDEBUGSIG); sigaddset(&blockMask, LESSDEBUGSIG); sigaddset(&blockMask, SIGINT); sigaddset(&blockMask, SIGQUIT); sigaddset(&blockMask, SIGTERM); sigaddset(&blockMask, SIGHUP); } /* * io_open_sockets - call socket creation routine */ void io_open_sockets(void) { static bool already_opened = false; if (already_opened) return; already_opened = true; /* * Create the sockets */ create_sockets(NTP_PORT); init_async_notifications(); DPRINT(3, ("io_open_sockets: maxactivefd %d\n", maxactivefd)); } #ifdef DEBUG /* * function to dump the contents of the interface structure * for debugging use only. */ void interface_dump(const endpt *itf) { printf("Dumping interface: %p\n", itf); printf("fd = %d\n", itf->fd); printf("bfd = %d\n", itf->bfd); printf("sin = %s,\n", socktoa(&itf->sin)); sockaddr_dump(&itf->sin); printf("bcast = %s,\n", socktoa(&itf->bcast)); sockaddr_dump(&itf->bcast); printf("mask = %s,\n", socktoa(&itf->mask)); sockaddr_dump(&itf->mask); printf("name = %s\n", itf->name); printf("flags = 0x%08x\n", itf->flags); printf("addr_refid = %08x\n", itf->addr_refid); printf("received = %ld\n", itf->received); printf("sent = %ld\n", itf->sent); printf("notsent = %ld\n", itf->notsent); printf("ifindex = %u\n", itf->ifindex); printf("peercnt = %u\n", itf->peercnt); printf("phase = %u\n", itf->phase); } /* * sockaddr_dump - hex dump the start of a sockaddr_u */ static void sockaddr_dump(const sockaddr_u *psau) { /* Limit the size of the sockaddr_in6 hex dump */ const int maxsize = min(32, sizeof(psau->sa6)); const uint8_t * cp; /* XXX: Should we limit maxsize based on psau->saX.sin_family? */ cp = (const void *)&psau->sa6; for(int i = 0; i < maxsize; i++) { printf("%02x", *cp++); if (!((i + 1) % 4)) printf(" "); } printf("\n"); } /* * print_interface - helper to output debug information */ static void print_interface(const endpt *iface, const char *pfx, const char *sfx) { printf("%sinterface #%u: fd=%d, bfd=%d, name=%s, " "flags=0x%x, ifindex=%u, sin=%s", pfx, iface->ifnum, iface->fd, iface->bfd, iface->name, iface->flags, iface->ifindex, socktoa(&iface->sin)); if (AF_INET == iface->family) { if (iface->flags & INT_BROADCAST) printf(", bcast=%s", socktoa(&iface->bcast)); printf(", mask=%s", socktoa(&iface->mask)); } printf(", %s:%s", (iface->ignore_packets) ? "Disabled" : "Enabled", sfx); if (debug > 4) /* in-depth debugging only */ /* SPECIAL DEBUG */ interface_dump(iface); } #endif #if defined(USE_ROUTING_SOCKET) /* * create an asyncio_reader structure */ static struct asyncio_reader * new_asyncio_reader(void) { struct asyncio_reader *reader; reader = emalloc_zero(sizeof(*reader)); reader->fd = INVALID_SOCKET; return reader; } /* * delete a reader */ static void delete_asyncio_reader( struct asyncio_reader *reader ) { free(reader); } /* * add asynchio_reader */ static void add_asyncio_reader( struct asyncio_reader * reader, enum desc_type type) { LINK_SLIST(asyncio_reader_list, reader, link); add_fd_to_list(reader->fd, type); } /* * remove asynchio_reader */ static void remove_asyncio_reader( struct asyncio_reader *reader ) { struct asyncio_reader *unlinked; UNLINK_SLIST(unlinked, asyncio_reader_list, reader, link, struct asyncio_reader); if (reader->fd != INVALID_SOCKET) close_and_delete_fd_from_list(reader->fd); reader->fd = INVALID_SOCKET; } #endif /* defined(USE_ROUTING_SOCKET) */ static void netaddr_fromsockaddr(isc_netaddr_t *t, const isc_sockaddr_t *s) { int family = s->type.sa.sa_family; t->family = (unsigned int)family; switch (family) { case AF_INET: t->type.in = s->type.sin.sin_addr; t->zone = 0; break; case AF_INET6: memcpy(&t->type.in6, &s->type.sin6.sin6_addr, 16); t->zone = s->type.sin6.sin6_scope_id; break; default: INSIST(0); } } static bool netaddr_eqprefix(const isc_netaddr_t *a, const isc_netaddr_t *b, unsigned int prefixlen) { const unsigned char *pa = NULL, *pb = NULL; unsigned int ipabytes = 0; /* Length of whole IP address in bytes */ unsigned int nbytes; /* Number of significant whole bytes */ unsigned int nbits; /* Number of significant leftover bits */ REQUIRE(a != NULL && b != NULL); if (a->family != b->family) return (false); if (a->zone != b->zone && b->zone != 0) return (false); switch (a->family) { case AF_INET: pa = (const unsigned char *) &a->type.in; pb = (const unsigned char *) &b->type.in; ipabytes = 4; break; case AF_INET6: pa = (const unsigned char *) &a->type.in6; pb = (const unsigned char *) &b->type.in6; ipabytes = 16; break; default: return (false); } /* * Don't crash if we get a pattern like 10.0.0.1/9999999. */ if (prefixlen > ipabytes * 8) prefixlen = ipabytes * 8; nbytes = prefixlen / 8; nbits = prefixlen % 8; if (nbytes > 0) { if (memcmp(pa, pb, nbytes) != 0) return (false); } if (nbits > 0) { unsigned int bytea, byteb, mask; INSIST(nbytes < ipabytes); INSIST(nbits < 8); bytea = pa[nbytes]; byteb = pb[nbytes]; mask = (0xFF << (8-nbits)) & 0xFF; if ((bytea & mask) != (byteb & mask)) return (false); } return (true); } /* compare two sockaddr prefixes */ static bool addr_eqprefix( const sockaddr_u * a, const sockaddr_u * b, int prefixlen ) { isc_netaddr_t isc_a; isc_netaddr_t isc_b; isc_sockaddr_t isc_sa; ZERO(isc_sa); memcpy(&isc_sa.type, a, min(sizeof(isc_sa.type), sizeof(*a))); netaddr_fromsockaddr(&isc_a, &isc_sa); ZERO(isc_sa); memcpy(&isc_sa.type, b, min(sizeof(isc_sa.type), sizeof(*b))); netaddr_fromsockaddr(&isc_b, &isc_sa); return netaddr_eqprefix(&isc_a, &isc_b, (unsigned int)prefixlen); } /* * Code to tell if we have an IP address * If we have then return the sockaddr structure * and set the return value * see the bind9/getaddresses.c for details */ bool is_ip_address( const char * host, unsigned short af, sockaddr_u * addr ) { struct in_addr in4; struct addrinfo hints; struct addrinfo *result; struct sockaddr_in6 *resaddr6; char tmpbuf[128]; char *pch; REQUIRE(host != NULL); REQUIRE(addr != NULL); ZERO_SOCK(addr); /* * Try IPv4, then IPv6. In order to handle the extended format * for IPv6 scoped addresses (address%scope_ID), we'll use a local * working buffer of 128 bytes. The length is an ad-hoc value, but * should be enough for this purpose; the buffer can contain a string * of at least 80 bytes for scope_ID in addition to any IPv6 numeric * addresses (up to 46 bytes), the delimiter character and the * terminating NULL character. */ if (AF_UNSPEC == af || AF_INET == af) if (inet_pton(AF_INET, host, &in4) == 1) { AF(addr) = AF_INET; SET_ADDR4N(addr, in4.s_addr); return true; } if (AF_UNSPEC == af || AF_INET6 == af) if (sizeof(tmpbuf) > strlen(host)) { if ('[' == host[0]) { strlcpy(tmpbuf, &host[1], sizeof(tmpbuf)); pch = strchr(tmpbuf, ']'); if (pch != NULL) *pch = '\0'; } else { strlcpy(tmpbuf, host, sizeof(tmpbuf)); } ZERO(hints); hints.ai_family = AF_INET6; hints.ai_flags |= AI_NUMERICHOST; if (getaddrinfo(tmpbuf, NULL, &hints, &result) == 0) { AF(addr) = AF_INET6; resaddr6 = (struct sockaddr_in6 *)result->ai_addr; SET_ADDR6N(addr, resaddr6->sin6_addr); SET_SCOPE(addr, resaddr6->sin6_scope_id); freeaddrinfo(result); return true; } } /* * If we got here it was not an IP address */ return false; } /* * do standard initialization of interface structure */ static void init_interface( endpt *ep ) { ZERO(*ep); ep->fd = INVALID_SOCKET; ep->bfd = INVALID_SOCKET; ep->phase = sys_interphase; } /* * create new interface structure initialize from * template structure or via standard initialization * function */ static endpt * new_interface( endpt *interface ) { endpt * iface; iface = emalloc(sizeof(*iface)); if (NULL == interface) init_interface(iface); else /* use the template */ memcpy(iface, interface, sizeof(*iface)); /* count every new instance of an interface in the system */ iface->ifnum = sys_ifnum++; iface->starttime = current_time; return iface; } /* * return interface storage into free memory pool */ static inline void delete_interface( endpt *ep ) { free(ep); } /* * link interface into list of known interfaces */ static void add_interface( endpt * ep ) { /* Calculate the refid */ ep->addr_refid = addr2refid(&ep->sin); /* link at tail so ntpq -c ifstats index increases each row */ LINK_TAIL_SLIST(ep_list, ep, elink, endpt); ninterfaces++; } /* * remove interface from known interface list and clean up * associated resources */ static void remove_interface( endpt * ep ) { endpt * unlinked; sockaddr_u resmask; UNLINK_SLIST(unlinked, ep_list, ep, elink, endpt); delete_interface_from_list(ep); if (ep->fd != INVALID_SOCKET) { msyslog(LOG_INFO, "IO: Deleting interface #%u %s, %s#%d, interface stats: " "received=%ld, sent=%ld, dropped=%ld, " "active_time=%lu secs", ep->ifnum, ep->name, socktoa(&ep->sin), SRCPORT(&ep->sin), ep->received, ep->sent, ep->notsent, current_time - ep->starttime); close_and_delete_fd_from_list(ep->fd); ep->fd = INVALID_SOCKET; } if (ep->bfd != INVALID_SOCKET) { msyslog(LOG_INFO, "IO: stop listening for broadcasts to %s " "on interface #%u %s", socktoa(&ep->bcast), ep->ifnum, ep->name); close_and_delete_fd_from_list(ep->bfd); ep->bfd = INVALID_SOCKET; ep->flags &= ~INT_BCASTOPEN; } ninterfaces--; mon_clearinterface(ep); /* remove restrict interface entry */ SET_HOSTMASK(&resmask, AF(&ep->sin)); hack_restrict(RESTRICT_REMOVEIF, &ep->sin, &resmask, RESM_NTPONLY | RESM_INTERFACE, RES_IGNORE, 0); } static void log_listen_address( endpt * ep ) { msyslog(LOG_INFO, "IO: %s on %u %s %s", (ep->ignore_packets) ? "Listen and drop" : "Listen normally", ep->ifnum, ep->name, sockporttoa(&ep->sin)); } static void create_wildcards( unsigned short port ) { bool v4wild; bool v6wild; sockaddr_u wildaddr; nic_rule_action action; endpt * wildif; /* * silence "potentially uninitialized" warnings from VC9 * failing to follow the logic. Ideally action could remain * uninitialized, and the memset be the first statement under * the first if (v4wild). */ action = ACTION_LISTEN; ZERO(wildaddr); /* * create pseudo-interface with wildcard IPv6 address */ v6wild = ipv6_works; if (v6wild) { /* set wildaddr to the v6 wildcard address :: */ ZERO(wildaddr); AF(&wildaddr) = AF_INET6; SET_ADDR6N(&wildaddr, in6addr_any); SET_PORT(&wildaddr, port); SET_SCOPE(&wildaddr, 0); /* check for interface/nic rules affecting the wildcard */ action = interface_action(NULL, &wildaddr, 0); v6wild = (ACTION_IGNORE != action); } if (v6wild) { wildif = new_interface(NULL); strlcpy(wildif->name, "v6wildcard", sizeof(wildif->name)); memcpy(&wildif->sin, &wildaddr, sizeof(wildif->sin)); wildif->family = AF_INET6; AF(&wildif->mask) = AF_INET6; SET_ONESMASK(&wildif->mask); wildif->flags = INT_UP | INT_WILDCARD; wildif->ignore_packets = (ACTION_DROP == action); wildif->fd = open_socket(&wildif->sin, 0, 1, wildif); if (wildif->fd != INVALID_SOCKET) { wildipv6 = wildif; any6_interface = wildif; add_addr_to_list(&wildif->sin, wildif); add_interface(wildif); log_listen_address(wildif); } else { msyslog(LOG_ERR, "IO: unable to bind to wildcard address %s - another process may be running: %m; EXITING", socktoa(&wildif->sin)); exit(1); } DPRINT_INTERFACE(2, (wildif, "created ", "\n")); } /* * create pseudo-interface with wildcard IPv4 address */ v4wild = ipv4_works; if (v4wild) { /* set wildaddr to the v4 wildcard address 0.0.0.0 */ AF(&wildaddr) = AF_INET; SET_ADDR4N(&wildaddr, INADDR_ANY); SET_PORT(&wildaddr, port); /* check for interface/nic rules affecting the wildcard */ action = interface_action(NULL, &wildaddr, 0); v4wild = (ACTION_IGNORE != action); } if (v4wild) { wildif = new_interface(NULL); strlcpy(wildif->name, "v4wildcard", sizeof(wildif->name)); memcpy(&wildif->sin, &wildaddr, sizeof(wildif->sin)); wildif->family = AF_INET; AF(&wildif->mask) = AF_INET; SET_ONESMASK(&wildif->mask); wildif->flags = INT_BROADCAST | INT_UP | INT_WILDCARD; wildif->ignore_packets = (ACTION_DROP == action); wildif->fd = open_socket(&wildif->sin, 0, 1, wildif); if (wildif->fd != INVALID_SOCKET) { wildipv4 = wildif; any_interface = wildif; add_addr_to_list(&wildif->sin, wildif); add_interface(wildif); log_listen_address(wildif); } else { msyslog(LOG_ERR, "IO: unable to bind to wildcard address %s - another process may be running: %m; EXITING", socktoa(&wildif->sin)); exit(1); } DPRINT_INTERFACE(2, (wildif, "created ", "\n")); } } /* * add_nic_rule() -- insert a rule entry at the head of nic_rule_list. */ void add_nic_rule( nic_rule_match match_type, const char * if_name, /* interface name or numeric address */ int prefixlen, nic_rule_action action ) { nic_rule * rule; bool is_ip; rule = emalloc_zero(sizeof(*rule)); rule->match_type = match_type; rule->prefixlen = prefixlen; rule->action = action; if (MATCH_IFNAME == match_type) { REQUIRE(NULL != if_name); rule->if_name = estrdup(if_name); } else if (MATCH_IFADDR == match_type) { REQUIRE(NULL != if_name); /* set rule->addr */ is_ip = is_ip_address(if_name, AF_UNSPEC, &rule->addr); REQUIRE(is_ip); } else REQUIRE(NULL == if_name); LINK_SLIST(nic_rule_list, rule, next); } #ifdef DEBUG static const char * action_text( nic_rule_action action ) { const char *t; switch (action) { default: t = "ERROR"; /* quiet uninit warning */ DPRINT(1, ("fatal: unknown nic_rule_action %u\n", action)); ENSURE(0); break; case ACTION_LISTEN: t = "listen"; break; case ACTION_IGNORE: t = "ignore"; break; case ACTION_DROP: t = "drop"; break; } return t; } #endif /* DEBUG */ static nic_rule_action interface_action( char * if_name, sockaddr_u * if_addr, uint32_t if_flags ) { nic_rule * rule; int isloopback; int iswildcard; DPRINT(4, ("interface_action: interface %s ", (if_name != NULL) ? if_name : "wildcard")); iswildcard = is_wildcard_addr(if_addr); isloopback = !!(INT_LOOPBACK & if_flags); /* * Find any matching NIC rule from --interface / -I or ntp.conf * interface/nic rules. */ for (rule = nic_rule_list; rule != NULL; rule = rule->next) { switch (rule->match_type) { default: /* huh? */ case MATCH_ALL: /* loopback and wildcard excluded from "all" */ if (isloopback || iswildcard) break; DPRINT(4, ("nic all %s\n", action_text(rule->action))); return rule->action; case MATCH_IPV4: if (IS_IPV4(if_addr)) { DPRINT(4, ("nic ipv4 %s\n", action_text(rule->action))); return rule->action; } break; case MATCH_IPV6: if (IS_IPV6(if_addr)) { DPRINT(4, ("nic ipv6 %s\n", action_text(rule->action))); return rule->action; } break; case MATCH_WILDCARD: if (iswildcard) { DPRINT(4, ("nic wildcard %s\n", action_text(rule->action))); return rule->action; } break; case MATCH_IFADDR: if (rule->prefixlen != -1) { if (addr_eqprefix(if_addr, &rule->addr, rule->prefixlen)) { DPRINT(4, ("subnet address match - %s\n", action_text(rule->action))); return rule->action; } } else if (SOCK_EQ(if_addr, &rule->addr)) { DPRINT(4, ("address match - %s\n", action_text(rule->action))); return rule->action; } break; case MATCH_IFNAME: if (if_name != NULL #if defined(FNM_CASEFOLD) && !fnmatch(rule->if_name, if_name, FNM_CASEFOLD) #else && !strcasecmp(if_name, rule->if_name) #endif ) { DPRINT(4, ("interface name match - %s\n", action_text(rule->action))); return rule->action; } break; } } /* * Unless explicitly disabled such as with "nic ignore ::1" * listen on loopback addresses. Since ntpq query * "localhost" by default, which typically resolves to ::1 and * 127.0.0.1, it's useful to default to listening on both. */ if (isloopback) { DPRINT(4, ("default loopback listen\n")); return ACTION_LISTEN; } /* * Treat wildcard addresses specially. If there is no explicit * "nic ... wildcard" or "nic ... 0.0.0.0" or "nic ... ::" rule * default to drop. */ if (iswildcard) { DPRINT(4, ("default wildcard drop\n")); return ACTION_DROP; } /* * Check for "virtual IP" (colon in the interface name) after * the rules so that "ntpd --interface eth0:1 -novirtualips" * does indeed listen on eth0:1's addresses. */ if (!listen_to_virtual_ips && if_name != NULL && (strchr(if_name, ':') != NULL)) { DPRINT(4, ("virtual ip - ignore\n")); return ACTION_IGNORE; } /* * If there are no --interface/-I command-line options and no * interface/nic rules in ntp.conf, the default action is to * listen. In the presence of rules from either, the default * is to ignore. This implements ntpd's traditional listen- * every default with no interface listen configuration, and * ensures a single -I eth0 or "nic listen eth0" means do not * listen on any other addresses. */ if (NULL == nic_rule_list) { DPRINT(4, ("default listen\n")); return ACTION_LISTEN; } DPRINT(4, ("implicit ignore\n")); return ACTION_IGNORE; } static void convert_isc_if( isc_interface_t *isc_if, endpt *itf, unsigned short port ) { const uint8_t v6loop[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; strlcpy(itf->name, isc_if->name, sizeof(itf->name)); itf->ifindex = isc_if->ifindex; itf->family = (unsigned short)isc_if->af; AF(&itf->sin) = (sa_family_t)itf->family; AF(&itf->mask) = (sa_family_t)itf->family; AF(&itf->bcast) = (sa_family_t)itf->family; SET_PORT(&itf->sin, port); SET_PORT(&itf->mask, port); SET_PORT(&itf->bcast, port); if (IS_IPV4(&itf->sin)) { NSRCADR(&itf->sin) = isc_if->address.type.in.s_addr; NSRCADR(&itf->mask) = isc_if->netmask.type.in.s_addr; if (isc_if->flags & INTERFACE_F_BROADCAST) { itf->flags |= INT_BROADCAST; NSRCADR(&itf->bcast) = isc_if->broadcast.type.in.s_addr; } } else if (IS_IPV6(&itf->sin)) { SET_ADDR6N(&itf->sin, isc_if->address.type.in6); SET_ADDR6N(&itf->mask, isc_if->netmask.type.in6); SET_SCOPE(&itf->sin, isc_if->address.zone); } /* Process the rest of the flags */ itf->flags |= ((INTERFACE_F_UP & isc_if->flags) ? INT_UP : 0) | ((INTERFACE_F_LOOPBACK & isc_if->flags) ? INT_LOOPBACK : 0) | ((INTERFACE_F_POINTTOPOINT & isc_if->flags) ? INT_PPP : 0) | ((INTERFACE_F_PRIVACY & isc_if->flags) ? INT_PRIVACY : 0) ; /* * Clear the loopback flag if the address is not localhost. * http://bugs.ntp.org/1683 */ if (INT_LOOPBACK & itf->flags) { if (AF_INET == itf->family) { if (127 != (SRCADR(&itf->sin) >> 24)) itf->flags &= ~INT_LOOPBACK; } else { if (memcmp(v6loop, NSRCADR6(&itf->sin), sizeof(NSRCADR6(&itf->sin)))) itf->flags &= ~INT_LOOPBACK; } } } /* * refresh_interface * * some OSes have been observed to keep * cached routes even when more specific routes * become available. * this can be mitigated by re-binding * the socket. */ static bool refresh_interface( endpt * interface ) { #ifdef OS_MISSES_SPECIFIC_ROUTE_UPDATES if (interface->fd != INVALID_SOCKET) { int bcast = (interface->flags & INT_BCASTXMIT) != 0; /* as we forcibly close() the socket remove the broadcast permission indication */ if (bcast) socket_broadcast_disable(interface, &interface->sin); close_and_delete_fd_from_list(interface->fd); /* create new socket picking up a new first hop binding at connect() time */ interface->fd = open_socket(&interface->sin, bcast, 0, interface); /* * reset TTL indication so TTL is is set again * next time around */ return (interface->fd != INVALID_SOCKET); } else return false; /* invalid sockets are not refreshable */ #else /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */ return (interface->fd != INVALID_SOCKET); #endif /* !OS_MISSES_SPECIFIC_ROUTE_UPDATES */ } /* * interface_update - externally callable update function */ void interface_update( interface_receiver_t receiver, void * data) { bool new_interface_found; if (disable_dynamic_updates) return; new_interface_found = update_interfaces(NTP_PORT, receiver, data); if (!new_interface_found) return; #ifdef ENABLE_DNS_LOOKUP #ifdef DEBUG msyslog(LOG_DEBUG, "IO: new interface(s) found: waking up resolver"); #endif dns_new_interface(); #endif } static bool is_wildcard_addr( const sockaddr_u *psau ) { if (IS_IPV4(psau) && !NSRCADR(psau)) return true; if (IS_IPV6(psau) && S_ADDR6_EQ(psau, &in6addr_any)) return true; return false; } #ifdef NEED_REUSEADDR_FOR_IFADDRBIND /* * enable/disable re-use of wildcard address socket */ static void set_wildcard_reuse( unsigned short family, int on ) { endpt *any; SOCKET fd = INVALID_SOCKET; any = ANY_INTERFACE_BYFAM(family); if (any != NULL) fd = any->fd; if (fd != INVALID_SOCKET) { if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) msyslog(LOG_ERR, "IO: set_wildcard_reuse: setsockopt(SO_REUSEADDR, %s) failed: %m", on ? "on" : "off"); DPRINT(4, ("set SO_REUSEADDR to %s on %s\n", on ? "on" : "off", socktoa(&any->sin))); } } #endif /* NEED_REUSEADDR_FOR_IFADDRBIND */ static bool check_flags6( sockaddr_u *psau, const char *name, uint32_t flags6 ) { #if defined(SIOCGIFAFLAG_IN6) struct in6_ifreq ifr6; int fd; if (AF(psau) != AF_INET6) return false; if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) return false; ZERO(ifr6); memcpy(&ifr6.ifr_addr, &psau->sa6, sizeof(ifr6.ifr_addr)); strlcpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name)); if (ioctl(fd, SIOCGIFAFLAG_IN6, &ifr6) < 0) { close(fd); return false; } close(fd); if ((ifr6.ifr_ifru.ifru_flags6 & flags6) != 0) return true; #else UNUSED_ARG(psau); UNUSED_ARG(name); UNUSED_ARG(flags6); #endif /* SIOCGIFAFLAG_IN6 */ return false; } static bool is_anycast( sockaddr_u *psau, const char *name ) { #ifdef IN6_IFF_ANYCAST return check_flags6(psau, name, IN6_IFF_ANYCAST); #else UNUSED_ARG(psau); UNUSED_ARG(name); return false; #endif } static bool is_valid( sockaddr_u *psau, const char *name ) { uint32_t flags6; flags6 = 0; #ifdef IN6_IFF_DEPARTED flags6 |= IN6_IFF_DEPARTED; #endif #ifdef IN6_IFF_DETACHED flags6 |= IN6_IFF_DETACHED; #endif #ifdef IN6_IFF_TENTATIVE flags6 |= IN6_IFF_TENTATIVE; #endif return check_flags6(psau, name, flags6) ? false : true; } /* * update_interface strategy * * toggle configuration phase * * Phase 1: * forall currently existing interfaces * if address is known: * drop socket - rebind again * * if address is NOT known: * attempt to create a new interface entry * * Phase 2: * forall currently known non MCAST and WILDCARD interfaces * if interface does not match configuration phase (not seen in phase 1): * remove interface from known interface list * forall peers associated with this interface * disconnect peer from this interface * * Phase 3: * attempt to re-assign interfaces to peers * */ static bool update_interfaces( unsigned short port, interface_receiver_t receiver, void * data ) { isc_mem_t * mctx = (void *)-1; interface_info_t ifi; isc_interfaceiter_t * iter; bool result; isc_interface_t isc_if; int new_interface_found; unsigned int family; endpt enumep; endpt * ep; endpt * next_ep; DPRINT(3, ("update_interfaces(%d)\n", port)); /* * phase one - scan interfaces * - create those that are not found * - update those that are found */ new_interface_found = false; iter = NULL; result = isc_interfaceiter_create_bool(mctx, &iter); if (!result) return false; /* * Toggle system interface scan phase to find untouched * interfaces to be deleted. */ sys_interphase ^= 0x1; for (result = isc_interfaceiter_first_bool(iter); result; result = isc_interfaceiter_next_bool(iter)) { result = isc_interfaceiter_current_bool(iter, &isc_if); if (!result) break; /* See if we have a valid family to use */ family = isc_if.address.family; if (AF_INET != family && AF_INET6 != family) continue; if (AF_INET == family && !ipv4_works) continue; if (AF_INET6 == family && !ipv6_works) continue; /* create prototype */ init_interface(&enumep); convert_isc_if(&isc_if, &enumep, port); DPRINT_INTERFACE(4, (&enumep, "examining ", "\n")); /* * Check if and how we are going to use the interface. */ switch (interface_action(enumep.name, &enumep.sin, enumep.flags)) { default: case ACTION_IGNORE: DPRINT(4, ("ignoring interface %s (%s) - by nic rules\n", enumep.name, socktoa(&enumep.sin))); continue; case ACTION_LISTEN: DPRINT(4, ("listen interface %s (%s) - by nic rules\n", enumep.name, socktoa(&enumep.sin))); enumep.ignore_packets = false; break; case ACTION_DROP: DPRINT(4, ("drop on interface %s (%s) - by nic rules\n", enumep.name, socktoa(&enumep.sin))); enumep.ignore_packets = true; break; } /* interfaces must be UP to be usable */ if (!(enumep.flags & INT_UP)) { DPRINT(4, ("skipping interface %s (%s) - DOWN\n", enumep.name, socktoa(&enumep.sin))); continue; } /* * skip any interfaces UP and bound to a wildcard * address - some dhcp clients produce that in the * wild */ if (is_wildcard_addr(&enumep.sin)) continue; if (is_anycast(&enumep.sin, isc_if.name)) continue; /* * skip any address that is an invalid state to be used */ if (!is_valid(&enumep.sin, isc_if.name)) continue; /* * map to local *address* in order to map all duplicate * interfaces to an endpt structure with the appropriate * socket. Our name space is (ip-address), NOT * (interface name, ip-address). */ ep = getinterface(&enumep.sin, INT_WILDCARD); if (ep != NULL && refresh_interface(ep)) { /* * found existing and up to date interface - * mark present. */ if (ep->phase != sys_interphase) { /* * On a new round we reset the name so * the interface name shows up again if * this address is no longer shared. * We reset ignore_packets from the * new prototype to respect any runtime * changes to the nic rules. */ strlcpy(ep->name, enumep.name, sizeof(ep->name)); ep->ignore_packets = enumep.ignore_packets; } else { /* name collision - rename interface */ strlcpy(ep->name, "*multiple*", sizeof(ep->name)); } DPRINT_INTERFACE(4, (ep, "updating ", " present\n")); if (ep->ignore_packets != enumep.ignore_packets) { /* * We have conflicting configurations * for the interface address. This is * caused by using -I * for an interface that shares its * address with other interfaces. We * can not disambiguate incoming * packets delivered to this socket * without extra syscalls/features. * These are not (commonly) available. * Note this is a more unusual * configuration where several * interfaces share an address but * filtering via interface name is * attempted. We resolve the * configuration conflict by disabling * the processing of received packets. * This leads to no service on the * interface address where the conflict * occurs. */ msyslog(LOG_ERR, "CONFIG: WARNING: conflicting enable configuration for interfaces %s and %s for address %s - unsupported configuration - address DISABLED", enumep.name, ep->name, socktoa(&enumep.sin)); ep->ignore_packets = true; } ep->phase = sys_interphase; ifi.action = IFS_EXISTS; ifi.ep = ep; if (receiver != NULL) (*receiver)(data, &ifi); } else { /* * This is new or refreshing failed - add to * our interface list. If refreshing failed we * will delete the interface structure in phase * 2 as the interface was not marked current. * We can bind to the address as the refresh * code already closed the offending socket */ ep = create_interface(port, &enumep); if (ep != NULL) { ifi.action = IFS_CREATED; ifi.ep = ep; if (receiver != NULL) (*receiver)(data, &ifi); new_interface_found = true; DPRINT_INTERFACE(3, (ep, "updating ", " new - created\n")); } else { DPRINT_INTERFACE(3, (&enumep, "updating ", " new - creation FAILED")); msyslog(LOG_INFO, "IO: failed to init interface for address %s", socktoa(&enumep.sin)); continue; } } } isc_interfaceiter_destroy(&iter); /* * phase 2 - delete gone interfaces - reassigning peers to * other interfaces */ for (ep = ep_list; ep != NULL; ep = next_ep) { next_ep = ep->elink; /* * if phase does not match sys_phase this interface was * not enumerated during the last interface scan - so it * is gone and will be deleted here unless it did not * originate from interface enumeration INT_WILDCARD, */ if ((INT_WILDCARD & ep->flags) || ep->phase == sys_interphase) continue; DPRINT_INTERFACE(3, (ep, "updating ", "GONE - deleting\n")); remove_interface(ep); ifi.action = IFS_DELETED; ifi.ep = ep; if (receiver != NULL) (*receiver)(data, &ifi); /* disconnect peers from deleted endpt. */ while (ep->peers != NULL) set_peerdstadr(ep->peers, NULL); /* * update globals in case we lose * a loopback interface */ if (ep == loopback_interface) loopback_interface = NULL; delete_interface(ep); } /* * phase 3 - re-configure as the world has possibly changed * * never ever make this conditional again - it is needed to track * routing updates. see bug #2506 */ refresh_all_peerinterfaces(); return new_interface_found; } /* * create_sockets - create a socket for each interface plus a default * socket for when we don't know where to send */ static int create_sockets( unsigned short port ) { maxactivefd = 0; FD_ZERO(&activefds); DPRINT(2, ("create_sockets(%d)\n", port)); create_wildcards(port); update_interfaces(port, NULL, NULL); /* * Now that we have opened all the sockets, turn off the reuse * flag for security. */ set_reuseaddr(0); DPRINT(2, ("create_sockets: Total interfaces = %d\n", ninterfaces)); return ninterfaces; } /* * create_interface - create a new interface for a given prototype * binding the socket. */ static endpt * create_interface( unsigned short port, endpt * protot ) { sockaddr_u resmask; endpt * iface; DPRINT(2, ("create_interface(%s#%d)\n", socktoa(&protot->sin), port)); /* build an interface */ iface = new_interface(protot); /* * create socket */ iface->fd = open_socket(&iface->sin, 0, 0, iface); if (iface->fd != INVALID_SOCKET) log_listen_address(iface); if ((INT_BROADCAST & iface->flags) && iface->bfd != INVALID_SOCKET) msyslog(LOG_INFO, "IO: Listening on broadcast address %s#%d", socktoa((&iface->bcast)), port); if (INVALID_SOCKET == iface->fd && INVALID_SOCKET == iface->bfd) { msyslog(LOG_ERR, "IO: unable to create socket on %s (%u) for %s#%d", iface->name, iface->ifnum, socktoa((&iface->sin)), port); delete_interface(iface); return NULL; } /* * Blacklist our own addresses, no use talking to ourself */ SET_HOSTMASK(&resmask, AF(&iface->sin)); hack_restrict(RESTRICT_FLAGS, &iface->sin, &resmask, RESM_NTPONLY | RESM_INTERFACE, RES_IGNORE, 0); /* * set globals with the first found * loopback interface of the appropriate class */ if (NULL == loopback_interface && AF_INET == iface->family && (INT_LOOPBACK & iface->flags)) loopback_interface = iface; /* * put into our interface list */ add_addr_to_list(&iface->sin, iface); add_interface(iface); DPRINT_INTERFACE(2, (iface, "created ", "\n")); return iface; } #ifdef SO_EXCLUSIVEADDRUSE static void set_excladdruse( SOCKET fd ) { int one = 1; int failed; failed = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&one, sizeof(one)); if (!failed) return; msyslog(LOG_ERR, "IO: setsockopt(%d, SO_EXCLUSIVEADDRUSE, on): %m", (int)fd); } #endif /* SO_EXCLUSIVEADDRUSE */ /* * set_reuseaddr() - set/clear REUSEADDR on all sockets * NB possible hole - should we be doing this on broadcast * fd's also? */ static void set_reuseaddr( int flag ) { #ifndef SO_EXCLUSIVEADDRUSE endpt *ep; for (ep = ep_list; ep != NULL; ep = ep->elink) { if (ep->flags & INT_WILDCARD) continue; /* * if ep->fd is INVALID_SOCKET, we might have a adapter * configured but not present */ DPRINT(4, ("setting SO_REUSEADDR on %.16s@%s to %s\n", ep->name, socktoa(&ep->sin), flag ? "on" : "off")); if (ep->fd != INVALID_SOCKET) { if (setsockopt(ep->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag))) { msyslog(LOG_ERR, "IO: set_reuseaddr: setsockopt(%s, SO_REUSEADDR, %s) failed: %m", socktoa(&ep->sin), flag ? "on" : "off"); } } } #endif /* ! SO_EXCLUSIVEADDRUSE */ } /* * This is just a wrapper around an internal function so we can * make other changes as necessary later on */ void enable_broadcast( endpt * iface, sockaddr_u * baddr ) { #ifdef OPEN_BCAST_SOCKET socket_broadcast_enable(iface, iface->fd, baddr); #endif } #ifdef OPEN_BCAST_SOCKET /* * Enable a broadcast address to a given socket * The socket is in the ep_list all we need to do is enable * broadcasting. It is not this function's job to select the socket */ static bool socket_broadcast_enable( endpt * iface, SOCKET fd, sockaddr_u * baddr ) { int on = 1; if (IS_IPV4(baddr)) { /* if this interface can support broadcast, set SO_BROADCAST */ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on))) msyslog(LOG_ERR, "IO: setsockopt(SO_BROADCAST) enable failure on address %s: %m", socktoa(baddr)); else DPRINT(2, ("Broadcast enabled on socket %d for address %s\n", fd, socktoa(baddr))); } iface->flags |= INT_BCASTXMIT; return true; } #ifdef OS_MISSES_SPECIFIC_ROUTE_UPDATES /* * Remove a broadcast address from a given socket * The socket is in the ep_list all we need to do is disable * broadcasting. It is not this function's job to select the socket */ static bool socket_broadcast_disable( endpt * iface, sockaddr_u * baddr ) { int off = 0; /* This seems to be OK as an int */ if (IS_IPV4(baddr) && setsockopt(iface->fd, SOL_SOCKET, SO_BROADCAST, (char *)&off, sizeof(off))) msyslog(LOG_ERR, "IO: setsockopt(SO_BROADCAST) disable failure on address %s: %m", socktoa(baddr)); iface->flags &= ~INT_BCASTXMIT; return true; } #endif /* OS_MISSES_SPECIFIC_ROUTE_UPDATES */ #endif /* OPEN_BCAST_SOCKET */ /* * open_socket - open a socket, returning the file descriptor */ SOCKET open_socket( sockaddr_u * addr, bool bcast, bool turn_off_reuse, endpt * interf ) { SOCKET fd; int errval; /* * int is OK for REUSEADR per * http://www.kohala.com/start/mcast.api.txt */ const int on = 1; const int off = 0; if (IS_IPV6(addr) && !ipv6_works) return INVALID_SOCKET; /* create a datagram (UDP) socket */ fd = socket(AF(addr), SOCK_DGRAM, 0); if (INVALID_SOCKET == fd) { errval = errno; msyslog(LOG_ERR, "IO: socket(AF_INET%s, SOCK_DGRAM, 0) failed on address %s: %m", IS_IPV6(addr) ? "6" : "", socktoa(addr)); if (errval == EPROTONOSUPPORT || errval == EAFNOSUPPORT || errval == EPFNOSUPPORT) return (INVALID_SOCKET); errno = errval; #ifndef __COVERITY__ msyslog(LOG_ERR, "IO: unexpected socket() error %m code %d (not EPROTONOSUPPORT nor EAFNOSUPPORT nor EPFNOSUPPORT) - exiting", errno); exit(1); #endif /* __COVERITY__ */ } /* * Fixup the file descriptor for some systems * See bug #530 for details of the issue. */ fd = move_fd(fd); /* * set SO_REUSEADDR since we will be binding the same port * number on each interface according to turn_off_reuse. */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)((turn_off_reuse) ? &off : &on), sizeof(on))) { msyslog(LOG_ERR, "IO: setsockopt SO_REUSEADDR %s fails for address %s: %m", (turn_off_reuse) ? "off" : "on", socktoa(addr)); close(fd); return INVALID_SOCKET; } #ifdef SO_EXCLUSIVEADDRUSE /* * setting SO_EXCLUSIVEADDRUSE on the wildcard we open * first will cause more specific binds to fail. */ if (!(interf->flags & INT_WILDCARD)) set_excladdruse(fd); #endif /* * IPv4 specific options go here */ if (IS_IPV4(addr)) { if (setsockopt(fd, IPPROTO_IP, IP_TOS, (char*)&qos, sizeof(qos))) msyslog(LOG_ERR, "IO: setsockopt IP_TOS (%02x) fails on " "address %s: %m", (unsigned)qos, socktoa(addr)); if (bcast) socket_broadcast_enable(interf, fd, addr); } /* * IPv6 specific options go here */ if (IS_IPV6(addr)) { #ifdef IPV6_TCLASS if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, (char*)&qos, sizeof(qos))) msyslog(LOG_ERR, "IO: setsockopt IPV6_TCLASS (%02x) " "fails on address %s: %m", (unsigned)qos, socktoa(addr)); #endif /* IPV6_TCLASS */ if (isc_net_probe_ipv6only_bool() && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *)&on, sizeof(on))) msyslog(LOG_ERR, "IO: setsockopt IPV6_V6ONLY on fails on address %s: %m", socktoa(addr)); } #ifdef NEED_REUSEADDR_FOR_IFADDRBIND /* * some OSes don't allow binding to more specific * addresses if a wildcard address already bound * to the port and SO_REUSEADDR is not set */ if (!is_wildcard_addr(addr)) set_wildcard_reuse(AF(addr), 1); #endif /* * bind the local address. */ errval = bind(fd, &addr->sa, SOCKLEN(addr)); #ifdef NEED_REUSEADDR_FOR_IFADDRBIND if (!is_wildcard_addr(addr)) set_wildcard_reuse(AF(addr), 0); #endif if (errval < 0) { /* * Don't log this under all conditions */ if (turn_off_reuse == 0 #ifdef DEBUG || debug > 1 /* SPECIAL DEBUG */ #endif ) { msyslog(LOG_ERR, "IO: bind(%d) AF_INET%s %s#%d flags 0x%x failed: %m", fd, IS_IPV6(addr) ? "6" : "", socktoa(addr), SRCPORT(addr), interf->flags); } close(fd); return INVALID_SOCKET; } enable_packetstamps(fd, addr); DPRINT(4, ("bind(%d) AF_INET%s, addr %s%%%u#%d, flags 0x%x\n", fd, IS_IPV6(addr) ? "6" : "", socktoa(addr), SCOPE(addr), SRCPORT(addr), interf->flags)); make_socket_nonblocking(fd); add_fd_to_list(fd, FD_TYPE_SOCKET); #ifdef F_GETFL /* F_GETFL may not be defined if the underlying OS isn't really Unix */ DPRINT(4, ("flags for fd %d: 0x%x\n", fd, (unsigned)fcntl(fd, F_GETFL, 0))); #endif return fd; } /* * sendpkt - send a packet to the specified destination. Maintain a * send error cache so that only the first consecutive error for a * destination is logged. */ void sendpkt( sockaddr_u * dest, endpt * ep, void * pkt, int len ) { endpt * src; ssize_t cc; src = ep; if (NULL == src) { /* * unbound peer - drop request and wait for better * network conditions */ DPRINT(2, ("sendpkt(dst=%s, len=%d): no interface - IGNORED\n", socktoa(dest), len)); return; } DPRINT(2, ("sendpkt(%d, dst=%s, src=%s, len=%d)\n", src->fd, socktoa(dest), socktoa(&src->sin), len)); cc = sendto(src->fd, pkt, (unsigned int)len, 0, &dest->sa, SOCKLEN(dest)); if (cc == -1) { src->notsent++; packets_notsent++; } else { src->sent++; packets_sent++; } } #ifdef REFCLOCK /* * Routine to read the refclock packets for a specific interface * Return the number of bytes read. That way we know if we should * read it again or go on to the next one if no bytes returned * * Note: too big to inline */ static int read_refclock_packet( SOCKET fd, struct refclockio * rp, l_fp ts ) { size_t i; ssize_t buflen; int saved_errno; int consumed; struct recvbuf * rb; rb = get_free_recv_buffer(); if (NULL == rb) { /* * No buffer space available - just drop the packet */ char buf[RX_BUFF_SIZE]; buflen = read(fd, buf, sizeof buf); packets_dropped++; return (buflen); } i = (rp->datalen == 0 || rp->datalen > sizeof(rb->recv_space)) ? sizeof(rb->recv_space) : rp->datalen; do { buflen = read(fd, (char *)&rb->recv_space, i); } while (buflen < 0 && EINTR == errno); if (buflen <= 0) { saved_errno = errno; freerecvbuf(rb); errno = saved_errno; return (int)buflen; } /* * Got one. Mark how and when it got here, * put it on the full list and do bookkeeping. */ rb->recv_length = (size_t)buflen; rb->recv_peer = rp->srcclock; rb->dstadr = 0; rb->cast_flags = 0; rb->fd = fd; rb->recv_time = ts; rb->receiver = rp->clock_recv; rb->network_packet = false; consumed = indicate_refclock_packet(rp, rb); if (!consumed) { rp->recvcount++; packets_received++; } return (int)buflen; } #endif /* REFCLOCK */ /* * Routine to read the network NTP packets for a specific interface * Return the number of bytes read. That way we know if we should * read it again or go on to the next one if no bytes returned */ static int read_network_packet( SOCKET fd, endpt * itf, l_fp ts ) { socklen_t fromlen; ssize_t buflen; struct recvbuf *rb; struct msghdr msghdr; struct iovec iovec; char control[100]; /* FIXME: Need space for time stamp plus overhead */ /* * Get a buffer and read the frame. If we * haven't got a buffer, or this is received * on a disallowed socket, just dump the * packet. */ rb = get_free_recv_buffer(); if (NULL == rb || itf->ignore_packets) { char buf[RX_BUFF_SIZE]; sockaddr_u from; if (rb != NULL) freerecvbuf(rb); fromlen = sizeof(from); buflen = recvfrom(fd, buf, sizeof(buf), 0, &from.sa, &fromlen); DPRINT(4, ("%s on (%lu) fd=%d from %s\n", (itf->ignore_packets) ? "ignore" : "drop", free_recvbuffs(), fd, socktoa(&from))); if (itf->ignore_packets) packets_ignored++; else packets_dropped++; return (buflen); } fromlen = sizeof(rb->recv_srcadr); iovec.iov_base = &rb->recv_space; iovec.iov_len = sizeof(rb->recv_space); memset(&msghdr, '\0', sizeof(msghdr)); msghdr.msg_name = &rb->recv_srcadr; msghdr.msg_namelen = fromlen; msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; msghdr.msg_flags = 0; msghdr.msg_control = (void *)&control; msghdr.msg_controllen = sizeof(control); buflen = recvmsg(fd, &msghdr, 0); rb->recv_length = (size_t)buflen; if (buflen == 0 || (buflen == -1 && ((EWOULDBLOCK == errno) #ifdef EAGAIN || (EAGAIN == errno) #endif ))) { freerecvbuf(rb); return (buflen); } else if (buflen < 0) { msyslog(LOG_ERR, "IO: recvfrom(%s) fd=%d: %m", socktoa(&rb->recv_srcadr), fd); DPRINT(5, ("read_network_packet: fd=%d dropped (bad recvfrom)\n", fd)); freerecvbuf(rb); return (buflen); } DPRINT(3, ("read_network_packet: fd=%d length %d from %s\n", fd, (int)buflen, socktoa(&rb->recv_srcadr))); /* * We used to drop network packets with addresses matching the magic * refclock format here. Now we do the check in the protocol machine, * rejecting any source address that matches an active clock. */ /* ** Classic Bug 2672: Some OSes (MacOSX, Linux) don't block spoofed ::1 */ if (AF_INET6 == itf->family) { DPRINT(2, ("Got an IPv6 packet, from <%s> (%d) to <%s> (%d)\n", socktoa(&rb->recv_srcadr), IN6_IS_ADDR_LOOPBACK(PSOCK_ADDR6(&rb->recv_srcadr)), socktoa(&itf->sin), !IN6_IS_ADDR_LOOPBACK(PSOCK_ADDR6(&itf->sin)) )); if ( IN6_IS_ADDR_LOOPBACK(PSOCK_ADDR6(&rb->recv_srcadr)) && !IN6_IS_ADDR_LOOPBACK(PSOCK_ADDR6(&itf->sin)) ) { packets_dropped++; DPRINT(2, ("DROPPING that packet\n")); freerecvbuf(rb); return buflen; } DPRINT(2, ("processing that packet\n")); } /* * Got one. Mark how and when it got here, * put it on the full list and do bookkeeping. */ rb->dstadr = itf; rb->cast_flags = (uint8_t)(rb->fd == rb->dstadr->bfd ? MDF_BCAST : MDF_UCAST); rb->fd = fd; ts = fetch_packetstamp(rb, &msghdr, ts); rb->recv_time = ts; rb->receiver = receive; #ifdef REFCLOCK rb->network_packet = true; #endif /* REFCLOCK */ add_full_recv_buffer(rb); itf->received++; packets_received++; return (buflen); } /* * attempt to handle io */ void io_handler(void) { bool flag; sigset_t runMask; fd_set rdfdes; int nfound; /* * Use select() on all input fd's for unlimited * time. select() will terminate on SIGALARM or on the * reception of input. */ pthread_sigmask(SIG_BLOCK, &blockMask, &runMask); flag = sawALRM || sawQuit || sawHUP || sawDNS; if (!flag) { rdfdes = activefds; nfound = pselect(maxactivefd+1, &rdfdes, NULL, NULL, NULL, &runMask); } else { nfound = -1; errno = EINTR; } pthread_sigmask(SIG_SETMASK, &runMask, NULL); if (nfound > 0) { l_fp ts; get_systime(&ts); input_handler(&rdfdes, &ts); } else if (nfound == -1 && errno != EINTR) { msyslog(LOG_ERR, "IO: select() error: %m"); } # ifdef DEBUG else if (debug > 4) { /* SPECIAL DEBUG */ msyslog(LOG_DEBUG, "IO: select(): nfound=%d, error: %m", nfound); } else { DPRINT(1, ("select() returned %d: %m\n", nfound)); } # endif /* DEBUG */ } /* * input_handler - receive packets */ static void input_handler( fd_set * fds, l_fp * cts ) { int buflen; int doing; SOCKET fd; l_fp ts; /* Timestamp at BOselect() gob */ #ifdef ENABLE_DEBUG_TIMING l_fp ts_e; /* Timestamp at EOselect() gob */ #endif size_t select_count; endpt * ep; #ifdef REFCLOCK struct refclockio *rp; int saved_errno; const char * clk; #endif #ifdef USE_ROUTING_SOCKET struct asyncio_reader * asyncio_reader; struct asyncio_reader * next_asyncio_reader; #endif handler_calls++; select_count = 0; /* * If we have something to do, freeze a timestamp. * See below for the other cases (nothing left to do or error) */ ts = *cts; ++handler_pkts; #ifdef REFCLOCK /* * Check out the reference clocks first, if any */ if (refio != NULL) { for (rp = refio; rp != NULL; rp = rp->next) { fd = rp->fd; if (!FD_ISSET(fd, fds)) continue; ++select_count; buflen = read_refclock_packet(fd, rp, ts); /* * The first read must succeed after select() * indicates readability, or we've reached * a permanent EOF. http://bugs.ntp.org/1732 * reported ntpd munching CPU after a USB GPS * was unplugged because select was indicating * EOF but ntpd didn't remove the descriptor * from the activefds set. */ if (buflen < 0 && EAGAIN != errno) { saved_errno = errno; clk = refclock_name(rp->srcclock); errno = saved_errno; msyslog(LOG_ERR, "IO: %s read: %m", clk); maintain_activefds(fd, true); } else if (0 == buflen) { clk = refclock_name(rp->srcclock); msyslog(LOG_ERR, "IO: %s read EOF", clk); maintain_activefds(fd, true); } else { /* drain any remaining refclock input */ do { buflen = read_refclock_packet(fd, rp, ts); } while (buflen > 0); } } } #endif /* REFCLOCK */ /* * Loop through the interfaces looking for data to read. */ for (ep = ep_list; ep != NULL; ep = ep->elink) { for (doing = 0; doing < 2; doing++) { if (!doing) { fd = ep->fd; } else { if (!(ep->flags & INT_BCASTOPEN)) break; fd = ep->bfd; } if (fd < 0) continue; if (FD_ISSET(fd, fds)) do { ++select_count; buflen = read_network_packet( fd, ep, ts); } while (buflen > 0); /* Check more interfaces */ } } #ifdef USE_ROUTING_SOCKET /* * scan list of asyncio readers - currently only used for routing sockets */ asyncio_reader = asyncio_reader_list; while (asyncio_reader != NULL) { /* callback may unlink and free asyncio_reader */ next_asyncio_reader = asyncio_reader->link; if (FD_ISSET(asyncio_reader->fd, fds)) { ++select_count; (*asyncio_reader->receiver)(asyncio_reader); } asyncio_reader = next_asyncio_reader; } #endif /* USE_ROUTING_SOCKET */ /* * Done everything from that select. * If nothing to do, just return. * If an error occurred, complain and return. */ if (select_count == 0) { /* We really had nothing to do */ #ifdef DEBUG if (debug) /* SPECIAL DEBUG */ msyslog(LOG_DEBUG, "IO: input_handler: select() returned 0"); #endif /* DEBUG */ return; } /* We've done our work */ #ifdef ENABLE_DEBUG_TIMING get_systime(&ts_e); /* * (ts_e - ts) is the amount of time we spent processing this * gob of file descriptors. Log it. */ ts_e -= ts; collect_timing(NULL, "input handler", 1, ts_e); if (debug > 3) /* SPECIAL DEBUG */ msyslog(LOG_DEBUG, "IO: input_handler: Processed a gob of fd's in %s msec", lfptoms(ts_e, 6)); #endif /* ENABLE_DEBUG_TIMING */ /* We're done... */ return; } /* * find an interface suitable for the src address */ endpt * select_peerinterface( struct peer * peer, sockaddr_u * srcadr, endpt * dstadr ) { endpt *ep; endpt *wild; wild = ANY_INTERFACE_CHOOSE(srcadr); /* * Initialize the peer structure and dance the interface jig. * Reference clocks step the loopback waltz, the others * squaredance around the interface list looking for a buddy. If * the dance peters out, there is always the wildcard interface. * This might happen in some systems and would preclude proper * operation with public key cryptography. */ if (IS_PEER_REFCLOCK(peer)) { ep = loopback_interface; } else if (peer->cast_flags & MDF_BCAST) { ep = findbcastinter(srcadr); if (ep != NULL) DPRINT(4, ("Found *-cast interface %s for address %s\n", socktoa(&ep->sin), socktoa(srcadr))); else DPRINT(4, ("No *-cast local address found for address %s\n", socktoa(srcadr))); } else { ep = dstadr; if (NULL == ep) ep = wild; } /* * For unicast, we get to find the interface when dstadr is * given to us as the wildcard (ANY_INTERFACE_CHOOSE). */ if (wild == ep) ep = findinterface(srcadr); /* * we do not bind to the wildcard interfaces for output * as our (network) source address would be undefined and * crypto will not work without knowing the own transmit address */ if (ep != NULL && INT_WILDCARD & ep->flags) if (!accept_wildcard_if_for_winnt) ep = NULL; return ep; } /* * findinterface - find local interface corresponding to address */ endpt * findinterface( sockaddr_u *addr ) { endpt *iface; iface = findlocalinterface(addr, INT_WILDCARD, 0); if (NULL == iface) { DPRINT(4, ("Found no interface for address %s - returning wildcard\n", socktoa(addr))); iface = ANY_INTERFACE_CHOOSE(addr); } else DPRINT(4, ("Found interface #%u %s for address %s\n", iface->ifnum, iface->name, socktoa(addr))); return iface; } /* * findlocalinterface - find local interface corresponding to addr, * which does not have any of flags set. If bast is nonzero, addr is * a broadcast address. * * This code attempts to find the local sending address for an outgoing * address by connecting a new socket to destinationaddress:NTP_PORT * and reading the sockname of the resulting connect. * the complicated sequence simulates the routing table lookup * for to first hop without duplicating any of the routing logic into * ntpd. preferably we would have used an API call - but its not there - * so this is the best we can do here short of duplicating to entire routing * logic in ntpd which would be a silly and really unportable thing to do. * */ static endpt * findlocalinterface( sockaddr_u * addr, int flags, int bcast ) { socklen_t sockaddrlen; endpt * iface; sockaddr_u saddr; SOCKET s; int rtn; int on; DPRINT(4, ("Finding interface for addr %s in list of addresses\n", socktoa(addr))); s = socket(AF(addr), SOCK_DGRAM, 0); if (INVALID_SOCKET == s) return NULL; /* * If we are looking for broadcast interface we need to set this * socket to allow broadcast */ if (bcast) { on = 1; if (SOCKET_ERROR == setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on))) { close(s); return NULL; } } rtn = connect(s, &addr->sa, SOCKLEN(addr)); if (SOCKET_ERROR == rtn) { close(s); return NULL; } sockaddrlen = sizeof(saddr); rtn = getsockname(s, &saddr.sa, &sockaddrlen); close(s); if (SOCKET_ERROR == rtn) return NULL; DPRINT(4, ("findlocalinterface: kernel maps %s to %s\n", socktoa(addr), socktoa(&saddr))); iface = getinterface(&saddr, (uint32_t)flags); /* * if we didn't find an exact match on saddr, find the closest * available local address. This handles the case of the * address suggested by the kernel being excluded by nic rules * or the user's -I and -L options to ntpd. * See http://bugs.ntp.org/1184 and http://bugs.ntp.org/1683 * for more background. */ if (NULL == iface || iface->ignore_packets) iface = findclosestinterface(&saddr, (int)(flags | (int)INT_LOOPBACK)); /* Don't use an interface which will ignore replies */ if (iface != NULL && iface->ignore_packets) iface = NULL; return iface; } /* * findclosestinterface * * If there are -I/--interface or -L/novirtualips command-line options, * or "nic" or "interface" rules in ntp.conf, findlocalinterface() may * find the kernel's preferred local address for a given peer address is * administratively unavailable to ntpd, and punt to this routine's more * expensive search. * * Find the numerically closest local address to the one connect() * suggested. This matches an address on the same subnet first, as * needed by Classic Bug 1184, and provides a consistent choice if * there are multiple feasible local addresses, regardless of the * order ntpd enumerated them. */ endpt * findclosestinterface( sockaddr_u * addr, int flags ) { endpt * ep; endpt * winner; sockaddr_u addr_dist; sockaddr_u min_dist; ZERO_SOCK(&min_dist); winner = NULL; for (ep = ep_list; ep != NULL; ep = ep->elink) { if (ep->ignore_packets || AF(addr) != ep->family || (unsigned int)flags & ep->flags) continue; calc_addr_distance(&addr_dist, addr, &ep->sin); if (NULL == winner || -1 == cmp_addr_distance(&addr_dist, &min_dist)) { min_dist = addr_dist; winner = ep; } } if (NULL == winner) DPRINT(4, ("findclosestinterface(%s) failed\n", socktoa(addr))); else DPRINT(4, ("findclosestinterface(%s) -> %s\n", socktoa(addr), socktoa(&winner->sin))); return winner; } /* * calc_addr_distance - calculate the distance between two addresses, * the absolute value of the difference between * the addresses numerically, stored as an address. */ static void calc_addr_distance( sockaddr_u * dist, const sockaddr_u * a1, const sockaddr_u * a2 ) { uint32_t a1val; uint32_t a2val; uint32_t v4dist; bool found_greater; bool a1_greater; int i; REQUIRE(AF(a1) == AF(a2)); ZERO_SOCK(dist); AF(dist) = AF(a1); /* v4 can be done a bit simpler */ if (IS_IPV4(a1)) { a1val = SRCADR(a1); a2val = SRCADR(a2); v4dist = (a1val > a2val) ? a1val - a2val : a2val - a1val; SET_ADDR4(dist, v4dist); return; } found_greater = false; a1_greater = false; /* suppress pot. uninit. warning */ for (i = 0; i < (int)sizeof(NSRCADR6(a1)); i++) { if (!found_greater && NSRCADR6(a1)[i] != NSRCADR6(a2)[i]) { found_greater = true; a1_greater = (NSRCADR6(a1)[i] > NSRCADR6(a2)[i]); } if (!found_greater) { NSRCADR6(dist)[i] = 0; } else { if (a1_greater) NSRCADR6(dist)[i] = NSRCADR6(a1)[i] - NSRCADR6(a2)[i]; else NSRCADR6(dist)[i] = NSRCADR6(a2)[i] - NSRCADR6(a1)[i]; } } } /* * cmp_addr_distance - compare two address distances, returning -1, 0, * 1 to indicate their relationship. */ static int cmp_addr_distance( const sockaddr_u * d1, const sockaddr_u * d2 ) { int i; REQUIRE(AF(d1) == AF(d2)); if (IS_IPV4(d1)) { if (SRCADR(d1) < SRCADR(d2)) return COMPARE_LESSTHAN; else if (SRCADR(d1) == SRCADR(d2)) return COMPARE_EQUAL; else return COMPARE_GREATERTHAN; } for (i = 0; i < (int)sizeof(NSRCADR6(d1)); i++) { if (NSRCADR6(d1)[i] < NSRCADR6(d2)[i]) return COMPARE_LESSTHAN; else if (NSRCADR6(d1)[i] > NSRCADR6(d2)[i]) return COMPARE_GREATERTHAN; } return COMPARE_EQUAL; } /* * fetch an interface structure the matches the * address and has the given flags NOT set */ endpt * getinterface( sockaddr_u * addr, uint32_t flags ) { endpt *iface; iface = find_addr_in_list(addr); if (iface != NULL && (iface->flags & flags)) iface = NULL; return iface; } /* * findbcastinter - find broadcast interface corresponding to address */ static endpt * findbcastinter( sockaddr_u *addr ) { endpt * iface; iface = NULL; #if defined(SIOCGIFCONF) DPRINT(4, ("Finding broadcast interface for addr %s in list of addresses\n", socktoa(addr))); iface = findlocalinterface(addr, INT_LOOPBACK | INT_WILDCARD, 1); if (iface != NULL) { DPRINT(4, ("Easily found bcast-/mcast- interface index #%d %s\n", iface->ifnum, iface->name)); return iface; } /* * plan B - try to find something reasonable in our lists in * case kernel lookup doesn't help */ for (iface = ep_list; iface != NULL; iface = iface->elink) { if (iface->flags & INT_WILDCARD) continue; /* Don't bother with ignored interfaces */ if (iface->ignore_packets) continue; /* * First look if this is the correct family */ if(AF(&iface->sin) != AF(addr)) continue; /* Skip the loopback addresses */ if (iface->flags & INT_LOOPBACK) continue; /* * We match only those interfaces marked as * broadcastable and either the explicit broadcast * address or the network portion of the IP address. * Sloppy. */ if (IS_IPV4(addr)) { if (SOCK_EQ(&iface->bcast, addr)) break; if ((NSRCADR(&iface->sin) & NSRCADR(&iface->mask)) == (NSRCADR(addr) & NSRCADR(&iface->mask))) break; } else if (IS_IPV6(addr)) { if (SOCK_EQ(&iface->bcast, addr)) break; if (SOCK_EQ(netof6(&iface->sin), netof6(addr))) break; } } #endif /* SIOCGIFCONF */ if (NULL == iface) { DPRINT(4, ("No bcast interface found for %s\n", socktoa(addr))); iface = ANY_INTERFACE_CHOOSE(addr); } else { DPRINT(4, ("Found bcast-/mcast- interface index #%u %s\n", iface->ifnum, iface->name)); } return iface; } /* * io_clr_stats - clear I/O module statistics */ void io_clr_stats(void) { packets_dropped = 0; packets_ignored = 0; packets_received = 0; packets_sent = 0; packets_notsent = 0; handler_calls = 0; handler_pkts = 0; io_timereset = current_time; } #ifdef REFCLOCK /* * io_addclock - add a reference clock to the list and arrange that we * get SIGIO interrupts from it. */ bool io_addclock( struct refclockio *rio ) { /* * Stuff the I/O structure in the list and mark the descriptor * in use. There is a harmless (I hope) race condition here. */ rio->active = true; /* * enqueue */ LINK_SLIST(refio, rio, next); /* * register fd */ add_fd_to_list(rio->fd, FD_TYPE_FILE); return true; } /* * io_closeclock - close the clock in the I/O structure given */ void io_closeclock( struct refclockio *rio ) { struct refclockio *unlinked; /* * Remove structure from the list */ rio->active = false; UNLINK_SLIST(unlinked, refio, rio, next, struct refclockio); if (NULL != unlinked) { purge_recv_buffers_for_fd(rio->fd); /* * Close the descriptor. */ close_and_delete_fd_from_list(rio->fd); } rio->fd = -1; } #endif /* REFCLOCK */ /* * Add and delete functions for the list of open sockets */ static void add_fd_to_list( SOCKET fd, enum desc_type type ) { vsock_t *lsock = emalloc(sizeof(*lsock)); lsock->fd = fd; lsock->type = type; LINK_SLIST(fd_list, lsock, link); maintain_activefds(fd, false); } static void close_and_delete_fd_from_list( SOCKET fd ) { vsock_t *lsock; UNLINK_EXPR_SLIST(lsock, fd_list, fd == UNLINK_EXPR_SLIST_CURRENT()->fd, link, vsock_t); if (NULL == lsock) return; switch (lsock->type) { case FD_TYPE_SOCKET: close(lsock->fd); break; case FD_TYPE_FILE: close(lsock->fd); break; default: msyslog(LOG_ERR, "IO: internal error - illegal descriptor type %d - EXITING", (int)lsock->type); exit(1); } free(lsock); /* * remove from activefds */ maintain_activefds(fd, true); } static void add_addr_to_list( sockaddr_u * addr, endpt * ep ) { remaddr_t *laddr; #ifdef DEBUG if (find_addr_in_list(addr) == NULL) { #endif /* not there yet - add to list */ laddr = emalloc(sizeof(*laddr)); laddr->addr = *addr; laddr->ep = ep; LINK_SLIST(remoteaddr_list, laddr, link); DPRINT(4, ("Added addr %s to list of addresses\n", socktoa(addr))); #ifdef DEBUG } else DPRINT(4, ("WARNING: Attempt to add duplicate addr %s to address list\n", socktoa(addr))); #endif } static void delete_interface_from_list( endpt *iface ) { remaddr_t *unlinked; for (;;) { UNLINK_EXPR_SLIST(unlinked, remoteaddr_list, iface == UNLINK_EXPR_SLIST_CURRENT()->ep, link, remaddr_t); if (unlinked == NULL) break; DPRINT(4, ("Deleted addr %s for interface #%u %s " "from list of addresses\n", socktoa(&unlinked->addr), iface->ifnum, iface->name)); free(unlinked); } } static endpt * find_addr_in_list( sockaddr_u *addr ) { remaddr_t *entry; DPRINT(4, ("Searching for addr %s in list of addresses - ", socktoa(addr))); for (entry = remoteaddr_list; entry != NULL; entry = entry->link) if (SOCK_EQ(&entry->addr, addr)) { DPRINT(4, ("FOUND\n")); return entry->ep; } DPRINT(4, ("NOT FOUND\n")); return NULL; } const char * latoa( endpt *la ) { return (NULL == la) ? "" : socktoa(&la->sin); } #ifdef USE_ROUTING_SOCKET # ifndef UPDATE_GRACE # define UPDATE_GRACE 2 /* wait UPDATE_GRACE seconds before scanning */ # endif static void process_routing_msgs(struct asyncio_reader *reader) { char buffer[5120]; ssize_t cnt; int msg_type; #ifdef HAVE_LINUX_RTNETLINK_H struct nlmsghdr *nh; #else struct rt_msghdr rtm; char *p; #endif if (disable_dynamic_updates) { /* * discard ourselves if we are not needed any more * usually happens when running unprivileged */ remove_asyncio_reader(reader); delete_asyncio_reader(reader); return; } cnt = read(reader->fd, buffer, sizeof(buffer)); if (cnt < 0) { if (errno == ENOBUFS) { msyslog(LOG_ERR, "IO: routing socket reports: %m"); } else { msyslog(LOG_ERR, "IO: routing socket reports: %m - disabling"); remove_asyncio_reader(reader); delete_asyncio_reader(reader); } return; } /* * process routing message */ #ifdef HAVE_LINUX_RTNETLINK_H for (nh = (struct nlmsghdr *)buffer; NLMSG_OK(nh, (unsigned) cnt); nh = NLMSG_NEXT(nh, cnt)) { msg_type = nh->nlmsg_type; #else for (p = buffer; (p + sizeof(struct rt_msghdr)) <= (buffer + cnt); p += rtm.rtm_msglen) { memcpy(&rtm, p, sizeof(rtm)); if (rtm.rtm_version != RTM_VERSION) { msyslog(LOG_ERR, "IO: version mismatch (got %d - expected %d) on routing socket - disabling", rtm.rtm_version, RTM_VERSION); remove_asyncio_reader(reader); delete_asyncio_reader(reader); return; } msg_type = rtm.rtm_type; #endif switch (msg_type) { #ifdef RTM_NEWADDR case RTM_NEWADDR: #endif #ifdef RTM_DELADDR case RTM_DELADDR: #endif #ifdef RTM_ADD case RTM_ADD: #endif #ifdef RTM_DELETE case RTM_DELETE: #endif #ifdef RTM_REDIRECT case RTM_REDIRECT: #endif #ifdef RTM_CHANGE case RTM_CHANGE: #endif #ifdef RTM_LOSING case RTM_LOSING: #endif #ifdef RTM_IFINFO case RTM_IFINFO: #endif #ifdef RTM_IFANNOUNCE case RTM_IFANNOUNCE: #endif #ifdef RTM_NEWLINK case RTM_NEWLINK: #endif #ifdef RTM_DELLINK case RTM_DELLINK: #endif #ifdef RTM_NEWROUTE case RTM_NEWROUTE: #endif #ifdef RTM_DELROUTE case RTM_DELROUTE: #endif /* * we are keen on new and deleted addresses and * if an interface goes up and down or routing * changes */ DPRINT(3, ("routing message op = %d: scheduling interface update\n", msg_type)); timer_interfacetimeout(current_time + UPDATE_GRACE); break; #ifdef HAVE_LINUX_RTNETLINK_H case NLMSG_DONE: /* end of multipart message */ return; #endif default: /* * the rest doesn't bother us. */ DPRINT(4, ("routing message op = %d: ignored\n", msg_type)); break; } } } /* * set up routing notifications */ static void init_async_notifications() { struct asyncio_reader *reader; #ifdef HAVE_LINUX_RTNETLINK_H int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); struct sockaddr_nl sa; #else int fd = socket(PF_ROUTE, SOCK_RAW, 0); #endif if (fd < 0) { msyslog(LOG_ERR, "IO: unable to open routing socket (%m) - using polled interface update"); return; } fd = move_fd(fd); #ifdef HAVE_LINUX_RTNETLINK_H ZERO(sa); sa.nl_family = PF_NETLINK; sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_MROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_MROUTE; if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { msyslog(LOG_ERR, "IO: bind failed on routing socket (%m) - using polled interface update"); return; } #endif make_socket_nonblocking(fd); reader = new_asyncio_reader(); reader->fd = fd; reader->receiver = process_routing_msgs; add_asyncio_reader(reader, FD_TYPE_SOCKET); msyslog(LOG_INFO, "IO: Listening on routing socket on fd #%d for interface updates", fd); } #else /* USE_ROUTING_SOCKET not defined */ static void init_async_notifications(void) { } #endif ntpsec-1.1.0+dfsg1/ntpd/declcond.h0000644000175000017500000000145313252364117016521 0ustar rlaagerrlaager/* * declcond.h - declarations conditionalized for ntpd * * The NTP reference implementation distribution includes two distinct * declcond.h files, one in ntpd/ used only by ntpd, and another in * include/ used by libntp and utilities. This relies on the source * file's directory being ahead of include/ in the include search. * * The ntpd variant of declcond.h declares "debug" only #ifdef DEBUG, * as the --disable-debugging version of ntpd should not reference * "debug". The libntp and utilities variant always declares debug, * as it is used in those codebases even without DEBUG defined. */ #ifndef GUARD_DECLCOND_H #define GUARD_DECLCOND_H #ifdef DEBUG /* uncommented in ntpd/declcond.h */ extern int debug; #endif /* uncommented in ntpd/declcond.h */ #endif /* GUARD_DECLCOND_H */ ntpsec-1.1.0+dfsg1/ntpd/refclock_conf.c0000644000175000017500000000642513252364117017542 0ustar rlaagerrlaager/* * refclock_conf.c - reference clock configuration * * This is the only place in the code that knows how to map driver numeric types * to driver method tables and their attributes. */ #include "config.h" #include #include #include "ntpd.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" struct refclock refclock_none = { NULL, /* basename of driver */ NULL, /* start up driver */ NULL, /* shut down driver */ NULL, /* transmit poll message */ NULL, /* not used (old hpgps_control) */ NULL, /* initialize driver */ NULL, /* timer - not used */ }; /* * This is the only place in the code that knows about the mapping between * old-style numeric driver types and the drivers. * * The symbols in the comments are no longer defined. If you compact or * reorder this table, legacy ntpq instances that think they can * deduce driver types from address fields will get them wrong. * Also the addresses generated into statistics files won't match * what people expect - the type in the third octet will be off. */ struct refclock * const refclock_conf[] = { &refclock_none, /* 0 REFCLK_NONE */ &refclock_local, /* 1 REFCLK_LOCAL */ &refclock_none, /* 2 was: REFCLK_GPS_TRAK */ &refclock_none, /* 3 was: REFCLK_WWV_PST */ &refclock_spectracom, /* 4 REFCLK_SPECTRACOM */ &refclock_true, /* 5 REFCLK_TRUETIME */ &refclock_none, /* 6 was: REFCLK_IRIG_AUDIO */ &refclock_none, /* 7 was: REFCLK_CHU_AUDIO */ &refclock_parse, /* 8 REFCLK_GENERIC */ &refclock_none, /* 9 was: REFCLK_GPS_MX4200 */ &refclock_none, /* 10 was: REFCLK_GPS_AS2201 */ &refclock_arbiter, /* 11 REFCLK_GPS_ARBITER */ &refclock_none, /* 12 was: REFCLK_IRIG_TPRO */ &refclock_none, /* 13 was: REFCLK_ATOM_LEITCH */ &refclock_none, /* 14 was: REFCLOCK_MSF_EES */ &refclock_none, /* 15 was: OLD TrueTime GPS/TM-TMD Receiver */ &refclock_none, /* 16 was: REFCLK_IRIG_BANCOMM */ &refclock_none, /* 17 was: REFCLK_GPS_DATUM */ &refclock_modem, /* 18 REFCLK_ACTS */ &refclock_none, /* 19 was: REFCLK_WWV_HEATH */ &refclock_nmea, /* 20 REFCLK_GPS_NMEA */ &refclock_none, /* 21 was: REFCLK_GPS_VME */ &refclock_pps, /* 22 REFCLK_ATOM_PPS */ &refclock_none, /* 23 not used */ &refclock_none, /* 24 not used */ &refclock_none, /* 25 not used */ &refclock_hpgps, /* 26 REFCLK_GPS_HP */ &refclock_none, /* 27 was: REFCLK_ARCRON_MSF */ &refclock_shm, /* 28 REFCLK_SHM */ &refclock_trimble, /* 29 REFCLK_PALISADE */ &refclock_oncore, /* 30 REFCLK_ONCORE */ &refclock_none, /* 31 was: REFCLK_GPS_JUPITER */ &refclock_none, /* 32 was: REFCLK_CHRONOLOG */ &refclock_none, /* 33 was: REFCLK_DUMBCLOCK */ &refclock_none, /* 34 was: REFCLOCK_ULINK */ &refclock_none, /* 35 was: REFCLOCK_PCF */ &refclock_none, /* 36 was: REFCLOCK_WWV_AUDIO */ &refclock_none, /* 37 was: REFCLOCK_FG */ &refclock_none, /* 38 was: REFCLK_HOPF_SERIAL */ &refclock_none, /* 39 was: REFCLK_HOPF_PCI */ &refclock_jjy, /* 40 REFCLK_JJY */ &refclock_none, /* 41 was: REFCLK_TT560 */ &refclock_zyfer, /* 42 REFCLK_ZYFER */ &refclock_none, /* 43 was: REFCLK_RIPENCC */ &refclock_neoclock4x, /* 44 REFCLK_NEOCLOCK4X */ &refclock_none, /* 45 was: REFCLK_TSYNCPCI */ &refclock_gpsdjson /* 46 REFCLK_GPSDJSON */ }; const uint8_t num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *); ntpsec-1.1.0+dfsg1/ntpd/refclock_neoclock.c0000644000175000017500000007064313252364117020415 0ustar rlaagerrlaager/* * * Refclock_neoclock4x.c * - NeoClock4X driver for DCF77 or FIA Timecode * * Date: 2009-12-04 v1.16 * * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir * for details about the NeoClock4X device * */ #include "config.h" #include #include #include #include #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "timespecops.h" #include "ntp_calendar.h" /* for SECSPERHR */ #define NAME "NEOCLOCK" #define DESCRIPTION "NeoClock4X" /* * WARNING: This driver depends on the system clock for year * disambiguation. It will thus not be usable for recovery if the * system clock is trashed. * * If you want the driver for whatever reason to not use * the TX line to send anything to your NeoClock4X * device you must tell the NTP refclock driver which * firmware you NeoClock4X device uses. * * If you want to enable this feature change the "#if 0" * line to "#if 1" and make sure that the defined firmware * matches the firmware off your NeoClock4X receiver! * */ #if 0 #define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A #endif #if defined(NEOCLOCK4X_FIRMWARE) /* at this time only firmware version A is known */ # define NEOCLOCK4X_FIRMWARE_VERSION_A 'A' #endif #define NEOCLOCK4X_TIMECODELEN 37 #define NEOCLOCK4X_OFFSET_SERIAL 3 #define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9 #define NEOCLOCK4X_OFFSET_DAY 12 #define NEOCLOCK4X_OFFSET_MONTH 14 #define NEOCLOCK4X_OFFSET_YEAR 16 #define NEOCLOCK4X_OFFSET_HOUR 18 #define NEOCLOCK4X_OFFSET_MINUTE 20 #define NEOCLOCK4X_OFFSET_SECOND 22 #define NEOCLOCK4X_OFFSET_HSEC 24 /* #define NEOCLOCK4X_OFFSET_DOW 26 UNUSED */ #define NEOCLOCK4X_OFFSET_TIMESOURCE 28 #define NEOCLOCK4X_OFFSET_DSTSTATUS 29 #define NEOCLOCK4X_OFFSET_QUARZSTATUS 30 #define NEOCLOCK4X_OFFSET_ANTENNA1 31 #define NEOCLOCK4X_OFFSET_ANTENNA2 33 #define NEOCLOCK4X_OFFSET_CRC 35 #define NEOCLOCK4X_DRIVER_VERSION "1.16 (2009-12-04)" #define NSEC_TO_MILLI 1000000 struct neoclock4x_unit { l_fp laststamp; /* last receive timestamp */ short unit; /* NTP refclock unit number */ unsigned long polled; /* flag to detect noreplies */ char leap_status; /* leap second flag */ bool recvnow; char firmware[80]; char firmwaretag; char serial[7]; char radiosignal[4]; char timesource; char dststatus; char quarzstatus; int antenna1; int antenna2; int utc_year; int utc_month; int utc_day; int utc_hour; int utc_minute; int utc_second; int utc_msec; }; static bool neoclock4x_start (int, struct peer *); static void neoclock4x_shutdown (struct refclockproc *); static void neoclock4x_receive (struct recvbuf *); static void neoclock4x_poll (int, struct peer *); static void neoclock4x_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); static int neol_atoi_len (const char str[], int *, int); static int neol_hexatoi_len (const char str[], int *, int); static void neol_jdn_to_ymd (unsigned long, int *, int *, int *); static void neol_localtime (unsigned long, int* , int*, int*, int*, int*, int*); static unsigned long neol_mktime (int, int, int, int, int, int); #if !defined(NEOCLOCK4X_FIRMWARE) static int neol_query_firmware (int, int, char *, size_t); static bool neol_check_firmware (int, const char*, char *); #endif struct refclock refclock_neoclock4x = { NAME, /* basename of driver */ neoclock4x_start, /* start up driver */ neoclock4x_shutdown, /* shut down driver */ neoclock4x_poll, /* transmit poll message */ neoclock4x_control, /* device control */ NULL, /* initialize driver (not used) */ NULL, /* tiner - not used */ }; static bool neoclock4x_start(int unit, struct peer *peer) { struct neoclock4x_unit *up; struct refclockproc *pp; int fd; char dev[20]; int sl232; struct termios termsettings; (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit); /* LDISC_STD, LDISC_RAW * Open serial port. Use CLK line discipline, if available. */ fd = refclock_open(peer->cfg.path ? peer->cfg.path : dev, peer->cfg.baud ? peer->cfg.baud : B2400, LDISC_STD); if(fd <= 0) { /* coverity[leaked_handle] */ return false; } #if 1 if(tcgetattr(fd, &termsettings) < 0) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); (void) close(fd); return false; } /* 2400 Baud 8N2 */ termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL; termsettings.c_oflag = 0; termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD; (void)cfsetispeed(&termsettings, (unsigned int)B2400); (void)cfsetospeed(&termsettings, (unsigned int)B2400); if(tcsetattr(fd, TCSANOW, &termsettings) < 0) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); (void) close(fd); return false; } #else if(tcgetattr(fd, &termsettings) < 0) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); (void) close(fd); return false; } /* 2400 Baud 8N2 */ termsettings.c_cflag &= ~PARENB; termsettings.c_cflag |= CSTOPB; termsettings.c_cflag &= ~CSIZE; termsettings.c_cflag |= CS8; if(tcsetattr(fd, TCSANOW, &termsettings) < 0) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); (void) close(fd); return false; } #endif #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) /* turn on RTS, and DTR for power supply */ /* NeoClock4x is powered from serial line */ if(ioctl(fd, TIOCMGET, (void *)&sl232) == -1) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): can't query RTS/DTR state: %m", unit); (void) close(fd); return false; } #ifdef TIOCM_RTS sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */ #else sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */ #endif if(ioctl(fd, TIOCMSET, (void *)&sl232) == -1) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); (void) close(fd); return false; } #else msyslog(LOG_EMERG, "REFCLOCK: NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!", unit); (void) close(fd); return false; #endif up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit)); if(!(up)) { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): can't allocate memory for: %m",unit); (void) close(fd); return false; } memset((char *)up, 0, sizeof(struct neoclock4x_unit)); pp = peer->procptr; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; pp->unitptr = up; pp->io.clock_recv = neoclock4x_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd; /* * no fudge time is given by user! * use 169.583333 ms to compensate the serial line delay * formula is: * 2400 Baud / 11 bit = 218.18 characters per second * (NeoClock4X timecode len) */ pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0; /* * Initialize miscellaneous variables */ peer->precision = -10; memcpy((char *)&pp->refid, "neol", REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; up->leap_status = 0; up->unit = (short)unit; strlcpy(up->firmware, "?", sizeof(up->firmware)); up->firmwaretag = '?'; strlcpy(up->serial, "?", sizeof(up->serial)); strlcpy(up->radiosignal, "?", sizeof(up->radiosignal)); up->timesource = '?'; up->dststatus = '?'; up->quarzstatus = '?'; up->antenna1 = -1; up->antenna2 = -1; up->utc_year = 0; up->utc_month = 0; up->utc_day = 0; up->utc_hour = 0; up->utc_minute = 0; up->utc_second = 0; up->utc_msec = 0; #if defined(NEOCLOCK4X_FIRMWARE) #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)", sizeof(up->firmware)); up->firmwaretag = 'A'; #else msyslog(LOG_EMERG, "REFCLOCK: NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X", unit); (void) close(fd); pp->io.fd = -1; free(pp->unitptr); pp->unitptr = NULL; return false; #endif #else for(int tries = 0; tries < 5; tries++) { NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "REFCLOCK: NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries); /* wait 3 seconds for receiver to power up */ sleep(3); if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware))) { break; } } /* can I handle this firmware version? */ if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag)) { (void) close(fd); pp->io.fd = -1; free(pp->unitptr); pp->unitptr = NULL; return false; } #endif if(!io_addclock(&pp->io)) { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): error add peer to ntpd: %m", unit); (void) close(fd); pp->io.fd = -1; free(pp->unitptr); pp->unitptr = NULL; return false; } NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "REFCLOCK: NeoClock4X(%d): receiver setup successful done", unit); return true; } static void neoclock4x_shutdown( struct refclockproc *pp) { struct neoclock4x_unit *up; int sl232; if (pp != NULL) { up = pp->unitptr; if(up != NULL) { if(-1 != pp->io.fd) { #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) /* turn on RTS, and DTR for power supply */ /* NeoClock4x is powered from serial line */ if(ioctl(pp->io.fd, TIOCMGET, (void *)&sl232) == -1) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): can't query RTS/DTR state: %m", pp->refclkunit); } #ifdef TIOCM_RTS /* turn on RTS, and DTR for power supply */ sl232 &= ~(TIOCM_DTR | TIOCM_RTS); #else /* turn on RTS, and DTR for power supply */ sl232 &= ~(CIOCM_DTR | CIOCM_RTS); #endif if(ioctl(pp->io.fd, TIOCMSET, (void *)&sl232) == -1) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", pp->refclkunit); } #endif io_closeclock(&pp->io); } free(up); pp->unitptr = NULL; } } msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): shutdown", pp->refclkunit); NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "REFCLOCK: NeoClock4X(%d): receiver shutdown done", pp->refclkunit); } static void neoclock4x_receive(struct recvbuf *rbufp) { struct neoclock4x_unit *up; struct refclockproc *pp; struct peer *peer; unsigned long calc_utc; int day; int month; /* ddd conversion */ int dsec; unsigned char calc_chksum; int recv_chksum; peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; /* wait till poll interval is reached */ if (!up->recvnow) return; /* reset poll interval flag */ up->recvnow = false; /* read last received timecode */ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); pp->leap = LEAP_NOWARNING; if(NEOCLOCK4X_TIMECODELEN != pp->lencode) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "REFCLOCK: NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s", up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode); refclock_report(peer, CEVNT_BADREPLY); return; } neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); /* calculate checksum */ calc_chksum = 0; for(int c = 0; c < NEOCLOCK4X_OFFSET_CRC; c++) { calc_chksum += pp->a_lastcode[c]; } if(recv_chksum != calc_chksum) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "REFCLOCK: NeoClock4X(%d): received data has invalid chksum: %s", up->unit, pp->a_lastcode); refclock_report(peer, CEVNT_BADREPLY); return; } /* Allow synchronization even is quartz clock is * never initialized. * WARNING: This is dangerous! */ up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; if(0==(pp->sloppyclockflag & CLK_FLAG2)) { if('I' != up->quarzstatus) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_NOTICE, "REFCLOCK: NeoClock4X(%d): quartz clock is not initialized: %s", up->unit, pp->a_lastcode); pp->leap = LEAP_NOTINSYNC; refclock_report(peer, CEVNT_BADDATE); return; } } if('I' != up->quarzstatus) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_NOTICE, "REFCLOCK: NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", up->unit, pp->a_lastcode); } /* * If NeoClock4X is not synchronized to a radio clock * check if we're allowed to synchronize with the quartz * clock. */ up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; if(0==(pp->sloppyclockflag & CLK_FLAG2)) { if('A' != up->timesource) { /* not allowed to sync with quartz clock */ if(0==(pp->sloppyclockflag & CLK_FLAG1)) { refclock_report(peer, CEVNT_BADTIME); pp->leap = LEAP_NOTINSYNC; return; } } } /* this should only used when first install is done */ if(pp->sloppyclockflag & CLK_FLAG4) { msyslog(LOG_DEBUG, "REFCLOCK: NeoClock4X(%d): received data: %s", up->unit, pp->a_lastcode); } /* 123456789012345678901234567890123456789012345 */ /* S/N123456DCF1004021010001202ASX1213CR\r\n */ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2); pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */ memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3); up->radiosignal[3] = 0; memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6); up->serial[6] = 0; up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS]; neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2); neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2); /* Validate received values at least enough to prevent internal array-bounds problems, etc. */ if((pp->hour < 0) || (pp->hour > 23) || (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || (day < 1) || (day > 31) || (month < 1) || (month > 12) || (pp->year < 0) || (pp->year > 99)) { /* Data out of range. */ NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "REFCLOCK: NeoClock4X(%d): date/time out of range: %s", up->unit, pp->a_lastcode); refclock_report(peer, CEVNT_BADDATE); return; } /* Year-2000 check not needed anymore. Same problem * will arise at 2099 but what should we do...? * * wrap 2-digit date into 4-digit * * if(pp->year < YEAR_PIVOT) * { * pp->year += 100; * } */ pp->year += 2000; /* adjust NeoClock4X local time to UTC */ calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second); calc_utc -= SECSPERHR; /* adjust NeoClock4X daylight saving time if needed */ if('S' == up->dststatus) calc_utc -= SECSPERHR; neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second); /* some preparations */ pp->day = ymd2yd(pp->year, month, day); pp->leap = 0; if(pp->sloppyclockflag & CLK_FLAG4) { msyslog(LOG_DEBUG, "REFCLOCK: NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld", up->unit, pp->year, month, day, pp->hour, pp->minute, pp->second, pp->nsec/NSEC_TO_MILLI ); } up->utc_year = pp->year; up->utc_month = month; up->utc_day = day; up->utc_hour = pp->hour; up->utc_minute = pp->minute; up->utc_second = pp->second; up->utc_msec = (int)pp->nsec/NSEC_TO_MILLI; if(!refclock_process(pp)) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "REFCLOCK: NeoClock4X(%d): refclock_process failed!", up->unit); refclock_report(peer, CEVNT_FAULT); return; } refclock_receive(peer); /* report good status */ refclock_report(peer, CEVNT_NOMINAL); record_clock_stats(peer, pp->a_lastcode); } static void neoclock4x_poll(int unit, struct peer *peer) { struct neoclock4x_unit *up; struct refclockproc *pp; UNUSED_ARG(unit); pp = peer->procptr; up = pp->unitptr; pp->polls++; up->recvnow = true; } static void neoclock4x_control(int unit, const struct refclockstat *in, struct refclockstat *out, struct peer *peer) { struct neoclock4x_unit *up; struct refclockproc *pp; if(NULL == peer) { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): control: unit invalid/inactive", unit); return; } pp = peer->procptr; if(NULL == pp) { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): control: unit invalid/inactive", unit); return; } up = pp->unitptr; if(NULL == up) { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): control: unit invalid/inactive", unit); return; } if(NULL != in) { /* check to see if a user supplied time offset is given */ if(in->haveflags & CLK_HAVETIME1) { pp->fudgetime1 = in->fudgetime1; NLOG(NLOG_CLOCKINFO) msyslog(LOG_NOTICE, "REFCLOCK: NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.", unit, pp->fudgetime1); } /* notify */ if(pp->sloppyclockflag & CLK_FLAG1) { NLOG(NLOG_CLOCKINFO) msyslog(LOG_NOTICE, "REFCLOCK: NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit); } else { NLOG(NLOG_CLOCKINFO) msyslog(LOG_NOTICE, "REFCLOCK: NeoClock4X(%d): time is only adjusted with radio signal reception.", unit); } } if(NULL != out) { char *tt; char tmpbuf[80]; out->kv_list = (struct ctl_var *)0; snprintf(tmpbuf, sizeof(tmpbuf)-1, "%04d-%02d-%02d %02d:%02d:%02d.%03d", up->utc_year, up->utc_month, up->utc_day, up->utc_hour, up->utc_minute, up->utc_second, up->utc_msec); tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF); snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf); #define MAXINTSIZE 20 /* max % of decimal digits in integer */ #define S_RADIOSIGNAL sizeof(up->radiosignal) + 15 tt = add_var(&out->kv_list, S_RADIOSIGNAL+1, RO|DEF); snprintf(tt, S_RADIOSIGNAL, "radiosignal=\"%s\"", up->radiosignal); #undef S_RADIOSIGNAL #define S_ANTENNA1 MAXINTSIZE + 12 tt = add_var(&out->kv_list, S_ANTENNA1+1, RO|DEF); snprintf(tt, S_ANTENNA1, "antenna1=\"%d\"", up->antenna1); #undef S_ANTENNA1 #define S_ANTENNA2 MAXINTSIZE + 12 tt = add_var(&out->kv_list, S_ANTENNA2+1, RO|DEF); snprintf(tt, S_ANTENNA2, "antenna2=\"%d\"", up->antenna2); #undef S_ANTENNA2 tt = add_var(&out->kv_list, 40, RO|DEF); if('A' == up->timesource) snprintf(tt, 39, "timesource=\"radio\""); else if('C' == up->timesource) snprintf(tt, 39, "timesource=\"quartz\""); else snprintf(tt, 39, "timesource=\"unknown\""); tt = add_var(&out->kv_list, 40, RO|DEF); if('I' == up->quarzstatus) snprintf(tt, 39, "quartzstatus=\"synchronized\""); else if('X' == up->quarzstatus) snprintf(tt, 39, "quartzstatus=\"not synchronized\""); else snprintf(tt, 39, "quartzstatus=\"unknown\""); tt = add_var(&out->kv_list, 40, RO|DEF); if('S' == up->dststatus) snprintf(tt, 39, "dststatus=\"summer\""); else if('W' == up->dststatus) snprintf(tt, 39, "dststatus=\"winter\""); else snprintf(tt, 39, "dststatus=\"unknown\""); #define S_FIRMWARE sizeof(up->firmware) + 13 tt = add_var(&out->kv_list, S_FIRMWARE+1, RO|DEF); snprintf(tt, S_FIRMWARE, "firmware=\"%s\"", up->firmware); #undef S_FIRMWARE #define S_FIRMWARETAG sizeof(up->firmware) + 15 tt = add_var(&out->kv_list, S_FIRMWARETAG+1, RO|DEF); snprintf(tt, S_FIRMWARETAG, "firmwaretag=\"%c\"", up->firmwaretag); #undef S_FIRMWARETAG tt = add_var(&out->kv_list, 80, RO|DEF); snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION); #define S_SERIAL sizeof(up->serial) + 16 tt = add_var(&out->kv_list, S_SERIAL+1, RO|DEF); snprintf(tt, S_SERIAL, "serialnumber=\"%s\"", up->serial); #undef S_SERIAL #undef MAXINTSIZE } } static int neol_hexatoi_len(const char str[], int *result, int maxlen) { int hexdigit; int n = 0; for(int i = 0; isxdigit((unsigned char)str[i]) && i < maxlen; i++) { hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10; n = 16 * n + hexdigit; } *result = n; return (n); } static int neol_atoi_len(const char str[], int *result, int maxlen) { int digit; int n = 0; for(int i = 0; isdigit((unsigned char)str[i]) && i < maxlen; i++) { digit = str[i] - '0'; n = 10 * n + digit; } *result = n; return (n); } /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. * * [For the Julian calendar (which was used in Russia before 1917, * Britain & colonies before 1752, anywhere else before 1582, * and is still in use by some communities) leave out the * -year/100+year/400 terms, and add 10.] * * This algorithm was first published by Gauss (I think). * * WARNING: this function will overflow on 2106-02-07 06:28:16 on * machines were long is 32-bit! (However, as time_t is signed, we * will already get problems at other places on 2038-01-19 03:14:08) */ static unsigned long neol_mktime(int year, int mon, int day, int hour, int min, int sec) { if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */ mon += 12; /* Puts Feb last since it has leap day */ year -= 1; } return ((( (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + (unsigned int)year*365 - 719499 )*24 + (unsigned int)hour /* now have hours */ )*60 + (unsigned int)min /* now have minutes */ )*60 + (unsigned int)sec; /* finally seconds */ } static void neol_localtime(unsigned long utc, int* year, int* month, int* day, int* hour, int* min, int* sec) { *sec = (int)(utc % 60); utc /= 60; *min = (int)(utc % 60); utc /= 60; *hour = (int)(utc % 24); utc /= 24; /* JDN Date 1/1/1970 */ neol_jdn_to_ymd(utc + 2440588L, year, month, day); } static void neol_jdn_to_ymd(unsigned long jdn, int *yy, int *mm, int *dd) { unsigned long x, z, m, d, y; unsigned long daysPer400Years = 146097UL; unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL; x = jdn + 68569UL; z = 4UL * x / daysPer400Years; x = x - (daysPer400Years * z + 3UL) / 4UL; y = 4000UL * (x + 1) / fudgedDaysPer4000Years; x = x - 1461UL * y / 4UL + 31UL; m = 80UL * x / 2447UL; d = x - 2447UL * m / 80UL; x = m / 11UL; m = m + 2UL - 12UL * x; y = 100UL * (z - 49UL) + y + x; *yy = (int)y; *mm = (int)m; *dd = (int)d; } #if !defined(NEOCLOCK4X_FIRMWARE) static int neol_query_firmware(int fd, int unit, char *firmware, size_t maxlen) { char tmpbuf[256]; size_t len; int lastsearch; unsigned char c; bool last_c_was_crlf; bool last_crlf_conv_len; bool init; int read_errors; bool flag = false; int chars_read; /* wait a little bit */ sleep(1); if(-1 != write(fd, "V", 1)) { /* wait a little bit */ sleep(1); memset(tmpbuf, 0x00, sizeof(tmpbuf)); len = 0; lastsearch = 0; last_c_was_crlf = false; last_crlf_conv_len = 0; init = true; read_errors = 0; chars_read = 0; for(;;) { if(read_errors > 5) { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): can't read firmware version (timeout)", unit); strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf)); break; } if(chars_read > 500) { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): can't read firmware version (garbage)", unit); strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf)); break; } if(-1 == read(fd, &c, 1)) { if(EAGAIN != errno) { msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit); read_errors++; } else { sleep(1); } continue; } else { chars_read++; } if(init) { if(0xA9 != c) /* wait for (c) char in input stream */ continue; strlcpy(tmpbuf, "(c)", sizeof(tmpbuf)); len = 3; init = false; continue; } #if 0 msyslog(LOG_NOTICE, "REFCLOCK: NeoClock4X(%d): firmware %c = %02Xh", unit, c, c); #endif if(0x0A == c || 0x0D == c) { if(last_c_was_crlf) { char *ptr; ptr = strstr(&tmpbuf[lastsearch], "S/N"); if(NULL != ptr) { tmpbuf[last_crlf_conv_len] = 0; flag = true; break; } /* convert \n to / */ last_crlf_conv_len = len; tmpbuf[len++] = ' '; tmpbuf[len++] = '/'; tmpbuf[len++] = ' '; lastsearch = (int)len; } last_c_was_crlf = true; } else { last_c_was_crlf = false; if(0x00 != c) tmpbuf[len++] = (char) c; } tmpbuf[len] = '\0'; if (len > sizeof(tmpbuf)-5) break; } } else { msyslog(LOG_ERR, "REFCLOCK: NeoClock4X(%d): can't query firmware version", unit); strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf)); } if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen) strlcpy(firmware, "buffer too small", maxlen); if(flag) { NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "REFCLOCK: NeoClock4X(%d): firmware version: %s", unit, firmware); if(strstr(firmware, "/R2")) { msyslog(LOG_INFO, "REFCLOCK: NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit); } } return (flag); } static bool neol_check_firmware(int unit, const char *firmware, char *firmwaretag) { char *ptr; *firmwaretag = '?'; ptr = strstr(firmware, "NDF:"); if(NULL != ptr) { if((strlen(firmware) - strlen(ptr)) >= 7) { if(':' == *(ptr+5) && '*' == *(ptr+6)) *firmwaretag = *(ptr+4); } } if('A' != *firmwaretag) { msyslog(LOG_CRIT, "REFCLOCK: NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag); return false; } return true; } #endif /* * History: * refclock_neoclock4x.c * * 2002/04/27 cjh * Revision 1.0 first release * * 2002/07/15 cjh * preparing for bitkeeper reposity * * 2002/09/09 cjh * Revision 1.1 * - don't assume sprintf returns an int anymore * - change the way the firmware version is read * - some customers would like to put a device called * data diode to the NeoClock4X device to disable * the write line. We need to now the firmware * version even in this case. We made a compile time * definition in this case. The code was previously * only available on request. * * 2003/01/08 cjh * Revision 1.11 * - changing xprinf to xnprinf to avoid buffer overflows * - change some logic * - fixed memory leaks if drivers can't initialize * * 2003/01/10 cjh * Revision 1.12 * - replaced ldiv * - add code to support FreeBSD * * 2003/07/07 cjh * Revision 1.13 * - fix reporting of clock status * changes. previously a bad clock * status was never reset. * * 2004/04/07 cjh * Revision 1.14 * - open serial port in a way * AIX and some other OS can * handle much better * * 2006/01/11 cjh * Revision 1.15 * - remove some unused #ifdefs * - fix nsec calculation, closes #499 * * 2009/12/04 cjh * Revision 1.16 * - change license to ntp COPYRIGHT notice. This should allow Debian * to add this refclock driver in further releases. * - detect R2 hardware * */ ntpsec-1.1.0+dfsg1/ntpd/ntp_monitor.c0000644000175000017500000003340713252364117017315 0ustar rlaagerrlaager/* * ntp_monitor - monitor ntpd statistics */ #include "config.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_lists.h" #include "ntp_stdlib.h" /* * Record statistics based on source address, mode and version. The * receive procedure calls us with the incoming rbufp before it does * anything else. While at it, implement rate controls for inbound * traffic. * * Each entry is doubly linked into two lists, a hash table and a most- * recently-used (MRU) list. When a packet arrives it is looked up in * the hash table. If found, the statistics are updated and the entry * relinked at the head of the MRU list. If not found, a new entry is * allocated, initialized and linked into both the hash table and at the * head of the MRU list. * * Memory is usually allocated by grabbing a big chunk of new memory and * cutting it up into littler pieces. The exception to this when we hit * the memory limit. Then we free memory by grabbing entries off the * tail for the MRU list, unlinking from the hash table, and * reinitializing. * * INC_MONLIST is the default allocation granularity in entries. * INIT_MONLIST is the default initial allocation in entries. */ #ifndef INC_MONLIST # define INC_MONLIST (4 * 1024 / sizeof(mon_entry)) #endif #ifndef INIT_MONLIST # define INIT_MONLIST (4 * 1024 / sizeof(mon_entry)) #endif #ifndef MRU_MAXDEPTH_DEF # define MRU_MAXDEPTH_DEF (1024 * 1024 / sizeof(mon_entry)) #endif /* * Hashing stuff */ uint8_t mon_hash_bits; /* * Pointers to the hash table and the MRU list. Memory for the hash * table is allocated only if monitoring is enabled. * Total size can easily exceed 32 bits (4 GB) * Total count is unlikely to exceed 32 bits in 2017 * but memories keep growing. */ mon_entry ** mon_hash; /* MRU hash table */ mon_entry mon_mru_list; /* mru listhead */ uint64_t mru_entries; /* mru list count */ /* * List of free structures, and counters of in-use and total * structures. The free structures are linked with the hash_next field. */ static mon_entry *mon_free; /* free list or null if none */ static uint64_t mru_alloc; /* mru list + free list count */ uint64_t mru_peakentries; /* highest mru_entries seen */ uint64_t mru_initalloc = INIT_MONLIST;/* entries to preallocate */ uint64_t mru_incalloc = INC_MONLIST;/* allocation batch factor */ static uint64_t mon_mem_increments; /* times called malloc() */ /* * Parameters of the RES_LIMITED restriction option. We define headway * as the idle time between packets. A packet is discarded if the * headway is less than the minimum, as well as if the average headway * is less than eight times the increment. */ int ntp_minpkt = NTP_MINPKT; /* minimum (log 2 s) */ uint8_t ntp_minpoll = NTP_MINPOLL; /* increment (log 2 s) */ /* * Initialization state. We may be monitoring, we may not. If * we aren't, we may not even have allocated any memory yet. */ unsigned int mon_enabled; /* enable switch */ uint64_t mru_mindepth = 600; /* preempt above this */ int mru_maxage = 3600; /* recycle if older than this */ int mru_minage = 64; /* recycle if full and older than this */ uint64_t mru_maxdepth = MRU_MAXDEPTH_DEF; /* MRU count hard limit */ int mon_age = 3000; /* preemption limit */ static void mon_getmoremem(void); static void remove_from_hash(mon_entry *); static inline void mon_free_entry(mon_entry *); static inline void mon_reclaim_entry(mon_entry *); /* MRU counters */ uint64_t mru_exists = 0; /* slot already exists */ uint64_t mru_new = 0; /* allocate a new slot (2 cases) */ uint64_t mru_recycleold = 0; /* recycle slot: age > mru_maxage */ uint64_t mru_recyclefull = 0; /* recycle slot: full and age > mru_minage */ uint64_t mru_none = 0; /* couldn't get one */ /* * init_mon - initialize monitoring global data */ void init_mon(void) { /* * Don't do much of anything here. We don't allocate memory * until mon_start(). */ mon_enabled = MON_OFF; INIT_DLIST(mon_mru_list, mru); } /* * remove_from_hash - removes an entry from the address hash table and * decrements mru_entries. */ static void remove_from_hash( mon_entry *mon ) { unsigned int hash; mon_entry *punlinked; mru_entries--; hash = MON_HASH(&mon->rmtadr); UNLINK_SLIST(punlinked, mon_hash[hash], mon, hash_next, mon_entry); ENSURE(punlinked == mon); } static inline void mon_free_entry( mon_entry *m ) { ZERO(*m); LINK_SLIST(mon_free, m, hash_next); } /* * mon_reclaim_entry - Remove an entry from the MRU list and from the * hash array, then zero-initialize it. Indirectly * decrements mru_entries. * The entry is prepared to be reused. Before return, in * remove_from_hash(), mru_entries is decremented. It is the caller's * responsibility to increment it again. */ static inline void mon_reclaim_entry( mon_entry *m ) { INSIST(NULL != m); UNLINK_DLIST(m, mru); remove_from_hash(m); ZERO(*m); } /* * mon_getmoremem - get more memory and put it on the free list */ static void mon_getmoremem(void) { mon_entry *chunk; unsigned int entries; entries = (0 == mon_mem_increments) ? mru_initalloc : mru_incalloc; if (entries) { chunk = eallocarray(entries, sizeof(*chunk)); mru_alloc += entries; for (chunk += entries; entries; entries--) mon_free_entry(--chunk); mon_mem_increments++; } /* chunk not free()ed, chunk added to free list */ /* coverity[leaked_storage] */ } /* * mon_start - start up the monitoring software */ void mon_start( int mode ) { size_t octets; unsigned int min_hash_slots; if (MON_OFF == mode) /* MON_OFF is 0 */ return; if (mon_enabled) { mon_enabled |= (unsigned int)mode; return; } if (0 == mon_mem_increments) mon_getmoremem(); /* * Select the MRU hash table size to limit the average count * per bucket at capacity (mru_maxdepth) to 8, if possible * given our hash is limited to 16 bits. */ min_hash_slots = (mru_maxdepth / 8) + 1; mon_hash_bits = 0; while (min_hash_slots >>= 1) mon_hash_bits++; mon_hash_bits = max(4, mon_hash_bits); mon_hash_bits = min(16, mon_hash_bits); octets = sizeof(*mon_hash) * MON_HASH_SIZE; mon_hash = erealloc_zero(mon_hash, octets, 0); mon_enabled = (unsigned int)mode; } /* * mon_stop - stop the monitoring software */ void mon_stop( int mode ) { mon_entry *mon; if (MON_OFF == mon_enabled) return; if ((mon_enabled & (unsigned int)mode) == 0 || mode == MON_OFF) return; mon_enabled &= (unsigned int)~mode; if (mon_enabled != MON_OFF) return; /* * Move everything on the MRU list to the free list quickly, * without bothering to remove each from either the MRU list or * the hash table. */ ITER_DLIST_BEGIN(mon_mru_list, mon, mru, mon_entry) mon_free_entry(mon); ITER_DLIST_END() /* empty the MRU list and hash table. */ mru_entries = 0; INIT_DLIST(mon_mru_list, mru); memset(mon_hash, '\0', sizeof(*mon_hash) * MON_HASH_SIZE); } /* * mon_clearinterface -- remove mru entries referring to a local address * which is going away. */ void mon_clearinterface( endpt *lcladr ) { mon_entry *mon; /* iterate mon over mon_mru_list */ ITER_DLIST_BEGIN(mon_mru_list, mon, mru, mon_entry) if (mon->lcladr == lcladr) { /* remove from mru list */ UNLINK_DLIST(mon, mru); /* remove from hash list, adjust mru_entries */ remove_from_hash(mon); /* put on free list */ mon_free_entry(mon); } ITER_DLIST_END() } int mon_get_oldest_age(l_fp now) { mon_entry * oldest; if (mru_entries == 0) return 0; oldest = TAIL_DLIST(mon_mru_list, mru); now -= oldest->last; /* add one-half second to round up */ now += 0x80000000; return lfpsint(now); } /* * ntp_monitor - record stats about this packet * * Returns supplied restriction flags, with RES_LIMITED and RES_KOD * cleared unless the packet should not be responded to normally * (RES_LIMITED) and possibly should trigger a KoD response (RES_KOD). * The returned flags are saved in the MRU entry, so that it reflects * whether the last packet from that source triggered rate limiting, * and if so, possible KoD response. This implies you can not tell * whether a given address is eligible for rate limiting/KoD from the * monlist restrict bits, only whether or not the last packet triggered * such responses. ntpq -c reslist lets you see whether RES_LIMITED * or RES_KOD is lit for a particular address before ntp_monitor()'s * typical dousing. */ unsigned short ntp_monitor( struct recvbuf *rbufp, unsigned short flags ) { l_fp interval_fp; struct pkt * pkt; mon_entry * mon; mon_entry * oldest; int oldest_age; unsigned int hash; unsigned short restrict_mask; uint8_t mode; uint8_t version; int interval; int head; /* headway increment */ int leak; /* new headway */ int limit; /* average threshold */ if (mon_enabled == MON_OFF) return ~(RES_LIMITED | RES_KOD) & flags; pkt = &rbufp->recv_pkt; hash = MON_HASH(&rbufp->recv_srcadr); mode = PKT_MODE(pkt->li_vn_mode); version = PKT_VERSION(pkt->li_vn_mode); mon = mon_hash[hash]; /* * We keep track of all traffic for a given IP in one entry, * otherwise cron'ed ntpdate or similar evades RES_LIMITED. */ for (; mon != NULL; mon = mon->hash_next) if (SOCK_EQ(&mon->rmtadr, &rbufp->recv_srcadr)) break; if (mon != NULL) { mru_exists++; interval_fp = rbufp->recv_time; interval_fp -= mon->last; /* add one-half second to round up */ interval_fp += 0x80000000; interval = lfpsint(interval_fp); mon->last = rbufp->recv_time; NSRCPORT(&mon->rmtadr) = NSRCPORT(&rbufp->recv_srcadr); mon->count++; restrict_mask = flags; mon->vn_mode = VN_MODE(version, mode); /* Shuffle to the head of the MRU list. */ UNLINK_DLIST(mon, mru); LINK_DLIST(mon_mru_list, mon, mru); /* * At this point the most recent arrival is first in the * MRU list. Decrease the counter by the headway, but * not less than zero. */ mon->leak -= interval; mon->leak = max(0, mon->leak); head = 1 << ntp_minpoll; leak = mon->leak + head; limit = NTP_SHIFT * head; DPRINT(2, ("MRU: interval %d headway %d limit %d\n", interval, leak, limit)); /* * If the minimum and average thresholds are not * exceeded, douse the RES_LIMITED and RES_KOD bits and * increase the counter by the headway increment. Note * that we give a 1-s grace for the minimum threshold * and a 2-s grace for the headway increment. If one or * both thresholds are exceeded and the old counter is * less than the average threshold, set the counter to * the average threshold plus the increment and leave * the RES_LIMITED and RES_KOD bits lit. Otherwise, * leave the counter alone and douse the RES_KOD bit. * This rate-limits the KoDs to no less than the average * headway. */ if (interval + 1 >= ntp_minpkt && leak < limit) { mon->leak = leak - 2; restrict_mask &= ~(RES_LIMITED | RES_KOD); } else if (mon->leak < limit) mon->leak = limit + head; else restrict_mask &= ~RES_KOD; mon->flags = restrict_mask; return mon->flags; } /* * If we got here, this is the first we've heard of this * guy. Get him some memory, either from the free list * or from the tail of the MRU list. * * The following ntp.conf "mru" knobs come into play determining * the depth (or count) of the MRU list: * - mru_mindepth ("mru mindepth") is a floor beneath which * entries are kept without regard to their age. The * default is 600 which matches the longtime implementation * limit on the total number of entries. * - mru_maxage ("mru maxage") is a ceiling on the age in * seconds of entries. Entries older than this are * reclaimed once mon_mindepth is exceeded. 3600s default. * Note that entries older than this can easily survive * as they are reclaimed only as needed. * - mru_maxdepth ("mru maxdepth") is a hard limit on the * number of entries. * - "mru maxmem" sets mru_maxdepth to the number of entries * which fit in the given number of kilobytes. The default is * 1024, or 1 megabyte. * - mru_initalloc ("mru initalloc" sets the count of the * initial allocation of MRU entries. * - "mru initmem" sets mru_initalloc in units of kilobytes. * The default is 4. * - mru_incalloc ("mru incalloc" sets the number of entries to * allocate on-demand each time the free list is empty. * - "mru incmem" sets mru_incalloc in units of kilobytes. * The default is 4. * Whichever of "mru maxmem" or "mru maxdepth" occurs last in * ntp.conf controls. Similarly for "mru initalloc" and "mru * initmem", and for "mru incalloc" and "mru incmem". */ if (mru_entries < mru_mindepth) { mru_new++; if (NULL == mon_free) mon_getmoremem(); UNLINK_HEAD_SLIST(mon, mon_free, hash_next); } else { oldest = TAIL_DLIST(mon_mru_list, mru); oldest_age = mon_get_oldest_age(rbufp->recv_time); if (mru_maxage < oldest_age) { mru_recycleold++; mon_reclaim_entry(oldest); mon = oldest; } else if (mon_free != NULL || mru_alloc < mru_maxdepth) { mru_new++; if (NULL == mon_free) mon_getmoremem(); UNLINK_HEAD_SLIST(mon, mon_free, hash_next); } else if (oldest_age < mru_minage) { mru_none++; return ~(RES_LIMITED | RES_KOD) & flags; } else { mru_recyclefull++; /* coverity[var_deref_model] */ mon_reclaim_entry(oldest); mon = oldest; } } /* * Got one, initialize it */ REQUIRE(mon != NULL); mru_entries++; mru_peakentries = max(mru_peakentries, mru_entries); mon->last = rbufp->recv_time; mon->first = mon->last; mon->count = 1; mon->flags = ~(RES_LIMITED | RES_KOD) & flags; mon->leak = 0; memcpy(&mon->rmtadr, &rbufp->recv_srcadr, sizeof(mon->rmtadr)); mon->vn_mode = VN_MODE(version, mode); mon->lcladr = rbufp->dstadr; mon->cast_flags = rbufp->cast_flags; /* * Drop him into front of the hash table. Also put him on top of * the MRU list. */ LINK_SLIST(mon_hash[hash], mon, hash_next); LINK_DLIST(mon_mru_list, mon, mru); return mon->flags; } ntpsec-1.1.0+dfsg1/ntpd/ntp_parser.y0000644000175000017500000006604613252364117017155 0ustar rlaagerrlaager/* ntp_parser.y * * The parser for the NTP configuration file. * * Written By: Sachin Kamboj * University of Delaware * Newark, DE 19711 * Copyright (c) 2006 * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-clause */ %{ #include "config.h" #include "ntp.h" #include "ntpd.h" #include "ntp_machine.h" #include "ntp_stdlib.h" #include "ntp_filegen.h" #include "ntp_scanner.h" #include "ntp_config.h" #define YYMALLOC emalloc #define YYFREE free #define YYERROR_VERBOSE #define YYMAXDEPTH 1000 /* stop the madness sooner */ void yyerror(const char *msg); %} /* * Enable generation of token names array even without YYDEBUG. * We access via token_name() defined below. */ %token-table %union { char * String; double Double; int Integer; unsigned U_int; gen_fifo * Generic_fifo; attr_val * Attr_val; attr_val_fifo * Attr_val_fifo; int_fifo * Int_fifo; string_fifo * String_fifo; address_node * Address_node; address_fifo * Address_fifo; setvar_node * Set_var; } /* Terminals (do not appear left of colon) */ %token T_Age %token T_All %token T_Allan %token T_Allpeers %token T_Auth %token T_Average %token T_Baud %token T_Bias %token T_Burst %token T_Calibrate %token T_Ceiling %token T_Clock %token T_Clockstats %token T_Cohort %token T_ControlKey %token T_Ctl %token T_Day %token T_Default %token T_Disable %token T_Discard %token T_Dispersion %token T_Double /* not a token */ %token T_Driftfile %token T_Drop %token T_Dscp %token T_Ellipsis /* "..." not "ellipsis" */ %token T_Enable %token T_End %token T_False %token T_File %token T_Filegen %token T_Filenum %token T_Flag1 %token T_Flag2 %token T_Flag3 %token T_Flag4 %token T_Flake %token T_Floor %token T_Freq %token T_Fudge %token T_Holdover %token T_Huffpuff %token T_Iburst %token T_Ignore %token T_Incalloc %token T_Incmem %token T_Initalloc %token T_Initmem %token T_Includefile %token T_Integer /* not a token */ %token T_Interface %token T_Intrange /* not a token */ %token T_Io %token T_Ipv4 %token T_Ipv4_flag %token T_Ipv6 %token T_Ipv6_flag %token T_Kernel %token T_Key %token T_Keys %token T_Kod %token T_Mssntp %token T_Leapfile %token T_Leapsmearinterval %token T_Limited %token T_Link %token T_Listen %token T_Logconfig %token T_Logfile %token T_Loopstats %token T_Mask %token T_Maxage %token T_Maxclock %token T_Maxdepth %token T_Maxdisp %token T_Maxdist %token T_Maxmem %token T_Maxpoll %token T_Mdnstries %token T_Mem %token T_Memlock %token T_Minage %token T_Minclock %token T_Mindepth %token T_Mindist %token T_Minimum %token T_Minpoll %token T_Minsane %token T_Mode %token T_Monitor %token T_Month %token T_Mru %token T_Nic %token T_Nolink %token T_Nomodify %token T_Nomrulist %token T_None %token T_Nonvolatile %token T_Nopeer %token T_Noquery %token T_Noselect %token T_Noserve %token T_Notrap %token T_Notrust %token T_Ntp %token T_Ntpport %token T_NtpSignDsocket %token T_Orphan %token T_Orphanwait %token T_Panic %token T_Path %token T_Peer %token T_Peerstats %token T_Phone %token T_Pid %token T_Pidfile %token T_Pool %token T_Ppspath %token T_Port %token T_Prefer %token T_Protostats %token T_Rawstats %token T_Refclock %token T_Refid %token T_Requestkey %token T_Reset %token T_Restrict %token T_Rlimit %token T_Saveconfigdir %token T_Server %token T_Setvar %token T_Source %token T_Stacksize %token T_Statistics %token T_Stats %token T_Statsdir %token T_Step %token T_Stepback %token T_Stepfwd %token T_Stepout %token T_Stratum %token T_Subtype %token T_String /* not a token */ %token T_Sys %token T_Sysstats %token T_Tick %token T_Time1 %token T_Time2 %token T_Timer %token T_Timingstats %token T_Tinker %token T_Tos %token T_Trap %token T_True %token T_Trustedkey %token T_Type %token T_U_int /* Not a token */ %token T_Unit %token T_Unconfig %token T_Unpeer %token T_Unrestrict %token T_Usestats %token T_Version %token T_WanderThreshold /* Not a token */ %token T_Week %token T_Wildcard %token T_Year %token T_Flag /* Not a token */ %token T_EOC /*** NON-TERMINALS ***/ %type access_control_flag %type ac_flag_list %type address %type address_fam %type boolean %type client_type %type counter_set_keyword %type counter_set_list %type discard_option %type discard_option_keyword %type discard_option_list %type enable_disable %type filegen_option %type filegen_option_list %type filegen_type %type fudge_factor %type fudge_factor_bool_keyword %type fudge_factor_dbl_keyword %type fudge_factor_list %type integer_list_range %type integer_list_range_elt %type integer_range %type nic_rule_action %type interface_command %type interface_nic %type ip_address %type link_nolink %type log_config_command %type log_config_list %type misc_cmd_dbl_keyword %type misc_cmd_int_keyword %type misc_cmd_str_keyword %type misc_cmd_str_lcl_keyword %type mru_option %type mru_option_keyword %type mru_option_list %type nic_rule_class %type number %type option %type option_flag %type option_flag_keyword %type option_list %type option_boolean %type option_bool_keyword %type option_double %type option_double_keyword %type option_int %type option_int_keyword %type option_string %type optional_unit %type reset_command %type rlimit_option_keyword %type rlimit_option %type rlimit_option_list %type stat %type stats_list %type string_list %type system_option %type system_option_flag_keyword %type system_option_local_flag_keyword %type system_option_list %type t_default_or_zero %type tinker_option_keyword %type tinker_option %type tinker_option_list %type tos_option %type tos_option_dbl_keyword %type tos_option_int_keyword %type tos_option_list %type unpeer_keyword %type variable_assign %type restrict_prefix %% /* ntp.conf * Configuration File Grammar * -------------------------- */ configuration : command_list ; command_list : command_list command T_EOC | command T_EOC | error T_EOC { /* I will need to incorporate much more fine grained * error messages. The following should suffice for * the time being. */ struct FILE_INFO * ip_ctx = lex_current(); msyslog(LOG_ERR, "CONFIG: syntax error in %s line %d, column %d", ip_ctx->fname, ip_ctx->errpos.nline, ip_ctx->errpos.ncol); } ; command : /* NULL STATEMENT */ | server_command | unpeer_command | other_mode_command | authentication_command | monitoring_command | access_control_command | orphan_mode_command | fudge_command | refclock_command | rlimit_command | system_option_command | tinker_command | miscellaneous_command ; /* Server Commands * --------------- */ server_command : client_type address option_list { peer_node *my_node; my_node = create_peer_node($1, $2, $3); APPEND_G_FIFO(cfgt.peers, my_node); } ; client_type : T_Server | T_Pool | T_Peer ; address : ip_address | address_fam T_String { $$ = create_address_node($2, $1); } ; ip_address : T_String { $$ = create_address_node($1, AF_UNSPEC); } ; address_fam : T_Ipv4_flag { $$ = AF_INET; } | T_Ipv6_flag { $$ = AF_INET6; } ; option_list : /* empty list */ { $$ = NULL; } | option_list option { $$ = $1; APPEND_G_FIFO($$, $2); } ; option : option_flag | option_int | option_double | option_string | option_boolean ; option_flag : option_flag_keyword { $$ = create_attr_ival(T_Flag, $1); } ; option_flag_keyword : T_Burst | T_Iburst | T_Noselect | T_Prefer | T_True ; option_int : option_int_keyword T_Integer { $$ = create_attr_ival($1, $2); } | option_int_keyword T_U_int { $$ = create_attr_uval($1, (unsigned int)$2); } | T_Stratum T_Integer { if ($2 >= 0 && $2 <= STRATUM_UNSPEC) { $$ = create_attr_ival($1, $2); } else { $$ = NULL; yyerror("fudge factor: stratum value out of bounds, ignored"); } } ; option_int_keyword : T_Key | T_Minpoll | T_Maxpoll | T_Mode | T_Subtype | T_Version | T_Baud | T_Holdover ; option_double : option_double_keyword number { $$ = create_attr_dval($1, $2); } ; option_boolean : option_bool_keyword boolean { $$ = create_attr_ival($1, $2); } ; option_string : T_Refid T_String { $$ = create_attr_sval($1, $2); } | T_Path T_String { $$ = create_attr_sval($1, $2); } | T_Ppspath T_String { $$ = create_attr_sval($1, $2); } ; option_double_keyword : T_Bias | T_Time1 | T_Time2 ; option_bool_keyword : T_Flag1 | T_Flag2 | T_Flag3 | T_Flag4 ; /* unpeer commands * --------------- */ unpeer_command : unpeer_keyword address { unpeer_node *my_node; my_node = create_unpeer_node($2); if (my_node) APPEND_G_FIFO(cfgt.unpeers, my_node); } | unpeer_keyword T_Clock T_String optional_unit { #ifdef REFCLOCK unpeer_node *my_node; my_node = create_unpeer_node(addr_from_typeunit($3, $4)); if (my_node) APPEND_G_FIFO(cfgt.unpeers, my_node); #else yyerror("no refclock support was compiled in."); #endif /* REFCLOCK */ } ; unpeer_keyword : T_Unconfig | T_Unpeer ; /* Other Modes * (manycastserver) * ------------------------------------------------ */ other_mode_command : T_Mdnstries T_Integer { cfgt.mdnstries = $2; } ; /* Authentication Commands * ----------------------- */ authentication_command : T_ControlKey T_Integer { cfgt.auth.control_key = $2; } | T_Keys T_String { cfgt.auth.keys = $2; } | T_Requestkey T_Integer { msyslog(LOG_WARNING, "CONFIG: requestkey is a no-op because " "ntpdc has been removed."); } | T_Trustedkey integer_list_range { CONCAT_G_FIFOS(cfgt.auth.trusted_key_list, $2); } | T_NtpSignDsocket T_String { cfgt.auth.ntp_signd_socket = $2; } ; /* Orphan Mode Commands * -------------------- */ orphan_mode_command : T_Tos tos_option_list { CONCAT_G_FIFOS(cfgt.orphan_cmds, $2); } ; tos_option_list : tos_option_list tos_option { $$ = $1; APPEND_G_FIFO($$, $2); } | tos_option { $$ = NULL; APPEND_G_FIFO($$, $1); } ; tos_option : tos_option_int_keyword T_Integer { $$ = create_attr_dval($1, (double)$2); } | tos_option_dbl_keyword number { $$ = create_attr_dval($1, $2); } | T_Cohort boolean { $$ = create_attr_dval($1, (double)$2); } ; tos_option_int_keyword : T_Ceiling | T_Floor | T_Orphan | T_Orphanwait | T_Minsane ; tos_option_dbl_keyword : T_Mindist | T_Maxdisp | T_Maxdist | T_Minclock | T_Maxclock ; /* Monitoring Commands * ------------------- */ monitoring_command : T_Statistics stats_list { CONCAT_G_FIFOS(cfgt.stats_list, $2); } | T_Statsdir T_String { if (lex_from_file()) { cfgt.stats_dir = $2; } else { YYFREE($2); yyerror("statsdir remote configuration ignored"); } } | T_Filegen stat filegen_option_list { filegen_node *fgn; fgn = create_filegen_node($2, $3); APPEND_G_FIFO(cfgt.filegen_opts, fgn); } ; stats_list : stats_list stat { $$ = $1; APPEND_G_FIFO($$, create_int_node($2)); } | stat { $$ = NULL; APPEND_G_FIFO($$, create_int_node($1)); } ; stat : T_Clockstats | T_Loopstats | T_Peerstats | T_Rawstats | T_Sysstats | T_Protostats | T_Timingstats | T_Usestats ; filegen_option_list : /* empty list */ { $$ = NULL; } | filegen_option_list filegen_option { $$ = $1; APPEND_G_FIFO($$, $2); } ; filegen_option : T_File T_String { if (lex_from_file()) { $$ = create_attr_sval($1, $2); } else { $$ = NULL; YYFREE($2); yyerror("filegen file remote config ignored"); } } | T_Type filegen_type { if (lex_from_file()) { $$ = create_attr_ival($1, $2); } else { $$ = NULL; yyerror("filegen type remote config ignored"); } } | link_nolink { const char *err; if (lex_from_file()) { $$ = create_attr_ival(T_Flag, $1); } else { $$ = NULL; if (T_Link == $1) err = "filegen link remote config ignored"; else err = "filegen nolink remote config ignored"; yyerror(err); } } | enable_disable { $$ = create_attr_ival(T_Flag, $1); } ; link_nolink : T_Link | T_Nolink ; enable_disable : T_Enable | T_Disable ; filegen_type : T_None | T_Pid | T_Day | T_Week | T_Month | T_Year | T_Age ; /* Access Control Commands * ----------------------- */ restrict_prefix : T_Restrict | T_Unrestrict ; access_control_command : T_Discard discard_option_list { CONCAT_G_FIFOS(cfgt.discard_opts, $2); } | T_Mru mru_option_list { CONCAT_G_FIFOS(cfgt.mru_opts, $2); } | restrict_prefix address ac_flag_list { restrict_node *rn; rn = create_restrict_node($1, $2, NULL, $3, lex_current()->curpos.nline); APPEND_G_FIFO(cfgt.restrict_opts, rn); } | restrict_prefix ip_address T_Mask ip_address ac_flag_list { restrict_node *rn; rn = create_restrict_node($1, $2, $4, $5, lex_current()->curpos.nline); APPEND_G_FIFO(cfgt.restrict_opts, rn); } | restrict_prefix T_Default ac_flag_list { restrict_node *rn; rn = create_restrict_node($1, NULL, NULL, $3, lex_current()->curpos.nline); APPEND_G_FIFO(cfgt.restrict_opts, rn); } | restrict_prefix T_Ipv4_flag T_Default ac_flag_list { restrict_node *rn; rn = create_restrict_node($1, create_address_node( estrdup("0.0.0.0"), AF_INET), create_address_node( estrdup("0.0.0.0"), AF_INET), $4, lex_current()->curpos.nline); APPEND_G_FIFO(cfgt.restrict_opts, rn); } | restrict_prefix T_Ipv6_flag T_Default ac_flag_list { restrict_node *rn; rn = create_restrict_node($1, create_address_node( estrdup("::"), AF_INET6), create_address_node( estrdup("::"), AF_INET6), $4, lex_current()->curpos.nline); APPEND_G_FIFO(cfgt.restrict_opts, rn); } | restrict_prefix T_Source ac_flag_list { restrict_node * rn; APPEND_G_FIFO($3, create_int_node($2)); rn = create_restrict_node( $1, NULL, NULL, $3, lex_current()->curpos.nline); APPEND_G_FIFO(cfgt.restrict_opts, rn); } ; ac_flag_list : /* empty list is allowed */ { $$ = NULL; } | ac_flag_list access_control_flag { $$ = $1; APPEND_G_FIFO($$, create_int_node($2)); } ; access_control_flag : T_Flake | T_Ignore | T_Kod | T_Mssntp | T_Limited | T_Nomodify | T_Nomrulist | T_Nopeer | T_Noquery | T_Noserve | T_Notrap | T_Notrust | T_Ntpport | T_Version ; discard_option_list : discard_option_list discard_option { $$ = $1; APPEND_G_FIFO($$, $2); } | discard_option { $$ = NULL; APPEND_G_FIFO($$, $1); } ; discard_option : discard_option_keyword T_Integer { $$ = create_attr_ival($1, $2); } ; discard_option_keyword : T_Average | T_Minimum | T_Monitor ; mru_option_list : mru_option_list mru_option { $$ = $1; APPEND_G_FIFO($$, $2); } | mru_option { $$ = NULL; APPEND_G_FIFO($$, $1); } ; mru_option : mru_option_keyword T_Integer { $$ = create_attr_ival($1, $2); } ; mru_option_keyword : T_Incalloc | T_Incmem | T_Initalloc | T_Initmem | T_Maxage | T_Minage | T_Maxdepth | T_Maxmem | T_Mindepth ; /* Fudge Commands * -------------- */ fudge_command : T_Fudge address fudge_factor_list { addr_opts_node *aon; aon = create_addr_opts_node($2, $3); APPEND_G_FIFO(cfgt.fudge, aon); } ; fudge_factor_list : fudge_factor_list fudge_factor { $$ = $1; APPEND_G_FIFO($$, $2); } | fudge_factor { $$ = NULL; APPEND_G_FIFO($$, $1); } ; fudge_factor : fudge_factor_dbl_keyword number { $$ = create_attr_dval($1, $2); } | fudge_factor_bool_keyword boolean { $$ = create_attr_ival($1, $2); } | T_Stratum T_Integer { if ($2 >= 0 && $2 <= 16) { $$ = create_attr_ival($1, $2); } else { $$ = NULL; yyerror("fudge factor: stratum value not in [0..16], ignored"); } } | T_Refid T_String { $$ = create_attr_sval($1, $2); } ; fudge_factor_dbl_keyword : T_Time1 | T_Time2 ; fudge_factor_bool_keyword : T_Flag1 | T_Flag2 | T_Flag3 | T_Flag4 ; /* Refclock Command * ---------------- */ refclock_command : T_Refclock T_String optional_unit option_list { #ifdef REFCLOCK address_node *fakeaddr = addr_from_typeunit($2, $3); peer_node *my_node = create_peer_node(T_Server, fakeaddr, $4); APPEND_G_FIFO(cfgt.peers, my_node); #endif /* REFCLOCK */ } ; optional_unit : {$$ = 0;} | T_Unit T_Integer {$$ = $2;} ; /* rlimit Commands * --------------- */ rlimit_command : T_Rlimit rlimit_option_list { CONCAT_G_FIFOS(cfgt.rlimit, $2); } ; rlimit_option_list : rlimit_option_list rlimit_option { $$ = $1; APPEND_G_FIFO($$, $2); } | rlimit_option { $$ = NULL; APPEND_G_FIFO($$, $1); } ; rlimit_option : rlimit_option_keyword T_Integer { $$ = create_attr_ival($1, $2); } ; rlimit_option_keyword : T_Memlock | T_Stacksize | T_Filenum ; /* Command for System Options * -------------------------- */ system_option_command : T_Enable system_option_list { CONCAT_G_FIFOS(cfgt.enable_opts, $2); } | T_Disable system_option_list { CONCAT_G_FIFOS(cfgt.disable_opts, $2); } ; system_option_list : system_option_list system_option { $$ = $1; APPEND_G_FIFO($$, $2); } | system_option { $$ = NULL; APPEND_G_FIFO($$, $1); } ; system_option : system_option_flag_keyword { $$ = create_attr_ival(T_Flag, $1); } | system_option_local_flag_keyword { if (lex_from_file()) { $$ = create_attr_ival(T_Flag, $1); } else { char err_str[128]; $$ = NULL; snprintf(err_str, sizeof(err_str), "enable/disable %s remote configuration ignored", keyword($1)); yyerror(err_str); } } ; system_option_flag_keyword : T_Calibrate | T_Kernel | T_Monitor | T_Ntp ; system_option_local_flag_keyword : T_Stats ; /* Tinker Commands * --------------- */ tinker_command : T_Tinker tinker_option_list { CONCAT_G_FIFOS(cfgt.tinker, $2); } ; tinker_option_list : tinker_option_list tinker_option { $$ = $1; APPEND_G_FIFO($$, $2); } | tinker_option { $$ = NULL; APPEND_G_FIFO($$, $1); } ; tinker_option : tinker_option_keyword number { $$ = create_attr_dval($1, $2); } ; tinker_option_keyword : T_Allan | T_Dispersion | T_Freq | T_Huffpuff | T_Panic | T_Step | T_Stepback | T_Stepfwd | T_Stepout | T_Tick ; /* Miscellaneous Commands * ---------------------- */ miscellaneous_command : interface_command | reset_command | misc_cmd_dbl_keyword number { attr_val *av; av = create_attr_dval($1, $2); APPEND_G_FIFO(cfgt.vars, av); } | misc_cmd_int_keyword T_Integer { attr_val *av; av = create_attr_ival($1, $2); APPEND_G_FIFO(cfgt.vars, av); } | misc_cmd_str_keyword T_String { attr_val *av; av = create_attr_sval($1, $2); APPEND_G_FIFO(cfgt.vars, av); } | misc_cmd_str_lcl_keyword T_String { char error_text[64]; attr_val *av; if (lex_from_file()) { av = create_attr_sval($1, $2); APPEND_G_FIFO(cfgt.vars, av); } else { YYFREE($2); snprintf(error_text, sizeof(error_text), "%s remote config ignored", keyword($1)); yyerror(error_text); } } | T_Includefile T_String command { if (!lex_from_file()) { YYFREE($2); /* avoid leak */ yyerror("remote includefile ignored"); break; } if (lex_level() > MAXINCLUDELEVEL) { fprintf(stderr, "getconfig(): Maximum include file level exceeded.\n"); msyslog(LOG_ERR, "CONFIG: Maximum include file level exceeded."); } else { const char * path = $2; if (!lex_push_file(path)) { fprintf(stderr, "getconfig(): Couldn't open <%s>\n", path); msyslog(LOG_ERR, "CONFIG: Couldn't open <%s>", path); } } YYFREE($2); /* avoid leak */ } | T_End { lex_flush_stack(); } | T_Driftfile drift_parm { /* see drift_parm below for actions */ } | T_Logconfig log_config_list { CONCAT_G_FIFOS(cfgt.logconfig, $2); } | T_Phone string_list { CONCAT_G_FIFOS(cfgt.phone, $2); } | T_Setvar variable_assign { APPEND_G_FIFO(cfgt.setvar, $2); } ; misc_cmd_dbl_keyword : T_Nonvolatile | T_Tick ; misc_cmd_int_keyword : T_Dscp ; misc_cmd_int_keyword : T_Leapsmearinterval { #ifndef ENABLE_LEAP_SMEAR yyerror("Built without LEAP_SMEAR support."); #endif } ; misc_cmd_str_keyword : T_Leapfile ; misc_cmd_str_lcl_keyword : T_Logfile | T_Pidfile | T_Saveconfigdir ; drift_parm : T_String { if (lex_from_file()) { attr_val *av; av = create_attr_sval(T_Driftfile, $1); APPEND_G_FIFO(cfgt.vars, av); } else { YYFREE($1); yyerror("driftfile remote configuration ignored"); } } | T_String T_Double { attr_val *av; av = create_attr_sval(T_Driftfile, $1); APPEND_G_FIFO(cfgt.vars, av); av = create_attr_dval(T_WanderThreshold, $2); APPEND_G_FIFO(cfgt.vars, av); } | /* Null driftfile, indicated by empty string "" */ { attr_val *av; av = create_attr_sval(T_Driftfile, estrdup("")); APPEND_G_FIFO(cfgt.vars, av); } ; variable_assign : T_String '=' T_String t_default_or_zero { $$ = create_setvar_node($1, $3, $4); } ; t_default_or_zero : T_Default | /* empty, no "default" modifier */ { $$ = 0; } ; log_config_list : log_config_list log_config_command { $$ = $1; APPEND_G_FIFO($$, $2); } | log_config_command { $$ = NULL; APPEND_G_FIFO($$, $1); } ; log_config_command : T_String { char prefix; char * type; switch ($1[0]) { case '+': case '-': case '=': prefix = $1[0]; type = $1 + 1; break; default: prefix = '='; type = $1; } $$ = create_attr_sval(prefix, estrdup(type)); YYFREE($1); } ; interface_command : interface_nic nic_rule_action nic_rule_class { nic_rule_node *nrn; nrn = create_nic_rule_node($3, NULL, $2); APPEND_G_FIFO(cfgt.nic_rules, nrn); } | interface_nic nic_rule_action T_String { nic_rule_node *nrn; nrn = create_nic_rule_node(0, $3, $2); APPEND_G_FIFO(cfgt.nic_rules, nrn); } ; interface_nic : T_Interface | T_Nic ; nic_rule_class : T_All | T_Ipv4 | T_Ipv6 | T_Wildcard ; nic_rule_action : T_Listen | T_Ignore | T_Drop ; reset_command : T_Reset counter_set_list { CONCAT_G_FIFOS(cfgt.reset_counters, $2); } ; counter_set_list : counter_set_list counter_set_keyword { $$ = $1; APPEND_G_FIFO($$, create_int_node($2)); } | counter_set_keyword { $$ = NULL; APPEND_G_FIFO($$, create_int_node($1)); } ; counter_set_keyword : T_Allpeers | T_Auth | T_Ctl | T_Io | T_Mem | T_Sys | T_Timer ; /* Miscellaneous Rules * ------------------- */ integer_list_range : integer_list_range integer_list_range_elt { $$ = $1; APPEND_G_FIFO($$, $2); } | integer_list_range_elt { $$ = NULL; APPEND_G_FIFO($$, $1); } ; integer_list_range_elt : T_Integer { $$ = create_attr_ival('i', $1); } | integer_range ; integer_range : '(' T_Integer T_Ellipsis T_Integer ')' { $$ = create_attr_rangeval('-', $2, $4); } ; string_list : string_list T_String { $$ = $1; APPEND_G_FIFO($$, create_string_node($2)); } | T_String { $$ = NULL; APPEND_G_FIFO($$, create_string_node($1)); } ; boolean : T_Integer { if ($1 != 0 && $1 != 1) { yyerror("Integer value is not boolean (0 or 1). Assuming 1"); $$ = 1; } else { $$ = $1; } } | T_True { $$ = 1; } | T_False { $$ = 0; } ; number : T_Integer { $$ = (double)$1; } | T_Double ; %% #ifdef REFCLOCK address_node * addr_from_typeunit(char *type, int unit) { char addrbuf[1025]; /* NI_MAXHOSTS on Linux */ int dtype; for (dtype = 1; dtype < (int)num_refclock_conf; dtype++) if (refclock_conf[dtype]->basename != NULL && strcasecmp(refclock_conf[dtype]->basename, type) == 0) goto foundit; msyslog(LOG_ERR, "CONFIG: Unknown driver name %s", type); exit(1); foundit: snprintf(addrbuf, sizeof(addrbuf), "127.127.%d.%d", dtype, unit); return create_address_node(estrdup(addrbuf), AF_INET); } #endif /* REFCLOCK */ void yyerror( const char *msg ) { int retval; struct FILE_INFO * ip_ctx; ip_ctx = lex_current(); ip_ctx->errpos = ip_ctx->tokpos; msyslog(LOG_ERR, "CONFIG: line %d column %d %s", ip_ctx->errpos.nline, ip_ctx->errpos.ncol, msg); if (!lex_from_file()) { /* Save the error message in the correct buffer */ retval = snprintf(remote_config.err_msg + remote_config.err_pos, (size_t)(MAXLINE - remote_config.err_pos), "column %d %s", ip_ctx->errpos.ncol, msg); /* Increment the value of err_pos */ if (retval > 0) remote_config.err_pos += retval; /* Increment the number of errors */ ++remote_config.no_errors; } } /* * token_name - convert T_ token integers to text * example: token_name(T_Server) returns "T_Server" */ const char * token_name( int token ) { return yytname[YYTRANSLATE(token)]; } /* Initial Testing function -- ignore */ #if 0 int main(int argc, char *argv[]) { ip_file = FOPEN(argv[1], "r"); if (!ip_file) fprintf(stderr, "ERROR!! Could not open file: %s\n", argv[1]); yyparse(); return 0; } #endif ntpsec-1.1.0+dfsg1/ntpd/refclock_hpgps.c0000644000175000017500000003775713252364117017752 0ustar rlaagerrlaager/* * refclock_hpgps - clock driver for HP GPS receivers */ #include "config.h" #include "ntp.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_calendar.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include #include /* Version 0.1 April 1, 1995 * 0.2 April 25, 1995 * tolerant of missing timecode response prompt and sends * clear status if prompt indicates error; * can use either local time or UTC from receiver; * can get receiver status screen via flag4 * * WARNING!: This driver is UNDER CONSTRUCTION * Everything in here should be treated with suspicion. * If it looks wrong, it probably is. * * Comments and/or questions to: Dave Vitanye * Hewlett Packard Company * dave@scd.hp.com * (408) 553-2856 * * Thanks to the author of the PST driver, which was the starting point for * this one. * * This driver supports the HP 58503A Time and Frequency Reference Receiver. * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver. * The receiver accuracy when locked to GPS in normal operation is better * than 1 usec. The accuracy when operating in holdover is typically better * than 10 usec. per day. * * The same driver also handles the HP Z3801A which is available surplus * from the cell phone industry. It's popular with hams. * It needs a different line setup: 19200 baud, 7 data bits, odd parity * That is selected by adding "subtype 1" to the server line in ntp.conf * HP Z3801A code from Jeff Mock added by Hal Murray, Sep 2005 * * * The receiver should be operated with factory default settings. * Initial driver operation: expects the receiver to be already locked * to GPS, configured and able to output timecode format 2 messages. * * The driver uses the poll sequence :PTIME:TCODE? to get a response from * the receiver. The receiver responds with a timecode string of ASCII * printing characters, followed by a , followed by a prompt string * issued by the receiver, in the following format: * T#yyyymmddhhmmssMFLRVccscpi > * * The driver processes the response at the and , so what the * driver sees is the prompt from the previous poll, followed by this * timecode. The prompt from the current poll is (usually) left unread until * the next poll. So (except on the very first poll) the driver sees this: * * scpi > T#yyyymmddhhmmssMFLRVcc * * The T is the on-time character, at 980 msec. before the next 1PPS edge. * The # is the timecode format type. We look for format 2. * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp * at the is 24 characters later, which is about 25 msec. at 9600 bps, * so the first approximation for fudge time1 is nominally -0.955 seconds. * This number probably needs adjusting for each machine / OS type, so far: * -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05 * -0.953175 on an HP 9000 Model 370 HP-UX 9.10 * * This receiver also provides a 1PPS signal, but I haven't figured out * how to deal with any of the CLK or PPS stuff yet. Stay tuned. * */ /* * Fudge time1 is used to accommodate the timecode serial interface adjustment. * Option flag4 can be set to request a receiver status screen summary, which * is recorded in the clockstats file. */ /* * Interface definitions */ #define DEVICE "/dev/hpgps%d" /* device name and unit */ #define SPEED232 B9600 /* uart speed (9600 baud) */ #define SPEED232Z B19200 /* uart speed (19200 baud) */ #define PRECISION (-10) /* precision assumed (about 1 ms) */ #define REFID "GPS\0" /* reference ID */ #define NAME "HPGPS" /* shortname */ #define DESCRIPTION "HP GPS Time and Frequency Reference Receiver" #define SMAX 23*80+1 /* for :SYSTEM:PRINT? status screen response */ #define MTZONE 2 /* number of fields in timezone reply */ #define MTCODET2 12 /* number of fields in timecode format T2 */ #define NTCODET2 21 /* number of chars to checksum in format T2 */ /* * Tables to compute the day of year from yyyymmdd timecode. * Viva la leap. */ static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* * Unit control structure */ struct hpgpsunit { int pollcnt; /* poll message counter */ int tzhour; /* timezone offset, hours */ int tzminute; /* timezone offset, minutes */ int linecnt; /* set for expected multiple line responses */ char *lastptr; /* pointer to receiver response data */ char statscrn[SMAX]; /* receiver status screen buffer */ }; /* * Function prototypes */ static bool hpgps_start (int, struct peer *); static void hpgps_receive (struct recvbuf *); static void hpgps_poll (int, struct peer *); /* * Transfer vector */ struct refclock refclock_hpgps = { NAME, /* basename of driver */ hpgps_start, /* start up driver */ NULL, /* shut down driver in the standard way */ hpgps_poll, /* transmit poll message */ NULL, /* not used (old hpgps_control) */ NULL, /* initialize driver */ NULL /* timer - not used */ }; /* * hpgps_start - open the devices and initialize data for processing */ static bool hpgps_start( int unit, struct peer *peer ) { struct hpgpsunit *up; struct refclockproc *pp; int fd; unsigned int ldisc; unsigned int speed; char device[20]; /* * Open serial port. Use CLK line discipline, if available. * Default is HP 58503A, mode arg selects HP Z3801A */ snprintf(device, sizeof(device), DEVICE, unit); ldisc = LDISC_CLK; speed = SPEED232; /* subtype parameter to server config line shares mode slot */ if (1 == peer->cfg.mode) { ldisc |= LDISC_7O1; speed = SPEED232Z; } fd = refclock_open(peer->cfg.path ? peer->cfg.path : device, peer->cfg.baud ? peer->cfg.baud : speed, ldisc); if (fd <= 0) /* coverity[leaked_handle] */ return false; /* * Allocate and initialize unit structure */ up = emalloc_zero(sizeof(*up)); pp = peer->procptr; pp->io.clock_recv = hpgps_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { close(fd); pp->io.fd = -1; free(up); return false; } pp->unitptr = up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; up->tzhour = 0; up->tzminute = 0; *up->statscrn = '\0'; up->lastptr = up->statscrn; up->pollcnt = 2; /* * Get the identifier string, which is logged but otherwise ignored, * and get the local timezone information */ up->linecnt = 1; if (write(pp->io.fd, "*IDN?\r:PTIME:TZONE?\r", 20) != 20) refclock_report(peer, CEVNT_FAULT); return true; } /* * hpgps_receive - receive data from the serial interface */ static void hpgps_receive( struct recvbuf *rbufp ) { struct hpgpsunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; char tcodechar1; /* identifies timecode format */ char tcodechar2; /* identifies timecode format */ char timequal; /* time figure of merit: 0-9 */ char freqqual; /* frequency figure of merit: 0-3 */ char leapchar; /* leapsecond: + or 0 or - */ char servchar; /* request for service: 0 = no, 1 = yes */ char syncchar; /* time info is invalid: 0 = no, 1 = yes */ short expectedsm; /* expected timecode byte checksum */ short tcodechksm; /* computed timecode byte checksum */ int i,m,n; int month, day, lastday; char *tcp; /* timecode pointer (skips over the prompt) */ char prompt[BMAX]; /* prompt in response from receiver */ /* * Initialize pointers and read the receiver response */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; *pp->a_lastcode = '\0'; pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); DPRINT(1, ("hpgps: lencode: %d timecode:%s\n", pp->lencode, pp->a_lastcode)); /* * If there's no characters in the reply, we can quit now */ if (pp->lencode == 0) return; /* * If linecnt is greater than zero, we are getting information only, * such as the receiver identification string or the receiver status * screen, so put the receiver response at the end of the status * screen buffer. When we have the last line, write the buffer to * the clockstats file and return without further processing. * * If linecnt is zero, we are expecting either the timezone * or a timecode. At this point, also write the response * to the clockstats file, and go on to process the prompt (if any), * timezone, or timecode and timestamp. */ if (up->linecnt-- > 0) { if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) { *up->lastptr++ = '\n'; memcpy(up->lastptr, pp->a_lastcode, (size_t)pp->lencode); up->lastptr += pp->lencode; } if (up->linecnt == 0) record_clock_stats(peer, up->statscrn); return; } record_clock_stats(peer, pp->a_lastcode); pp->lastrec = trtmp; up->lastptr = up->statscrn; *up->lastptr = '\0'; up->pollcnt = 2; /* * We get down to business: get a prompt if one is there, issue * a clear status command if it contains an error indication. * Next, check for either the timezone reply or the timecode reply * and decode it. If we don't recognize the reply, or don't get the * proper number of decoded fields, or get an out of range timezone, * or if the timecode checksum is bad, then we declare bad format * and exit. * * Timezone format (including nominal prompt): * scpi > -H,-M * * Timecode format (including nominal prompt): * scpi > T2yyyymmddhhmmssMFLRVcc * */ strlcpy(prompt, pp->a_lastcode, sizeof(prompt)); tcp = strrchr(pp->a_lastcode,'>'); if (tcp == NULL) tcp = pp->a_lastcode; else tcp++; prompt[tcp - pp->a_lastcode] = '\0'; while ((*tcp == ' ') || (*tcp == '\t')) tcp++; /* * deal with an error indication in the prompt here */ if (strrchr(prompt,'E') > strrchr(prompt,'s')){ DPRINT(1, ("hpgps: error indicated in prompt: %s\n", prompt)); if (write(pp->io.fd, "*CLS\r\r", 6) != 6) refclock_report(peer, CEVNT_FAULT); } /* * make sure we got a timezone or timecode format and * then process accordingly */ m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2); if (m != 2){ DPRINT(1, ("hpgps: no format indicator\n")); refclock_report(peer, CEVNT_BADREPLY); return; } switch (tcodechar1) { case '+': case '-': m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute); if (m != MTZONE) { DPRINT(1, ("hpgps: only %d fields recognized in timezone\n", m)); refclock_report(peer, CEVNT_BADREPLY); return; } if ((up->tzhour < -12) || (up->tzhour > 13) || (up->tzminute < -59) || (up->tzminute > 59)){ DPRINT(1, ("hpgps: timezone %d, %d out of range\n", up->tzhour, up->tzminute)); refclock_report(peer, CEVNT_BADREPLY); return; } return; case 'T': break; default: DPRINT(1, ("hpgps: unrecognized reply format %c%c\n", tcodechar1, tcodechar2)); refclock_report(peer, CEVNT_BADREPLY); return; } /* end of tcodechar1 switch */ switch (tcodechar2) { case '2': m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx", &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, &timequal, &freqqual, &leapchar, &servchar, &syncchar, (short unsigned int*)&expectedsm); n = NTCODET2; if (m != MTCODET2){ DPRINT(1, ("hpgps: only %d fields recognized in timecode\n", m)); refclock_report(peer, CEVNT_BADREPLY); return; } break; default: DPRINT(1, ("hpgps: unrecognized timecode format %c%c\n", tcodechar1, tcodechar2)); refclock_report(peer, CEVNT_BADREPLY); return; } /* end of tcodechar2 format switch */ /* * Compute and verify the checksum. * Characters are summed starting at tcodechar1, ending at just * before the expected checksum. Bail out if incorrect. */ tcodechksm = 0; while (n-- > 0) tcodechksm += *tcp++; tcodechksm &= 0x00ff; if (tcodechksm != expectedsm) { DPRINT(1, ("hpgps: checksum %2hX doesn't match %2hX expected\n", tcodechksm, expectedsm)); refclock_report(peer, CEVNT_BADREPLY); return; } /* * Compute the day of year from the yyyymmdd format. */ if (month < 1 || month > 12 || day < 1) { refclock_report(peer, CEVNT_BADTIME); return; } if ( ! is_leapyear(pp->year) ) { /* Y2KFixes */ /* not a leap year */ if (day > day1tab[month - 1]) { refclock_report(peer, CEVNT_BADTIME); return; } for (i = 0; i < month - 1; i++) day += day1tab[i]; lastday = 365; } else { /* a leap year */ if (day > day2tab[month - 1]) { refclock_report(peer, CEVNT_BADTIME); return; } for (i = 0; i < month - 1; i++) day += day2tab[i]; lastday = 366; } /* * Deal with the timezone offset here. The receiver timecode is in * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values. * For example, Pacific Standard Time is -8 hours , 0 minutes. * Deal with the underflows and overflows. */ pp->minute -= up->tzminute; pp->hour -= up->tzhour; if (pp->minute < 0) { pp->minute += 60; pp->hour--; } if (pp->minute > 59) { pp->minute -= 60; pp->hour++; } if (pp->hour < 0) { pp->hour += 24; day--; if (day < 1) { pp->year--; if ( is_leapyear(pp->year) ) /* Y2KFixes */ day = 366; else day = 365; } } if (pp->hour > 23) { pp->hour -= 24; day++; if (day > lastday) { pp->year++; day = 1; } } pp->day = day; /* * Decode the MFLRV indicators. * NEED TO FIGURE OUT how to deal with the request for service, * time quality, and frequency quality indicators some day. */ if (syncchar != '0') { pp->leap = LEAP_NOTINSYNC; } else { pp->leap = LEAP_NOWARNING; switch (leapchar) { case '0': break; /* See http://bugs.ntp.org/1090 * Ignore leap announcements unless June or December. * Better would be to use :GPSTime? to find the month, * but that seems too likely to introduce other bugs. */ case '+': if ((month==6) || (month==12)) pp->leap = LEAP_ADDSECOND; break; case '-': if ((month==6) || (month==12)) pp->leap = LEAP_DELSECOND; break; default: DPRINT(1, ("hpgps: unrecognized leap indicator: %c\n", leapchar)); refclock_report(peer, CEVNT_BADTIME); return; } /* end of leapchar switch */ } /* * Process the new sample in the median filter and determine the * reference clock offset and dispersion. We use lastrec as both * the reference time and receive time in order to avoid being * cute, like setting the reference time later than the receive * time, which may cause a paranoid protocol module to chuck out * the data. */ if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); return; } pp->lastref = pp->lastrec; refclock_receive(peer); /* * If CLK_FLAG4 is set, ask for the status screen response. */ if (pp->sloppyclockflag & CLK_FLAG4){ up->linecnt = 22; if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15) refclock_report(peer, CEVNT_FAULT); } } /* * hpgps_poll - called by the transmit procedure */ static void hpgps_poll( int unit, struct peer *peer ) { struct hpgpsunit *up; struct refclockproc *pp; UNUSED_ARG(unit); /* * Time to poll the clock. The HP 58503A responds to a * ":PTIME:TCODE?" by returning a timecode in the format specified * above. If nothing is heard from the clock for two polls, * declare a timeout and keep going. */ pp = peer->procptr; up = pp->unitptr; if (up->pollcnt == 0) refclock_report(peer, CEVNT_TIMEOUT); else up->pollcnt--; if (write(pp->io.fd, ":PTIME:TCODE?\r", 14) != 14) { refclock_report(peer, CEVNT_FAULT); } else pp->polls++; } ntpsec-1.1.0+dfsg1/ntpd/ntp_signd.c0000644000175000017500000001216513252364117016730 0ustar rlaagerrlaager/* Copyright 2008, Red Hat, Inc. Copyright 2008, Andrew Tridgell. Licensed under the same terms as NTP itself. */ #include "config.h" #ifdef ENABLE_MSSNTP #include "ntpd.h" #include "ntp_io.h" #include "ntp_stdlib.h" #include #include #include #include /* socket routines by tridge - from junkcode.samba.org */ /* connect to a unix domain socket */ static int ux_socket_connect(const char *name) { int fd; struct sockaddr_un addr; if (!name) { return -1; } ZERO(addr); addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, name, sizeof(addr.sun_path)); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { return -1; } if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { close(fd); return -1; } return fd; } /* keep writing until its all sent */ static int write_all(int fd, const void *buf, size_t len) { size_t total = 0; while (len) { int n = write(fd, buf, len); if (n <= 0) return total; buf = n + (const char *)buf; len -= (unsigned int)n; total += (unsigned int)n; } return total; } /* keep reading until its all read */ static int read_all(int fd, void *buf, size_t len) { size_t total = 0; while (len) { int n = read(fd, buf, len); if (n <= 0) return total; buf = n + (char *)buf; len -= (unsigned int)n; total += (unsigned int)n; } return total; } /* send a packet in length prefix format */ static int send_packet(int fd, const char *buf, uint32_t len) { uint32_t net_len = htonl(len); if (write_all(fd, &net_len, sizeof(net_len)) != sizeof(net_len)) return -1; if (write_all(fd, buf, len) != (int)len) return -1; return 0; } /* receive a packet in length prefix format */ static int recv_packet(int fd, char **buf, uint32_t *len) { if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1; *len = ntohl(*len); (*buf) = emalloc(*len); if (read_all(fd, *buf, *len) != (int)*len) { free(*buf); return -1; } return 0; } void send_via_ntp_signd( struct recvbuf *rbufp, /* receive packet pointer */ int xmode, keyid_t xkeyid, int flags, struct pkt *xpkt ) { UNUSED_ARG(flags); #ifndef DEBUG UNUSED_ARG(xmode); #endif /* We are here because it was detected that the client * sent an all-zero signature, and we therefore know * it's windows trying to talk to an AD server * * Because we don't want to dive into Samba's secrets * database just to find the long-term kerberos key * that is re-used as the NTP key, we instead hand the * packet over to Samba to sign, and return to us. * * The signing method Samba will use is described by * Microsoft in MS-SNTP, found here: * http://msdn.microsoft.com/en-us/library/cc212930.aspx */ int fd, sendlen; struct samba_key_in { uint32_t version; uint32_t op; uint32_t packet_id; uint32_t key_id_le; struct pkt pkt; } samba_pkt; struct samba_key_out { uint32_t version; uint32_t op; uint32_t packet_id; struct pkt pkt; } samba_reply; char full_socket[256]; char *reply = NULL; uint32_t reply_len; ZERO(samba_pkt); samba_pkt.op = 0; /* Sign message */ /* This will be echoed into the reply - a different * implementation might want multiple packets * awaiting signing */ samba_pkt.packet_id = 1; /* Swap the byte order back - it's actually little * endian on the wire, but it was read above as * network byte order */ samba_pkt.key_id_le = htonl(xkeyid); samba_pkt.pkt = *xpkt; snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket); fd = ux_socket_connect(full_socket); /* Only continue with this if we can talk to Samba */ if (fd != -1) { /* Send old packet to Samba, expect response */ /* Packet to Samba is quite simple: All values BIG endian except key ID as noted [packet size as BE] - 4 bytes [protocol version (0)] - 4 bytes [packet ID] - 4 bytes [operation (sign message=0)] - 4 bytes [key id] - LITTLE endian (as on wire) - 4 bytes [message to sign] - as marshalled, without signature */ if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) { /* Huh? could not talk to Samba... */ close(fd); return; } if (recv_packet(fd, &reply, &reply_len) != 0) { close(fd); return; } /* Return packet is also simple: [packet size] - network byte order - 4 bytes [protocol version (0)] network byte order - - 4 bytes [operation (signed success=3, failure=4)] network byte order - - 4 byte (optional) [signed message] - as provided before, with signature appended */ if (reply_len <= sizeof(samba_reply)) { memcpy(&samba_reply, reply, reply_len); if (ntohl(samba_reply.op) == 3 && reply_len > offsetof(struct samba_key_out, pkt)) { sendlen = reply_len - offsetof(struct samba_key_out, pkt); xpkt = &samba_reply.pkt; sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, xpkt, sendlen); DPRINT(1, ("transmit ntp_signd packet: at %u %s->%s mode %d keyid %08x len %d\n", current_time, socktoa(&rbufp->dstadr->sin), socktoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen)); } } if (reply) { free(reply); } close(fd); } } #endif ntpsec-1.1.0+dfsg1/ntpd/ntp_filegen.c0000644000175000017500000003110413252364117017227 0ustar rlaagerrlaager/* * implements file generations support for NTP * logfiles and statistic files * * * Copyright (C) 1992, 1996 by Rainer Pruy * Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany * * SPDX-License-Identifier: BSD-2-clause */ #include "config.h" #include #include #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_calendar.h" #include "ntp_filegen.h" #include "ntp_stdlib.h" /* * NTP is intended to run long periods of time without restart. * Thus log and statistic files generated by NTP will grow large. * * this set of routines provides a central interface * to generating files using file generations * * the generation of a file is changed according to file generation type */ /* * redefine this if your system dislikes filename suffixes like * X.19910101 or X.1992W50 or .... */ #define SUFFIX_SEP '.' static void filegen_open (FILEGEN *, const time_t); static int valid_fileref (const char *, const char *) __attribute__((pure)); static void filegen_init (const char *, const char *, FILEGEN *); #ifdef DEBUG static void filegen_uninit (FILEGEN *); #endif /* DEBUG */ /* * filegen_init */ static void filegen_init( const char * dir, const char * fname, FILEGEN * fgp ) { fgp->fp = NULL; fgp->dir = estrdup(dir); fgp->fname = estrdup(fname); fgp->id_lo = 0; fgp->id_hi = 0; fgp->type = FILEGEN_DAY; fgp->flag = FGEN_FLAG_LINK; /* not yet enabled !!*/ } /* * filegen_uninit - free memory allocated by filegen_init */ #ifdef DEBUG static void filegen_uninit( FILEGEN *fgp ) { free(fgp->dir); free(fgp->fname); } #endif /* * open a file generation according to the current settings of gen * will also provide a link to basename if requested to do so */ static void filegen_open( FILEGEN * gen, const time_t stamp ) { char *savename; /* temp store for name collision handling */ char *fullname; /* name with any designation extension */ char *filename; /* name without designation extension */ char *suffix; /* where to print suffix extension */ unsigned int len, suflen; FILE *fp; struct tm tm; /* get basic filename in buffer, leave room for extensions */ len = strlen(gen->dir) + strlen(gen->fname) + 65; filename = emalloc(len); fullname = emalloc(len); savename = NULL; snprintf(filename, len, "%s%s", gen->dir, gen->fname); /* where to place suffix */ suflen = strlcpy(fullname, filename, len); suffix = fullname + suflen; suflen = len - suflen; /* last octet of fullname set to '\0' for truncation check */ fullname[len - 1] = '\0'; switch (gen->type) { default: msyslog(LOG_ERR, "LOG: unsupported file generations type %d for " "\"%s\" - reverting to FILEGEN_NONE", gen->type, filename); gen->type = FILEGEN_NONE; break; case FILEGEN_NONE: /* no suffix, all set */ break; case FILEGEN_PID: gen->id_lo = getpid(); gen->id_hi = 0; snprintf(suffix, suflen, "%c#%lld", SUFFIX_SEP, (long long)gen->id_lo); break; case FILEGEN_DAY: /* * You can argue here in favor of using MJD, but I * would assume it to be easier for humans to interpret * dates in a format they are used to in everyday life. */ gmtime_r(&stamp, &tm); snprintf(suffix, suflen, "%c%04d%02d%02d", SUFFIX_SEP, tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday); gen->id_lo = stamp - (stamp % SECSPERDAY); gen->id_hi = gen->id_lo + SECSPERDAY; break; case FILEGEN_WEEK: /* week number is day-of-year mod 7 */ gmtime_r(&stamp, &tm); snprintf(suffix, suflen, "%c%04dw%02d", SUFFIX_SEP, tm.tm_year+1900, tm.tm_yday % 7); /* See comment below at MONTH */ gen->id_lo = stamp - (stamp % SECSPERDAY); gen->id_hi = gen->id_lo + SECSPERDAY; break; case FILEGEN_MONTH: gmtime_r(&stamp, &tm); snprintf(suffix, suflen, "%c%04d%02d", SUFFIX_SEP, tm.tm_year+1900, tm.tm_mon+1); /* If we had a mktime that didn't use the local time zone * we could setup id_lo and id_hi to bracket the month. * This will have to recalculate things each day. */ gen->id_lo = stamp - (stamp % SECSPERDAY); gen->id_hi = gen->id_lo + SECSPERDAY; break; case FILEGEN_YEAR: gmtime_r(&stamp, &tm); snprintf(suffix, suflen, "%c%04d", SUFFIX_SEP, tm.tm_year+1900); /* See comment above at MONTH */ gen->id_lo = stamp - (stamp % SECSPERDAY); gen->id_hi = gen->id_lo + SECSPERDAY; break; case FILEGEN_AGE: gen->id_lo = (time_t)(current_time - (current_time % SECSPERDAY)); gen->id_hi = gen->id_lo + SECSPERDAY; snprintf(suffix, suflen, "%ca%08lld", SUFFIX_SEP, (long long)gen->id_lo); } /* check possible truncation */ if ('\0' != fullname[len - 1]) { fullname[len - 1] = '\0'; msyslog(LOG_ERR, "LOG: logfile name truncated: \"%s\"", fullname); } if (FILEGEN_NONE != gen->type) { /* * check for existence of a file with name 'basename' * as we disallow such a file * if FGEN_FLAG_LINK is set create a link */ struct stat stats; /* * try to resolve name collisions */ static unsigned long conflicts = 0; #ifndef S_ISREG #define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG) #endif /* coverity[toctou] */ if (stat(filename, &stats) == 0) { /* Hm, file exists... */ if (S_ISREG(stats.st_mode)) { if (stats.st_nlink <= 1) { /* * Oh, it is not linked - try to save it */ savename = emalloc(len); snprintf(savename, len, "%s%c%dC%lu", filename, SUFFIX_SEP, (int)getpid(), conflicts++); if (rename(filename, savename) != 0) msyslog(LOG_ERR, "LOG: couldn't save %s: %m", filename); free(savename); } else { /* * there is at least a second link to * this file. * just remove the conflicting one */ /* coverity[toctou] */ if (unlink(filename) != 0) msyslog(LOG_ERR, "LOG: couldn't unlink %s: %m", filename); } } else { /* * Ehh? Not a regular file ?? strange !!!! */ msyslog(LOG_ERR, "LOG: expected regular file for %s " "(found mode 0%lo)", filename, (unsigned long)stats.st_mode); } } else { /* * stat(..) failed, but it is absolutely correct for * 'basename' not to exist */ if (ENOENT != errno) msyslog(LOG_ERR, "LOG: stat(%s) failed: %m", filename); } } /* * now, try to open new file generation... */ DPRINT(4, ("opening filegen (type=%d/stamp=%lld) \"%s\"\n", gen->type, (long long)stamp, fullname)); fp = fopen(fullname, "a"); if (NULL == fp) { /* open failed -- keep previous state * * If the file was open before keep the previous generation. * This will cause output to end up in the 'wrong' file, * but I think this is still better than losing output * * ignore errors due to missing directories */ if (ENOENT != errno) msyslog(LOG_ERR, "LOG: can't open %s: %m", fullname); } else { if (NULL != gen->fp) { fclose(gen->fp); gen->fp = NULL; } gen->fp = fp; if (gen->flag & FGEN_FLAG_LINK) { /* * need to link file to basename * have to use hardlink for now as I want to allow * gen->basename spanning directory levels * this would make it more complex to get the correct * fullname for symlink * * Ok, it would just mean taking the part following * the last '/' in the name.... Should add it later.... */ if (link(fullname, filename) != 0) if (EEXIST != errno) msyslog(LOG_ERR, "LOG: can't link(%s, %s): %m", fullname, filename); } /* flags & FGEN_FLAG_LINK */ } /* else fp == NULL */ free(filename); free(fullname); return; } /* * this function sets up gen->fp to point to the correct * generation of the file for the time specified by 'now' */ void filegen_setup( FILEGEN * gen, time_t now ) { bool current; if (!(gen->flag & FGEN_FLAG_ENABLED)) { if (NULL != gen->fp) { fclose(gen->fp); gen->fp = NULL; } return; } switch (gen->type) { default: case FILEGEN_NONE: current = true; break; case FILEGEN_PID: current = ((int)gen->id_lo == getpid()); break; case FILEGEN_AGE: current = (gen->id_lo <= (long)current_time) && (gen->id_hi > (long)current_time); break; case FILEGEN_DAY: case FILEGEN_WEEK: case FILEGEN_MONTH: case FILEGEN_YEAR: current = (gen->id_lo <= now) && (gen->id_hi > now); break; } /* * try to open file if not yet open * reopen new file generation file on change of generation id */ if (NULL == gen->fp || !current) { DPRINT(1, ("filegen %0x %lld\n", gen->type, (long long)now)); filegen_open(gen, now); } } /* * change settings for filegen files */ void filegen_config( FILEGEN * gen, const char * dir, const char * fname, unsigned int type, unsigned int flag ) { bool file_existed; /* * if nothing would be changed... */ if (strcmp(dir, gen->dir) == 0 && strcmp(fname, gen->fname) == 0 && type == gen->type && flag == gen->flag) return; /* * validate parameters */ if (!valid_fileref(dir, fname)) return; if (NULL != gen->fp) { fclose(gen->fp); gen->fp = NULL; file_existed = true; } else { file_existed = false; } DPRINT(3, ("configuring filegen:\n" "\tdir:\t%s -> %s\n" "\tfname:\t%s -> %s\n" "\ttype:\t%d -> %u\n" "\tflag: %x -> %x\n", gen->dir, dir, gen->fname, fname, gen->type, type, gen->flag, flag)); if (strcmp(gen->dir, dir) != 0) { free(gen->dir); gen->dir = estrdup(dir); } if (strcmp(gen->fname, fname) != 0) { free(gen->fname); gen->fname = estrdup(fname); } gen->type = (uint8_t)type; gen->flag = (uint8_t)flag; /* * make filegen use the new settings * special action is only required when a generation file * is currently open * otherwise the new settings will be used anyway at the next open */ if (file_existed) { filegen_setup(gen, time(NULL)); } } /* * check whether concatenating prefix and basename * yields a legal filename */ static int valid_fileref( const char * dir, const char * fname ) { /* * dir cannot be changed dynamically * (within the context of filegen) * so just reject basenames containing '..' * * ASSUMPTION: * file system parts 'below' dir may be * specified without infringement of security * * restricting dir to legal values * has to be ensured by other means * (however, it would be possible to perform some checks here...) */ const char *p; /* * Just to catch, dumb errors opening up the world... */ if (NULL == dir || '\0' == dir[0]) return false; if (NULL == fname) return false; for (p = fname; p != NULL; p = strchr(p, DIR_SEP)) { if ('.' == p[0] && '.' == p[1] && ('\0' == p[2] || DIR_SEP == p[2])) return false; } return true; } /* * filegen registry */ static struct filegen_entry { char * name; FILEGEN * filegen; struct filegen_entry * next; } *filegen_registry = NULL; FILEGEN * filegen_get( const char * name ) { struct filegen_entry *f = filegen_registry; while (f) { if (f->name == name || strcmp(name, f->name) == 0) { DPRINT(4, ("filegen_get(%s) = %p\n", name, f->filegen)); return f->filegen; } f = f->next; } DPRINT(4, ("filegen_get(%s) = NULL\n", name)); return NULL; } void filegen_register( const char * dir, const char * name, FILEGEN * filegen ) { struct filegen_entry **ppfe; DPRINT(4, ("filegen_register(%s, %p)\n", name, filegen)); filegen_init(dir, name, filegen); ppfe = &filegen_registry; while (NULL != *ppfe) { if ((*ppfe)->name == name || !strcmp((*ppfe)->name, name)) { DPRINT(5, ("replacing filegen %p\n", (*ppfe)->filegen)); (*ppfe)->filegen = filegen; return; } ppfe = &((*ppfe)->next); } *ppfe = emalloc(sizeof **ppfe); (*ppfe)->next = NULL; (*ppfe)->name = estrdup(name); (*ppfe)->filegen = filegen; DPRINT(6, ("adding new filegen\n")); return; } /* * filegen_statsdir() - reset each filegen entry's dir to statsdir. */ void filegen_statsdir(void) { struct filegen_entry *f; for (f = filegen_registry; f != NULL; f = f->next) filegen_config(f->filegen, statsdir, f->filegen->fname, f->filegen->type, f->filegen->flag); } /* * filegen_unregister frees memory allocated by filegen_register for * name. */ #ifdef DEBUG void filegen_unregister( const char *name ) { struct filegen_entry ** ppfe; struct filegen_entry * pfe; FILEGEN * fg; DPRINT(4, ("filegen_unregister(%s)\n", name)); ppfe = &filegen_registry; while (NULL != *ppfe) { if ((*ppfe)->name == name || !strcmp((*ppfe)->name, name)) { pfe = *ppfe; *ppfe = (*ppfe)->next; fg = pfe->filegen; free(pfe->name); free(pfe); filegen_uninit(fg); break; } ppfe = &((*ppfe)->next); } } #endif /* DEBUG */ ntpsec-1.1.0+dfsg1/ntpd/refclock_generic.c0000644000175000017500000045563713252364117020246 0ustar rlaagerrlaager/* * generic reference clock driver for several DCF/GPS/MSF/... receivers * * PPS notes: * On systems that support PPSAPI (RFC 2783) PPSAPI is the * preferred interface. * * Copyright (c) 1989-2015 by Frank Kardel * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-3-Clause * * Note: some subtypes are obsolete and could probably stand to be removed * next time this code gets serious attention. In particular, subtypes 9 and 10 * support the Trimble SVeeSix, which was discontinued before 2003. Related * code in the parse library could also be dropped. * Also see subtypes 3 and 4, for which no information in use since 1999 and * 2001 respectively can be found on the web. * * WARNING: Most modes of this driver depend on the system clock for * year disambiguation. They will thus not be usable for recovery if * the system clock is trashed. The only exceptions are the Scheitzer 240x * and the two Trimble devices. */ #include "config.h" #include "ntp_types.h" #include "timespecops.h" /* * This driver currently provides the support for * - Meinberg receiver DCF77 PZF535 (TCXO version) (DCF) * - Meinberg receiver DCF77 PZF535 (OCXO version) (DCF) * - Meinberg receiver DCF77 PZF509 (DCF) * - Meinberg receiver DCF77 AM receivers (e.g. C51) (DCF) * - IGEL CLOCK (DCF) * - ELV DCF7000 (DCF) * - Schmid clock (DCF) * - Conrad DCF77 receiver module (DCF) * - FAU DCF77 NTP receiver (TimeBrick) (DCF) * - WHARTON 400A Series clock (DCF) * * - Meinberg GPS receivers (GPS) * - Trimble (TSIP and TAIP protocol) (GPS) * * - RCC8000 MSF Receiver (MSF) * - VARITEXT clock (MSF) */ /* * Meinberg receivers are usually connected via a * 9600/7E1 or 19200/8N1 serial line. * * The Meinberg GPS receivers also have a special NTP time stamp * format. The firmware release is Uni-Erlangen. * * Meinberg generic receiver setup: * output time code every second * Baud rate 9600 7E2S * * Meinberg GPS receiver setup: * output time code every second * Baudrate 19200 8N1 * * This software supports the standard data formats used * in Meinberg receivers. * * Special software versions are only sensible for the * oldest GPS receiver, GPS16x. For newer receiver types * the output string format can be configured at the device, * and the device name is generally GPSxxx instead of GPS16x. * * Meinberg can be reached via: http://www.meinberg.de/ */ #include "ntpd.h" #include "ntp_refclock.h" #include "ntp_calendar.h" #include #include #include #include #include #include #include #define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_)) #define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_)) #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_PPSAPI # include "ppsapi_timepps.h" # include "refclock_pps.h" #endif #ifdef HAVE_LINUX_SERIAL_H # include #endif #define BUFFER_SIZE(_BUF, _PTR) ((size_t)((_BUF) + sizeof(_BUF) - (_PTR))) #define BUFFER_SIZES(_BUF, _PTR, _SZ) ((int)((_BUF) + (_SZ) - (_PTR))) /* * COND_DEF can be conditionally defined as DEF or 0. If defined as DEF * then some more parse-specific variables are flagged to be printed with * "ntpq -c cv ". This can be lengthy, so by default COND_DEF * should be defined as 0. */ #if 0 # define COND_DEF DEF // enable this for testing #else # define COND_DEF 0 // enable this by default #endif #include "ntp_io.h" #include "ntp_stdlib.h" #include "parse.h" #include "mbg_gps166.h" #include "trimble.h" #include "binio.h" #include "ascii.h" #include "recvbuff.h" #define VERSION "4.81 2009/05/01 10:15:29" /**=========================================================================== ** external interface to ntp mechanism **/ static bool parse_start (int, struct peer *); static void parse_shutdown (struct refclockproc *); static void parse_poll (int, struct peer *); static void parse_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); struct refclock refclock_parse = { "GENERIC", /* basename of driver */ parse_start, /* start up driver */ parse_shutdown, /* shut down driver */ parse_poll, /* transmit poll message */ parse_control, /* control settings */ NULL, /* init */ NULL, /* timer */ }; /* * Definitions */ #define PARSEDEVICE "/dev/refclock-%u" /* device to open %d is unit number */ #define PARSEPPSDEVICE "/dev/refclockpps-%u" /* optional pps device to open %d is unit number */ #undef ABS #define ABS(_X_) (((_X_) < 0) ? -(_X_) : (_X_)) #ifdef HAVE_PPSAPI # define PARSE_HARDPPS_DISABLE 0 # define PARSE_HARDPPS_ENABLE 1 #endif /**=========================================================================== ** function vector for dynamically binding io handling mechanism **/ struct parseunit; /* to keep inquiring minds happy */ typedef struct bind { const char *bd_description; /* name of type of binding */ int (*bd_init) (struct parseunit *); /* initialize */ void (*bd_end) (struct parseunit *); /* end */ bool (*bd_setcs) (struct parseunit *, parsectl_t *); /* set character size */ int (*bd_disable) (struct parseunit *); /* disable */ int (*bd_enable) (struct parseunit *); /* enable */ int (*bd_getfmt) (struct parseunit *, parsectl_t *); /* get format */ int (*bd_setfmt) (struct parseunit *, parsectl_t *); /* setfmt */ int (*bd_timecode) (struct parseunit *, parsectl_t *); /* get time code */ void (*bd_receive) (struct recvbuf *); /* receive operation */ int (*bd_io_input) (struct recvbuf *); /* input operation */ } bind_t; #define PARSE_END(_X_) (*(_X_)->binding->bd_end)(_X_) #define PARSE_SETCS(_X_, _CS_) (*(_X_)->binding->bd_setcs)(_X_, _CS_) #define PARSE_GETFMT(_X_, _DCT_) (*(_X_)->binding->bd_getfmt)(_X_, _DCT_) #define PARSE_SETFMT(_X_, _DCT_) (*(_X_)->binding->bd_setfmt)(_X_, _DCT_) #define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_) /* * special handling flags */ #define PARSE_F_PPSONSECOND 0x00000001 /* PPS pulses are on second */ #define PARSE_F_POWERUPTRUST 0x00000100 /* POWERUP state ist trusted for */ /* trusttime after SYNC was seen */ /**=========================================================================== ** error message regression handling ** ** there are quite a few errors that can occur in rapid succession such as ** noisy input data or no data at all. in order to reduce the amount of ** syslog messages in such case, we are using a backoff algorithm. We limit ** the number of error messages of a certain class to 1 per time unit. if a ** configurable number of messages is displayed that way, we move on to the ** next time unit / count for that class. a count of messages that have been ** suppressed is held and displayed whenever a corresponding message is ** displayed. the time units for a message class will also be displayed. ** whenever an error condition clears we reset the error message state, ** thus we would still generate much output on pathological conditions ** where the system oscillates between OK and NOT OK states. coping ** with that condition is currently considered too complicated. **/ #define ERR_ALL (unsigned)~0 /* "all" errors */ #define ERR_BADDATA (unsigned)0 /* unusable input data/conversion errors */ #define ERR_NODATA (unsigned)1 /* no input data */ #define ERR_BADIO (unsigned)2 /* read/write/select errors */ #define ERR_BADSTATUS (unsigned)3 /* unsync states */ #define ERR_INTERNAL (unsigned)5 /* internal error */ #define ERR_CNT (unsigned)(ERR_INTERNAL+1) #define ERR(_X_) if (list_err(parse, (_X_))) struct errorregression { unsigned long err_count; /* number of repetitions per class */ unsigned long err_delay; /* minimum delay between messages */ }; static struct errorregression err_baddata[] = /* error messages for bad input data */ { { 1, 0 }, /* output first message immediately */ { 5, 60 }, /* output next five messages in 60 second intervals */ { 3, SECSPERHR }, /* output next 3 messages in hour intervals */ { 0, 12 * SECSPERHR } /* repeat messages only every 12 hours */ }; static struct errorregression err_nodata[] = /* error messages for missing input data */ { { 1, 0 }, /* output first message immediately */ { 5, 60 }, /* output next five messages in 60 second intervals */ { 3, SECSPERHR }, /* output next 3 messages in hour intervals */ { 0, 12 * SECSPERHR } /* repeat messages only every 12 hours */ }; static struct errorregression err_badstatus[] = /* unsynchronized state messages */ { { 1, 0 }, /* output first message immediately */ { 5, 60 }, /* output next five messages in 60 second intervals */ { 3, SECSPERHR }, /* output next 3 messages in hour intervals */ { 0, 12 * SECSPERHR } /* repeat messages only every 12 hours */ }; static struct errorregression err_badio[] = /* io failures (bad reads, selects, ...) */ { { 1, 0 }, /* output first message immediately */ { 5, 60 }, /* output next five messages in 60 second intervals */ { 5, SECSPERHR }, /* output next 3 messages in hour intervals */ { 0, 12 * SECSPERHR } /* repeat messages only every 12 hours */ }; static struct errorregression err_badevent[] = /* non nominal events */ { { 20, 0 }, /* output first message immediately */ { 6, 60 }, /* output next five messages in 60 second intervals */ { 5, SECSPERHR }, /* output next 3 messages in hour intervals */ { 0, 12 * SECSPERHR } /* repeat messages only every 12 hours */ }; static struct errorregression err_internal[] = /* really bad things - basically coding/OS errors */ { { 0, 0 }, /* output all messages immediately */ }; static struct errorregression * err_tbl[] = { err_baddata, err_nodata, err_badio, err_badstatus, err_badevent, err_internal }; struct errorinfo { uptime_t err_started; /* begin time (ntp) of error condition */ uptime_t err_last; /* last time (ntp) error occurred */ unsigned long err_cnt; /* number of error repetitions */ unsigned long err_suppressed; /* number of suppressed messages */ struct errorregression *err_stage; /* current error stage */ }; /**=========================================================================== ** refclock instance data **/ struct parseunit { /* * NTP management */ struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */ struct refclockproc *generic; /* backlink to refclockproc structure */ /* * PARSE io */ bind_t *binding; /* io handling binding */ /* * parse state */ parse_t parseio; /* io handling structure (user level parsing) */ /* * type specific parameters */ struct parse_clockinfo *parse_type; /* link to clock description */ /* * clock state handling/reporting */ uint8_t flags; /* flags (leap_control) */ uptime_t lastchange; /* time (ntp) when last state change accured */ unsigned long statetime[CEVNT_MAX+1]; /* accumulated time of clock states */ unsigned long pollneeddata; /* current_time(!=0) for receive sample expected in PPS mode */ unsigned short lastformat; /* last format used */ unsigned long lastsync; /* time (ntp) when clock was last seen fully synchronized */ unsigned long maxunsync; /* max time in seconds a receiver is trusted after losing synchronisation */ double ppsphaseadjust; /* phase adjustment of PPS time stamp */ unsigned long lastmissed; /* time (ntp) when poll didn't get data (powerup heuristic) */ unsigned long ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */ int ppsfd; /* fd to ise for PPS io */ #ifdef HAVE_PPSAPI int hardppsstate; /* current hard pps state */ struct refclock_ppsctl ppsctl; /* PPSAPI structure */ #endif parsetime_t timedata; /* last (parse module) data */ void *localdata; /* optional local, receiver-specific data */ unsigned long localstate; /* private local state */ struct errorinfo errors[ERR_CNT]; /* error state table for suppressing excessive error messages */ struct ctl_var *kv; /* additional pseudo variables */ uptime_t laststatistic; /* time when staticstics where output */ }; /**=========================================================================== ** Clockinfo section all parameter for specific clock types ** includes NTP parameters, TTY parameters and IO handling parameters **/ static void poll_dpoll (struct parseunit *); static void poll_poll (struct peer *); static bool poll_init (struct parseunit *); typedef struct poll_info { unsigned long rate; /* poll once every "rate" seconds - 0 off */ const char *string; /* string to send for polling */ unsigned long count; /* number of characters in string */ } poll_info_t; #define NO_CL_FLAGS 0 #define NO_POLL 0 #define NO_INIT 0 #define NO_END 0 #define NO_EVENT 0 #define NO_LCLDATA 0 #define NO_MESSAGE 0 #define DCF_ID "DCF" /* generic DCF */ #define DCF_A_ID "DCFa" /* AM demodulation */ #define DCF_P_ID "DCFp" /* pseudo random phase shift */ #define GPS_ID "GPS" /* GPS receiver */ #define MSF_ID "MSF" /* MSF receiver */ #define DCF_TYPE CTL_SST_TS_LF #define GPS_TYPE CTL_SST_TS_UHF /* * receiver specific constants */ #define MBG_SPEED (B9600) #define MBG_CFLAG (CS7|PARENB|CREAD|CLOCAL|HUPCL|CSTOPB) #define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP) #define MBG_OFLAG 0 #define MBG_LFLAG 0 #define MBG_FLAGS PARSE_F_PPSONSECOND /* * Meinberg DCF77 receivers */ #define DCFUA31_ROOTDELAY 0.0 /* 0 */ #define DCFUA31_BASEDELAY 0.010 /* 10.7421875ms: 10 ms (+/- 3 ms) */ #define DCFUA31_NAME "MEINBERG_C51" #define DCFUA31_DESCRIPTION "Meinberg DCF77 C51 or compatible" #define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */ #define DCFUA31_SPEED MBG_SPEED #define DCFUA31_CFLAG MBG_CFLAG #define DCFUA31_IFLAG MBG_IFLAG #define DCFUA31_OFLAG MBG_OFLAG #define DCFUA31_LFLAG MBG_LFLAG #define DCFUA31_SAMPLES 5 #define DCFUA31_KEEP 3 #define DCFUA31_FORMAT "Meinberg Standard" /* * Meinberg DCF PZF535/TCXO (FM/PZF) receiver */ #define DCFPZF535_ROOTDELAY 0.0 #define DCFPZF535_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ #define DCFPZF535_NAME "MEINBERG_5XX" #define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/509 / TCXO" #define DCFPZF535_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours * @ 5e-8df/f we have accumulated * at most 2.16 ms (thus we move to * NTP synchronisation */ #define DCFPZF535_SPEED MBG_SPEED #define DCFPZF535_CFLAG MBG_CFLAG #define DCFPZF535_IFLAG MBG_IFLAG #define DCFPZF535_OFLAG MBG_OFLAG #define DCFPZF535_LFLAG MBG_LFLAG #define DCFPZF535_SAMPLES 5 #define DCFPZF535_KEEP 3 #define DCFPZF535_FORMAT "Meinberg Standard" /* * Meinberg DCF PZF535/OCXO receiver */ #define DCFPZF535OCXO_ROOTDELAY 0.0 #define DCFPZF535OCXO_BASEDELAY 0.001968 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ #define DCFPZF535OCXO_NAME "MEINBERG_5XX" #define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/509 / OCXO" #define DCFPZF535OCXO_MAXUNSYNC 60*60*96 /* only trust clock for 4 days * @ 5e-9df/f we have accumulated * at most an error of 1.73 ms * (thus we move to NTP synchronisation) */ #define DCFPZF535OCXO_SPEED MBG_SPEED #define DCFPZF535OCXO_CFLAG MBG_CFLAG #define DCFPZF535OCXO_IFLAG MBG_IFLAG #define DCFPZF535OCXO_OFLAG MBG_OFLAG #define DCFPZF535OCXO_LFLAG MBG_LFLAG #define DCFPZF535OCXO_SAMPLES 5 #define DCFPZF535OCXO_KEEP 3 #define DCFPZF535OCXO_FORMAT "Meinberg Standard" /* * Meinberg GPS receivers */ static void gps16x_message (struct parseunit *, parsetime_t *); static bool gps16x_poll_init (struct parseunit *); #define GPS16X_ROOTDELAY 0.0 /* nothing here */ #define GPS16X_BASEDELAY 0.001968 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */ #define GPS16X_NAME "GPS_MEINBERG" #define GPS16X_DESCRIPTION "Meinberg GPS receiver" #define GPS16X_MAXUNSYNC 60*60*96 /* only trust clock for 4 days * @ 5e-9df/f we have accumulated * at most an error of 1.73 ms * (thus we move to NTP synchronisation) */ #define GPS16X_SPEED B19200 #define GPS16X_CFLAG (CS8|CREAD|CLOCAL|HUPCL) #define GPS16X_IFLAG (IGNBRK|IGNPAR) #define GPS16X_OFLAG MBG_OFLAG #define GPS16X_LFLAG MBG_LFLAG #define GPS16X_POLLRATE 6 #define GPS16X_POLLCMD "" #define GPS16X_CMDSIZE 0 static poll_info_t gps16x_pollinfo = { GPS16X_POLLRATE, GPS16X_POLLCMD, GPS16X_CMDSIZE }; #define GPS16X_INIT gps16x_poll_init #define GPS16X_POLL 0 #define GPS16X_END 0 #define GPS16X_DATA ((void *)(&gps16x_pollinfo)) #define GPS16X_MESSAGE gps16x_message #define GPS16X_ID GPS_ID #define GPS16X_FORMAT "Meinberg GPS Extended" #define GPS16X_SAMPLES 5 #define GPS16X_KEEP 3 /* * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit) * * This is really not the hottest clock - but before you have nothing ... */ #define DCF7000_ROOTDELAY 0.0 /* 0 */ #define DCF7000_BASEDELAY 0.405 /* slow blow */ #define DCF7000_NAME "ELV_DCF7000" #define DCF7000_DESCRIPTION "ELV DCF7000" #define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */ #define DCF7000_SPEED (B9600) #define DCF7000_CFLAG (CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL) #define DCF7000_IFLAG (IGNBRK) #define DCF7000_OFLAG 0 #define DCF7000_LFLAG 0 #define DCF7000_SAMPLES 5 #define DCF7000_KEEP 3 #define DCF7000_FORMAT "ELV DCF7000" /* * Schmid DCF Receiver Kit * * When the WSDCF clock is operating optimally we want the primary clock * distance to come out at 300 ms. Thus, peer.distance in the WSDCF peer * structure is set to 290 ms and we compute delays which are at least * 10 ms long. The following are 290 ms and 10 ms expressed in unsigned fp format */ #define WS_POLLRATE 1 /* every second - watch interdependency with poll routine */ #define WS_POLLCMD "\163" #define WS_CMDSIZE 1 static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE }; #define WSDCF_INIT poll_init #define WSDCF_POLL poll_dpoll #define WSDCF_END 0 #define WSDCF_DATA ((void *)(&wsdcf_pollinfo)) #define WSDCF_ROOTDELAY 0.0 /* 0 */ #define WSDCF_BASEDELAY 0.010 /* ~ 10ms */ #define WSDCF_NAME "WSDCF" #define WSDCF_DESCRIPTION "WS/DCF Receiver" #define WSDCF_FORMAT "Schmid" #define WSDCF_MAXUNSYNC (60*60) /* assume this beast hold at 1 h better than 2 ms XXX-must verify */ #define WSDCF_SPEED (B1200) #define WSDCF_CFLAG (CS8|CREAD|CLOCAL) #define WSDCF_IFLAG 0 #define WSDCF_OFLAG 0 #define WSDCF_LFLAG 0 #define WSDCF_SAMPLES 5 #define WSDCF_KEEP 3 /* * RAW DCF77 - input of DCF marks via RS232 - many variants */ #define RAWDCF_FLAGS 0 #define RAWDCF_ROOTDELAY 0.0 /* 0 */ #define RAWDCF_BASEDELAY 0.258 #define RAWDCF_FORMAT "RAW DCF77 Timecode" #define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */ #define RAWDCF_SPEED (B50) #define RAWDCF_CFLAG (CS8|CREAD|CLOCAL|PARENB) #define RAWDCF_IFLAG (IGNPAR) #define RAWDCF_OFLAG 0 #define RAWDCF_LFLAG 0 #define RAWDCF_SAMPLES 20 #define RAWDCF_KEEP 12 #define RAWDCF_INIT 0 /* * RAW DCF variants */ /* * Conrad receiver * * simplest (cheapest) DCF clock - e. g. DCF77 receiver by Conrad * (~40DM - roughly $30 ) followed by a level converter for RS232 */ #define CONRAD_BASEDELAY 0.292 /* Conrad receiver @ 50 Baud on a Sun */ #define CONRAD_NAME "RAWDCF_CONRAD" #define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)" /* Gude Analog- und Digitalsystem GmbH 'Expert mouseCLOCK USB v2.0' */ #define GUDE_EMC_USB_V20_SPEED (B4800) #define GUDE_EMC_USB_V20_BASEDELAY 0.425 /* USB serial<->USB converter FTDI232R */ #define GUDE_EMC_USB_V20_NAME "RAWDCF_MOUSECLOCK" #define GUDE_EMC_USB_V20_DESCRIPTION "RAW DCF77 CODE (Expert mouseCLOCK USB v2.0)" /* * TimeBrick receiver */ #define TIMEBRICK_BASEDELAY 0.210 /* TimeBrick @ 50 Baud on a Sun */ #define TIMEBRICK_NAME "RAWDCF_TIMEBRICK" #define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)" /* * IGEL:clock receiver */ #define IGELCLOCK_BASEDELAY 0.258 /* IGEL:clock receiver */ #define IGELCLOCK_NAME "RAWDCF_IGEL" #define IGELCLOCK_DESCRIPTION "RAW DCF77 CODE (IGEL:clock)" #define IGELCLOCK_SPEED (B1200) #define IGELCLOCK_CFLAG (CS8|CREAD|HUPCL|CLOCAL) /* * RAWDCF receivers that need to be powered from DTR * (like Expert mouse clock) */ static bool rawdcf_init_1 (struct parseunit *); #define RAWDCFDTRSET_NAME "RAW_DCF77" #define RAWDCFDTRSET_DESCRIPTION "RAW DCF77 CODE (DTR SET/RTS CLR)" #define RAWDCFDTRSET75_DESCRIPTION "RAW DCF77 CODE (DTR SET/RTS CLR @ 75 baud)" #define RAWDCFDTRSET_INIT rawdcf_init_1 /* * RAWDCF receivers that need to be powered from * DTR CLR and RTS SET */ static bool rawdcf_init_2 (struct parseunit *); #define RAWDCFDTRCLRRTSSET_NAME "RAW_DCF77" #define RAWDCFDTRCLRRTSSET_DESCRIPTION "RAW DCF77 CODE (DTR CLR/RTS SET)" #define RAWDCFDTRCLRRTSSET75_DESCRIPTION "RAW DCF77 CODE (DTR CLR/RTS SET @ 75 baud)" #define RAWDCFDTRCLRRTSSET_INIT rawdcf_init_2 /* * Trimble GPS receivers (TAIP and TSIP protocols) */ #ifndef TRIM_POLLRATE #define TRIM_POLLRATE 0 /* only true direct polling */ #endif #define TRIM_TAIPPOLLCMD ">SRM;FR_FLAG=F;EC_FLAG=F<>QTM<" #define TRIM_TAIPCMDSIZE (sizeof(TRIM_TAIPPOLLCMD)-1) static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE }; static bool trimbletaip_init (struct parseunit *); static void trimbletaip_event (struct parseunit *, int); /* query time & UTC correction data */ static char tsipquery[] = { DLE, 0x21, DLE, ETX, DLE, 0x2F, DLE, ETX }; static poll_info_t trimbletsip_pollinfo = { TRIM_POLLRATE, tsipquery, sizeof(tsipquery) }; static bool trimbletsip_init (struct parseunit *); static void trimbletsip_end (struct parseunit *); static void trimbletsip_message (struct parseunit *, parsetime_t *); static void trimbletsip_event (struct parseunit *, int); #define TRIMBLETSIP_IDLE_TIME (300) /* 5 minutes silence at most */ #define TRIMBLE_RESET_HOLDOFF TRIMBLETSIP_IDLE_TIME #define TRIMBLETAIP_SPEED (B4800) #define TRIMBLETAIP_CFLAG (CS8|CREAD|CLOCAL) #define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON) #define TRIMBLETAIP_OFLAG (OPOST|ONLCR) #define TRIMBLETAIP_LFLAG (0) #define TRIMBLETSIP_SPEED (B9600) #define TRIMBLETSIP_CFLAG (CS8|CLOCAL|CREAD|PARENB|PARODD) #define TRIMBLETSIP_IFLAG (IGNBRK) #define TRIMBLETSIP_OFLAG (0) #define TRIMBLETSIP_LFLAG (ICANON) #define TRIMBLETSIP_SAMPLES 5 #define TRIMBLETSIP_KEEP 3 #define TRIMBLETAIP_SAMPLES 5 #define TRIMBLETAIP_KEEP 3 #define TRIMBLETAIP_FLAGS (PARSE_F_PPSONSECOND) #define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS) #define TRIMBLETAIP_POLL poll_dpoll #define TRIMBLETSIP_POLL poll_dpoll #define TRIMBLETAIP_INIT trimbletaip_init #define TRIMBLETSIP_INIT trimbletsip_init #define TRIMBLETAIP_EVENT trimbletaip_event #define TRIMBLETSIP_EVENT trimbletsip_event #define TRIMBLETSIP_MESSAGE trimbletsip_message #define TRIMBLETAIP_END 0 #define TRIMBLETSIP_END trimbletsip_end #define TRIMBLETAIP_DATA ((void *)(&trimbletaip_pollinfo)) #define TRIMBLETSIP_DATA ((void *)(&trimbletsip_pollinfo)) #define TRIMBLETAIP_ID GPS_ID #define TRIMBLETSIP_ID GPS_ID #define TRIMBLETAIP_FORMAT "Trimble TAIP" #define TRIMBLETSIP_FORMAT "Trimble TSIP" #define TRIMBLETAIP_ROOTDELAY 0x0 #define TRIMBLETSIP_ROOTDELAY 0x0 #define TRIMBLETAIP_BASEDELAY 0.0 #define TRIMBLETSIP_BASEDELAY 0.020 /* GPS time message latency */ #define TRIMBLETAIP_NAME "GPS_TAIP" #define TRIMBLETSIP_NAME "GPS_TSIP" #define TRIMBLETAIP_DESCRIPTION "Trimble GPS (TAIP) receiver" #define TRIMBLETSIP_DESCRIPTION "Trimble GPS (TSIP) receiver" #define TRIMBLETAIP_MAXUNSYNC 0 #define TRIMBLETSIP_MAXUNSYNC 0 #define TRIMBLETAIP_EOL '<' /* * RadioCode Clocks RCC 800 receiver */ #define RCC_POLLRATE 0 /* only true direct polling */ #define RCC_POLLCMD "\r" #define RCC_CMDSIZE 1 static poll_info_t rcc8000_pollinfo = { RCC_POLLRATE, RCC_POLLCMD, RCC_CMDSIZE }; #define RCC8000_POLL poll_dpoll #define RCC8000_INIT poll_init #define RCC8000_END 0 #define RCC8000_DATA ((void *)(&rcc8000_pollinfo)) #define RCC8000_ROOTDELAY 0.0 #define RCC8000_BASEDELAY 0.0 #define RCC8000_ID MSF_ID #define RCC8000_NAME "MSF_RCC8000" #define RCC8000_DESCRIPTION "RCC 8000 MSF Receiver" #define RCC8000_FORMAT "Radiocode RCC8000" #define RCC8000_MAXUNSYNC (60*60) /* should be ok for an hour */ #define RCC8000_SPEED (B2400) #define RCC8000_CFLAG (CS8|CREAD|CLOCAL) #define RCC8000_IFLAG (IGNBRK|IGNPAR) #define RCC8000_OFLAG 0 #define RCC8000_LFLAG 0 #define RCC8000_SAMPLES 5 #define RCC8000_KEEP 3 /* * Hopf Radio clock 6021 Format * */ #define HOPF6021_ROOTDELAY 0.0 #define HOPF6021_BASEDELAY 0.0 #define HOPF6021_NAME "HOPF_6021" #define HOPF6021_DESCRIPTION "HOPF 6021" #define HOPF6021_FORMAT "hopf Funkuhr 6021" #define HOPF6021_MAXUNSYNC (60*60) /* should be ok for an hour */ #define HOPF6021_SPEED (B9600) #define HOPF6021_CFLAG (CS8|CREAD|CLOCAL) #define HOPF6021_IFLAG (IGNBRK|ISTRIP) #define HOPF6021_OFLAG 0 #define HOPF6021_LFLAG 0 #define HOPF6021_FLAGS 0 #define HOPF6021_SAMPLES 5 #define HOPF6021_KEEP 3 /* * Diem's Computime Radio Clock Receiver */ #define COMPUTIME_FLAGS 0 #define COMPUTIME_ROOTDELAY 0.0 #define COMPUTIME_BASEDELAY 0.0 #define COMPUTIME_ID DCF_ID #define COMPUTIME_NAME "COMPUTIME" #define COMPUTIME_DESCRIPTION "Diem's Computime receiver" #define COMPUTIME_FORMAT "Diem's Computime Radio Clock" #define COMPUTIME_TYPE DCF_TYPE #define COMPUTIME_MAXUNSYNC (60*60) /* only trust clock for 1 hour */ #define COMPUTIME_SPEED (B9600) #define COMPUTIME_CFLAG (CSTOPB|CS7|CREAD|CLOCAL) #define COMPUTIME_IFLAG (IGNBRK|IGNPAR|ISTRIP) #define COMPUTIME_OFLAG 0 #define COMPUTIME_LFLAG 0 #define COMPUTIME_SAMPLES 5 #define COMPUTIME_KEEP 3 /* * Varitext Radio Clock Receiver */ #define VARITEXT_FLAGS 0 #define VARITEXT_ROOTDELAY 0.0 #define VARITEXT_BASEDELAY 0.0 #define VARITEXT_ID MSF_ID #define VARITEXT_NAME "VARITEXT" #define VARITEXT_DESCRIPTION "Varitext receiver" #define VARITEXT_FORMAT "Varitext Radio Clock" #define VARITEXT_TYPE DCF_TYPE #define VARITEXT_MAXUNSYNC (60*60) /* only trust clock for 1 hour */ #define VARITEXT_SPEED (B9600) #define VARITEXT_CFLAG (CS7|CREAD|CLOCAL|PARENB|PARODD) #define VARITEXT_IFLAG (IGNPAR|IGNBRK|INPCK) /*|ISTRIP)*/ #define VARITEXT_OFLAG 0 #define VARITEXT_LFLAG 0 #define VARITEXT_SAMPLES 32 #define VARITEXT_KEEP 20 /* * SEL240x Satellite Sychronized Clock */ #define SEL240X_POLLRATE 0 /* only true direct polling */ #define SEL240X_POLLCMD "BUB8" #define SEL240X_CMDSIZE 4 static poll_info_t sel240x_pollinfo = { SEL240X_POLLRATE, SEL240X_POLLCMD, SEL240X_CMDSIZE }; #define SEL240X_FLAGS (PARSE_F_PPSONSECOND) #define SEL240X_POLL poll_dpoll #define SEL240X_INIT poll_init #define SEL240X_END 0 #define SEL240X_DATA ((void *)(&sel240x_pollinfo)) #define SEL240X_ROOTDELAY 0.0 #define SEL240X_BASEDELAY 0.0 #define SEL240X_ID GPS_ID #define SEL240X_NAME "SEL240X" #define SEL240X_DESCRIPTION "SEL240x Satellite Synchronized Clock" #define SEL240X_FORMAT "SEL B8" #define SEL240X_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours */ #define SEL240X_SPEED (B9600) #define SEL240X_CFLAG (CS8|CREAD|CLOCAL) #define SEL240X_IFLAG (IGNBRK|IGNPAR) #define SEL240X_OFLAG (0) #define SEL240X_LFLAG (0) #define SEL240X_SAMPLES 5 #define SEL240X_KEEP 3 static struct parse_clockinfo { unsigned long cl_flags; /* operation flags (PPS interpretation, trust handling) */ void (*cl_poll) (struct parseunit *); /* active poll routine */ bool (*cl_init) (struct parseunit *); /* active poll init routine */ void (*cl_event) (struct parseunit *, int); /* special event handling (e.g. reset clock) */ void (*cl_end) (struct parseunit *); /* active poll end routine */ void (*cl_message) (struct parseunit *, parsetime_t *); /* process a lower layer message */ void *cl_data; /* local data area for "poll" mechanism */ double cl_rootdelay; /* rootdelay */ double cl_basedelay; /* current offset by which the RS232 time code is delayed from the actual time */ const char *cl_id; /* ID code */ const char *cl_name; /* device name (tag for logging) */ const char *cl_description; /* device description */ const char *cl_format; /* fixed format */ uint8_t cl_type; /* clock type (ntp control) */ unsigned long cl_maxunsync; /* time to trust oscillator after losing synch */ unsigned long cl_speed; /* terminal input & output baudrate */ unsigned long cl_cflag; /* terminal control flags */ unsigned long cl_iflag; /* terminal input flags */ unsigned long cl_oflag; /* terminal output flags */ unsigned long cl_lflag; /* terminal local flags */ unsigned long cl_samples; /* samples for median filter */ unsigned long cl_keep; /* samples for median filter to keep */ } parse_clockinfo[] = { { /* subtype 0 */ MBG_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, DCFPZF535_ROOTDELAY, DCFPZF535_BASEDELAY, DCF_P_ID, DCFPZF535_NAME, DCFPZF535_DESCRIPTION, DCFPZF535_FORMAT, DCF_TYPE, DCFPZF535_MAXUNSYNC, DCFPZF535_SPEED, DCFPZF535_CFLAG, DCFPZF535_IFLAG, DCFPZF535_OFLAG, DCFPZF535_LFLAG, DCFPZF535_SAMPLES, DCFPZF535_KEEP }, { /* subtype 1 */ MBG_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, DCFPZF535OCXO_ROOTDELAY, DCFPZF535OCXO_BASEDELAY, DCF_P_ID, DCFPZF535OCXO_NAME, DCFPZF535OCXO_DESCRIPTION, DCFPZF535OCXO_FORMAT, DCF_TYPE, DCFPZF535OCXO_MAXUNSYNC, DCFPZF535OCXO_SPEED, DCFPZF535OCXO_CFLAG, DCFPZF535OCXO_IFLAG, DCFPZF535OCXO_OFLAG, DCFPZF535OCXO_LFLAG, DCFPZF535OCXO_SAMPLES, DCFPZF535OCXO_KEEP }, { /* subtype 2 */ MBG_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, DCFUA31_ROOTDELAY, DCFUA31_BASEDELAY, DCF_A_ID, DCFUA31_NAME, DCFUA31_DESCRIPTION, DCFUA31_FORMAT, DCF_TYPE, DCFUA31_MAXUNSYNC, DCFUA31_SPEED, DCFUA31_CFLAG, DCFUA31_IFLAG, DCFUA31_OFLAG, DCFUA31_LFLAG, DCFUA31_SAMPLES, DCFUA31_KEEP }, { /* subtype 3 */ MBG_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, DCF7000_ROOTDELAY, DCF7000_BASEDELAY, DCF_A_ID, DCF7000_NAME, DCF7000_DESCRIPTION, DCF7000_FORMAT, DCF_TYPE, DCF7000_MAXUNSYNC, DCF7000_SPEED, DCF7000_CFLAG, DCF7000_IFLAG, DCF7000_OFLAG, DCF7000_LFLAG, DCF7000_SAMPLES, DCF7000_KEEP }, { /* subtype 4 */ NO_CL_FLAGS, WSDCF_POLL, WSDCF_INIT, NO_EVENT, WSDCF_END, NO_MESSAGE, WSDCF_DATA, WSDCF_ROOTDELAY, WSDCF_BASEDELAY, DCF_A_ID, WSDCF_NAME, WSDCF_DESCRIPTION, WSDCF_FORMAT, DCF_TYPE, WSDCF_MAXUNSYNC, WSDCF_SPEED, WSDCF_CFLAG, WSDCF_IFLAG, WSDCF_OFLAG, WSDCF_LFLAG, WSDCF_SAMPLES, WSDCF_KEEP }, { /* subtype 5 */ RAWDCF_FLAGS, NO_POLL, RAWDCF_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, CONRAD_BASEDELAY, DCF_A_ID, CONRAD_NAME, CONRAD_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, RAWDCF_SPEED, RAWDCF_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 6 */ RAWDCF_FLAGS, NO_POLL, RAWDCF_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, TIMEBRICK_BASEDELAY, DCF_A_ID, TIMEBRICK_NAME, TIMEBRICK_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, RAWDCF_SPEED, RAWDCF_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 7 */ MBG_FLAGS, GPS16X_POLL, GPS16X_INIT, NO_EVENT, GPS16X_END, GPS16X_MESSAGE, GPS16X_DATA, GPS16X_ROOTDELAY, GPS16X_BASEDELAY, GPS16X_ID, GPS16X_NAME, GPS16X_DESCRIPTION, GPS16X_FORMAT, GPS_TYPE, GPS16X_MAXUNSYNC, GPS16X_SPEED, GPS16X_CFLAG, GPS16X_IFLAG, GPS16X_OFLAG, GPS16X_LFLAG, GPS16X_SAMPLES, GPS16X_KEEP }, { /* subtype 8 */ RAWDCF_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, IGELCLOCK_BASEDELAY, DCF_A_ID, IGELCLOCK_NAME, IGELCLOCK_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, IGELCLOCK_SPEED, IGELCLOCK_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 9 */ TRIMBLETAIP_FLAGS, #if TRIM_POLLRATE /* DHD940515: Allow user config */ NO_POLL, #else TRIMBLETAIP_POLL, #endif TRIMBLETAIP_INIT, TRIMBLETAIP_EVENT, TRIMBLETAIP_END, NO_MESSAGE, TRIMBLETAIP_DATA, TRIMBLETAIP_ROOTDELAY, TRIMBLETAIP_BASEDELAY, TRIMBLETAIP_ID, TRIMBLETAIP_NAME, TRIMBLETAIP_DESCRIPTION, TRIMBLETAIP_FORMAT, GPS_TYPE, TRIMBLETAIP_MAXUNSYNC, TRIMBLETAIP_SPEED, TRIMBLETAIP_CFLAG, TRIMBLETAIP_IFLAG, TRIMBLETAIP_OFLAG, TRIMBLETAIP_LFLAG, TRIMBLETAIP_SAMPLES, TRIMBLETAIP_KEEP }, { /* subtype 10 */ TRIMBLETSIP_FLAGS, #if TRIM_POLLRATE /* DHD940515: Allow user config */ NO_POLL, #else TRIMBLETSIP_POLL, #endif TRIMBLETSIP_INIT, TRIMBLETSIP_EVENT, TRIMBLETSIP_END, TRIMBLETSIP_MESSAGE, TRIMBLETSIP_DATA, TRIMBLETSIP_ROOTDELAY, TRIMBLETSIP_BASEDELAY, TRIMBLETSIP_ID, TRIMBLETSIP_NAME, TRIMBLETSIP_DESCRIPTION, TRIMBLETSIP_FORMAT, GPS_TYPE, TRIMBLETSIP_MAXUNSYNC, TRIMBLETSIP_SPEED, TRIMBLETSIP_CFLAG, TRIMBLETSIP_IFLAG, TRIMBLETSIP_OFLAG, TRIMBLETSIP_LFLAG, TRIMBLETSIP_SAMPLES, TRIMBLETSIP_KEEP }, { /* subtype 11 */ NO_CL_FLAGS, RCC8000_POLL, RCC8000_INIT, NO_EVENT, RCC8000_END, NO_MESSAGE, RCC8000_DATA, RCC8000_ROOTDELAY, RCC8000_BASEDELAY, RCC8000_ID, RCC8000_NAME, RCC8000_DESCRIPTION, RCC8000_FORMAT, DCF_TYPE, RCC8000_MAXUNSYNC, RCC8000_SPEED, RCC8000_CFLAG, RCC8000_IFLAG, RCC8000_OFLAG, RCC8000_LFLAG, RCC8000_SAMPLES, RCC8000_KEEP }, { /* subtype 12 */ HOPF6021_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, HOPF6021_ROOTDELAY, HOPF6021_BASEDELAY, DCF_ID, HOPF6021_NAME, HOPF6021_DESCRIPTION, HOPF6021_FORMAT, DCF_TYPE, HOPF6021_MAXUNSYNC, HOPF6021_SPEED, HOPF6021_CFLAG, HOPF6021_IFLAG, HOPF6021_OFLAG, HOPF6021_LFLAG, HOPF6021_SAMPLES, HOPF6021_KEEP }, { /* subtype 13 */ COMPUTIME_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, COMPUTIME_ROOTDELAY, COMPUTIME_BASEDELAY, COMPUTIME_ID, COMPUTIME_NAME, COMPUTIME_DESCRIPTION, COMPUTIME_FORMAT, COMPUTIME_TYPE, COMPUTIME_MAXUNSYNC, COMPUTIME_SPEED, COMPUTIME_CFLAG, COMPUTIME_IFLAG, COMPUTIME_OFLAG, COMPUTIME_LFLAG, COMPUTIME_SAMPLES, COMPUTIME_KEEP }, { /* subtype 14 */ RAWDCF_FLAGS, NO_POLL, RAWDCFDTRSET_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, RAWDCF_BASEDELAY, DCF_A_ID, RAWDCFDTRSET_NAME, RAWDCFDTRSET_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, RAWDCF_SPEED, RAWDCF_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 15 */ 0, /* operation flags (io modes) */ NO_POLL, /* active poll routine */ NO_INIT, /* active poll init routine */ NO_EVENT, /* special event handling (e.g. reset clock) */ NO_END, /* active poll end routine */ NO_MESSAGE, /* process a lower layer message */ NO_LCLDATA, /* local data area for "poll" mechanism */ 0, /* rootdelay */ 11.0 /* bits */ / 9600, /* current offset by which the RS232 time code is delayed from the actual time */ DCF_ID, /* ID code */ "WHARTON400A", "WHARTON 400A Series clock", /* device description */ "WHARTON 400A Series clock Output Format 1", /* fixed format */ /* Must match a format-name in a libparse/clk_xxx.c file */ DCF_TYPE, /* clock type (ntp control) */ (1*60*60), /* time to trust oscillator after losing synch */ B9600, /* terminal input & output baudrate */ (CS8|CREAD|PARENB|CLOCAL|HUPCL),/* terminal control flags */ 0, /* terminal input flags */ 0, /* terminal output flags */ 0, /* terminal local flags */ 5, /* samples for median filter */ 3, /* samples for median filter to keep */ }, { /* subtype 16 - RAWDCF RTS set, DTR clr */ RAWDCF_FLAGS, NO_POLL, RAWDCFDTRCLRRTSSET_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, RAWDCF_BASEDELAY, DCF_A_ID, RAWDCFDTRCLRRTSSET_NAME, RAWDCFDTRCLRRTSSET_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, RAWDCF_SPEED, RAWDCF_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 17 */ VARITEXT_FLAGS, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, VARITEXT_ROOTDELAY, VARITEXT_BASEDELAY, VARITEXT_ID, VARITEXT_NAME, VARITEXT_DESCRIPTION, VARITEXT_FORMAT, VARITEXT_TYPE, VARITEXT_MAXUNSYNC, VARITEXT_SPEED, VARITEXT_CFLAG, VARITEXT_IFLAG, VARITEXT_OFLAG, VARITEXT_LFLAG, VARITEXT_SAMPLES, VARITEXT_KEEP }, { /* subtype 18 */ MBG_FLAGS, NO_POLL, NO_INIT, NO_EVENT, GPS16X_END, GPS16X_MESSAGE, GPS16X_DATA, GPS16X_ROOTDELAY, GPS16X_BASEDELAY, GPS16X_ID, GPS16X_NAME, GPS16X_DESCRIPTION, GPS16X_FORMAT, GPS_TYPE, GPS16X_MAXUNSYNC, GPS16X_SPEED, GPS16X_CFLAG, GPS16X_IFLAG, GPS16X_OFLAG, GPS16X_LFLAG, GPS16X_SAMPLES, GPS16X_KEEP }, { /* subtype 19 */ RAWDCF_FLAGS, NO_POLL, RAWDCF_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, GUDE_EMC_USB_V20_BASEDELAY, DCF_A_ID, GUDE_EMC_USB_V20_NAME, GUDE_EMC_USB_V20_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, GUDE_EMC_USB_V20_SPEED, RAWDCF_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 20, like subtype 14 but driven by 75 baud */ RAWDCF_FLAGS, NO_POLL, RAWDCFDTRSET_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, RAWDCF_BASEDELAY, DCF_A_ID, RAWDCFDTRSET_NAME, RAWDCFDTRSET75_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, B75, RAWDCF_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 21, like subtype 16 but driven by 75 baud - RAWDCF RTS set, DTR clr */ RAWDCF_FLAGS, NO_POLL, RAWDCFDTRCLRRTSSET_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, RAWDCF_ROOTDELAY, RAWDCF_BASEDELAY, DCF_A_ID, RAWDCFDTRCLRRTSSET_NAME, RAWDCFDTRCLRRTSSET75_DESCRIPTION, RAWDCF_FORMAT, DCF_TYPE, RAWDCF_MAXUNSYNC, B75, RAWDCF_CFLAG, RAWDCF_IFLAG, RAWDCF_OFLAG, RAWDCF_LFLAG, RAWDCF_SAMPLES, RAWDCF_KEEP }, { /* subtype 22 - like 2 with POWERUP trust */ MBG_FLAGS | PARSE_F_POWERUPTRUST, NO_POLL, NO_INIT, NO_EVENT, NO_END, NO_MESSAGE, NO_LCLDATA, DCFUA31_ROOTDELAY, DCFUA31_BASEDELAY, DCF_A_ID, DCFUA31_NAME, DCFUA31_DESCRIPTION, DCFUA31_FORMAT, DCF_TYPE, DCFUA31_MAXUNSYNC, DCFUA31_SPEED, DCFUA31_CFLAG, DCFUA31_IFLAG, DCFUA31_OFLAG, DCFUA31_LFLAG, DCFUA31_SAMPLES, DCFUA31_KEEP }, { /* subtype 23 - like 7 with POWERUP trust */ MBG_FLAGS | PARSE_F_POWERUPTRUST, GPS16X_POLL, GPS16X_INIT, NO_EVENT, GPS16X_END, GPS16X_MESSAGE, GPS16X_DATA, GPS16X_ROOTDELAY, GPS16X_BASEDELAY, GPS16X_ID, GPS16X_NAME, GPS16X_DESCRIPTION, GPS16X_FORMAT, GPS_TYPE, GPS16X_MAXUNSYNC, GPS16X_SPEED, GPS16X_CFLAG, GPS16X_IFLAG, GPS16X_OFLAG, GPS16X_LFLAG, GPS16X_SAMPLES, GPS16X_KEEP }, { /* subtype 24 */ SEL240X_FLAGS, SEL240X_POLL, SEL240X_INIT, NO_EVENT, SEL240X_END, NO_MESSAGE, SEL240X_DATA, SEL240X_ROOTDELAY, SEL240X_BASEDELAY, SEL240X_ID, SEL240X_NAME, SEL240X_DESCRIPTION, SEL240X_FORMAT, GPS_TYPE, SEL240X_MAXUNSYNC, SEL240X_SPEED, SEL240X_CFLAG, SEL240X_IFLAG, SEL240X_OFLAG, SEL240X_LFLAG, SEL240X_SAMPLES, SEL240X_KEEP }, }; static int ncltypes = sizeof(parse_clockinfo) / sizeof(struct parse_clockinfo); #define CLK_REALTYPE(x) ((int)(((x)->cfg.mode) & 0x7F)) /* carefull, CLK_TYPE() in refclock_trimle.c is different */ #define CLK_TYPE(x) ((CLK_REALTYPE(x) >= ncltypes) ? ~0 : CLK_REALTYPE(x)) #define CLK_PPS(x) (((x)->cfg.mode) & 0x80) /* * Other constant stuff */ #define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */ #define PARSESTATISTICS (60*60) /* output state statistics every hour */ static int notice = 0; #define PARSE_STATETIME(parse, i) ((parse->generic->currentstatus == i) ? parse->statetime[i] + current_time - parse->lastchange : parse->statetime[i]) static void parse_event (struct parseunit *, int); static void parse_process (struct parseunit *, parsetime_t *); static void clear_err (struct parseunit *, unsigned long); static int list_err (struct parseunit *, unsigned long); static char * l_mktime (unsigned long); /**=========================================================================== ** implementation error message regression module **/ static void clear_err( struct parseunit *parse, unsigned long lstate ) { if (lstate == ERR_ALL) { size_t i; for (i = 0; i < ERR_CNT; i++) { parse->errors[i].err_stage = err_tbl[i]; parse->errors[i].err_cnt = 0; parse->errors[i].err_last = 0; parse->errors[i].err_started = 0; parse->errors[i].err_suppressed = 0; } } else { parse->errors[lstate].err_stage = err_tbl[lstate]; parse->errors[lstate].err_cnt = 0; parse->errors[lstate].err_last = 0; parse->errors[lstate].err_started = 0; parse->errors[lstate].err_suppressed = 0; } } static int list_err( struct parseunit *parse, unsigned long lstate ) { int do_it; struct errorinfo *err = &parse->errors[lstate]; if (err->err_started == 0) { err->err_started = current_time; } do_it = (current_time - err->err_last) >= err->err_stage->err_delay; if (do_it) err->err_cnt++; if (err->err_stage->err_count && (err->err_cnt >= err->err_stage->err_count)) { err->err_stage++; err->err_cnt = 0; } if (!err->err_cnt && do_it) msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: interval for following error message class is at least %s", parse->peer->procptr->refclkunit, l_mktime(err->err_stage->err_delay)); if (!do_it) err->err_suppressed++; else err->err_last = current_time; if (do_it && err->err_suppressed) { msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: %lu message%s suppressed, error " "condition class persists for %s", parse->peer->procptr->refclkunit, err->err_suppressed, (err->err_suppressed == 1) ? " was" : "s where", l_mktime(current_time - err->err_started)); err->err_suppressed = 0; } return do_it; } /*-------------------------------------------------- * mkreadable - make a printable ascii string (without * embedded quotes so that the ntpq protocol isn't * fooled */ static char * mkreadable( char *buffer, long blen, const char *src, unsigned long srclen, int hex ) { static const char ellipsis[] = "..."; char *b = buffer; char *endb = NULL; if (blen < 4) return NULL; /* don't bother with mini buffers */ endb = buffer + blen - sizeof(ellipsis); blen--; /* account for '\0' */ while (blen && srclen--) { if (!hex && /* no binary only */ (*src != '\\') && /* no plain \ */ (*src != '"') && /* no " */ isprint((unsigned char)*src)) /* only printables */ { /* they are easy... */ *buffer++ = *src++; blen--; } else { if (blen < 4) { while (blen--) { *buffer++ = '.'; } *buffer = '\0'; return b; } else { if (*src == '\\') { memcpy(buffer, "\\\\", 2); buffer += 2; blen -= 2; src++; } else { snprintf(buffer, (size_t)blen, "\\x%02x", (unsigned)(*src++)); blen -= 4; buffer += 4; } } } if (srclen && !blen && endb) /* overflow - set last chars to ... */ memcpy(endb, ellipsis, sizeof(ellipsis)); } *buffer = '\0'; return b; } /*-------------------------------------------------- * mkascii - make a printable ascii string * assumes (unless defined better) 7-bit ASCII */ static char * mkascii( char *buffer, long blen, const char *src, unsigned long srclen ) { return mkreadable(buffer, blen, src, srclen, 0); } /**=========================================================================== ** implementation of i/o handling methods ** (all STREAM, partial STREAM, user level) **/ static int local_init (struct parseunit *); static void local_end (struct parseunit *); static int local_nop (struct parseunit *); static bool local_setcs (struct parseunit *, parsectl_t *); static int local_getfmt (struct parseunit *, parsectl_t *); static int local_setfmt (struct parseunit *, parsectl_t *); static int local_timecode (struct parseunit *, parsectl_t *); static void local_receive (struct recvbuf *); static int local_input (struct recvbuf *); static bind_t io_bindings[] = { { "normal", local_init, local_end, local_setcs, local_nop, local_nop, local_getfmt, local_setfmt, local_timecode, local_receive, local_input, }, { (char *)0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, } }; /*-------------------------------------------------- * local init */ static int local_init( struct parseunit *parse ) { return parse_ioinit(&parse->parseio); } /*-------------------------------------------------- * local end */ static void local_end( struct parseunit *parse ) { parse_ioend(&parse->parseio); } /*-------------------------------------------------- * local nop */ static int local_nop( struct parseunit *parse ) { UNUSED_ARG(parse); return true; } /*-------------------------------------------------- * local setcs */ static bool local_setcs( struct parseunit *parse, parsectl_t *tcl ) { return parse_setcs(tcl, &parse->parseio); } /*-------------------------------------------------- * local getfmt */ static int local_getfmt( struct parseunit *parse, parsectl_t *tcl ) { return parse_getfmt(tcl, &parse->parseio); } /*-------------------------------------------------- * local setfmt */ static int local_setfmt( struct parseunit *parse, parsectl_t *tcl ) { return parse_setfmt(tcl, &parse->parseio); } /*-------------------------------------------------- * local timecode */ static int local_timecode( struct parseunit *parse, parsectl_t *tcl ) { return parse_timecode(tcl, &parse->parseio); } /*-------------------------------------------------- * local input */ static int local_input( struct recvbuf *rbufp ) { struct parseunit * parse; int count; unsigned char *s; timestamp_t ts; parse = (struct parseunit *)rbufp->recv_peer->procptr->unitptr; if (!parse->peer) return false; /* * eat all characters, parsing then and feeding complete samples */ count = (int)rbufp->recv_length; s = (unsigned char *)rbufp->recv_buffer; ts = rbufp->recv_time; while (count--) { if (parse_ioread(&parse->parseio, (char)(*s++), &ts)) { struct recvbuf *buf; /* * got something good to eat */ if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state)) { #ifdef HAVE_PPSAPI if (parse->flags & PARSE_PPSAPI) { struct timespec pps_timeout; pps_info_t pps_info; pps_timeout.tv_sec = 0; pps_timeout.tv_nsec = 0; if (time_pps_fetch(parse->ppsctl.handle, PPS_TSFMT_TSPEC, &pps_info, &pps_timeout) == 0) { if (pps_info.assert_sequence + pps_info.clear_sequence != parse->ppsserial) { double dtemp; struct timespec pts; /* * add PPS time stamp if available via ppsclock module * and not supplied already. */ if (parse->flags & PARSE_CLEAR) pts = pps_info.clear_timestamp; else pts = pps_info.assert_timestamp; setlfpuint(parse->parseio.parse_dtime.parse_ptime, (uint64_t)pts.tv_sec + JAN_1970); dtemp = (double) pts.tv_nsec * S_PER_NS; if (dtemp < 0.) { dtemp += 1; bumplfpuint(parse->parseio.parse_dtime.parse_ptime, -1); } if (dtemp > 1.) { dtemp -= 1; bumplfpuint(parse->parseio.parse_dtime.parse_ptime, 1); } setlfpfrac(parse->parseio.parse_dtime.parse_ptime, (uint32_t)(dtemp * FRAC)); parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; DPRINT(4, ("parse: local_receive: fd %d PPSAPI seq %ld - PPS %s\n", rbufp->fd, (long)pps_info.assert_sequence + (long)pps_info.clear_sequence, lfptoa(parse->parseio.parse_dtime.parse_ptime, 6))); } #ifdef DEBUG else { if (debug > 3) /* SPECIAL DEBUG */ { printf( "parse: local_receive: fd %d PPSAPI seq assert %ld, seq clear %ld - NO PPS event\n", rbufp->fd, (long)pps_info.assert_sequence, (long)pps_info.clear_sequence); } } #endif parse->ppsserial = pps_info.assert_sequence + pps_info.clear_sequence; } #ifdef DEBUG else { if (debug > 3) /* SPECIAL DEBUG */ { printf( "parse: local_receive: fd %d PPSAPI time_pps_fetch errno = %d\n", rbufp->fd, errno); } } #endif } #endif /* !HAVE_PPSAPI */ } if (count) { /* simulate receive */ buf = get_free_recv_buffer(); if (buf != NULL) { memmove((void *)buf->recv_buffer, (void *)&parse->parseio.parse_dtime, sizeof(parsetime_t)); buf->recv_length = sizeof(parsetime_t); buf->recv_time = rbufp->recv_time; buf->srcadr = rbufp->srcadr; buf->dstadr = rbufp->dstadr; buf->receiver = rbufp->receiver; buf->fd = rbufp->fd; buf->X_from_where = rbufp->X_from_where; parse->generic->io.recvcount++; packets_received++; add_full_recv_buffer(buf); } parse_iodone(&parse->parseio); } else { memmove((void *)rbufp->recv_buffer, (void *)&parse->parseio.parse_dtime, sizeof(parsetime_t)); parse_iodone(&parse->parseio); rbufp->recv_length = sizeof(parsetime_t); return true; /* got something & in place return */ } } } return false; /* nothing to pass up */ } /*-------------------------------------------------- * local receive */ static void local_receive( struct recvbuf *rbufp ) { struct parseunit * parse; parsetime_t parsetime; parse = (struct parseunit *)rbufp->recv_peer->procptr->unitptr; if (!parse->peer) return; if (rbufp->recv_length != sizeof(parsetime_t)) { ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: local_receive: bad size " " (got %zu expected %zu)", parse->peer->procptr->refclkunit, rbufp->recv_length, sizeof(parsetime_t)); parse_event(parse, CEVNT_BADREPLY); return; } clear_err(parse, ERR_BADIO); memmove((void *)&parsetime, (void *)rbufp->recv_buffer, sizeof(parsetime_t)); DPRINT(4, ("PARSE receiver #%d: status %06x, state %08x, time(fp) %lx.%08lx, stime(fp) %lx.%08lx, ptime(fp) %lx.%08lx\n", parse->peer->procptr->refclkunit, (unsigned int)parsetime.parse_status, (unsigned int)parsetime.parse_state, (unsigned long)lfpuint(parsetime.parse_time), (unsigned long)lfpfrac(parsetime.parse_time), (unsigned long)lfpuint(parsetime.parse_stime), (unsigned long)lfpfrac(parsetime.parse_stime), (unsigned long)lfpuint(parsetime.parse_ptime), (unsigned long)lfpfrac(parsetime.parse_ptime))); parse_process(parse, &parsetime); } /*-------------------------------------------------- * init_iobinding - find and initialize lower layers */ static bind_t * init_iobinding( struct parseunit *parse ) { bind_t *b = io_bindings; while (b->bd_description != (char *)0) { if ((*b->bd_init)(parse)) { return b; } b++; } return (bind_t *)0; } /**=========================================================================== ** support routines **/ static NTP_PRINTF(4, 5) char * ap(char *buffer, size_t len, char *pos, const char *fmt, ...) { va_list va; int l; size_t rem = len - (size_t)(pos - buffer); if (rem == 0) return pos; va_start(va, fmt); l = vsnprintf(pos, rem, fmt, va); va_end(va); if (l != -1) { rem--; if (rem >= (size_t)l) pos += l; else pos += rem; } return pos; } /*-------------------------------------------------- * convert a flag field to a string */ static char * parsestate( unsigned long lstate, char *buffer, int size ) { static struct bits { unsigned long bit; const char *name; } flagstrings[] = { { PARSEB_ANNOUNCE, "DST SWITCH WARNING" }, { PARSEB_POWERUP, "NOT SYNCHRONIZED" }, { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" }, { PARSEB_DST, "DST" }, { PARSEB_UTC, "UTC DISPLAY" }, { PARSEB_LEAPADD, "LEAP ADD WARNING" }, { PARSEB_LEAPDEL, "LEAP DELETE WARNING" }, { PARSEB_LEAPSECOND, "LEAP SECOND" }, { PARSEB_CALLBIT, "CALL BIT" }, { PARSEB_TIMECODE, "TIME CODE" }, { PARSEB_PPS, "PPS" }, { PARSEB_POSITION, "POSITION" }, { 0, NULL } }; static struct sbits { unsigned long bit; const char *name; } sflagstrings[] = { { PARSEB_S_LEAP, "LEAP INDICATION" }, { PARSEB_S_PPS, "PPS SIGNAL" }, { PARSEB_S_CALLBIT, "CALLBIT" }, { PARSEB_S_POSITION, "POSITION" }, { 0, NULL } }; int i; char *s, *t; *buffer = '\0'; s = t = buffer; i = 0; while (flagstrings[i].bit) { if (flagstrings[i].bit & lstate) { if (s != t) t = ap(buffer, (size_t)size, t, "; "); t = ap(buffer, (size_t)size, t, "%s", flagstrings[i].name); } i++; } if (lstate & (PARSEB_S_LEAP|PARSEB_S_CALLBIT|PARSEB_S_PPS|PARSEB_S_POSITION)) { if (s != t) t = ap(buffer, (size_t)size, t, "; "); t = ap(buffer, (size_t)size, t, "("); s = t; i = 0; while (sflagstrings[i].bit) { if (sflagstrings[i].bit & lstate) { if (t != s) { t = ap(buffer, (size_t)size, t, "; "); } t = ap(buffer, (size_t)size, t, "%s", sflagstrings[i].name); } i++; } ap(buffer, (size_t)size, t, ")"); } return buffer; } /*-------------------------------------------------- * convert a status flag field to a string */ static char * parsestatus( unsigned long lstate, char *buffer, int size ) { static struct bits { unsigned long bit; const char *name; } flagstrings[] = { { CVT_OK, "CONVERSION SUCCESSFUL" }, { CVT_NONE, "NO CONVERSION" }, { CVT_FAIL, "CONVERSION FAILED" }, { CVT_BADFMT, "ILLEGAL FORMAT" }, { CVT_BADDATE, "DATE ILLEGAL" }, { CVT_BADTIME, "TIME ILLEGAL" }, { CVT_ADDITIONAL, "ADDITIONAL DATA" }, { 0, NULL } }; int i; char *t; t = buffer; *buffer = '\0'; i = 0; while (flagstrings[i].bit) { if (flagstrings[i].bit & lstate) { if (t != buffer) t = ap(buffer, (size_t)size, t, "; "); t = ap(buffer, (size_t)size, t, "%s", flagstrings[i].name); } i++; } return buffer; } /*-------------------------------------------------- * convert a clock status flag field to a string */ static const char * clockstatus( unsigned long lstate ) { static char buffer[20]; static struct status { unsigned long value; const char *name; } flagstrings[] = { { CEVNT_NOMINAL, "NOMINAL" }, { CEVNT_TIMEOUT, "NO RESPONSE" }, { CEVNT_BADREPLY,"BAD FORMAT" }, { CEVNT_FAULT, "FAULT" }, { CEVNT_PROP, "PROPAGATION DELAY" }, { CEVNT_BADDATE, "ILLEGAL DATE" }, { CEVNT_BADTIME, "ILLEGAL TIME" }, { (unsigned)~0L, NULL } }; int i; i = 0; while (flagstrings[i].value != (unsigned int)~0) { if (flagstrings[i].value == lstate) { return flagstrings[i].name; } i++; } snprintf(buffer, sizeof(buffer), "unknown #%lu", lstate); return buffer; } /*-------------------------------------------------- * l_mktime - make representation of a relative time */ static char * l_mktime( unsigned long delta ) { unsigned long tmp, m, s; static char buffer[40]; char *t; buffer[0] = '\0'; t = buffer; if ((tmp = delta / (60*60*24)) != 0) { t = ap(buffer, sizeof(buffer), t, "%lud+", tmp); delta -= tmp * 60*60*24; } s = delta % 60; delta /= 60; m = delta % 60; delta /= 60; ap(buffer, sizeof(buffer), t, "%02d:%02d:%02d", (int)delta, (int)m, (int)s); return buffer; } /*-------------------------------------------------- * parse_statistics - list summary of clock states */ static void parse_statistics( struct parseunit *parse ) { int i; NLOG(NLOG_CLOCKSTATIST) /* conditional if clause for conditional syslog */ { msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: running time: %s", parse->peer->procptr->refclkunit, l_mktime(current_time - parse->generic->timestarted)); msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: current status: %s", parse->peer->procptr->refclkunit, clockstatus(parse->generic->currentstatus)); for (i = 0; i <= CEVNT_MAX; i++) { unsigned long s_time; unsigned long percent, d = current_time - parse->generic->timestarted; percent = s_time = PARSE_STATETIME(parse, i); while (((unsigned long)(~0) / 10000) < percent) { percent /= 10; d /= 10; } if (d) percent = (percent * 10000) / d; else percent = 10000; if (s_time) msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: state %18s: " "%13s (%3lu.%02lu%%)", parse->peer->procptr->refclkunit, clockstatus((unsigned int)i), l_mktime(s_time), percent / 100, percent % 100); } } } /*-------------------------------------------------- * cparse_statistics - wrapper for statistics call */ static void cparse_statistics( struct parseunit *parse ) { if (parse->laststatistic + PARSESTATISTICS < current_time) parse_statistics(parse); parse->laststatistic = current_time; } /**=========================================================================== ** ntp interface routines **/ /*-------------------------------------------------- * parse_shutdown - shut down a PARSE clock */ static void parse_shutdown( struct refclockproc *pp ) { struct parseunit *parse = NULL; if (pp) parse = pp->unitptr; if (!parse) { /* nothing to clean up */ return; } if (!parse->peer) { msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: INTERNAL ERROR - unit already inactive - `shutdown ignored", pp->refclkunit); return; } #ifdef HAVE_PPSAPI if (parse->flags & PARSE_PPSAPI) { (void)time_pps_destroy(parse->ppsctl.handle); } #endif if (parse->generic->io.fd != parse->ppsfd && parse->ppsfd != -1) (void)close(parse->ppsfd); /* close separate PPS source */ /* * print statistics a last time and * stop statistics machine */ parse_statistics(parse); if (parse->parse_type->cl_end) { parse->parse_type->cl_end(parse); } /* * cleanup before leaving this world */ if (parse->binding) PARSE_END(parse); /* * Tell the I/O module to turn us off. We're history. */ io_closeclock(&parse->generic->io); free_varlist(parse->kv); NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: reference clock \"%s\" removed", parse->peer->procptr->refclkunit, parse->parse_type->cl_description); parse->peer = (struct peer *)0; /* unused now */ pp->unitptr = (void *)0; free(parse); } #ifdef HAVE_PPSAPI /*---------------------------------------- * set up HARDPPS via PPSAPI */ static void parse_hardpps( struct parseunit *parse, int mode ) { if (parse->hardppsstate == mode) return; if (CLK_PPS(parse->peer) && (parse->flags & PARSE_PPSKERNEL)) { int i = 0; if (mode == PARSE_HARDPPS_ENABLE) { if (parse->flags & PARSE_CLEAR) i = PPS_CAPTURECLEAR; else i = PPS_CAPTUREASSERT; } if (time_pps_kcbind(parse->ppsctl.handle, PPS_KC_HARDPPS, i, PPS_TSFMT_TSPEC) < 0) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: time_pps_kcbind failed: %m", parse->peer->procptr->refclkunit); } else { NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: kernel PPS synchronisation %sabled", parse->peer->procptr->refclkunit, (mode == PARSE_HARDPPS_ENABLE) ? "en" : "dis"); /* * tell the rest, that we have a kernel PPS source, iff we ever enable HARDPPS */ if (mode == PARSE_HARDPPS_ENABLE) hardpps_enable = true; } } parse->hardppsstate = mode; } /*---------------------------------------- * set up PPS via PPSAPI */ static bool parse_ppsapi( struct parseunit *parse ) { int cap, mode_ppsoffset; const char *cp; parse->flags &= (uint8_t) (~PARSE_PPSAPI); /* * collect PPSAPI offset capability - should move into generic handling */ if (time_pps_getcap(parse->ppsctl.handle, &cap) < 0) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_ppsapi: time_pps_getcap failed: %m", parse->peer->procptr->refclkunit); return false; } /* * initialize generic PPSAPI interface * * we leave out CLK_FLAG3 as time_pps_kcbind() * is handled here for now. Ideally this should also * be part of the generic PPSAPI interface */ if (!refclock_params(parse->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG4), &parse->ppsctl)) return false; /* nb. only turn things on, if someone else has turned something * on before we get here, leave it alone! */ if (parse->flags & PARSE_CLEAR) { cp = "CLEAR"; mode_ppsoffset = PPS_OFFSETCLEAR; } else { cp = "ASSERT"; mode_ppsoffset = PPS_OFFSETASSERT; } msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: initializing PPS to %s", parse->peer->procptr->refclkunit, cp); if (!(mode_ppsoffset & cap)) { msyslog(LOG_WARNING, "REFCLOCK: PARSE receiver #%u: Cannot set PPS_%sCLEAR, " " this will increase jitter (PPS API capabilities=0x%x)", parse->peer->procptr->refclkunit, cp, (unsigned)cap); mode_ppsoffset = 0; } else { if (mode_ppsoffset == PPS_OFFSETCLEAR) { parse->ppsctl.pps_params.clear_offset.tv_sec = (time_t)(-parse->ppsphaseadjust); parse->ppsctl.pps_params.clear_offset.tv_nsec = (long)(-1e9*(parse->ppsphaseadjust - (double)(long)parse->ppsphaseadjust)); } if (mode_ppsoffset == PPS_OFFSETASSERT) { parse->ppsctl.pps_params.assert_offset.tv_sec = (time_t)(-parse->ppsphaseadjust); parse->ppsctl.pps_params.assert_offset.tv_nsec = (long)(-1e9*(parse->ppsphaseadjust - (double)(long)parse->ppsphaseadjust)); } } parse->ppsctl.pps_params.mode |= mode_ppsoffset; if (time_pps_setparams(parse->ppsctl.handle, &parse->ppsctl.pps_params) < 0) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: FAILED set PPS parameters: %m", parse->peer->procptr->refclkunit); return false; } parse->flags |= PARSE_PPSAPI; return true; } #else #define parse_hardpps(_PARSE_, _MODE_) /* empty */ #endif /*-------------------------------------------------- * parse_start - open the PARSE devices and initialize data for processing */ static bool parse_start( int sysunit, struct peer *peer ) { unsigned int unit; int fd232; struct termios tio; /* NEEDED FOR A LONG TIME ! */ struct parseunit * parse; char parsedev[sizeof(PARSEDEVICE)+20]; char parseppsdev[sizeof(PARSEPPSDEVICE)+20]; parsectl_t tmp_ctl; unsigned int type; char *path, *ppspath; UNUSED_ARG(sysunit); /* * get out Copyright information once */ if (!notice) { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_INFO, "REFCLOCK: NTP PARSE support: Copyright (c) 1989-2015, Frank Kardel"); notice = 1; } #ifdef ENABLE_CLASSIC_MODE peer->cfg.mode = (peer->procptr->refclkunit & ~0x80) >> 2; peer->procptr->refclkunit = peer->procptr->refclkunit & 0x03; #endif /* ENABLE_CLASSIC_MODE */ type = (unsigned int)CLK_TYPE(peer); unit = peer->procptr->refclkunit; if ((type == (unsigned int)~0) || (parse_clockinfo[type].cl_description == (char *)0)) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%u: parse_start: " "unsupported clock type %d (max %d)", unit, CLK_REALTYPE(peer), ncltypes-1); return false; } /* * Unit okay, attempt to open the device. */ if (peer->cfg.path) path = peer->cfg.path; else { (void) snprintf(parsedev, sizeof(parsedev), PARSEDEVICE, unit); path = parsedev; } if (peer->cfg.ppspath) ppspath = peer->cfg.ppspath; else { (void) snprintf(parseppsdev, sizeof(parsedev), PARSEPPSDEVICE, unit); ppspath = parseppsdev; } fd232 = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK, 0777); if (fd232 == -1) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%u: parse_start: open of %s failed: %m", unit, path); return false; } parse = emalloc_zero(sizeof(*parse)); parse->generic = peer->procptr; /* link up */ parse->generic->unitptr = (void *)parse; /* link down */ /* * Set up the structures */ parse->generic->timestarted = current_time; parse->lastchange = current_time; parse->flags = 0; parse->pollneeddata = 0; parse->laststatistic = current_time; parse->lastformat = (unsigned short)~0; /* assume no format known */ parse->timedata.parse_status = (unsigned short)~0; /* be sure to mark initial status change */ parse->lastmissed = 0; /* assume got everything */ parse->ppsserial = 0; parse->ppsfd = -1; parse->localdata = (void *)0; parse->localstate = 0; parse->kv = (struct ctl_var *)0; clear_err(parse, ERR_ALL); parse->parse_type = &parse_clockinfo[type]; parse->maxunsync = parse->parse_type->cl_maxunsync; parse->generic->fudgetime1 = parse->parse_type->cl_basedelay; parse->generic->fudgetime2 = 0.0; parse->ppsphaseadjust = parse->generic->fudgetime2; parse->generic->clockname = parse->parse_type->cl_name; parse->generic->clockdesc = parse->parse_type->cl_description; peer->rootdelay = parse->parse_type->cl_rootdelay; peer->sstclktype = parse->parse_type->cl_type; peer->precision = sys_precision; peer->stratum = STRATUM_REFCLOCK; if (peer->stratum <= 1) memmove((char *)&parse->generic->refid, parse->parse_type->cl_id, REFIDLEN); else parse->generic->refid = htonl(PARSEHSREFID); parse->generic->io.fd = fd232; parse->peer = peer; /* marks it also as busy */ /* * configure terminal line */ if (TTY_GETATTR(fd232, &tio) == -1) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%u: parse_start: tcgetattr(%d, &tio): %m", unit, fd232); /* let our cleaning staff do the work */ parse_shutdown(peer->procptr); return false; } else { #ifndef _PC_VDISABLE memset((char *)tio.c_cc, 0, sizeof(tio.c_cc)); #else int disablec; errno = 0; /* pathconf can deliver -1 without changing errno ! */ disablec = fpathconf(parse->generic->io.fd, _PC_VDISABLE); if (disablec == -1 && errno) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", parse->peer->procptr->refclkunit); memset((char *)tio.c_cc, 0, sizeof(tio.c_cc)); /* best guess */ } else if (disablec != -1) memset((char *)tio.c_cc, disablec, sizeof(tio.c_cc)); #endif if ((parse_clockinfo[type].cl_lflag & ICANON) == 0) { tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; } tio.c_cflag = (tcflag_t) parse_clockinfo[type].cl_cflag; tio.c_iflag = (tcflag_t) parse_clockinfo[type].cl_iflag; tio.c_oflag = (tcflag_t) parse_clockinfo[type].cl_oflag; tio.c_lflag = (tcflag_t) parse_clockinfo[type].cl_lflag; if ((cfsetospeed(&tio, (speed_t) parse_clockinfo[type].cl_speed) == -1) || (cfsetispeed(&tio, (speed_t) parse_clockinfo[type].cl_speed) == -1)) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%u: parse_start: " " tcset{i,o}speed(&tio, speed): %m", unit); /* let our cleaning staff do the work */ parse_shutdown(peer->procptr); return false; } /* * set up pps device * if the PARSEPPSDEVICE can be opened that will be used * for PPS else PARSEDEVICE will be used */ parse->ppsfd = open(ppspath, O_RDWR | O_NOCTTY | O_NONBLOCK, 0777); if (parse->ppsfd == -1) { parse->ppsfd = fd232; } /* * Linux PPS - the old way */ #if defined(HAVE_LINUX_SERIAL_H) /* Linux hack: define PPS interface */ { struct serial_struct ss; if (ioctl(parse->ppsfd, TIOCGSERIAL, &ss) < 0 || ( #ifdef ASYNC_LOW_LATENCY ss.flags |= (int)ASYNC_LOW_LATENCY, #endif #ifndef HAVE_PPSAPI #ifdef ASYNC_PPS_CD_NEG ss.flags |= (int)ASYNC_PPS_CD_NEG, #endif #endif ioctl(parse->ppsfd, TIOCSSERIAL, &ss)) < 0) { msyslog(LOG_NOTICE, "REFCLOCK: refclock_parse: TIOCSSERIAL fd %d, %m", parse->ppsfd); msyslog(LOG_NOTICE, "REFCLOCK: refclock_parse: optional PPS processing not available"); } else { parse->flags |= PARSE_PPSAPI; #ifdef ASYNC_PPS_CD_NEG NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "REFCLOCK: refclock_parse: PPS detection on"); #endif } } #endif /* * PPS via PPSAPI */ #if defined(HAVE_PPSAPI) parse->hardppsstate = PARSE_HARDPPS_DISABLE; if (CLK_PPS(parse->peer)) { if (!refclock_ppsapi(parse->ppsfd, &parse->ppsctl)) { msyslog(LOG_NOTICE, "REFCLOCK: PARSE receiver #%d: parse_start: could not set up PPS: %m", parse->peer->procptr->refclkunit); } else { parse_ppsapi(parse); } } #endif if (TTY_SETATTR(fd232, &tio) == -1) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%u: parse_start: tcsetattr(%d, &tio): %m", unit, fd232); /* let our cleaning staff do the work */ parse_shutdown(peer->procptr); return false; } } /* * pick correct input machine */ parse->generic->io.srcclock = peer; parse->generic->io.datalen = 0; parse->binding = init_iobinding(parse); if (parse->binding == (bind_t *)0) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_start: io sub system initialisation failed.", parse->peer->procptr->refclkunit); parse_shutdown(peer->procptr); /* let our cleaning staff do the work */ return false; /* well, ok - special initialisation broke */ } parse->generic->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */ parse->generic->io.io_input = parse->binding->bd_io_input; /* pick correct input routine */ /* * as we always(?) get 8 bit chars we want to be * sure, that the upper bits are zero for less * than 8 bit I/O - so we pass that information on. * note that there can be only one bit count format * per file descriptor */ switch (tio.c_cflag & CSIZE) { case CS5: tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS5; break; case CS6: tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS6; break; case CS7: tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS7; break; case CS8: tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS8; break; default: /* huh? */ break; } if (!PARSE_SETCS(parse, &tmp_ctl)) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%u: parse_start: parse_setcs() FAILED.", unit); parse_shutdown(peer->procptr); /* let our cleaning staff do the work */ return false; /* well, ok - special initialisation broke */ } strlcpy(tmp_ctl.parseformat.parse_buffer, parse->parse_type->cl_format, sizeof(tmp_ctl.parseformat.parse_buffer)); tmp_ctl.parseformat.parse_count = (unsigned short) strlen(tmp_ctl.parseformat.parse_buffer); if (!PARSE_SETFMT(parse, &tmp_ctl)) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%u: parse_start: parse_setfmt() FAILED.", unit); parse_shutdown(peer->procptr); /* let our cleaning staff do the work */ return false; /* well, ok - special initialisation broke */ } /* * get rid of all IO accumulated so far */ (void) tcflush(parse->generic->io.fd, TCIOFLUSH); /* * try to do any special initializations */ if (parse->parse_type->cl_init) { if (parse->parse_type->cl_init(parse)) { parse_shutdown(peer->procptr); /* let our cleaning staff do the work */ return false; /* well, ok - special initialisation broke */ } } /* * Insert in async io device list. */ if (!io_addclock(&parse->generic->io)) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", parse->peer->procptr->refclkunit, parsedev); parse_shutdown(peer->procptr); /* let our cleaning staff do the work */ return false; } /* * print out configuration */ NLOG(NLOG_CLOCKINFO) { /* conditional if clause for conditional syslog */ msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: reference clock \"%s\" (I/O device %s, PPS device %s) added", parse->peer->procptr->refclkunit, parse->parse_type->cl_description, parsedev, (parse->ppsfd != parse->generic->io.fd) ? parseppsdev : parsedev); msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: Stratum %d, trust time %s, precision %d", parse->peer->procptr->refclkunit, parse->peer->stratum, l_mktime(parse->maxunsync), parse->peer->precision); msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: rootdelay %.6f s, phase adjustment %.6f s, PPS phase adjustment %.6f s, %s IO handling", parse->peer->procptr->refclkunit, parse->parse_type->cl_rootdelay, parse->generic->fudgetime1, parse->ppsphaseadjust, parse->binding->bd_description); msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: Format recognition: %s", parse->peer->procptr->refclkunit, parse->parse_type->cl_format); msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: %sPPS support", parse->peer->procptr->refclkunit, CLK_PPS(parse->peer) ? "" : "NO "); } return true; } /*-------------------------------------------------- * parse_ctl - process changes on flags/time values */ static void parse_ctl( struct parseunit *parse, const struct refclockstat *in ) { if (in) { if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)) { uint8_t mask = CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4; parse->flags = (parse->flags & (uint8_t)(~mask)) | (in->flags & mask); #if defined(HAVE_PPSAPI) if (CLK_PPS(parse->peer)) { parse_ppsapi(parse); } #endif } if (in->haveflags & CLK_HAVETIME1) { parse->generic->fudgetime1 = in->fudgetime1; msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: new phase adjustment %.6f s", parse->peer->procptr->refclkunit, parse->generic->fudgetime1); } if (in->haveflags & CLK_HAVETIME2) { parse->generic->fudgetime2 = in->fudgetime2; if (parse->flags & PARSE_TRUSTTIME) { parse->maxunsync = (unsigned long)ABS(in->fudgetime2); msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: new trust time %s", parse->peer->procptr->refclkunit, l_mktime(parse->maxunsync)); } else { parse->ppsphaseadjust = in->fudgetime2; msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: new PPS phase adjustment %.6f s", parse->peer->procptr->refclkunit, parse->ppsphaseadjust); #if defined(HAVE_PPSAPI) if (CLK_PPS(parse->peer)) { parse_ppsapi(parse); } #endif } } } } /*-------------------------------------------------- * parse_poll - called by the transmit procedure */ static void parse_poll( int unit, struct peer *peer ) { struct parseunit *parse = peer->procptr->unitptr; if (peer != parse->peer) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: poll: INTERNAL: peer incorrect", unit); return; } /* * Update clock stat counters */ parse->generic->polls++; if (parse->pollneeddata && ((int)(current_time - parse->pollneeddata) > (1<<(max(min(parse->peer->hpoll, parse->peer->ppoll), parse->peer->cfg.minpoll))))) { /* * start worrying when exceeding a poll interval * bad news - didn't get a response last time */ parse->lastmissed = current_time; parse_event(parse, CEVNT_TIMEOUT); ERR(ERR_NODATA) msyslog(LOG_WARNING, "REFCLOCK: PARSE receiver #%d: no data from device within poll interval (check receiver / wiring)", parse->peer->procptr->refclkunit); } /* * we just mark that we want the next sample for the clock filter */ parse->pollneeddata = current_time; if (parse->parse_type->cl_poll) { parse->parse_type->cl_poll(parse); } cparse_statistics(parse); return; } #define LEN_STATES 300 /* length of state string */ /*-------------------------------------------------- * parse_control - set fudge factors, return statistics */ static void parse_control( int unit, const struct refclockstat *in, struct refclockstat *out, struct peer *peer ) { struct parseunit *parse = peer->procptr->unitptr; parsectl_t tmpctl; static char outstatus[400]; /* status output buffer */ if (out) { out->lencode = 0; out->p_lastcode = 0; out->kv_list = (struct ctl_var *)0; } if (!parse || !parse->peer) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)", unit); return; } unit = parse->peer->procptr->refclkunit; /* * handle changes */ parse_ctl(parse, in); /* * supply data */ if (out) { unsigned long sum = 0; char *tt, *start; int i; outstatus[0] = '\0'; /* * keep fudgetime2 in sync with TRUSTTIME/MAXUNSYNC flag1 */ parse->generic->fudgetime2 = (parse->flags & PARSE_TRUSTTIME) ? (double)parse->maxunsync : parse->ppsphaseadjust; /* * figure out skew between PPS and RS232 - just for informational * purposes */ if (PARSE_SYNC(parse->timedata.parse_state)) { if (PARSE_PPS(parse->timedata.parse_state) && PARSE_TIMECODE(parse->timedata.parse_state)) { l_fp off; /* * we have a PPS and RS232 signal - calculate the skew * WARNING: assumes on TIMECODE == PULSE (timecode after pulse) */ off = parse->timedata.parse_stime; off -= parse->timedata.parse_ptime; /* true offset */ tt = add_var(&out->kv_list, 80, RO); snprintf(tt, 80, "refclock_ppsskew=%s", lfptoms(off, 6)); } } if (PARSE_PPS(parse->timedata.parse_state)) { tt = add_var(&out->kv_list, 80, RO|DEF); snprintf(tt, 80, "refclock_ppstime=\"%s\"", prettydate(parse->timedata.parse_ptime)); } start = tt = add_var(&out->kv_list, 128, RO|DEF); tt = ap(start, 128, tt, "refclock_time=\""); if (lfpuint(parse->timedata.parse_time) == 0) { ap(start, 128, tt, "\""); } else { ap(start, 128, tt, "%s\"", prettydate(parse->timedata.parse_time)); } if (!PARSE_GETTIMECODE(parse, &tmpctl)) { ERR(ERR_INTERNAL) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit); } else { start = tt = add_var(&out->kv_list, 512, RO|DEF); tt = ap(start, 512, tt, "refclock_status=\""); /* * copy PPS flags from last read transaction (informational only) */ tmpctl.parsegettc.parse_state |= parse->timedata.parse_state & (PARSEB_PPS|PARSEB_S_PPS); (void)parsestate(tmpctl.parsegettc.parse_state, tt, BUFFER_SIZES(start, tt, 512)); tt += strlen(tt); ap(start, 512, tt, "\""); if (tmpctl.parsegettc.parse_count) mkascii(outstatus+strlen(outstatus), (int)(sizeof(outstatus)- strlen(outstatus) - 1), tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count)); } tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format; if (!PARSE_GETFMT(parse, &tmpctl)) { ERR(ERR_INTERNAL) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_control: parse_getfmt() FAILED", unit); } else { int count = tmpctl.parseformat.parse_count - 1; start = tt = add_var(&out->kv_list, 80, RO|DEF); tt = ap(start, 80, tt, "refclock_format=\""); if (count > 0) { tt = ap(start, 80, tt, "%*.*s", count, count, tmpctl.parseformat.parse_buffer); } ap(start, 80, tt, "\""); } /* * gather state statistics */ start = tt = add_var(&out->kv_list, LEN_STATES, RO|DEF); tt = ap(start, LEN_STATES, tt, "refclock_states=\""); for (i = 0; i <= CEVNT_MAX; i++) { unsigned long s_time; unsigned long d = current_time - parse->generic->timestarted; unsigned long percent; percent = s_time = PARSE_STATETIME(parse, i); while (((unsigned long)(~0) / 10000) < percent) { percent /= 10; d /= 10; } if (d) percent = (percent * 10000) / d; else percent = 10000; if (s_time) { char item[80]; int count; snprintf(item, 80, "%s%s%s: %s (%d.%02d%%)", sum ? "; " : "", (parse->generic->currentstatus == i) ? "*" : "", clockstatus((unsigned int)i), l_mktime(s_time), (int)(percent / 100), (int)(percent % 100)); if ((count = (int) strlen(item)) < (LEN_STATES - 40 - (tt - start))) { tt = ap(start, LEN_STATES, tt, "%s", item); } sum += s_time; } } tt = ap(start, LEN_STATES, tt, "; running time: %s\"", l_mktime(sum)); add_var(&out->kv_list, 32, RO); snprintf(tt, 32, "refclock_id=\"%s\"", parse->parse_type->cl_id); add_var(&out->kv_list, 80, RO); snprintf(tt, 80, "refclock_iomode=\"%s\"", parse->binding->bd_description); add_var(&out->kv_list, 128, RO); snprintf(tt, 128, "refclock_driver_version=\"%s\"", VERSION); { struct ctl_var *k; k = parse->kv; while (k && !(k->flags & EOV)) { set_var(&out->kv_list, k->text, strlen(k->text)+1, k->flags); k++; } } out->lencode = (unsigned short) strlen(outstatus); out->p_lastcode = outstatus; } } /**=========================================================================== ** processing routines **/ /*-------------------------------------------------- * event handling - note that nominal events will also be posted * keep track of state dwelling times */ static void parse_event( struct parseunit *parse, int event ) { if (parse->generic->currentstatus != (uint8_t) event) { parse->statetime[parse->generic->currentstatus] += current_time - parse->lastchange; parse->lastchange = current_time; if (parse->parse_type->cl_event) parse->parse_type->cl_event(parse, event); if (event == CEVNT_NOMINAL) { NLOG(NLOG_CLOCKSTATUS) msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: SYNCHRONIZED", parse->peer->procptr->refclkunit); } refclock_report(parse->peer, event); } } /*-------------------------------------------------- * process a PARSE time sample */ static void parse_process( struct parseunit *parse, parsetime_t *parsetime ) { l_fp off, rectime = 0, reftime = 0; double fudge; /* silence warning: integral part may be used uninitialized in this function */ ZERO(off); /* * check for changes in conversion status * (only one for each new status !) */ if (((parsetime->parse_status & CVT_MASK) != CVT_OK) && ((parsetime->parse_status & CVT_MASK) != CVT_NONE) && (parse->timedata.parse_status != parsetime->parse_status)) { char buffer[400]; NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_WARNING, "REFCLOCK: PARSE receiver #%d: conversion status \"%s\"", parse->peer->procptr->refclkunit, parsestatus(parsetime->parse_status, buffer, sizeof(buffer))); if ((parsetime->parse_status & CVT_MASK) == CVT_FAIL) { /* * tell more about the story - list time code * there is a slight change for a race condition and * the time code might be overwritten by the next packet */ parsectl_t tmpctl; if (!PARSE_GETTIMECODE(parse, &tmpctl)) { ERR(ERR_INTERNAL) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_process: parse_timecode() FAILED", parse->peer->procptr->refclkunit); } else { ERR(ERR_BADDATA) msyslog(LOG_WARNING, "REFCLOCK: PARSE receiver #%d: FAILED TIMECODE: \"%s\" (check receiver configuration / wiring)", parse->peer->procptr->refclkunit, mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, (unsigned)(tmpctl.parsegettc.parse_count - 1))); } /* copy status to show only changes in case of failures */ parse->timedata.parse_status = parsetime->parse_status; } } /* * examine status and post appropriate events */ if ((parsetime->parse_status & CVT_MASK) != CVT_OK) { /* * got bad data - tell the rest of the system */ switch (parsetime->parse_status & CVT_MASK) { case CVT_NONE: if ((parsetime->parse_status & CVT_ADDITIONAL) && parse->parse_type->cl_message) parse->parse_type->cl_message(parse, parsetime); /* * save PPS information that comes piggyback */ if (PARSE_PPS(parsetime->parse_state)) { parse->timedata.parse_state |= PARSEB_PPS|PARSEB_S_PPS; parse->timedata.parse_ptime = parsetime->parse_ptime; } break; /* well, still waiting - timeout is handled at higher levels */ case CVT_FAIL: if (parsetime->parse_status & CVT_BADFMT) { parse_event(parse, CEVNT_BADREPLY); } else if (parsetime->parse_status & CVT_BADDATE) { parse_event(parse, CEVNT_BADDATE); } else if (parsetime->parse_status & CVT_BADTIME) { parse_event(parse, CEVNT_BADTIME); } else { /* for the lack of something better */ parse_event(parse, CEVNT_BADREPLY); } break; default: /* huh? */ break; } return; /* skip the rest - useless */ } /* * check for format changes * (in case somebody has swapped clocks 8-) */ if (parse->lastformat != parsetime->parse_format) { parsectl_t tmpctl; tmpctl.parseformat.parse_format = parsetime->parse_format; if (!PARSE_GETFMT(parse, &tmpctl)) { ERR(ERR_INTERNAL) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: parse_getfmt() FAILED", parse->peer->procptr->refclkunit); } else { NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_INFO, "REFCLOCK: PARSE receiver #%d: packet format \"%s\"", parse->peer->procptr->refclkunit, tmpctl.parseformat.parse_buffer); } parse->lastformat = parsetime->parse_format; } /* * now, any changes ? */ if ((parse->timedata.parse_state ^ parsetime->parse_state) & ~(unsigned)(PARSEB_PPS|PARSEB_S_PPS)) { char tmp1[200]; char tmp2[200]; /* * something happened - except for PPS events */ (void) parsestate(parsetime->parse_state, tmp1, sizeof(tmp1)); (void) parsestate(parse->timedata.parse_state, tmp2, sizeof(tmp2)); NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_INFO,"REFCLOCK: PARSE receiver #%d: STATE CHANGE: %s -> %s", parse->peer->procptr->refclkunit, tmp2, tmp1); } /* * carry on PPS information if still usable */ if (PARSE_PPS(parse->timedata.parse_state) && !PARSE_PPS(parsetime->parse_state)) { parsetime->parse_state |= PARSEB_PPS|PARSEB_S_PPS; parsetime->parse_ptime = parse->timedata.parse_ptime; } /* * remember for future */ parse->timedata = *parsetime; /* * check to see, whether the clock did a complete powerup or lost PZF signal * and post correct events for current condition */ if (PARSE_POWERUP(parsetime->parse_state)) { /* * this is bad, as we have completely lost synchronisation * well this is a problem with the receiver here * for PARSE Meinberg DCF77 receivers the lost synchronisation * is true as it is the powerup state and the time is taken * from a crude real time clock chip * for the PZF/GPS series this is only partly true, as * PARSE_POWERUP only means that the pseudo random * phase shift sequence cannot be found. this is only * bad, if we have never seen the clock in the SYNC * state, where the PHASE and EPOCH are correct. * for reporting events the above business does not * really matter, but we can use the time code * even in the POWERUP state after having seen * the clock in the synchronized state (PZF class * receivers) unless we have had a telegram disruption * after having seen the clock in the SYNC state. we * thus require having seen the clock in SYNC state * *after* having missed telegrams (noresponse) from * the clock. one problem remains: we might use erroneously * POWERUP data if the disruption is shorter than 1 polling * interval. fortunately powerdowns last usually longer than 64 * seconds and the receiver is at least 2 minutes in the * POWERUP or NOSYNC state before switching to SYNC * for GPS receivers this can mean antenna problems and other causes. * the additional grace period can be enables by a clock * mode having the PARSE_F_POWERUPTRUST flag in cl_flag set. */ parse_event(parse, CEVNT_FAULT); NLOG(NLOG_CLOCKSTATUS) ERR(ERR_BADSTATUS) msyslog(LOG_ERR,"REFCLOCK: PARSE receiver #%d: NOT SYNCHRONIZED/RECEIVER PROBLEMS", parse->peer->procptr->refclkunit); } else { /* * we have two states left * * SYNC: * this state means that the EPOCH (timecode) and PHASE * information has be read correctly (at least two * successive PARSE timecodes were received correctly) * this is the best possible state - full trust * * NOSYNC: * The clock should be on phase with respect to the second * signal, but the timecode has not been received correctly within * at least the last two minutes. this is a sort of half baked state * for PARSE Meinberg DCF77 clocks this is bad news (clock running * without timecode confirmation) * PZF 535 has also no time confirmation, but the phase should be * very precise as the PZF signal can be decoded */ if (PARSE_SYNC(parsetime->parse_state)) { /* * currently completely synchronized - best possible state */ parse->lastsync = current_time; clear_err(parse, ERR_BADSTATUS); } else { /* * we have had some problems receiving the time code */ parse_event(parse, CEVNT_PROP); NLOG(NLOG_CLOCKSTATUS) ERR(ERR_BADSTATUS) msyslog(LOG_ERR,"REFCLOCK: PARSE receiver #%d: TIMECODE NOT CONFIRMED", parse->peer->procptr->refclkunit); } } fudge = parse->generic->fudgetime1; /* standard RS232 Fudgefactor */ if (PARSE_TIMECODE(parsetime->parse_state)) { rectime = parsetime->parse_stime; off = reftime = parsetime->parse_time; off -= rectime; /* prepare for PPS adjustments logic */ DPRINT(4, ("REFCLOCK: PARSE receiver #%d: Reftime %s, Recvtime %s - initial offset %s\n", parse->peer->procptr->refclkunit, prettydate(reftime), prettydate(rectime), lfptoa(off,6))); } if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer)) { l_fp offset; double ppsphaseadjust = parse->ppsphaseadjust; #ifdef HAVE_PPSAPI /* * set fudge = 0.0 if already included in PPS time stamps */ if (parse->ppsctl.pps_params.mode & (PPS_OFFSETCLEAR|PPS_OFFSETASSERT)) { ppsphaseadjust = 0.0; } #endif /* * we have a PPS signal - much better than the RS232 stuff (we hope) */ offset = parsetime->parse_ptime; DPRINT(4, ("REFCLOCK: PARSE receiver #%d: PPStime %s\n", parse->peer->procptr->refclkunit, prettydate(offset))); if (PARSE_TIMECODE(parsetime->parse_state)) { if (fabsl(lfptod(off)) <= 0.5) { fudge = ppsphaseadjust; /* pick PPS fudge factor */ /* * RS232 offsets within [-0.5..0.5] - take PPS offsets */ if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND) { reftime = off = offset; if (lfpfrac(reftime) & 0x80000000) bumplfpuint(reftime, 1); setlfpfrac(reftime, 0); /* * implied on second offset */ /* map [0.5..1[ -> [-0.5..0[ */ setlfpfrac(off, ~lfpfrac(off)); /* sign extend */ setlfpuint(off, (unsigned long int)( (lfpfrac(off) & 0x80000000) ? -1 : 0)); } else { /* * time code describes pulse */ reftime = off = parsetime->parse_time; off -= offset; /* true offset */ } } /* * take RS232 offset when PPS when out of bounds */ } else { fudge = ppsphaseadjust; /* pick PPS fudge factor */ /* * Well, no time code to guide us - assume on second pulse * and pray, that we are within [-0.5..0.5[ */ off = offset; reftime = offset; if (lfpfrac(reftime) & 0x80000000) bumplfpuint(reftime, 1); setlfpfrac(reftime, 0); /* * implied on second offset */ /* map [0.5..1[ -> [-0.5..0[ */ setlfpfrac(off, ~lfpfrac(off)); /* sign extend */ setlfpuint(off, ((lfpfrac(off) & 0x80000000) ? (unsigned long int)-1 : 0)); } } else { if (!PARSE_TIMECODE(parsetime->parse_state)) { /* * Well, no PPS, no TIMECODE, no more work ... */ if ((parsetime->parse_status & CVT_ADDITIONAL) && parse->parse_type->cl_message) parse->parse_type->cl_message(parse, parsetime); return; } } DPRINT(4, ("REFCLOCK: PARSE receiver #%d: Reftime %s, Recvtime %s - final offset %s\n", parse->peer->procptr->refclkunit, prettydate(reftime), prettydate(rectime), lfptoa(off,6))); rectime = reftime; rectime -= off; /* just to keep the ntp interface happy */ DPRINT(4, ("REFCLOCK: PARSE receiver #%d: calculated Reftime %s, Recvtime %s\n", parse->peer->procptr->refclkunit, prettydate(reftime), prettydate(rectime))); if ((parsetime->parse_status & CVT_ADDITIONAL) && parse->parse_type->cl_message) parse->parse_type->cl_message(parse, parsetime); if (PARSE_SYNC(parsetime->parse_state)) { /* * log OK status */ parse_event(parse, CEVNT_NOMINAL); } clear_err(parse, ERR_BADIO); clear_err(parse, ERR_BADDATA); clear_err(parse, ERR_NODATA); clear_err(parse, ERR_INTERNAL); /* * and now stick it into the clock machine * samples are only valid iff lastsync is not too old and * we have seen the clock in sync at least once * after the last time we didn't see an expected data telegram * at startup being not in sync is also bad just like * POWERUP state unless PARSE_F_POWERUPTRUST is set * see the clock states section above for more reasoning */ if (((current_time - parse->lastsync) > parse->maxunsync) || (parse->lastsync < parse->lastmissed) || ((parse->lastsync == 0) && !PARSE_SYNC(parsetime->parse_state)) || (((parse->parse_type->cl_flags & PARSE_F_POWERUPTRUST) == 0) && PARSE_POWERUP(parsetime->parse_state))) { parse->generic->leap = LEAP_NOTINSYNC; parse->lastsync = 0; /* wait for full sync again */ } else { if (PARSE_LEAPADD(parsetime->parse_state)) { /* * we pick this state also for time code that pass leap warnings * without direction information (as earth is currently slowing * down). */ parse->generic->leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND; } else if (PARSE_LEAPDEL(parsetime->parse_state)) { parse->generic->leap = LEAP_DELSECOND; } else { parse->generic->leap = LEAP_NOWARNING; } } if (parse->generic->leap != LEAP_NOTINSYNC) { /* * only good/trusted samples are interesting */ DPRINT(3, ("REFCLOCK: PARSE receiver #%d: refclock_process_offset(reftime=%s, rectime=%s, Fudge=%f)\n", parse->peer->procptr->refclkunit, prettydate(reftime), prettydate(rectime), fudge)); parse->generic->lastref = reftime; refclock_process_offset(parse->generic, reftime, rectime, fudge); #ifdef HAVE_PPSAPI /* * pass PPS information on to PPS clock */ if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer)) { parse->peer->cfg.flags |= (FLAG_PPS | FLAG_TSTAMP_PPS); parse_hardpps(parse, PARSE_HARDPPS_ENABLE); } #endif } else { parse_hardpps(parse, PARSE_HARDPPS_DISABLE); parse->peer->cfg.flags &= ~(FLAG_PPS | FLAG_TSTAMP_PPS); } /* * ready, unless the machine wants a sample or * we are in fast startup mode (peer->dist > MAXDISTANCE) */ if (!parse->pollneeddata && parse->peer->disp <= MAXDISTANCE) return; parse->pollneeddata = 0; parse->timedata.parse_state &= ~(unsigned)(PARSEB_PPS|PARSEB_S_PPS); refclock_receive(parse->peer); } /**=========================================================================== ** special code for special clocks **/ static void mk_utcinfo( char *t, // pointer to the output string buffer int wnt, int wnlsf, int dn, int dtls, int dtlsf, int size // size of the output string buffer ) { /* * The week number transmitted by the GPS satellites for the leap date * is truncated to 8 bits only. If the nearest leap second date is off * the current date by more than +/- 128 weeks then conversion to a * calendar date is ambiguous. On the other hand, if a leap second is * currently being announced (i.e. dtlsf != dtls) then the week number * wnlsf is close enough, and we can unambiguously determine the date * for which the leap second is scheduled. */ if ( dtlsf != dtls ) { time_t t_ls; struct tm tmbuf, *tm; int n = 0; if (wnlsf < GPSWRAP) wnlsf += GPSWEEKS; if (wnt < GPSWRAP) wnt += GPSWEEKS; t_ls = (time_t)(wnlsf * SECSPERWEEK + dn * SECSPERDAY + (int)GPS_SEC_BIAS - 1); tm = gmtime_r( &t_ls, &tmbuf ); if (tm == NULL) // gmtime_r() failed { snprintf( t, (size_t)size, "** (gmtime_r() failed in mk_utcinfo())" ); return; } n += snprintf( t, (size_t)size, "UTC offset transition from %is to %is due to leap second %s", dtls, dtlsf, ( dtls < dtlsf ) ? "insertion" : "deletion" ); n += snprintf(t + n, (size_t)(size - n), " at UTC midnight at the end of %04i-%02i-%02i", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); } else snprintf( t, (size_t)size, "UTC offset parameter: %is, no leap second announced.\n", dtls ); } #ifdef CLOCK_MEINBERG /**=========================================================================== ** Meinberg GPS receiver support **/ /*------------------------------------------------------------ * gps16x_message - process messages from Meinberg GPS receiver */ static void gps16x_message( struct parseunit *parse, parsetime_t *parsetime ) { if (parse->timedata.parse_msglen && parsetime->parse_msg[0] == SOH) { GPS_MSG_HDR header; unsigned char *bufp = (unsigned char *)parsetime->parse_msg + 1; #ifdef DEBUG if (debug > 2) /* SPECIAL DEBUG */ { char msgbuffer[600]; mkreadable(msgbuffer, sizeof(msgbuffer), (char *)parsetime->parse_msg, parsetime->parse_msglen, 1); printf("REFCLOCK: PARSE receiver #%d: received message (%d bytes) >%s<\n", parse->peer->procptr->refclkunit, parsetime->parse_msglen, msgbuffer); } #endif get_mbg_header(&bufp, &header); if (header.hdr_csum == mbg_csum(parsetime->parse_msg + 1, 6) && (header.len == 0 || (header.len < sizeof(parsetime->parse_msg) && header.data_csum == mbg_csum(bufp, header.len)))) { /* * clean message */ switch (header.cmd) { case GPS_SW_REV: { char buffer[64]; SW_REV gps_sw_rev; get_mbg_sw_rev(&bufp, &gps_sw_rev); snprintf(buffer, sizeof(buffer), "meinberg_gps_version=\"%x.%02x%s%s\"", (unsigned)((gps_sw_rev.code >> 8) & 0xFF), (unsigned)(gps_sw_rev.code & 0xFF), gps_sw_rev.name[0] ? " " : "", gps_sw_rev.name); set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF); } break; case GPS_BVAR_STAT: { static struct state { BVAR_STAT flag; /* status flag */ const char *string; /* bit name */ } states[] = { { BVAR_CFGH_INVALID, "Configuration/Health" }, { BVAR_ALM_NOT_COMPLETE, "Almanachs" }, { BVAR_UTC_INVALID, "UTC Correction" }, { BVAR_IONO_INVALID, "Ionospheric Correction" }, { BVAR_RCVR_POS_INVALID, "Receiver Position" }, { 0, "" } }; BVAR_STAT status; struct state *s = states; char buffer[512]; char *p, *b; status = (BVAR_STAT)get_lsb_uint16(&bufp); p = b = buffer; p = ap(buffer, sizeof(buffer), p, "meinberg_gps_status=\"[0x%04x] ", status); if (status) { p = ap(buffer, sizeof(buffer), p, "incomplete buffered data: "); b = p; while (s->flag) { if (status & s->flag) { if (p != b) { p = ap(buffer, sizeof(buffer), p, ", "); } p = ap(buffer, sizeof(buffer), p, "%s", (const char *)s->string); } s++; } ap(buffer, sizeof(buffer), p, "\""); } else { ap(buffer, sizeof(buffer), p, "\""); } set_var(&parse->kv, buffer, strlen(buffer)+1, RO|DEF); } break; case GPS_POS_XYZ: { XYZ xyz; char buffer[256]; get_mbg_xyz(&bufp, xyz); snprintf(buffer, sizeof(buffer), "gps_position(XYZ)=\"%s m, %s m, %s m\"", mfptoa(xyz[XP], 1), mfptoa(xyz[YP], 1), mfptoa(xyz[ZP], 1)); set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF); } break; case GPS_POS_LLA: { LLA lla; char buffer[256]; get_mbg_lla(&bufp, lla); snprintf(buffer, sizeof(buffer), "gps_position(LLA)=\"%s deg, %s deg, %s m\"", mfptoa(lla[LAT], 4), mfptoa(lla[LON], 4), mfptoa(lla[ALT], 1)); set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF); } break; case GPS_TZDL: break; case GPS_PORT_PARM: break; case GPS_SYNTH: break; case GPS_ANT_INFO: { ANT_INFO antinfo; char buffer[512]; char *p, *q; get_mbg_antinfo(&bufp, &antinfo); p = buffer; p = ap(buffer, sizeof(buffer), p, "meinberg_antenna_status=\""); switch (antinfo.status) { case ANT_INVALID: // No other fields valid since antenna has not yet been disconnected p = ap(buffer, sizeof(buffer), p, ""); break; case ANT_DISCONN: // Antenna is disconnected, tm_reconn and delta_t not yet set q = ap(buffer, sizeof(buffer), p, "DISCONNECTED since "); NLOG(NLOG_CLOCKSTATUS) ERR(ERR_BADSTATUS) msyslog(LOG_ERR,"REFCLOCK: PARSE receiver #%d: ANTENNA FAILURE: %s", parse->peer->procptr->refclkunit, p); p = q; mbg_tm_str(&p, &antinfo.tm_disconn, BUFFER_SIZE(buffer, p), 0); *p = '\0'; break; case ANT_RECONN: // Antenna had been disconnect, but receiver sync. after reconnect, so all fields valid p = ap(buffer, sizeof(buffer), p, "SYNC AFTER RECONNECT on "); mbg_tm_str(&p, &antinfo.tm_reconn, BUFFER_SIZE(buffer, p), 0); p = ap(buffer, sizeof(buffer), p, ", clock offset at reconnect %c%ld.%07ld s, disconnect time ", (antinfo.delta_t < 0) ? '-' : '+', (long) ABS(antinfo.delta_t) / 10000, (long) ABS(antinfo.delta_t) % 10000); mbg_tm_str(&p, &antinfo.tm_disconn, BUFFER_SIZE(buffer, p), 0); *p = '\0'; break; default: p = ap(buffer, sizeof(buffer), p, "bad status 0x%04x", (unsigned)antinfo.status); break; } ap(buffer, sizeof(buffer), p, "\""); set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF); } break; case GPS_UCAP: break; case GPS_CFGH: { CFGH cfgh; char buffer[512]; char *p; get_mbg_cfgh(&bufp, &cfgh); if (cfgh.valid) { const char *cp; uint16_t tmp_val; int i; p = buffer; p = ap(buffer, sizeof(buffer), p, "gps_tot_51=\""); mbg_tgps_str(&p, &cfgh.tot_51, BUFFER_SIZE(buffer, p)); ap(buffer, sizeof(buffer), p, "\""); set_var(&parse->kv, buffer, sizeof(buffer), RO|COND_DEF); p = buffer; p = ap(buffer, sizeof(buffer), p, "gps_tot_63=\""); mbg_tgps_str(&p, &cfgh.tot_63, BUFFER_SIZE(buffer, p)); ap(buffer, sizeof(buffer), p, "\""); set_var(&parse->kv, buffer, sizeof(buffer), RO|COND_DEF); p = buffer; p = ap(buffer, sizeof(buffer), p, "gps_t0a=\""); mbg_tgps_str(&p, &cfgh.t0a, BUFFER_SIZE(buffer, p)); ap(buffer, sizeof(buffer), p, "\""); set_var(&parse->kv, buffer, sizeof(buffer), RO|COND_DEF); for (i = 0; i < N_SVNO_GPS; i++) { p = buffer; p = ap(buffer, sizeof(buffer), p, "sv_info[%d]=\"PRN%d", i, i + N_SVNO_GPS); tmp_val = cfgh.health[i]; /* a 6 bit SV health code */ p = ap(buffer, sizeof(buffer), p, "; health=0x%02x (", tmp_val); /* "All Ones" has a special meaning" */ if (tmp_val == 0x3F) /* satellite is unusable or doesn't even exist */ cp = "SV UNAVAILABLE"; else { /* The MSB contains a summary of the 3 MSBs of the 8 bit health code, * indicating if the data sent by the satellite is OK or not. */ p = ap(buffer, sizeof(buffer), p, "DATA %s, ", (tmp_val & 0x20) ? "BAD" : "OK" ); /* The 5 LSBs contain the status of the different signals sent by the satellite. */ switch (tmp_val & 0x1F) { case 0x00: cp = "SIGNAL OK"; break; /* codes 0x01 through 0x1B indicate that one or more * specific signal components are weak or dead. * We don't decode this here in detail. */ case 0x1C: cp = "SV IS TEMP OUT"; break; case 0x1D: cp = "SV WILL BE TEMP OUT"; break; default: cp = "TRANSMISSION PROBLEMS"; break; } } p = ap(buffer, sizeof(buffer), p, "%s)", cp ); tmp_val = cfgh.cfg[i]; /* a 4 bit SV configuration/type code */ p = ap(buffer, sizeof(buffer), p, "; cfg=0x%02x (", tmp_val); switch (tmp_val & 0x7) { case 0x00: cp = "(reserved)"; break; case 0x01: cp = "BLOCK II/IIA/IIR"; break; case 0x02: cp = "BLOCK IIR-M"; break; case 0x03: cp = "BLOCK IIF"; break; case 0x04: cp = "BLOCK III"; break; default: cp = "unknown SV type"; break; } p = ap(buffer, sizeof(buffer), p, "%s", cp ); if (tmp_val & 0x08) /* A-S is on, P-code is encrypted */ p = ap( buffer, sizeof(buffer), p, ", A-S on" ); ap(buffer, sizeof(buffer), p, ")\""); set_var(&parse->kv, buffer, sizeof(buffer), RO|COND_DEF); } } } break; case GPS_ALM: break; case GPS_EPH: break; case GPS_UTC: { UTC utc; char buffer[512]; char *p; p = buffer; get_mbg_utc(&bufp, &utc); if (utc.valid) { p = ap(buffer, sizeof(buffer), p, "gps_utc_correction=\""); mk_utcinfo(p, utc.t0t.wn, utc.WNlsf, utc.DNt, utc.delta_tls, utc.delta_tlsf, (int)BUFFER_SIZE(buffer, p)); p += strlen(p); ap(buffer, sizeof(buffer), p, "\""); } else { ap(buffer, sizeof(buffer), p, "gps_utc_correction=\"\""); } set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF); } break; case GPS_IONO: break; case GPS_ASCII_MSG: { ASCII_MSG gps_ascii_msg; char buffer[128]; get_mbg_ascii_msg(&bufp, &gps_ascii_msg); if (gps_ascii_msg.valid) { char buffer1[128]; mkreadable(buffer1, sizeof(buffer1), gps_ascii_msg.s, strlen(gps_ascii_msg.s), (int)0); snprintf(buffer, sizeof(buffer), "gps_message=\"%s\"", buffer1); } else snprintf(buffer, sizeof(buffer), "gps_message="); set_var(&parse->kv, buffer, sizeof(buffer), RO|DEF); } break; default: break; } } else { msyslog(LOG_DEBUG, "REFCLOCK: PARSE receiver #%d: gps16x_message: message checksum error: hdr_csum = 0x%x (expected 0x%x), " "data_len = %d, data_csum = 0x%x (expected 0x%x)", parse->peer->procptr->refclkunit, header.hdr_csum, mbg_csum(parsetime->parse_msg + 1, 6), header.len, header.data_csum, mbg_csum(bufp, (unsigned)((header.len < sizeof(parsetime->parse_msg)) ? header.len : 0))); } } return; } /*------------------------------------------------------------ * gps16x_poll - query the receiver periodically */ static void gps16x_poll( struct peer *peer ) { struct parseunit *parse = peer->procptr->unitptr; static GPS_MSG_HDR sequence[] = { { GPS_SW_REV, 0, 0, 0 }, { GPS_BVAR_STAT, 0, 0, 0 }, { GPS_UTC, 0, 0, 0 }, { GPS_ASCII_MSG, 0, 0, 0 }, { GPS_ANT_INFO, 0, 0, 0 }, { GPS_CFGH, 0, 0, 0 }, { GPS_POS_XYZ, 0, 0, 0 }, { GPS_POS_LLA, 0, 0, 0 }, { (unsigned short)~0, 0, 0, 0 } }; int rtc; unsigned char cmd_buffer[64]; unsigned char *outp = cmd_buffer; GPS_MSG_HDR *header; if (((poll_info_t *)parse->parse_type->cl_data)->rate) { parse->peer->procptr->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate; } if (sequence[parse->localstate].cmd == (unsigned short)~0) parse->localstate = 0; header = sequence + parse->localstate++; *outp++ = SOH; /* start command */ put_mbg_header(&outp, header); outp = cmd_buffer + 1; header->hdr_csum = (CSUM)mbg_csum(outp, 6); put_mbg_header(&outp, header); #ifdef DEBUG if (debug > 2) /* SPECIAL DEBUG */ { char buffer[128]; mkreadable(buffer, sizeof(buffer), (char *)cmd_buffer, (unsigned)(outp - cmd_buffer), 1); printf( "REFCLOCK: PARSE receiver #%d: transmitted message #%lu (%d bytes) >%s<\n", parse->peer->procptr->refclkunit, parse->localstate - 1, (int)(outp - cmd_buffer), buffer); } #endif rtc = (int) write(parse->generic->io.fd, cmd_buffer, (unsigned long)(outp - cmd_buffer)); if (rtc < 0) { ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: gps16x_poll: failed to send cmd to clock: %m", parse->peer->procptr->refclkunit); } else if (rtc != outp - cmd_buffer) { ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: gps16x_poll: failed to send cmd incomplete (%d of %d bytes sent)", parse->peer->procptr->refclkunit, rtc, (int)(outp - cmd_buffer)); } clear_err(parse, ERR_BADIO); return; } /*-------------------------------------------------- * init routine - setup timer */ static bool gps16x_poll_init( struct parseunit *parse ) { if (((poll_info_t *)parse->parse_type->cl_data)->rate) { parse->peer->procptr->action = gps16x_poll; gps16x_poll(parse->peer); } return false; } #else static void gps16x_message( struct parseunit *parse, parsetime_t *parsetime ) {} static bool gps16x_poll_init( struct parseunit *parse ) { return true; } #endif /* CLOCK_MEINBERG */ /**=========================================================================== ** clock polling support **/ /*-------------------------------------------------- * direct poll routine */ static void poll_dpoll( struct parseunit *parse ) { long rtc; const char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string; long ct = (long)(((poll_info_t *)parse->parse_type->cl_data)->count); rtc = write(parse->generic->io.fd, ps, (size_t)ct); if (rtc < 0) { ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", parse->peer->procptr->refclkunit); } else if (rtc != ct) { ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%ld of %ld bytes sent)", parse->peer->procptr->refclkunit, rtc, ct); } clear_err(parse, ERR_BADIO); } /*-------------------------------------------------- * periodic poll routine */ static void poll_poll( struct peer *peer ) { struct parseunit *parse = peer->procptr->unitptr; if (parse->parse_type->cl_poll) parse->parse_type->cl_poll(parse); if (((poll_info_t *)parse->parse_type->cl_data)->rate) { parse->peer->procptr->nextaction = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate; } } /*-------------------------------------------------- * init routine - setup timer */ static bool poll_init( struct parseunit *parse ) { if (((poll_info_t *)parse->parse_type->cl_data)->rate) { parse->peer->procptr->action = poll_poll; poll_poll(parse->peer); } return false; } /**=========================================================================== ** Trimble support **/ /*------------------------------------------------------------- * trimble TAIP init routine - setup EOL and then do poll_init. */ static bool trimbletaip_init( struct parseunit *parse ) { struct termios tio; /* * configure terminal line for trimble receiver */ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tio): %m", parse->peer->procptr->refclkunit); return false; } else { tio.c_cc[VEOL] = TRIMBLETAIP_EOL; if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tio): %m", parse->peer->procptr->refclkunit); return false; } } return poll_init(parse); } /*-------------------------------------------------- * trimble TAIP event routine - reset receiver upon data format trouble */ static const char *taipinit[] = { ">FPV00000000<", ">SRM;ID_FLAG=F;CS_FLAG=T;EC_FLAG=F;FR_FLAG=T;CR_FLAG=F<", ">FTM00020001<", (char *)0 }; static void trimbletaip_event( struct parseunit *parse, int event ) { switch (event) { case CEVNT_BADREPLY: /* reset on garbled input */ case CEVNT_TIMEOUT: /* reset on no input */ { const char **iv; iv = taipinit; while (*iv) { int rtc = (int) write(parse->generic->io.fd, *iv, strlen(*iv)); if (rtc < 0) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletaip_event: failed to send cmd to clock: %m", parse->peer->procptr->refclkunit); return; } else { if (rtc != (int)strlen(*iv)) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletaip_event: failed to send cmd incomplete (%d of %d bytes sent)", parse->peer->procptr->refclkunit, rtc, (int)strlen(*iv)); return; } } iv++; } NLOG(NLOG_CLOCKINFO) ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletaip_event: RECEIVER INITIALIZED", parse->peer->procptr->refclkunit); } break; default: /* ignore */ break; } } /* * This driver supports the Trimble SVee Six Plus GPS receiver module. * It should support other Trimble receivers which use the Trimble Standard * Interface Protocol (see below). * * The module has a serial I/O port for command/data and a 1 pulse-per-second * output, about 1 microsecond wide. The leading edge of the pulse is * coincident with the change of the GPS second. This is the same as * the change of the UTC second +/- ~1 microsecond. Some other clocks * specifically use a feature in the data message as a timing reference, but * the SVee Six Plus does not do this. In fact there is considerable jitter * on the timing of the messages, so this driver only supports the use * of the PPS pulse for accurate timing. Where it is determined that * the offset is way off, when first starting up ntpd for example, * the timing of the data stream is used until the offset becomes low enough * (|offset| < CLOCK_MAX), at which point the pps offset is used. * * It can use either option for receiving PPS information - the 'ppsclock' * stream pushed onto the serial data interface to timestamp the Carrier * Detect interrupts, where the 1PPS connects to the CD line. This only * works on SunOS 4.1.x currently. To select this, define PPSPPS in * Config.local. The other option is to use a pulse-stretcher/level-converter * to convert the PPS pulse into a RS232 start pulse & feed this into another * tty port. To use this option, define PPSCLK in Config.local. The pps input, * by whichever method, is handled in ntp_loopfilter.c * * The receiver uses a serial message protocol called Trimble Standard * Interface Protocol (it can support others but this driver only supports * TSIP). Messages in this protocol have the following form: * * ... ... * * Any bytes within the portion of value 10 hex () are doubled * on transmission and compressed back to one on reception. Otherwise * the values of data bytes can be anything. The serial interface is RS-422 * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits * in total!), and 1 stop bit. The protocol supports byte, integer, single, * and double datatypes. Integers are two bytes, sent most significant first. * Singles are IEEE754 single precision floating point numbers (4 byte) sent * sign & exponent first. Doubles are IEEE754 double precision floating point * numbers (8 byte) sent sign & exponent first. * The receiver supports a large set of messages, only a small subset of * which are used here. From driver to receiver the following are used: * * ID Description * * 21 Request current time * 22 Mode Select * 2C Set/Request operating parameters * 2F Request UTC info * 35 Set/Request I/O options * From receiver to driver the following are recognised: * * ID Description * * 41 GPS Time * 44 Satellite selection, PDOP, mode * 46 Receiver health * 4B Machine code/status * 4C Report operating parameters (debug only) * 4F UTC correction data (used to get leap second warnings) * 55 I/O options (debug only) * * All others are accepted but ignored. * */ #define PI 3.1415926535898 /* lots of sig figs */ #define D2R PI/180.0 /*------------------------------------------------------------------- * sendcmd, sendbyte, sendetx, sendflt implement the command * interface to the receiver. * * CAVEAT: the sendflt routine is byte order dependent and * float implementation dependent - these must be converted to portable * versions ! * * CURRENT LIMITATION: float implementation. This runs only on systems * with IEEE754 floats as native floats */ typedef struct trimble { unsigned long last_msg; /* last message received */ unsigned long last_reset; /* last time a reset was issued */ uint8_t qtracking; /* query tracking status */ unsigned long ctrack; /* current tracking set */ unsigned long ltrack; /* last tracking set */ } trimble_t; union uval { uint8_t bd[8]; int iv; float fv; double dv; }; struct txbuf { short idx; /* index to first unused byte */ uint8_t *txt; /* pointer to actual data buffer */ }; static void sendcmd (struct txbuf *buf, int c); static void sendbyte (struct txbuf *buf, int b); static void sendetx (struct txbuf *buf, struct parseunit *parse); static void sendflt (struct txbuf *buf, double a); void sendcmd( struct txbuf *buf, int c ) { buf->txt[0] = DLE; buf->txt[1] = (uint8_t)c; buf->idx = 2; } void sendbyte( struct txbuf *buf, int b ) { if (b == DLE) buf->txt[buf->idx++] = DLE; buf->txt[buf->idx++] = (uint8_t)b; } void sendetx( struct txbuf *buf, struct parseunit *parse ) { buf->txt[buf->idx++] = DLE; buf->txt[buf->idx++] = ETX; if (write(parse->generic->io.fd, buf->txt, (size_t)buf->idx) != buf->idx) { ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", parse->peer->procptr->refclkunit); } else { #ifdef DEBUG if (debug > 2) /* SPECIAL DEBUG */ { char buffer[256]; mkreadable(buffer, sizeof(buffer), (char *)buf->txt, (unsigned long)buf->idx, 1); printf("REFCLOCK: PARSE receiver #%d: transmitted message (%d bytes) >%s<\n", parse->peer->procptr->refclkunit, buf->idx, buffer); } #endif clear_err(parse, ERR_BADIO); } } void sendflt( struct txbuf *buf, double a ) { int i; union uval uval; uval.fv = (float) a; #ifdef WORDS_BIGENDIAN for (i=0; i<=3; i++) #else for (i=3; i>=0; i--) #endif sendbyte(buf, uval.bd[i]); } #define TRIM_POS_OPT 0x13 /* output position with high precision */ #define TRIM_TIME_OPT 0x03 /* use UTC time stamps, on second */ /*-------------------------------------------------- * trimble TSIP setup routine */ static bool trimbletsip_setup( struct parseunit *parse, const char *reason ) { uint8_t buffer[256]; struct txbuf buf; trimble_t *t = parse->localdata; if (t && t->last_reset && ((t->last_reset + TRIMBLE_RESET_HOLDOFF) > current_time)) { return true; /* not yet */ } if (t) t->last_reset = current_time; buf.txt = buffer; sendcmd(&buf, CMD_CVERSION); /* request software versions */ sendetx(&buf, parse); sendcmd(&buf, CMD_COPERPARAM); /* set operating parameters */ sendbyte(&buf, 4); /* static */ sendflt(&buf, 5.0*D2R); /* elevation angle mask = 10 deg XXX */ sendflt(&buf, 4.0); /* s/n ratio mask = 6 XXX */ sendflt(&buf, 12.0); /* PDOP mask = 12 */ sendflt(&buf, 8.0); /* PDOP switch level = 8 */ sendetx(&buf, parse); sendcmd(&buf, CMD_CMODESEL); /* fix mode select */ sendbyte(&buf, 1); /* time transfer mode */ sendetx(&buf, parse); sendcmd(&buf, CMD_CMESSAGE); /* request system message */ sendetx(&buf, parse); sendcmd(&buf, CMD_CSUPER); /* superpacket fix */ sendbyte(&buf, 0x2); /* binary mode */ sendetx(&buf, parse); sendcmd(&buf, CMD_CIOOPTIONS); /* set I/O options */ sendbyte(&buf, TRIM_POS_OPT); /* position output */ sendbyte(&buf, 0x00); /* no velocity output */ sendbyte(&buf, TRIM_TIME_OPT); /* UTC, compute on seconds */ sendbyte(&buf, 0x00); /* no raw measurements */ sendetx(&buf, parse); sendcmd(&buf, CMD_CUTCPARAM); /* request UTC correction data */ sendetx(&buf, parse); NLOG(NLOG_CLOCKINFO) ERR(ERR_BADIO) msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletsip_setup: RECEIVER RE-INITIALIZED (%s)", parse->peer->procptr->refclkunit, reason); return false; } /*-------------------------------------------------- * TRIMBLE TSIP check routine */ static void trimble_check( struct peer *peer ) { struct parseunit *parse = peer->procptr->unitptr; trimble_t *t = parse->localdata; uint8_t buffer[256]; struct txbuf buf; buf.txt = buffer; if (t) { if (current_time > t->last_msg + TRIMBLETSIP_IDLE_TIME) (void)trimbletsip_setup(parse, "message timeout"); } poll_poll(parse->peer); /* emit query string and re-arm timer */ if (t && t->qtracking) { unsigned long oldsats = t->ltrack & ~t->ctrack; t->qtracking = 0; t->ltrack = t->ctrack; if (oldsats) { int i; for (i = 0; oldsats; i++) { if (oldsats & (1U << i)) { sendcmd(&buf, CMD_CSTATTRACK); sendbyte(&buf, i+1); /* old sat */ sendetx(&buf, parse); } oldsats &= ~(1U << i); } } sendcmd(&buf, CMD_CSTATTRACK); sendbyte(&buf, 0x00); /* current tracking set */ sendetx(&buf, parse); } } /*-------------------------------------------------- * TRIMBLE TSIP end routine */ static void trimbletsip_end( struct parseunit *parse ) { trimble_t *t = parse->localdata; if (t) { free(t); parse->localdata = NULL; } parse->peer->procptr->nextaction = 0; parse->peer->procptr->action = NULL; } /*-------------------------------------------------- * TRIMBLE TSIP init routine */ static bool trimbletsip_init( struct parseunit *parse ) { #if defined(VEOL) || defined(VEOL2) struct termios tio; /* NEEDED FOR A LONG TIME ! */ unsigned int type; /* * allocate local data area */ if (!parse->localdata) { trimble_t *t; t = emalloc_zero(sizeof(trimble_t)); parse->localdata = t; t->last_msg = current_time; } parse->peer->procptr->action = trimble_check; parse->peer->procptr->nextaction = current_time; /* * configure terminal line for ICANON mode with VEOL characters */ if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletsip_init: tcgetattr(%d, &tio): %m", parse->peer->procptr->refclkunit, parse->generic->io.fd); return false; } else { type = (unsigned int)CLK_TYPE(parse->peer); if ( (type != (unsigned int)~0) && (parse_clockinfo[type].cl_lflag & ICANON)) { #ifdef VEOL tio.c_cc[VEOL] = ETX; #endif #ifdef VEOL2 tio.c_cc[VEOL2] = DLE; #endif } if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1) { msyslog(LOG_ERR, "REFCLOCK: PARSE receiver #%d: trimbletsip_init: tcsetattr(%d, &tio): %m", parse->peer->procptr->refclkunit, parse->generic->io.fd); return false; } } #endif return trimbletsip_setup(parse, "initial startup"); } /*------------------------------------------------------------ * trimbletsip_event - handle Trimble events * simple evente handler - attempt to re-initialize receiver */ static void trimbletsip_event( struct parseunit *parse, int event ) { switch (event) { case CEVNT_BADREPLY: /* reset on garbled input */ case CEVNT_TIMEOUT: /* reset on no input */ (void)trimbletsip_setup(parse, "event BAD_REPLY/TIMEOUT"); break; default: /* ignore */ break; } } /* * getflt, getint convert fields in the incoming data into the * appropriate type of item * * CAVEAT: these routines are currently definitely byte order dependent * and assume Representation(float) == IEEE754 * These functions MUST be converted to portable versions (especially * converting the float representation into ntp_fp formats in order * to avoid floating point operations at all! */ static double getflt( uint8_t *bp ) { union uval uval; #ifdef WORDS_BIGENDIAN uval.bd[0] = *bp++; uval.bd[1] = *bp++; uval.bd[2] = *bp++; uval.bd[3] = *bp; #else /* ! WORDS_BIGENDIAN */ uval.bd[3] = *bp++; uval.bd[2] = *bp++; uval.bd[1] = *bp++; uval.bd[0] = *bp; #endif /* ! WORDS_BIGENDIAN */ return (double)uval.fv; } static double getdbl( uint8_t *bp ) { union uval uval; #ifdef WORDS_BIGENDIAN uval.bd[0] = *bp++; uval.bd[1] = *bp++; uval.bd[2] = *bp++; uval.bd[3] = *bp++; uval.bd[4] = *bp++; uval.bd[5] = *bp++; uval.bd[6] = *bp++; uval.bd[7] = *bp; #else /* ! WORDS_BIGENDIAN */ uval.bd[7] = *bp++; uval.bd[6] = *bp++; uval.bd[5] = *bp++; uval.bd[4] = *bp++; uval.bd[3] = *bp++; uval.bd[2] = *bp++; uval.bd[1] = *bp++; uval.bd[0] = *bp; #endif /* ! WORDS_BIGENDIAN */ return uval.dv; } /*-------------------------------------------------- * trimbletsip_message - process trimble messages */ #define RTOD (180.0 / 3.1415926535898) #define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */ static void trimbletsip_message( struct parseunit *parse, parsetime_t *parsetime ) { unsigned char *buffer = parsetime->parse_msg; unsigned int size = parsetime->parse_msglen; if ((size < 4) || (buffer[0] != DLE) || (buffer[size-1] != ETX) || (buffer[size-2] != DLE)) { #ifdef DEBUG if (debug > 2) { /* SPECIAL DEBUG */ size_t i; printf("TRIMBLE BAD packet, size %u:\n ", size); for (i = 0; i < size; i++) { printf ("%2.2x, ", buffer[i]&0xff); if (i%16 == 15) printf("\n\t"); } printf("\n"); } #endif return; } else { unsigned short var_flag; trimble_t *tr = parse->localdata; unsigned int cmd = buffer[1]; char pbuffer[200]; char *t = pbuffer; cmd_info_t *s; #ifdef DEBUG if (debug > 3) { /* SPECIAL DEBUG */ size_t i; printf("TRIMBLE packet 0x%02x, size %u:\n ", cmd, size); for (i = 0; i < size; i++) { printf ("%2.2x, ", buffer[i]&0xff); if (i%16 == 15) printf("\n\t"); } printf("\n"); } #endif if (tr) tr->last_msg = current_time; s = trimble_convert(cmd, trimble_rcmds); if (s) { t = ap(pbuffer, sizeof(pbuffer), t, "%s=\"", s->varname); } else { DPRINT(1, ("TRIMBLE UNKNOWN COMMAND 0x%02x\n", cmd)); return; } var_flag = (unsigned short) s->varmode; switch(cmd) { case CMD_RCURTIME: t = ap(pbuffer, sizeof(pbuffer), t, "%f, %d, %f", getflt((unsigned char *)&mb(0)), getmsb_short(&mb(4)), getflt((unsigned char *)&mb(6))); break; case CMD_RBEST4: t = ap(pbuffer, sizeof(pbuffer), t, "mode: "); switch (mb(0) & 0xF) { default: t = ap(pbuffer, sizeof(pbuffer), t, "0x%x", (unsigned)(mb(0) & 0x7)); break; case 1: t = ap(pbuffer, sizeof(pbuffer), t, "0D"); break; case 3: t = ap(pbuffer, sizeof(pbuffer), t, "2D"); break; case 4: t = ap(pbuffer, sizeof(pbuffer), t, "3D"); break; } if (mb(0) & 0x10) t = ap(pbuffer, sizeof(pbuffer), t, "-MANUAL, "); else t = ap(pbuffer, sizeof(pbuffer), t, "-AUTO, "); t = ap(pbuffer, sizeof(pbuffer), t, "satellites %02d %02d %02d %02d, PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f", mb(1), mb(2), mb(3), mb(4), getflt((unsigned char *)&mb(5)), getflt((unsigned char *)&mb(9)), getflt((unsigned char *)&mb(13)), getflt((unsigned char *)&mb(17))); break; case CMD_RVERSION: t = ap(pbuffer, sizeof(pbuffer), t, "%d.%d (%d/%d/%d)", mb(0)&0xff, mb(1)&0xff, 1900+(mb(4)&0xff), mb(2)&0xff, mb(3)&0xff); break; case CMD_RRECVHEALTH: { static const char *msgs[] = { "Battery backup failed", "Signal processor error", "Alignment error, channel or chip 1", "Alignment error, channel or chip 2", "Antenna feed line fault", "Excessive ref freq. error", "", "" }; int i, bits; switch (mb(0) & 0xFF) { default: t = ap(pbuffer, sizeof(pbuffer), t, "illegal value 0x%02x", mb(0) & 0xFF); break; case 0x00: t = ap(pbuffer, sizeof(pbuffer), t, "doing position fixes"); break; case 0x01: t = ap(pbuffer, sizeof(pbuffer), t, "no GPS time yet"); break; case 0x03: t = ap(pbuffer, sizeof(pbuffer), t, "PDOP too high"); break; case 0x08: t = ap(pbuffer, sizeof(pbuffer), t, "no usable satellites"); break; case 0x09: t = ap(pbuffer, sizeof(pbuffer), t, "only ONE usable satellite"); break; case 0x0A: t = ap(pbuffer, sizeof(pbuffer), t, "only TWO usable satellites"); break; case 0x0B: t = ap(pbuffer, sizeof(pbuffer), t, "only THREE usable satellites"); break; case 0x0C: t = ap(pbuffer, sizeof(pbuffer), t, "the chosen satellite is unusable"); break; } bits = mb(1) & 0xFF; for (i = 0; i < 8; i++) if (bits & (0x1<", "", "" }; int i, bits; t = ap(pbuffer, sizeof(pbuffer), t, "machine id 0x%02x", mb(0) & 0xFF); bits = mb(1) & 0xFF; for (i = 0; i < 8; i++) if (bits & (0x1<"); } } break; case CMD_RSAT1BIAS: t = ap(pbuffer, sizeof(pbuffer), t, "%.1fm %.2fm/s at %.1fs", getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8))); break; case CMD_RIOOPTIONS: { t = ap(pbuffer, sizeof(pbuffer), t, "%02x %02x %02x %02x", mb(0), mb(1), mb(2), mb(3)); if (mb(0) != TRIM_POS_OPT || mb(2) != TRIM_TIME_OPT) { (void)trimbletsip_setup(parse, "bad io options"); } } break; case CMD_RSPOSXYZ: { double x = getflt((unsigned char *)&mb(0)); double y = getflt((unsigned char *)&mb(4)); double z = getflt((unsigned char *)&mb(8)); double f = getflt((unsigned char *)&mb(12)); if (f > 0.0) t = ap(pbuffer, sizeof(pbuffer), t, "x= %.1fm, y= %.1fm, z= %.1fm, time_of_fix= %f sec", x, y, z, f); else return; } break; case CMD_RSLLAPOS: { double lat = getflt((unsigned char *)&mb(0)); double lng = getflt((unsigned char *)&mb(4)); double f = getflt((unsigned char *)&mb(12)); if (f > 0.0) t = ap(pbuffer, sizeof(pbuffer), t, "lat %f %c, long %f %c, alt %.2fm", ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'), ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'), getflt((unsigned char *)&mb(8))); else return; } break; case CMD_RDOUBLEXYZ: { double x = getdbl((unsigned char *)&mb(0)); double y = getdbl((unsigned char *)&mb(8)); double z = getdbl((unsigned char *)&mb(16)); t = ap(pbuffer, sizeof(pbuffer), t, "x= %.1fm, y= %.1fm, z= %.1fm", x, y, z); } break; case CMD_RDOUBLELLA: { double lat = getdbl((unsigned char *)&mb(0)); double lng = getdbl((unsigned char *)&mb(8)); t = ap(pbuffer, sizeof(pbuffer), t, "lat %f %c, lon %f %c, alt %.2fm", ((lat < 0.0) ? (-lat) : (lat))*RTOD, (lat < 0.0 ? 'S' : 'N'), ((lng < 0.0) ? (-lng) : (lng))*RTOD, (lng < 0.0 ? 'W' : 'E'), getdbl((unsigned char *)&mb(16))); } break; case CMD_RALLINVIEW: { int i, sats; t = ap(pbuffer, sizeof(pbuffer), t, "mode: "); switch (mb(0) & 0x7) { default: t = ap(pbuffer, sizeof(pbuffer), t, "0x%x", (unsigned)(mb(0) & 0x7)); break; case 3: t = ap(pbuffer, sizeof(pbuffer), t, "2D"); break; case 4: t = ap(pbuffer, sizeof(pbuffer), t, "3D"); break; } if (mb(0) & 0x8) t = ap(pbuffer, sizeof(pbuffer), t, "-MANUAL, "); else t = ap(pbuffer, sizeof(pbuffer), t, "-AUTO, "); sats = (mb(0)>>4) & 0xF; t = ap(pbuffer, sizeof(pbuffer), t, "PDOP %.2f, HDOP %.2f, VDOP %.2f, TDOP %.2f, %d satellite%s in view: ", getflt((unsigned char *)&mb(1)), getflt((unsigned char *)&mb(5)), getflt((unsigned char *)&mb(9)), getflt((unsigned char *)&mb(13)), sats, (sats == 1) ? "" : "s"); for (i=0; i < sats; i++) { t = ap(pbuffer, sizeof(pbuffer), t, "%s%02d", i ? ", " : "", mb(17+i)); if (tr) tr->ctrack |= (1U << (mb(17+i)-1)); } if (tr) { /* mark for tracking status query */ tr->qtracking = 1; } } break; case CMD_RSTATTRACK: { t = ap(pbuffer, sizeof(pbuffer), t-2, "[%02d]=\"", mb(0)); /* add index to var name */ if (getflt((unsigned char *)&mb(4)) < 0.0) { t = ap(pbuffer, sizeof(pbuffer), t, ""); var_flag &= (unsigned short)(~DEF); } else { t = ap(pbuffer, sizeof(pbuffer), t, "ch=%d, acq=%s, eph=%d, signal_level= %5.2f, elevation= %5.2f, azimuth= %6.2f", (mb(1) & 0xFF)>>3, mb(2) ? ((mb(2) == 1) ? "ACQ" : "SRCH") : "NEVER", mb(3), getflt((unsigned char *)&mb(4)), getflt((unsigned char *)&mb(12)) * RTOD, getflt((unsigned char *)&mb(16)) * RTOD); if (mb(20)) { var_flag &= (unsigned short)(~DEF); t = ap(pbuffer, sizeof(pbuffer), t, ", OLD"); } if (mb(22)) { if (mb(22) == 1) t = ap(pbuffer, sizeof(pbuffer), t, ", BAD PARITY"); else if (mb(22) == 2) t = ap(pbuffer, sizeof(pbuffer), t, ", BAD EPH HEALTH"); } if (mb(23)) t = ap(pbuffer, sizeof(pbuffer), t, ", collecting data"); } } break; default: t = ap(pbuffer, sizeof(pbuffer), t, ""); break; } ap(pbuffer, sizeof(pbuffer), t, "\""); set_var(&parse->kv, pbuffer, sizeof(pbuffer), var_flag); } } /**============================================================ ** RAWDCF support **/ /*-------------------------------------------------- * rawdcf_init_1 - set up modem lines for RAWDCF receivers * SET DTR line */ #if defined(TIOCMSET) && (defined(TIOCM_DTR) || defined(CIOCM_DTR)) static bool rawdcf_init_1( struct parseunit *parse ) { /* fixed 2000 for using with Linux by Wolfram Pienkoss */ /* * You can use the RS232 to supply the power for a DCF77 receiver. * Here a voltage between the DTR and the RTS line is used. Unfortunately * the name has changed from CIOCM_DTR to TIOCM_DTR recently. */ int sl232; if (ioctl(parse->generic->io.fd, TIOCMGET, (void *)&sl232) == -1) { msyslog(LOG_NOTICE, "REFCLOCK: PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_DTR): %m", parse->peer->procptr->refclkunit); return 0; } #ifdef TIOCM_DTR sl232 = (sl232 & ~TIOCM_RTS) | TIOCM_DTR; /* turn on DTR, clear RTS for power supply */ #else sl232 = (sl232 & ~CIOCM_RTS) | CIOCM_DTR; /* turn on DTR, clear RTS for power supply */ #endif if (ioctl(parse->generic->io.fd, TIOCMSET, (void *)&sl232) == -1) { msyslog(LOG_NOTICE, "REFCLOCK: PARSE receiver #%d: rawdcf_init_1: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_DTR): %m", parse->peer->procptr->refclkunit); } return 0; } #else static int rawdcfdtr_init_1( struct parseunit *parse ) { msyslog(LOG_NOTICE, "REFCLOCK: PARSE receiver #%d: rawdcf_init_1: WARNING: OS interface incapable of setting DTR to power DCF modules", parse->peer->procptr->refclkunit); return 0; } #endif /* DTR initialisation type */ /*-------------------------------------------------- * rawdcf_init_2 - set up modem lines for RAWDCF receivers * CLR DTR line, SET RTS line */ #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) static bool rawdcf_init_2( struct parseunit *parse ) { /* fixed 2000 for using with Linux by Wolfram Pienkoss */ /* * You can use the RS232 to supply the power for a DCF77 receiver. * Here a voltage between the DTR and the RTS line is used. Unfortunately * the name has changed from CIOCM_DTR to TIOCM_DTR recently. */ int sl232; if (ioctl(parse->generic->io.fd, TIOCMGET, (void *)&sl232) == -1) { msyslog(LOG_NOTICE, "REFCLOCK: PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMGET, [C|T]IOCM_RTS): %m", parse->peer->procptr->refclkunit); return 0; } #ifdef TIOCM_RTS sl232 = (sl232 & ~TIOCM_DTR) | TIOCM_RTS; /* turn on RTS, clear DTR for power supply */ #else sl232 = (sl232 & ~CIOCM_DTR) | CIOCM_RTS; /* turn on RTS, clear DTR for power supply */ #endif if (ioctl(parse->generic->io.fd, TIOCMSET, (void *)&sl232) == -1) { msyslog(LOG_NOTICE, "REFCLOCK: PARSE receiver #%d: rawdcf_init_2: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_RTS): %m", parse->peer->procptr->refclkunit); } return 0; } #else static int rawdcf_init_2( struct parseunit *parse ) { msyslog(LOG_NOTICE, "REFCLOCK: PARSE receiver #%d: rawdcf_init_2: WARNING: OS interface incapable of setting RTS to power DCF modules", parse->peer->procptr->refclkunit); return 0; } #endif /* DTR initialisation type */ /* * History: * * refclock_parse.c,v * Revision 4.81 2009/05/01 10:15:29 kardel * use new refclock_ppsapi interface * * Revision 4.80 2007/08/11 12:06:29 kardel * update comments wrt/ to PPS * * Revision 4.79 2007/08/11 11:52:23 kardel * - terminate io bindings before io_closeclock() will close our file descriptor * * Revision 4.78 2006/12/22 20:08:27 kardel * Bug 746 (RFE): add configuration for Expert mouseCLOCK USB v2.0 as mode 19 * * Revision 4.77 2006/08/05 07:44:49 kardel * support optionally separate PPS devices via /dev/refclockpps-{0..3} * * Revision 4.76 2006/06/22 18:40:47 kardel * clean up signedness (gcc 4) * * Revision 4.75 2006/06/22 16:58:10 kardel * Bug #632: call parse_ppsapi() in parse_ctl() when updating * the PPS offset. Fix sign of offset passed to kernel. * * Revision 4.74 2006/06/18 21:18:37 kardel * NetBSD Coverity CID 3796: possible NULL deref * * Revision 4.73 2006/05/26 14:23:46 kardel * cleanup of copyright info * * Revision 4.72 2006/05/26 14:19:43 kardel * cleanup of ioctl cruft * * Revision 4.71 2006/05/26 14:15:57 kardel * delay adding refclock to async refclock io after all initializations * * Revision 4.70 2006/05/25 18:20:50 kardel * bug #619 * terminate parse io engine after de-registering * from refclock io engine * * Revision 4.69 2006/05/25 17:28:02 kardel * complete refclock io structure initialization *before* inserting it into the * refclock input machine (avoids null pointer deref) (bug #619) * * Revision 4.68 2006/05/01 17:02:51 kardel * copy receiver method also for newlwy created receive buffers * * Revision 4.67 2006/05/01 14:37:29 kardel * If an input buffer parses into more than one message do insert the * parsed message in a new input buffer instead of processing it * directly. This avoids deed complicated processing in signal * handling. * * Revision 4.66 2006/03/18 00:45:30 kardel * coverity fixes found in NetBSD coverity scan * * Revision 4.65 2006/01/26 06:08:33 kardel * output errno on PPS setup failure * * Revision 4.64 2005/11/09 20:44:47 kardel * utilize full PPS timestamp resolution from PPS API * * Revision 4.63 2005/10/07 22:10:25 kardel * bounded buffer implementation * * Revision 4.62.2.2 2005/09/25 10:20:16 kardel * avoid unexpected buffer overflows due to sprintf("%f") on strange floats: * replace almost all str* and *printf functions be their buffer bounded * counterparts * * Revision 4.62.2.1 2005/08/27 16:19:27 kardel * limit re-set rate of trimble clocks * * Revision 4.62 2005/08/06 17:40:00 kardel * cleanup size handling wrt/ to buffer boundaries * * Revision 4.61 2005/07/27 21:16:19 kardel * fix a long (> 11 years) misconfiguration wrt/ Meinberg cflag factory * default setup. CSTOPB was missing for the 7E2 default data format of * the DCF77 clocks. * * Revision 4.60 2005/07/17 21:14:44 kardel * change contents of version string to include the RCS/CVS Id * * Revision 4.59 2005/07/06 06:56:38 kardel * syntax error * * Revision 4.58 2005/07/04 13:10:40 kardel * fix bug 455: tripping over NULL pointer on cleanup * fix shadow storage logic for ppsphaseadjust and trustime wrt/ time2 * fix compiler warnings for some platforms wrt/ printf formatstrings and * varying structure element sizes * reorder assignment in binding to avoid tripping over NULL pointers * * Revision 4.57 2005/06/25 09:25:19 kardel * sort out log output sequence * * Revision 4.56 2005/06/14 21:47:27 kardel * collect samples only if samples are ok (sync or trusted flywheel) * propagate pps phase adjustment value to kernel via PPSAPI to help HARDPPS * en- and dis-able HARDPPS in correlation to receiver sync state * * Revision 4.55 2005/06/02 21:28:31 kardel * clarify trust logic * * Revision 4.54 2005/06/02 17:06:49 kardel * change status reporting to use fixed refclock_report() * * Revision 4.53 2005/06/02 16:33:31 kardel * fix acceptance of clocks unsync clocks right at start * * Revision 4.52 2005/05/26 21:55:06 kardel * cleanup status reporting * * Revision 4.51 2005/05/26 19:19:14 kardel * implement fast refclock startup * * Revision 4.50 2005/04/16 20:51:35 kardel * set hardpps_enable = 1 when binding a kernel PPS source * * Revision 4.49 2005/04/16 17:29:26 kardel * add non polling clock type 18 for just listenning to Meinberg clocks * * Revision 4.48 2005/04/16 16:22:27 kardel * bk sync 20050415 ntp-dev * * Revision 4.47 2004/11/29 10:42:48 kardel * bk sync ntp-dev 20041129 * * Revision 4.46 2004/11/29 10:26:29 kardel * keep fudgetime2 in sync with trusttime/ppsphaseadjust depending in flag1 * * Revision 4.45 2004/11/14 20:53:20 kardel * clear PPS flags after using them * * Revision 4.44 2004/11/14 15:29:41 kardel * support PPSAPI, upgrade Copyright to Berkeley style * * Revision 4.43 2001/05/26 22:53:16 kardel * 20010526 reconciliation * * Revision 4.42 2000/05/14 15:31:51 kardel * PPSAPI && RAWDCF modemline support * * Revision 4.41 2000/04/09 19:50:45 kardel * fixed rawdcfdtr_init() -> rawdcf_init_1 * * Revision 4.40 2000/04/09 15:27:55 kardel * modem line fiddle in rawdcf_init_2 * * Revision 4.39 2000/03/18 09:16:55 kardel * PPSAPI integration * * Revision 4.38 2000/03/05 20:25:06 kardel * support PPSAPI * * Revision 4.37 2000/03/05 20:11:14 kardel * 4.0.99g reconciliation * * Revision 4.36 1999/11/28 17:18:20 kardel * disabled burst mode * * Revision 4.35 1999/11/28 09:14:14 kardel * RECON_4_0_98F * * Revision 4.34 1999/05/14 06:08:05 kardel * store current_time in a suitable container (unsigned long) * * Revision 4.33 1999/05/13 21:48:38 kardel * double the no response timeout interval * * Revision 4.32 1999/05/13 20:09:13 kardel * complain only about missing polls after a full poll interval * * Revision 4.31 1999/05/13 19:59:32 kardel * add clock type 16 for RTS set DTR clr in RAWDCF * * Revision 4.30 1999/02/28 20:36:43 kardel * fixed printf fmt * * Revision 4.29 1999/02/28 19:58:23 kardel * updated copyright information * * Revision 4.28 1999/02/28 19:01:50 kardel * improved debug out on sent Meinberg messages * * Revision 4.27 1999/02/28 18:05:55 kardel * no linux/ppsclock.h stuff * * Revision 4.26 1999/02/28 15:27:27 kardel * wharton clock integration * * Revision 4.25 1999/02/28 14:04:46 kardel * added missing double quotes to UTC information string * * Revision 4.24 1999/02/28 12:06:50 kardel * (parse_control): using gmprettydate instead of prettydate() * (mk_utcinfo): new function for formatting GPS derived UTC information * (gps16x_message): changed to use mk_utcinfo() * (trimbletsip_message): changed to use mk_utcinfo() * ignoring position information in unsynchronized mode * (parse_start): augument linux support for optional ASYNC_LOW_LATENCY * * Revision 4.23 1999/02/23 19:47:53 kardel * fixed #endifs * (stream_receive): fixed formats * * Revision 4.22 1999/02/22 06:21:02 kardel * use new autoconfig symbols * * Revision 4.21 1999/02/21 12:18:13 kardel * 4.91f reconciliation * * Revision 4.20 1999/02/21 10:53:36 kardel * initial Linux PPSkit version * * Revision 4.19 1999/02/07 09:10:45 kardel * clarify STREAMS mitigation rules in comment * * Revision 4.18 1998/12/20 23:45:34 kardel * fix types and warnings * * Revision 4.17 1998/11/15 21:24:51 kardel * cannot access mbg_ routines when CLOCK_MEINBERG * is not defined * * Revision 4.16 1998/11/15 20:28:17 kardel * Release 4.0.73e13 reconciliation * * Revision 4.15 1998/08/22 21:56:08 kardel * fixed IO handling for non-STREAM IO * * Revision 4.14 1998/08/16 19:00:48 kardel * (gps16x_message): reduced UTC parameter information (dropped A0,A1) * made uval a local variable (killed one of the last globals) * (sendetx): added logging of messages when in debug mode * (trimble_check): added periodic checks to facilitate re-initialization * (trimbletsip_init): made use of EOL character if in non-kernel operation * (trimbletsip_message): extended message interpretation * (getdbl): fixed data conversion * * Revision 4.13 1998/08/09 22:29:13 kardel * Trimble TSIP support * * Revision 4.12 1998/07/11 10:05:34 kardel * Release 4.0.73d reconciliation * * Revision 4.11 1998/06/14 21:09:42 kardel * Sun acc cleanup * * Revision 4.10 1998/06/13 12:36:45 kardel * signed/unsigned, name clashes * * Revision 4.9 1998/06/12 15:30:00 kardel * prototype fixes * * Revision 4.8 1998/06/12 11:19:42 kardel * added direct input processing routine for refclocks in * order to avaiod that single character io gobbles up all * receive buffers and drops input data. (Problem started * with fast machines so a character a buffer was possible * one of the few cases where faster machines break existing * allocation algorithms) * * Revision 4.7 1998/06/06 18:35:20 kardel * (parse_start): added BURST mode initialisation * * Revision 4.6 1998/05/27 06:12:46 kardel * RAWDCF_BASEDELAY default added * old comment removed * casts for ioctl() * * Revision 4.5 1998/05/25 22:05:09 kardel * RAWDCF_SETDTR option removed * clock type 14 attempts to set DTR for * power supply of RAWDCF receivers * * Revision 4.4 1998/05/24 16:20:47 kardel * updated comments referencing Meinberg clocks * added RAWDCF clock with DTR set option as type 14 * * Revision 4.3 1998/05/24 10:48:33 kardel * calibrated CONRAD RAWDCF default fudge factor * * Revision 4.2 1998/05/24 09:59:35 kardel * corrected version information (ntpq support) * * Revision 4.1 1998/05/24 09:52:31 kardel * use fixed format only (new IO model) * output debug to stdout instead of msyslog() * don't include >"< in ASCII output in order not to confuse * ntpq parsing * * Revision 4.0 1998/04/10 19:52:11 kardel * Start 4.0 release version numbering * * Revision 1.2 1998/04/10 19:28:04 kardel * initial NTP VERSION 4 integration of PARSE with GPS166 binary support * derived from 3.105.1.2 from V3 tree * * Revision information 3.1 - 3.105 from log deleted 1998/04/10 kardel * */ ntpsec-1.1.0+dfsg1/ntpd/ntp.conf-man.txt0000644000175000017500000000036413252364117017634 0ustar rlaagerrlaager= ntp.conf(5) = :doctype: manpage :man source: NTPsec :man version: @NTPSEC_VERSION@ :man manual: NTPsec == NAME == ntp.conf - Network Time Protocol (NTP) daemon configuration file format include::../docs/includes/ntp-conf-body.txt[] // end ntpsec-1.1.0+dfsg1/ntpd/refclock_spectracom.c0000644000175000017500000003602213252364117020751 0ustar rlaagerrlaager/* * refclock_spectracom.c - clock driver for Spectracom GPS receivers */ #include "config.h" #include "ntp.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include #include #ifdef HAVE_PPSAPI #include "ppsapi_timepps.h" #include "refclock_pps.h" #endif /* HAVE_PPSAPI */ /* * This driver supports the "Type 2" format emitted by Spectracom time * servers including the 9483, 9489, and SecureSync. * * WARNING: This driver depends on the system clock for year disambiguation. * It will thus not be usable for recovery if the system clock is trashed. * * In former times this driver supported the Spectracom 9300 (now * end-of-lifed) and several models of Spectracom radio clocks that were * obsolesced by the WWVB modulation change at 2012-10-29T15:00:00Z. * * There are two timecode formats used by these clocks: * * Format 0 (22 ASCII printing characters): * * i ddd hh:mm:ss TZ=zz * * on-time = first * hh:mm:ss = hours, minutes, seconds * i = synchronization flag (' ' = in synch, '?' = out of synch) * * The alarm condition is indicated by other than ' ' at i, which occurs * during initial synchronization and when received signal is lost for * about ten hours. * * Format 2 (24 ASCII printing characters): * * iqyy ddd hh:mm:ss.fff ld * * on-time = * i = synchronization flag (' ' = in synch, '?' = out of synch) * q = quality indicator (' ' = locked, 'A'...'D' = unlocked) * yy = year (as broadcast) * ddd = day of year * hh:mm:ss.fff = hours, minutes, seconds, milliseconds * * The alarm condition is indicated by other than ' ' at i, which occurs * during initial synchronization and when received signal is lost for * about ten hours. The unlock condition is indicated by other than ' ' * at q. * * The q is normally ' ' when the time error is less than 1 ms and a * character in the set 'A'...'D' when the time error is less than 10, * 100, 500 and greater than 500 ms respectively. The l is normally ' ', * but is set to 'L' early in the month of an upcoming UTC leap second * and reset to ' ' on the first day of the following month. The d is * set to 'S' for standard time 'I' on the day preceding a switch to * daylight time, 'D' for daylight time and 'O' on the day preceding a * switch to standard time. The start bit of the first is * synchronized to the indicated time as returned. * * This driver does not need to be told which format is in use - it * figures out which one from the length of the message. The driver * makes no attempt to correct for the intrinsic jitter of the radio * itself, which is a known problem with the older radios. * * PPS Signal Processing * * When PPS signal processing is enabled, and when the system clock has * been set by this or another driver and the PPS signal offset is * within 0.4 s of the system clock offset, the PPS signal replaces the * timecode for as long as the PPS signal is active. If for some reason * the PPS signal fails for one or more poll intervals, the driver * reverts to the timecode. If the timecode fails for one or more poll * intervals, the PPS signal is disconnected. * * Fudge Factors * * This driver can retrieve a table of quality data maintained * internally by the Netclock/2 clock. If option flag4 is set to 1, * the driver will retrieve this table and write it to the clockstats * file when the first timecode message of a new day is received. * * PPS calibration fudge time 1: format 0 .003134, format 2 .004034 */ /* * Interface definitions */ #define DEVICE "/dev/spectracom%d" /* device name and unit */ #define SPEED232 B9600 /* uart speed (9600 baud) */ #define PRECISION (-13) /* precision assumed (about 100 us) */ #ifdef HAVE_PPSAPI # define PPS_PRECISION (-13) /* precision assumed (about 100 us) */ #endif #define REFID "GPS\0" /* reference ID */ #define NAME "SPECTRACOM" /* shortname */ #define DESCRIPTION "Spectracom GPS Receiver" /* WRU */ #define LENTYPE0 22 /* format 0 timecode length */ #define LENTYPE2 24 /* format 2 timecode length */ #define LENTYPE3 29 /* format 3 timecode length */ #define MONLIN 15 /* number of monitoring lines */ /* * Spectracom unit control structure */ struct spectracomunit { #ifdef HAVE_PPSAPI struct refclock_ppsctl ppsctl; /* PPSAPI structure */ int ppsapi_tried; /* attempt PPSAPI once */ int ppsapi_lit; /* time_pps_create() worked */ int tcount; /* timecode sample counter */ int pcount; /* PPS sample counter */ #endif /* HAVE_PPSAPI */ l_fp laststamp; /* last timestamp */ bool prev_eol_cr; /* was last EOL (not )? */ uint8_t lasthour; /* last hour (for monitor) */ uint8_t linect; /* count ignored lines (for monitor */ }; /* * Function prototypes */ static bool spectracom_start (int, struct peer *); static void spectracom_receive (struct recvbuf *); static void spectracom_poll (int, struct peer *); static void spectracom_timer (int, struct peer *); #ifdef HAVE_PPSAPI static void spectracom_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); #define SPECTRACOM_CONTROL spectracom_control #else #define SPECTRACOM_CONTROL NULL #endif /* HAVE_PPSAPI */ /* * Transfer vector */ struct refclock refclock_spectracom = { NAME, /* basename of driver */ spectracom_start, /* start up driver */ NULL, /* shut down driver in standard way */ spectracom_poll, /* transmit poll message */ SPECTRACOM_CONTROL, /* fudge set/change notification */ NULL, /* initialize driver (not used) */ spectracom_timer /* called once per second */ }; /* * spectracom_start - open the devices and initialize data for processing */ static bool spectracom_start( int unit, struct peer *peer ) { struct spectracomunit *up; struct refclockproc *pp; int fd; char device[20]; /* * Open serial port. Use CLK line discipline, if available. */ snprintf(device, sizeof(device), DEVICE, unit); fd = refclock_open(peer->cfg.path ? peer->cfg.path : device, peer->cfg.baud ? peer->cfg.baud : SPEED232, LDISC_CLK); if (fd <= 0) /* coverity[leaked_handle] */ return false; /* * Allocate and initialize unit structure */ up = emalloc_zero(sizeof(*up)); pp = peer->procptr; pp->io.clock_recv = spectracom_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { close(fd); pp->io.fd = -1; free(up); return false; } pp->unitptr = up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy(&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_LF; return true; } /* * spectracom_receive - receive data from the serial interface */ static void spectracom_receive( struct recvbuf *rbufp ) { struct spectracomunit *up; struct refclockproc *pp; struct peer *peer; l_fp trtmp; /* arrival timestamp */ int tz; /* time zone */ int day, month; /* ddd conversion */ int temp; /* int temp */ char syncchar; /* synchronization indicator */ char qualchar; /* quality indicator */ char leapchar; /* leap indicator */ char dstchar; /* daylight/standard indicator */ char tmpchar; /* trashbin */ /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); /* * Note we get a buffer and timestamp for both a and , * but only the timestamp is retained. Note: in format 0 on * a Netclock/2 or upgraded 8170 the start bit is delayed 100 * +-50 us relative to the pps; however, on an unmodified 8170 * the start bit can be delayed up to 10 ms. In format 2 the * reading precision is only to the millisecond. Thus, unless * you have a PPS gadget and don't have to have the year, format * 0 provides the lowest jitter. * Save the timestamp of each in up->laststamp. Lines with * no characters occur for every , and for some s when * format 0 is used. Format 0 starts and ends each cycle with a * pair, format 2 starts each cycle with its only pair. * The preceding is the on-time character for both formats. * The timestamp provided with non-empty lines corresponds to * the following the timecode, which is ultimately not used * with format 0 and is used for the following timecode for * format 2. */ if (temp == 0) { if (up->prev_eol_cr) { DPRINT(2, ("spectracom: @ %s\n", prettydate(trtmp))); } else { up->laststamp = trtmp; DPRINT(2, ("spectracom: @ %s\n", prettydate(trtmp))); } up->prev_eol_cr = !up->prev_eol_cr; return; } pp->lencode = temp; pp->lastrec = up->laststamp; up->laststamp = trtmp; up->prev_eol_cr = true; DPRINT(2, ("spectracom: code @ %s\n" " using %s minus one char\n", prettydate(trtmp), prettydate(pp->lastrec))); if (pp->lastrec == 0) return; /* * We get down to business, check the timecode format and decode * its contents. This code uses the timecode length to determine * format 0, 2 or 3. If the timecode has invalid length or is * not in proper format, we declare bad format and exit. */ syncchar = qualchar = leapchar = dstchar = ' '; tz = 0; switch (pp->lencode) { case LENTYPE0: /* * Timecode format 0: "I ddd hh:mm:ss DTZ=nn" */ if (sscanf(pp->a_lastcode, "%c %3d %2d:%2d:%2d%c%cTZ=%2d", &syncchar, &pp->day, &pp->hour, &pp->minute, &pp->second, &tmpchar, &dstchar, &tz) == 8) { pp->nsec = 0; break; } goto bad_format; case LENTYPE2: /* * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD" */ if (sscanf(pp->a_lastcode, "%c%c %2d %3d %2d:%2d:%2d.%3ld %c", &syncchar, &qualchar, &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec, &leapchar) == 9) { pp->nsec *= 1000000; break; } goto bad_format; case LENTYPE3: /* * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#" * WARNING: Undocumented, and the on-time character # is * not yet handled correctly by this driver. It may be * as simple as compensating for an additional 1/960 s. */ if (sscanf(pp->a_lastcode, "0003%c %4d%2d%2d %2d%2d%2d+0000%c%c", &syncchar, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second, &dstchar, &leapchar) == 8) { pp->day = ymd2yd(pp->year, month, day); pp->nsec = 0; break; } goto bad_format; default: bad_format: /* * Unknown format: If dumping internal table, record * stats; otherwise, declare bad format. */ if (up->linect > 0) { up->linect--; record_clock_stats(peer, pp->a_lastcode); } else { refclock_report(peer, CEVNT_BADREPLY); } return; } /* * Decode synchronization, quality and leap characters. If * unsynchronized, set the leap bits accordingly and exit. * Otherwise, set the leap bits according to the leap character. * Once synchronized, the dispersion depends only on the * quality character. */ switch (qualchar) { case ' ': pp->disp = .001; pp->lastref = pp->lastrec; break; case 'A': pp->disp = .01; break; case 'B': pp->disp = .1; break; case 'C': pp->disp = .5; break; case 'D': pp->disp = sys_maxdisp; break; default: pp->disp = sys_maxdisp; refclock_report(peer, CEVNT_BADREPLY); break; } if (syncchar != ' ') pp->leap = LEAP_NOTINSYNC; else if (leapchar == 'L') pp->leap = LEAP_ADDSECOND; else pp->leap = LEAP_NOWARNING; /* * Process the new sample in the median filter and determine the * timecode timestamp, but only if the PPS is not in control. */ #ifdef HAVE_PPSAPI up->tcount++; if (peer->cfg.flags & FLAG_PPS) return; #endif /* HAVE_PPSAPI */ if (!refclock_process_f(pp, pp->fudgetime2)) refclock_report(peer, CEVNT_BADTIME); } /* * spectracom_timer - called once per second by the transmit procedure */ static void spectracom_timer( int unit, struct peer *peer ) { UNUSED_ARG(unit); struct spectracomunit *up; struct refclockproc *pp; char pollchar; /* character sent to clock */ #ifdef DEBUG l_fp now; #endif /* * Time to poll the clock. The Spectracom clock responds to a * 'T' by returning a timecode in the format(s) specified above. * Note there is no checking on state, since this may not be the * only customer reading the clock. Only one customer need poll * the clock; all others just listen in. */ pp = peer->procptr; up = pp->unitptr; if (up->linect > 0) pollchar = 'R'; else pollchar = 'T'; if (write(pp->io.fd, &pollchar, 1) != 1) refclock_report(peer, CEVNT_FAULT); #ifdef DEBUG get_systime(&now); if (debug) /* SPECIAL DEBUG */ printf("%c poll at %s\n", pollchar, prettydate(now)); #endif #ifdef HAVE_PPSAPI if (up->ppsapi_lit && refclock_catcher(peer, &up->ppsctl, pp->sloppyclockflag) > 0) { up->pcount++, peer->cfg.flags |= FLAG_PPS; peer->precision = PPS_PRECISION; } #endif /* HAVE_PPSAPI */ } /* * spectracom_poll - called by the transmit procedure */ static void spectracom_poll( int unit, struct peer *peer ) { struct spectracomunit *up; struct refclockproc *pp; UNUSED_ARG(unit); /* * Sweep up the samples received since the last poll. If none * are received, declare a timeout and keep going. */ pp = peer->procptr; up = pp->unitptr; pp->polls++; /* * If the monitor flag is set (flag4), we dump the internal * quality table at the first timecode beginning the day. */ if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour < (int)up->lasthour) up->linect = MONLIN; up->lasthour = (uint8_t)pp->hour; /* * Process median filter samples. If none received, declare a * timeout and keep going. */ #ifdef HAVE_PPSAPI if (up->pcount == 0) { peer->cfg.flags &= ~FLAG_PPS; peer->precision = PRECISION; } if (up->tcount == 0) { pp->coderecv = pp->codeproc; refclock_report(peer, CEVNT_TIMEOUT); return; } up->pcount = up->tcount = 0; #else /* HAVE_PPSAPI */ if (pp->coderecv == pp->codeproc) { refclock_report(peer, CEVNT_TIMEOUT); return; } #endif /* HAVE_PPSAPI */ refclock_receive(peer); record_clock_stats(peer, pp->a_lastcode); DPRINT(1, ("spectracom: timecode %d %s\n", pp->lencode, pp->a_lastcode)); } /* * spectracom_control - fudge parameters have been set or changed */ #ifdef HAVE_PPSAPI static void spectracom_control( int unit, const struct refclockstat *in_st, struct refclockstat *out_st, struct peer *peer ) { struct spectracomunit *up; struct refclockproc *pp; UNUSED_ARG(unit); UNUSED_ARG(in_st); UNUSED_ARG(out_st); pp = peer->procptr; up = pp->unitptr; if (!(pp->sloppyclockflag & CLK_FLAG1)) { if (!up->ppsapi_tried) return; up->ppsapi_tried = 0; if (!up->ppsapi_lit) return; peer->cfg.flags &= ~FLAG_PPS; peer->precision = PRECISION; time_pps_destroy(up->ppsctl.handle); up->ppsctl.handle = 0; up->ppsapi_lit = 0; return; } if (up->ppsapi_tried) return; /* * Light up the PPSAPI interface. */ up->ppsapi_tried = 1; if (refclock_ppsapi(pp->io.fd, &up->ppsctl)) { up->ppsapi_lit = 1; return; } msyslog(LOG_WARNING, "REFCLOCK %s flag1 1 but PPSAPI fails", refclock_name(peer)); } #endif /* HAVE_PPSAPI */ ntpsec-1.1.0+dfsg1/ntpd/ntp_packetstamp.c0000644000175000017500000001125613252364117020140 0ustar rlaagerrlaager/* * ntp_packetstamp.c - grubby platform-dependent details of packet timestamps * * One of our serious platform dependencies (things POSIX doesn't * specify a facility for) is isolated here. */ #include "config.h" #ifdef HAVE_SYS_IOCTL_H # include #endif #include "ntpd.h" #include "ntp_stdlib.h" #include "timespecops.h" /* We handle 2 flavors of timestamp: * SO_TIMESTAMPNS/SCM_TIMESTAMPNS Linux * SO_TIMESTAMP/SCM_TIMESTAMP FreeBSD, NetBSD, OpenBSD, Linux, macOS, * Solaris * * Linux supports both SO_TIMESTAMP and SO_TIMESTAMPNS so it's * important to check for SO_TIMESTAMPNS first to get the better accuracy. * * Note that the if/elif tests are done in several places. * It's important that they all check in the same order to * be consistent in case some systems support more than one. * * If SO_xxx exists, we assume that SCM_xxx does too. * All flavors assume the CMSG_xxx macros exist. * * FreeBSD has SO_BINTIME/SCM_BINTIME * It has better resolution, but it doesn't work for IPv6 * bintime documentation is at * http://phk.freebsd.dk/pubs/timecounter.pdf */ void enable_packetstamps( int fd, sockaddr_u * addr ) { const int on = 1; static bool once = false; #if defined (SO_TIMESTAMPNS) if (!once) { once = true; msyslog(LOG_INFO, "INIT: Using SO_TIMESTAMPNS"); } if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, (const void *)&on, sizeof(on))) msyslog(LOG_DEBUG, "ERR: setsockopt SO_TIMESTAMPNS on fails on address %s: %m", socktoa(addr)); else DPRINT(4, ("ERR: setsockopt SO_TIMESTAMPNS enabled on fd %d address %s\n", fd, socktoa(addr))); #elif defined(SO_TIMESTAMP) if (!once) { once = true; msyslog(LOG_INFO, "INIT: Using SO_TIMESTAMP"); } if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (const void*)&on, sizeof(on))) msyslog(LOG_DEBUG, "ERR: setsockopt SO_TIMESTAMP on fails on address %s: %m", socktoa(addr)); else DPRINT(4, ("setsockopt SO_TIMESTAMP enabled on fd %d address %s\n", fd, socktoa(addr))); #else # error "Can't get packet timestamp" #endif } /* * extract timestamps from control message buffer */ l_fp fetch_packetstamp( struct recvbuf * rb, struct msghdr * msghdr, l_fp ts ) { struct cmsghdr * cmsghdr; #if defined(SO_TIMESTAMPNS) struct timespec * tsp; #elif defined(SO_TIMESTAMP) struct timeval * tvp; #endif unsigned long ticks; double fuzz; l_fp lfpfuzz; l_fp nts = 0; /* network time stamp */ #ifdef ENABLE_DEBUG_TIMING l_fp dts; #endif #ifndef ENABLE_DEBUG_TIMING UNUSED_ARG(rb); #endif /* There should be only one cmsg. */ cmsghdr = CMSG_FIRSTHDR(msghdr); if (NULL == cmsghdr) { DPRINT(4, ("fetch_timestamp: can't find timestamp\n")); msyslog(LOG_ERR, "ERR: fetch_timestamp: no msghdrs, %s", socktoa(&rb->recv_srcadr)); exit(2); /* return ts; ** Kludge to use time from select. */ } #if defined(SO_TIMESTAMPNS) if (SCM_TIMESTAMPNS != cmsghdr->cmsg_type) { #elif defined(SO_TIMESTAMP) if (SCM_TIMESTAMP != cmsghdr->cmsg_type) { #else # error "Can't get packet timestamp" #endif DPRINT(4, ("fetch_timestamp: strange control message 0x%x\n", (unsigned)cmsghdr->cmsg_type)); msyslog(LOG_ERR, "ERR: fetch_timestamp: strange control message 0x%x", (unsigned)cmsghdr->cmsg_type); exit(2); /* Could loop and skip strange types. */ /* cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr); */ } /* cmsghdr now points to a timestamp slot */ #if defined(SO_TIMESTAMPNS) tsp = (struct timespec *)CMSG_DATA(cmsghdr); if (sys_tick > measured_tick && sys_tick > S_PER_NS) { ticks = (unsigned long) ((tsp->tv_nsec * S_PER_NS) / sys_tick); tsp->tv_nsec = (long) (ticks * NS_PER_S * sys_tick); } DPRINT(4, ("fetch_timestamp: system nsec network time stamp: %ld.%09ld\n", tsp->tv_sec, tsp->tv_nsec)); nts = tspec_stamp_to_lfp(*tsp); #elif defined(SO_TIMESTAMP) tvp = (struct timeval *)CMSG_DATA(cmsghdr); if (sys_tick > measured_tick && sys_tick > S_PER_NS) { ticks = (unsigned long) ((tvp->tv_usec * S_PER_NS) / sys_tick); tvp->tv_usec = (long)(ticks * US_PER_S * sys_tick); } DPRINT(4, ("fetch_timestamp: system usec network time stamp: %jd.%06ld\n", (intmax_t)tvp->tv_sec, (long)tvp->tv_usec)); nts = tspec_stamp_to_lfp(tval_to_tspec(*tvp)); #else # error "Can't get packet timestamp" #endif fuzz = ntp_random() * 2. / FRAC * sys_fuzz; lfpfuzz = dtolfp(fuzz); nts += lfpfuzz; #ifdef ENABLE_DEBUG_TIMING dts = ts; dts -= nts; collect_timing(rb, "input processing delay", 1, dts); DPRINT(4, ("fetch_timestamp: timestamp delta: %s (incl. fuzz)\n", lfptoa(dts, 9))); #endif /* ENABLE_DEBUG_TIMING */ ts = nts; return ts; } // end ntpsec-1.1.0+dfsg1/ntpd/refclock_jjy.c0000644000175000017500000043651613252364117017421 0ustar rlaagerrlaager/* * refclock_jjy - clock driver for JJY receivers * * WARNING: Some modes of this driver depend on the system clock for * year disambiguation. They will thus not be usable for recovery if * the system clock is trashed. */ /**********************************************************************/ /* */ /* Copyright (C) 2001-2015, Takao Abe. All rights reserved. */ /* */ /* SPDX-License-Identifier: BSD-3-clause */ /* */ /* This driver is developed in my private time, and is opened as */ /* voluntary contributions for the NTP. */ /* The manufacturer of the JJY receiver has not participated in */ /* a development of this driver. */ /* The manufacturer does not warrant anything about this driver, */ /* and is not liable for anything about this driver. */ /* */ /**********************************************************************/ /* */ /* Author Takao Abe */ /* Email takao_abe@xurb.jp */ /* Homepage http://www.bea.hi-ho.ne.jp/abetakao/ */ /* */ /* The email address abetakao@bea.hi-ho.ne.jp is never read */ /* from 2010, because a few filtering rule are provided by the */ /* "hi-ho.ne.jp", and lots of spam mail are reached. */ /* New email address for supporting the refclock_jjy is */ /* takao_abe@xurb.jp */ /* */ /**********************************************************************/ /* */ /* History */ /* */ /* 2001/07/15 */ /* [New] Support the Tristate Ltd. JJY receiver */ /* */ /* 2001/08/04 */ /* [Change] Log to clockstats even if bad reply */ /* [Fix] PRECISION = (-3) (about 100 ms) */ /* [Add] Support the C-DEX Co.Ltd. JJY receiver */ /* */ /* 2001/12/04 */ /* [Fix] C-DEX JST2000 ( fukusima@goto.info.waseda.ac.jp ) */ /* */ /* 2002/07/12 */ /* [Fix] Portability for FreeBSD ( patched by the user ) */ /* */ /* 2004/10/31 */ /* [Change] Command send timing for the Tristate Ltd. JJY receiver */ /* JJY-01 ( Firmware version 2.01 ) */ /* Thanks to Andy Taki for testing under FreeBSD */ /* */ /* 2004/11/28 */ /* [Add] Support the Echo Keisokuki LT-2000 receiver */ /* */ /* 2006/11/04 */ /* [Fix] C-DEX JST2000 */ /* Thanks to Hideo Kuramatsu for the patch */ /* */ /* 2009/04/05 */ /* [Add] Support the CITIZEN T.I.C JJY-200 receiver */ /* */ /* 2010/11/20 */ /* [Change] Bug 1618 ( Harmless ) */ /* Code clean up ( Remove unreachable codes ) in */ /* jjy_start() */ /* [Change] Change clockstats format of the Tristate JJY01/02 */ /* Issues more command to get the status of the receiver */ /* when "flag1 1" is specified */ /* ( DATE,STIM -> DCST,STUS,DATE,STIM ) */ /* */ /* 2011/04/30 */ /* [Add] Support the Tristate Ltd. TS-GPSclock-01 */ /* */ /* 2015/03/29 */ /* [Add] Support the Telephone JJY */ /* [Change] Split the start up routine into each JJY receivers. */ /* Change raw data internal bufferring process */ /* Change over midnight handling of TS-JJY01 and TS-GPS01 */ /* to put DATE command between before and after TIME's. */ /* Unify the writing clockstats of all JJY receivers. */ /* */ /* 2015/05/15 */ /* [Add] Support the SEIKO TIME SYSTEMS TDC-300 */ /* */ /* 2015/05/08 */ /* [Fix] C-DEX JST2000 */ /* Thanks to Mr. Kuramatsu for the report and the patch. */ /* */ /* 2017/04/30 */ /* [Change] Avoid a wrong report of the coverity static analysis */ /* tool. ( The code is harmless and has no bug. ) */ /* teljjy_conn_send() */ /* */ /**********************************************************************/ #include "config.h" #include #include #include #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_tty.h" #include "ntp_refclock.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include "ntp_debug.h" /**********************************************************************/ /* * Interface definitions */ #define DEVICE "/dev/jjy%d" /* device name and unit */ #define SPEED232_TRISTATE_JJY01 B9600 /* UART speed (9600 baud) */ #define SPEED232_CDEX_JST2000 B9600 /* UART speed (9600 baud) */ #define SPEED232_ECHOKEISOKUKI_LT2000 B9600 /* UART speed (9600 baud) */ #define SPEED232_CITIZENTIC_JJY200 B4800 /* UART speed (4800 baud) */ #define SPEED232_TRISTATE_GPSCLOCK01 B38400 /* USB speed (38400 baud) */ #define SPEED232_SEIKO_TIMESYS_TDC_300 B2400 /* UART speed (2400 baud) */ #define SPEED232_TELEPHONE B2400 /* UART speed (4800 baud) */ #define REFID "JJY" /* reference ID */ #define NAME "JJY" /* shortname */ #define DESCRIPTION "JJY Receiver" #define PRECISION (-3) /* precision assumed (about 100 ms) */ /* * JJY unit control structure */ struct jjyRawDataBreak { const char *pString ; int iLength ; } ; #define MAX_TIMESTAMP 6 #define MAX_RAWBUF 100 #define MAX_LOOPBACK 5 struct jjyunit { /* Set up by the function "jjy_start_xxxxxxxx" */ char unittype ; /* UNITTYPE_XXXXXXXXXX */ short operationmode ; /* Echo Keisokuki LT-2000 */ int linespeed ; /* SPEED232_XXXXXXXXXX */ short linediscipline ; /* LDISC_CLK or LDISC_RAW */ /* Receiving data */ bool bInitError ; /* Set by jjy_start if any error during initialization */ short iProcessState ; /* JJY_PROCESS_STATE_XXXXXX */ bool bReceiveFlag ; /* Set and reset by jjy_receive */ bool bLineError ; /* Reset by jjy_poll / Set by jjy_receive_xxxxxxxx*/ short iCommandSeq ; /* 0:Idle Non-Zero:Issued */ short iReceiveSeq ; int iLineCount ; int year, month, day, hour, minute, second, msecond ; int leapsecond ; int iTimestampCount ; /* TS-JJY01, TS-GPS01, Telephone-JJY */ int iTimestamp [ MAX_TIMESTAMP ] ; /* Serial second ( 0 - 86399 ) */ /* LDISC_RAW only */ char sRawBuf [ MAX_RAWBUF ] ; int iRawBufLen ; struct jjyRawDataBreak *pRawBreak ; bool bWaitBreakString ; char sLineBuf [ MAX_RAWBUF ] ; int iLineBufLen ; char sTextBuf [ MAX_RAWBUF ] ; int iTextBufLen ; bool bSkipCntrlCharOnly ; /* Telephone JJY auto measurement of the loopback delay */ bool bLoopbackMode ; short iLoopbackCount ; struct timespec sendTime[MAX_LOOPBACK], delayTime[MAX_LOOPBACK] ; bool bLoopbackTimeout[MAX_LOOPBACK] ; short iLoopbackValidCount ; /* Telephone JJY timer */ short iTeljjySilentTimer ; short iTeljjyStateTimer ; /* Telephone JJY control finite state machine */ short iClockState ; short iClockEvent ; short iClockCommandSeq ; /* Modem timer */ short iModemSilentCount ; short iModemSilentTimer ; short iModemStateTimer ; /* Modem control finite state machine */ short iModemState ; short iModemEvent ; short iModemCommandSeq ; }; #define UNITTYPE_TRISTATE_JJY01 1 #define UNITTYPE_CDEX_JST2000 2 #define UNITTYPE_ECHOKEISOKUKI_LT2000 3 #define UNITTYPE_CITIZENTIC_JJY200 4 #define UNITTYPE_TRISTATE_GPSCLOCK01 5 #define UNITTYPE_SEIKO_TIMESYS_TDC_300 6 #define UNITTYPE_TELEPHONE 100 #define JJY_PROCESS_STATE_IDLE 0 #define JJY_PROCESS_STATE_POLL 1 #define JJY_PROCESS_STATE_RECEIVE 2 #define JJY_PROCESS_STATE_DONE 3 #define JJY_PROCESS_STATE_ERROR 4 /**********************************************************************/ /* * Function calling structure * * jjy_start * |-- jjy_start_tristate_jjy01 * |-- jjy_start_cdex_jst2000 * |-- jjy_start_echokeisokuki_lt2000 * |-- jjy_start_citizentic_jjy200 * |-- jjy_start_tristate_gpsclock01 * |-- jjy_start_seiko_tsys_tdc_300 * |-- jjy_start_telephone * * jjy_poll * |-- jjy_poll_tristate_jjy01 * |-- jjy_poll_cdex_jst2000 * |-- jjy_poll_echokeisokuki_lt2000 * |-- jjy_poll_citizentic_jjy200 * |-- jjy_poll_tristate_gpsclock01 * |-- jjy_poll_seiko_tsys_tdc_300 * |-- jjy_poll_telephone * |-- teljjy_control * |-- teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * |-- modem_connect * |-- modem_control * |-- modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * * jjy_receive * | * |-- jjy_receive_tristate_jjy01 * | |-- jjy_synctime * |-- jjy_receive_cdex_jst2000 * | |-- jjy_synctime * |-- jjy_receive_echokeisokuki_lt2000 * | |-- jjy_synctime * |-- jjy_receive_citizentic_jjy200 * | |-- jjy_synctime * |-- jjy_receive_tristate_gpsclock01 * | |-- jjy_synctime * |-- jjy_receive_seiko_tsys_tdc_300 * | |-- jjy_synctime * |-- jjy_receive_telephone * |-- modem_receive * | |-- modem_control * | |-- modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * |-- teljjy_control * |-- teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * |-- jjy_synctime * |-- modem_disconnect * |-- modem_control * |-- modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * * jjy_timer * |-- jjy_timer_telephone * |-- modem_timer * | |-- modem_control * | |-- modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * |-- teljjy_control * |-- teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * |-- modem_disconnect * |-- modem_control * |-- modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. ) * * Function prototypes */ static bool jjy_start (int, struct peer *); static int jjy_start_tristate_jjy01 (int, struct peer *, struct jjyunit *); static int jjy_start_cdex_jst2000 (int, struct peer *, struct jjyunit *); static int jjy_start_echokeisokuki_lt2000 (int, struct peer *, struct jjyunit *); static int jjy_start_citizentic_jjy200 (int, struct peer *, struct jjyunit *); static int jjy_start_tristate_gpsclock01 (int, struct peer *, struct jjyunit *); static int jjy_start_seiko_tsys_tdc_300 (int, struct peer *, struct jjyunit *); static bool jjy_start_telephone (int, struct peer *, struct jjyunit *); static void jjy_poll (int, struct peer *); static void jjy_poll_tristate_jjy01 (int, struct peer *); static void jjy_poll_cdex_jst2000 (int, struct peer *); static void jjy_poll_echokeisokuki_lt2000 (int, struct peer *); static void jjy_poll_citizentic_jjy200 (int, struct peer *); static void jjy_poll_tristate_gpsclock01 (int, struct peer *); static void jjy_poll_seiko_tsys_tdc_300 (int, struct peer *); static void jjy_poll_telephone (int, struct peer *); static void jjy_receive (struct recvbuf *); static int jjy_receive_tristate_jjy01 (struct recvbuf *); static int jjy_receive_cdex_jst2000 (struct recvbuf *); static int jjy_receive_echokeisokuki_lt2000 (struct recvbuf *); static int jjy_receive_citizentic_jjy200 (struct recvbuf *); static int jjy_receive_tristate_gpsclock01 (struct recvbuf *); static int jjy_receive_seiko_tsys_tdc_300 (struct recvbuf *); static int jjy_receive_telephone (struct recvbuf *); static void jjy_timer (int, struct peer *); static void jjy_timer_telephone (int, struct peer *); static void jjy_synctime ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static void jjy_write_clockstats ( struct peer *, int, const char* ) ; static int getRawDataBreakPosition ( struct jjyunit *, int ) ; static short getModemState ( struct jjyunit * ) ; static int isModemStateConnect ( short ) ; static int isModemStateDisconnect ( short ) ; static int isModemStateTimerOn ( struct jjyunit * ) ; static void modem_connect ( int, struct peer * ) ; static void modem_disconnect ( int, struct peer * ) ; static int modem_receive ( struct recvbuf * ) ; static void modem_timer ( int, struct peer * ); static void printableString ( char*, int, const char*, int ) ; /* * Transfer vector */ struct refclock refclock_jjy = { NAME, /* basename of driver */ jjy_start, /* start up driver */ NULL, /* shutdown driver in standard way */ jjy_poll, /* transmit poll message */ NULL, /* control - not used */ NULL, /* init - not used */ jjy_timer /* 1 second interval timer */ }; /* * Local constants definition */ /* gcc 7 says 100 is too short */ #define MAX_LOGTEXT 192 /* Local constants definition for the return code of the jjy_receive_xxxxxxxx */ #define JJY_RECEIVE_DONE 0 #define JJY_RECEIVE_SKIP 1 #define JJY_RECEIVE_UNPROCESS 2 #define JJY_RECEIVE_WAIT 3 #define JJY_RECEIVE_ERROR 4 /* Local constants definition for the 2nd parameter of the jjy_write_clockstats */ /* #define JJY_CLOCKSTATS_MARK_NONE 0 UNUSED */ #define JJY_CLOCKSTATS_MARK_JJY 1 #define JJY_CLOCKSTATS_MARK_SEND 2 #define JJY_CLOCKSTATS_MARK_RECEIVE 3 #define JJY_CLOCKSTATS_MARK_INFORMATION 4 #define JJY_CLOCKSTATS_MARK_ATTENTION 5 #define JJY_CLOCKSTATS_MARK_WARNING 6 #define JJY_CLOCKSTATS_MARK_ERROR 7 #define JJY_CLOCKSTATS_MARK_BUG 8 /* Local constants definition for the clockstats messages */ #define JJY_CLOCKSTATS_MESSAGE_ECHOBACK "* Echoback" #define JJY_CLOCKSTATS_MESSAGE_IGNORE_REPLY "* Ignore replay : [%s]" #define JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2 "* Over midnight : timestamp=%d, %d" /* UNUSED * #define JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_3 "* Over midnight : timestamp=%d, %d, %d" */ #define JJY_CLOCKSTATS_MESSAGE_TIMESTAMP_UNSURE "* Unsure timestamp : %s" #define JJY_CLOCKSTATS_MESSAGE_LOOPBACK_DELAY "* Loopback delay : %d.%03d mSec." #define JJY_CLOCKSTATS_MESSAGE_DELAY_ADJUST "* Delay adjustment : %d mSec. ( valid=%hd/%d )" #define JJY_CLOCKSTATS_MESSAGE_DELAY_UNADJUST "* Delay adjustment : None ( valid=%hd/%d )" #define JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY "# Unexpected reply : [%s]" #define JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH "# Invalid length : length=%d" #define JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY "# Too many reply : count=%d" #define JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY "# Invalid reply : [%s]" #define JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2 "# Slow reply : timestamp=%d, %d" /* UNUSED * #define JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_3 "# Slow reply : timestamp=%d, %d, %d" */ #define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE "# Invalid date : rc=%d year=%d month=%d day=%d" #define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME "# Invalid time : rc=%d hour=%d minute=%d second=%d" #define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME "# Invalid time : rc=%d year=%d month=%d day=%d hour=%d minute=%d second=%d" #define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_LEAP "# Invalid leap : leapsecond=[%s]" /* UNUSED * #define JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_STATUS "# Invalid status : status=[%s]" */ /* Debug print macro */ #ifdef DEBUG #define DEBUG_PRINTF_JJY_RECEIVE(sFunc,iLen) { if ( debug ) { printf ( "refclock_jjy.c : %s : iProcessState=%d bLineError=%d iCommandSeq=%d iLineCount=%d iTimestampCount=%d iLen=%d\n", sFunc, up->iProcessState, up->bLineError, up->iCommandSeq, up->iLineCount, up->iTimestampCount, iLen ) ; } } #else #define DEBUG_PRINTF_JJY_RECEIVE(sFunc,iLen) #endif /**************************************************************************************************/ /* jjy_start - open the devices and initialize data for processing */ /**************************************************************************************************/ static bool jjy_start ( int unit, struct peer *peer ) { struct refclockproc *pp ; struct jjyunit *up ; int rc ; int fd ; char sDeviceName [ sizeof(DEVICE) + 10 ], sLog [ MAX_LOGTEXT ] ; DPRINT(1, ("refclock_jjy.c: jjy_start: %s mode=%u dev=%s unit=%d\n", socktoa(&peer->srcadr), peer->cfg.mode, DEVICE, unit )) ; /* Allocate memory for the unit structure */ up = emalloc_zero( sizeof(*up) ) ; if ( up == NULL ) { msyslog ( LOG_ERR, "refclock_jjy.c : jjy_start : emalloc" ) ; return false ; } up->bInitError = false ; up->iProcessState = JJY_PROCESS_STATE_IDLE ; up->bReceiveFlag = false ; up->iCommandSeq = 0 ; up->iLineCount = 0 ; up->iTimestampCount = 0 ; up->bWaitBreakString = false ; up->iRawBufLen = up->iLineBufLen = up->iTextBufLen = 0 ; up->bSkipCntrlCharOnly = true ; /* Set up the device name */ snprintf( sDeviceName, sizeof(sDeviceName), DEVICE, unit ) ; snprintf(sLog, sizeof(sLog), "subtype=%u dev=%s", peer->cfg.mode, sDeviceName ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ; /* * peer->cfg.mode is a subtype number specified by "jjy subtype N" in the ntp.conf */ switch ( peer->cfg.mode ) { case 0 : case 1 : rc = jjy_start_tristate_jjy01 ( unit, peer, up ) ; break ; case 2 : rc = jjy_start_cdex_jst2000 ( unit, peer, up ) ; break ; case 3 : rc = jjy_start_echokeisokuki_lt2000 ( unit, peer, up ) ; break ; case 4 : rc = jjy_start_citizentic_jjy200 ( unit, peer, up ) ; break ; case 5 : rc = jjy_start_tristate_gpsclock01 ( unit, peer, up ) ; break ; case 6 : rc = jjy_start_seiko_tsys_tdc_300 ( unit, peer, up ) ; break ; case 100 : rc = jjy_start_telephone ( unit, peer, up ) ; break ; default : if ( 101 <= peer->cfg.mode && peer->cfg.mode <= 180 ) { rc = jjy_start_telephone ( unit, peer, up ) ; } else { msyslog (LOG_ERR, "JJY receiver [ %s subtype %u ] : Unsupported mode", socktoa(&peer->srcadr), peer->cfg.mode ) ; free ( (void*) up ) ; return false ; } } if ( rc != 0 ) { msyslog(LOG_ERR, "REFCLOCK: JJY receiver [ %s subtype %u ] : Initialize error", socktoa(&peer->srcadr), peer->cfg.mode ) ; free ( (void*) up ) ; return false ; } /* Open the device */ fd = refclock_open ( sDeviceName, (unsigned int)up->linespeed, (unsigned int)up->linediscipline ) ; if ( fd <= 0 ) { free ( (void*) up ) ; /* coverity[leaked_handle] */ return false ; } /* * Initialize variables */ pp = peer->procptr ; pp->clockname = NAME; pp->clockdesc = DESCRIPTION ; pp->unitptr = up ; pp->io.clock_recv = jjy_receive ; pp->io.srcclock = peer ; pp->io.datalen = 0 ; pp->io.fd = fd ; if ( ! io_addclock(&pp->io) ) { close ( fd ) ; pp->io.fd = -1 ; free ( up ) ; pp->unitptr = NULL ; return false ; } memcpy( (char*)&pp->refid, REFID, strlen(REFID) ) ; peer->precision = PRECISION ; snprintf( sLog, sizeof(sLog), "minpoll=%d maxpoll=%d", peer->cfg.minpoll, peer->cfg.maxpoll ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ; return true; } /**************************************************************************************************/ /* jjy_receive - receive data from the serial interface */ /**************************************************************************************************/ static void jjy_receive ( struct recvbuf *rbufp ) { #ifdef DEBUG static const char *sFunctionName = "jjy_receive" ; #endif struct jjyunit *up ; struct refclockproc *pp ; struct peer *peer; l_fp tRecvTimestamp; /* arrival timestamp */ int rc ; char *pBuf, sLogText [ MAX_LOGTEXT ] ; int i, j, iReadRawBuf, iBreakPosition ; /* * Initialize pointers and read the timecode and timestamp */ peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; /* * Get next input line */ if ( up->linediscipline == LDISC_RAW ) { pp->lencode = (int)refclock_gtraw ( rbufp, pp->a_lastcode, BMAX-1, &tRecvTimestamp ) ; /* 3rd argument can be BMAX, but the coverity scan tool claim "Memory - corruptions (OVERRUN)" */ /* "a_lastcode" is defined as "char a_lastcode[BMAX]" in the ntp_refclock.h */ /* To avoid its claim, pass the value BMAX-1. */ /* * Append received characters to temporary buffer */ for ( i = 0 ; i < pp->lencode && up->iRawBufLen < MAX_RAWBUF - 2 ; i ++ , up->iRawBufLen ++ ) { up->sRawBuf[up->iRawBufLen] = pp->a_lastcode[i] ; } up->sRawBuf[up->iRawBufLen] = 0 ; } else { pp->lencode = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ; } #ifdef DEBUG printf( "\nrefclock_jjy.c : %s : Len=%d ", sFunctionName, pp->lencode ) ; for ( i = 0 ; i < pp->lencode ; i ++ ) { if ( iscntrl( pp->a_lastcode[i] & 0x7F ) ) { printf("", (unsigned)(pp->a_lastcode[i] & 0xFF)); } else { printf( "%c", pp->a_lastcode[i] ) ; } } printf( "\n" ) ; #endif /* * The reply with gives a blank line */ if ( pp->lencode == 0 ) return ; /* * Receiving data is not expected */ if ( up->iProcessState == JJY_PROCESS_STATE_IDLE || up->iProcessState == JJY_PROCESS_STATE_DONE || up->iProcessState == JJY_PROCESS_STATE_ERROR ) { /* Discard received data */ up->iRawBufLen = 0 ; DPRINT(1, ( "refclock_jjy.c : %s : Discard received data\n", sFunctionName )) ; return ; } /* * We get down to business */ pp->lastrec = tRecvTimestamp ; up->iLineCount ++ ; up->iProcessState = JJY_PROCESS_STATE_RECEIVE ; up->bReceiveFlag = true ; iReadRawBuf = 0 ; iBreakPosition = up->iRawBufLen - 1 ; for ( ; up->iProcessState == JJY_PROCESS_STATE_RECEIVE ; ) { if ( up->linediscipline == LDISC_RAW ) { if ( up->bWaitBreakString ) { iBreakPosition = getRawDataBreakPosition( up, iReadRawBuf ) ; if ( iBreakPosition == -1 ) { /* Break string have not come yet */ if ( up->iRawBufLen < MAX_RAWBUF - 2 || iReadRawBuf > 0 ) { /* Temporary buffer is not full */ break ; } else { /* Temporary buffer is full */ iBreakPosition = up->iRawBufLen - 1 ; } } } else { iBreakPosition = up->iRawBufLen - 1 ; } /* Copy characters from temporary buffer to process buffer */ up->iLineBufLen = up->iTextBufLen = 0 ; for ( i = iReadRawBuf ; i <= iBreakPosition ; i ++ ) { /* Copy all characters */ up->sLineBuf[up->iLineBufLen] = up->sRawBuf[i] ; up->iLineBufLen ++ ; /* Copy printable characters */ if ( ! iscntrl( (int)up->sRawBuf[i] ) ) { up->sTextBuf[up->iTextBufLen] = up->sRawBuf[i] ; up->iTextBufLen ++ ; } } up->sLineBuf[up->iLineBufLen] = 0 ; up->sTextBuf[up->iTextBufLen] = 0 ; #ifdef DEBUG printf( "refclock_jjy.c : %s : up->iLineBufLen=%d up->iTextBufLen=%d\n", sFunctionName, up->iLineBufLen, up->iTextBufLen ) ; #endif if ( up->bSkipCntrlCharOnly && up->iTextBufLen == 0 ) { #ifdef DEBUG printf( "refclock_jjy.c : %s : Skip cntrl char only : up->iRawBufLen=%d iReadRawBuf=%d iBreakPosition=%d\n", sFunctionName, up->iRawBufLen, iReadRawBuf, iBreakPosition ) ; #endif if ( iBreakPosition + 1 < up->iRawBufLen ) { iReadRawBuf = iBreakPosition + 1 ; continue ; } else { break ; } } } if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sLineBuf ; } else { pBuf = pp->a_lastcode ; } strlcpy( sLogText, pBuf, sizeof(sLogText) ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_RECEIVE, sLogText ) ; switch ( up->unittype ) { case UNITTYPE_TRISTATE_JJY01 : rc = jjy_receive_tristate_jjy01 ( rbufp ) ; break ; case UNITTYPE_CDEX_JST2000 : rc = jjy_receive_cdex_jst2000 ( rbufp ) ; break ; case UNITTYPE_ECHOKEISOKUKI_LT2000 : rc = jjy_receive_echokeisokuki_lt2000 ( rbufp ) ; break ; case UNITTYPE_CITIZENTIC_JJY200 : rc = jjy_receive_citizentic_jjy200 ( rbufp ) ; break ; case UNITTYPE_TRISTATE_GPSCLOCK01 : rc = jjy_receive_tristate_gpsclock01 ( rbufp ) ; break ; case UNITTYPE_SEIKO_TIMESYS_TDC_300 : rc = jjy_receive_seiko_tsys_tdc_300 ( rbufp ) ; break ; case UNITTYPE_TELEPHONE : rc = jjy_receive_telephone ( rbufp ) ; break ; default : rc = JJY_RECEIVE_ERROR ; break ; } switch ( rc ) { case JJY_RECEIVE_DONE : case JJY_RECEIVE_SKIP : up->iProcessState = JJY_PROCESS_STATE_DONE ; break ; case JJY_RECEIVE_ERROR : up->iProcessState = JJY_PROCESS_STATE_ERROR ; break ; default : break ; } if ( up->linediscipline == LDISC_RAW ) { if ( rc == JJY_RECEIVE_UNPROCESS ) { break ; } iReadRawBuf = iBreakPosition + 1 ; if ( iReadRawBuf >= up->iRawBufLen ) { /* Processed all received data */ break ; } } if ( up->linediscipline == LDISC_CLK ) { break ; } } if ( up->linediscipline == LDISC_RAW && iReadRawBuf > 0 ) { for ( i = 0, j = iReadRawBuf ; j < up->iRawBufLen ; i ++, j++ ) { up->sRawBuf[i] = up->sRawBuf[j] ; } up->iRawBufLen -= iReadRawBuf ; if ( up->iRawBufLen < 0 ) { up->iRawBufLen = 0 ; } } up->bReceiveFlag = false ; } /**************************************************************************************************/ static int getRawDataBreakPosition ( struct jjyunit *up, int iStart ) { int i, j ; if ( iStart >= up->iRawBufLen ) { #ifdef DEBUG printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=-1\n", iStart ) ; #endif return -1 ; } for ( i = iStart ; i < up->iRawBufLen ; i ++ ) { for ( j = 0 ; up->pRawBreak[j].pString != NULL ; j ++ ) { if ( i + up->pRawBreak[j].iLength <= up->iRawBufLen ) { if ( strncmp( up->sRawBuf + i, up->pRawBreak[j].pString, (size_t)up->pRawBreak[j].iLength ) == 0 ) { #ifdef DEBUG printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=%d\n", iStart, i + up->pRawBreak[j].iLength - 1 ) ; #endif return i + up->pRawBreak[j].iLength - 1 ; } } } } #ifdef DEBUG printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=-1\n", iStart ) ; #endif return -1 ; } /**************************************************************************************************/ /* jjy_poll - called by the transmit procedure */ /**************************************************************************************************/ static void jjy_poll ( int unit, struct peer *peer ) { char sLog [ MAX_LOGTEXT ], sReach [ 9 ] ; struct jjyunit *up; struct refclockproc *pp; pp = peer->procptr; up = pp->unitptr ; if ( up->bInitError ) { jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, "Ignore polling because of error during initializing" ) ; return ; } if ( pp->polls > 0 && up->iLineCount == 0 ) { /* * No reply for last command */ refclock_report ( peer, CEVNT_TIMEOUT ) ; } pp->polls ++ ; sReach[0] = peer->reach & 0x80 ? '1' : '0' ; sReach[1] = peer->reach & 0x40 ? '1' : '0' ; sReach[2] = peer->reach & 0x20 ? '1' : '0' ; sReach[3] = peer->reach & 0x10 ? '1' : '0' ; sReach[4] = peer->reach & 0x08 ? '1' : '0' ; sReach[5] = peer->reach & 0x04 ? '1' : '0' ; sReach[6] = peer->reach & 0x02 ? '1' : '0' ; sReach[7] = 0 ; /* This poll */ sReach[8] = 0 ; snprintf(sLog, sizeof(sLog), "polls=%lu reach=%s", pp->polls, sReach); jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ATTENTION, sLog ) ; up->iProcessState = JJY_PROCESS_STATE_POLL ; up->iCommandSeq = 0 ; up->iReceiveSeq = 0 ; up->iLineCount = 0 ; up->bLineError = false ; up->iRawBufLen = 0 ; switch ( up->unittype ) { case UNITTYPE_TRISTATE_JJY01 : jjy_poll_tristate_jjy01 ( unit, peer ) ; break ; case UNITTYPE_CDEX_JST2000 : jjy_poll_cdex_jst2000 ( unit, peer ) ; break ; case UNITTYPE_ECHOKEISOKUKI_LT2000 : jjy_poll_echokeisokuki_lt2000 ( unit, peer ) ; break ; case UNITTYPE_CITIZENTIC_JJY200 : jjy_poll_citizentic_jjy200 ( unit, peer ) ; break ; case UNITTYPE_TRISTATE_GPSCLOCK01 : jjy_poll_tristate_gpsclock01 ( unit, peer ) ; break ; case UNITTYPE_SEIKO_TIMESYS_TDC_300 : jjy_poll_seiko_tsys_tdc_300 ( unit, peer ) ; break ; case UNITTYPE_TELEPHONE : jjy_poll_telephone ( unit, peer ) ; break ; default : break ; } } /**************************************************************************************************/ /* jjy_timer - called at one-second intervals */ /**************************************************************************************************/ static void jjy_timer ( int unit, struct peer *peer ) { struct refclockproc *pp ; struct jjyunit *up ; DPRINT(1, ( "refclock_jjy.c : jjy_timer\n" )) ; pp = peer->procptr ; up = pp->unitptr ; if ( up->bReceiveFlag ) { DPRINT(1, ( "refclock_jjy.c : jjy_timer : up->bReceiveFlag= true : Timer skipped.\n" )) ; return ; } switch ( up->unittype ) { case UNITTYPE_TELEPHONE : jjy_timer_telephone ( unit, peer ) ; break ; default : break ; } } /**************************************************************************************************/ /* jjy_synctime */ /**************************************************************************************************/ static void jjy_synctime ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { char sLog [ MAX_LOGTEXT ], cStatus ; const char *pStatus ; pp->year = up->year ; pp->day = ymd2yd( up->year, up->month, up->day ) ; pp->hour = up->hour ; pp->minute = up->minute ; pp->second = up->second ; pp->nsec = up->msecond * 1000000 ; /* * JST to UTC */ pp->hour -= 9 ; if ( pp->hour < 0 ) { pp->hour += 24 ; pp->day -- ; if ( pp->day < 1 ) { pp->year -- ; pp->day = ymd2yd( pp->year, 12, 31 ) ; } } /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if ( ! refclock_process( pp ) ) { refclock_report( peer, CEVNT_BADTIME ) ; return ; } pp->lastref = pp->lastrec ; refclock_receive( peer ) ; /* * Write into the clockstats file */ snprintf ( sLog, sizeof(sLog), "%04d/%02d/%02d %02d:%02d:%02d.%03d JST ( %04d/%03d %02d:%02d:%02d.%03d UTC )", up->year, up->month, up->day, up->hour, up->minute, up->second, up->msecond, pp->year, pp->day, pp->hour, pp->minute, pp->second, (int)(pp->nsec/1000000) ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ATTENTION, sLog ) ; cStatus = ' ' ; pStatus = "" ; switch ( peer->status ) { case 0 : cStatus = ' ' ; pStatus = "Reject" ; break ; case 1 : cStatus = 'x' ; pStatus = "FalseTick" ; break ; case 2 : cStatus = '.' ; pStatus = "Excess" ; break ; case 3 : cStatus = '-' ; pStatus = "Outlier" ; break ; case 4 : cStatus = '+' ; pStatus = "Candidate" ; break ; case 5 : cStatus = '#' ; pStatus = "Selected" ; break ; case 6 : cStatus = '*' ; pStatus = "Sys.Peer" ; break ; case 7 : cStatus = 'o' ; pStatus = "PPS.Peer" ; break ; default : break ; } snprintf ( sLog, sizeof(sLog), "status %d [%c] %s : offset %3.3f mSec. : jitter %3.3f mSec.", peer->status, cStatus, pStatus, peer->offset * 1000, peer->jitter * 1000 ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## The Tristate Ltd. JJY receiver TS-JJY01, TS-JJY02 ##*/ /*## ##*/ /*## refclock jjy unit X subtype 1 ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* */ /* Command Response Remarks */ /* -------------------- ---------------------------------------- ---------------------------- */ /* dcst VALID or INVALID */ /* stus ADJUSTED or UNADJUSTED */ /* date YYYY/MM/DD XXX XXX is the day of the week */ /* time HH:MM:SS Not used by this driver */ /* stim HH:MM:SS Reply at just second */ /* */ /*################################################################################################*/ #define TS_JJY01_COMMAND_NUMBER_DATE 1 #define TS_JJY01_COMMAND_NUMBER_TIME 2 #define TS_JJY01_COMMAND_NUMBER_STIM 3 #define TS_JJY01_COMMAND_NUMBER_STUS 4 #define TS_JJY01_COMMAND_NUMBER_DCST 5 /* #define TS_JJY01_REPLY_DATE "yyyy/mm/dd www" UNUSED */ /* #define TS_JJY01_REPLY_STIM "hh:mm:ss" UNUSED */ #define TS_JJY01_REPLY_STUS_ADJUSTED "adjusted" #define TS_JJY01_REPLY_STUS_UNADJUSTED "unadjusted" #define TS_JJY01_REPLY_DCST_VALID "valid" #define TS_JJY01_REPLY_DCST_INVALID "invalid" #define TS_JJY01_REPLY_LENGTH_DATE 14 /* Length without */ #define TS_JJY01_REPLY_LENGTH_TIME 8 /* Length without */ #define TS_JJY01_REPLY_LENGTH_STIM 8 /* Length without */ #define TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED 8 /* Length without */ #define TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED 10 /* Length without */ #define TS_JJY01_REPLY_LENGTH_DCST_VALID 5 /* Length without */ #define TS_JJY01_REPLY_LENGTH_DCST_INVALID 7 /* Length without */ static struct { const char commandNumber ; const char *command ; int commandLength ; int iExpectedReplyLength [ 2 ] ; } tristate_jjy01_command_sequence[] = { { 0, NULL, 0, { 0, 0 } }, /* Idle */ { TS_JJY01_COMMAND_NUMBER_DCST, "dcst\r\n", 6, { TS_JJY01_REPLY_LENGTH_DCST_VALID , TS_JJY01_REPLY_LENGTH_DCST_INVALID } }, { TS_JJY01_COMMAND_NUMBER_STUS, "stus\r\n", 6, { TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED, TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED } }, { TS_JJY01_COMMAND_NUMBER_TIME, "time\r\n", 6, { TS_JJY01_REPLY_LENGTH_TIME , TS_JJY01_REPLY_LENGTH_TIME } }, { TS_JJY01_COMMAND_NUMBER_DATE, "date\r\n", 6, { TS_JJY01_REPLY_LENGTH_DATE , TS_JJY01_REPLY_LENGTH_DATE } }, { TS_JJY01_COMMAND_NUMBER_STIM, "stim\r\n", 6, { TS_JJY01_REPLY_LENGTH_STIM , TS_JJY01_REPLY_LENGTH_STIM } }, /* End of command */ { 0, NULL, 0, { 0, 0 } } } ; /**************************************************************************************************/ static int jjy_start_tristate_jjy01 ( int unit, struct peer *peer, struct jjyunit *up ) { UNUSED_ARG(unit); jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Tristate Ltd. TS-JJY01, TS-JJY02" ) ; up->unittype = UNITTYPE_TRISTATE_JJY01 ; up->linespeed = SPEED232_TRISTATE_JJY01 ; up->linediscipline = LDISC_CLK ; return 0 ; } /**************************************************************************************************/ static int jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp ) { struct jjyunit *up ; struct refclockproc *pp ; struct peer *peer; char *pBuf, sLog [ MAX_LOGTEXT ] ; int iLen ; int rc ; const char *pCmd ; int iCmdLen ; /* Initialize pointers */ peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_tristate_jjy01", iLen ) ; /* Check expected reply */ if ( tristate_jjy01_command_sequence[up->iCommandSeq].command == NULL ) { /* Command sequence has not been started, or has been completed */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } /* Check reply length */ if ( iLen != tristate_jjy01_command_sequence[up->iCommandSeq].iExpectedReplyLength[0] && iLen != tristate_jjy01_command_sequence[up->iCommandSeq].iExpectedReplyLength[1] ) { /* Unexpected reply length */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH, iLen ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } /* Parse reply */ switch ( tristate_jjy01_command_sequence[up->iCommandSeq].commandNumber ) { case TS_JJY01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD WWW */ rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ; if ( rc != 3 || up->year < 2000 || 2099 <= up->year || up->month < 1 || 12 < up->month || up->day < 1 || 31 < up->day ) { /* Invalid date */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE, rc, up->year, up->month, up->day ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } break ; case TS_JJY01_COMMAND_NUMBER_TIME : /* HH:MM:SS */ case TS_JJY01_COMMAND_NUMBER_STIM : /* HH:MM:SS */ if ( up->iTimestampCount >= 2 ) { /* Too many time reply */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY, up->iTimestampCount ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ; if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME, rc, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ; up->iTimestampCount++ ; up->msecond = 0 ; break ; case TS_JJY01_COMMAND_NUMBER_STUS : if ( strncmp( pBuf, TS_JJY01_REPLY_STUS_ADJUSTED, TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED ) == 0 || strncmp( pBuf, TS_JJY01_REPLY_STUS_UNADJUSTED, TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED ) == 0 ) { /* Good */ } else { snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } break ; case TS_JJY01_COMMAND_NUMBER_DCST : if ( strncmp( pBuf, TS_JJY01_REPLY_DCST_VALID, TS_JJY01_REPLY_LENGTH_DCST_VALID ) == 0 || strncmp( pBuf, TS_JJY01_REPLY_DCST_INVALID, TS_JJY01_REPLY_LENGTH_DCST_INVALID ) == 0 ) { /* Good */ } else { snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } break ; default : /* Unexpected reply */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } if ( up->iTimestampCount == 2 ) { /* Process date and time */ if ( up->iTimestamp[1] - 2 <= up->iTimestamp[0] && up->iTimestamp[0] <= up->iTimestamp[1] ) { /* 3 commands (time,date,stim) was executed in two seconds */ jjy_synctime( peer, pp, up ) ; return JJY_RECEIVE_DONE ; } else if ( up->iTimestamp[0] > up->iTimestamp[1] ) { /* Over midnight, and date is unsure */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2, up->iTimestamp[0], up->iTimestamp[1] ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ; return JJY_RECEIVE_SKIP ; } else { /* Slow reply */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2, up->iTimestamp[0], up->iTimestamp[1] ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } } /* Issue next command */ if ( tristate_jjy01_command_sequence[up->iCommandSeq].command != NULL ) { up->iCommandSeq ++ ; } if ( tristate_jjy01_command_sequence[up->iCommandSeq].command == NULL ) { /* Command sequence completed */ return JJY_RECEIVE_DONE ; } pCmd = tristate_jjy01_command_sequence[up->iCommandSeq].command ; iCmdLen = tristate_jjy01_command_sequence[up->iCommandSeq].commandLength ; if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report ( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; return JJY_RECEIVE_WAIT ; } /**************************************************************************************************/ static void jjy_poll_tristate_jjy01 ( int unit, struct peer *peer ) { #ifdef DEBUG static const char *sFunctionName = "jjy_poll_tristate_jjy01" ; #endif UNUSED_ARG(unit); struct refclockproc *pp ; struct jjyunit *up ; const char *pCmd ; int iCmdLen ; pp = peer->procptr; up = pp->unitptr ; up->bLineError = false ; up->iTimestampCount = 0 ; if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) { /* Skip "dcst" and "stus" commands */ up->iCommandSeq = 2 ; up->iLineCount = 2 ; } DPRINT(1, ("%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X " "up->iLineCount=%d\n", sFunctionName, pp->sloppyclockflag, (unsigned)CLK_FLAG1, up->iLineCount )) ; /* * Send a first command */ up->iCommandSeq ++ ; pCmd = tristate_jjy01_command_sequence[up->iCommandSeq].command ; iCmdLen = tristate_jjy01_command_sequence[up->iCommandSeq].commandLength ; if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report ( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## The C-DEX Co. Ltd. JJY receiver JST2000 ##*/ /*## ##*/ /*## refclock jjy unit X subtype 2 ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* */ /* Command Response Remarks */ /* -------------------- ---------------------------------------- ---------------------------- */ /* 1J JYYMMDD HHMMSSS J is a fixed character */ /* */ /*################################################################################################*/ static struct jjyRawDataBreak cdex_jst2000_raw_break [ ] = { { "\x03", 1 }, { NULL, 0 } } ; /**************************************************************************************************/ static int jjy_start_cdex_jst2000 ( int unit, struct peer *peer, struct jjyunit *up ) { jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: C-DEX Co. Ltd. JST2000" ) ; UNUSED_ARG(unit); up->unittype = UNITTYPE_CDEX_JST2000 ; up->linespeed = SPEED232_CDEX_JST2000 ; up->linediscipline = LDISC_RAW ; up->pRawBreak = cdex_jst2000_raw_break ; up->bWaitBreakString = true ; up->bSkipCntrlCharOnly = false ; return 0 ; } /**************************************************************************************************/ static int jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp ) { struct jjyunit *up ; struct refclockproc *pp ; struct peer *peer ; char *pBuf, sLog [ MAX_LOGTEXT ] ; int iLen ; int rc ; /* Initialize pointers */ peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_cdex_jst2000", iLen ) ; /* Check expected reply */ if ( up->iCommandSeq != 1 ) { /* Command sequence has not been started, or has been completed */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } /* Wait until ETX comes */ if ( up->iLineBufLen < 17 || up->sLineBuf[up->iLineBufLen-1] != 0x03 ) { return JJY_RECEIVE_UNPROCESS ; } /* Check reply length */ if ( iLen != 15 ) { /* Unexpected reply length */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH, iLen ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } /* JYYMMDDWHHMMSSS */ rc = sscanf ( pBuf, "J%2d%2d%2d%*1d%2d%2d%2d%1d", &up->year, &up->month, &up->day, &up->hour, &up->minute, &up->second, &up->msecond ) ; if ( rc != 7 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid date and time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME, rc, up->year, up->month, up->day, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } up->year += 2000 ; up->msecond *= 100 ; jjy_synctime( peer, pp, up ) ; return JJY_RECEIVE_DONE ; } /**************************************************************************************************/ static void jjy_poll_cdex_jst2000 ( int unit, struct peer *peer ) { struct refclockproc *pp ; struct jjyunit *up ; UNUSED_ARG(unit); pp = peer->procptr ; up = pp->unitptr ; up->bLineError = false ; up->iRawBufLen = 0 ; up->iLineBufLen = 0 ; up->iTextBufLen = 0 ; /* * Send "1J" command */ up->iCommandSeq ++ ; if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4 ) { refclock_report ( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, "\0051J\003" ) ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## The Echo Keisokuki Co. Ltd. JJY receiver LT2000 ##*/ /*## ##*/ /*## refclock jjy unit X subtype 3 ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* */ /* Command Response Remarks */ /* -------------------- ---------------------------------------- ---------------------------- */ /* # Mode 1 ( Request & Send ) */ /* T YYMMDDWHHMMSS */ /* C Mode 2 ( Continuous ) */ /* YYMMDDWHHMMSS 0.5 sec before time stamp */ /* Second signal */ /* */ /*################################################################################################*/ #define ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND 1 #define ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS 2 #define ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS 3 #define ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND "#" /* #define ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_TIME "T" UNUSED */ /* #define ECHOKEISOKUKI_LT2000_COMMAND_CONTINUOUS "C" UNUSED */ /**************************************************************************************************/ static int jjy_start_echokeisokuki_lt2000 ( int unit, struct peer *peer, struct jjyunit *up ) { UNUSED_ARG(unit); jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Echo Keisokuki Co. Ltd. LT2000" ) ; up->unittype = UNITTYPE_ECHOKEISOKUKI_LT2000 ; up->linespeed = SPEED232_ECHOKEISOKUKI_LT2000 ; up->linediscipline = LDISC_CLK ; up->operationmode = ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ; return 0 ; } /**************************************************************************************************/ static int jjy_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp ) { struct jjyunit *up ; struct refclockproc *pp ; struct peer *peer; char *pBuf, sLog [ MAX_LOGTEXT ], sErr [ 60 ] ; int iLen ; int rc ; int i, ibcc, ibcc1, ibcc2 ; /* Initialize pointers */ peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_echokeisokuki_lt2000", iLen ) ; /* Check reply length */ if ( ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND && iLen != 15 ) || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS && iLen != 17 ) || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS && iLen != 17 ) ) { /* Unexpected reply length */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH, iLen ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } if ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND && iLen == 15 ) { /* YYMMDDWHHMMSS */ for ( i = ibcc = 0 ; i < 13 ; i ++ ) { ibcc ^= pBuf[i] ; } ibcc1 = 0x30 | ( ( ibcc >> 4 ) & 0xF ) ; ibcc2 = 0x30 | ( ( ibcc ) & 0xF ) ; if ( pBuf[13] != ibcc1 || pBuf[14] != ibcc2 ) { snprintf(sErr, sizeof(sErr), " BCC error : Recv=%02X,%02X / Calc=%02X,%02X ", (unsigned)(pBuf[13] & 0xFF), (unsigned)(pBuf[14] & 0xFF), (unsigned)ibcc1, (unsigned)ibcc2); snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY, sErr ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } } if ( ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND && iLen == 15 ) || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS && iLen == 17 ) || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS && iLen == 17 ) ) { /* YYMMDDWHHMMSS or YYMMDDWHHMMSS */ rc = sscanf ( pBuf, "%2d%2d%2d%*1d%2d%2d%2d", &up->year, &up->month, &up->day, &up->hour, &up->minute, &up->second ) ; if ( rc != 6 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid date and time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME, rc, up->year, up->month, up->day, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } up->year += 2000 ; if ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS || up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ) { /* A time stamp comes on every 0.5 second in the mode 2 of the LT-2000. */ up->msecond = 500 ; up->second -- ; if ( up->second < 0 ) { up->second = 59 ; up->minute -- ; if ( up->minute < 0 ) { up->minute = 59 ; up->hour -- ; if ( up->hour < 0 ) { up->hour = 23 ; up->day -- ; if ( up->day < 1 ) { up->month -- ; if ( up->month < 1 ) { up->month = 12 ; up->year -- ; } } } } } } jjy_synctime( peer, pp, up ) ; } if (up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ) { /* Switch from mode 2 to mode 1 in order to restraint of * useless time stamp. */ iLen = (int)strlen( ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND ) ; if ( write ( pp->io.fd, ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND, (size_t)iLen ) != iLen ) { refclock_report ( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND ) ; } return JJY_RECEIVE_DONE ; } /**************************************************************************************************/ static void jjy_poll_echokeisokuki_lt2000 ( int unit, struct peer *peer ) { struct refclockproc *pp ; struct jjyunit *up ; char sCmd[2] ; UNUSED_ARG(unit); pp = peer->procptr ; up = pp->unitptr ; up->bLineError = false ; /* * Send "T" or "C" command */ switch ( up->operationmode ) { case ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND : sCmd[0] = 'T' ; break ; case ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS : case ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS : sCmd[0] = 'C' ; break ; default: /* huh? */ break ; } sCmd[1] = 0 ; if ( write ( pp->io.fd, sCmd, 1 ) != 1 ) { refclock_report ( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, sCmd ) ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## The CITIZEN T.I.C CO., LTD. JJY receiver JJY200 ##*/ /*## ##*/ /*## refclock jjy unit X subtype 4 ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* */ /* Command Response Remarks */ /* -------------------- ---------------------------------------- ---------------------------- */ /* 'XX YY/MM/DD W HH:MM:SS XX:OK|NG|ER W:0(Mon)-6(Sun) */ /* */ /*################################################################################################*/ static int jjy_start_citizentic_jjy200 ( int unit, struct peer *peer, struct jjyunit *up ) { UNUSED_ARG(unit); jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: CITIZEN T.I.C CO. LTD. JJY200" ) ; up->unittype = UNITTYPE_CITIZENTIC_JJY200 ; up->linespeed = SPEED232_CITIZENTIC_JJY200 ; up->linediscipline = LDISC_CLK ; return 0 ; } /**************************************************************************************************/ static int jjy_receive_citizentic_jjy200 ( struct recvbuf *rbufp ) { struct jjyunit *up ; struct refclockproc *pp ; struct peer *peer; char *pBuf, sLog [ MAX_LOGTEXT ], sMsg [ 16 ] ; int iLen ; int rc ; char cApostrophe, sStatus[3] ; int iWeekday ; /* Initialize pointers */ peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_citizentic_jjy200", iLen ) ; /* * JJY-200 sends a timestamp every second. * So, a timestamp is ignored unless it is right after polled. */ if ( up->iProcessState != JJY_PROCESS_STATE_RECEIVE ) { return JJY_RECEIVE_SKIP ; } /* Check reply length */ if ( iLen != 23 ) { /* Unexpected reply length */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH, iLen ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } /* 'XX YY/MM/DD W HH:MM:SS */ rc = sscanf ( pBuf, "%c%2s %2d/%2d/%2d %1d %2d:%2d:%2d", &cApostrophe, sStatus, &up->year, &up->month, &up->day, &iWeekday, &up->hour, &up->minute, &up->second ) ; sStatus[2] = 0 ; if ( rc != 9 || cApostrophe != '\'' || ( strcmp( sStatus, "OK" ) != 0 && strcmp( sStatus, "NG" ) != 0 && strcmp( sStatus, "ER" ) != 0 ) || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 || iWeekday > 6 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid date and time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME, rc, up->year, up->month, up->day, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } else if ( strcmp( sStatus, "NG" ) == 0 || strcmp( sStatus, "ER" ) == 0 ) { /* Timestamp is unsure */ snprintf( sMsg, sizeof(sMsg), "status=%s", sStatus ) ; snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_TIMESTAMP_UNSURE, sMsg ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ; return JJY_RECEIVE_SKIP ; } up->year += 2000 ; up->msecond = 0 ; jjy_synctime( peer, pp, up ) ; return JJY_RECEIVE_DONE ; } /**************************************************************************************************/ static void jjy_poll_citizentic_jjy200 ( int unit, struct peer *peer ) { UNUSED_ARG(unit); struct refclockproc *pp ; struct jjyunit *up ; pp = peer->procptr ; up = pp->unitptr ; up->bLineError = false ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## The Tristate Ltd. GPS clock TS-GPS01 ##*/ /*## ##*/ /*## refclock jjy unit X subtype 5 ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* */ /* This clock has NMEA mode and command/response mode. */ /* When this jjy driver are used, set to command/respose mode of this clock */ /* by the onboard switch SW4, and make sure the LED-Y is tured on. */ /* Other than this JJY driver, the refclock driver type 20, generic NMEA driver, */ /* works with the NMEA mode of this clock. */ /* */ /* Command Response Remarks */ /* -------------------- ---------------------------------------- ---------------------------- */ /* stus *R|*G|*U|+U */ /* date YY/MM/DD */ /* time HH:MM:SS */ /* */ /*################################################################################################*/ #define TS_GPS01_COMMAND_NUMBER_DATE 1 #define TS_GPS01_COMMAND_NUMBER_TIME 2 #define TS_GPS01_COMMAND_NUMBER_STUS 4 /* #define TS_GPS01_REPLY_DATE "yyyy/mm/dd" UNUSED */ /* #define TS_GPS01_REPLY_TIME "hh:mm:ss" UNUSED */ #define TS_GPS01_REPLY_STUS_RTC "*R" #define TS_GPS01_REPLY_STUS_GPS "*G" #define TS_GPS01_REPLY_STUS_UTC "*U" #define TS_GPS01_REPLY_STUS_PPS "+U" #define TS_GPS01_REPLY_LENGTH_DATE 10 /* Length without */ #define TS_GPS01_REPLY_LENGTH_TIME 8 /* Length without */ #define TS_GPS01_REPLY_LENGTH_STUS 2 /* Length without */ static struct { char commandNumber ; const char *command ; int commandLength ; int iExpectedReplyLength ; } tristate_gps01_command_sequence[] = { { 0, NULL, 0, 0 }, /* Idle */ { TS_GPS01_COMMAND_NUMBER_STUS, "stus\r\n", 6, TS_GPS01_REPLY_LENGTH_STUS }, { TS_GPS01_COMMAND_NUMBER_TIME, "time\r\n", 6, TS_GPS01_REPLY_LENGTH_TIME }, { TS_GPS01_COMMAND_NUMBER_DATE, "date\r\n", 6, TS_GPS01_REPLY_LENGTH_DATE }, { TS_GPS01_COMMAND_NUMBER_TIME, "time\r\n", 6, TS_GPS01_REPLY_LENGTH_TIME }, /* End of command */ { 0, NULL, 0, 0 } } ; /**************************************************************************************************/ static int jjy_start_tristate_gpsclock01 ( int unit, struct peer *peer, struct jjyunit *up ) { UNUSED_ARG(unit); jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Tristate Ltd. TS-GPS01" ) ; up->unittype = UNITTYPE_TRISTATE_GPSCLOCK01 ; up->linespeed = SPEED232_TRISTATE_GPSCLOCK01 ; up->linediscipline = LDISC_CLK ; return 0 ; } /**************************************************************************************************/ static int jjy_receive_tristate_gpsclock01 ( struct recvbuf *rbufp ) { #ifdef DEBUG static const char *sFunctionName = "jjy_receive_tristate_gpsclock01" ; #endif struct jjyunit *up ; struct refclockproc *pp ; struct peer *peer; char *pBuf, sLog [ MAX_LOGTEXT ] ; int iLen ; int rc ; const char *pCmd ; int iCmdLen ; /* Initialize pointers */ peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_tristate_gpsclock01", iLen ) ; /* Ignore NMEA data stream */ if ( iLen > 5 && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) { DPRINT(1, ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n", sFunctionName, pBuf )) ; return JJY_RECEIVE_WAIT ; } /* * Skip command prompt '$Cmd>' from the TS-GPSclock-01 */ if ( iLen == 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) { return JJY_RECEIVE_WAIT ; } else if ( iLen > 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) { pBuf += 5 ; iLen -= 5 ; } /* * Ignore NMEA data stream after command prompt */ if ( iLen > 5 && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) { DPRINT(1, ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n", sFunctionName, pBuf )) ; return JJY_RECEIVE_WAIT ; } /* Check expected reply */ if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) { /* Command sequence has not been started, or has been completed */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } /* Check reply length */ if ( iLen != tristate_gps01_command_sequence[up->iCommandSeq].iExpectedReplyLength ) { /* Unexpected reply length */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH, iLen ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } /* Parse reply */ switch ( tristate_gps01_command_sequence[up->iCommandSeq].commandNumber ) { case TS_GPS01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD */ rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ; if ( rc != 3 || up->year < 2000 || 2099 <= up->year || up->month < 1 || 12 < up->month || up->day < 1 || 31 < up->day ) { /* Invalid date */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE, rc, up->year, up->month, up->day ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } break ; case TS_GPS01_COMMAND_NUMBER_TIME : /* HH:MM:SS */ if ( up->iTimestampCount >= 2 ) { /* Too many time reply */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY, up->iTimestampCount ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ; if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME, rc, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ; up->iTimestampCount++ ; up->msecond = 0 ; break ; case TS_GPS01_COMMAND_NUMBER_STUS : if ( strncmp( pBuf, TS_GPS01_REPLY_STUS_RTC, TS_GPS01_REPLY_LENGTH_STUS ) == 0 || strncmp( pBuf, TS_GPS01_REPLY_STUS_GPS, TS_GPS01_REPLY_LENGTH_STUS ) == 0 || strncmp( pBuf, TS_GPS01_REPLY_STUS_UTC, TS_GPS01_REPLY_LENGTH_STUS ) == 0 || strncmp( pBuf, TS_GPS01_REPLY_STUS_PPS, TS_GPS01_REPLY_LENGTH_STUS ) == 0 ) { /* Good */ } else { snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } break ; default : /* Unexpected reply */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } if ( up->iTimestampCount == 2 ) { /* Process date and time */ if ( up->iTimestamp[1] - 2 <= up->iTimestamp[0] && up->iTimestamp[0] <= up->iTimestamp[1] ) { /* 3 commands (time,date,stim) was executed in two seconds */ jjy_synctime( peer, pp, up ) ; return JJY_RECEIVE_DONE ; } else if ( up->iTimestamp[0] > up->iTimestamp[1] ) { /* Over midnight, and date is unsure */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2, up->iTimestamp[0], up->iTimestamp[1] ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ; return JJY_RECEIVE_SKIP ; } else { /* Slow reply */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2, up->iTimestamp[0], up->iTimestamp[1] ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } } if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) { /* Command sequence completed */ jjy_synctime( peer, pp, up ) ; return JJY_RECEIVE_DONE ; } /* Issue next command */ if ( tristate_gps01_command_sequence[up->iCommandSeq].command != NULL ) { up->iCommandSeq ++ ; } if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) { /* Command sequence completed */ up->iProcessState = JJY_PROCESS_STATE_DONE ; return JJY_RECEIVE_DONE ; } pCmd = tristate_gps01_command_sequence[up->iCommandSeq].command ; iCmdLen = tristate_gps01_command_sequence[up->iCommandSeq].commandLength ; if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report ( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; return JJY_RECEIVE_WAIT ; } /**************************************************************************************************/ static void jjy_poll_tristate_gpsclock01 ( int unit, struct peer *peer ) { #ifdef DEBUG static const char *sFunctionName = "jjy_poll_tristate_gpsclock01" ; #endif struct refclockproc *pp ; struct jjyunit *up ; const char *pCmd ; int iCmdLen ; UNUSED_ARG(unit); pp = peer->procptr ; up = pp->unitptr ; up->iTimestampCount = 0 ; if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) { /* Skip "stus" command */ up->iCommandSeq = 1 ; up->iLineCount = 1 ; } DPRINT(1, ("%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X " "up->iLineCount=%d\n", sFunctionName, pp->sloppyclockflag, (unsigned)CLK_FLAG1, up->iLineCount )) ; /* * Send a first command */ up->iCommandSeq ++ ; pCmd = tristate_gps01_command_sequence[up->iCommandSeq].command ; iCmdLen = tristate_gps01_command_sequence[up->iCommandSeq].commandLength ; if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report ( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## The SEIKO TIME SYSTEMS TDC-300 ##*/ /*## ##*/ /*## refclock jjy unit X subtype 6 ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* */ /* Type Response Remarks */ /* -------------------- ---------------------------------------- ---------------------------- */ /* Type 1 HH:MM:SS */ /* Type 2 YYMMDDHHMMSSWLSCU W:0(Sun)-6(Sat) */ /* Type 3 YYMMDDWHHMMSS W:0(Sun)-6(Sat) */ /* 5 to 10 mSec. before second */ /* */ /*################################################################################################*/ static struct jjyRawDataBreak seiko_tsys_tdc_300_raw_break [ ] = { { "\x03", 1 }, { NULL, 0 } } ; /**************************************************************************************************/ static int jjy_start_seiko_tsys_tdc_300 ( int unit, struct peer *peer, struct jjyunit *up ) { UNUSED_ARG(unit); jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: SEIKO TIME SYSTEMS TDC-300" ) ; up->unittype = UNITTYPE_SEIKO_TIMESYS_TDC_300 ; up->linespeed = SPEED232_SEIKO_TIMESYS_TDC_300 ; up->linediscipline = LDISC_RAW ; up->pRawBreak = seiko_tsys_tdc_300_raw_break ; up->bWaitBreakString = true ; up->bSkipCntrlCharOnly = false ; return 0 ; } /**************************************************************************************************/ static int jjy_receive_seiko_tsys_tdc_300 ( struct recvbuf *rbufp ) { struct peer *peer; struct refclockproc *pp ; struct jjyunit *up ; char *pBuf, sLog [ MAX_LOGTEXT ] ; int iLen, i ; int rc, iWeekday ; time_t now ; struct tm *pTime ; struct tm tmbuf; /* Initialize pointers */ peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_seiko_tsys_tdc_300", iLen ) ; /* * TDC-300 sends a timestamp every second. * So, a timestamp is ignored unless it is right after polled. */ if ( up->iProcessState != JJY_PROCESS_STATE_RECEIVE ) { return JJY_RECEIVE_SKIP ; } /* Process timestamp */ up->iReceiveSeq ++ ; switch ( iLen ) { case 8 : /* Type 1 : HH:MM:SS */ for ( i = 0 ; i < iLen ; i ++ ) { pBuf[i] &= 0x7F ; } rc = sscanf ( pBuf+1, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ; if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME, rc, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } else if ( up->hour == 23 && up->minute == 59 && up->second >= 55 ) { /* Uncertainty date guard */ return JJY_RECEIVE_WAIT ; } time( &now ) ; if ((pTime = localtime_r( &now, &tmbuf )) != NULL) { up->year = pTime->tm_year ; up->month = pTime->tm_mon + 1 ; up->day = pTime->tm_mday ; } break ; case 17 : /* Type 2 : YYMMDDHHMMSSWLSCU */ for ( i = 0 ; i < iLen ; i ++ ) { pBuf[i] &= 0x7F ; } rc = sscanf ( pBuf+1, "%2d%2d%2d%2d%2d%2d%1d", &up->year, &up->month, &up->day, &up->hour, &up->minute, &up->second, &iWeekday ) ; if ( rc != 7 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 || iWeekday > 6 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid date and time */ snprintf(sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME, rc, up->year, up->month, up->day, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } break ; case 13 : /* Type 3 : YYMMDDWHHMMSS */ rc = sscanf ( pBuf, "%2d%2d%2d%1d%2d%2d%2d", &up->year, &up->month, &up->day, &iWeekday, &up->hour, &up->minute, &up->second ) ; if ( rc != 7 || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31 || iWeekday > 6 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid date and time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME, rc, up->year, up->month, up->day, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } return JJY_RECEIVE_WAIT ; case 1 : /* Type 3 : */ if ( ( *pBuf & 0xFF ) != 0xE5 ) { /* Invalid second signal */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY, up->sLineBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } else if ( up->iReceiveSeq == 1 ) { /* Wait for next timestamp */ up->iReceiveSeq -- ; return JJY_RECEIVE_WAIT ; } else if ( up->iReceiveSeq >= 3 ) { /* Unexpected second signal */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY, up->sLineBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } break ; default : /* Unexpected reply length */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH, iLen ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; return JJY_RECEIVE_ERROR ; } up->year += 2000 ; up->msecond = 0 ; jjy_synctime( peer, pp, up ) ; return JJY_RECEIVE_DONE ; } /**************************************************************************************************/ static void jjy_poll_seiko_tsys_tdc_300 ( int unit, struct peer *peer ) { UNUSED_ARG(unit); struct refclockproc *pp ; struct jjyunit *up ; pp = peer->procptr ; up = pp->unitptr ; up->bLineError = false ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## Telephone JJY ##*/ /*## ##*/ /*## refclock jjy unit X subtype 100 to 180 ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* */ /* Prompt Command Response Remarks */ /* -------------------- -------------------- -------------------- -------------------------- */ /* Name? TJJY Welcome messages TJJY is a guest user ID */ /* > 4DATE YYYYMMDD */ /* > LEAPSEC XX One of 0, +1, -1 */ /* > TIME HHMMSS 3 times on second */ /* > BYE Sayounara messages */ /* */ /*################################################################################################*/ static struct jjyRawDataBreak teljjy_raw_break [ ] = { { "\r\n", 2 }, { "\r" , 1 }, { "\n" , 1 }, { "Name ? ", 7 }, { ">" , 1 }, { "+++" , 3 }, { NULL , 0 } } ; #define TELJJY_STATE_IDLE 0 #define TELJJY_STATE_DAILOUT 1 #define TELJJY_STATE_LOGIN 2 #define TELJJY_STATE_CONNECT 3 #define TELJJY_STATE_BYE 4 #define TELJJY_EVENT_NULL 0 #define TELJJY_EVENT_START 1 #define TELJJY_EVENT_CONNECT 2 #define TELJJY_EVENT_DISCONNECT 3 #define TELJJY_EVENT_COMMAND 4 #define TELJJY_EVENT_LOGIN 5 /* Posted by the jjy_receive_telephone */ #define TELJJY_EVENT_PROMPT 6 /* Posted by the jjy_receive_telephone */ #define TELJJY_EVENT_DATA 7 /* Posted by the jjy_receive_telephone */ #define TELJJY_EVENT_ERROR 8 /* Posted by the jjy_receive_telephone */ #define TELJJY_EVENT_SILENT 9 /* Posted by the jjy_timer_telephone */ #define TELJJY_EVENT_TIMEOUT 10 /* Posted by the jjy_timer_telephone */ static void teljjy_control ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_idle_ignore ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_idle_dialout ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_dial_ignore ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_dial_login ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_dial_disc ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_login_ignore ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_login_disc ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_login_conn ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_login_login ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_login_silent ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_login_error ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_conn_ignore ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_conn_disc ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_conn_send ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_conn_data ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_conn_silent ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_conn_error ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_bye_ignore ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_bye_disc ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int teljjy_bye_modem ( struct peer *peer, struct refclockproc *, struct jjyunit * ) ; static int ( *pTeljjyHandler [ ] [ 5 ] ) (struct peer *, struct refclockproc *, struct jjyunit *) = { /*STATE_IDLE STATE_DAILOUT STATE_LOGIN STATE_CONNECT STATE_BYE */ /* NULL */ { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore }, /* START */ { teljjy_idle_dialout, teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore }, /* CONNECT */ { teljjy_idle_ignore , teljjy_dial_login , teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore }, /* DISCONNECT */ { teljjy_idle_ignore , teljjy_dial_disc , teljjy_login_disc , teljjy_conn_disc , teljjy_bye_disc }, /* COMMAND */ { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_modem }, /* LOGIN */ { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_login , teljjy_conn_error , teljjy_bye_ignore }, /* PROMPT */ { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_conn , teljjy_conn_send , teljjy_bye_ignore }, /* DATA */ { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_data , teljjy_bye_ignore }, /* ERROR */ { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_error , teljjy_conn_error , teljjy_bye_ignore }, /* SILENT */ { teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_silent, teljjy_conn_silent, teljjy_bye_ignore }, /* TIMEOUT */ { teljjy_idle_ignore , teljjy_dial_disc , teljjy_login_error , teljjy_conn_error , teljjy_bye_modem } } ; static short iTeljjyNextState [ ] [ 5 ] = { /*STATE_IDLE STATE_DAILOUT STATE_LOGIN STATE_CONNECT STATE_BYE */ /* NULL */ { TELJJY_STATE_IDLE , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE }, /* START */ { TELJJY_STATE_DAILOUT, TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE }, /* CONNECT */ { TELJJY_STATE_IDLE , TELJJY_STATE_LOGIN , TELJJY_STATE_LOGIN , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE }, /* DISCONNECT */ { TELJJY_STATE_IDLE , TELJJY_STATE_IDLE , TELJJY_STATE_IDLE , TELJJY_STATE_IDLE , TELJJY_STATE_IDLE }, /* COMMAND */ { TELJJY_STATE_IDLE , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE }, /* LOGIN */ { TELJJY_STATE_IDLE , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN , TELJJY_STATE_BYE , TELJJY_STATE_BYE }, /* PROMPT */ { TELJJY_STATE_IDLE , TELJJY_STATE_DAILOUT, TELJJY_STATE_CONNECT, TELJJY_STATE_BYE , TELJJY_STATE_BYE }, /* DATA */ { TELJJY_STATE_IDLE , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE }, /* ERROR */ { TELJJY_STATE_IDLE , TELJJY_STATE_DAILOUT, TELJJY_STATE_BYE , TELJJY_STATE_BYE , TELJJY_STATE_BYE }, /* SILENT */ { TELJJY_STATE_IDLE , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE }, /* TIMEOUT */ { TELJJY_STATE_IDLE , TELJJY_STATE_IDLE , TELJJY_STATE_BYE , TELJJY_STATE_BYE , TELJJY_STATE_BYE } } ; static short iTeljjyPostEvent [ ] [ 5 ] = { /*STATE_IDLE STATE_DAILOUT STATE_LOGIN STATE_CONNECT STATE_BYE */ /* NULL */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_NULL , TELJJY_EVENT_NULL }, /* START */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_NULL , TELJJY_EVENT_NULL }, /* CONNECT */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_NULL , TELJJY_EVENT_NULL }, /* DISCONNECT */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_NULL , TELJJY_EVENT_NULL }, /* COMMAND */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_NULL , TELJJY_EVENT_NULL }, /* LOGIN */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL }, /* PROMPT */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_PROMPT , TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL }, /* DATA */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_NULL , TELJJY_EVENT_NULL }, /* ERROR */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_COMMAND, TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL }, /* SILENT */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL , TELJJY_EVENT_NULL , TELJJY_EVENT_NULL }, /* TIMEOUT */ { TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_COMMAND, TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL } } ; static short iTeljjySilentTimeout [ 5 ] = { 0, 0, 10, 5, 0 } ; static short iTeljjyStateTimeout [ 5 ] = { 0, 120, 60, 60, 40 } ; #define TELJJY_STAY_CLOCK_STATE 0 #define TELJJY_CHANGE_CLOCK_STATE 1 /* Command and replay */ #define TELJJY_REPLY_NONE 0 #define TELJJY_REPLY_4DATE 1 #define TELJJY_REPLY_TIME 2 #define TELJJY_REPLY_LEAPSEC 3 #define TELJJY_REPLY_LOOP 4 /* #define TELJJY_REPLY_PROMPT 5 UNUSED */ #define TELJJY_REPLY_LOOPBACK 6 #define TELJJY_REPLY_COM 7 #define TELJJY_COMMAND_START_SKIP_LOOPBACK 7 static struct { const char *command ; int commandLength ; int iEchobackReplyLength ; int iExpectedReplyType ; int iExpectedReplyLength ; } teljjy_command_sequence[] = { { NULL, 0, 0, 0, 0 }, /* Idle */ { "LOOP\r" , 5, 4, TELJJY_REPLY_LOOP , 0 }, /* Getting into loopback mode */ { ">" , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */ { ">" , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */ { ">" , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */ { ">" , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */ { ">" , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */ { "COM\r" , 4, 3, TELJJY_REPLY_COM , 0 }, /* Exit from loopback mode */ /* TELJJY_COMMAND_START_SKIP_LOOPBACK */ { "TIME\r" , 5, 4, TELJJY_REPLY_TIME , 6 }, { "4DATE\r" , 6, 5, TELJJY_REPLY_4DATE , 8 }, { "LEAPSEC\r", 8, 7, TELJJY_REPLY_LEAPSEC , 2 }, { "TIME\r" , 5, 4, TELJJY_REPLY_TIME , 6 }, { "BYE\r" , 4, 3, TELJJY_REPLY_NONE , 0 }, /* End of command */ { NULL, 0, 0, 0, 0 } } ; #define TELJJY_LOOPBACK_DELAY_THRESHOLD 700 /* Milli second */ #ifdef DEBUG #define DEBUG_TELJJY_PRINTF(sFunc) { if ( debug ) { printf ( "refclock_jjy.c : %s : iClockState=%d iClockEvent=%d iTeljjySilentTimer=%d iTeljjyStateTimer=%d iClockCommandSeq=%d\n", sFunc, up->iClockState, up->iClockEvent, up->iTeljjySilentTimer, up->iTeljjyStateTimer, up->iClockCommandSeq ) ; } } #else #define DEBUG_TELJJY_PRINTF(sFunc) #endif /**************************************************************************************************/ static bool jjy_start_telephone ( int unit, struct peer *peer, struct jjyunit *up ) { char sLog [ MAX_LOGTEXT ], sFirstThreeDigits [ 4 ] ; int iNumberOfDigitsOfPhoneNumber, iCommaCount, iCommaPosition ; size_t i; int iFirstThreeDigitsCount ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Telephone JJY" ) ; UNUSED_ARG(unit); up->unittype = UNITTYPE_TELEPHONE ; up->linespeed = SPEED232_TELEPHONE ; up->linediscipline = LDISC_RAW ; up->pRawBreak = teljjy_raw_break ; up->bWaitBreakString = true ; up->bSkipCntrlCharOnly = true ; up->iClockState = TELJJY_STATE_IDLE ; up->iClockEvent = TELJJY_EVENT_NULL ; /* Check the telephone number */ if ( sys_phone[0] == NULL ) { msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf must be specified." ) ; up->bInitError = true ; return true ; } if ( sys_phone[1] != NULL ) { msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be only one." ) ; up->bInitError = true ; return true ; } iNumberOfDigitsOfPhoneNumber = iCommaCount = iCommaPosition = iFirstThreeDigitsCount = 0 ; for ( i = 0 ; i < strlen( sys_phone[0] ) ; i ++ ) { if ( isdigit( (int)*(sys_phone[0]+i) ) ) { if ( iFirstThreeDigitsCount < (int)sizeof(sFirstThreeDigits)-1 ) { sFirstThreeDigits[iFirstThreeDigitsCount++] = *(sys_phone[0]+i) ; } iNumberOfDigitsOfPhoneNumber ++ ; } else if ( *(sys_phone[0]+i) == ',' ) { iCommaCount ++ ; if ( iCommaCount > 1 ) { msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be zero or one comma." ) ; up->bInitError = true ; return true ; } iFirstThreeDigitsCount = 0 ; iCommaPosition = (int)i ; } else if ( *(sys_phone[0]+i) != '-' ) { msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be a number or a hyphen." ) ; up->bInitError = true ; return true ; } } sFirstThreeDigits[iFirstThreeDigitsCount] = 0 ; if ( iCommaCount == 1 ) { if ( iCommaPosition != 1 || *sys_phone[0] != '0' ) { msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : Getting an outside line should be '0,'." ) ; up->bInitError = true ; return true ; } } if ( iNumberOfDigitsOfPhoneNumber - iCommaPosition < 6 || 10 < iNumberOfDigitsOfPhoneNumber - iCommaPosition ) { /* Too short or too long */ msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone=%s : Number of digits should be 6 to 10.", sys_phone[0] ) ; up->bInitError = true ; return true ; } if ( strncmp( sFirstThreeDigits + iCommaPosition, "00" , 2 ) == 0 || strncmp( sFirstThreeDigits + iCommaPosition, "10" , 2 ) == 0 || strncmp( sFirstThreeDigits + iCommaPosition, "11" , 2 ) == 0 || strncmp( sFirstThreeDigits + iCommaPosition, "12" , 2 ) == 0 || strncmp( sFirstThreeDigits + iCommaPosition, "171", 3 ) == 0 || strncmp( sFirstThreeDigits + iCommaPosition, "177", 3 ) == 0 || ( sFirstThreeDigits[0] == '0' && sFirstThreeDigits[2] == '0' ) ) { /* Not allowed because of emergency numbers or special service numbers */ msyslog(LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone=%s : First 2 or 3 digits are not allowed.", sys_phone[0] ) ; up->bInitError = true ; return true ; } snprintf( sLog, sizeof(sLog), "phone=%s", sys_phone[0] ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ; if ( peer->cfg.minpoll < 8 ) { /* minpoll must be greater or equal to 8 ( 256 seconds = about 4 minutes ) */ int oldminpoll = peer->cfg.minpoll ; peer->cfg.minpoll = 8 ; if ( peer->ppoll < peer->cfg.minpoll ) { peer->ppoll = peer->cfg.minpoll ; } if ( peer->cfg.maxpoll < peer->cfg.minpoll ) { peer->cfg.maxpoll = peer->cfg.minpoll ; } snprintf( sLog, sizeof(sLog), "minpoll=%d -> %d", oldminpoll, peer->cfg.minpoll ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ; } return false ; } /**************************************************************************************************/ static int jjy_receive_telephone ( struct recvbuf *rbufp ) { #ifdef DEBUG static const char *sFunctionName = "jjy_receive_telephone" ; #endif struct peer *peer; struct refclockproc *pp ; struct jjyunit *up ; char *pBuf ; size_t iLen ; short iPreviousModemState ; peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; DEBUG_TELJJY_PRINTF( sFunctionName ) ; if ( up->iClockState == TELJJY_STATE_IDLE || up->iClockState == TELJJY_STATE_DAILOUT || up->iClockState == TELJJY_STATE_BYE ) { iPreviousModemState = getModemState( up ) ; modem_receive ( rbufp ) ; if ( iPreviousModemState != up->iModemState ) { /* Modem state is changed just now. */ if ( isModemStateDisconnect( up->iModemState ) ) { up->iClockEvent = TELJJY_EVENT_DISCONNECT ; teljjy_control ( peer, pp, up ) ; } else if ( isModemStateConnect( up->iModemState ) ) { up->iClockEvent = TELJJY_EVENT_CONNECT ; teljjy_control ( peer, pp, up ) ; } } return JJY_RECEIVE_WAIT ; } if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = (size_t)up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = (size_t)pp->lencode ; } up->iTeljjySilentTimer = 0 ; if ( iLen == 7 && strncmp( pBuf, "Name ? ", 7 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_LOGIN ; } else if ( iLen == 1 && strncmp( pBuf, ">" , 1 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_PROMPT ; } else if ( iLen >= 1 && strncmp( pBuf, "?" , 1 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_ERROR ; } else { up->iClockEvent = TELJJY_EVENT_DATA ; } teljjy_control ( peer, pp, up ) ; return JJY_RECEIVE_WAIT ; } /**************************************************************************************************/ static void jjy_poll_telephone ( int unit, struct peer *peer ) { #ifdef DEBUG static const char *sFunctionName = "jjy_poll_telephone" ; #endif struct refclockproc *pp ; struct jjyunit *up ; UNUSED_ARG(unit); pp = peer->procptr ; up = pp->unitptr ; DEBUG_TELJJY_PRINTF( sFunctionName ) ; if ( up->iClockState == TELJJY_STATE_IDLE ) { up->iRawBufLen = 0 ; up->iLineBufLen = 0 ; up->iTextBufLen = 0 ; } up->iClockEvent = TELJJY_EVENT_START ; teljjy_control ( peer, pp, up ) ; } /**************************************************************************************************/ static void jjy_timer_telephone ( int unit, struct peer *peer ) { #ifdef DEBUG static const char *sFunctionName = "jjy_timer_telephone" ; #endif struct refclockproc *pp ; struct jjyunit *up ; short iPreviousModemState ; pp = peer->procptr ; up = pp->unitptr ; DEBUG_TELJJY_PRINTF( sFunctionName ) ; if ( iTeljjySilentTimeout[up->iClockState] != 0 ) { up->iTeljjySilentTimer++ ; if ( iTeljjySilentTimeout[up->iClockState] <= up->iTeljjySilentTimer ) { up->iClockEvent = TELJJY_EVENT_SILENT ; teljjy_control ( peer, pp, up ) ; } } if ( iTeljjyStateTimeout[up->iClockState] != 0 ) { up->iTeljjyStateTimer++ ; if ( iTeljjyStateTimeout[up->iClockState] <= up->iTeljjyStateTimer ) { up->iClockEvent = TELJJY_EVENT_TIMEOUT ; teljjy_control ( peer, pp, up ) ; } } if ( isModemStateTimerOn( up ) ) { iPreviousModemState = getModemState( up ) ; modem_timer ( unit, peer ) ; if ( iPreviousModemState != up->iModemState ) { /* Modem state is changed just now. */ if ( isModemStateDisconnect( up->iModemState ) ) { up->iClockEvent = TELJJY_EVENT_DISCONNECT ; teljjy_control ( peer, pp, up ) ; } else if ( isModemStateConnect( up->iModemState ) ) { up->iClockEvent = TELJJY_EVENT_CONNECT ; teljjy_control ( peer, pp, up ) ; } } } } /**************************************************************************************************/ static void teljjy_control ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { int i, rc ; short iPostEvent = TELJJY_EVENT_NULL ; DEBUG_TELJJY_PRINTF( "teljjy_control" ) ; rc = (*pTeljjyHandler[up->iClockEvent][up->iClockState])( peer, pp, up ) ; if ( rc == TELJJY_CHANGE_CLOCK_STATE ) { iPostEvent = iTeljjyPostEvent[up->iClockEvent][up->iClockState] ; DPRINT(1, ( "refclock_jjy.c : teljjy_control : iClockState=%hd -> %hd iPostEvent=%hd\n", up->iClockState, iTeljjyNextState[up->iClockEvent][up->iClockState], iPostEvent )) ; up->iTeljjySilentTimer = 0 ; if ( up->iClockState != iTeljjyNextState[up->iClockEvent][up->iClockState] ) { /* Telephone JJY state is changing now */ up->iTeljjyStateTimer = 0 ; up->bLineError = false ; up->iClockCommandSeq = 0 ; up->iTimestampCount = 0 ; up->iLoopbackCount = 0 ; for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) { up->bLoopbackTimeout[i] = false ; } if (iTeljjyNextState[up->iClockEvent][up->iClockState] == TELJJY_STATE_IDLE ) { /* Telephone JJY state is changing to IDLE just now */ up->iProcessState = JJY_PROCESS_STATE_DONE ; } } up->iClockState = iTeljjyNextState[up->iClockEvent][up->iClockState] ; } if ( iPostEvent != TELJJY_EVENT_NULL ) { up->iClockEvent = iPostEvent ; teljjy_control ( peer, pp, up ) ; } up->iClockEvent = TELJJY_EVENT_NULL ; } /**************************************************************************************************/ static void teljjy_setDelay ( struct peer *peer, struct jjyunit *up ) { char sLog [ MAX_LOGTEXT ] ; int milliSecond, microSecond ; clock_gettime(CLOCK_REALTIME, &(up->delayTime[up->iLoopbackCount])); up->delayTime[up->iLoopbackCount].tv_sec -= up->sendTime[up->iLoopbackCount].tv_sec ; up->delayTime[up->iLoopbackCount].tv_nsec -= up->sendTime[up->iLoopbackCount].tv_nsec ; if ( up->delayTime[up->iLoopbackCount].tv_nsec < 0 ) { up->delayTime[up->iLoopbackCount].tv_sec -- ; up->delayTime[up->iLoopbackCount].tv_nsec += 1000000000 ; } milliSecond = up->delayTime[up->iLoopbackCount].tv_nsec / 1000000 ; microSecond = (up->delayTime[up->iLoopbackCount].tv_nsec/1000) - milliSecond * 1000 ; milliSecond += up->delayTime[up->iLoopbackCount].tv_sec * 1000 ; snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_LOOPBACK_DELAY, milliSecond, microSecond ) ; if ( milliSecond > TELJJY_LOOPBACK_DELAY_THRESHOLD ) { /* Delay > 700 mS */ jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ; } else { /* Delay <= 700 mS */ jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ; } } /**************************************************************************************************/ static int teljjy_getDelay ( struct peer *peer, struct jjyunit *up ) { struct timespec maxTime, minTime, averTime ; int i ; int minIndex = 0, maxIndex = 0, iAverCount = 0 ; int iThresholdSecond, iThresholdMicroSecond ; int iPercent ; minTime.tv_sec = minTime.tv_nsec = 0 ; maxTime.tv_sec = maxTime.tv_nsec = 0 ; iThresholdSecond = TELJJY_LOOPBACK_DELAY_THRESHOLD / 1000 ; iThresholdMicroSecond = ( TELJJY_LOOPBACK_DELAY_THRESHOLD - ( TELJJY_LOOPBACK_DELAY_THRESHOLD / 1000 ) * 1000 ) * 1000 ; up->iLoopbackValidCount = 0 ; for ( i = 0 ; i < MAX_LOOPBACK && i < up->iLoopbackCount ; i ++ ) { if ( up->bLoopbackTimeout[i] || up->delayTime[i].tv_sec > iThresholdSecond || ( up->delayTime[i].tv_sec == iThresholdSecond && up->delayTime[i].tv_nsec/1000 > iThresholdMicroSecond ) ) { continue ; } if ( up->iLoopbackValidCount == 0 ) { minTime.tv_sec = up->delayTime[i].tv_sec ; minTime.tv_nsec = up->delayTime[i].tv_nsec ; maxTime.tv_sec = up->delayTime[i].tv_sec ; maxTime.tv_nsec = up->delayTime[i].tv_nsec ; minIndex = maxIndex = i ; } else if ( minTime.tv_sec > up->delayTime[i].tv_sec || ( minTime.tv_sec == up->delayTime[i].tv_sec && minTime.tv_nsec > up->delayTime[i].tv_nsec ) ) { minTime.tv_sec = up->delayTime[i].tv_sec ; minTime.tv_nsec = up->delayTime[i].tv_nsec ; minIndex = i ; } else if ( maxTime.tv_sec < up->delayTime[i].tv_sec || ( maxTime.tv_sec == up->delayTime[i].tv_sec && maxTime.tv_nsec < up->delayTime[i].tv_nsec ) ) { maxTime.tv_sec = up->delayTime[i].tv_sec ; maxTime.tv_nsec = up->delayTime[i].tv_nsec ; maxIndex = i ; } up->iLoopbackValidCount ++ ; } if ( up->iLoopbackValidCount < 2 ) { return -1 ; } averTime.tv_nsec = 0; for ( i = 0 ; i < MAX_LOOPBACK && i < up->iLoopbackCount ; i ++ ) { if ( up->bLoopbackTimeout[i] || up->delayTime[i].tv_sec > iThresholdSecond || ( up->delayTime[i].tv_sec == iThresholdSecond && up->delayTime[i].tv_nsec/1000 > iThresholdMicroSecond ) ) { continue ; } if ( up->iLoopbackValidCount >= 3 && i == maxIndex ) { continue ; } if ( up->iLoopbackValidCount >= 4 && i == minIndex ) { continue ; } averTime.tv_nsec += up->delayTime[i].tv_nsec ; iAverCount ++ ; } if ( iAverCount == 0 ) { /* This is never happened. */ /* Previous for-if-for blocks assure iAverCount > 0. */ /* This code avoids a claim by the coverity scan tool. */ return -1 ; } /* subtype 101 = 1%, subtype 150 = 50%, subtype 180 = 80% */ iPercent = (int)peer->cfg.mode - 100 ; /* Average delay time in milli second */ return ( ( (averTime.tv_nsec / 1000) / iAverCount ) * iPercent ) / 100000 ; } /******************************/ static int teljjy_idle_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_idle_ignore" ) ; return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_idle_dialout ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_idle_dialout" ) ; modem_connect ( peer->procptr->refclkunit, peer ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_dial_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_dial_ignore" ) ; return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_dial_login ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_dial_login" ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_dial_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_dial_disc" ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_login_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_login_ignore" ) ; return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_login_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_login_disc" ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_login_conn ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { int i ; UNUSED_ARG(peer); UNUSED_ARG(pp); DEBUG_TELJJY_PRINTF( "teljjy_login_conn" ) ; up->bLineError = false ; up->iClockCommandSeq = 0 ; up->iTimestampCount = 0 ; up->iLoopbackCount = 0 ; for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) { up->bLoopbackTimeout[i] = false ; } return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_login_login ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { const char *pCmd ; int iCmdLen ; UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_login_login" ) ; /* Send a guest user ID */ pCmd = "TJJY\r" ; /* Send login ID */ iCmdLen = (int)strlen( pCmd ) ; if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_login_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { DEBUG_TELJJY_PRINTF( "teljjy_login_silent" ) ; if ( write( pp->io.fd, "\r", 1 ) != 1 ) { refclock_report( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, "\r" ) ; up->iTeljjySilentTimer = 0 ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_login_error ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_login_error" ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_conn_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_conn_ignore" ) ; return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_conn_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_conn_disc" ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_conn_send ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { const char *pCmd ; int i, iLen, iNextClockState ; char sLog[120]; DEBUG_TELJJY_PRINTF( "teljjy_conn_send" ) ; if ( up->iClockCommandSeq > 0 && teljjy_command_sequence[up->iClockCommandSeq].command == NULL ) { /* Command sequence has been completed */ return TELJJY_CHANGE_CLOCK_STATE ; } if ( up->iClockCommandSeq == 0 && peer->cfg.mode == 100 ) { /* Skip loopback */ up->iClockCommandSeq = TELJJY_COMMAND_START_SKIP_LOOPBACK ; } else if ( up->iClockCommandSeq == 0 && peer->cfg.mode != 100 ) { /* Loopback start */ up->iLoopbackCount = 0 ; for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) { up->bLoopbackTimeout[i] = false ; } } else if ( up->iClockCommandSeq > 0 && peer->cfg.mode != 100 && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK && up->iLoopbackCount < MAX_LOOPBACK ) { /* Loopback character comes */ DPRINT(1, ( "refclock_jjy.c : teljjy_conn_send : iClockCommandSeq=%d iLoopbackCount=%d\n", up->iClockCommandSeq, up->iLoopbackCount )) ; teljjy_setDelay( peer, up ) ; up->iLoopbackCount ++ ; } up->iClockCommandSeq++ ; pCmd = teljjy_command_sequence[up->iClockCommandSeq].command ; iLen = teljjy_command_sequence[up->iClockCommandSeq].commandLength ; if ( pCmd != NULL ) { if ( write( pp->io.fd, pCmd, (size_t)iLen ) != iLen ) { refclock_report( peer, CEVNT_FAULT ) ; } if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) { /* Loopback character and timestamp */ if ( up->iLoopbackCount < MAX_LOOPBACK ) { clock_gettime(CLOCK_REALTIME, &(up->sendTime[up->iLoopbackCount])); up->bLoopbackMode = true ; } else { /* This else-block is never come. */ /* This code avoid wrong report of the coverity static analysis scan tool. */ snprintf( sLog, sizeof(sLog)-1, "refclock_jjy.c ; teljjy_conn_send ; iClockCommandSeq=%d iLoopbackCount=%d MAX_LOOPBACK=%d", up->iClockCommandSeq, up->iLoopbackCount, MAX_LOOPBACK ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_BUG, sLog ) ; //msyslog ( LOG_ERR, sLog ) ; up->bLoopbackMode = false ; } } else { /* Regular command */ up->bLoopbackMode = false ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; if ( teljjy_command_sequence[up->iClockCommandSeq+1].command == NULL ) { /* Last command of the command sequence */ iNextClockState = TELJJY_CHANGE_CLOCK_STATE ; } else { /* More commands to be issued */ iNextClockState = TELJJY_STAY_CLOCK_STATE ; } } else { iNextClockState = TELJJY_CHANGE_CLOCK_STATE ; } return iNextClockState ; } /******************************/ static int teljjy_conn_data ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { char *pBuf ; int iLen, rc ; char sLog [ MAX_LOGTEXT ] ; char bAdjustment ; DEBUG_TELJJY_PRINTF( "teljjy_conn_data" ) ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength == iLen && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK && up->sTextBuf[0] == *(teljjy_command_sequence[up->iClockCommandSeq].command) && up->iLoopbackCount < MAX_LOOPBACK ) { /* Loopback */ teljjy_setDelay( peer, up ) ; up->iLoopbackCount ++ ; } else if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength == iLen && strncmp( pBuf, teljjy_command_sequence[up->iClockCommandSeq].command, (size_t) iLen ) == 0 ) { /* Maybe echoback */ jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, JJY_CLOCKSTATS_MESSAGE_ECHOBACK ) ; } else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_4DATE ) { /* 4DATE -> YYYYMMDD */ rc = sscanf ( pBuf, "%4d%2d%2d", &up->year, &up->month, &up->day ) ; if ( rc != 3 || up->year < 2000 || 2099 <= up->year || up->month < 1 || 12 < up->month || up->day < 1 || 31 < up->day ) { /* Invalid date */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE, rc, up->year, up->month, up->day ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; } } else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LEAPSEC && ( strncmp( pBuf, " 0", 2 ) == 0 || strncmp( pBuf, "+1", 2 ) == 0 || strncmp( pBuf, "-1", 2 ) == 0 ) ) { /* LEAPSEC -> XX ( One of 0, +1, -1 ) */ rc = sscanf ( pBuf, "%2d", &up->leapsecond ) ; if ( rc != 1 || up->leapsecond < -1 || 1 < up->leapsecond ) { /* Invalid leap second */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_LEAP, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; } } else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_TIME ) { /* TIME -> HHMMSS ( 3 times on second ) */ rc = sscanf ( pBuf, "%2d%2d%2d", &up->hour, &up->minute, &up->second ) ; if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) { /* Invalid time */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME, rc, up->hour, up->minute, up->second ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; up->bLineError = true ; } up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ; up->iTimestampCount++ ; if ( up->iTimestampCount == 6 && ! up->bLineError ) { #ifdef DEBUG printf( "refclock_jjy.c : teljjy_conn_data : bLineError=%d iTimestamp=%d, %d, %d\n", up->bLineError, up->iTimestamp[3], up->iTimestamp[4], up->iTimestamp[5] ) ; #endif bAdjustment = true ; if ( peer->cfg.mode == 100 ) { /* subtype=100 */ up->msecond = 0 ; } else { /* subtype=101 to 110 */ up->msecond = teljjy_getDelay( peer, up ) ; if (up->msecond < 0 ) { up->msecond = 0 ; bAdjustment = false ; } } if ( ( up->iTimestamp[3] - 15 ) <= up->iTimestamp[2] && up->iTimestamp[2] <= up->iTimestamp[3] && ( up->iTimestamp[3] + 1 ) == up->iTimestamp[4] && ( up->iTimestamp[4] + 1 ) == up->iTimestamp[5] ) { /* Non over midnight */ jjy_synctime( peer, pp, up ) ; if ( peer->cfg.mode != 100 ) { if ( bAdjustment ) { snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_DELAY_ADJUST, up->msecond, up->iLoopbackValidCount, MAX_LOOPBACK ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ; } else { snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_DELAY_UNADJUST, up->iLoopbackValidCount, MAX_LOOPBACK ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; } } } } } else if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength != iLen && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) { /* Loopback noise ( Unexpected replay ) */ snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_IGNORE_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ; } else { up->bLineError = true ; snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY, pBuf ) ; jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ; } return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_conn_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { const char *pCmd ; DEBUG_TELJJY_PRINTF( "teljjy_conn_silent" ) ; if ( up->iClockCommandSeq >= 1 && up->iClockCommandSeq < TELJJY_COMMAND_START_SKIP_LOOPBACK ) { /* Loopback */ DPRINT(1, ( "refclock_jjy.c : teljjy_conn_silent : call teljjy_conn_send\n" )) ; if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) { up->bLoopbackTimeout[up->iLoopbackCount] = true ; } up->iTeljjySilentTimer = 0 ; return teljjy_conn_send( peer, pp, up ) ; } else { pCmd = "\r" ; } if ( write( pp->io.fd, pCmd, 1 ) != 1 ) { refclock_report( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; up->iTeljjySilentTimer = 0 ; return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_conn_error ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_conn_error" ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_bye_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_bye_ignore" ) ; return TELJJY_STAY_CLOCK_STATE ; } /******************************/ static int teljjy_bye_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_bye_disc" ) ; return TELJJY_CHANGE_CLOCK_STATE ; } /******************************/ static int teljjy_bye_modem ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_TELJJY_PRINTF( "teljjy_bye_modem" ) ; modem_disconnect ( peer->procptr->refclkunit, peer ) ; return TELJJY_STAY_CLOCK_STATE ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## Modem control finite state machine ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ /* struct jjyunit.iModemState */ #define MODEM_STATE_DISCONNECT 0 #define MODEM_STATE_INITIALIZE 1 #define MODEM_STATE_DAILING 2 #define MODEM_STATE_CONNECT 3 #define MODEM_STATE_ESCAPE 4 /* struct jjyunit.iModemEvent */ #define MODEM_EVENT_NULL 0 #define MODEM_EVENT_INITIALIZE 1 #define MODEM_EVENT_DIALOUT 2 #define MODEM_EVENT_DISCONNECT 3 #define MODEM_EVENT_RESP_OK 4 #define MODEM_EVENT_RESP_CONNECT 5 #define MODEM_EVENT_RESP_RING 6 #define MODEM_EVENT_RESP_NO_CARRIER 7 #define MODEM_EVENT_RESP_ERROR 8 #define MODEM_EVENT_RESP_CONNECT_X 9 #define MODEM_EVENT_RESP_NO_DAILTONE 10 #define MODEM_EVENT_RESP_BUSY 11 #define MODEM_EVENT_RESP_NO_ANSWER 12 #define MODEM_EVENT_RESP_UNKNOWN 13 #define MODEM_EVENT_SILENT 14 #define MODEM_EVENT_TIMEOUT 15 /* Function prototypes */ static void modem_control ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_disc_ignore ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_disc_init ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_init_ignore ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_init_start ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_init_disc ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_init_resp00 ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_init_resp04 ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_dial_ignore ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_dial_dialout ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_dial_escape ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_dial_connect ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_dial_disc ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_conn_ignore ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_conn_escape ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_esc_ignore ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_esc_escape ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_esc_data ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_esc_silent ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int modem_esc_disc ( struct peer *, struct refclockproc *, struct jjyunit * ) ; static int ( *pModemHandler [ ] [ 5 ] ) (struct peer *, struct refclockproc *, struct jjyunit * ) = { /*STATE_DISCONNECT STATE_INITIALIZE STATE_DAILING STATE_CONNECT STATE_ESCAPE */ /* NULL */ { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_ignore }, /* INITIALIZE */ { modem_disc_init , modem_init_start , modem_dial_ignore , modem_conn_ignore, modem_esc_ignore }, /* DIALOUT */ { modem_disc_ignore, modem_init_ignore, modem_dial_dialout, modem_conn_ignore, modem_esc_ignore }, /* DISCONNECT */ { modem_disc_ignore, modem_init_disc , modem_dial_escape , modem_conn_escape, modem_esc_escape }, /* RESP: 0: OK */ { modem_disc_ignore, modem_init_resp00, modem_dial_ignore , modem_conn_ignore, modem_esc_data }, /* RESP: 1: CONNECT */ { modem_disc_ignore, modem_init_ignore, modem_dial_connect, modem_conn_ignore, modem_esc_data }, /* RESP: 2: RING */ { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_data }, /* RESP: 3: NO CARRIER */ { modem_disc_ignore, modem_init_ignore, modem_dial_disc , modem_conn_ignore, modem_esc_data }, /* RESP: 4: ERROR */ { modem_disc_ignore, modem_init_resp04, modem_dial_disc , modem_conn_ignore, modem_esc_data }, /* RESP: 5: CONNECT */ { modem_disc_ignore, modem_init_ignore, modem_dial_connect, modem_conn_ignore, modem_esc_data }, /* RESP: 6: NO DAILTONE */ { modem_disc_ignore, modem_init_ignore, modem_dial_disc , modem_conn_ignore, modem_esc_data }, /* RESP: 7: BUSY */ { modem_disc_ignore, modem_init_ignore, modem_dial_disc , modem_conn_ignore, modem_esc_data }, /* RESP: 8: NO ANSWER */ { modem_disc_ignore, modem_init_ignore, modem_dial_disc , modem_conn_ignore, modem_esc_data }, /* RESP: 9: UNKNOWN */ { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_data }, /* SILENT */ { modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_silent }, /* TIMEOUT */ { modem_disc_ignore, modem_init_disc , modem_dial_escape , modem_conn_escape, modem_esc_disc } } ; static short iModemNextState [ ] [ 5 ] = { /*STATE_DISCONNECT STATE_INITIALIZE STATE_DAILING STATE_CONNECT STATE_ESCAPE */ /* NULL */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* INITIALIZE */ { MODEM_STATE_INITIALIZE, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* DIALOUT */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* DISCONNECT */ { MODEM_STATE_DISCONNECT, MODEM_STATE_DISCONNECT, MODEM_STATE_ESCAPE , MODEM_STATE_ESCAPE , MODEM_STATE_ESCAPE }, /* RESP: 0: OK */ { MODEM_STATE_DISCONNECT, MODEM_STATE_DAILING , MODEM_STATE_DAILING , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 1: CONNECT */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_CONNECT , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 2: RING */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 3: NO CARRIER */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 4: ERROR */ { MODEM_STATE_DISCONNECT, MODEM_STATE_DAILING , MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 5: CONNECT X */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_CONNECT , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 6: NO DAILTONE */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 7: BUSY */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 8: NO ANSWER */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* RESP: 9: UNKNOWN */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE }, /* SILENT */ { MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING , MODEM_STATE_CONNECT, MODEM_STATE_DISCONNECT }, /* TIMEOUT */ { MODEM_STATE_DISCONNECT, MODEM_STATE_DISCONNECT, MODEM_STATE_ESCAPE , MODEM_STATE_ESCAPE , MODEM_STATE_DISCONNECT } } ; static short iModemPostEvent [ ] [ 5 ] = { /*STATE_DISCONNECT STATE_INITIALIZE STATE_DAILING STATE_CONNECT STATE_ESCAPE */ /* NULL */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* INITIALIZE */ { MODEM_EVENT_INITIALIZE, MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* DIALOUT */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* DISCONNECT */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_DISCONNECT, MODEM_EVENT_DISCONNECT, MODEM_EVENT_NULL }, /* RESP: 0: OK */ { MODEM_EVENT_NULL , MODEM_EVENT_DIALOUT, MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 1: CONNECT */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 2: RING */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 3: NO CARRIER */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 4: ERROR */ { MODEM_EVENT_NULL , MODEM_EVENT_DIALOUT, MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 5: CONNECT X */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 6: NO DAILTONE */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 7: BUSY */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 8: NO ANSWER */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* RESP: 9: UNKNOWN */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* SILENT */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_NULL }, /* TIMEOUT */ { MODEM_EVENT_NULL , MODEM_EVENT_NULL , MODEM_EVENT_DISCONNECT, MODEM_EVENT_DISCONNECT, MODEM_EVENT_NULL } } ; static short iModemSilentTimeout [ 5 ] = { 0, 0, 0, 0, 5 } ; static short iModemStateTimeout [ 5 ] = { 0, 20, 90, 0, 20 } ; #define STAY_MODEM_STATE 0 #define CHANGE_MODEM_STATE 1 #ifdef DEBUG #define DEBUG_MODEM_PRINTF(sFunc) { if ( debug ) { printf ( "refclock_jjy.c : %s : iModemState=%d iModemEvent=%d iModemSilentTimer=%d iModemStateTimer=%d\n", sFunc, up->iModemState, up->iModemEvent, up->iModemSilentTimer, up->iModemStateTimer ) ; } } #else #define DEBUG_MODEM_PRINTF(sFunc) #endif /**************************************************************************************************/ static short getModemState ( struct jjyunit *up ) { return up->iModemState ; } /**************************************************************************************************/ static int isModemStateConnect ( short iCheckState ) { return ( iCheckState == MODEM_STATE_CONNECT ) ; } /**************************************************************************************************/ static int isModemStateDisconnect ( short iCheckState ) { return ( iCheckState == MODEM_STATE_DISCONNECT ) ; } /**************************************************************************************************/ static int isModemStateTimerOn ( struct jjyunit *up ) { return ( iModemSilentTimeout[up->iModemState] != 0 || iModemStateTimeout[up->iModemState] != 0 ) ; } /**************************************************************************************************/ static void modem_connect ( int unit, struct peer *peer ) { struct refclockproc *pp; struct jjyunit *up; UNUSED_ARG(unit); pp = peer->procptr ; up = pp->unitptr ; DEBUG_MODEM_PRINTF( "modem_connect" ) ; up->iModemEvent = MODEM_EVENT_INITIALIZE ; modem_control ( peer, pp, up ) ; } /**************************************************************************************************/ static void modem_disconnect ( int unit, struct peer *peer ) { struct refclockproc *pp; struct jjyunit *up; UNUSED_ARG(unit); pp = peer->procptr ; up = pp->unitptr ; DEBUG_MODEM_PRINTF( "modem_disconnect" ) ; up->iModemEvent = MODEM_EVENT_DISCONNECT ; modem_control ( peer, pp, up ) ; } /**************************************************************************************************/ static int modem_receive ( struct recvbuf *rbufp ) { struct peer *peer; struct jjyunit *up; struct refclockproc *pp; char *pBuf ; int iLen ; #ifdef DEBUG static const char *sFunctionName = "modem_receive" ; #endif peer = rbufp->recv_peer ; pp = peer->procptr ; up = pp->unitptr ; DEBUG_MODEM_PRINTF( sFunctionName ) ; if ( up->linediscipline == LDISC_RAW ) { pBuf = up->sTextBuf ; iLen = up->iTextBufLen ; } else { pBuf = pp->a_lastcode ; iLen = pp->lencode ; } if ( iLen == 2 && strncmp( pBuf, "OK" , 2 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_OK ; } else if ( iLen == 7 && strncmp( pBuf, "CONNECT" , 7 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_CONNECT ; } else if ( iLen == 4 && strncmp( pBuf, "RING" , 4 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_RING ; } else if ( iLen == 10 && strncmp( pBuf, "NO CARRIER" , 10 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_CARRIER ; } else if ( iLen == 5 && strncmp( pBuf, "ERROR" , 5 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_ERROR ; } else if ( iLen >= 8 && strncmp( pBuf, "CONNECT " , 8 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_CONNECT_X ; } else if ( iLen == 11 && strncmp( pBuf, "NO DAILTONE", 11 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_DAILTONE ; } else if ( iLen == 4 && strncmp( pBuf, "BUSY" , 4 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_BUSY ; } else if ( iLen == 9 && strncmp( pBuf, "NO ANSWER" , 9 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_ANSWER ; } else { up->iModemEvent = MODEM_EVENT_RESP_UNKNOWN ; } #ifdef DEBUG if ( debug ) { /* SPECIAL DEBUG */ char sResp [ 40 ] ; size_t iCopyLen ; iCopyLen = iLen <= (int)sizeof(sResp)-1 ? (size_t)iLen : sizeof(sResp) - 1U; strlcpy( sResp, pBuf, sizeof(sResp) ) ; printf( "refclock_jjy.c : modem_receive : iLen=%zu " "pBuf=[%s] iModemEvent=%d\n", iCopyLen, sResp, up->iModemEvent ) ; } #endif modem_control ( peer, pp, up ) ; return 0 ; } /**************************************************************************************************/ static void modem_timer ( int unit, struct peer *peer ) { struct refclockproc *pp ; struct jjyunit *up ; UNUSED_ARG(unit); pp = peer->procptr ; up = pp->unitptr ; DEBUG_MODEM_PRINTF( "modem_timer" ) ; if ( iModemSilentTimeout[up->iModemState] != 0 ) { up->iModemSilentTimer++ ; if ( iModemSilentTimeout[up->iModemState] <= up->iModemSilentTimer ) { up->iModemEvent = MODEM_EVENT_SILENT ; modem_control ( peer, pp, up ) ; } } if ( iModemStateTimeout[up->iModemState] != 0 ) { up->iModemStateTimer++ ; if ( iModemStateTimeout[up->iModemState] <= up->iModemStateTimer ) { up->iModemEvent = MODEM_EVENT_TIMEOUT ; modem_control ( peer, pp, up ) ; } } } /**************************************************************************************************/ static void modem_control ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { int rc ; short iPostEvent = MODEM_EVENT_NULL ; DEBUG_MODEM_PRINTF( "modem_control" ) ; rc = (*pModemHandler[up->iModemEvent][up->iModemState])( peer, pp, up ) ; if ( rc == CHANGE_MODEM_STATE ) { iPostEvent = iModemPostEvent[up->iModemEvent][up->iModemState] ; DPRINT(1, ( "refclock_jjy.c : modem_control : iModemState=%d -> %d iPostEvent=%d\n", up->iModemState, iModemNextState[up->iModemEvent][up->iModemState], iPostEvent )) ; if ( up->iModemState != iModemNextState[up->iModemEvent][up->iModemState] ) { up->iModemSilentCount = 0 ; up->iModemStateTimer = 0 ; up->iModemCommandSeq = 0 ; } up->iModemState = iModemNextState[up->iModemEvent][up->iModemState] ; } if ( iPostEvent != MODEM_EVENT_NULL ) { up->iModemEvent = iPostEvent ; modem_control ( peer, pp, up ) ; } up->iModemEvent = MODEM_EVENT_NULL ; } /******************************/ static int modem_disc_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_disc_ignore" ) ; return STAY_MODEM_STATE ; } /******************************/ static int modem_disc_init ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_disc_init" ) ; return CHANGE_MODEM_STATE ; } /******************************/ static int modem_init_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_init_ignore" ) ; return STAY_MODEM_STATE ; } /******************************/ static int modem_init_start ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); DEBUG_MODEM_PRINTF( "modem_init_start" ) ; up->iModemCommandSeq = 0 ; DPRINT(1, ( "refclock_jjy.c : modem_init_start : call modem_init_resp00\n" )) ; return modem_init_resp00( peer, pp, up ) ; } /******************************/ static int modem_init_resp00 ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { const char *pCmd; char cBuf [ 46 ] ; int iCmdLen ; int iErrorCorrection, iSpeakerSwitch, iSpeakerVolume ; int iNextModemState = STAY_MODEM_STATE ; DEBUG_MODEM_PRINTF( "modem_init_resp00" ) ; up->iModemCommandSeq++ ; switch ( up->iModemCommandSeq ) { case 1 : /* En = Echoback 0:Off 1:On */ /* Qn = Result codes 0:On 1:Off */ /* Vn = Result codes 0:Numeric 1:Text */ pCmd = "ATE0Q0V1\r\n" ; break ; case 2 : /* Mn = Speaker switch 0:Off 1:On until remote carrier detected 2:On */ if ( ( pp->sloppyclockflag & CLK_FLAG3 ) == 0 ) { /* refclock jjy unit n flag3 0 */ iSpeakerSwitch = 0 ; } else { /* refclock jjy unit n flag3 1 */ iSpeakerSwitch = 2 ; } /* Ln = Speaker volume 0:Very low 1:Low 2:Middle 3:High */ if ( ( pp->sloppyclockflag & CLK_FLAG4 ) == 0 ) { /* refclock jjy unit n flag4 0 */ iSpeakerVolume = 1 ; } else { /* refclock jjy unit n flag4 1 */ iSpeakerVolume = 2 ; } snprintf(cBuf, sizeof(cBuf), "ATM%dL%d\r\n", iSpeakerSwitch, iSpeakerVolume ) ; pCmd = cBuf ; break ; case 3 : /* &Kn = Flow control 4:XON/XOFF */ pCmd = "AT&K4\r\n" ; break ; case 4 : /* +MS = Protocol V22B:1200,2400bpsiV.22bis) */ pCmd = "AT+MS=V22B\r\n" ; break ; case 5 : /* %Cn = Data compression 0:No data compression */ pCmd = "AT%C0\r\n" ; break ; case 6 : /* \Nn = Error correction 0:Normal mode 1:Direct mode 2:V42,MNP 3:V42,MNP,Normal */ if ( ( pp->sloppyclockflag & CLK_FLAG2 ) == 0 ) { /* refclock jjy unit n flag2 0 */ iErrorCorrection = 0 ; } else { /* refclock jjy unit n flag2 1 */ iErrorCorrection = 3 ; } snprintf(cBuf, sizeof(cBuf), "AT\\N%d\r\n", iErrorCorrection); pCmd = cBuf ; break ; case 7 : /* Hn = Hook 0:Hook-On ( Disconnect ) 1:Hook-Off ( Connect ) */ pCmd = "ATH1\r\n" ; break ; case 8 : /* Initialize completion */ pCmd = NULL ; iNextModemState = CHANGE_MODEM_STATE ; break ; default : pCmd = NULL ; break ; } if ( pCmd != NULL ) { iCmdLen = (int)strlen( pCmd ) ; if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; } return iNextModemState ; } /******************************/ static int modem_init_resp04 ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { DEBUG_MODEM_PRINTF( "modem_init_resp04" ) ; return modem_init_resp00( peer, pp, up ) ; } /******************************/ static int modem_init_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_init_disc" ) ; DPRINT(1, ( "refclock_jjy.c : modem_init_disc : call modem_esc_disc\n" )) ; return CHANGE_MODEM_STATE ; } /******************************/ static int modem_dial_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_dial_ignore" ) ; return STAY_MODEM_STATE ; } /******************************/ static int modem_dial_dialout ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(up); char sCmd [ 46 ] ; int iCmdLen ; char cToneOrPulse ; DEBUG_MODEM_PRINTF( "modem_dial_dialout" ) ; /* Tone or Pulse */ if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) { /* refclock jjy unit n flag1 0 */ cToneOrPulse = 'T' ; } else { /* refclock jjy unit n flag1 1 */ cToneOrPulse = 'P' ; } /* Connect ( Dial number ) */ snprintf( sCmd, sizeof(sCmd), "ATDW%c%s\r\n", cToneOrPulse, *sys_phone ) ; /* Send command */ iCmdLen = (int)strlen( sCmd ) ; if ( write( pp->io.fd, sCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, sCmd ) ; return STAY_MODEM_STATE ; } /******************************/ static int modem_dial_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { DEBUG_MODEM_PRINTF( "modem_dial_escape" ) ; DPRINT(1, ( "refclock_jjy.c : modem_dial_escape : call modem_conn_escape\n" )) ; return modem_conn_escape( peer, pp, up ) ; } /******************************/ static int modem_dial_connect ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_dial_connect" ) ; return CHANGE_MODEM_STATE ; } /******************************/ static int modem_dial_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { DEBUG_MODEM_PRINTF( "modem_dial_disc" ) ; DPRINT(1, ( "refclock_jjy.c : modem_dial_disc : call modem_esc_disc\n" )) ; modem_esc_disc( peer, pp, up ) ; return CHANGE_MODEM_STATE ; } /******************************/ static int modem_conn_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_conn_ignore" ) ; return STAY_MODEM_STATE ; } /******************************/ static int modem_conn_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_conn_escape" ) ; return CHANGE_MODEM_STATE ; } /******************************/ static int modem_esc_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_esc_ignore" ) ; return STAY_MODEM_STATE ; } /******************************/ static int modem_esc_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(up); const char *pCmd ; int iCmdLen ; DEBUG_MODEM_PRINTF( "modem_esc_escape" ) ; /* Escape command ( Go to command mode ) */ pCmd = "+++" ; /* Send command */ iCmdLen = (int)strlen( pCmd ) ; if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; return STAY_MODEM_STATE ; } /******************************/ static int modem_esc_data ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { UNUSED_ARG(peer); UNUSED_ARG(pp); DEBUG_MODEM_PRINTF( "modem_esc_data" ) ; up->iModemSilentTimer = 0 ; return STAY_MODEM_STATE ; } /******************************/ static int modem_esc_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { DEBUG_MODEM_PRINTF( "modem_esc_silent" ) ; up->iModemSilentCount ++ ; if ( up->iModemSilentCount < iModemStateTimeout[up->iModemState] / iModemSilentTimeout[up->iModemState] ) { DPRINT(1, ( "refclock_jjy.c : modem_esc_silent : call modem_esc_escape\n" )) ; modem_esc_escape( peer, pp, up ) ; up->iModemSilentTimer = 0 ; return STAY_MODEM_STATE ; } DPRINT(1, ( "refclock_jjy.c : modem_esc_silent : call modem_esc_disc\n" )) ; return modem_esc_disc( peer, pp, up ) ; } /******************************/ static int modem_esc_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up ) { const char *pCmd ; int iCmdLen ; UNUSED_ARG(up); DEBUG_MODEM_PRINTF( "modem_esc_disc" ) ; /* Disconnect */ pCmd = "ATH0\r\n" ; /* Send command */ iCmdLen = (int)strlen( pCmd ) ; if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) { refclock_report( peer, CEVNT_FAULT ) ; } jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ; return CHANGE_MODEM_STATE ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## jjy_write_clockstats ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ static void jjy_write_clockstats ( struct peer *peer, int iMark, const char *pData ) { char sLog [ MAX_LOGTEXT ] ; const char *pMark ; int iMarkLen, iDataLen ; switch ( iMark ) { case JJY_CLOCKSTATS_MARK_JJY : pMark = "JJY " ; break ; case JJY_CLOCKSTATS_MARK_SEND : pMark = "--> " ; break ; case JJY_CLOCKSTATS_MARK_RECEIVE : pMark = "<-- " ; break ; case JJY_CLOCKSTATS_MARK_INFORMATION : pMark = "--- " ; break ; case JJY_CLOCKSTATS_MARK_ATTENTION : pMark = "=== " ; break ; case JJY_CLOCKSTATS_MARK_WARNING : pMark = "-W- " ; break ; case JJY_CLOCKSTATS_MARK_ERROR : pMark = "-X- " ; break ; case JJY_CLOCKSTATS_MARK_BUG : pMark = "!!! " ; break ; default : pMark = "" ; break ; } iDataLen = (int)strlen( pData ) ; iMarkLen = (int)strlen( pMark ) ; strlcpy( sLog, pMark, sizeof( sLog )) ; printableString( sLog+iMarkLen, (int)sizeof(sLog)-iMarkLen, pData, iDataLen ) ; DPRINT(1, ( "refclock_jjy.c : clockstats : %s\n", sLog )) ; record_clock_stats( peer, sLog ) ; } /*################################################################################################*/ /*################################################################################################*/ /*## ##*/ /*## printableString ##*/ /*## ##*/ /*################################################################################################*/ /*################################################################################################*/ static void printableString ( char *sOutput, int iOutputLen, const char *sInput, int iInputLen ) { const char *printableControlChar[] = { "", "", "", "", "", "", "", "", "" , "" , "" , "" , "" , "" , "" , "" , "", "", "", "", "", "", "", "", "", "" , "", "", "" , "" , "" , "" , " " } ; size_t i, j, n ; size_t InputLen; size_t OutputLen; InputLen = (size_t)iInputLen; OutputLen = (size_t)iOutputLen; for ( i = j = 0 ; i < InputLen && j < OutputLen ; i ++ ) { if ( isprint( (unsigned char)sInput[i] ) ) { n = 1 ; if ( j + 1 >= OutputLen ) break ; sOutput[j] = sInput[i] ; } else if ( ( sInput[i] & 0xFF ) < COUNTOF(printableControlChar) ) { n = strlen( printableControlChar[sInput[i] & 0xFF] ) ; if ( j + n + 1 >= OutputLen ) break ; strlcpy( sOutput + j, printableControlChar[sInput[i] & 0xFF], OutputLen - j ) ; } else { n = 5 ; if ( j + n + 1 >= OutputLen ) break ; snprintf( sOutput + j, OutputLen - j, "", (unsigned)(sInput[i] & 0xFF) ) ; } j += n ; } sOutput[min(j, (size_t)iOutputLen - 1)] = '\0' ; } /**************************************************************************************************/ ntpsec-1.1.0+dfsg1/ntpd/wscript0000644000175000017500000001002613252364117016207 0ustar rlaagerrlaager def build(ctx): srcnode = ctx.srcnode.abspath() bldnode = ctx.bldnode.abspath() target3 = ctx.srcnode.make_node('ntpd/version.h') target4 = ctx.srcnode.make_node('wafhelpers/.autorevision-cache') if ctx.variant == "host": bison_source = ["ntp_parser.y"] ctx( features="c src_include bld_include", includes=["%s/ntpd/" % srcnode, "%s/" % ctx.bldnode.parent.abspath() ], source=bison_source, target="bison_obj", ) ctx( cwd=srcnode, rule='VCS_EXTRA=`cat ${SRC[0]}` wafhelpers/autorevision.sh ' '-o ${TGT[1].abspath()} -e VERSION -t h >${TGT[0].abspath()}', source=["../VERSION", '../wafhelpers/autorevision.sh'], target=[target3, target4], ) # Generate Bison and version.h files first. ctx.add_group() keyword_gen_source = ["keyword-gen.c", ] ctx( features="c cprogram bld_include src_include", includes=["%s/ntpd/" % bldnode, "%s/" % ctx.bldnode.parent.abspath() ], install_path=None, source=keyword_gen_source, target="keyword-gen", ) # XXX: needs a dependency to rebuild ntp_keyword.h # when keyword-gen is rebuilt # Make sure keyword-gen is created next. ctx.add_group() ctx( features="c bld_include src_include", rule="%s/ntpd/keyword-gen ${SRC} > ${TGT}" % bldnode, source="ntp_parser.tab.h", target="ntp_keyword.h" ) # Make sure ntp_keyword.h is created last. ctx.add_group() return libntpd_source = [ "ntp_control.c", "ntp_filegen.c", "ntp_leapsec.c", "ntp_monitor.c", # Needed by the restrict code "ntp_restrict.c", "ntp_util.c", ] ctx( features="c bld_include src_include", includes=ctx.env.PLATFORM_INCLUDES, source=libntpd_source, target="libntpd_obj", use="CRYPTO", ) ctx( target="ntpd_lib", features="c cstlib", use="libntpd_obj", # use="libntpd_obj bison_obj", ) use_refclock = "" # XXX: there must be a better way to do this if ctx.env.REFCLOCK_ENABLE: refclock_source = ["ntp_refclock.c", "refclock_conf.c" ] ctx( target="refclock", features="c bld_include src_include", source=refclock_source, ) use_refclock += "refclock" for file, define in ctx.env.REFCLOCK_SOURCE: ctx( defines=["%s=1" % define], features="c bld_include src_include", # XXX: These need to go into config.h # rather than the command line for the individual drivers source="refclock_%s.c" % file, target="refclock_%s" % file, ) use_refclock += " refclock_%s" % file ntpd_source = [ "ntp_config.c", "ntp_io.c", "ntp_loopfilter.c", "ntp_packetstamp.c", "ntp_peer.c", "ntp_proto.c", "ntp_sandbox.c", "ntp_scanner.c", "ntp_signd.c", "ntp_timer.c", "ntpd.c", ctx.bldnode.parent.find_node("host/ntpd/ntp_parser.tab.c") ] ctx( features="c rtems_trace cprogram bld_include src_include", includes=["%s/host/ntpd/" % ctx.bldnode.parent.abspath(), "%s/ntpd/" % srcnode, ] + ctx.env.PLATFORM_INCLUDES, install_path='${SBINDIR}', source=ntpd_source, target="ntpd", use="libntpd_obj ntp M parse RT CAP SECCOMP PTHREAD NTPD " "CRYPTO DNS_SD DNS_SD_INCLUDES %s SOCKET NSL SCF" % use_refclock, ) ctx.manpage(8, "ntpd-man.txt") ctx.manpage(5, "ntp.conf-man.txt") ctx.manpage(5, "ntp.keys-man.txt") ntpsec-1.1.0+dfsg1/ntpd/ntp_proto.c0000644000175000017500000024201113252364117016762 0ustar rlaagerrlaager/* * ntp_proto.c - NTP version 4 protocol machinery * */ #include "config.h" #include "ntpd.h" #include "ntp_endian.h" #include "ntp_stdlib.h" #include "ntp_leapsec.h" #include "ntp_dns.h" #include "timespecops.h" #include #include #ifdef HAVE_LIBSCF_H #include #endif #include /* * Byte order conversion */ #define HTONS_FP(x) (htonl(x)) /* * Generate the wire-format version (that is, big-endian all the way down) * of a timestamp expressed as a 64-bit scalar. */ static inline l_fp_w htonl_fp(l_fp lfp) { l_fp_w lfpw; lfpw.l_ui = htonl(lfpuint(lfp)); lfpw.l_uf = htonl(lfpfrac(lfp)); return lfpw; } /* * Definitions for the clear() routine. We use memset() to clear * the parts of the peer structure which go to zero. These are * used to calculate the start address and length of the area. */ #define CLEAR_TO_ZERO(p) ((char *)&((p)->clear_to_zero)) #define END_CLEAR_TO_ZERO(p) ((char *)&((p)->end_clear_to_zero)) #define LEN_CLEAR_TO_ZERO(p) (END_CLEAR_TO_ZERO(p) - CLEAR_TO_ZERO(p)) /* * traffic shaping parameters */ #define NTP_IBURST 6 /* packets in iburst */ #define RESP_DELAY 1 /* refclock burst delay (s) */ /* * Clock filter algorithm tuning parameters */ #define MAXDISPERSE 16. /* max dispersion */ #define NTP_FWEIGHT .5 /* clock filter weight */ /* * Selection algorithm tuning parameters */ #define NTP_MINCLOCK 3 /* min survivors */ #define NTP_MAXCLOCK 10 /* max candidates */ #define MINDISPERSE .001 /* min distance */ #define CLOCK_SGATE 3. /* popcorn spike gate */ #define NTP_ORPHWAIT 300 /* orphan wait (s) */ /* * pool soliciting restriction duration (s) */ #define POOL_SOLICIT_WINDOW 8 #define DIFF(x, y) (SQUARE((x) - (y))) /* * Dealing with stratum. 0 gets mapped to 16 incoming, and back to 0 * on output. */ #define PKT_TO_STRATUM(s) ((uint8_t)(((s) == (STRATUM_PKT_UNSPEC)) ?\ (STRATUM_UNSPEC) : (s))) #define STRATUM_TO_PKT(s) ((uint8_t)(((s) == (STRATUM_UNSPEC)) ?\ (STRATUM_PKT_UNSPEC) : (s))) /* * peer_select groups statistics for a peer used by clock_select() and * clock_cluster(). */ typedef struct peer_select_tag { struct peer * peer; double synch; /* sync distance */ double error; /* jitter */ double seljit; /* selection jitter */ } peer_select; /* * System variables are declared here. Unless specified otherwise, all * times are in seconds. */ uint8_t sys_leap; /* system leap indicator */ static uint8_t xmt_leap; /* leap indicator sent in client requests */ uint8_t sys_stratum; /* system stratum */ int8_t sys_precision; /* local clock precision (log2 s) */ double sys_rootdelay; /* roundtrip delay to primary source */ double sys_rootdisp; /* dispersion to primary source */ double sys_rootdist; /* only used fror Mode 6 export */ uint32_t sys_refid; /* reference id (network byte order) */ l_fp sys_reftime; /* last update time */ struct peer *sys_peer; /* current peer */ #ifdef ENABLE_LEAP_SMEAR struct leap_smear_info leap_smear; #endif bool leap_sec_in_progress; /* * Rate controls. Leaky buckets are used to throttle the packet * transmission rates in order to protect busy servers such as at NIST * and USNO. There is a counter for each association and another for KoD * packets. The association counter decrements each second, but not * below zero. Each time a packet is sent the counter is incremented by * a configurable value representing the average interval between * packets. A packet is delayed as long as the counter is greater than * zero. Note this does not affect the time value computations. */ /* * Nonspecified system state variables */ l_fp sys_authdelay; /* authentication delay */ double sys_offset; /* current local clock offset */ double sys_mindisp = MINDISPERSE; /* minimum distance (s) */ static double sys_maxdist = MAXDISTANCE; /* selection threshold */ double sys_maxdisp = MAXDISPERSE; /* maximum dispersion */ double sys_jitter; /* system jitter */ static unsigned long sys_epoch; /* last clock update time */ static double sys_clockhop; /* clockhop threshold */ static int leap_vote_ins; /* leap consensus for insert */ static int leap_vote_del; /* leap consensus for delete */ static unsigned long leapsec; /* seconds to next leap (proximity class) */ int peer_ntpdate; /* active peers in ntpdate mode */ static int sys_survivors; /* truest of the truechimers */ /* * TOS and multicast mapping stuff */ static int sys_floor = 0; /* cluster stratum floor */ static int sys_ceiling = STRATUM_UNSPEC - 1; /* cluster stratum ceiling */ int sys_minsane = 1; /* minimum candidates */ static int sys_minclock = NTP_MINCLOCK; /* minimum candidates */ int sys_maxclock = NTP_MAXCLOCK; /* maximum candidates */ int sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */ static int sys_orphwait = NTP_ORPHWAIT; /* orphan wait */ /* * Statistics counters - first the good, then the bad * These get reset every hour if sysstats is enabled. */ uptime_t sys_stattime; /* elapsed time since sysstats reset */ uint64_t sys_received; /* packets received */ uint64_t sys_processed; /* packets for this host */ uint64_t sys_newversion; /* current version */ uint64_t sys_oldversion; /* old version */ uint64_t sys_restricted; /* access denied */ uint64_t sys_badlength; /* bad length or format */ uint64_t sys_badauth; /* bad authentication */ uint64_t sys_declined; /* declined */ uint64_t sys_limitrejected; /* rate exceeded */ uint64_t sys_kodsent; /* KoD sent */ uptime_t use_stattime; /* elapsed time since usestats reset */ double measured_tick; /* non-overridable sys_tick (s) */ static void clock_combine (peer_select *, int, int); static void clock_select (void); static void clock_update (struct peer *); static void fast_xmit (struct recvbuf *, int, keyid_t, int); static int local_refid (struct peer *); static void measure_precision(const bool); static double measure_tick_fuzz(void); static void peer_xmit (struct peer *); static int peer_unfit (struct peer *); static double root_distance (struct peer *); void set_sys_leap(unsigned char new_sys_leap) { sys_leap = new_sys_leap; xmt_leap = sys_leap; /* * Under certain conditions we send faked leap bits to clients, so * eventually change xmt_leap below, but never change LEAP_NOTINSYNC. */ if (xmt_leap != LEAP_NOTINSYNC) { if (leap_sec_in_progress) { /* always send "not sync" */ xmt_leap = LEAP_NOTINSYNC; } #ifdef ENABLE_LEAP_SMEAR else { /* * If leap smear is enabled in general we must * never send a leap second warning to clients, so * make sure we only send "in sync". */ if (leap_smear.enabled) xmt_leap = LEAP_NOWARNING; } #endif /* ENABLE_LEAP_SMEAR */ } } /* Returns false for packets we want to reject out of hand: those with an out-of-range version number or an unsupported mode. */ static bool is_vn_mode_acceptable( struct recvbuf const* rbufp ) { return rbufp->recv_length >= 1 && PKT_VERSION(rbufp->recv_space.X_recv_buffer[0]) >= 1 && PKT_VERSION(rbufp->recv_space.X_recv_buffer[0]) <= 4 && PKT_MODE(rbufp->recv_space.X_recv_buffer[0]) != MODE_PRIVATE && PKT_MODE(rbufp->recv_space.X_recv_buffer[0]) != MODE_UNSPEC; } static bool is_control_packet( struct recvbuf const* rbufp ) { return rbufp->recv_length >= 1 && PKT_VERSION(rbufp->recv_space.X_recv_buffer[0]) <= 4 && PKT_MODE(rbufp->recv_space.X_recv_buffer[0]) == MODE_CONTROL; } /* Free a parsed_pkt structure allocated by parsed_packet(). In the event of a parse error, this function may be called from within parse_packet() while the structure is only partially initialized, so we must be careful not to dereference uninitialized pointers. This is achieved by making sure we use calloc() everywhere in parse_packet(), and then comparing to NULL before dereferencing. */ static void free_packet( struct parsed_pkt *pkt ) { if(pkt == NULL) { return; }; if(pkt->extensions != NULL) { for(size_t i = 0; i < pkt->num_extensions; i++) { free(pkt->extensions[i].body); pkt->extensions[i].body = NULL; } free(pkt->extensions); pkt->extensions = NULL; } free(pkt); } static struct parsed_pkt* parse_packet( struct recvbuf const* rbufp ) { REQUIRE(rbufp != NULL); size_t recv_length = rbufp->recv_length; uint8_t const* recv_buf = rbufp->recv_space.X_recv_buffer; if(recv_length < LEN_PKT_NOMAC) { /* Packet is too short to possibly be valid. */ return NULL; } struct parsed_pkt *pkt = calloc(1, sizeof (struct parsed_pkt)); uint8_t const* bufptr = recv_buf + LEN_PKT_NOMAC; if(pkt == NULL) { goto fail; } /* Parse header fields */ pkt->li_vn_mode = recv_buf[0]; pkt->stratum = recv_buf[1]; pkt->ppoll = recv_buf[2]; pkt->precision = (int8_t)recv_buf[3]; pkt->rootdelay = ntp_be32dec(recv_buf + 4); pkt->rootdisp = ntp_be32dec(recv_buf + 8); memcpy(pkt->refid, recv_buf + 12, REFIDLEN); pkt->reftime = ntp_be64dec(recv_buf + 16); pkt->org = ntp_be64dec(recv_buf + 24); pkt->rec = ntp_be64dec(recv_buf + 32); pkt->xmt = ntp_be64dec(recv_buf + 40); /* These initializations should have already been taken care of by calloc(), but let's be explicit. */ pkt->num_extensions = 0; pkt->extensions = NULL; pkt->keyid_present = false; pkt->keyid = 0; pkt->mac_len = 0; if(PKT_VERSION(pkt->li_vn_mode) > 4) { /* Unsupported version */ goto fail; } else if(PKT_VERSION(pkt->li_vn_mode) == 4) { /* Only version 4 packets support extensions. */ /* Count and validate extensions */ size_t ext_count = 0; size_t extlen = 0; while(bufptr <= recv_buf + recv_length - 28) { extlen = ntp_be16dec(bufptr + 2); if(extlen % 4 != 0 || extlen < 16) { /* Illegal extension length */ goto fail; } if((size_t)(recv_buf + recv_length - bufptr) < extlen) { /* Extension length field points past * end of packet */ goto fail; } bufptr += extlen; ext_count++; } pkt->num_extensions = (unsigned int)ext_count; pkt->extensions = calloc(ext_count, sizeof (struct exten)); if(pkt->extensions == NULL) { goto fail; } /* Copy extensions */ bufptr = recv_buf + LEN_PKT_NOMAC; for(size_t i = 0; i < ext_count; i++) { pkt->extensions[i].type = ntp_be16dec(bufptr); pkt->extensions[i].len = ntp_be16dec(bufptr + 2) - 4; pkt->extensions[i].body = calloc(1, pkt->extensions[i].len); if(pkt->extensions[i].body == NULL) { goto fail; } memcpy(pkt->extensions[i].body, bufptr + 4, pkt->extensions[i].len); bufptr += pkt->extensions[i].len + 4; } } /* Parse the authenticator */ switch(recv_buf + recv_length - bufptr) { case 0: /* No authenticator */ pkt->keyid_present = false; pkt->keyid = 0; pkt->mac_len = 0; break; case 4: /* crypto-NAK */ if(PKT_VERSION(pkt->li_vn_mode) < 3) { /* Only allowed as of NTPv3 */ goto fail; } pkt->keyid_present = true; pkt->keyid = ntp_be32dec(bufptr); pkt->mac_len = 0; break; case 6: /* NTPv2 authenticator, which we allow but strip because we don't support it any more */ if(PKT_VERSION(pkt->li_vn_mode) != 2) { goto fail; } pkt->keyid_present = false; pkt->keyid = 0; pkt->mac_len = 0; break; case 20: /* MD5 authenticator */ if(PKT_VERSION(pkt->li_vn_mode) < 3) { /* Only allowed as of NTPv3 */ goto fail; } pkt->keyid_present = true; pkt->keyid = ntp_be32dec(bufptr); pkt->mac_len = 16; memcpy(pkt->mac, bufptr + 4, 16); break; case 24: /* SHA-1 authenticator */ if(PKT_VERSION(pkt->li_vn_mode) < 3) { /* Only allowed as of NTPv3 */ goto fail; } pkt->keyid_present = true; pkt->keyid = ntp_be32dec(bufptr); pkt->mac_len = 20; memcpy(pkt->mac, bufptr + 4, 20); break; case 72: /* MS-SNTP */ if(PKT_VERSION(pkt->li_vn_mode) != 3) { /* Only allowed for NTPv3 */ goto fail; } /* We don't deal with the MS-SNTP fields, so just strip * them. */ pkt->keyid_present = false; pkt->keyid = 0; pkt->mac_len = 0; break; default: /* Any other length is illegal */ goto fail; } return pkt; fail: free_packet(pkt); return NULL; } /* Returns true if we should not accept any unauthenticated packets from this peer. There are three ways the user can configure this requirement: 1. A 'restrict notrust' command applies to the peer's IP, or, 2. We've configured the peer with a 'key' option, 3. The packet wants to create a new associations, and a 'restrict nopeer' command applies to the peer's IP. The 'peer' argument may be NULL to indicate that we have no current association. In contrast to NTP classic, We don't enforce 'restrict nopeer' against pool-mode responses. */ static bool i_require_authentication( struct peer const* peer, struct parsed_pkt const* pkt, unsigned short restrict_mask ) { bool restrict_notrust = restrict_mask & RES_DONTTRUST; bool peer_has_key = peer != NULL && peer->cfg.peerkey != 0; bool wants_association = PKT_MODE(pkt->li_vn_mode) == MODE_BROADCAST || (peer == NULL && PKT_MODE(pkt->li_vn_mode == MODE_ACTIVE)); bool restrict_nopeer = (restrict_mask & RES_NOPEER) && wants_association; return restrict_notrust || peer_has_key || restrict_nopeer; } static bool is_crypto_nak( struct parsed_pkt const* pkt ) { return pkt->keyid_present && pkt->keyid == 0 && pkt->mac_len == 0; } static bool is_kod( struct parsed_pkt const* pkt ) { return PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC && PKT_TO_STRATUM(pkt->stratum) == STRATUM_UNSPEC; } /* Check the restrictions which can be checked just based on the source IP address and the first byte of the packet, namely RES_IGNORE, RES_FLAKE, RES_FLAKE, RES_NOQUERY, RES_DONTSERVE, and RES_VERSION. */ static bool check_early_restrictions( struct recvbuf const* rbufp, unsigned short restrict_mask ) { return (restrict_mask & RES_IGNORE) || ((restrict_mask & RES_FLAKE) && (double)ntp_random() / 0x7fffffff < .1) || (restrict_mask & (is_control_packet(rbufp) ? RES_NOQUERY : RES_DONTSERVE)) || rbufp->recv_length < 1 || ((restrict_mask & RES_VERSION) && (rbufp->recv_length < 1 || PKT_VERSION(rbufp->recv_space.X_recv_buffer[0]) != NTP_VERSION)); } static void handle_fastxmit( struct recvbuf *rbufp, unsigned short restrict_mask, struct parsed_pkt const* pkt, struct peer *peer, bool request_already_authenticated ) { uint32_t xkeyid; /* This argument is currently unused. */ (void)peer; if (rbufp->dstadr->flags & INT_MCASTOPEN) { sys_restricted++; } /* To prevent exposing an authentication oracle, only MAC the response if the request passed authentication. */ if(request_already_authenticated || (pkt->keyid_present && authdecrypt(pkt->keyid, (uint32_t*)rbufp->recv_space.X_recv_buffer, (int)(rbufp->recv_length - (pkt->mac_len + 4)), (int)(pkt->mac_len + 4)))) { xkeyid = pkt->keyid; } else { xkeyid = 0; } int xmode = PKT_MODE(pkt->li_vn_mode) == MODE_ACTIVE ? MODE_PASSIVE : MODE_SERVER; fast_xmit(rbufp, xmode, xkeyid, restrict_mask); } static void handle_procpkt( struct recvbuf *rbufp, unsigned short restrict_mask, struct parsed_pkt const* pkt, struct peer *peer, bool request_already_authenticated ) { /* These arguments are currently unused. */ (void)restrict_mask; (void)request_already_authenticated; /* Shouldn't happen, but include this for safety. */ if(peer == NULL) { return; } peer->flash &= ~PKT_BOGON_MASK; /* Duplicate detection */ if(pkt->xmt == peer->xmt) { peer->flash |= BOGON1; peer->oldpkt++; return; } /* Origin timestamp validation */ if(PKT_MODE(pkt->li_vn_mode) == MODE_SERVER) { if(peer->outcount == 0) { peer->flash |= BOGON1; peer->oldpkt++; return; } if(pkt->org == 0) { peer->flash |= BOGON3; peer->bogusorg++; return; } else if(pkt->org != peer->org) { peer->flash |= BOGON2; peer->bogusorg++; return; } } else { /* This case should be unreachable. */ sys_declined++; return; } /* We've now cryptographically authenticated the packet if required, and we've checked the origin timestamp, so if we've gotten this far we can safely assume the packet is not spoofed and start updating important peer variables. */ peer->outcount = 0; if(is_kod(pkt)) { if(!memcmp(pkt->refid, "RATE", REFIDLEN)) { peer->selbroken++; report_event(PEVNT_RATE, peer, NULL); if (peer->cfg.minpoll < 10) { peer->cfg.minpoll = 10; } peer->burst = peer->retry = 0; peer->throttle = (NTP_SHIFT + 1) * (1 << peer->cfg.minpoll); poll_update(peer, 10); } return; } if (PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC || PKT_TO_STRATUM(pkt->stratum) < sys_floor || PKT_TO_STRATUM(pkt->stratum) >= sys_ceiling) { peer->flash |= BOGON6; return; } if(scalbn((double)pkt->rootdelay/2.0 + (double)pkt->rootdisp, -16) >= sys_maxdisp) { peer->flash |= BOGON7; return; } /* Compute theta (peer offset), delta (peer distance), and epsilon (peer dispersion) statistics. The timestamps may be large but the difference between them should be small, so it's important to do the subtraction *before* converting to floating point to avoid loss of precision. */ const double t34 = (pkt->xmt >= rbufp->recv_time) ? scalbn((double)(pkt->xmt - rbufp->recv_time), -32) : -scalbn((double)(rbufp->recv_time - pkt->xmt), -32); const double t21 = (pkt->rec >= peer->org) ? scalbn((double)(pkt->rec - peer->org), -32) : -scalbn((double)(peer->org - pkt->rec), -32); const double theta = (t21 + t34) / 2.; const double delta = fabs(t21 - t34); const double epsilon = LOGTOD(sys_precision) + LOGTOD(peer->precision) + clock_phi * delta; /* One final test before we touch any variables that could affect the clock: don't accept any packets with a round trip delay longer than sys_maxdist. Failure to enforce this constraint could allow an on-path attacker, despite authentication, to mess with the clock by adding long artificial delays in one direction but not the other. Anything rejected here should be getting rejected later by clock filtering anyhow, but performing this check early makes the desired security invariant easier to verify. */ if(delta > sys_maxdist) { peer->flash |= BOGON1; /*XXX we should probably allocate a new bogon bit here rather than recycling BOGON1. */ peer->oldpkt++; return; } peer->leap = PKT_LEAP(pkt->li_vn_mode); peer->stratum = min(PKT_TO_STRATUM(pkt->stratum), STRATUM_UNSPEC); peer->pmode = PKT_MODE(pkt->li_vn_mode); peer->precision = pkt->precision; peer->rootdelay = scalbn((double)pkt->rootdelay, -16); peer->rootdisp = scalbn((double)pkt->rootdisp, -16); memcpy(&peer->refid, pkt->refid, REFIDLEN); peer->reftime = pkt->reftime; peer->rec = pkt->rec; peer->xmt = pkt->xmt; peer->dst = rbufp->recv_time; record_raw_stats(peer, /* What we want to be reporting is values in the packet, not the values in the peer structure, but when we reach here they're the same thing. Passing the values in the peer structure is a convenience, because they're already in the l_fp format that record_raw_stats() expects. */ PKT_LEAP(pkt->li_vn_mode), PKT_VERSION(pkt->li_vn_mode), PKT_MODE(pkt->li_vn_mode), PKT_TO_STRATUM(pkt->stratum), pkt->ppoll, pkt->precision, pkt->rootdelay, pkt->rootdisp, /* FIXME: this cast is disgusting */ *(const uint32_t*)pkt->refid, /* This will always be 0 by the time we get here */ peer->outcount); /* If either burst mode is armed, enable the burst. * Compute the headway for the next packet and delay if * necessary to avoid exceeding the threshold. */ if (peer->retry > 0) { peer->retry = 0; if (peer->reach) peer->burst = min(1 << (peer->hpoll - peer->cfg.minpoll), NTP_SHIFT) - 1; else peer->burst = NTP_IBURST - 1; if (peer->burst > 0) peer->nextdate = current_time; } poll_update(peer, peer->hpoll); /* If the peer was previously unreachable, raise a trap. In any * case, mark it reachable. */ if (!peer->reach) { report_event(PEVNT_REACH, peer, NULL); peer->timereachable = current_time; } peer->reach |= 1; /* Hooray! Pass our new sample off to the clock filter. */ clock_filter(peer, theta + peer->cfg.bias, delta, epsilon); } static void handle_manycast( struct recvbuf *rbufp, unsigned short restrict_mask, struct parsed_pkt const* pkt, struct peer *mpeer, bool request_already_authenticated ) { struct peer_ctl mctl; (void)request_already_authenticated; (void)restrict_mask; if(mpeer == NULL) { sys_restricted++; return; }; if(mpeer->cast_flags & MDF_POOL) { mpeer->nextdate = current_time + 1; } /* Don't bother associating with unsynchronized servers */ if (PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC || PKT_TO_STRATUM(pkt->stratum) < sys_floor || PKT_TO_STRATUM(pkt->stratum) >= sys_ceiling || scalbn((double)pkt->rootdelay/2.0 + (double)pkt->rootdisp, -16) >= sys_maxdisp) { return; } memset(&mctl, '\0', sizeof(struct peer_ctl)); mctl.version = PKT_VERSION(pkt->li_vn_mode); mctl.flags = FLAG_PREEMPT | (FLAG_IBURST & mpeer->cfg.flags); mctl.minpoll = mpeer->cfg.minpoll; mctl.maxpoll = mpeer->cfg.maxpoll; mctl.mode = 0; mctl.peerkey = mpeer->cfg.peerkey; newpeer(&rbufp->recv_srcadr, NULL, rbufp->dstadr, MODE_CLIENT, &mctl, MDF_UCAST | MDF_UCLNT, false); } void receive( struct recvbuf *rbufp ) { struct parsed_pkt *pkt = NULL; struct peer *peer = NULL; unsigned short restrict_mask; int match = AM_NOMATCH; bool authenticated = false; sys_received++; if(!is_vn_mode_acceptable(rbufp)) { sys_badlength++; goto done; } #ifdef REFCLOCK restrict_mask = rbufp->network_packet ? restrictions(&rbufp->recv_srcadr) : 0; #else restrict_mask = restrictions(&rbufp->recv_srcadr); #endif if(check_early_restrictions(rbufp, restrict_mask)) { sys_restricted++; goto done; } restrict_mask = ntp_monitor(rbufp, restrict_mask); if (restrict_mask & RES_LIMITED) { sys_limitrejected++; if(!(restrict_mask & RES_KOD)) { goto done; } } if(is_control_packet(rbufp)) { process_control(rbufp, restrict_mask); sys_processed++; goto done; } /* * Version check must be after the query packets, since they * intentionally use an early version. */ { uint8_t hisversion = PKT_VERSION(rbufp->recv_pkt.li_vn_mode); if (hisversion == NTP_VERSION) { sys_newversion++; /* new version */ } else if (!(restrict_mask & RES_VERSION) && hisversion >= NTP_OLDVERSION) { sys_oldversion++; /* previous version */ } else { sys_badlength++; goto done; /* old version */ } } pkt = parse_packet(rbufp); if(pkt == NULL) { sys_badlength++; goto done; } peer = findpeer(rbufp, PKT_MODE(pkt->li_vn_mode), &match); if(peer == NULL && match == AM_MANYCAST) { peer = findmanycastpeer(rbufp); } if(i_require_authentication(peer, pkt, restrict_mask)) { if( /* Check whether an authenticator is even present. */ !pkt->keyid_present || is_crypto_nak(pkt) || /* If we require a specific key from this peer, check that it matches. */ (peer != NULL && peer->cfg.peerkey != 0 && peer->cfg.peerkey != pkt->keyid) || /* Verify the MAC. TODO: rewrite authdecrypt() to give it a better name and a saner interface so we don't have to do this screwy buffer-length arithmetic in order to call it. */ !authdecrypt(pkt->keyid, (uint32_t*)rbufp->recv_space.X_recv_buffer, (int)(rbufp->recv_length - (pkt->mac_len + 4)), (int)(pkt->mac_len + 4))) { sys_badauth++; if(peer != NULL) { peer->badauth++; peer->flash |= BOGON5; } goto done; } else { authenticated = true; } } if (peer != NULL) { peer->received++; peer->timereceived = current_time; } switch(match) { case AM_FXMIT: case AM_NEWPASS: handle_fastxmit(rbufp, restrict_mask, pkt, peer, authenticated); sys_processed++; if (peer != NULL) /* possible during pool query */ peer->processed++; break; case AM_PROCPKT: handle_procpkt(rbufp, restrict_mask, pkt, peer, authenticated); sys_processed++; if (peer != NULL) /* just to be on the safe side */ peer->processed++; break; case AM_MANYCAST: handle_manycast(rbufp, restrict_mask, pkt, peer, authenticated); sys_processed++; if (peer != NULL) /* possible during pool query */ peer->processed++; break; default: /* Everything else is for broadcast modes, which are a security nightmare. So they go to the bit bucket until this improves. */ sys_declined++; break; } done: free_packet(pkt); } /* * transmit - transmit procedure called by poll timeout */ void transmit( struct peer *peer /* peer structure pointer */ ) { uint8_t hpoll; /* * The polling state machine. There are two kinds of machines, * those that never expect a reply (broadcast and manycast * server modes) and those that do (all other modes). The dance * is intricate... */ hpoll = peer->hpoll; /* * In broadcast mode the poll interval is never changed from * minpoll. */ if (peer->cast_flags & MDF_BCAST) { peer->outdate = current_time; if (sys_leap != LEAP_NOTINSYNC) peer_xmit(peer); poll_update(peer, hpoll); return; } /* * Pool associations transmit unicast solicitations when there * are less than a hard limit of 2 * sys_maxclock associations, * and either less than sys_minclock survivors or less than * sys_maxclock associations. The hard limit prevents unbounded * growth in associations if the system clock or network quality * result in survivor count dipping below sys_minclock often. * This was observed testing with pool, where sys_maxclock == 12 * resulted in 60 associations without the hard limit. */ #ifdef ENABLE_DNS_LOOKUP if (peer->cast_flags & MDF_POOL) { peer->outdate = current_time; if ((peer_associations <= 2 * sys_maxclock) && (peer_associations < sys_maxclock || sys_survivors < sys_minclock)) if (!dns_probe(peer)) return; poll_update(peer, hpoll); return; } /* Does server need DNS lookup? */ if (peer->cfg.flags & FLAG_DNS) { peer->outdate = current_time; if (!dns_probe(peer)) return; poll_update(peer, hpoll); return; } #endif /* * In unicast modes the dance is much more intricate. It is * designed to back off whenever possible to minimize network * traffic. */ if (peer->burst == 0) { uint8_t oreach; /* * Update the reachability status. If not heard for * three consecutive polls, stuff infinity in the clock * filter. */ oreach = peer->reach; peer->outdate = current_time; peer->unreach++; peer->reach <<= 1; if (!peer->reach) { /* * Here the peer is unreachable. If it was * previously reachable raise a trap. Send a * burst if enabled. */ clock_filter(peer, 0., 0., sys_maxdisp); if (oreach) { peer_unfit(peer); report_event(PEVNT_UNREACH, peer, NULL); } if ((peer->cfg.flags & FLAG_IBURST) && peer->retry == 0) peer->retry = NTP_RETRY; } else { /* * Here the peer is reachable. Send a burst if * enabled and the peer is fit. Reset unreach * for persistent and ephemeral associations. * Unreach is also reset for survivors in * clock_select(). */ hpoll = sys_poll; if (!(peer->cfg.flags & FLAG_PREEMPT)) peer->unreach = 0; if ((peer->cfg.flags & FLAG_BURST) && peer->retry == 0 && !peer_unfit(peer)) peer->retry = NTP_RETRY; } /* * Watch for timeout. If ephemeral, toss the rascal; * otherwise, bump the poll interval. Note the * poll_update() routine will clamp it to maxpoll. * If preemptible and we have more peers than maxclock, * and this peer has the minimum score of preemptibles, * demobilize. */ if (peer->unreach >= NTP_UNREACH) { hpoll++; /* ephemeral: no FLAG_CONFIG nor FLAG_PREEMPT */ if (!(peer->cfg.flags & (FLAG_CONFIG | FLAG_PREEMPT))) { report_event(PEVNT_RESTART, peer, "timeout"); peer_clear(peer, "TIME", false); unpeer(peer); return; } if ((peer->cfg.flags & FLAG_PREEMPT) && (peer_associations > sys_maxclock) && score_all(peer)) { report_event(PEVNT_RESTART, peer, "timeout"); peer_clear(peer, "TIME", false); unpeer(peer); return; } } } else { peer->burst--; if (peer->burst == 0) { /* * If ntpdate mode and the clock has not been * set and all peers have completed the burst, * we declare a successful failure. */ if (mode_ntpdate) { peer_ntpdate--; if (peer_ntpdate == 0) { msyslog(LOG_NOTICE, "SYNC: no servers found"); if (!termlogit) printf( "SYNC: no servers found\n"); exit(0); } } } } if (peer->retry > 0) peer->retry--; peer_xmit(peer); poll_update(peer, hpoll); } /* * clock_update - Called at system process update intervals. */ static void clock_update( struct peer *peer /* peer structure pointer */ ) { double dtemp; time_t now; #ifdef HAVE_LIBSCF_H char *fmri; #endif /* HAVE_LIBSCF_H */ /* * Update the system state variables. We do this very carefully, * as the poll interval might need to be clamped differently. */ sys_peer = peer; sys_epoch = peer->epoch; if (sys_poll < peer->cfg.minpoll) sys_poll = peer->cfg.minpoll; if (sys_poll > peer->cfg.maxpoll) sys_poll = peer->cfg.maxpoll; poll_update(peer, sys_poll); sys_stratum = min(peer->stratum + 1, STRATUM_UNSPEC); if (peer->stratum == STRATUM_REFCLOCK || peer->stratum == STRATUM_UNSPEC) sys_refid = peer->refid; else sys_refid = addr2refid(&peer->srcadr); /* * Root Dispersion (E) is defined (in RFC 5905) as: * * E = p.epsilon_r + p.epsilon + p.psi + PHI*(s.t - p.t) + |THETA| * * where: * p.epsilon_r is the PollProc's root dispersion * p.epsilon is the PollProc's dispersion * p.psi is the PollProc's jitter * THETA is the combined offset * * NB: Think Hard about where these numbers come from and * what they mean. When did peer->update happen? Has anything * interesting happened since then? What values are the most * defensible? Why? * * DLM thinks this equation is probably the best of all worse choices. */ dtemp = peer->rootdisp + peer->disp + sys_jitter + clock_phi * (current_time - peer->update) + fabs(sys_offset); if (dtemp > sys_mindisp) sys_rootdisp = dtemp; else sys_rootdisp = sys_mindisp; sys_rootdelay = peer->delay + peer->rootdelay; sys_reftime = peer->dst; DPRINT(1, ("clock_update: at %u sample %u associd %d\n", current_time, peer->epoch, peer->associd)); /* * Comes now the moment of truth. Crank the clock discipline and * see what comes out. */ switch (local_clock(peer, sys_offset)) { /* * Clock exceeds panic threshold. Life as we know it ends. */ case -1: #ifdef HAVE_LIBSCF_H /* * For Solaris enter the maintenance mode. */ if ((fmri = getenv("SMF_FMRI")) != NULL) { if (smf_maintain_instance(fmri, 0) < 0) { printf("smf_maintain_instance: %s\n", scf_strerror(scf_error())); exit(1); } /* * Sleep until SMF kills us. */ for (;;) pause(); } #endif /* HAVE_LIBSCF_H */ msyslog(LOG_ERR, "CLOCK: Panic: offset too big: %.3f", sys_offset); exit (1); /* not reached */ /* * Clock was stepped. Flush all time values of all peers. */ case 2: clear_all(); set_sys_leap(LEAP_NOTINSYNC); sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "STEP", REFIDLEN); sys_rootdelay = 0; sys_rootdisp = 0; sys_reftime = 0; sys_jitter = LOGTOD(sys_precision); leapsec_reset_frame(); break; /* * Clock was slewed. Handle the leapsecond stuff. */ case 1: /* * If this is the first time the clock is set, reset the * leap bits. If crypto, the timer will goose the setup * process. */ if (sys_leap == LEAP_NOTINSYNC) { set_sys_leap(LEAP_NOWARNING); /* * If our parent process is waiting for the * first clock sync, send them home satisfied. */ #ifdef HAVE_WORKING_FORK if (waitsync_fd_to_close != -1) { close(waitsync_fd_to_close); waitsync_fd_to_close = -1; DPRINT(1, ("notified parent --wait-sync is done\n")); } #endif /* HAVE_WORKING_FORK */ } /* * If there is no leap second pending and the number of * survivor leap bits is greater than half the number of * survivors, try to schedule a leap for the end of the * current month. (This only works if no leap second for * that range is in the table, so doing this more than * once is mostly harmless.) */ if (leapsec == LSPROX_NOWARN) { if (leap_vote_ins > leap_vote_del && leap_vote_ins > sys_survivors / 2) { time(&now); leapsec_add_dyn(true, now); } if (leap_vote_del > leap_vote_ins && leap_vote_del > sys_survivors / 2) { time(&now); leapsec_add_dyn(false, now); } } break; /* * Popcorn spike or step threshold exceeded. Pretend it never * happened. */ default: break; } } /* * poll_update - update peer poll interval */ void poll_update( struct peer *peer, /* peer structure pointer */ uint8_t mpoll ) { uptime_t next, utemp; uint8_t hpoll; /* * This routine figures out when the next poll should be sent. * That turns out to be wickedly complicated. One problem is * that sometimes the time for the next poll is in the past when * the poll interval is reduced. We watch out for races here * between the receive process and the poll process. * * Clamp the poll interval between minpoll and maxpoll. */ hpoll = max(min(peer->cfg.maxpoll, mpoll), peer->cfg.minpoll); peer->hpoll = hpoll; /* * There are three variables important for poll scheduling, the * current time (current_time), next scheduled time (nextdate) * and the earliest time (utemp). The earliest time is 2 s * seconds, but could be more due to rate management. When * sending in a burst, use the earliest time. When not in a * burst but with a reply pending, send at the earliest time * unless the next scheduled time has not advanced. This can * only happen if multiple replies are pending in the same * response interval. Otherwise, send at the later of the next * scheduled time and the earliest time. * * Now we figure out if there is an override. If a burst is in * progress and we get called from the receive process, just * slink away. If called from the poll process, delay 1 s for a * reference clock, otherwise 2 s. */ utemp = current_time + (unsigned long)max(peer->throttle - (NTP_SHIFT - 1) * (1 << peer->cfg.minpoll), ntp_minpkt); if (peer->burst > 0) { if (peer->nextdate > current_time) return; #ifdef REFCLOCK else if (peer->cfg.flags & FLAG_REFCLOCK) peer->nextdate = current_time + RESP_DELAY; #endif /* REFCLOCK */ else peer->nextdate = utemp; /* * The ordinary case. If a retry, use minpoll; if unreachable, * use host poll; otherwise, use the minimum of host and peer * polls; In other words, oversampling is okay but * understampling is evil. Use the maximum of this value and the * headway. If the average headway is greater than the headway * threshold, increase the headway by the minimum interval. */ } else { if (peer->retry > 0) hpoll = peer->cfg.minpoll; else if (!(peer->reach)) hpoll = peer->hpoll; else hpoll = min(peer->ppoll, peer->hpoll); #ifdef REFCLOCK if (peer->cfg.flags & FLAG_REFCLOCK) next = 1U << hpoll; else #endif /* REFCLOCK */ next = ((0x1000UL | (ntp_random() & 0x0ff)) << hpoll) >> 12; next += peer->outdate; if (next > utemp) peer->nextdate = next; else peer->nextdate = utemp; if (peer->throttle > (1 << peer->cfg.minpoll)) peer->nextdate += (unsigned long)ntp_minpkt; } DPRINT(2, ("poll_update: at %u %s poll %d burst %d retry %d head %d early %u next %u\n", current_time, socktoa(&peer->srcadr), peer->hpoll, peer->burst, peer->retry, peer->throttle, utemp - current_time, peer->nextdate - current_time)); } /* * peer_clear - clear peer filter registers. See Section 3.4.8 of the * spec. */ void peer_clear( struct peer *peer, /* peer structure */ const char *ident, /* tally lights */ const bool initializing1 ) { uint8_t u; /* * Clear all values, including the optional crypto values above. */ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO(peer)); peer->ppoll = peer->cfg.maxpoll; peer->hpoll = peer->cfg.minpoll; peer->disp = sys_maxdisp; peer->flash = peer_unfit(peer); peer->jitter = LOGTOD(sys_precision); for (u = 0; u < NTP_SHIFT; u++) { peer->filter_order[u] = u; peer->filter_disp[u] = sys_maxdisp; } #ifdef REFCLOCK if (!(peer->cfg.flags & FLAG_REFCLOCK)) { #endif peer->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_UNSPEC; memcpy(&peer->refid, ident, REFIDLEN); #ifdef REFCLOCK } #endif /* * During initialization use the association count to spread out * the polls at one-second intervals. Passive associations' * first poll is delayed by the "discard minimum" to avoid rate * limiting. Other post-startup new or cleared associations * randomize the first poll over the minimum poll interval to * avoid implosion. */ peer->nextdate = peer->update = peer->outdate = current_time; if (initializing1) { peer->nextdate += (unsigned long)peer_associations; } else if (MODE_PASSIVE == peer->hmode) { peer->nextdate += (unsigned long)ntp_minpkt; } else { /* * Randomizing the next poll interval used to be done with * ntp_random(); any deterministic but uniformly * distributed function of the peer state would be good * enough. Furthermore, changing the function creates no * interop problems. For security reasons (to prevent * hypothetical timing attacks) we want at least one input * to be invisible from outside ntpd; the internal * association ID fits the bill. */ int pseudorandom = peer->associd ^ sock_hash(&peer->srcadr); peer->nextdate += (unsigned long)(pseudorandom % (1 << peer->cfg.minpoll)); } DPRINT(1, ("peer_clear: at %u next %u associd %d refid %s\n", current_time, peer->nextdate, peer->associd, ident)); } /* * clock_filter - add incoming clock sample to filter register and run * the filter procedure to find the best sample. */ void clock_filter( struct peer *peer, /* peer structure pointer */ double sample_offset, /* clock offset */ double sample_delay, /* roundtrip delay */ double sample_disp /* dispersion */ ) { double dst[NTP_SHIFT]; /* distance vector */ int ord[NTP_SHIFT]; /* index vector */ int i, j, k, m; double dtemp, etemp, jtemp; char tbuf[80]; /* * A sample consists of the offset, delay, dispersion and epoch * of arrival. The offset and delay are determined by the on- * wire protocol. The dispersion grows from the last outbound * packet to the arrival of this one increased by the sum of the * peer precision and the system precision as required by the * error budget. First, shift the new arrival into the shift * register discarding the oldest one. */ j = peer->filter_nextpt; peer->filter_offset[j] = sample_offset; peer->filter_delay[j] = sample_delay; peer->filter_disp[j] = sample_disp; peer->filter_epoch[j] = current_time; j = (j + 1) % NTP_SHIFT; peer->filter_nextpt = j; /* * Update dispersions since the last update and at the same * time initialize the distance and index lists. Since samples * become increasingly uncorrelated beyond the Allan intercept, * only under exceptional cases will an older sample be used. * Therefore, the distance list uses a compound metric. If the * dispersion is greater than the maximum dispersion, clamp the * distance at that value. If the time since the last update is * less than the Allan intercept use the delay; otherwise, use * the sum of the delay and dispersion. */ dtemp = clock_phi * (current_time - peer->update); peer->update = current_time; for (i = NTP_SHIFT - 1; i >= 0; i--) { if (i != 0) peer->filter_disp[j] += dtemp; if (peer->filter_disp[j] >= sys_maxdisp) { peer->filter_disp[j] = sys_maxdisp; dst[i] = sys_maxdisp; } else if (peer->update - peer->filter_epoch[j] > (unsigned long)ULOGTOD(allan_xpt)) { dst[i] = peer->filter_delay[j] + peer->filter_disp[j]; } else { dst[i] = peer->filter_delay[j]; } ord[i] = j; j = (j + 1) % NTP_SHIFT; } /* * If the clock has stabilized, sort the samples by distance. */ if (freq_cnt == 0) { for (i = 1; i < NTP_SHIFT; i++) { for (j = 0; j < i; j++) { if (dst[j] > dst[i]) { k = ord[j]; ord[j] = ord[i]; ord[i] = k; etemp = dst[j]; dst[j] = dst[i]; dst[i] = etemp; } } } } /* * Copy the index list to the association structure so ntpq * can see it later. Prune the distance list to leave only * samples less than the maximum dispersion, which disfavors * uncorrelated samples older than the Allan intercept. To * further improve the jitter estimate, of the remainder leave * only samples less than the maximum distance, but keep at * least two samples for jitter calculation. */ m = 0; for (i = 0; i < NTP_SHIFT; i++) { peer->filter_order[i] = (uint8_t) ord[i]; if (dst[i] >= sys_maxdisp || (m >= 2 && dst[i] >= sys_maxdist)) continue; m++; } /* * Compute the dispersion and jitter. The dispersion is weighted * exponentially by NTP_FWEIGHT (0.5) so it is normalized close * to 1.0. The jitter is the RMS differences relative to the * lowest delay sample. */ peer->disp = jtemp = 0; k = ord[0]; for (i = NTP_SHIFT - 1; i >= 0; i--) { j = ord[i]; peer->disp = NTP_FWEIGHT * (peer->disp + peer->filter_disp[j]); if (i < m) jtemp += DIFF(peer->filter_offset[j], peer->filter_offset[k]); } /* * If no acceptable samples remain in the shift register, * quietly tiptoe home leaving only the dispersion. Otherwise, * save the offset, delay and jitter. Note the jitter must not * be less than the precision. */ if (m == 0) { clock_select(); return; } etemp = fabs(peer->offset - peer->filter_offset[k]); peer->offset = peer->filter_offset[k]; peer->delay = peer->filter_delay[k]; peer->jitter = jtemp; if (m > 1) peer->jitter /= m - 1; peer->jitter = SQRT(peer->jitter); /* * If the new sample and the current sample are both valid * and the difference between their offsets exceeds CLOCK_SGATE * (3) times the jitter and the interval between them is less * than twice the host poll interval, consider the new sample * a popcorn spike and ignore it. */ if (peer->disp < sys_maxdist && peer->filter_disp[k] < sys_maxdist && etemp > CLOCK_SGATE * peer->jitter && peer->filter_epoch[k] - peer->epoch < 2. * ULOGTOD(peer->hpoll)) { snprintf(tbuf, sizeof(tbuf), "%.6f s", etemp); report_event(PEVNT_POPCORN, peer, tbuf); return; } /* * A new minimum sample is useful only if it is later than the * last one used. In this design the maximum lifetime of any * sample is not greater than eight times the poll interval, so * the maximum interval between minimum samples is eight * packets. */ if (peer->filter_epoch[k] <= peer->epoch) { DPRINT(2, ("clock_filter: old sample %u\n", current_time - peer->filter_epoch[k])); return; } peer->epoch = peer->filter_epoch[k]; /* * The mitigated sample statistics are saved for later * processing. If not synchronized or not in a burst, tickle the * clock select algorithm. */ record_peer_stats(peer, ctlpeerstatus(peer)); DPRINT(1, ("clock_filter: n %d off %.6f del %.6f dsp %.6f jit %.6f\n", m, peer->offset, peer->delay, peer->disp, peer->jitter)); if (peer->burst == 0 || sys_leap == LEAP_NOTINSYNC) clock_select(); } /* * clock_select - find the pick-of-the-litter clock * * ENABLE_LOCKCLOCK: (1) If the local clock is the prefer peer, it will always * be enabled, even if declared falseticker, (2) only the prefer peer * can be selected as the system peer, (3) if the external source is * down, the system leap bits are set to 11 and the stratum set to * infinity. */ void clock_select(void) { struct peer *peer; int i, j, k, n; int nlist, nl2; int allow; int speer; double d, e, f, g; double high, low; double speermet; double orphmet = 2.0 * UINT32_MAX; /* 2x is greater than */ struct endpoint endp; struct peer *osys_peer; struct peer *sys_prefer = NULL; /* prefer peer */ struct peer *typesystem = NULL; struct peer *typeorphan = NULL; #ifdef REFCLOCK struct peer *typemodem = NULL; struct peer *typelocal = NULL; struct peer *typepps = NULL; #endif /* REFCLOCK */ static struct endpoint *endpoint = NULL; static int *indx = NULL; static peer_select *peers = NULL; static unsigned int endpoint_size = 0; static unsigned int peers_size = 0; static unsigned int indx_size = 0; size_t octets; /* * Initialize and create endpoint, index and peer lists big * enough to handle all associations. */ osys_peer = sys_peer; sys_survivors = 0; #ifdef ENABLE_LOCKCLOCK set_sys_leap(LEAP_NOTINSYNC); sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "DOWN", REFIDLEN); #endif /* ENABLE_LOCKCLOCK */ /* * Allocate dynamic space depending on the number of * associations. */ nlist = 1; for (peer = peer_list; peer != NULL; peer = peer->p_link) nlist++; endpoint_size = ALIGNED_SIZE((unsigned int)nlist * 2 * sizeof(*endpoint)); peers_size = ALIGNED_SIZE((unsigned int)nlist * sizeof(*peers)); indx_size = ALIGNED_SIZE((unsigned int)nlist * 2 * sizeof(*indx)); octets = endpoint_size + peers_size + indx_size; endpoint = erealloc(endpoint, octets); peers = INC_ALIGNED_PTR(endpoint, endpoint_size); indx = INC_ALIGNED_PTR(peers, peers_size); /* * Initially, we populate the island with all the rifraff peers * that happen to be lying around. Those with seriously * defective clocks are immediately booted off the island. Then, * the falsetickers are culled and put to sea. The truechimers * remaining are subject to repeated rounds where the most * unpopular at each round is kicked off. When the population * has dwindled to sys_minclock, the survivors split a million * bucks and collectively crank the chimes. */ nlist = nl2 = 0; /* none yet */ for (peer = peer_list; peer != NULL; peer = peer->p_link) { peer->new_status = CTL_PST_SEL_REJECT; /* * Leave the island immediately if the peer is * unfit to synchronize. */ if (peer_unfit(peer)) continue; /* * If this peer is an orphan parent, elect the * one with the lowest metric defined as the * IPv4 address or the first 64 bits of the * hashed IPv6 address. To ensure convergence * on the same selected orphan, consider as * well that this system may have the lowest * metric and be the orphan parent. If this * system wins, sys_peer will be NULL to trigger * orphan mode in timer(). */ if (peer->stratum == sys_orphan) { uint32_t localmet; uint32_t peermet; if (peer->dstadr != NULL) localmet = ntohl(peer->dstadr->addr_refid); else localmet = UINT32_MAX; peermet = ntohl(addr2refid(&peer->srcadr)); if (peermet < localmet && peermet < orphmet) { typeorphan = peer; orphmet = peermet; } continue; } /* * If this peer could have the orphan parent * as a synchronization ancestor, exclude it * from selection to avoid forming a * synchronization loop within the orphan mesh, * triggering stratum climb to infinity * instability. Peers at stratum higher than * the orphan stratum could have the orphan * parent in ancestry so are excluded. * See http://bugs.ntp.org/2050 */ if (peer->stratum > sys_orphan) continue; #ifdef REFCLOCK /* * This is an attempt to set up fallbacks in case falseticker * elimination leaves no survivors with better service quality. */ if (!(peer->cfg.flags & FLAG_PREFER)) { if (peer->sstclktype == CTL_SST_TS_LOCAL) { if (current_time > orphwait && typelocal == NULL) typelocal = peer; continue; } else if (peer->sstclktype == CTL_SST_TS_TELEPHONE) { if (current_time > orphwait && typemodem == NULL) typemodem = peer; continue; } } #endif /* REFCLOCK */ /* * If we get this far, the peer can stay on the * island, but does not yet have the immunity * idol. */ peer->new_status = CTL_PST_SEL_SANE; f = root_distance(peer); peers[nlist].peer = peer; peers[nlist].error = peer->jitter; peers[nlist].synch = f; nlist++; /* * Insert each interval endpoint on the unsorted * endpoint[] list. */ e = peer->offset; endpoint[nl2].type = -1; /* lower end */ endpoint[nl2].val = e - f; nl2++; endpoint[nl2].type = 1; /* upper end */ endpoint[nl2].val = e + f; nl2++; } /* * Construct sorted indx[] of endpoint[] indexes ordered by * offset. */ for (i = 0; i < nl2; i++) indx[i] = i; for (i = 0; i < nl2; i++) { endp = endpoint[indx[i]]; e = endp.val; k = i; for (j = i + 1; j < nl2; j++) { endp = endpoint[indx[j]]; if (endp.val < e) { e = endp.val; k = j; } } if (k != i) { j = indx[k]; indx[k] = indx[i]; indx[i] = j; } } for (i = 0; i < nl2; i++) DPRINT(3, ("select: endpoint %2d %.6f\n", endpoint[indx[i]].type, endpoint[indx[i]].val)); /* * This is the actual algorithm that cleaves the truechimers * from the falsetickers. The original algorithm was described * in Keith Marzullo's dissertation, but has been modified for * better accuracy. * * Briefly put, we first assume there are no falsetickers, then * scan the candidate list first from the low end upwards and * then from the high end downwards. The scans stop when the * number of intersections equals the number of candidates less * the number of falsetickers. If this doesn't happen for a * given number of falsetickers, we bump the number of * falsetickers and try again. If the number of falsetickers * becomes equal to or greater than half the number of * candidates, the Albanians have won the Byzantine wars and * correct synchronization is not possible. * * Here, nlist is the number of candidates and allow is the * number of falsetickers. Upon exit, the truechimers are the * survivors with offsets not less than low and not greater than * high. There may be none of them. */ low = 1e9; high = -1e9; for (allow = 0; 2 * allow < nlist; allow++) { /* * Bound the interval (low, high) as the smallest * interval containing points from the most sources. */ n = 0; for (i = 0; i < nl2; i++) { low = endpoint[indx[i]].val; n -= endpoint[indx[i]].type; if (n >= nlist - allow) break; } n = 0; for (j = nl2 - 1; j >= 0; j--) { high = endpoint[indx[j]].val; n += endpoint[indx[j]].type; if (n >= nlist - allow) break; } /* * If an interval containing truechimers is found, stop. * If not, increase the number of falsetickers and go * around again. */ if (high > low) break; } /* * Clustering algorithm. Whittle candidate list of falsetickers, * who leave the island immediately. The TRUE peer is always a * truechimer. We must leave at least one peer to collect the * million bucks. * * We assert the correct time is contained in the interval, but * the best offset estimate for the interval might not be * contained in the interval. For this purpose, a truechimer is * defined as the midpoint of an interval that overlaps the * intersection interval. */ j = 0; for (i = 0; i < nlist; i++) { double h; peer = peers[i].peer; h = peers[i].synch; if ((high <= low || peer->offset + h < low || peer->offset - h > high) && !(peer->cfg.flags & FLAG_TRUE)) continue; #ifdef REFCLOCK /* * Eligible PPS peers must survive the intersection * algorithm. Use the first one found, but don't * include any of them in the cluster population. */ if (peer->cfg.flags & FLAG_PPS) { if (typepps == NULL) typepps = peer; if (!(peer->cfg.flags & FLAG_TSTAMP_PPS)) continue; } #endif /* REFCLOCK */ if (j != i) peers[j] = peers[i]; j++; } nlist = j; /* * If no survivors remain at this point, check if we have a modem * driver, local driver or orphan parent in that order. If so, * nominate the first one found as the only survivor. * Otherwise, give up and leave the island to the rats. */ if (nlist == 0) { peers[0].error = 0; peers[0].synch = sys_mindisp; #ifdef REFCLOCK if (typemodem != NULL) { peers[0].peer = typemodem; nlist = 1; } else if (typelocal != NULL) { peers[0].peer = typelocal; nlist = 1; } else #endif /* REFCLOCK */ if (typeorphan != NULL) { peers[0].peer = typeorphan; nlist = 1; } } /* * Mark the candidates at this point as truechimers. */ for (i = 0; i < nlist; i++) { peers[i].peer->new_status = CTL_PST_SEL_SELCAND; DPRINT(2, ("select: survivor %s %f\n", socktoa(&peers[i].peer->srcadr), peers[i].synch)); } /* * Now, vote outliers off the island by select jitter weighted * by root distance. Continue voting as long as there are more * than sys_minclock survivors and the select jitter of the peer * with the worst metric is greater than the minimum peer * jitter. Stop if we are about to discard a TRUE or PREFER * peer, who of course have the immunity idol. */ while (1) { d = 1e9; // Minimum peer jitter e = -1e9; // Worst peer select jitter * synch g = 0; // Worst peer select jitter k = 0; // Index of the worst peer for (i = 0; i < nlist; i++) { if (peers[i].error < d) d = peers[i].error; peers[i].seljit = 0; if (nlist > 1) { f = 0; for (j = 0; j < nlist; j++) f += DIFF(peers[j].peer->offset, peers[i].peer->offset); peers[i].seljit = SQRT(f / (nlist - 1)); } if (peers[i].seljit * peers[i].synch > e) { g = peers[i].seljit; e = peers[i].seljit * peers[i].synch; k = i; } } if (nlist <= max(1, sys_minclock) || g <= d || ((FLAG_TRUE | FLAG_PREFER) & peers[k].peer->cfg.flags)) break; DPRINT(3, ("select: drop %s seljit %.6f jit %.6f\n", socktoa(&peers[k].peer->srcadr), g, d)); if (nlist > sys_maxclock) peers[k].peer->new_status = CTL_PST_SEL_EXCESS; for (j = k + 1; j < nlist; j++) peers[j - 1] = peers[j]; nlist--; } /* * What remains is a list usually not greater than sys_minclock * peers. Note that unsynchronized peers cannot survive this * far. Count and mark these survivors. * * While at it, count the number of leap warning bits found. * This will be used later to vote the system leap warning bit. * If a leap warning bit is found on a reference clock, the vote * is always won. * * Choose the system peer using a hybrid metric composed of the * selection jitter scaled by the root distance augmented by * stratum scaled by sys_mindisp (.001 by default). The goal of * the small stratum factor is to avoid clockhop between a * reference clock and a network peer which has a refclock and * is using an older ntpd, which does not floor sys_rootdisp at * sys_mindisp. * * In contrast, ntpd 4.2.6 and earlier used stratum primarily * in selecting the system peer, using a weight of 1 second of * additional root distance per stratum. This heavy bias is no * longer appropriate, as the scaled root distance provides a * more rational metric carrying the cumulative error budget. */ e = 1e9; speer = 0; leap_vote_ins = 0; leap_vote_del = 0; for (i = 0; i < nlist; i++) { peer = peers[i].peer; peer->unreach = 0; peer->new_status = CTL_PST_SEL_SYNCCAND; sys_survivors++; if (peer->leap == LEAP_ADDSECOND) { if (peer->cfg.flags & FLAG_REFCLOCK) leap_vote_ins = nlist; else if (leap_vote_ins < nlist) leap_vote_ins++; } if (peer->leap == LEAP_DELSECOND) { if (peer->cfg.flags & FLAG_REFCLOCK) leap_vote_del = nlist; else if (leap_vote_del < nlist) leap_vote_del++; } if (peer->cfg.flags & FLAG_PREFER) sys_prefer = peer; speermet = peers[i].seljit * peers[i].synch + peer->stratum * sys_mindisp; if (speermet < e) { e = speermet; speer = i; } } /* * Unless there are at least sys_minsane survivors, leave the * building dark. Otherwise, do a clockhop dance. Ordinarily, * use the selected survivor speer. However, if the current * system peer is not speer, stay with the current system peer * as long as it doesn't get too old or too ugly. */ if (nlist > 0 && nlist >= sys_minsane) { double x; typesystem = peers[speer].peer; if (osys_peer == NULL || osys_peer == typesystem) { sys_clockhop = 0; } else if ((x = fabs(typesystem->offset - osys_peer->offset)) < sys_mindisp) { if ( D_ISZERO_NS(sys_clockhop) ) sys_clockhop = sys_mindisp; else sys_clockhop *= .5; DPRINT(1, ("select: clockhop %d %.6f %.6f\n", j, x, sys_clockhop)); if (fabs(x) < sys_clockhop) typesystem = osys_peer; else sys_clockhop = 0; } else { sys_clockhop = 0; } } /* * Mitigation rules of the game. We have the pick of the * litter in typesystem if any survivors are left. If * there is a prefer peer, use its offset and jitter. * Otherwise, use the combined offset and jitter of all kittens. */ if (typesystem != NULL) { if (sys_prefer == NULL) { typesystem->new_status = CTL_PST_SEL_SYSPEER; clock_combine(peers, sys_survivors, speer); } else { typesystem = sys_prefer; sys_clockhop = 0; typesystem->new_status = CTL_PST_SEL_SYSPEER; sys_offset = typesystem->offset; sys_jitter = typesystem->jitter; sys_rootdist = root_distance(typesystem); } DPRINT(1, ("select: combine offset %.9f jitter %.9f\n", sys_offset, sys_jitter)); } #ifdef REFCLOCK /* * If a PPS driver is lit and the combined offset is less than * 0.4 s, select the driver as the PPS peer and use its offset * and jitter. However, if this is the pps driver, use it only * if there is a prefer peer or there are no survivors and none * are required. */ if (typepps != NULL && fabs(sys_offset) < 0.4 && (!typepps->is_pps_driver || (typepps->is_pps_driver && (sys_prefer != NULL || (typesystem == NULL && sys_minsane == 0))))) { typesystem = typepps; sys_clockhop = 0; typesystem->new_status = CTL_PST_SEL_PPS; sys_offset = typesystem->offset; sys_jitter = typesystem->jitter; sys_rootdist = root_distance(typesystem); DPRINT(1, ("select: pps offset %.9f jitter %.9f\n", sys_offset, sys_jitter)); } else if ( typepps && ( CTL_PST_SEL_PPS == typepps->new_status )) { /* uh, oh, it WAS a valid PPS, but no longer */ typepps->status = CTL_PST_SEL_REJECT; } #endif /* REFCLOCK */ /* * If there are no survivors at this point, there is no * system peer. If so and this is an old update, keep the * current statistics, but do not update the clock. */ if (typesystem == NULL) { if (osys_peer != NULL) { if (sys_orphwait > 0) orphwait = current_time + (unsigned long)sys_orphwait; report_event(EVNT_NOPEER, NULL, NULL); } sys_peer = NULL; for (peer = peer_list; peer != NULL; peer = peer->p_link) peer->status = peer->new_status; return; } /* * Do not use old data, as this may mess up the clock discipline * stability. */ if (typesystem->epoch <= sys_epoch) return; /* * We have found the alpha male. Wind the clock. */ if (osys_peer != typesystem) report_event(PEVNT_NEWPEER, typesystem, NULL); for (peer = peer_list; peer != NULL; peer = peer->p_link) peer->status = peer->new_status; clock_update(typesystem); } static void clock_combine( peer_select * peers, /* survivor list */ int npeers, /* number of survivors */ int syspeer /* index of sys.peer */ ) { int i; double x, y, z, w; y = z = w = 0; for (i = 0; i < npeers; i++) { x = 1. / peers[i].synch; y += x; z += x * peers[i].peer->offset; w += x * DIFF(peers[i].peer->offset, peers[syspeer].peer->offset); } sys_offset = z / y; sys_jitter = SQRT(w / y + SQUARE(peers[syspeer].seljit)); sys_rootdist = peers[syspeer].synch; } /* * root_distance - compute synchronization distance from peer to root */ static double root_distance( struct peer *peer /* peer structure pointer */ ) { double dtemp; /* * Root Distance (LAMBDA) is defined as: * (delta + DELTA)/2 + epsilon + EPSILON + phi * * where: * delta is the round-trip delay * DELTA is the root delay * epsilon is the remote server precision + local precision * + (15 usec each second) * EPSILON is the root dispersion * phi is the peer jitter statistic * * NB: Think hard about why we are using these values, and what * the alternatives are, and the various pros/cons. * * DLM thinks these are probably the best choices from any of the * other worse choices. */ dtemp = (peer->delay + peer->rootdelay) / 2 + LOGTOD(peer->precision) + LOGTOD(sys_precision) + clock_phi * (current_time - peer->update) + peer->rootdisp + peer->jitter; /* * Careful squeak here. The value returned must be greater than * the minimum root dispersion in order to avoid clockhop with * highly precise reference clocks. Note that the root distance * cannot exceed the sys_maxdist, as this is the cutoff by the * selection algorithm. */ if (dtemp < sys_mindisp) dtemp = sys_mindisp; return (dtemp); } /* * peer_xmit - send packet for persistent association. */ static void peer_xmit( struct peer *peer /* peer structure pointer */ ) { struct pkt xpkt; /* transmit packet */ size_t sendlen, authlen; keyid_t xkeyid = 0; /* transmit key ID */ l_fp xmt_tx; if (!peer->dstadr) /* drop peers without interface */ return; xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, peer->cfg.version, peer->hmode); xpkt.stratum = STRATUM_TO_PKT(sys_stratum); xpkt.ppoll = peer->hpoll; xpkt.precision = sys_precision; xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOUFP(sys_rootdelay)); xpkt.rootdisp = HTONS_FP(DTOUFP(sys_rootdisp)); xpkt.reftime = htonl_fp(sys_reftime); xpkt.org = htonl_fp(peer->xmt); xpkt.rec = htonl_fp(peer->dst); /* * If the received packet contains a MAC, the transmitted packet * is authenticated and contains a MAC. If not, the transmitted * packet is not authenticated. */ sendlen = LEN_PKT_NOMAC; if (peer->cfg.peerkey == 0) { /* * Transmit a-priori timestamps. This is paired with * a later call used to record transmission time. */ get_systime(&xmt_tx); peer->org = xmt_tx; xpkt.xmt = htonl_fp(xmt_tx); peer->t21_bytes = (int)sendlen; sendpkt(&peer->srcadr, peer->dstadr, &xpkt, (int)sendlen); peer->sent++; peer->outcount++; peer->throttle += (1 << peer->cfg.minpoll) - 2; DPRINT(1, ("transmit: at %u %s->%s mode %d len %zu\n", current_time, peer->dstadr ? socktoa(&peer->dstadr->sin) : "-", socktoa(&peer->srcadr), peer->hmode, sendlen)); return; } /* * Authentication is enabled, so the transmitted packet must be * authenticated. ` */ /* * Transmit a-priori timestamps */ get_systime(&xmt_tx); peer->org = xmt_tx; xpkt.xmt = htonl_fp(xmt_tx); xkeyid = peer->cfg.peerkey; authlen = (size_t)authencrypt(xkeyid, (uint32_t *)&xpkt, (int)sendlen); if (authlen == 0) { report_event(PEVNT_AUTH, peer, "no key"); peer->flash |= BOGON5; /* auth error */ peer->badauth++; return; } sendlen += authlen; if (sendlen > sizeof(xpkt)) { msyslog(LOG_ERR, "PROTO: buffer overflow %zu", sendlen); exit(1); } peer->t21_bytes = (int)sendlen; sendpkt(&peer->srcadr, peer->dstadr, &xpkt, (int)sendlen); peer->sent++; peer->outcount++; peer->throttle += (1 << peer->cfg.minpoll) - 2; DPRINT(1, ("transmit: at %u %s->%s mode %d keyid %08x len %zu\n", current_time, peer->dstadr ? socktoa(&peer->dstadr->sin) : "-", socktoa(&peer->srcadr), peer->hmode, xkeyid, sendlen)); } #ifdef ENABLE_LEAP_SMEAR static void leap_smear_add_offs(l_fp *t, l_fp *t_recv) { UNUSED_ARG(t_recv); t += leap_smear.offset; } #endif /* ENABLE_LEAP_SMEAR */ /* * fast_xmit - Send packet for nonpersistent association. Note that * neither the source or destination can be a broadcast address. */ static void fast_xmit( struct recvbuf *rbufp, /* receive packet pointer */ int xmode, /* receive mode */ keyid_t xkeyid, /* transmit key ID */ int flags /* restrict mask */ ) { struct pkt xpkt; /* transmit packet structure */ struct pkt *rpkt; /* receive packet structure */ l_fp xmt_tx, xmt_ty; size_t sendlen; /* * Initialize transmit packet header fields from the receive * buffer provided. We leave the fields intact as received, but * set the peer poll at the maximum of the receive peer poll and * the system minimum poll (ntp_minpoll). This is for KoD rate * control and not strictly specification compliant, but doesn't * break anything. */ rpkt = &rbufp->recv_pkt; /* * If this is a kiss-o'-death (KoD) packet, show leap * unsynchronized, stratum zero, reference ID the four-character * kiss code and system root delay. Note we don't reveal the * local time, so these packets can't be used for * synchronization. */ if (flags & RES_KOD) { sys_kodsent++; xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_PKT_UNSPEC; xpkt.ppoll = max(rpkt->ppoll, ntp_minpoll); xpkt.precision = rpkt->precision; memcpy(&xpkt.refid, "RATE", REFIDLEN); xpkt.rootdelay = rpkt->rootdelay; xpkt.rootdisp = rpkt->rootdisp; xpkt.reftime = rpkt->reftime; xpkt.org = rpkt->xmt; xpkt.rec = rpkt->xmt; xpkt.xmt = rpkt->xmt; /* * This is a normal packet. Use the system variables. */ } else { #ifdef ENABLE_LEAP_SMEAR /* * Make copies of the variables which can be affected by smearing. */ l_fp this_ref_time; l_fp this_recv_time; #endif /* * If we are inside the leap smear interval we add the * current smear offset to the packet receive time, to * the packet transmit time, and eventually to the * reftime to make sure the reftime isn't later than * the transmit/receive times. */ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_TO_PKT(sys_stratum); xpkt.ppoll = max(rpkt->ppoll, ntp_minpoll); xpkt.precision = sys_precision; xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOUFP(sys_rootdelay)); xpkt.rootdisp = HTONS_FP(DTOUFP(sys_rootdisp)); #ifdef ENABLE_LEAP_SMEAR this_ref_time = sys_reftime; if (leap_smear.in_progress) { leap_smear_add_offs(&this_ref_time, NULL); xpkt.refid = convertLFPToRefID(leap_smear.offset); DPRINT(2, ("fast_xmit: leap_smear.in_progress: refid %8x, smear %s\n", ntohl(xpkt.refid), lfptoa(leap_smear.offset, 8) )); } xpkt.reftime = htonl_fp(this_ref_time); #else xpkt.reftime = htonl_fp(sys_reftime); #endif xpkt.org = rpkt->xmt; #ifdef ENABLE_LEAP_SMEAR this_recv_time = rbufp->recv_time; if (leap_smear.in_progress) leap_smear_add_offs(&this_recv_time, NULL); xpkt.rec = htonl_fp(this_recv_time); #else xpkt.rec = htonl_fp(rbufp->recv_time); #endif get_systime(&xmt_tx); #ifdef ENABLE_LEAP_SMEAR if (leap_smear.in_progress) leap_smear_add_offs(&xmt_tx, &this_recv_time); #endif xpkt.xmt = htonl_fp(xmt_tx); } #ifdef ENABLE_MSSNTP if (flags & RES_MSSNTP) { send_via_ntp_signd(rbufp, xmode, xkeyid, flags, &xpkt); return; } #endif /* ENABLE_MSSNTP */ /* * If the received packet contains a MAC, the transmitted packet * is authenticated and contains a MAC. If not, the transmitted * packet is not authenticated. */ sendlen = LEN_PKT_NOMAC; if (rbufp->recv_length == sendlen) { sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, &xpkt, (int)sendlen); DPRINT(1, ("transmit: at %u %s->%s mode %d len %zu\n", current_time, socktoa(&rbufp->dstadr->sin), socktoa(&rbufp->recv_srcadr), xmode, sendlen)); return; } /* * The received packet contains a MAC, so the transmitted packet * must be authenticated. For symmetric key cryptography, use * the predefined and trusted symmetric keys to generate the * cryptosum. */ get_systime(&xmt_tx); sendlen += (size_t)authencrypt(xkeyid, (uint32_t *)&xpkt, (int)sendlen); sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, &xpkt, (int)sendlen); get_systime(&xmt_ty); xmt_ty -= xmt_tx; sys_authdelay = xmt_ty; DPRINT(1, ("transmit: at %u %s->%s mode %d keyid %08x len %zu\n", current_time, socktoa(&rbufp->dstadr->sin), socktoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen)); } #ifdef ENABLE_DNS_LOOKUP /* * dns_take_server - process DNS query for server. */ void dns_take_server( struct peer *server, sockaddr_u *rmtadr ) { int restrict_mask; struct peer * pp; if(!(server->cfg.flags & FLAG_DNS)) /* Already got an address for this slot. */ return; pp = findexistingpeer(rmtadr, NULL, NULL, MODE_CLIENT); if (NULL != pp) { /* Already in use. */ msyslog(LOG_INFO, "DNS: Server skipping: %s", socktoa(rmtadr)); return; } msyslog(LOG_INFO, "DNS: Server taking: %s", socktoa(rmtadr)); server->cfg.flags &= (unsigned)~FLAG_DNS; server->srcadr = *rmtadr; peer_update_hash(server); restrict_mask = restrictions(&server->srcadr); if (RES_FLAGS & restrict_mask) { msyslog(LOG_INFO, "DNS: Server poking hole in restrictions for: %s", socktoa(&server->srcadr)); restrict_source(&server->srcadr, false, 0); } peer_refresh_interface(server); server->hpoll = server->cfg.minpoll; server->nextdate = current_time; peer_xmit(server); if (server->cfg.flags & FLAG_IBURST) server->retry = NTP_RETRY; poll_update(server, server->hpoll); } /* dns_take_pool - process DNS query for pool. */ void dns_take_pool( struct peer *pool, /* pool solicitor association */ sockaddr_u * rmtadr ) { struct peer_ctl pctl; struct peer * peer; int restrict_mask; endpt * lcladr; peer = findexistingpeer(rmtadr, NULL, NULL, MODE_CLIENT); if (NULL != peer) { /* This address is already in use. */ msyslog(LOG_INFO, "DNS: Pool skipping: %s", socktoa(rmtadr)); return; } msyslog(LOG_INFO, "DNS: Pool taking: %s", socktoa(rmtadr)); lcladr = findinterface(rmtadr); memset(&pctl, '\0', sizeof(struct peer_ctl)); pctl.version = pool->cfg.version; pctl.minpoll = pool->cfg.minpoll; pctl.maxpoll = pool->cfg.maxpoll; pctl.flags = FLAG_PREEMPT | (FLAG_IBURST & pool->cfg.flags); pctl.mode = 0; pctl.peerkey = 0; peer = newpeer(rmtadr, NULL, lcladr, MODE_CLIENT, &pctl, MDF_UCAST | MDF_UCLNT, false); peer_xmit(peer); if (peer->cfg.flags & FLAG_IBURST) peer->retry = NTP_RETRY; poll_update(peer, peer->hpoll); restrict_mask = restrictions(&peer->srcadr); /* FIXME-DNS: RES_FLAGS includes RES_DONTSERVE?? */ if (RES_FLAGS & restrict_mask) { msyslog(LOG_INFO, "DNS: Pool poking hole in restrictions for: %s", socktoa(&peer->srcadr)); restrict_source(&peer->srcadr, false, current_time + POOL_SOLICIT_WINDOW + 1); } DPRINT(1, ("dns_take_pool: at %u %s->%s pool\n", current_time, latoa(lcladr), socktoa(rmtadr))); } /* * dns_take_status -- setup retry time * There are 2 considerations. * 1) clutter in the log file * 2) the load on the server * (API is broken, no way to get TTL) */ void dns_take_status(struct peer* peer, DNS_Status status) { const char *txt; uint8_t hpoll = peer->hpoll; if (hpoll < 8) hpoll = 8; /* min retry: 256 seconds, ~5 min */ switch (status) { case DNS_good: txt = "good"; if (FLAG_DNS & peer->cfg.flags) /* server: got answer, but didn't like any */ /* (all) already in use ?? */ hpoll += 1; else /* pool: maybe need more */ hpoll = 8; break; case DNS_temp: txt = "temp"; hpoll += 1; break; case DNS_error: txt = "error"; hpoll += 4; break; default: txt = "default"; hpoll += 4; } if (hpoll > 12) hpoll = 12; /* 4096, a bit over an hour */ if ((DNS_good == status) && (MDF_UCAST & peer->cast_flags) && !(FLAG_DNS & peer->cfg.flags)) hpoll = 0; /* server: no more */ msyslog(LOG_INFO, "DNS: dns_take_status: %s=>%s, %d", peer->hostname, txt, hpoll); if (0 == hpoll) return; /* hpoll already in use by new server */ peer->hpoll = hpoll; peer->nextdate = current_time + (1U << hpoll); } /* * dns_new_interface * A new interface is now active * retry danging DNS lookups */ void dns_new_interface(void) { struct peer *p; for (p = peer_list; p != NULL; p = p->p_link) { if ((p->cfg.flags & FLAG_DNS) || (p->cast_flags & MDF_POOL)) { p->hpoll = p->cfg.minpoll; transmit(p); /* does all the work */ } } } #endif /* ENABLE_DNS_LOOKUP */ /* * local_refid(peer) - check peer refid to avoid selecting peers * currently synced to this ntpd. */ static int local_refid( struct peer * p ) { endpt * unicast_ep; if (p->dstadr != NULL && !(INT_MCASTIF & p->dstadr->flags)) unicast_ep = p->dstadr; else unicast_ep = findinterface(&p->srcadr); if (unicast_ep != NULL && p->refid == unicast_ep->addr_refid) return true; else return false; } /* * Determine if the peer is unfit for synchronization * * A peer is unfit for synchronization if * > BOGON10 bad leap or stratum below floor or at or above ceiling * > BOGON11 root distance exceeded for remote peer * > BOGON12 a direct or indirect synchronization loop would form * > BOGON13 unreachable or noselect */ int /* false if fit, true if unfit */ peer_unfit( struct peer *peer /* peer structure pointer */ ) { int rval = 0; /* * A stratum error occurs if (1) the server has never been * synchronized, (2) the server stratum is below the floor or * greater than or equal to the ceiling. */ if (peer->leap == LEAP_NOTINSYNC || peer->stratum < sys_floor || peer->stratum >= sys_ceiling) rval |= BOGON10; /* bad synch or stratum */ /* * A distance error for a remote peer occurs if the root * distance is greater than or equal to the distance threshold * plus the increment due to one host poll interval. */ if (!(peer->cfg.flags & FLAG_REFCLOCK) && root_distance(peer) >= sys_maxdist + clock_phi * ULOGTOD(peer->hpoll)) rval |= BOGON11; /* distance exceeded */ /* Startup bug, https://gitlab.com/NTPsec/ntpsec/issues/68 * introduced with ntp-dev-4.2.7p385 * [2085] Fix root distance and root dispersion calculations. */ if (!(peer->cfg.flags & FLAG_REFCLOCK) && peer->disp >= sys_maxdist + clock_phi * ULOGTOD(peer->hpoll)) rval |= BOGON11; /* Initialization */ /* * A loop error occurs if the remote peer is synchronized to the * local peer or if the remote peer is synchronized to the same * server as the local peer but only if the remote peer is * neither a reference clock nor an orphan. */ if (peer->stratum > 1 && local_refid(peer)) rval |= BOGON12; /* synchronization loop */ /* * An unreachable error occurs if the server is unreachable or * the noselect bit is set. */ if (!peer->reach || (peer->cfg.flags & FLAG_NOSELECT)) rval |= BOGON13; /* unreachable */ peer->flash &= ~PEER_BOGON_MASK; peer->flash |= rval; return (rval); } /* * Find the precision of this particular machine */ #define MINSTEP 20e-9 /* minimum clock increment (s) */ #define MAXSTEP 1 /* maximum clock increment (s) */ #define MINCHANGES 12 /* minimum number of step samples */ #define MAXLOOPS ((int)(1. / MINSTEP)) /* avoid infinite loop */ /* * This routine measures the system precision defined as the minimum of * a sequence of differences between successive readings of the system * clock. However, if a difference is less than MINSTEP, the clock has * been read more than once during a clock tick and the difference is * ignored. We set MINSTEP greater than zero in case something happens * like a cache miss, and to tolerate underlying system clocks which * ensure each reading is strictly greater than prior readings while * using an underlying stepping (not interpolated) clock. * * sys_tick and sys_precision represent the time to read the clock for * systems with high-precision clocks, and the tick interval or step * size for lower-precision stepping clocks. * * This routine also measures the time to read the clock on stepping * system clocks by counting the number of readings between changes of * the underlying clock. With either type of clock, the minimum time * to read the clock is saved as sys_fuzz, and used to ensure the * get_systime() readings always increase and are fuzzed below sys_fuzz. */ void measure_precision(const bool verbose) { /* * With sys_fuzz set to zero, get_systime() fuzzing of low bits * is effectively disabled. trunc_os_clock is false to disable * get_ostime() simulation of a low-precision system clock. */ set_sys_fuzz(0.); trunc_os_clock = false; measured_tick = measure_tick_fuzz(); set_sys_tick_precision(measured_tick); if (verbose) { msyslog(LOG_INFO, "INIT: precision = %.3f usec (%d)", sys_tick * US_PER_S, sys_precision); if (sys_fuzz < sys_tick) { msyslog(LOG_NOTICE, "INIT: fuzz beneath %.3f usec", sys_fuzz * US_PER_S); } } } /* * measure_tick_fuzz() * * measures the minimum time to read the clock (stored in sys_fuzz) * and returns the tick, the larger of the minimum increment observed * between successive clock readings and the time to read the clock. */ double measure_tick_fuzz(void) { l_fp minstep; /* MINSTEP as l_fp */ l_fp val; /* current seconds fraction */ l_fp last; /* last seconds fraction */ l_fp ldiff; /* val - last */ double tick; /* computed tick value */ double diff; long repeats; long max_repeats; int changes; int i; /* log2 precision */ tick = MAXSTEP; max_repeats = 0; repeats = 0; changes = 0; minstep = dtolfp(MINSTEP); get_systime(&last); for (i = 0; i < MAXLOOPS && changes < MINCHANGES; i++) { get_systime(&val); ldiff = val; ldiff -= last; last = val; if (L_ISGT(ldiff, minstep)) { max_repeats = max(repeats, max_repeats); repeats = 0; changes++; diff = lfptod(ldiff); tick = min(diff, tick); } else { repeats++; } } if (changes < MINCHANGES) { msyslog(LOG_ERR, "PROTO: Fatal error: precision could not be measured (MINSTEP too large?)"); exit(1); } if (0 == max_repeats) { set_sys_fuzz(tick); } else { set_sys_fuzz(tick / max_repeats); } return tick; } void set_sys_tick_precision( double tick ) { int i; if (tick > 1.) { msyslog(LOG_ERR, "INIT: unsupported tick %.3f > 1s ignored", tick); return; } if (tick < measured_tick) { msyslog(LOG_ERR, "INIT: tick %.3f less than measured tick %.3f, ignored", tick, measured_tick); return; } else if (tick > measured_tick) { trunc_os_clock = true; msyslog(LOG_NOTICE, "INIT: truncating system clock to multiples of %.9f", tick); } sys_tick = tick; /* * Find the nearest power of two. */ for (i = 0; tick <= 1; i--) tick *= 2; if (tick - 1 > 1 - tick / 2) i++; sys_precision = (int8_t)i; } /* * init_proto - initialize the protocol module's data */ void init_proto(const bool verbose) { l_fp dummy; /* * Fill in the sys_* stuff. Default is don't listen to * broadcasting, require authentication. */ sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "INIT", REFIDLEN); sys_peer = NULL; sys_rootdelay = 0; sys_rootdisp = 0; sys_reftime = 0; sys_jitter = 0; measure_precision(verbose); get_systime(&dummy); sys_survivors = 0; sys_stattime = current_time; orphwait = current_time + (unsigned long)sys_orphwait; proto_clr_stats(); use_stattime = current_time; hardpps_enable = false; stats_control = true; } /* * proto_config - configure the protocol module */ void proto_config( int item, unsigned long value, double dvalue ) { /* * Figure out what he wants to change, then do it */ DPRINT(2, ("proto_config: code %d value %lu dvalue %lf\n", item, value, dvalue)); switch (item) { /* * enable and disable commands - arguments are Boolean. */ #ifdef REFCLOCK case PROTO_CAL: /* refclock calibrate (calibrate) */ cal_enable = value; break; #endif /* REFCLOCK */ case PROTO_KERNEL: /* kernel discipline (kernel) */ select_loop((int)value); break; case PROTO_MONITOR: /* monitoring (monitor) */ if (value) mon_start(MON_ON); else { mon_stop(MON_ON); if (mon_enabled) msyslog(LOG_WARNING, "CONFIG: 'monitor' cannot be disabled while 'limited' is enabled"); } break; case PROTO_NTP: /* NTP discipline (ntp) */ ntp_enable = (bool)value; break; case PROTO_PPS: /* PPS discipline (pps) */ hardpps_enable = (bool)value; break; case PROTO_FILEGEN: /* statistics (stats) */ stats_control = (bool)value; break; /* * tos command - arguments are double, sometimes cast to int */ case PROTO_CEILING: /* stratum ceiling (ceiling) */ sys_ceiling = (int)dvalue; break; case PROTO_FLOOR: /* stratum floor (floor) */ sys_floor = (int)dvalue; break; case PROTO_MAXCLOCK: /* maximum candidates (maxclock) */ sys_maxclock = (int)dvalue; break; case PROTO_MAXDISP: /* maximum dispersion (maxdisp) */ sys_maxdisp = dvalue; break; case PROTO_MAXDIST: /* select threshold (maxdist) */ sys_maxdist = dvalue; break; case PROTO_CALLDELAY: /* modem call delay (mdelay) */ break; /* NOT USED */ case PROTO_MINCLOCK: /* minimum candidates (minclock) */ sys_minclock = (int)dvalue; break; case PROTO_MINDISP: /* minimum distance (mindist) */ sys_mindisp = dvalue; break; case PROTO_MINSANE: /* minimum survivors (minsane) */ sys_minsane = (int)dvalue; break; case PROTO_ORPHAN: /* orphan stratum (orphan) */ sys_orphan = (int)dvalue; break; case PROTO_ORPHWAIT: /* orphan wait (orphwait) */ orphwait -= (unsigned long)sys_orphwait; sys_orphwait = (int)dvalue; orphwait += (unsigned long)sys_orphwait; break; default: msyslog(LOG_NOTICE, "CONFIG: unsupported protocol option %d", item); } } /* * proto_clr_stats - clear protocol stat counters */ void proto_clr_stats(void) { sys_stattime = current_time; sys_received = 0; sys_processed = 0; sys_newversion = 0; sys_oldversion = 0; sys_declined = 0; sys_restricted = 0; sys_badlength = 0; sys_badauth = 0; sys_limitrejected = 0; sys_kodsent = 0; } ntpsec-1.1.0+dfsg1/ntpd/ntp_restrict.c0000644000175000017500000003741613252364117017471 0ustar rlaagerrlaager/* * ntp_restrict.c - determine host restrictions */ #include "config.h" #include #include #include "ntpd.h" #include "ntp_lists.h" #include "ntp_stdlib.h" #include "ntp_assert.h" /* * This code keeps a simple address-and-mask list of hosts we want * to place restrictions on (or remove them from). The restrictions * are implemented as a set of flags which tell you what the host * can't do. There is a subroutine entry to return the flags. The * list is kept sorted to reduce the average number of comparisons * and make sure you get the set of restrictions most specific to * the address. * * The algorithm is that, when looking up a host, it is first assumed * that the default set of restrictions will apply. It then searches * down through the list. Whenever it finds a match it adopts the * match's flags instead. When you hit the point where the sorted * address is greater than the target, you return with the last set of * flags you found. Because of the ordering of the list, the most * specific match will provide the final set of flags. * * This was originally intended to restrict you from sync'ing to your * own broadcasts when you are doing that, by restricting yourself from * your own interfaces. It was also thought it would sometimes be useful * to keep a misbehaving host or two from abusing your primary clock. It * has been expanded, however, to suit the needs of those with more * restrictive access policies. */ /* * We will use two lists, one for IPv4 addresses and one for IPv6 * addresses. This is not protocol-independent but for now I can't * find a way to respect this. We'll check this later... JFB 07/2001 */ #define MASK_IPV6_ADDR(dst, src, msk) \ do { \ int idx; \ for (idx = 0; idx < (int)COUNTOF((dst)->s6_addr); idx++) { \ (dst)->s6_addr[idx] = (src)->s6_addr[idx] \ & (msk)->s6_addr[idx]; \ } \ } while (0) /* * We allocate INC_RESLIST{4|6} entries to the free list whenever empty. * Auto-tune these to be just less than 1KB (leaving at least 16 bytes * for allocator overhead). */ #define INC_RESLIST4 ((1024 - 16) / V4_SIZEOF_RESTRICT_U) #define INC_RESLIST6 ((1024 - 16) / V6_SIZEOF_RESTRICT_U) /* * The restriction list */ restrict_u *restrictlist4; restrict_u *restrictlist6; static int restrictcount; /* count in the restrict lists */ /* * The free list and associated counters. Also some uninteresting * stat counters. */ static restrict_u *resfree4; /* available entries (free list) */ static restrict_u *resfree6; static unsigned long res_calls; static unsigned long res_found; static unsigned long res_not_found; /* * Count number of restriction entries referring to RES_LIMITED, to * control implicit activation/deactivation of the MRU monlist. */ static unsigned long res_limited_refcnt; /* * Our default entries. */ static restrict_u restrict_def4; static restrict_u restrict_def6; /* * "restrict source ..." enabled knob and restriction bits. */ static bool restrict_source_enabled = false; static unsigned short restrict_source_flags; static unsigned short restrict_source_mflags; /* * private functions */ static restrict_u * alloc_res4(void); static restrict_u * alloc_res6(void); static void free_res(restrict_u *, int); static void inc_res_limited(void); static void dec_res_limited(void); static restrict_u * match_restrict4_addr(uint32_t, unsigned short); static restrict_u * match_restrict6_addr(const struct in6_addr *, unsigned short); static restrict_u * match_restrict_entry(const restrict_u *, int); static int res_sorts_before4(restrict_u *, restrict_u *); static int res_sorts_before6(restrict_u *, restrict_u *); /* * init_restrict - initialize the restriction data structures */ void init_restrict(void) { /* * The restriction lists begin with a default entry with address * and mask 0, which will match any entry. The lists are kept * sorted by descending address followed by descending mask: * * address mask * 192.168.0.0 255.255.255.0 kod limited noquery nopeer * 192.168.0.0 255.255.0.0 kod limited * 0.0.0.0 0.0.0.0 kod limited noquery * * The first entry which matches an address is used. With the * example restrictions above, 192.168.0.0/24 matches the first * entry, the rest of 192.168.0.0/16 matches the second, and * everything else matches the third (default). * * Note this achieves the same result a little more efficiently * than the documented behavior, which is to keep the lists * sorted by ascending address followed by ascending mask, with * the _last_ matching entry used. * * An additional wrinkle is we may have multiple entries with * the same address and mask but differing match flags (mflags). * At present there is only one, RESM_NTPONLY. Entries with * RESM_NTPONLY are sorted earlier so they take precedence over * any otherwise similar entry without. Again, this is the same * behavior as but reversed implementation compared to the docs. * */ LINK_SLIST(restrictlist4, &restrict_def4, link); LINK_SLIST(restrictlist6, &restrict_def6, link); restrictcount = 2; } static restrict_u * alloc_res4(void) { const size_t cb = V4_SIZEOF_RESTRICT_U; const size_t count = INC_RESLIST4; restrict_u * rl; restrict_u * res; int i; UNLINK_HEAD_SLIST(res, resfree4, link); if (res != NULL) return res; rl = emalloc_zero(count * cb); /* link all but the first onto free list */ res = (void *)((char *)rl + (count - 1) * cb); for (i = count - 1; i > 0; i--) { LINK_SLIST(resfree4, res, link); res = (void *)((char *)res - cb); } INSIST(rl == res); /* allocate the first */ return res; } static restrict_u * alloc_res6(void) { const size_t cb = V6_SIZEOF_RESTRICT_U; const size_t count = INC_RESLIST6; restrict_u * rl; restrict_u * res; int i; UNLINK_HEAD_SLIST(res, resfree6, link); if (res != NULL) return res; rl = emalloc_zero(count * cb); /* link all but the first onto free list */ res = (void *)((char *)rl + (count - 1) * cb); for (i = count - 1; i > 0; i--) { LINK_SLIST(resfree6, res, link); res = (void *)((char *)res - cb); } INSIST(rl == res); /* allocate the first */ return res; } static void free_res( restrict_u * res, int v6 ) { restrict_u ** plisthead; restrict_u * unlinked; restrictcount--; if (RES_LIMITED & res->flags) dec_res_limited(); if (v6) plisthead = &restrictlist6; else plisthead = &restrictlist4; UNLINK_SLIST(unlinked, *plisthead, res, link, restrict_u); INSIST(unlinked == res); if (v6) { memset(res, '\0', V6_SIZEOF_RESTRICT_U); plisthead = &resfree6; } else { memset(res, '\0', V4_SIZEOF_RESTRICT_U); plisthead = &resfree4; } LINK_SLIST(*plisthead, res, link); } static void inc_res_limited(void) { if (!res_limited_refcnt) mon_start(MON_RES); res_limited_refcnt++; } static void dec_res_limited(void) { res_limited_refcnt--; if (!res_limited_refcnt) mon_stop(MON_RES); } static restrict_u * match_restrict4_addr( uint32_t addr, unsigned short port ) { const int v6 = 0; restrict_u * res; restrict_u * next; for (res = restrictlist4; res != NULL; res = next) { next = res->link; if (res->expire && res->expire <= current_time) free_res(res, v6); if (res->u.v4.addr == (addr & res->u.v4.mask) && (!(RESM_NTPONLY & res->mflags) || NTP_PORT == port)) break; } return res; } static restrict_u * match_restrict6_addr( const struct in6_addr * addr, unsigned short port ) { const int v6 = 1; restrict_u * res; restrict_u * next; struct in6_addr masked; for (res = restrictlist6; res != NULL; res = next) { next = res->link; INSIST(next != res); if (res->expire && res->expire <= current_time) free_res(res, v6); MASK_IPV6_ADDR(&masked, addr, &res->u.v6.mask); if (ADDR6_EQ(&masked, &res->u.v6.addr) && (!(RESM_NTPONLY & res->mflags) || NTP_PORT == (int)port)) break; } return res; } /* * match_restrict_entry - find an exact match on a restrict list. * * Exact match is addr, mask, and mflags all equal. * In order to use more common code for IPv4 and IPv6, this routine * requires the caller to populate a restrict_u with mflags and either * the v4 or v6 address and mask as appropriate. Other fields in the * input restrict_u are ignored. */ static restrict_u * match_restrict_entry( const restrict_u * pmatch, int v6 ) { restrict_u *res; restrict_u *rlist; size_t cb; if (v6) { rlist = restrictlist6; cb = sizeof(pmatch->u.v6); } else { rlist = restrictlist4; cb = sizeof(pmatch->u.v4); } for (res = rlist; res != NULL; res = res->link) if (res->mflags == pmatch->mflags && !memcmp(&res->u, &pmatch->u, cb)) break; return res; } /* * res_sorts_before4 - compare two restrict4 entries * * Returns nonzero if r1 sorts before r2. We sort by descending * address, then descending mask, then descending mflags, so sorting * before means having a higher value. */ static int res_sorts_before4( restrict_u *r1, restrict_u *r2 ) { int r1_before_r2; if (r1->u.v4.addr > r2->u.v4.addr) r1_before_r2 = 1; else if (r1->u.v4.addr < r2->u.v4.addr) r1_before_r2 = 0; else if (r1->u.v4.mask > r2->u.v4.mask) r1_before_r2 = 1; else if (r1->u.v4.mask < r2->u.v4.mask) r1_before_r2 = 0; else if (r1->mflags > r2->mflags) r1_before_r2 = 1; else r1_before_r2 = 0; return r1_before_r2; } /* * res_sorts_before6 - compare two restrict6 entries * * Returns nonzero if r1 sorts before r2. We sort by descending * address, then descending mask, then descending mflags, so sorting * before means having a higher value. */ static int res_sorts_before6( restrict_u *r1, restrict_u *r2 ) { int r1_before_r2; int cmp; cmp = ADDR6_CMP(&r1->u.v6.addr, &r2->u.v6.addr); if (cmp > 0) /* r1->addr > r2->addr */ r1_before_r2 = 1; else if (cmp < 0) /* r2->addr > r1->addr */ r1_before_r2 = 0; else { cmp = ADDR6_CMP(&r1->u.v6.mask, &r2->u.v6.mask); if (cmp > 0) /* r1->mask > r2->mask*/ r1_before_r2 = 1; else if (cmp < 0) /* r2->mask > r1->mask */ r1_before_r2 = 0; else if (r1->mflags > r2->mflags) r1_before_r2 = 1; else r1_before_r2 = 0; } return r1_before_r2; } /* * restrictions - return restrictions for this host */ unsigned short restrictions( sockaddr_u *srcadr ) { restrict_u *match; struct in6_addr *pin6; unsigned short flags; res_calls++; flags = 0; /* IPv4 source address */ if (IS_IPV4(srcadr)) { /* * Ignore any packets with a multicast source address * (this should be done early in the receive process, * not later!) */ if (IN_CLASSD(SRCADR(srcadr))) return (int)RES_IGNORE; match = match_restrict4_addr(SRCADR(srcadr), SRCPORT(srcadr)); match->hitcount++; /* * res_not_found counts only use of the final default * entry, not any "restrict default ntpport ...", which * would be just before the final default. */ if (&restrict_def4 == match) res_not_found++; else res_found++; flags = match->flags; } /* IPv6 source address */ if (IS_IPV6(srcadr)) { pin6 = PSOCK_ADDR6(srcadr); /* * Ignore any packets with a multicast source address * (this should be done early in the receive process, * not later!) */ if (IN6_IS_ADDR_MULTICAST(pin6)) return (int)RES_IGNORE; match = match_restrict6_addr(pin6, SRCPORT(srcadr)); match->hitcount++; if (&restrict_def6 == match) res_not_found++; else res_found++; flags = match->flags; } return (flags); } /* * hack_restrict - add/subtract/manipulate entries on the restrict list */ void hack_restrict( int op, sockaddr_u * resaddr, sockaddr_u * resmask, unsigned short mflags, unsigned short flags, unsigned long expire ) { int v6; restrict_u match; restrict_u * res; restrict_u ** plisthead; DPRINT(1, ("restrict: op %d addr %s mask %s mflags %08x flags %08x\n", op, socktoa(resaddr), socktoa(resmask), mflags, flags)); if (NULL == resaddr) { REQUIRE(NULL == resmask); REQUIRE(RESTRICT_FLAGS == op); restrict_source_flags = flags; restrict_source_mflags = mflags; restrict_source_enabled = true; return; } ZERO(match); /* silence VC9 potentially uninit warnings */ res = NULL; v6 = 0; if (IS_IPV4(resaddr)) { v6 = 0; /* * Get address and mask in host byte order for easy * comparison as uint32_t */ match.u.v4.addr = SRCADR(resaddr); match.u.v4.mask = SRCADR(resmask); match.u.v4.addr &= match.u.v4.mask; } else if (IS_IPV6(resaddr)) { v6 = 1; /* * Get address and mask in network byte order for easy * comparison as byte sequences (e.g. memcmp()) */ match.u.v6.mask = SOCK_ADDR6(resmask); MASK_IPV6_ADDR(&match.u.v6.addr, PSOCK_ADDR6(resaddr), &match.u.v6.mask); } else /* not IPv4 nor IPv6 */ REQUIRE(0); match.flags = flags; match.mflags = mflags; match.expire = expire; res = match_restrict_entry(&match, v6); switch (op) { case RESTRICT_FLAGS: /* * Here we add bits to the flags. If this is a * new restriction add it. */ if (NULL == res) { if (v6) { res = alloc_res6(); memcpy(res, &match, V6_SIZEOF_RESTRICT_U); plisthead = &restrictlist6; } else { res = alloc_res4(); memcpy(res, &match, V4_SIZEOF_RESTRICT_U); plisthead = &restrictlist4; } LINK_SORT_SLIST( *plisthead, res, (v6) ? res_sorts_before6(res, L_S_S_CUR()) : res_sorts_before4(res, L_S_S_CUR()), link, restrict_u); restrictcount++; if (RES_LIMITED & flags) inc_res_limited(); } else { if ((RES_LIMITED & flags) && !(RES_LIMITED & res->flags)) inc_res_limited(); res->flags |= flags; } break; case RESTRICT_UNFLAG: /* * Remove some bits from the flags. If we didn't * find this one, just return. */ if (res != NULL) { if ((RES_LIMITED & res->flags) && (RES_LIMITED & flags)) dec_res_limited(); res->flags &= ~flags; } break; case RESTRICT_REMOVE: case RESTRICT_REMOVEIF: /* * Remove an entry from the table entirely if we * found one. Don't remove the default entry and * don't remove an interface entry. */ if (res != NULL && (RESTRICT_REMOVEIF == op || !(RESM_INTERFACE & res->mflags)) && res != &restrict_def4 && res != &restrict_def6) free_res(res, v6); break; default: /* unknown op */ INSIST(0); break; } } /* * restrict_source - maintains dynamic "restrict source ..." entries as * peers come and go. */ void restrict_source( sockaddr_u * addr, bool farewell, /* false to add, true to remove */ unsigned long expire /* 0 is infinite, valid until */ ) { sockaddr_u onesmask; restrict_u * res; int found_specific; if (!restrict_source_enabled || SOCK_UNSPEC(addr) || IS_MCAST(addr)) return; REQUIRE(AF_INET == AF(addr) || AF_INET6 == AF(addr)); SET_HOSTMASK(&onesmask, AF(addr)); if (farewell) { hack_restrict(RESTRICT_REMOVE, addr, &onesmask, 0, 0, 0); DPRINT(1, ("restrict_source: %s removed", socktoa(addr))); return; } /* * If there is a specific entry for this address, hands * off, as it is condidered more specific than "restrict * server ...". * However, if the specific entry found is a fleeting one * added by pool_xmit() before soliciting, replace it * immediately regardless of the expire value to make way * for the more persistent entry. */ if (IS_IPV4(addr)) { res = match_restrict4_addr(SRCADR(addr), SRCPORT(addr)); found_specific = (SRCADR(&onesmask) == res->u.v4.mask); } else { res = match_restrict6_addr(&SOCK_ADDR6(addr), SRCPORT(addr)); found_specific = ADDR6_EQ(&res->u.v6.mask, &SOCK_ADDR6(&onesmask)); } if (!expire && found_specific && res->expire) { found_specific = 0; free_res(res, IS_IPV6(addr)); } if (found_specific) return; hack_restrict(RESTRICT_FLAGS, addr, &onesmask, restrict_source_mflags, restrict_source_flags, expire); DPRINT(1, ("restrict_source: %s host restriction added\n", socktoa(addr))); } ntpsec-1.1.0+dfsg1/ntpd/ntp_scanner.c0000644000175000017500000005646113252364117017264 0ustar rlaagerrlaager /* ntp_scanner.c * * The source code for a simple lexical analyzer. * * Written By: Sachin Kamboj * University of Delaware * Newark, DE 19711 * Copyright (c) 2006 * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-clause */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "ntpd.h" #include "ntp_config.h" #include "ntp_scanner.h" #include "ntp_debug.h" #include "ntp_parser.tab.h" #include "timespecops.h" /* for D_ISZERO_NS() */ /* ntp_keyword.h declares finite state machine and token text */ #include "ntp_keyword.h" /* SCANNER GLOBAL VARIABLES * ------------------------ */ #define MAX_LEXEME (1024 + 1) /* The maximum size of a lexeme */ static char yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */ static uint32_t conf_file_sum; /* Simple sum of characters read */ static struct FILE_INFO * lex_stack = NULL; /* CONSTANTS AND MACROS * -------------------- */ #define ENDSWITH(str, suff) (strcmp(str + strlen(str) - strlen(suff), suff)==0) #define CONF_ENABLE(s) ENDSWITH(s, ".conf") /* SCANNER GLOBAL VARIABLES * ------------------------ */ static const char special_chars[] = "{}(),;|="; /* FUNCTIONS * --------- */ static int is_keyword(char *lexeme, follby *pfollowedby); /* * keyword() - Return the keyword associated with token T_ identifier. * See also token_name() for the string-ized T_ identifier. * Example: keyword(T_Server) returns "server" * token_name(T_Server) returns "T_Server" */ const char * keyword( int token ) { size_t i; const char *text; i = (size_t)(token - LOWEST_KEYWORD_ID); if (i < COUNTOF(keyword_text)) text = keyword_text[i]; else text = NULL; return (text != NULL) ? text : "(keyword not found)"; } /* FILE & STRING BUFFER INTERFACE * ------------------------------ * * This set out as a couple of wrapper functions around the standard C * fgetc and ungetc functions in order to include positional * bookkeeping. Alas, this is no longer a good solution with nested * input files and the possibility to send configuration commands via * 'ntpq'. * * Now there are a few functions to maintain a stack of nested input * sources (though nesting is only allowd for disk files) and from the * scanner / parser point of view there's no difference between both * types of sources. * * The 'fgetc()' / 'ungetc()' replacements now operate on a FILE_INFO * structure. Instead of trying different 'ungetc()' strategies for file * and buffer based parsing, we keep the backup char in our own * FILE_INFO structure. This is sufficient, as the parser does *not* * jump around via 'seek' or the like, and there's no need to * check/clear the backup store in other places than 'lex_getch()'. */ /* * Allocate an info structure and attach it to a file. * * Note: When 'mode' is NULL, then the INFO block will be set up to * contain a NULL file pointer, as suited for remote config command * parsing. Otherwise having a NULL file pointer is considered an error, * and a NULL info block pointer is returned to indicate failure! * * Note: We use a variable-sized structure to hold a copy of the file * name (or, more proper, the input source description). This is more * secure than keeping a reference to some other storage that might go * out of scope. */ static struct FILE_INFO * lex_open( const char *path, const char *mode ) { struct FILE_INFO *stream; size_t nnambuf; nnambuf = strlen(path); stream = emalloc_zero(sizeof(*stream) + nnambuf); stream->curpos.nline = 1; stream->backch = EOF; /* copy name with memcpy -- trailing NUL already there! */ memcpy(stream->fname, path, nnambuf); if (NULL != mode) { stream->fpi = fopen(path, mode); if (NULL == stream->fpi) { free(stream); stream = NULL; } } return stream; } /* get next character from buffer or file. This will return any putback * character first; it will also make sure the last line is at least * virtually terminated with a '\n'. */ static int lex_getch( struct FILE_INFO *stream ) { int ch; if (NULL == stream || stream->force_eof) return EOF; if (EOF != stream->backch) { ch = stream->backch; stream->backch = EOF; if (stream->fpi) conf_file_sum += (unsigned int)ch; } else if (stream->fpi) { /* fetch next 7-bit ASCII char (or EOF) from file */ while ((ch = fgetc(stream->fpi)) != EOF && ch > SCHAR_MAX) stream->curpos.ncol++; if (EOF != ch) { conf_file_sum += (unsigned int)ch; stream->curpos.ncol++; } } else { /* fetch next 7-bit ASCII char from buffer */ const char * scan; scan = &remote_config.buffer[remote_config.pos]; while ((ch = (uint8_t)*scan) > SCHAR_MAX) { scan++; stream->curpos.ncol++; } if ('\0' != ch) { scan++; stream->curpos.ncol++; } else { ch = EOF; } remote_config.pos = (int)(scan - remote_config.buffer); } /* If the last line ends without '\n', generate one. This * happens most likely on Windows, where editors often have a * sloppy concept of a line. */ if (EOF == ch && stream->curpos.ncol != 0) ch = '\n'; /* update scan position tallies */ if (ch == '\n') { stream->bakpos = stream->curpos; stream->curpos.nline++; stream->curpos.ncol = 0; } return ch; } /* Note: lex_ungetch will fail to track more than one line of push * back. But since it guarantees only one char of back storage anyway, * this should not be a problem. */ static int lex_ungetch( int ch, struct FILE_INFO *stream ) { /* check preconditions */ if (NULL == stream || stream->force_eof) return EOF; if (EOF != stream->backch || EOF == ch) return EOF; /* keep for later reference and update checksum */ stream->backch = (uint8_t)ch; if (stream->fpi) conf_file_sum -= (unsigned int)stream->backch; /* update position */ if (stream->backch == '\n') { stream->curpos = stream->bakpos; stream->bakpos.ncol = -1; } stream->curpos.ncol--; return stream->backch; } /* dispose of an input structure. If the file pointer is not NULL, close * the file. This function does not check the result of 'fclose()'. */ static void lex_close( struct FILE_INFO *stream ) { if (NULL != stream) { if (NULL != stream->fpi) fclose(stream->fpi); free(stream); } } /* INPUT STACK * ----------- * * Nested input sources are a bit tricky at first glance. We deal with * this problem using a stack of input sources, that is, a forward * linked list of FILE_INFO structs. * * This stack is never empty during parsing; while an encounter with EOF * can and will remove nested input sources, removing the last element * in the stack will not work during parsing, and the EOF condition of * the outermost input file remains until the parser folds up. */ static struct FILE_INFO * _drop_stack_do( struct FILE_INFO * head ) { struct FILE_INFO * tail; while (NULL != head) { tail = head->st_next; lex_close(head); head = tail; } return head; } /* Create a singleton input source on an empty lexer stack. This will * fail if there is already an input source, or if the underlying disk * file cannot be opened. * * Returns true if a new input object was successfully created. */ bool lex_init_stack( const char * path, const char * mode ) { if (NULL != lex_stack || NULL == path) return false; //fprintf(stderr, "lex_init_stack(%s)\n", path); lex_stack = lex_open(path, mode); return (NULL != lex_stack); } /* This removes *all* input sources from the stack, leaving the head * pointer as NULL. Any attempt to parse in that state is likely to bomb * with segmentation faults or the like. * * In other words: Use this to clean up after parsing, and do not parse * anything until the next 'lex_init_stack()' succeeded. */ void lex_drop_stack() { lex_stack = _drop_stack_do(lex_stack); } /* Flush the lexer input stack: This will nip all input objects on the * stack (but keeps the current top-of-stack) and marks the top-of-stack * as inactive. Any further calls to lex_getch yield only EOF, and it's * no longer possible to push something back. * * Returns true if there is a head element (top-of-stack) that was not * in the force-eof mode before this call. */ bool lex_flush_stack() { bool retv = false; if (NULL != lex_stack) { retv = !lex_stack->force_eof; lex_stack->force_eof = true; lex_stack->st_next = _drop_stack_do( lex_stack->st_next); } return retv; } /* Reversed string comparison - we want to LIFO directory subfiles so they * actually get evaluated in sort order. */ static int rcmpstring(const void *p1, const void *p2) { return strcmp(*(const char * const *)p1, *(const char * const *)p2); } bool is_directory(const char *path) { struct stat sb; return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode); } void reparent(char *fullpath, size_t fullpathsize, const char *dir, const char *base) { fullpath[0] = '\0'; if (base[0] != DIR_SEP) { char *dirpart = strdup(dir); char *end; strlcpy(fullpath, dirname(dirpart), fullpathsize-2); end = fullpath + strlen(fullpath); *end++ = DIR_SEP; *end++ = '\0'; free(dirpart); } strlcat(fullpath, base, fullpathsize); } /* Push another file on the parsing stack. If the mode is NULL, create a * FILE_INFO suitable for in-memory parsing; otherwise, create a * FILE_INFO that is bound to a local/disc file. Note that 'path' must * not be NULL, or the function will fail. * * If the pathname is a directory, push all subfiles and * subdirectories with paths satisying the predicate CONF_ENABLE(), * recursively depth first to be interpreted in ASCII sort order. * * Relative pathnames are interpreted relative to the directory * of the previous entry on the stack, not the current directory. * This is so "include foo" from within /etc/conf will reliably * pick up /etc/foo. * * Returns true if a new info record was pushed onto the stack. */ bool lex_push_file( const char * path ) { struct FILE_INFO * next = NULL; if (NULL != path) { char fullpath[PATH_MAX]; if (lex_stack != NULL) reparent(fullpath, sizeof(fullpath), lex_stack->fname, path); else strlcpy(fullpath, path, sizeof(fullpath)); //fprintf(stderr, "lex_push_file(%s)\n", fullpath); if (is_directory(fullpath)) { /* directory scanning */ DIR *dfd; struct dirent *dp; char **baselist; int basecount = 0; if ((dfd = opendir(fullpath)) == NULL) return false; baselist = (char **)malloc(sizeof(char *)); while ((dp = readdir(dfd)) != NULL) { if (!CONF_ENABLE(dp->d_name)) continue; baselist[basecount++] = strdup(dp->d_name); baselist = realloc(baselist, (size_t)(basecount+1) * sizeof(char *)); } closedir(dfd); qsort(baselist, (size_t)basecount, sizeof(char *), rcmpstring); for (int i = 0; i < basecount; i++) { char subpath[PATH_MAX]; strlcpy(subpath, fullpath, PATH_MAX); if (strlen(subpath) < PATH_MAX - 1) { char *ep = subpath + strlen(subpath); *ep++ = DIR_SEP; *ep = '\0'; } strlcat(subpath, baselist[i], PATH_MAX); /* This should barf safely if the complete * filename was too long to fit in the buffer. */ lex_push_file(subpath); } for (int i = 0; i < basecount; i++) free(baselist[i]); free(baselist); return basecount > 0; } else { next = lex_open(fullpath, "r"); if (NULL != next) { next->st_next = lex_stack; lex_stack = next; } } } return (NULL != next); } /* Pop, close & free the top of the include stack, unless the stack * contains only a singleton input object. In that case the function * fails, because the parser does not expect the input stack to be * empty. * * Returns true if an object was successfully popped from the stack. */ bool lex_pop_file(void) { struct FILE_INFO * head = lex_stack; struct FILE_INFO * tail = NULL; if (NULL != head) { tail = head->st_next; if (NULL != tail) { lex_stack = tail; lex_close(head); } } return (NULL != tail); } /* Get include nesting level. This currently loops over the stack and * counts elements; but since this is of concern only with an include * statement and the nesting depth has a small limit, there's no * bottleneck expected here. * * Returns the nesting level of includes, that is, the current depth of * the lexer input stack. * * Note: */ size_t lex_level(void) { size_t cnt = 0; struct FILE_INFO *ipf = lex_stack; while (NULL != ipf) { cnt++; ipf = ipf->st_next; } return cnt; } /* check if the current input is from a file */ bool lex_from_file(void) { return (NULL != lex_stack) && (NULL != lex_stack->fpi); } struct FILE_INFO * lex_current() { /* this became so simple, it could be a macro. But then, * lex_stack needed to be global... */ return lex_stack; } /* STATE MACHINES * -------------- */ /* Keywords */ static int is_keyword( char *lexeme, follby *pfollowedby ) { follby fb; int curr_s; /* current state index */ int token; int i; curr_s = SCANNER_INIT_S; token = 0; for (i = 0; lexeme[i]; i++) { while (curr_s && (lexeme[i] != SS_CH(sst[curr_s]))) curr_s = (int)SS_OTHER_N(sst[curr_s]); if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) { if ('\0' == lexeme[i + 1] && FOLLBY_NON_ACCEPTING != SS_FB(sst[curr_s])) { fb = SS_FB(sst[curr_s]); *pfollowedby = fb; token = curr_s; break; } curr_s = SS_MATCH_N(sst[curr_s]); } else break; } return token; } /* Integer */ static int is_integer( char *lexeme ) { int i; int is_neg; unsigned int u_val; i = 0; /* Allow a leading minus sign */ if (lexeme[i] == '-') { i++; is_neg = true; } else { is_neg = false; } /* Check that all the remaining characters are digits */ for (; lexeme[i] != '\0'; i++) { if (!isdigit((uint8_t)lexeme[i])) return false; } if (is_neg) return true; /* Reject numbers that fit in unsigned but not in signed int */ if (1 == sscanf(lexeme, "%u", &u_val)) return (u_val <= INT_MAX); else return false; } /* unsigned int -- assumes is_integer() has returned false */ static int is_u_int( char *lexeme ) { int i; int is_hex; i = 0; if ('0' == lexeme[i] && 'x' == tolower((uint8_t)lexeme[i + 1])) { i += 2; is_hex = true; } else { is_hex = false; } /* Check that all the remaining characters are digits */ for (; lexeme[i] != '\0'; i++) { if (is_hex && !isxdigit((uint8_t)lexeme[i])) return false; if (!is_hex && !isdigit((uint8_t)lexeme[i])) return false; } return true; } /* Double */ static bool is_double( char *lexeme ) { unsigned int num_digits = 0; /* Number of digits read */ unsigned int i; i = 0; /* Check for an optional '+' or '-' */ if ('+' == lexeme[i] || '-' == lexeme[i]) i++; /* Read the integer part */ for (; lexeme[i] && isdigit((uint8_t)lexeme[i]); i++) num_digits++; /* Check for the optional decimal point */ if ('.' == lexeme[i]) { i++; /* Check for any digits after the decimal point */ for (; lexeme[i] && isdigit((uint8_t)lexeme[i]); i++) num_digits++; } /* * The number of digits in both the decimal part and the * fraction part must not be zero at this point */ if (!num_digits) return false; /* Check if we are done */ if (!lexeme[i]) return true; /* There is still more input, read the exponent */ if ('e' == tolower((uint8_t)lexeme[i])) i++; else return false; /* Read an optional Sign */ if ('+' == lexeme[i] || '-' == lexeme[i]) i++; /* Now read the exponent part */ while (lexeme[i] && isdigit((uint8_t)lexeme[i])) i++; /* Check if we are done */ if (!lexeme[i]) return true; else return false; } /* is_special() - Test whether a character is a token */ static inline bool is_special( int ch ) { return strchr(special_chars, ch) != NULL; } static bool is_EOC( int ch ) { if ( ch == '\n') return true; return false; } char * quote_if_needed(char *str) { char *ret; size_t len; size_t octets; len = strlen(str); octets = len + 2 + 1; ret = emalloc(octets); if ('"' != str[0] && (strcspn(str, special_chars) < len || strchr(str, ' ') != NULL)) { snprintf(ret, octets, "\"%s\"", str); } else strlcpy(ret, str, octets); return ret; } static int create_string_token( char *lexeme ) { char *pch; /* * ignore end of line whitespace */ pch = lexeme; while (*pch && isspace((uint8_t)*pch)) pch++; if (!*pch) { yylval.Integer = T_EOC; return yylval.Integer; } yylval.String = estrdup(lexeme); return T_String; } /* * yylex() - function that does the actual scanning. * Bison expects this function to be called yylex and for it to take no * input and return an int. * Conceptually yylex "returns" yylval as well as the actual return * value representing the token or type. */ int yylex(void) { static follby followedby = FOLLBY_TOKEN; int i; bool instring; bool yylval_was_set; int converted; int token; /* The return value */ int ch; instring = false; yylval_was_set = false; do { /* Ignore whitespace at the beginning */ while (EOF != (ch = lex_getch(lex_stack)) && isspace(ch) && !is_EOC(ch)) ; /* Null Statement */ if (EOF == ch) { if ( ! lex_pop_file()) return 0; token = T_EOC; goto normal_return; } else if (is_EOC(ch)) { /* end FOLLBY_STRINGS_TO_EOC effect */ followedby = FOLLBY_TOKEN; token = T_EOC; goto normal_return; } else if (is_special(ch) && FOLLBY_TOKEN == followedby) { /* special chars are their own token values */ token = ch; /* * '=' outside simulator configuration implies * a single string following as in: * setvar Owner = "The Boss" default */ if ('=' == ch ) followedby = FOLLBY_STRING; yytext[0] = (char)ch; yytext[1] = '\0'; goto normal_return; } else lex_ungetch(ch, lex_stack); /* save the position of start of the token */ lex_stack->tokpos = lex_stack->curpos; /* Read in the lexeme */ i = 0; while (EOF != (ch = lex_getch(lex_stack))) { yytext[i] = (char)ch; /* Break on whitespace or a special character */ if (isspace(ch) || is_EOC(ch) || '"' == ch || (FOLLBY_TOKEN == followedby && is_special(ch))) break; /* Read the rest of the line on reading a start of comment character */ if ('#' == ch) { while (EOF != (ch = lex_getch(lex_stack)) && '\n' != ch) ; /* Null Statement */ break; } i++; if (i >= (int)COUNTOF(yytext)) goto lex_too_long; } /* Pick up all of the string inside between " marks, to * end of line. If we make it to EOL without a * terminating " assume it for them. * * XXX - HMS: I'm not sure we want to assume the closing " */ if ('"' == ch) { instring = true; while (EOF != (ch = lex_getch(lex_stack)) && ch != '"' && ch != '\n') { yytext[i++] = (char)ch; if (i >= (int)COUNTOF(yytext)) goto lex_too_long; } /* * yytext[i] will be pushed back as not part of * this lexeme, but any closing quote should * not be pushed back, so we read another char. */ if ('"' == ch) ch = lex_getch(lex_stack); } /* Pushback the last character read that is not a part * of this lexeme. This fails silently if ch is EOF, * but then the EOF condition persists and is handled on * the next turn by the include stack mechanism. */ lex_ungetch(ch, lex_stack); yytext[i] = '\0'; } while (i == 0); /* Now return the desired token */ /* First make sure that the parser is *not* expecting a string * as the next token (based on the previous token that was * returned) and that we haven't read a string. */ if (followedby == FOLLBY_TOKEN && !instring) { token = is_keyword(yytext, &followedby); if (token) { goto normal_return; } else if (is_integer(yytext)) { yylval_was_set = true; errno = 0; yylval.Integer = (int)strtol(yytext, NULL, 10); if (yylval.Integer == 0 && ((errno == EINVAL) || (errno == ERANGE))) { msyslog(LOG_ERR, "CONFIG: Integer cannot be represented: %s", yytext); if (lex_from_file()) { exit(1); } else { /* force end of parsing */ yylval.Integer = 0; return 0; } } token = T_Integer; goto normal_return; } else if (is_u_int(yytext)) { yylval_was_set = true; if ('0' == yytext[0] && 'x' == tolower((int)yytext[1])) converted = sscanf(&yytext[2], "%x", &yylval.U_int); else converted = sscanf(yytext, "%u", &yylval.U_int); if (1 != converted) { msyslog(LOG_ERR, "CONFIG: U_int cannot be represented: %s", yytext); if (lex_from_file()) { exit(1); } else { /* force end of parsing */ yylval.Integer = 0; return 0; } } token = T_U_int; goto normal_return; } else if (is_double(yytext)) { yylval_was_set = true; errno = 0; yylval.Double = atof(yytext); if ( D_ISZERO_NS(yylval.Double) && errno == ERANGE) { /* FIXME, POSIX says atof() never returns errors */ msyslog(LOG_ERR, "CONFIG: Double too large to represent: %s", yytext); exit(1); } else { token = T_Double; goto normal_return; } } else { /* Default: Everything is a string */ yylval_was_set = true; token = create_string_token(yytext); goto normal_return; } } /* * Either followedby is not FOLLBY_TOKEN or this lexeme is part * of a string. Hence, we need to return T_String. * * _Except_ we might have a -4 or -6 flag on a an association * configuration line (server, peer, pool, etc.). * * This is a terrible hack, but the grammar is ambiguous so we * don't have a choice. [SK] * * The ambiguity is in the keyword scanner, not ntp_parser.y. * We do not require server addresses be quoted in ntp.conf, * complicating the scanner's job. To avoid trying (and * failing) to match an IP address or DNS name to a keyword, * the association keywords use FOLLBY_STRING in the keyword * table, which tells the scanner to force the next token to be * a T_String, so it does not try to match a keyword but rather * expects a string when -4/-6 modifiers to server, peer, etc. * are encountered. * restrict -4 and restrict -6 parsing works correctly without * this hack, as restrict uses FOLLBY_TOKEN. [DH] */ if ('-' == yytext[0]) { if ('4' == yytext[1]) { token = T_Ipv4_flag; goto normal_return; } else if ('6' == yytext[1]) { token = T_Ipv6_flag; goto normal_return; } } instring = false; if (FOLLBY_STRING == followedby) followedby = FOLLBY_TOKEN; yylval_was_set = true; token = create_string_token(yytext); normal_return: if (T_EOC == token) DPRINT(4,("\t\n")); else DPRINT(4, ("yylex: lexeme '%s' -> %s\n", yytext, token_name(token))); if (!yylval_was_set) yylval.Integer = token; return token; lex_too_long: yytext[min(sizeof(yytext) - 1, 50)] = 0; msyslog(LOG_ERR, "CONFIG: configuration item on line %d longer than limit of %lu, began with '%s'", lex_stack->curpos.nline, (unsigned long)min(sizeof(yytext) - 1, 50), yytext); /* * If we hit the length limit reading the startup configuration * file, abort. */ if (lex_from_file()) exit(sizeof(yytext) - 1); /* * If it's runtime configuration via ntpq :config treat it as * if the configuration text ended before the too-long lexeme, * hostname, or string. */ yylval.Integer = 0; return 0; } ntpsec-1.1.0+dfsg1/ntpd/refclock_gpsd.c0000644000175000017500000016245413252364117017557 0ustar rlaagerrlaager/* * refclock_gpsdjson.c - clock driver as GPSD JSON client * Juergen Perlinger * Feb 11, 2014 for the NTP project. * * Heavily inspired by refclock_nmea.c * * Special thanks to Gary Miller and Hal Murray for their comments and * ideas. * * --------------------------------------------------------------------- * * This driver works slightly different from most others, as the PPS * information (if available) is also coming from GPSD via the data * connection. This makes using both the PPS data and the serial data * easier, but OTOH it's not possible to use the PPS driver to feed a * raw PPS stream to the core of NTPD. * * To go around this, the driver can use a secondary clock unit * (units>=128) that operate in tandem with the primary clock unit * (unit%128). The primary clock unit does all the IO stuff and data * decoding; if a a secondary unit is attached to a primary unit, this * secondary unit is feed with the PPS samples only and can act as a PPS * source to the clock selection. * * The drawback is that the primary unit must be present for the * secondary unit to work. * * This design is a compromise to reduce the IO load for both NTPD and * GPSD; it also ensures that data is transmitted and evaluated only * once on the side of NTPD. * * --------------------------------------------------------------------- * * trouble shooting hints: * * Enable and check the clock stats. Check if there are bad replies; * there should be none. If there are actually bad replies, then the * driver cannot parse all JSON records from GPSD, and some record * types are vital for the operation of the driver. This indicates a * problem on the protocol level. * * When started on the command line with a debug level >= 2, the * driver dumps the raw received data and the parser input to * stdout. Since the debug level is global, NTPD starts to create a * *lot* of output. It makes sense to pipe it through '(f)grep * GPSD_JSON' before writing the result to disk. * * A bit less intrusive is using netcat or telnet to connect to GPSD * and snoop what NTPD would get. If you try this, you have to send a * WATCH command to GPSD: * * ?WATCH={"device":"/dev/gps0","enable":true,"json":true,"pps":true}; * * should show you what GPSD has to say to NTPD. Replace "/dev/gps0" * with the device link used by GPSD, if necessary. * * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: NTP */ /* The strptime prototype is not provided unless explicitly requested. * See the POSIX spec for more info: * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_02_01_02 */ #define _XOPEN_SOURCE 600 #include "config.h" #include "ntp.h" #include "ntp_types.h" /* ===================================================================== * Get the little JSMN library directly into our guts. Use the 'parent * link' feature for maximum speed. */ #define JSMN_PARENT_LINKS #include "../libjsmn/jsmn.c" /* ===================================================================== * JSON parsing stuff */ #define JSMN_MAXTOK 350 #define INVALID_TOKEN (-1) typedef struct json_ctx { char * buf; int ntok; jsmntok_t tok[JSMN_MAXTOK]; } json_ctx; typedef int tok_ref; /* We roll our own integer number parser. */ typedef signed long int json_int; typedef unsigned long int json_uint; #define JSON_INT_MAX LONG_MAX #define JSON_INT_MIN LONG_MIN /* ===================================================================== * header stuff we need */ #include #include #include #include #include #include #include #include #include #include #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" #include "timespecops.h" /* get operation modes from mode word. * + SERIAL (default) evaluates only in-band time ('IBT') as * provided by TPV and TOFF records. TPV evaluation suffers from a * bigger jitter than TOFF, sine it does not contain the receive time * from GPSD and therefore the receive time of NTPD must be * substituted for it. The network latency makes this a second rate * guess. * * If TOFF records are detected in the data stream, the timing * information is gleaned from this record -- it contains the local * receive time stamp from GPSD and therefore eliminates the * transmission latency between GPSD and NTPD. The timing information * from TPV is ignored once a TOFF is detected or expected. * * TPV is still used to check the fix status, so the driver can stop * feeding samples when GPSD says that the time information is * effectively unreliable. * * + STRICT means only feed clock samples when a valid IBT/PPS pair is * available. Combines the reference time from IBT with the pulse time * from PPS. Masks the serial data jitter as long PPS is available, * but can rapidly deteriorate once PPS drops out. * * + AUTO tries to use IBT/PPS pairs if available for some time, and if * this fails for too long switches back to IBT only until the PPS * signal becomes available again. See the HTML docs for this driver * about the gotchas and why this is not the default. */ #define MODE_OP_MASK 0x03 #define MODE_OP_IBT 0 #define MODE_OP_STRICT 1 #define MODE_OP_AUTO 2 #define MODE_OP_MAXVAL 2 #define MODE_OP_MODE(x) ((x) & MODE_OP_MASK) #define PRECISION (-9) /* precision assumed (about 2 ms) */ #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ #define REFID "GPSD" /* reference id */ #define NAME "GPSD" /* shortname */ #define DESCRIPTION "GPSD JSON client clock" /* who we are */ #define MAX_PDU_LEN 1600 #define TICKOVER_LOW 10 #define TICKOVER_HIGH 120 #define LOGTHROTTLE SECSPERHR /* Primary channel PPS avilability dance: * Every good PPS sample gets us a credit of PPS_INCCOUNT points, every * bad/missing PPS sample costs us a debit of PPS_DECCOUNT points. When * the account reaches the upper limit we change to a mode where only * PPS-augmented samples are fed to the core; when the account drops to * zero we switch to a mode where TPV-only timestamps are fed to the * core. * This reduces the chance of rapid alternation between raw and * PPS-augmented time stamps. */ #define PPS_MAXCOUNT 60 /* upper limit of account */ #define PPS_INCCOUNT 3 /* credit for good samples */ #define PPS_DECCOUNT 1 /* debit for bad samples */ /* The secondary (PPS) channel uses a different strategy to avoid old * PPS samples in the median filter. */ #define PPS2_MAXCOUNT 10 #define PROTO_VERSION(hi,lo) \ ((((uint32_t)(hi) << 16) & 0xFFFF0000u) | \ ((uint32_t)(lo) & 0x0FFFFu)) /* some local typedefs: The NTPD formatting style cries for short type * names, and we provide them locally. Note:the suffix '_t' is reserved * for the standard; I use a capital T instead. */ typedef struct peer peerT; typedef struct refclockproc clockprocT; typedef struct addrinfo addrinfoT; /* ===================================================================== * We use the same device name scheme as does the NMEA driver; since * GPSD supports the same links, we can select devices by a fixed name. */ #define DEVICE "/dev/gps%d" /* GPS serial device */ /* ===================================================================== * forward declarations for transfer vector and the vector itself */ static void gpsd_init (void); static bool gpsd_start (int, peerT *); static void gpsd_shutdown (struct refclockproc *); static void gpsd_receive (struct recvbuf *); static void gpsd_poll (int, peerT *); static void gpsd_control (int, const struct refclockstat *, struct refclockstat *, peerT *); static void gpsd_timer (int, peerT *); static int myasprintf(char**, char const*, ...) NTP_PRINTF(2, 3); static void enter_opmode(peerT *peer, int mode); static void leave_opmode(peerT *peer, int mode); struct refclock refclock_gpsdjson = { NAME, /* basename of driver */ gpsd_start, /* start up driver */ gpsd_shutdown, /* shut down driver */ gpsd_poll, /* transmit poll message */ gpsd_control, /* fudge and option control */ gpsd_init, /* initialize driver */ gpsd_timer /* called once per second */ }; /* ===================================================================== * our local clock unit and data */ struct gpsd_unit; typedef struct gpsd_unit gpsd_unitT; struct gpsd_unit { /* links for sharing between master/slave units */ gpsd_unitT *next_unit; size_t refcount; /* data for the secondary PPS channel */ peerT *pps_peer; /* unit and operation modes */ int unit; int mode; char *logname; /* cached name for log/print */ char *device; /* device name of unit */ /* current line protocol version */ uint32_t proto_version; /* PPS time stamps primary + secondary channel */ l_fp pps_local; /* when we received the PPS message */ l_fp pps_stamp; /* related reference time */ l_fp pps_recvt; /* when GPSD detected the pulse */ l_fp pps_stamp2;/* related reference time (secondary) */ l_fp pps_recvt2;/* when GPSD detected the pulse (secondary)*/ int ppscount; /* PPS counter (primary unit) */ int ppscount2; /* PPS counter (secondary unit) */ /* TPV or TOFF serial time information */ l_fp ibt_local; /* when we received the TPV/TOFF message */ l_fp ibt_stamp; /* effective GPS time stamp */ l_fp ibt_recvt; /* when GPSD got the fix */ /* precision estimates */ int16_t ibt_prec; /* serial precision based on EPT */ int16_t pps_prec; /* PPS precision from GPSD or above */ /* fudge values for correction, mirrored as 'l_fp' */ l_fp pps_fudge; /* PPS fudge primary channel */ l_fp pps_fudge2; /* PPS fudge secondary channel */ l_fp ibt_fudge; /* TPV/TOFF serial data fudge */ /* Flags to indicate available data */ int fl_nosync: 1; /* GPSD signals bad quality */ int fl_ibt : 1; /* valid TPV/TOFF seen (have time) */ int fl_pps : 1; /* valid pulse seen */ int fl_pps2 : 1; /* valid pulse seen for PPS channel */ int fl_rawibt: 1; /* permit raw TPV/TOFF time stamps */ int fl_vers : 1; /* have protocol version */ int fl_watch : 1; /* watch reply seen */ /* protocol flags */ int pf_nsec : 1; /* have nanosec PPS info */ int pf_toff : 1; /* have TOFF record for timing */ /* admin stuff for sockets and device selection */ int fdt; /* current connecting socket */ addrinfoT * addr; /* next address to try */ unsigned int tickover; /* timeout countdown */ unsigned int tickpres; /* timeout preset */ /* tallies for the various events */ unsigned int tc_recv; /* received known records */ unsigned int tc_breply; /* bad replies / parsing errors */ unsigned int tc_nosync; /* TPV / sample cycles w/o fix */ unsigned int tc_ibt_recv;/* received serial time info records */ unsigned int tc_ibt_used;/* used --^-- */ unsigned int tc_pps_recv;/* received PPS timing info records */ unsigned int tc_pps_used;/* used --^-- */ /* log bloat throttle */ unsigned int logthrottle;/* seconds to next log slot */ /* The parse context for the current record */ json_ctx json_parse; /* record assemby buffer and saved length */ int buflen; char buffer[MAX_PDU_LEN]; }; /* ===================================================================== * static local helpers forward decls */ static void gpsd_init_socket(peerT * const peer); static void gpsd_test_socket(peerT * const peer); static void gpsd_stop_socket(peerT * const peer); static void gpsd_parse(peerT * const peer, const l_fp * const rtime); static bool convert_ascii_time(l_fp * fp, const char * gps_time); static void save_ltc(clockprocT * const pp, const char * const tc); static bool syslogok(clockprocT * const pp, gpsd_unitT * const up); static void log_data(peerT *peer, const char *what, const char *buf, size_t len); static int16_t clamped_precision(int rawprec); /* ===================================================================== * local / static stuff */ /* The logon string is actually the ?WATCH command of GPSD, using JSON * data and selecting the GPS device name we created from our unit * number. We have an old a newer version that request PPS (and TOFF) * transmission. * Note: These are actually format strings! */ static const char * const s_req_watch[2] = { "?WATCH={\"device\":\"%s\",\"enable\":true,\"json\":true};\r\n", "?WATCH={\"device\":\"%s\",\"enable\":true,\"json\":true,\"pps\":true};\r\n" }; static const char * const s_req_version = "?VERSION;\r\n"; /* We keep a static list of network addresses for 'localhost:gpsd' or a * fallback alias of it, and we try to connect to them in round-robin * fashion. The service lookup is done during the driver init * function to minmise the impact of 'getaddrinfo()'. * * Alas, the init function is called even if there are no clocks * configured for this driver. So it makes sense to defer the logging of * any errors or other notifications until the first clock unit is * started -- otherwise there might be syslog entries from a driver that * is not used at all. */ static addrinfoT *s_gpsd_addr; static gpsd_unitT *s_clock_units; /* list of service/socket names we want to resolve against */ static const char * const s_svctab[][2] = { { "localhost", "gpsd" }, { "localhost", "2947" }, { "127.0.0.1", "2947" }, { NULL, NULL } }; /* list of address resolution errors and index of service entry that * finally worked. */ static int s_svcerr[sizeof(s_svctab)/sizeof(s_svctab[0])]; static int s_svcidx; /* ===================================================================== * log throttling */ static bool syslogok( clockprocT * const pp, gpsd_unitT * const up) { int res = (0 != (pp->sloppyclockflag & CLK_FLAG3)) || (0 == up->logthrottle ) || (LOGTHROTTLE == up->logthrottle ); if (res) up->logthrottle = LOGTHROTTLE; return res; } /* ===================================================================== * the clock functions */ /* --------------------------------------------------------------------- * Init: This currently just gets the socket address for the GPS daemon */ static void gpsd_init(void) { addrinfoT hints; int rc, idx; memset(s_svcerr, 0, sizeof(s_svcerr)); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_protocol = IPPROTO_TCP; hints.ai_socktype = SOCK_STREAM; for (idx = 0; s_svctab[idx][0] && !s_gpsd_addr; idx++) { rc = getaddrinfo(s_svctab[idx][0], s_svctab[idx][1], &hints, &s_gpsd_addr); s_svcerr[idx] = rc; if (0 == rc) break; s_gpsd_addr = NULL; } s_svcidx = idx; } /* --------------------------------------------------------------------- * Init Check: flush pending log messages and check if we can proceed */ static bool gpsd_init_check(void) { int idx; /* Check if there is something to log */ if (s_svcidx == 0) return (s_gpsd_addr != NULL); /* spool out the resolver errors */ for (idx = 0; idx < s_svcidx; ++idx) { msyslog(LOG_WARNING, "REFCLOCK: GPSD_JSON: failed to resolve '%s:%s', rc=%d (%s)", s_svctab[idx][0], s_svctab[idx][1], s_svcerr[idx], gai_strerror(s_svcerr[idx])); } /* check if it was fatal, or if we can proceed */ if (s_gpsd_addr == NULL) msyslog(LOG_ERR, "REFCLOCK: GPSD_JSON: failed to get socket address, giving up."); else if (idx != 0) msyslog(LOG_WARNING, "REFCLOCK: GPSD_JSON: using '%s:%s' instead of '%s:%s'", s_svctab[idx][0], s_svctab[idx][1], s_svctab[0][0], s_svctab[0][1]); /* make sure this gets logged only once and tell if we can * proceed or not */ s_svcidx = 0; return (s_gpsd_addr != NULL); } /* --------------------------------------------------------------------- * Start: allocate a unit pointer and set up the runtime data */ static bool gpsd_start( int unit, peerT * peer) { clockprocT * const pp = peer->procptr; gpsd_unitT * up; gpsd_unitT ** uscan = &s_clock_units; struct stat sb; int ret; /* check if we can proceed at all or if init failed */ if ( ! gpsd_init_check()) return false; /* search for matching unit */ while ((up = *uscan) != NULL && up->unit != (unit & 0x7F)) uscan = &up->next_unit; if (up == NULL) { /* alloc unit, add to list and increment use count ASAP. */ up = emalloc_zero(sizeof(*up)); *uscan = up; ++up->refcount; /* initialize the unit structure */ pp->clockname = NAME; /* Hack, needed by refclock_name */ up->logname = estrdup(refclock_name(peer)); up->unit = unit & 0x7F; up->fdt = -1; up->addr = s_gpsd_addr; up->tickpres = TICKOVER_LOW; /* Create the device name and check for a Character * Device. It's assumed that GPSD was started with the * same link, so the names match. (If this is not * practicable, we will have to read the symlink, if * any, so we can get the true device file.) */ if ( peer->cfg.path ) { /* use the ntp.conf path name */ ret = myasprintf(&up->device, "%s", peer->cfg.path); } else { ret = myasprintf(&up->device, DEVICE, up->unit); } if (-1 == ret ) { /* more likely out of RAM */ msyslog(LOG_ERR, "REFCLOCK: %s: clock device name too long", up->logname); goto dev_fail; } if (-1 == stat(up->device, &sb) || !S_ISCHR(sb.st_mode)) { msyslog(LOG_ERR, "REFCLOCK: %s: '%s' is not a character device", up->logname, up->device); goto dev_fail; } } else { /* All set up, just increment use count. */ ++up->refcount; } /* setup refclock processing */ pp->unitptr = (void *)up; pp->io.fd = -1; pp->io.clock_recv = gpsd_receive; pp->io.srcclock = peer; pp->io.datalen = 0; pp->a_lastcode[0] = '\0'; pp->lencode = 0; pp->clockname = NAME; pp->clockdesc = DESCRIPTION; memcpy(&pp->refid, REFID, REFIDLEN); peer->sstclktype = CTL_SST_TS_UHF; /* Initialize miscellaneous variables */ if (unit >= 128) peer->precision = PPS_PRECISION; else peer->precision = PRECISION; /* If the daemon name lookup failed, just give up now. */ if (NULL == up->addr) { msyslog(LOG_ERR, "REFCLOCK: %s: no GPSD socket address, giving up", up->logname); goto dev_fail; } LOGIF(CLOCKINFO, (LOG_NOTICE, "%s: startup, device is '%s'", refclock_name(peer), up->device)); up->mode = MODE_OP_MODE(peer->cfg.mode); if (up->mode > MODE_OP_MAXVAL) up->mode = 0; if (unit >= 128) up->pps_peer = peer; else enter_opmode(peer, up->mode); return true; dev_fail: /* On failure, remove all UNIT resources and declare defeat. */ INSIST (up); if (!--up->refcount) { *uscan = up->next_unit; free(up->device); free(up); } pp->unitptr = NULL; return false; } /* ------------------------------------------------------------------ */ static void gpsd_shutdown( struct refclockproc *pp) { gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; gpsd_unitT ** uscan = &s_clock_units; /* The unit pointer might have been removed already. */ if (up == NULL) return; /* now check if we must close IO resources */ if (pp != up->pps_peer->procptr) { if (-1 != pp->io.fd) { DPRINT(1, ("%s: closing clock, fd=%d\n", up->logname, pp->io.fd)); io_closeclock(&pp->io); pp->io.fd = -1; } if (up->fdt != -1) close(up->fdt); } /* decrement use count and eventually remove this unit. */ if (!--up->refcount) { /* unlink this unit */ while (*uscan != NULL) if (*uscan == up) *uscan = up->next_unit; else uscan = &(*uscan)->next_unit; free(up->logname); free(up->device); free(up); } pp->unitptr = NULL; LOGIF(CLOCKINFO, (LOG_NOTICE, "shutdown: gpsd_json(%d)", (int)pp->refclkunit)); } /* ------------------------------------------------------------------ */ static void gpsd_receive( struct recvbuf * rbufp) { /* declare & init control structure ptrs */ peerT * const peer = rbufp->recv_peer; clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; const char *psrc, *esrc; char *pdst, *edst, ch; /* log the data stream, if this is enabled */ log_data(peer, "recv", (const char*)rbufp->recv_buffer, (size_t)rbufp->recv_length); /* Since we're getting a raw stream data, we must assemble lines * in our receive buffer. We can't use neither 'refclock_gtraw' * not 'refclock_gtlin' here... We process chars until we reach * an EoL (that is, line feed) but we truncate the message if it * does not fit the buffer. GPSD might truncate messages, too, * so dealing with truncated buffers is necessary anyway. */ psrc = (const char*)rbufp->recv_buffer; esrc = psrc + rbufp->recv_length; pdst = up->buffer + up->buflen; edst = pdst + sizeof(up->buffer) - 1; /* for trailing NUL */ while (psrc != esrc) { ch = *psrc++; if (ch == '\n') { /* trim trailing whitespace & terminate buffer */ while (pdst != up->buffer && pdst[-1] <= ' ') --pdst; *pdst = '\0'; /* process data and reset buffer */ up->buflen = (int)(pdst - up->buffer); gpsd_parse(peer, &rbufp->recv_time); pdst = up->buffer; } else if (pdst != edst) { /* add next char, ignoring leading whitespace */ if (ch > ' ' || pdst != up->buffer) *pdst++ = ch; } } up->buflen = (int)(pdst - up->buffer); up->tickover = TICKOVER_LOW; } /* ------------------------------------------------------------------ */ static void poll_primary( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { if (pp->coderecv != pp->codeproc) { /* all is well */ pp->lastref = pp->lastrec; refclock_report(peer, CEVNT_NOMINAL); refclock_receive(peer); } else { /* Not working properly, admit to it. If we have no * connection to GPSD, declare the clock as faulty. If * there were bad replies, this is handled as the major * cause, and everything else is just a timeout. */ peer->precision = PRECISION; if (-1 == pp->io.fd) refclock_report(peer, CEVNT_FAULT); else if (0 != up->tc_breply) refclock_report(peer, CEVNT_BADREPLY); else refclock_report(peer, CEVNT_TIMEOUT); } if (pp->sloppyclockflag & CLK_FLAG4) mprintf_clock_stats( peer,"%u %u %u %u %u %u %u", up->tc_recv, up->tc_breply, up->tc_nosync, up->tc_ibt_recv, up->tc_ibt_used, up->tc_pps_recv, up->tc_pps_used); /* clear tallies for next round */ up->tc_breply = 0; up->tc_recv = 0; up->tc_nosync = 0; up->tc_ibt_recv = 0; up->tc_ibt_used = 0; up->tc_pps_recv = 0; up->tc_pps_used = 0; } static void poll_secondary( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { UNUSED_ARG(up); if (pp->coderecv != pp->codeproc) { /* all is well */ pp->lastref = pp->lastrec; refclock_report(peer, CEVNT_NOMINAL); refclock_receive(peer); } else { peer->precision = PPS_PRECISION; peer->cfg.flags &= ~FLAG_PPS; refclock_report(peer, CEVNT_TIMEOUT); } } static void gpsd_poll( int unit, peerT * peer) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; UNUSED_ARG(unit); ++pp->polls; if (peer == up->pps_peer) poll_secondary(peer, pp, up); else poll_primary(peer, pp, up); } /* ------------------------------------------------------------------ */ static void gpsd_control( int unit, const struct refclockstat * in_st, struct refclockstat * out_st, peerT * peer ) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; UNUSED_ARG(unit); UNUSED_ARG(in_st); UNUSED_ARG(out_st); if (peer == up->pps_peer) { up->pps_fudge2 = dtolfp(pp->fudgetime1); if ( ! (pp->sloppyclockflag & CLK_FLAG1)) peer->cfg.flags &= ~FLAG_PPS; } else { /* save preprocessed fudge times */ up->pps_fudge = dtolfp(pp->fudgetime1); up->ibt_fudge = dtolfp(pp->fudgetime2); if (MODE_OP_MODE((uint32_t)up->mode ^ peer->cfg.mode)) { leave_opmode(peer, up->mode); up->mode = MODE_OP_MODE(peer->cfg.mode); enter_opmode(peer, up->mode); } } } /* ------------------------------------------------------------------ */ static void timer_primary( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { int rc; /* This is used for timeout handling. Nothing that needs * sub-second precision happens here, so receive/connect/retry * timeouts are simply handled by a count down, and then we * decide what to do by the socket values. * * Note that the timer stays at zero here, unless some of the * functions set it to another value. */ if (up->logthrottle) --up->logthrottle; if (up->tickover) --up->tickover; switch (up->tickover) { case 4: /* If we are connected to GPSD, try to get a live signal * by querying the version. Otherwise just check the * socket to become ready. */ if (-1 != pp->io.fd) { size_t rlen = strlen(s_req_version); DPRINT(2, ("%s: timer livecheck: '%s'\n", up->logname, s_req_version)); log_data(peer, "send", s_req_version, rlen); rc = write(pp->io.fd, s_req_version, rlen); (void)rc; } else if (-1 != up->fdt) { gpsd_test_socket(peer); } break; case 0: if (-1 != pp->io.fd) gpsd_stop_socket(peer); else if (-1 != up->fdt) gpsd_test_socket(peer); else if (NULL != s_gpsd_addr) gpsd_init_socket(peer); break; default: if (-1 == pp->io.fd && -1 != up->fdt) gpsd_test_socket(peer); } } static void timer_secondary( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { /* Reduce the count by one. Flush sample buffer and clear PPS * flag when this happens. */ up->ppscount2 = max(0, (up->ppscount2 - 1)); if (0 == up->ppscount2) { if (pp->coderecv != pp->codeproc) { refclock_report(peer, CEVNT_TIMEOUT); pp->coderecv = pp->codeproc; } peer->cfg.flags &= ~FLAG_PPS; } } static void gpsd_timer( int unit, peerT * peer) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; UNUSED_ARG(unit); if (peer == up->pps_peer) timer_secondary(peer, pp, up); else timer_primary(peer, pp, up); } /* ===================================================================== * handle opmode switches */ static void enter_opmode( peerT *peer, int mode) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; DPRINT(1, ("%s: enter operation mode %d\n", up->logname, MODE_OP_MODE(mode))); if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { up->fl_rawibt = 0; up->ppscount = PPS_MAXCOUNT / 2; } up->fl_pps = 0; up->fl_ibt = 0; } /* ------------------------------------------------------------------ */ static void leave_opmode( peerT *peer, int mode) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; DPRINT(1, ("%s: leaving operation mode %d\n", up->logname, MODE_OP_MODE(mode))); if (MODE_OP_MODE(mode) == MODE_OP_AUTO) { up->fl_rawibt = 0; up->ppscount = 0; } up->fl_pps = 0; up->fl_ibt = 0; } /* ===================================================================== * operation mode specific evaluation */ static void add_clock_sample( peerT * const peer , clockprocT * const pp , l_fp stamp, l_fp recvt) { pp->lastref = stamp; if (pp->coderecv == pp->codeproc) refclock_report(peer, CEVNT_NOMINAL); refclock_process_offset(pp, stamp, recvt, 0.0); } /* ------------------------------------------------------------------ */ static void eval_strict( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { if (up->fl_ibt && up->fl_pps) { /* use TPV reference time + PPS receive time */ add_clock_sample(peer, pp, up->ibt_stamp, up->pps_recvt); peer->precision = (int8_t)up->pps_prec; /* both packets consumed now... */ up->fl_pps = 0; up->fl_ibt = 0; ++up->tc_ibt_used; } } /* ------------------------------------------------------------------ */ /* PPS processing for the secondary channel. GPSD provides us with full * timing information, so there's no danger of PLL-locking to the wrong * second. The belts and suspenders needed for the raw ATOM clock are * unnecessary here. */ static void eval_pps_secondary( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { if (up->fl_pps2) { /* feed data */ add_clock_sample(peer, pp, up->pps_stamp2, up->pps_recvt2); peer->precision = (int8_t)up->pps_prec; /* PPS peer flag logic */ up->ppscount2 = min(PPS2_MAXCOUNT, (up->ppscount2 + 2)); if ((PPS2_MAXCOUNT == up->ppscount2) && (pp->sloppyclockflag & CLK_FLAG1) ) peer->cfg.flags |= FLAG_PPS; /* mark time stamp as burned... */ up->fl_pps2 = 0; ++up->tc_pps_used; } } /* ------------------------------------------------------------------ */ static void eval_serial( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { if (up->fl_ibt) { add_clock_sample(peer, pp, up->ibt_stamp, up->ibt_recvt); peer->precision = (int8_t)up->ibt_prec; /* mark time stamp as burned... */ up->fl_ibt = 0; ++up->tc_ibt_used; } } /* ------------------------------------------------------------------ */ static void eval_auto( peerT * const peer , clockprocT * const pp , gpsd_unitT * const up ) { /* If there's no TPV available, stop working here... */ if (!up->fl_ibt) return; /* check how to handle IBT+PPS: Can PPS be used to augment IBT * (or vice versae), do we drop the sample because there is a * temporary missing PPS signal, or do we feed on IBT time * stamps alone? * * Do a counter/threshold dance to decide how to proceed. */ if (up->fl_pps) { up->ppscount = min(PPS_MAXCOUNT, (up->ppscount + PPS_INCCOUNT)); if ((PPS_MAXCOUNT == up->ppscount) && up->fl_rawibt) { up->fl_rawibt = 0; msyslog(LOG_INFO, "REFCLOCK: %s: expect valid PPS from now", up->logname); } } else { up->ppscount = max(0, (up->ppscount - PPS_DECCOUNT)); if ((0 == up->ppscount) && !up->fl_rawibt) { up->fl_rawibt = -1; msyslog(LOG_WARNING, "REFCLOCK: %s: use TPV alone from now", up->logname); } } /* now eventually feed the sample */ if (up->fl_rawibt) eval_serial(peer, pp, up); else eval_strict(peer, pp, up); } /* ===================================================================== * JSON parsing stuff */ /* ------------------------------------------------------------------ */ /* Parse a decimal integer with a possible sign. Works like 'strtoll()' * or 'strtol()', but with a fixed base of 10 and without eating away * leading whitespace. For the error codes, the handling of the end * pointer and the return values see 'strtol()'. */ static json_int strtojint( const char *cp, char **ep) { json_uint accu, limit_lo, limit_hi; int flags; /* bit 0: overflow; bit 1: sign */ const char * hold; /* pointer union to circumvent a tricky/sticky const issue */ union { const char * c; char * v; } vep; /* store initial value of 'cp' -- see 'strtol()' */ vep.c = cp; /* Eat away an optional sign and set the limits accordingly: The * high limit is the maximum absolute value that can be returned, * and the low limit is the biggest value that does not cause an * overflow when multiplied with 10. Avoid negation overflows. */ if (*cp == '-') { cp += 1; flags = 2; limit_hi = (json_uint)-(JSON_INT_MIN + 1) + 1; } else { cp += (*cp == '+'); flags = 0; limit_hi = (json_uint)JSON_INT_MAX; } limit_lo = limit_hi / 10; /* Now try to convert a sequence of digits. */ hold = cp; accu = 0; while (isdigit(*(const unsigned char*)cp)) { flags |= (accu > limit_lo); accu = accu * 10 + (json_uint)(*(const unsigned char*)cp++ - '0'); flags |= (accu > limit_hi); } /* Check for empty conversion (no digits seen). */ if (hold != cp) vep.c = cp; else errno = EINVAL; /* accu is still zero */ /* Check for range overflow */ if (flags & 1) { errno = ERANGE; accu = limit_hi; } /* If possible, store back the end-of-conversion pointer */ if (ep) *ep = vep.v; /* If negative, return the negated result if the accu is not * zero. Avoid negation overflows. */ if ((flags & 2) && accu) return -(json_int)(accu - 1) - 1; else return (json_int)accu; } /* ------------------------------------------------------------------ */ static tok_ref json_token_skip( const json_ctx * ctx, tok_ref tid) { if (tid >= 0 && tid < ctx->ntok) { int len = ctx->tok[tid].size; /* For arrays and objects, the size is the number of * ITEMS in the compound. That's the number of objects in * the array, and the number of key/value pairs for * objects. In theory, the key must be a string, and we * could simply skip one token before skipping the * value, which can be anything. We're a bit paranoid * and lazy at the same time: We simply double the * number of tokens to skip and fall through into the * array processing when encountering an object. */ switch (ctx->tok[tid].type) { case JSMN_OBJECT: len *= 2; /* FALLTHROUGH */ case JSMN_ARRAY: for (++tid; len; --len) tid = json_token_skip(ctx, tid); break; case JSMN_PRIMITIVE: case JSMN_STRING: default: ++tid; break; } if (tid > ctx->ntok) /* Impossible? Paranoia rulez. */ tid = ctx->ntok; } return tid; } /* ------------------------------------------------------------------ */ static int json_object_lookup( const json_ctx * ctx , tok_ref tid , const char * key , int what) { int len; if (tid < 0 || tid >= ctx->ntok || ctx->tok[tid].type != JSMN_OBJECT) return INVALID_TOKEN; len = ctx->tok[tid].size; for (++tid; len && tid+1 < ctx->ntok; --len) { if (ctx->tok[tid].type != JSMN_STRING) { /* Blooper! */ tid = json_token_skip(ctx, tid); /* skip key */ tid = json_token_skip(ctx, tid); /* skip val */ } else if (strcmp(key, ctx->buf + ctx->tok[tid].start)) { tid = json_token_skip(ctx, tid+1); /* skip key+val */ } else if (what < 0 || what == (int)ctx->tok[tid+1].type) { return tid + 1; } else { break; } /* if skipping ahead returned an error, bail out here. */ if (tid < 0) break; } return INVALID_TOKEN; } /* ------------------------------------------------------------------ */ static const char* json_object_lookup_primitive( const json_ctx * ctx, tok_ref tid, const char * key) { tid = json_object_lookup(ctx, tid, key, JSMN_PRIMITIVE); if (INVALID_TOKEN != tid) return ctx->buf + ctx->tok[tid].start; else return NULL; } /* ------------------------------------------------------------------ */ /* look up a boolean value. This essentially returns a tribool: * 0->false, 1->true, (-1)->error/undefined */ static int json_object_lookup_bool( const json_ctx * ctx, tok_ref tid, const char * key) { const char *cp; cp = json_object_lookup_primitive(ctx, tid, key); switch ( cp ? *cp : '\0') { case 't': return 1; case 'f': return 0; default : return -1; } } /* ------------------------------------------------------------------ */ static const char* json_object_lookup_string( const json_ctx * ctx, tok_ref tid, const char * key) { tid = json_object_lookup(ctx, tid, key, JSMN_STRING); if (INVALID_TOKEN != tid) return ctx->buf + ctx->tok[tid].start; return NULL; } static const char* json_object_lookup_string_default( const json_ctx * ctx, tok_ref tid, const char * key, const char * def) { tid = json_object_lookup(ctx, tid, key, JSMN_STRING); if (INVALID_TOKEN != tid) return ctx->buf + ctx->tok[tid].start; return def; } /* ------------------------------------------------------------------ */ static json_int json_object_lookup_int( const json_ctx * ctx, tok_ref tid, const char * key) { json_int ret; const char * cp; char * ep; cp = json_object_lookup_primitive(ctx, tid, key); if (NULL != cp) { ret = strtojint(cp, &ep); if (cp != ep && '\0' == *ep) return ret; } else { errno = EINVAL; } return 0; } static json_int json_object_lookup_int_default( const json_ctx * ctx, tok_ref tid, const char * key, json_int def) { json_int ret; const char * cp; char * ep; cp = json_object_lookup_primitive(ctx, tid, key); if (NULL != cp) { ret = strtojint(cp, &ep); if (cp != ep && '\0' == *ep) return ret; } return def; } /* ------------------------------------------------------------------ */ static double json_object_lookup_float_default( const json_ctx * ctx, tok_ref tid, const char * key, double def) { double ret; const char * cp; char * ep; cp = json_object_lookup_primitive(ctx, tid, key); if (NULL != cp) { ret = strtod(cp, &ep); if (cp != ep && '\0' == *ep) return ret; } return def; } /* ------------------------------------------------------------------ */ static bool json_parse_record( json_ctx * ctx, char * buf, size_t len) { jsmn_parser jsm; int idx, rc; jsmn_init(&jsm); rc = jsmn_parse(&jsm, buf, len, ctx->tok, JSMN_MAXTOK); if (rc <= 0) return false; ctx->buf = buf; ctx->ntok = rc; if (JSMN_OBJECT != ctx->tok[0].type) return false; /* not object!?! */ /* Make all tokens NUL terminated by overwriting the * terminator symbol. Makes string compares and number parsing a * lot easier! */ for (idx = 0; idx < ctx->ntok; ++idx) if (ctx->tok[idx].end > ctx->tok[idx].start) ctx->buf[ctx->tok[idx].end] = '\0'; return true; } /* ===================================================================== * static local helpers */ static bool get_binary_time( l_fp * const dest , json_ctx * const jctx , const char * const time_name, const char * const frac_name, long fscale ) { bool retv = false; struct timespec ts; errno = 0; ts.tv_sec = (time_t)json_object_lookup_int(jctx, 0, time_name); ts.tv_nsec = (long )json_object_lookup_int(jctx, 0, frac_name); if (0 == errno) { ts.tv_nsec *= fscale; *dest = tspec_stamp_to_lfp(ts); retv = true; } return retv; } /* ------------------------------------------------------------------ */ /* Process a WATCH record * * Currently this is only used to recognise that the device is present * and that we're listed subscribers. */ static void process_watch( peerT * const peer , json_ctx * const jctx , const l_fp * const rtime) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; const char * path; UNUSED_ARG(rtime); path = json_object_lookup_string(jctx, 0, "device"); if (NULL == path || strcmp(path, up->device)) return; if (json_object_lookup_bool(jctx, 0, "enable") > 0 && json_object_lookup_bool(jctx, 0, "json" ) > 0 ) up->fl_watch = -1; else up->fl_watch = 0; DPRINT(2, ("%s: process_watch, enabled=%d\n", up->logname, (up->fl_watch & 1))); } /* ------------------------------------------------------------------ */ static void process_version( peerT * const peer , json_ctx * const jctx , const l_fp * const rtime) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; size_t len; ssize_t ret; char * buf; const char *revision; const char *release; uint16_t pvhi, pvlo; UNUSED_ARG(rtime); /* get protocol version number */ revision = json_object_lookup_string_default( jctx, 0, "rev", "(unknown)"); release = json_object_lookup_string_default( jctx, 0, "release", "(unknown)"); errno = 0; pvhi = (uint16_t)json_object_lookup_int(jctx, 0, "proto_major"); pvlo = (uint16_t)json_object_lookup_int(jctx, 0, "proto_minor"); if (0 == errno) { if ( ! up->fl_vers) msyslog(LOG_INFO, "REFCLOCK: %s: GPSD revision=%s release=%s protocol=%u.%u", up->logname, revision, release, pvhi, pvlo); up->proto_version = PROTO_VERSION(pvhi, pvlo); up->fl_vers = -1; } else { if (syslogok(pp, up)) msyslog(LOG_INFO, "REFCLOCK: %s: could not evaluate version data", up->logname); return; } /* With the 3.9 GPSD protocol, '*_musec' vanished from the PPS * record and was replace by '*_nsec'. */ up->pf_nsec = -(up->proto_version >= PROTO_VERSION(3,9)); /* With the 3.10 protocol we can get TOFF records for better * timing information. */ up->pf_toff = -(up->proto_version >= PROTO_VERSION(3,10)); /* request watch for our GPS device if not yet watched. * * The version string is also sent as a life signal, if we have * seen useable data. So if we're already watching the device, * skip the request. * * Reuse the input buffer, which is no longer needed in the * current cycle. Also assume that we can write the watch * request in one sweep into the socket; since we do not do * output otherwise, this should always work. (Unless the * TCP/IP window size gets lower than the length of the * request. We handle that when it happens.) */ if (up->fl_watch) return; snprintf(up->buffer, sizeof(up->buffer), s_req_watch[up->pf_toff != 0], up->device); buf = up->buffer; len = strlen(buf); log_data(peer, "send", buf, len); ret = write(pp->io.fd, buf, len); if ( (ret < 0 || (size_t)ret != len) && (syslogok(pp, up))) { /* Note: if the server fails to read our request, the * resulting data timeout will take care of the * connection! */ msyslog(LOG_ERR, "REFCLOCK: %s: failed to write watch request (%m)", up->logname); } } /* ------------------------------------------------------------------ */ static void process_tpv( peerT * const peer , json_ctx * const jctx , const l_fp * const rtime) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; const char * gps_time; int gps_mode; double ept; int xlog2; gps_mode = (int)json_object_lookup_int_default( jctx, 0, "mode", 0); gps_time = json_object_lookup_string( jctx, 0, "time"); /* accept time stamps only in 2d or 3d fix */ if (gps_mode < 2 || NULL == gps_time) { /* receiver has no fix; tell about and avoid stale data */ if ( ! up->pf_toff) ++up->tc_ibt_recv; ++up->tc_nosync; up->fl_ibt = 0; up->fl_pps = 0; up->fl_nosync = -1; return; } up->fl_nosync = 0; /* convert clock and set resulting ref time, but only if the * TOFF sentence is *not* available */ if ( ! up->pf_toff) { ++up->tc_ibt_recv; /* save last time code to clock data */ save_ltc(pp, gps_time); /* now parse the time string */ if (convert_ascii_time(&up->ibt_stamp, gps_time)) { DPRINT(2, ("%s: process_tpv, stamp='%s'," " recvt='%s' mode=%d\n", up->logname, prettydate(up->ibt_stamp), prettydate(up->ibt_recvt), gps_mode)); /* have to use local receive time as substitute * for the real receive time: TPV does not tell * us. */ up->ibt_local = *rtime; up->ibt_recvt = *rtime; up->ibt_recvt -= up->ibt_fudge; up->fl_ibt = -1; } else { ++up->tc_breply; up->fl_ibt = 0; } } /* Set the precision from the GPSD data * Use the ETP field for an estimation of the precision of the * serial data. If ETP is not available, use the default serial * data precision instead. (Note: The PPS branch has a different * precision estimation, since it gets the proper value directly * from GPSD!) */ ept = json_object_lookup_float_default(jctx, 0, "ept", 2.0e-3); ept = frexp(fabs(ept)*0.70710678, &xlog2); /* ~ sqrt(0.5) */ if (ept < 0.25) xlog2 = INT_MIN; if (ept > 2.0) xlog2 = INT_MAX; up->ibt_prec = clamped_precision(xlog2); } /* ------------------------------------------------------------------ */ static void process_pps( peerT * const peer , json_ctx * const jctx , const l_fp * const rtime) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; int xlog2; ++up->tc_pps_recv; /* Bail out if there's indication that time sync is bad or * if we're explicitly requested to ignore PPS data. */ if (up->fl_nosync) return; up->pps_local = *rtime; /* Now grab the time values. 'clock_*' is the event time of the * pulse measured on the local system clock; 'real_*' is the GPS * reference time GPSD associated with the pulse. */ if (up->pf_nsec) { if ( ! get_binary_time(&up->pps_recvt2, jctx, "clock_sec", "clock_nsec", 1)) goto fail; if ( ! get_binary_time(&up->pps_stamp2, jctx, "real_sec", "real_nsec", 1)) goto fail; } else { if ( ! get_binary_time(&up->pps_recvt2, jctx, "clock_sec", "clock_musec", 1000)) goto fail; if ( ! get_binary_time(&up->pps_stamp2, jctx, "real_sec", "real_musec", 1000)) goto fail; } /* Try to read the precision field from the PPS record. If it's * not there, take the precision from the serial data. */ xlog2 = (int)json_object_lookup_int_default( jctx, 0, "precision", up->ibt_prec); up->pps_prec = clamped_precision(xlog2); /* Get fudged receive times for primary & secondary unit */ up->pps_recvt = up->pps_recvt2; up->pps_recvt -= up->pps_fudge; up->pps_recvt2 -= up->pps_fudge2; pp->lastrec = up->pps_recvt; /* Map to nearest full second as reference time stamp for the * primary channel. Sanity checks are done in evaluation step. */ up->pps_stamp = up->pps_recvt; up->pps_stamp += 0x80000000u; setlfpfrac(up->pps_stamp, 0); if (NULL != up->pps_peer) save_ltc(up->pps_peer->procptr, prettydate(up->pps_stamp2)); DPRINT(2, ("%s: PPS record processed," " stamp='%s', recvt='%s'\n", up->logname, prettydate(up->pps_stamp2), prettydate(up->pps_recvt2))); up->fl_pps = (0 != (pp->sloppyclockflag & CLK_FLAG2)) - 1; up->fl_pps2 = -1; return; fail: DPRINT(1, ("%s: PPS record processing FAILED\n", up->logname)); ++up->tc_breply; } /* ------------------------------------------------------------------ */ static void process_toff( peerT * const peer , json_ctx * const jctx , const l_fp * const rtime) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; ++up->tc_ibt_recv; /* remember this! */ up->pf_toff = -1; /* bail out if there's indication that time sync is bad */ if (up->fl_nosync) return; if ( ! get_binary_time(&up->ibt_recvt, jctx, "clock_sec", "clock_nsec", 1)) goto fail; if ( ! get_binary_time(&up->ibt_stamp, jctx, "real_sec", "real_nsec", 1)) goto fail; up->ibt_recvt -= up->ibt_fudge; up->ibt_local = *rtime; up->fl_ibt = -1; save_ltc(pp, prettydate(up->ibt_stamp)); DPRINT(2, ("%s: TOFF record processed," " stamp='%s', recvt='%s'\n", up->logname, prettydate(up->ibt_stamp), prettydate(up->ibt_recvt))); return; fail: DPRINT(1, ("%s: TOFF record processing FAILED\n", up->logname)); ++up->tc_breply; } /* ------------------------------------------------------------------ */ static void gpsd_parse( peerT * const peer , const l_fp * const rtime) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; const char * clsid; DPRINT(2, ("%s: gpsd_parse: time %s '%.*s'\n", up->logname, ulfptoa(*rtime, 6), up->buflen, up->buffer)); /* See if we can grab anything potentially useful. JSMN does not * need a trailing NUL, but it needs the number of bytes to * process. */ if (!json_parse_record(&up->json_parse, up->buffer, (size_t)up->buflen)) { ++up->tc_breply; return; } /* Now dispatch over the objects we know */ clsid = json_object_lookup_string(&up->json_parse, 0, "class"); if (NULL == clsid) { ++up->tc_breply; return; } if (!strcmp("TPV", clsid)) process_tpv(peer, &up->json_parse, rtime); else if (!strcmp("PPS", clsid)) process_pps(peer, &up->json_parse, rtime); else if (!strcmp("TOFF", clsid)) process_toff(peer, &up->json_parse, rtime); else if (!strcmp("VERSION", clsid)) process_version(peer, &up->json_parse, rtime); else if (!strcmp("WATCH", clsid)) process_watch(peer, &up->json_parse, rtime); else return; /* nothing we know about... */ ++up->tc_recv; /* if possible, feed the PPS side channel */ if (up->pps_peer) eval_pps_secondary( up->pps_peer, up->pps_peer->procptr, up); /* check PPS vs. IBT receive times: * If IBT is before PPS, then clearly the IBT is too old. If PPS * is before IBT by more than one second, then PPS is too old. * Weed out stale time stamps & flags. */ if (up->fl_pps && up->fl_ibt) { l_fp diff; diff = up->ibt_local; diff -= up->pps_local; if (lfpsint(diff) > 0) up->fl_pps = 0; /* pps too old */ else if (lfpsint(diff) < 0) up->fl_ibt = 0; /* serial data too old */ } /* dispatch to the mode-dependent processing functions */ switch (up->mode) { default: case MODE_OP_IBT: eval_serial(peer, pp, up); break; case MODE_OP_STRICT: eval_strict(peer, pp, up); break; case MODE_OP_AUTO: eval_auto(peer, pp, up); break; } } /* ------------------------------------------------------------------ */ static void gpsd_stop_socket( peerT * const peer) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; if (-1 != pp->io.fd) { if (syslogok(pp, up)) msyslog(LOG_INFO, "REFCLOCK: %s: closing socket to GPSD, fd=%d", up->logname, pp->io.fd); else DPRINT(1, ("%s: closing socket to GPSD, fd=%d\n", up->logname, pp->io.fd)); io_closeclock(&pp->io); pp->io.fd = -1; } up->tickover = up->tickpres; up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); up->fl_vers = 0; up->fl_ibt = 0; up->fl_pps = 0; up->fl_watch = 0; } /* ------------------------------------------------------------------ */ static void gpsd_init_socket( peerT * const peer) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; addrinfoT * ai; int rc; int ov; /* draw next address to try */ if (NULL == up->addr) up->addr = s_gpsd_addr; ai = up->addr; up->addr = ai->ai_next; /* try to create a matching socket */ up->fdt = socket( ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (-1 == up->fdt) { if (syslogok(pp, up)) msyslog(LOG_ERR, "REFCLOCK: %s: cannot create GPSD socket: %m", up->logname); goto no_socket; } /* Make sure the socket is non-blocking. Connect/reconnect and * IO happen in an event-driven environment, and synchronous * operations wreak havoc on that. */ rc = fcntl(up->fdt, F_SETFL, O_NONBLOCK, 1); if (-1 == rc) { if (syslogok(pp, up)) msyslog(LOG_ERR, "REFCLOCK: %s: cannot set GPSD socket to non-blocking: %m", up->logname); goto no_socket; } /* Disable nagling. The way both GPSD and NTPD handle the * protocol makes it record-oriented, and in most cases * complete records (JSON serialised objects) will be sent in * one sweep. Nagling gives not much advantage but adds another * delay, which can worsen the situation for some packets. */ ov = 1; rc = setsockopt(up->fdt, IPPROTO_TCP, TCP_NODELAY, (char*)&ov, sizeof(ov)); if (-1 == rc) { if (syslogok(pp, up)) msyslog(LOG_INFO, "REFCLOCK: %s: cannot disable TCP nagle: %m", up->logname); } /* Start a non-blocking connect. There might be a synchronous * connection result we have to handle. */ rc = connect(up->fdt, ai->ai_addr, ai->ai_addrlen); if (-1 == rc) { if (errno == EINPROGRESS) { DPRINT(1, ("%s: async connect pending, fd=%d\n", up->logname, up->fdt)); return; } if (syslogok(pp, up)) msyslog(LOG_ERR, "REFCLOCK: %s: cannot connect GPSD socket: %m", up->logname); goto no_socket; } /* We had a successful synchronous connect, so we add the * refclock processing ASAP. We still have to wait for the * version string and apply the watch command later on, but we * might as well get the show on the road now. */ DPRINT(1, ("%s: new socket connection, fd=%d\n", up->logname, up->fdt)); pp->io.fd = up->fdt; up->fdt = -1; if (0 == io_addclock(&pp->io)) { if (syslogok(pp, up)) msyslog(LOG_ERR, "REFCLOCK: %s: failed to register with I/O engine", up->logname); goto no_socket; } return; no_socket: if (-1 != pp->io.fd) close(pp->io.fd); if (-1 != up->fdt) close(up->fdt); pp->io.fd = -1; up->fdt = -1; up->tickover = up->tickpres; up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); } /* ------------------------------------------------------------------ */ static void gpsd_test_socket( peerT * const peer) { clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; int ec, rc; socklen_t lc; /* Check if the non-blocking connect was finished by testing the * socket for writeability. Use the 'poll()' API if available * and 'select()' otherwise. */ DPRINT(2, ("%s: check connect, fd=%d\n", up->logname, up->fdt)); { struct timespec tout; fd_set wset; memset(&tout, 0, sizeof(tout)); FD_ZERO(&wset); FD_SET(up->fdt, &wset); rc = pselect(up->fdt+1, NULL, &wset, NULL, &tout, NULL); if (0 == rc || !(FD_ISSET(up->fdt, &wset))) return; } /* next timeout is a full one... */ up->tickover = TICKOVER_LOW; /* check for socket error */ ec = 0; lc = sizeof(ec); rc = getsockopt(up->fdt, SOL_SOCKET, SO_ERROR, &ec, &lc); if (-1 == rc || 0 != ec) { const char *errtxt; if (0 == ec) ec = errno; errtxt = strerror(ec); if (syslogok(pp, up)) msyslog(LOG_ERR, "REFCLOCK: %s: async connect to GPSD failed," " fd=%d, ec=%d(%s)", up->logname, up->fdt, ec, errtxt); else DPRINT(1, ("%s: async connect to GPSD failed," " fd=%d, ec=%d(%s)\n", up->logname, up->fdt, ec, errtxt)); goto no_socket; } else { DPRINT(1, ("%s: async connect to GPSD succeeded, fd=%d\n", up->logname, up->fdt)); } /* swap socket FDs, and make sure the clock was added */ pp->io.fd = up->fdt; up->fdt = -1; if (0 == io_addclock(&pp->io)) { if (syslogok(pp, up)) msyslog(LOG_ERR, "REFCLOCK: %s: failed to register with I/O engine", up->logname); goto no_socket; } return; no_socket: if (-1 != up->fdt) { DPRINT(1, ("%s: closing socket, fd=%d\n", up->logname, up->fdt)); close(up->fdt); } up->fdt = -1; up->tickover = up->tickpres; up->tickpres = min(up->tickpres + 5, TICKOVER_HIGH); } /* ===================================================================== * helper stuff */ /* ------------------------------------------------------------------- * store a properly clamped precision value */ static int16_t clamped_precision( int rawprec) { if (rawprec > 0) rawprec = 0; if (rawprec < -32) rawprec = -32; return (int16_t)rawprec; } /* ------------------------------------------------------------------- * Convert a GPSD timestamp (ISO 8601 Format) to an l_fp */ static bool convert_ascii_time( l_fp * fp , const char * gps_time) { char *ep; struct tm gd; struct timespec ts; long dw; /* Use 'strptime' to take the brunt of the work, then parse * the fractional part manually, starting with a digit weight of * 10^8 nanoseconds. */ ts.tv_nsec = 0; ep = strptime(gps_time, "%Y-%m-%dT%H:%M:%S", &gd); if (NULL == ep) return false; /* could not parse the mandatory stuff! */ if (*ep == '.') { dw = 100000000; while (isdigit(*(unsigned char*)++ep)) { ts.tv_nsec += (long)(*(unsigned char*)ep - '0') * dw; dw /= 10; } } if (ep[0] != 'Z' || ep[1] != '\0') return false; /* trailing garbage */ /* Now convert the whole thing into a 'l_fp'. We do not use * 'mkgmtime()' since its not standard and going through the * calendar routines is not much effort, either. */ ts.tv_sec = (ntpcal_tm_to_rd(&gd) - DAY_NTP_STARTS) * SECSPERDAY + ntpcal_tm_to_daysec(&gd); *fp = tspec_intv_to_lfp(ts); return true; } /* ------------------------------------------------------------------- * Save the last timecode string, making sure it's properly truncated * if necessary and NUL terminated in any case. */ static void save_ltc( clockprocT * const pp, const char * const tc) { size_t len; len = (tc) ? strlen(tc) : 0; if (len >= sizeof(pp->a_lastcode)) len = sizeof(pp->a_lastcode) - 1; pp->lencode = (unsigned short)len; memcpy(pp->a_lastcode, tc, len); pp->a_lastcode[len] = '\0'; } /* ------------------------------------------------------------------- * asprintf replacement... it's not available everywhere... */ static int myasprintf( char ** spp, char const * fmt, ... ) { size_t alen, plen; alen = 32; *spp = NULL; do { va_list va; alen += alen; free(*spp); *spp = (char*)malloc(alen); if (NULL == *spp) return -1; va_start(va, fmt); plen = (size_t)vsnprintf(*spp, alen, fmt, va); va_end(va); } while (plen >= alen); return (int)plen; } /* ------------------------------------------------------------------- * dump a raw data buffer */ static char * add_string( char *dp, char *ep, const char *sp) { while (dp != ep && *sp) *dp++ = *sp++; return dp; } static void log_data( peerT *peer, const char *what, const char *buf , size_t len ) { /* we're running single threaded with regards to the clocks. */ static char s_lbuf[2048]; clockprocT * const pp = peer->procptr; gpsd_unitT * const up = (gpsd_unitT *)pp->unitptr; if (debug > 1) { /* SPECIAL DEBUG */ const char *sptr = buf; const char *stop = buf + len; char *dptr = s_lbuf; char *dtop = s_lbuf + sizeof(s_lbuf) - 1; /* for NUL */ while (sptr != stop && dptr != dtop) { if (*sptr == '\\') { dptr = add_string(dptr, dtop, "\\\\"); } else if (isprint(*sptr)) { *dptr++ = *sptr; } else { char fbuf[6]; snprintf(fbuf, sizeof(fbuf), "\\%03o", *(const uint8_t*)sptr); dptr = add_string(dptr, dtop, fbuf); } sptr++; } *dptr = '\0'; mprintf("%s[%s]: '%s'\n", up->logname, what, s_lbuf); } } ntpsec-1.1.0+dfsg1/ntpd/refclock_trimble.c0000644000175000017500000007613613252364117020261 0ustar rlaagerrlaager/* * refclock_trimble - clock driver for the Trimble Palisade, Thunderbolt, * Acutime 2000, Acutime Gold and EndRun Technologies Praecis Ct/Cf/Ce/II * timing receivers * * For detailed information on this program, please refer to the * driver_trimble.html document accompanying the NTPsec distribution. * * Version 3.00; September 17, 2017 * refer to driver_trimble.html for change log * * This software was developed by the Software and Component Technologies * group of Trimble Navigation, Ltd. * * Copyright (c) 1997, 1998, 1999, 2000 Trimble Navigation Ltd. * All rights reserved. * Copyright 2017 by the NTPsec project contributors * SPDX-License-Identifier: BSD-4-clause */ #include "config.h" #ifdef HAVE_SYS_IOCTL_H # include #endif /* not HAVE_SYS_IOCTL_H */ #if defined HAVE_SYS_MODEM_H #include #endif #include #include #include #ifdef HAVE_SYS_IOCTL_H #include #endif #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_stdlib.h" #include "timespecops.h" #include "gpstolfp.h" /* * GPS Definitions */ #define DESCRIPTION "Trimble Palisade/Thunderbolt/Acutime GPSes" /* Long name */ #define NAME "TRIMBLE" /* shortname */ #define PRECISION (-20) /* precision assumed (about 1 us) */ #define REFID "GPS\0" /* reference ID */ #define TRMB_MINPOLL 4 /* 16 seconds */ #define TRMB_MAXPOLL 5 /* 32 seconds */ #define MIN_SAMPLES 7 /* minimum number of samples in the median filter to allow a poll */ /* * I/O Definitions */ #ifndef ENABLE_CLASSIC_MODE #define DEVICE "/dev/trimble%d" /* device name and unit */ #else #define DEVICE "/dev/palisade%d" /* device name and unit */ #endif #define SPEED232 B9600 /* uart speed (9600 baud) */ /* parse consts */ #define RMAX 172 /* TSIP packet 0x58 can be 172 bytes */ #define DLE 0x10 #define ETX 0x03 #define MSG_TSIP 0 #define MSG_PRAECIS 1 #define SPSTAT_LEN 34 /* length of reply from Praecis SPSTAT message */ /* parse states */ #define TSIP_PARSED_EMPTY 0 #define TSIP_PARSED_FULL 1 #define TSIP_PARSED_DLE_1 2 #define TSIP_PARSED_DATA 3 #define TSIP_PARSED_DLE_2 4 #define TSIP_PARSED_ASCII 5 #define TSIP_PARSED_PARITY 6 #define mb(_X_) (up->rpt_buf[(_X_ + 1)]) /* shortcut for buffer access */ /* Conversion Definitions */ #ifdef DEBUG # define GPS_PI (3.1415926535898) # define R2D (180.0/GPS_PI) #endif /* * Structure for build data packets for send (thunderbolt uses it only) * taken from Markus Prosch */ struct packettx { short size; uint8_t *data; }; /* * Trimble unit control structure. */ struct trimble_unit { short unit; /* NTP refclock unit number */ bool got_pkt; /* decoded a packet this poll */ bool got_time; /* got a time packet this poll */ int samples; /* samples in filter this poll */ unsigned char UTC_flags; /* UTC & leap second flag */ unsigned char trk_status; /* reported tracking status */ char rpt_status; /* TSIP Parser State */ size_t rpt_cnt; /* TSIP packet length so far */ char rpt_buf[RMAX]; /* packet assembly buffer */ int type; /* Clock mode type */ bool use_event; /* receiver has event input */ int MCR; /* modem control register value at startup */ bool parity_chk; /* enable parity checking */ l_fp p_recv_time; /* timestamp of last received packet */ unsigned int week; /* GPS week number */ unsigned int TOW; /* GPS time of week */ int UTC_offset; /* GPS-UTC offset */ struct calendar date; /* calendar to avoid leap early announce */ unsigned int build_week; /* GPS week number of ntpd build date */ }; /* * Function prototypes */ static bool trimble_start (int, struct peer *); static void trimble_poll (int, struct peer *); static void trimble_timer (int, struct peer *); static void trimble_io (struct recvbuf *); static void trimble_receive (struct peer *, int); static bool TSIP_decode (struct peer *); static void HW_poll (struct refclockproc *); static double getdbl (uint8_t *); static short getint (uint8_t *); static int32_t getlong (uint8_t *); static void sendsupercmd (struct packettx *buffer, int c1, int c2); static void sendbyte (struct packettx *buffer, int b); static void sendint (struct packettx *buffer, int a); static int sendetx (struct packettx *buffer, int fd); static void init_thunderbolt (int fd); #define PAL_TSTATS 14 #ifdef DEBUG static const char tracking_status[PAL_TSTATS+1][16] = { "Doing Fixes", "Good 1SV", "Approx. 1SV", "Need Time", "Need INIT", "PDOP too High", "Bad 1SV", "0SV Usable", "1SV Usable", "2SV Usable", "3SV Usable", "No Integrity", "Diff Corr", "Overdet Clock", "Invalid"}; #endif static const bool tracking_status_usable[PAL_TSTATS+1] = { true, true, false, false, false, false, false, false, false, false, false, false, false, true, false}; #define TB_DECOD_STATS 16 /* convert TB decoding status to tracking_status */ static const unsigned int tb_decod_conv[TB_DECOD_STATS+1] = { 0, 3, 14, 5, 14, 14, 14, 14, 7, 8, 9, 10, 6, 14, 14, 14, 11}; #define TB_DISC_MODES 7 #ifdef DEBUG static const char tb_disc_mode[TB_DISC_MODES+1][16] = { "normal", "power-up", "auto holdover", "manual holdover", "recovery", "unknown", "disabled", "invalid"}; #endif static const bool tb_disc_in_holdover[TB_DISC_MODES+1] = { false, false, true, true, false, false, false, false}; /* * Transfer vector */ struct refclock refclock_trimble = { NAME, /* basename of driver */ trimble_start, /* start up driver */ NULL, /* shut down driver in the standard way */ trimble_poll, /* transmit poll message */ NULL, /* control - not used */ NULL, /* initialize driver (not used) */ trimble_timer /* called at 1Hz by mainloop */ }; /* Extract the clock type from the mode setting */ #define CLK_TYPE(x) ((int)(((x)->cfg.mode) & 0x7F)) /* Supported clock types */ #define CLK_PALISADE 0 /* Trimble Palisade */ #define CLK_PRAECIS 1 /* Endrun Technologies Praecis */ #define CLK_THUNDERBOLT 2 /* Trimble Thunderbolt GPS Receiver */ #define CLK_ACUTIME 3 /* Trimble Acutime Gold */ /* packet 8f-ad UTC flags */ #define UTC_AVAILABLE 0x01 #define LEAP_SCHEDULED 0x10 /* * sendsupercmd - Build super data packet for sending */ static void sendsupercmd ( struct packettx *buffer, int c1, int c2 ) { *buffer->data = DLE; *(buffer->data + 1) = (unsigned char)c1; *(buffer->data + 2) = (unsigned char)c2; buffer->size = 3; } /* * sendbyte - */ static void sendbyte ( struct packettx *buffer, int b ) { if (b == DLE) *(buffer->data+buffer->size++) = DLE; *(buffer->data+buffer->size++) = (unsigned char)b; } /* * sendint - */ static void sendint ( struct packettx *buffer, int a ) { sendbyte(buffer, (unsigned char)((a>>8) & 0xff)); sendbyte(buffer, (unsigned char)(a & 0xff)); } /* * sendetx - Send packet or super packet to the device */ static int sendetx ( struct packettx *buffer, int fd ) { ssize_t result; *(buffer->data+buffer->size++) = DLE; *(buffer->data+buffer->size++) = ETX; result = write(fd, buffer->data, (size_t)buffer->size); if (result != -1) return (result); else return (-1); } /* * init_thunderbolt - Prepares Thunderbolt receiver to be used with * NTP (also taken from Markus Prosch). */ static void init_thunderbolt ( int fd ) { struct packettx tx; tx.size = 0; tx.data = (uint8_t *) malloc(100); /* set UTC time */ sendsupercmd (&tx, 0x8E, 0xA2); sendbyte (&tx, 0x3); sendetx (&tx, fd); /* activate packets 0x8F-AB and 0x8F-AC */ sendsupercmd (&tx, 0x8E, 0xA5); sendint (&tx, 0x5); sendetx (&tx, fd); free(tx.data); } /* * trimble_start - open the devices and initialize data for processing */ static bool trimble_start ( int unit, struct peer *peer ) { struct trimble_unit *up; struct refclockproc *pp; int fd; struct termios tio; struct calendar build_date; unsigned int cflag, iflag; char device[20], *path; pp = peer->procptr; pp->clockname = NAME; /* Open serial port. */ if (peer->cfg.path) path = peer->cfg.path; else { int rcode; snprintf(device, sizeof(device), DEVICE, unit); /* build a path */ rcode = snprintf(device, sizeof(device), DEVICE, unit); if ( 0 > rcode ) { /* failed, set to NUL */ device[0] = '\0'; } path = device; } fd = refclock_open(path, peer->cfg.baud ? peer->cfg.baud : SPEED232, LDISC_RAW); if (0 > fd) { msyslog(LOG_ERR, "REFCLOCK: %s Trimble device open(%s) failed", refclock_name(peer), path); /* coverity[leaked_handle] */ return false; } LOGIF(CLOCKINFO, (LOG_NOTICE, "%s open at %s", refclock_name(peer), path)); if (tcgetattr(fd, &tio) < 0) { msyslog(LOG_ERR, "REFCLOCK: %s tcgetattr failed: %m", refclock_name(peer)); close(fd); return false; } /* Allocate and initialize unit structure */ up = emalloc_zero(sizeof(*up)); up->type = CLK_TYPE(peer); up->parity_chk = true; up->use_event = true; pp->disp = 1000 * S_PER_NS; /* extra ~500ns for serial port delay */ switch (up->type) { case CLK_PALISADE: msyslog(LOG_NOTICE, "REFCLOCK: %s Palisade mode enabled", refclock_name(peer)); break; case CLK_PRAECIS: msyslog(LOG_NOTICE, "REFCLOCK: %s Praecis mode enabled", refclock_name(peer)); /* account for distance to tower */ pp->disp = .00002; break; case CLK_THUNDERBOLT: msyslog(LOG_NOTICE, "REFCLOCK: %s Thunderbolt mode enabled", refclock_name(peer)); up->parity_chk = false; up->use_event = false; /* * packet transmission delay varies from 9ms to 32ms depending * on the number of SVs the receiver is attempting to track */ pp->disp = .023; break; case CLK_ACUTIME: msyslog(LOG_NOTICE, "REFCLOCK: %s Acutime Gold mode enabled", refclock_name(peer)); break; default: msyslog(LOG_NOTICE, "REFCLOCK: %s mode unknown", refclock_name(peer)); break; close(fd); free(up); return false; } tio.c_cflag = (CS8|CLOCAL|CREAD); tio.c_iflag &= (unsigned)~ICRNL; if (up->parity_chk) { tio.c_cflag |= (PARENB|PARODD); tio.c_iflag &= (unsigned)~IGNPAR; tio.c_iflag |= (INPCK|PARMRK); } cflag = tio.c_cflag; iflag = tio.c_iflag; if (tcsetattr(fd, TCSANOW, &tio) == -1 || tcgetattr(fd, &tio) == -1 || tio.c_cflag != cflag || tio.c_iflag != iflag) { msyslog(LOG_ERR, "REFCLOCK: %s tcsetattr failed: wanted cflag 0x%x got 0x%x, wanted iflag 0x%x got 0x%x, return: %m", refclock_name(peer), cflag, (unsigned int)tio.c_cflag, iflag, (unsigned int)tio.c_iflag); close(fd); free(up); return false; } if (up->use_event) { /* * The width of the RTS pulse must be either less than 5us or * greater than 600ms or the Acutime 2000 may try to switch its * port A baud rate because of "Auto-DGPS". The Praecis will * produce unstable timestamps (-7us instead of +-40ns offsets) * when pulse width is more than a few us and less than 100us. * Palisade minimum puse width is specified as 1us. To satisfy * these constraints the RTS pin is idled with a positive * voltage and pulsed negative. */ if (ioctl(fd, TIOCMGET, &up->MCR) < 0) { msyslog(LOG_ERR, "REFCLOCK: %s TIOCMGET failed: %m", refclock_name(peer)); close(fd); free(up); return false; } up->MCR |= TIOCM_RTS; if (ioctl(fd, TIOCMSET, &up->MCR) < 0 || !(up->MCR & TIOCM_RTS)) { msyslog(LOG_ERR, "REFCLOCK: %s TIOCMSET failed: MCR=0x%x, return=%m", refclock_name(peer), (unsigned int)up->MCR); close(fd); free(up); return false; } } pp->io.clock_recv = trimble_io; pp->io.srcclock = peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { msyslog(LOG_ERR, "%s io_addclock failed", refclock_name(peer)); close(fd); pp->io.fd = -1; free(up); return false; } /* Initialize miscellaneous variables */ pp->unitptr = up; pp->clockdesc = DESCRIPTION; peer->precision = PRECISION; peer->sstclktype = CTL_SST_TS_UHF; peer->cfg.minpoll = TRMB_MINPOLL; peer->cfg.maxpoll = TRMB_MAXPOLL; memcpy((char *)&pp->refid, REFID, REFIDLEN); up->unit = (short) unit; up->rpt_status = TSIP_PARSED_EMPTY; up->rpt_cnt = 0; if (ntpcal_get_build_date(&build_date)) { caltogps(&build_date, 0, &up->build_week, NULL); up->build_week -= 2; /* timezone, UTC offset, build machine clock */ } else { up->build_week = 0; } if (up->build_week < MIN_BUILD_GPSWEEK || up->build_week > MAX_BUILD_GPSWEEK) { msyslog(LOG_ERR, "REFCLOCK: %s ntpcal_get_build_date() failed: %u", refclock_name(peer), up->build_week); close(fd); pp->io.fd = -1; free(up); return false; } if (up->type == CLK_THUNDERBOLT) init_thunderbolt(fd); return true; } /* * TSIP_decode - decode the TSIP data packets */ static bool TSIP_decode ( struct peer *peer ) { unsigned char id, decod_stat, disc_mode, timing_flags; long secint; double secs, secfrac; unsigned short event, m_alarms; uint32_t holdover_t; struct trimble_unit *up; struct refclockproc *pp; pp = peer->procptr; up = pp->unitptr; id = (unsigned char)up->rpt_buf[0]; if (id == 0x8f) { /* Superpackets */ event = (unsigned short) (getint((uint8_t *) &mb(1)) & 0xffff); if ((up->type != CLK_THUNDERBOLT) && !event) /* ignore auto-report */ return false; switch (mb(0) & 0xff) { case 0x0B: /* * comprehensive time packet: sent after 8f-ad from * Palisade and Acutime */ if (up->rpt_cnt != 74) { DPRINT(1, ("TSIP_decode: unit %d: 8f-0b packet length is not 74 (%d)\n", up->unit, (int)up->rpt_cnt)); refclock_report(peer, CEVNT_BADREPLY); return false; } up->got_time = true; #ifdef DEBUG if (debug > 1) { /* SPECIAL DEBUG */ int st, ts; double lat, lon, alt; lat = getdbl((uint8_t *) &mb(42)) * R2D; lon = getdbl((uint8_t *) &mb(50)) * R2D; alt = getdbl((uint8_t *) &mb(58)); printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n", up->unit, lat,lon,alt); printf("TSIP_decode: unit %d: Sats:", up->unit); for (st = 66, ts = 0; st <= 73; st++) if (mb(st)) { if (mb(st) > 0) ts++; printf(" %02d", mb(st)); } printf(" : Tracking %d\n", ts); } #endif if (!tracking_status_usable[up->trk_status]) { DPRINT(1, ("TSIP_decode: unit %d: unusable tracking status: %s\n", up->unit, tracking_status[up->trk_status])); return false; } up->UTC_offset = getint((uint8_t *) &mb(16)); if (!(up->UTC_flags & UTC_AVAILABLE) || (up->UTC_offset == 0)) { pp->leap = LEAP_NOTINSYNC; DPRINT(1, ("TSIP_decode: unit %d: UTC data not available\n", up->unit)); return false; } secs = getdbl((uint8_t *) &mb(3)); secint = (long) secs; secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */ pp->nsec = (long) (secfrac * NS_PER_S); secint %= SECSPERDAY; /* Only care about today */ up->date.hour = (int)(secint / SECSPERHR); secint %= SECSPERHR; up->date.minute = (int)(secint / 60); secint %= 60; up->date.second = secint % 60; up->date.monthday = (uint8_t)mb(11); up->date.month = (uint8_t)mb(12); up->date.year = (uint16_t)getint((uint8_t *) &mb(13)); up->date.yearday = 0; caltogps(&up->date, up->UTC_offset, &up->week, &up->TOW); gpsweekadj(&up->week, up->build_week); gpstocal(up->week, up->TOW, up->UTC_offset, &up->date); if ((up->UTC_flags & LEAP_SCHEDULED) && /* Avoid early announce: https://bugs.ntp.org/2773 */ (6 == up->date.month || 12 == up->date.month)) pp->leap = LEAP_ADDSECOND; else pp->leap = LEAP_NOWARNING; DPRINT(2, ("TSIP_decode: unit %d: 8f-0b #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC %d\n tracking status: %s\n", up->unit, event, up->date.hour, up->date.minute, up->date.second, pp->nsec, up->date.month, up->date.monthday, up->date.year, up->UTC_offset, tracking_status[up->trk_status])); /* don't reuse UTC flags or tracking status */ up->UTC_flags = 0; up->trk_status = PAL_TSTATS; return true; break; case 0xAD: /* * primary UTC time packet: first packet sent after PPS * from Palisade, Acutime, and Praecis */ if (up->rpt_cnt != 22) { DPRINT(1, ("TSIP_decode: unit %d: 8f-ad packet length is not 22 (%d)\n", up->unit, (int)up->rpt_cnt)); refclock_report(peer, CEVNT_BADREPLY); return false; } /* flags checked in 8f-0b for Palisade and Acutime */ up->trk_status = (unsigned char)mb(18); if (up->trk_status > PAL_TSTATS) up->trk_status = PAL_TSTATS; up->UTC_flags = (unsigned char)mb(19); /* get timecode from 8f-0b except with Praecis */ if (up->type != CLK_PRAECIS) return false; if (!tracking_status_usable[up->trk_status]) { DPRINT(1, ("TSIP_decode: unit %d: unusable tracking status: %s\n", up->unit, tracking_status[up->trk_status])); return false; } if (!(up->UTC_flags & UTC_AVAILABLE)) { pp->leap = LEAP_NOTINSYNC; DPRINT(1, ("TSIP_decode: unit %d: UTC data not available\n", up->unit)); return false; } pp->nsec = (long) (getdbl((uint8_t *) &mb(3)) * NS_PER_S); up->date.year = (uint16_t)getint((uint8_t *) &mb(16)); up->date.hour = (uint8_t)mb(11); up->date.minute = (uint8_t)mb(12); up->date.second = (uint8_t)mb(13); up->date.month = (uint8_t)mb(15); up->date.monthday = (uint8_t)mb(14); caltogps(&up->date, 0, &up->week, &up->TOW); gpsweekadj(&up->week, up->build_week); gpstocal(up->week, up->TOW, 0, &up->date); if ((up->UTC_flags & LEAP_SCHEDULED) && /* Avoid early announce: https://bugs.ntp.org/2773 */ (6 == up->date.month || 12 == up->date.month)) pp->leap = LEAP_ADDSECOND; else pp->leap = LEAP_NOWARNING; DPRINT(2, ("TSIP_decode: unit %d: 8f-ad #%d %02d:%02d:%02d.%09ld %02d/%02d/%04d UTC 0x%02x\n tracking status: %s\n", up->unit, event, up->date.hour, up->date.minute, up->date.second, pp->nsec, up->date.month, up->date.monthday, up->date.year, up->UTC_flags, tracking_status[up->trk_status])); return true; break; case 0xAC: /* * supplemental timing packet: sent after 8f-ab from * Thunderbolt */ if (up->rpt_cnt != 68) { DPRINT(1, ("TSIP_decode: unit %d: 8f-ac packet length is not 68 (%d)\n", up->unit, (int)up->rpt_cnt)); refclock_report(peer, CEVNT_BADREPLY); return false; } up->got_time = true; #ifdef DEBUG if (debug > 1) { /* SPECIAL DEBUG */ double lat, lon, alt; lat = getdbl((uint8_t *) &mb(36)) * R2D; lon = getdbl((uint8_t *) &mb(44)) * R2D; alt = getdbl((uint8_t *) &mb(52)); printf("TSIP_decode: unit %d: Latitude: %03.4f Longitude: %03.4f Alt: %05.2f m\n", up->unit, lat,lon,alt); } #endif decod_stat = (unsigned char)mb(12); if (decod_stat > TB_DECOD_STATS) decod_stat = TB_DECOD_STATS; disc_mode = (unsigned char)mb(2); if (disc_mode > TB_DISC_MODES) disc_mode = TB_DISC_MODES; DPRINT(2, ("TSIP_decode: unit %d: leap=%d decod.stat=%s disc.mode=%s\n", up->unit, pp->leap, tracking_status[tb_decod_conv[decod_stat]], tb_disc_mode[disc_mode])); m_alarms = (unsigned short)getint((uint8_t *) &mb(10)); if (m_alarms & 0x200) { DPRINT(1, ("TSIP_decode: unit %d: 'position questionable' flag is set,\n you must update the unit's stored position.\n", up->unit)); return false; } holdover_t = (uint32_t)getlong((uint8_t *) &mb(4)); if (!tracking_status_usable[tb_decod_conv[decod_stat]]) { if (pp->fudgetime2 < 0.5) { /* holdover not enabled */ DPRINT(1, ("TSIP_decode: unit %d: decod.stat of '%s' is unusable\n", up->unit, tracking_status[tb_decod_conv[decod_stat]])); return false; }else if (tb_disc_in_holdover[disc_mode] && holdover_t > pp->fudgetime2) { DPRINT(1, ("TSIP_decode: unit %d: unit in holdover (disc.mode=%s) with decod.stat of '%s' but holdover time of %us exceeds time2(%.fs)\n", up->unit, tb_disc_mode[disc_mode], tracking_status[tb_decod_conv[decod_stat]], holdover_t, pp->fudgetime2)); return false; } else if (!tb_disc_in_holdover[disc_mode]) { DPRINT(1, ("TSIP_decode: unit %d: not in holdover (disc.mode=%s) and decod.stat of '%s' is unusable\n", up->unit, tb_disc_mode[disc_mode], tracking_status[tb_decod_conv[decod_stat]])); return false; } } if (up->UTC_flags != UTC_AVAILABLE) return false; gpsweekadj(&up->week, up->build_week); gpstocal(up->week, up->TOW, up->UTC_offset, &up->date); if ((m_alarms & 0x80) && /* Avoid early announce: https://bugs.ntp.org/2773 */ (6 == up->date.month || 12 == up->date.month) ) pp->leap = LEAP_ADDSECOND; /* we ASSUME addsecond */ else pp->leap = LEAP_NOWARNING; DPRINT(2, ("TSIP_decode: unit %d: 8f-ac TOW: %u week: %u adj.t: %02d:%02d:%02d.0 %02d/%02d/%04d\n", up->unit, up->TOW, up->week, up->date.hour, up->date.minute, up->date.second, up->date.month, up->date.monthday, up->date.year)); return true; break; case 0xAB: /* * primary timing packet: first packet sent after PPS * from Thunderbolt */ if (up->rpt_cnt != 17) { DPRINT(1, ("TSIP_decode: unit %d: 8f-ab packet length is not 17 (%d)\n", up->unit, (int)up->rpt_cnt)); refclock_report(peer, CEVNT_BADREPLY); return 0; } timing_flags = (unsigned char)mb(9); #ifdef DEBUG if (debug > 1) { /* SPECIAL DEBUG */ printf("TSIP_decode: unit %d: timing flags:0x%02X=\n", up->unit, timing_flags); if (timing_flags & 0x08) { printf(" timecode aligned to GPS(UTC not avail.), PPS aligned to GPS(UTC not avail.)\n"); } else { if (timing_flags & 0x01) printf(" timecode aligned to UTC, "); else printf(" timecode aligned to GPS(misconfigured), "); if (timing_flags & 0x02) printf("PPS aligned to UTC\n"); else printf("PPS aligned to GPS(misconfigured)\n"); } if (timing_flags & 0x04) printf(" time is NOT set, "); else printf(" time is set, "); if (timing_flags & 0x08) printf("UTC is NOT available, "); else printf("UTC is available, "); if (timing_flags & 0x10) printf("test-mode timesource(misconfigured)\n"); else printf("satellite timesource\n"); } #endif up->UTC_flags = 0; up->UTC_offset = getint((uint8_t *) &mb(7)); if (timing_flags & 0x04 || timing_flags & 0x08 || up->UTC_offset == 0) { DPRINT(1, ("TSIP_decode: unit %d: time not set or UTC offset unavailable\n", up->unit)); return false; } /* * configuration is sent only at ntpd startup. if unit * loses power it will revert to the factory default * time alignment (GPS) */ if (!(timing_flags & 0x01) || !(timing_flags & 0x02) || (timing_flags & 0x10)) { DPRINT(1, ("TSIP_decode: unit %d: 8f-ab flags: not UTC time: unit is misconfigured (0x%02X)\n", up->unit, timing_flags)); pp->leap = LEAP_NOTINSYNC; refclock_report(peer, CEVNT_BADTIME); return false; } up->TOW = (uint32_t)getlong((uint8_t *) &mb(1)); up->week = (uint32_t)getint((uint8_t *) &mb(5)); pp->lastrec = up->p_recv_time; pp->nsec = 0; up->UTC_flags = UTC_AVAILABLE; /* flag for 8f-ac */ return false; break; default: break; } /* switch */ } return false; } /* * trimble__receive - receive data from the serial interface */ static void trimble_receive ( struct peer * peer, int type ) { struct trimble_unit *up; struct refclockproc *pp; /* Initialize pointers and read the timecode and timestamp. */ pp = peer->procptr; up = pp->unitptr; /* * Wait for fudge flags to initialize. Also, startup may have caused * a spurious edge, so wait for first HW_poll() */ if (pp->polls < 1) return; up->got_pkt = true; if (MSG_TSIP == type) { if (!TSIP_decode(peer)) return; } else { if (SPSTAT_LEN == up->rpt_cnt && up->rpt_buf[up->rpt_cnt - 1] == '\r') { up->rpt_buf[up->rpt_cnt - 1] = '\0'; record_clock_stats(peer, up->rpt_buf); } return; } /* add sample to filter */ pp->lastref = pp->lastrec; pp->year = up->date.year; pp->day = up->date.yearday; pp->hour = up->date.hour; pp->minute = up->date.minute; pp->second = up->date.second; DPRINT(2, ("trimble_receive: unit %d: %4d %03d %02d:%02d:%02d.%09ld\n", up->unit, pp->year, pp->day, pp->hour, pp->minute, pp->second, pp->nsec)); if (!refclock_process(pp)) { refclock_report(peer, CEVNT_BADTIME); DPRINT(1, ("trimble_receive: unit %d: refclock_process failed!\n", up->unit)); return; } up->samples++; } /* * trimble_poll - called by the transmit procedure */ static void trimble_poll ( int unit, struct peer *peer ) { struct trimble_unit *up; struct refclockproc *pp; int cl; bool err; UNUSED_ARG(unit); pp = peer->procptr; up = pp->unitptr; /* samples are not taken until second poll */ if (++pp->polls < 2) return; /* check status for the previous poll interval */ err = (up->samples < MIN_SAMPLES); if (err) { refclock_report(peer, CEVNT_TIMEOUT); if (!up->got_pkt) { DPRINT(1, ("trimble_poll: unit %d: no packets found\n", up->unit)); } else if (!up->got_time) { DPRINT(1, ("trimble_poll: unit %d: packet(s) found but none were usable.\nVerify unit isn't connected to Port B and flag3 is correct for Palisade/Acutime\n", up->unit)); } else { DPRINT(1, ("trimble_poll: unit %d: not enough samples (%d, min %d), skipping poll\n", up->unit, up->samples, MIN_SAMPLES)); pp->codeproc = pp->coderecv; /* reset filter */ } } up->got_time = false; up->got_pkt = false; up->samples = 0; if (err) return; /* ask Praecis for its signal status */ if(up->type == CLK_PRAECIS) { if(write(peer->procptr->io.fd,"SPSTAT\r\n",8) < 0) msyslog(LOG_ERR, "REFCLOCK: %s write: %m:", refclock_name(peer)); } /* record clockstats */ cl = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), "%4d %03d %02d:%02d:%02d.%09ld", pp->year, pp->day, pp->hour,pp->minute, pp->second, pp->nsec); pp->lencode = (cl < (int)sizeof(pp->a_lastcode)) ? cl : 0; record_clock_stats(peer, pp->a_lastcode); DPRINT(2, ("trimble_poll: unit %d: %s\n", up->unit, prettydate(pp->lastrec))); if (pp->hour == 0 && up->week > up->build_week + 1000) msyslog(LOG_WARNING, "REFCLOCK: %s current GPS week number (%u) is more than 1000 weeks past ntpd's build date (%u), please update", refclock_name(peer), up->week, up->build_week); /* process samples in filter */ refclock_receive(peer); } /* * trimble_io - create TSIP packets or ASCII strings from serial data stream */ static void trimble_io ( struct recvbuf *rbufp ) { struct trimble_unit *up; struct refclockproc *pp; struct peer *peer; char * c, * d; peer = rbufp->recv_peer; pp = peer->procptr; up = pp->unitptr; c = (char *) &rbufp->recv_space; d = c + rbufp->recv_length; while (c != d) { switch (up->rpt_status) { case TSIP_PARSED_DLE_1: switch (*c) { case 0: case DLE: case ETX: up->rpt_status = TSIP_PARSED_EMPTY; break; default: up->rpt_status = TSIP_PARSED_DATA; /* save packet ID */ up->rpt_buf[0] = *c; /* save packet receive time */ up->p_recv_time = rbufp->recv_time; break; } break; case TSIP_PARSED_DATA: if (*c == DLE) up->rpt_status = TSIP_PARSED_DLE_2; else if (up->parity_chk && *c == '\377') up->rpt_status = TSIP_PARSED_PARITY; else mb(up->rpt_cnt++) = *c; break; case TSIP_PARSED_PARITY: if (*c == '\377') { up->rpt_status = TSIP_PARSED_DATA; mb(up->rpt_cnt++) = *c; } else { msyslog(LOG_ERR, "REFCLOCK: %s: detected serial parity error or receive buffer overflow", refclock_name(peer)); up->rpt_status = TSIP_PARSED_EMPTY; } break; case TSIP_PARSED_DLE_2: if (*c == DLE) { up->rpt_status = TSIP_PARSED_DATA; mb(up->rpt_cnt++) = *c; } else if (*c == ETX) { up->rpt_status = TSIP_PARSED_FULL; trimble_receive(peer, MSG_TSIP); } else { /* error: start new report packet */ up->rpt_status = TSIP_PARSED_DLE_1; up->rpt_buf[0] = *c; } break; case TSIP_PARSED_ASCII: if (*c == '\n') { mb(up->rpt_cnt++) = *c; up->rpt_status = TSIP_PARSED_FULL; trimble_receive(peer, MSG_PRAECIS); } else if (up->parity_chk && *c == '\377') { up->rpt_status = TSIP_PARSED_PARITY; } else { mb(up->rpt_cnt++) = *c; } break; case TSIP_PARSED_FULL: case TSIP_PARSED_EMPTY: default: up->rpt_cnt = 0; if (*c == DLE) { up->rpt_status = TSIP_PARSED_DLE_1; } else if (up->type == CLK_PRAECIS && NULL != strchr("6L789ADTP", *c)) { /* Praecis command reply */ up->rpt_buf[0] = *c; up->rpt_status = TSIP_PARSED_ASCII; } else { up->rpt_status = TSIP_PARSED_EMPTY; } break; } c++; if (up->rpt_cnt > RMAX - 2) {/* additional byte for ID */ up->rpt_status = TSIP_PARSED_EMPTY; DPRINT(1, ("trimble_io: unit %d: oversize serial message (%luB) 0x%02x discarded\n", up->unit, (unsigned long)up->rpt_cnt, (uint8_t)up->rpt_buf[0])); } } /* while chars in buffer */ } /* * trimble_timer - trigger an event at 1Hz */ static void trimble_timer( int unit, struct peer * peer ) { struct trimble_unit *up; struct refclockproc *pp; UNUSED_ARG(unit); pp = peer->procptr; up = pp->unitptr; if (up->use_event) HW_poll(pp); } /* * HW_poll - trigger the event input */ static void HW_poll ( struct refclockproc * pp ) { struct trimble_unit *up; static const struct timespec ts = {0, 13 * NS_PER_MS}; up = pp->unitptr; /* Edge trigger */ if (pp->sloppyclockflag & CLK_FLAG3) { IGNORE(write (pp->io.fd, "", 1)); } else { up->MCR &= ~TIOCM_RTS; /* set RTS low from high idle state */ IGNORE(ioctl(pp->io.fd, TIOCMSET, &up->MCR)); /* * The Acutime 2000 will occasionally transmit with parity * errors if the low state is held for less than 1ms, and the * Praecis will produce unstable timestamps if the low state is * held for less than 12ms. */ nanosleep(&ts, NULL); up->MCR |= TIOCM_RTS; /* make edge / restore idle */ IGNORE(ioctl(pp->io.fd, TIOCMSET, &up->MCR)); } /* get timestamp after triggering since RAND_bytes is slow */ get_systime(&pp->lastrec); } /* * getdbl - copy/swap a big-endian palisade double into a host double */ static double getdbl ( uint8_t *bp ) { #ifdef WORDS_BIGENDIAN double out; memcpy(&out, bp, sizeof(out)); return out; #else union { uint8_t ch[8]; uint32_t u32[2]; } ui; union { double out; uint32_t u32[2]; } uo; memcpy(ui.ch, bp, sizeof(ui.ch)); /* least-significant 32 bits of double from swapped bp[4] to bp[7] */ uo.u32[0] = ntohl(ui.u32[1]); /* most-significant 32 bits from swapped bp[0] to bp[3] */ uo.u32[1] = ntohl(ui.u32[0]); return uo.out; #endif } /* * getint - copy/swap a big-endian palisade short into a host short */ static short getint ( uint8_t *bp ) { unsigned short us; memcpy(&us, bp, sizeof(us)); return (short)ntohs(us); } /* * getlong -copy/swap a big-endian palisade 32-bit int into a host 32-bit int */ static int32_t getlong( uint8_t *bp ) { uint32_t u32; memcpy(&u32, bp, sizeof(u32)); return (int32_t)(uint32_t)ntohl(u32); } ntpsec-1.1.0+dfsg1/libntp/0000775000175000017500000000000013252650651015120 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/libntp/socktoa.c0000644000175000017500000000465613252364117016737 0ustar rlaagerrlaager/* * socktoa.c socktoa(), sockporttoa(), and sock_hash() */ #include "config.h" #include #include #include #include #include #include "isc_netaddr.h" #include "ntp_fp.h" #include "lib_strbuf.h" #include "ntp_stdlib.h" #include "ntp.h" /* * socktoa - return a numeric host name from a sockaddr_storage structure */ const char * socktoa( const sockaddr_u *sock ) { int saved_errno; char * res; char * addr; unsigned long scope; saved_errno = errno; res = lib_getbuf(); if (NULL == sock) { strlcpy(res, "(null)", LIB_BUFLENGTH); } else { switch(AF(sock)) { case AF_INET: case AF_UNSPEC: inet_ntop(AF_INET, PSOCK_ADDR4(sock), res, LIB_BUFLENGTH); break; case AF_INET6: inet_ntop(AF_INET6, PSOCK_ADDR6(sock), res, LIB_BUFLENGTH); scope = SCOPE_VAR(sock); if (0 != scope && !strchr(res, '%')) { addr = res; res = lib_getbuf(); snprintf(res, LIB_BUFLENGTH, "%s%%%lu", addr, scope); res[LIB_BUFLENGTH - 1] = '\0'; } break; default: snprintf(res, LIB_BUFLENGTH, "(socktoa unknown family %d)", AF(sock)); } } errno = saved_errno; return res; } const char * sockporttoa( const sockaddr_u *sock ) { int saved_errno; const char * atext; char * buf; saved_errno = errno; atext = socktoa(sock); buf = lib_getbuf(); snprintf(buf, LIB_BUFLENGTH, (IS_IPV6(sock)) ? "[%s]:%hu" : "%s:%hu", atext, SRCPORT(sock)); errno = saved_errno; return buf; } /* * sock_hash - hash a sockaddr_u structure */ unsigned short sock_hash( const sockaddr_u *addr ) { unsigned int hashVal; size_t len; const uint8_t *pch; hashVal = 0; len = 0; /* * We can't just hash the whole thing because there are hidden * fields in sockaddr_in6 that might be filled in by recvfrom(), * so just use the family, port and address. */ pch = (const void *)&AF(addr); hashVal = 37 * hashVal + *pch; if (sizeof(AF(addr)) > 1) { pch++; hashVal = 37 * hashVal + *pch; } switch(AF(addr)) { case AF_INET: pch = (const void *)&SOCK_ADDR4(addr); len = sizeof(SOCK_ADDR4(addr)); break; case AF_INET6: pch = (const void *)&SOCK_ADDR6(addr); len = sizeof(SOCK_ADDR6(addr)); break; default: /* huh? */ break; } for (unsigned int j = 0; j < len ; j++) hashVal = 37 * hashVal + pch[j]; return (unsigned short)(hashVal & USHRT_MAX); } ntpsec-1.1.0+dfsg1/libntp/wscript0000644000175000017500000000330513252364117016534 0ustar rlaagerrlaagerdef build(ctx): libntp_source = [ "assert.c", "authkeys.c", "authreadkeys.c", "clocktime.c", "decodenetnum.c", "dolfptoa.c", "getopt.c", "initnetwork.c", "isc_error.c", "isc_interfaceiter.c", "isc_net.c", "macencrypt.c", "netof.c", "ntp_endian.c", "ntp_random.c", "ntp_dns.c", "numtoa.c", "recvbuff.c", "refidsmear.c", "socket.c", "socktoa.c", "ssl_init.c", "syssignal.c", ] libntp_source_sharable = [ "clockwork.c", "emalloc.c", "hextolfp.c", "lib_strbuf.c", "msyslog.c", "ntp_calendar.c", "prettydate.c", "statestr.c", "systime.c", "timespecops.c", ] if not ctx.env.HAVE_STRLCAT or not ctx.env.HAVE_STRLCPY: libntp_source_sharable += ["strl_obsd.c"] includes = ctx.env.PLATFORM_INCLUDES # C library ctx( features="c cstlib bld_include src_include", includes=includes, source=libntp_source + libntp_source_sharable, target="ntp", use="CRYPTO", ) # Loadable Python extension # # In theory this should use PYTHONARCHDIR, but that doesn't work on # CentOS (at least), for some as-yet-undetermined reason. On most # platforms, PYTHONARCHDIR == PYTHONDIR, anyway. ctx( features="c cshlib bld_include src_include pyext", install_path='${PYTHONDIR}/ntp', includes=includes, source=["pymodule.c"] + libntp_source_sharable, target="../pylib/ntpc", # Put the output in the pylib directory use="M RT CRYPTO", ) ntpsec-1.1.0+dfsg1/libntp/isc_interfaceiter.c0000644000175000017500000016257013252364117020756 0ustar rlaagerrlaager/* * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ #include "config.h" #include #include #ifdef HAVE_SYS_SOCKIO_H #include /* Required for ifiter_ioctl.c. */ #endif #include #include #include #include #include /* Contractual promise. */ #include "ntp_assert.h" #include "ntp_stdlib.h" #include "isc_interfaceiter.h" #include "isc_netaddr.h" #include "isc_result.h" #include "isc_error.h" /* Must follow . */ #ifdef HAVE_NET_IF6_H #include #endif #include #ifdef HAVE_LINUX_IF_ADDR_H # include #endif #define ISC_TF(x) ((x) ? true : false) static void isc_netaddr_setzone(isc_netaddr_t *netaddr, uint32_t zone); static void isc_netaddr_setzone(isc_netaddr_t *netaddr, uint32_t zone) { /* we currently only support AF_INET6. */ REQUIRE(netaddr->family == AF_INET6); netaddr->zone = zone; } typedef struct { unsigned int magic; } isc__magic_t; /* * To use this macro the magic number MUST be the first thing in the * structure, and MUST be of type "unsigned int". * The intent of this is to allow magic numbers to be checked even though * the object is otherwise opaque. */ #define ISC_MAGIC_VALID(a,b) (((a) != NULL) && \ (((const isc__magic_t *)(a))->magic == (b))) #define ISC_MAGIC(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) /* Common utility functions */ /*% * Extract the network address part from a "struct sockaddr". * \brief * The address family is given explicitly * instead of using src->sa_family, because the latter does not work * for copying a network mask obtained by SIOCGIFNETMASK (it does * not have a valid address family). */ static void get_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src, char *ifname) { struct sockaddr_in6 *sa6; /* clear any remaining value for safety */ memset(dst, 0, sizeof(*dst)); dst->family = family; switch (family) { case AF_INET: memcpy(&dst->type.in, &((struct sockaddr_in *)(void *)src)->sin_addr, sizeof(struct in_addr)); break; case AF_INET6: sa6 = (struct sockaddr_in6 *)(void *)src; memcpy(&dst->type.in6, &sa6->sin6_addr, sizeof(struct in6_addr)); if (sa6->sin6_scope_id != 0) isc_netaddr_setzone(dst, sa6->sin6_scope_id); else { /* * BSD variants embed scope zone IDs in the 128bit * address as a kernel internal form. Unfortunately, * the embedded IDs are not hidden from applications * when getting access to them by sysctl or ioctl. * We convert the internal format to the pure address * part and the zone ID part. * Since multicast addresses should not appear here * and they cannot be distinguished from netmasks, * we only consider unicast link-local addresses. */ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { uint16_t zone16; memcpy(&zone16, &sa6->sin6_addr.s6_addr[2], sizeof(zone16)); zone16 = ntohs(zone16); if (zone16 != 0) { /* the zone ID is embedded */ isc_netaddr_setzone(dst, (uint32_t)zone16); dst->type.in6.s6_addr[2] = 0; dst->type.in6.s6_addr[3] = 0; } else if (ifname != NULL) { unsigned int zone; /* * sin6_scope_id is still not provided, * but the corresponding interface name * is know. Use the interface ID as * the link ID. */ zone = if_nametoindex(ifname); if (zone != 0) { isc_netaddr_setzone(dst, (uint32_t)zone); } } } } break; default: INSIST(0); break; } } /* * Include system-dependent code. */ #ifdef __linux #define ISC_IF_INET6_SZ \ sizeof("00000000000000000000000000000001 01 80 10 80 XXXXXXloXXXXXXXX\n") static isc_result_t linux_if_inet6_next(isc_interfaceiter_t *); static isc_result_t linux_if_inet6_current(isc_interfaceiter_t *); static void linux_if_inet6_first(isc_interfaceiter_t *iter); static bool seenv6 = false; /* IF_NAMESIZE usually defined in net/if.h */ # ifndef IF_NAMESIZE # ifdef IFNAMSIZ # define IF_NAMESIZE IFNAMSIZ # else # define IF_NAMESIZE 16 # endif # endif #endif bool isc_interfaceiter_next_bool(isc_interfaceiter_t *iter) { return (ISC_R_SUCCESS == isc_interfaceiter_next(iter)); } bool isc_interfaceiter_create_bool(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { return (ISC_R_SUCCESS == isc_interfaceiter_create(mctx, iterp)); } bool isc_interfaceiter_first_bool(isc_interfaceiter_t *iter) { return (ISC_R_SUCCESS == isc_interfaceiter_first(iter)); } bool isc_interfaceiter_current_bool(isc_interfaceiter_t *iter, isc_interface_t *ifdata) { return (ISC_R_SUCCESS == isc_interfaceiter_current(iter, ifdata)); } /* * start of the big 3 way switch, in order, try: * * 1. have ifaddrs.h * 2. have sys/sysctl.h * 3. using the SIOCGLIFCONF ioctl. * * question, do we need all three?? */ #if HAVE_IFADDRS_H /* * Obtain the list of network interfaces using the getifaddrs(3) library. */ #include /*% Iterator Magic */ #define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G') /*% Valid Iterator */ #define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) /*% Iterator structure */ struct isc_interfaceiter { unsigned int magic; /*%< Magic number. */ isc_mem_t *mctx; void *buf; /*%< (unused) */ unsigned int bufsize; /*%< (always 0) */ struct ifaddrs *ifaddrs; /*%< List of ifaddrs */ struct ifaddrs *pos; /*%< Ptr to current ifaddr */ isc_interface_t current; /*%< Current interface data. */ isc_result_t result; /*%< Last result code. */ #ifdef __linux FILE * proc; char entry[ISC_IF_INET6_SZ]; isc_result_t valid; #endif }; isc_result_t isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { isc_interfaceiter_t *iter; isc_result_t result; char strbuf[BUFSIZ]; int trys, ret = 0; REQUIRE(mctx != NULL); REQUIRE(iterp != NULL); REQUIRE(*iterp == NULL); iter = emalloc(sizeof(*iter)); if (iter == NULL) return (ISC_R_NOMEMORY); iter->mctx = mctx; iter->buf = NULL; iter->bufsize = 0; iter->ifaddrs = NULL; #ifdef __linux /* * Only open "/proc/net/if_inet6" if we have never seen a IPv6 * address returned by getifaddrs(). */ if (!seenv6) { iter->proc = fopen("/proc/net/if_inet6", "r"); if (iter->proc == NULL) { IGNORE(strerror_r(errno, strbuf, sizeof(strbuf))); /* isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, "failed to open /proc/net/if_inet6"); */ } } else iter->proc = NULL; iter->valid = ISC_R_FAILURE; #endif /* If interrupted, try again */ for (trys = 0; trys < 3; trys++) { if ((ret = getifaddrs(&iter->ifaddrs)) >= 0) break; if (errno != EINTR) break; } if (ret < 0) { IGNORE(strerror_r(errno, strbuf, sizeof(strbuf))); UNEXPECTED_ERROR("getting interface addresses: getifaddrs: %s", strbuf); result = ISC_R_UNEXPECTED; goto failure; } /* * A newly created iterator has an undefined position * until isc_interfaceiter_first() is called. */ iter->pos = NULL; iter->result = ISC_R_FAILURE; iter->magic = IFITER_MAGIC; *iterp = iter; return (ISC_R_SUCCESS); failure: #ifdef __linux if (iter->proc != NULL) fclose(iter->proc); #endif if (iter->ifaddrs != NULL) /* just in case */ freeifaddrs(iter->ifaddrs); free(iter); return (result); } /* * Get information about the current interface to iter->current. * If successful, return ISC_R_SUCCESS. * If the interface has an unsupported address family, * return ISC_R_IGNORE. */ static isc_result_t internal_current(isc_interfaceiter_t *iter) { struct ifaddrs *ifa; int family; unsigned int namelen; REQUIRE(VALID_IFITER(iter)); ifa = iter->pos; #ifdef __linux /* * [Bug 2792] * burnicki: iter->pos is usually never NULL here (anymore?), * so linux_if_inet6_current(iter) is never called here. * However, that routine would check (under Linux), if the * interface is in a tentative state, e.g. if there's no link * yet but an IPv6 address has already be assigned. */ if (iter->pos == NULL) return (linux_if_inet6_current(iter)); #endif INSIST(ifa != NULL); INSIST(ifa->ifa_name != NULL); #ifdef IFF_RUNNING /* * [Bug 2792] * burnicki: if the interface is not running then * it may be in a tentative state. See above. */ if ((ifa->ifa_flags & IFF_RUNNING) == 0) return (ISC_R_IGNORE); #endif if (ifa->ifa_addr == NULL) return (ISC_R_IGNORE); family = ifa->ifa_addr->sa_family; if (family != AF_INET && family != AF_INET6) return (ISC_R_IGNORE); #ifdef __linux if (family == AF_INET6) seenv6 = true; #endif memset(&iter->current, 0, sizeof(iter->current)); namelen = (unsigned int)strlen(ifa->ifa_name); if (namelen > sizeof(iter->current.name) - 1) namelen = sizeof(iter->current.name) - 1; memset(iter->current.name, 0, sizeof(iter->current.name)); memcpy(iter->current.name, ifa->ifa_name, namelen); iter->current.flags = 0; if ((ifa->ifa_flags & IFF_UP) != 0) iter->current.flags |= INTERFACE_F_UP; if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) iter->current.flags |= INTERFACE_F_POINTTOPOINT; if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) iter->current.flags |= INTERFACE_F_LOOPBACK; if ((ifa->ifa_flags & IFF_BROADCAST) != 0) iter->current.flags |= INTERFACE_F_BROADCAST; #ifdef IFF_MULTICAST if ((ifa->ifa_flags & IFF_MULTICAST) != 0) iter->current.flags |= INTERFACE_F_MULTICAST; #endif iter->current.af = (unsigned int)family; get_addr((unsigned int)family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); if (ifa->ifa_netmask != NULL) get_addr((unsigned int)family, &iter->current.netmask, ifa->ifa_netmask, ifa->ifa_name); if (ifa->ifa_dstaddr != NULL && (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) get_addr((unsigned int)family, &iter->current.dstaddress, ifa->ifa_dstaddr, ifa->ifa_name); if (ifa->ifa_broadaddr != NULL && (iter->current.flags & INTERFACE_F_BROADCAST) != 0) get_addr((unsigned int)family, &iter->current.broadcast, ifa->ifa_broadaddr, ifa->ifa_name); iter->current.ifindex = if_nametoindex(iter->current.name); return (ISC_R_SUCCESS); } /* * Step the iterator to the next interface. Unlike * isc_interfaceiter_next(), this may leave the iterator * positioned on an interface that will ultimately * be ignored. Return ISC_R_NOMORE if there are no more * interfaces, otherwise ISC_R_SUCCESS. */ static isc_result_t internal_next(isc_interfaceiter_t *iter) { if (iter->pos != NULL) iter->pos = iter->pos->ifa_next; if (iter->pos == NULL) { #ifdef __linux if (!seenv6) return (linux_if_inet6_next(iter)); #endif return (ISC_R_NOMORE); } return (ISC_R_SUCCESS); } static void internal_destroy(isc_interfaceiter_t *iter) { #ifdef __linux if (iter->proc != NULL) fclose(iter->proc); iter->proc = NULL; #endif if (iter->ifaddrs) freeifaddrs(iter->ifaddrs); iter->ifaddrs = NULL; } static void internal_first(isc_interfaceiter_t *iter) { #ifdef __linux linux_if_inet6_first(iter); #endif iter->pos = iter->ifaddrs; } /* end HAVE_IFADDRS_H */ #elif defined(HAVE_IFLIST_SYSCTL) && HAVE_IFLIST_SYSCTL /* * Obtain the list of network interfaces using sysctl. * See TCP/IP Illustrated Volume 2, sections 19.8, 19.14, * and 19.16. */ #include #include #include #include #define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ : sizeof(long)) #define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'S') #define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) struct isc_interfaceiter { unsigned int magic; /* Magic number. */ isc_mem_t *mctx; void *buf; /* Buffer for sysctl data. */ unsigned int bufsize; /* Bytes allocated. */ unsigned int bufused; /* Bytes used. */ unsigned int pos; /* Current offset in sysctl data. */ isc_interface_t current; /* Current interface data. */ isc_result_t result; /* Last result code. */ }; static int mib[6] = { CTL_NET, PF_ROUTE, 0, 0, /* Any address family. */ NET_RT_IFLIST, 0 /* Flags. */ }; isc_result_t isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { isc_interfaceiter_t *iter; isc_result_t result; size_t bufsize; size_t bufused; char strbuf[BUFSIZ]; REQUIRE(mctx != NULL); REQUIRE(iterp != NULL); REQUIRE(*iterp == NULL); iter = emalloc(sizeof(*iter)); if (iter == NULL) return (ISC_R_NOMEMORY); iter->mctx = mctx; iter->buf = 0; /* * Determine the amount of memory needed. */ bufsize = 0; if (sysctl(mib, 6, NULL, &bufsize, NULL, (size_t) 0) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("getting interface list size: sysctl: %s", strbuf); result = ISC_R_UNEXPECTED; goto failure; } iter->bufsize = bufsize; iter->buf = emalloc(iter->bufsize); if (iter->buf == NULL) { result = ISC_R_NOMEMORY; goto failure; } bufused = bufsize; if (sysctl(mib, 6, iter->buf, &bufused, NULL, (size_t) 0) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("getting interface list: sysctl: %s", strbuf); result = ISC_R_UNEXPECTED; goto failure; } iter->bufused = bufused; INSIST(iter->bufused <= iter->bufsize); /* * A newly created iterator has an undefined position * until isc_interfaceiter_first() is called. */ iter->pos = (unsigned int) -1; iter->result = ISC_R_FAILURE; iter->magic = IFITER_MAGIC; *iterp = iter; return (ISC_R_SUCCESS); failure: if (iter->buf != NULL) free(iter->buf); free(iter); return (result); } /* * Get information about the current interface to iter->current. * If successful, return ISC_R_SUCCESS. * If the interface has an unsupported address family, * return ISC_R_IGNORE. In case of other failure, * return ISC_R_UNEXPECTED. */ static isc_result_t internal_current(isc_interfaceiter_t *iter) { struct ifa_msghdr *ifam, *ifam_end; REQUIRE(VALID_IFITER(iter)); REQUIRE (iter->pos < (unsigned int) iter->bufused); ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused); // Skip wrong RTM version headers if (ifam->ifam_version != RTM_VERSION) return (ISC_R_IGNORE); if (ifam->ifam_type == RTM_IFINFO) { struct if_msghdr *ifm = (struct if_msghdr *) ifam; struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1); unsigned int namelen; memset(&iter->current, 0, sizeof(iter->current)); iter->current.ifindex = sdl->sdl_index; namelen = sdl->sdl_nlen; if (namelen > sizeof(iter->current.name) - 1) namelen = sizeof(iter->current.name) - 1; memset(iter->current.name, 0, sizeof(iter->current.name)); memcpy(iter->current.name, sdl->sdl_data, namelen); iter->current.flags = 0; if ((ifam->ifam_flags & IFF_UP) != 0) iter->current.flags |= INTERFACE_F_UP; if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0) iter->current.flags |= INTERFACE_F_POINTTOPOINT; if ((ifam->ifam_flags & IFF_LOOPBACK) != 0) iter->current.flags |= INTERFACE_F_LOOPBACK; if ((ifam->ifam_flags & IFF_BROADCAST) != 0) iter->current.flags |= INTERFACE_F_BROADCAST; #ifdef IFF_MULTICAST if ((ifam->ifam_flags & IFF_MULTICAST) != 0) iter->current.flags |= INTERFACE_F_MULTICAST; #endif /* * This is not an interface address. * Force another iteration. */ return (ISC_R_IGNORE); } else if (ifam->ifam_type == RTM_NEWADDR) { int i; int family; struct sockaddr *mask_sa = NULL; struct sockaddr *addr_sa = NULL; struct sockaddr *dst_sa = NULL; struct sockaddr *sa = (struct sockaddr *)(ifam + 1); family = sa->sa_family; for (i = 0; i < RTAX_MAX; i++) { if ((ifam->ifam_addrs & (1 << i)) == 0) continue; INSIST(sa < (struct sockaddr *) ifam_end); switch (i) { case RTAX_NETMASK: /* Netmask */ mask_sa = sa; break; case RTAX_IFA: /* Interface address */ addr_sa = sa; break; case RTAX_BRD: /* Broadcast or destination address */ dst_sa = sa; break; } #ifdef ISC_PLATFORM_HAVESALEN sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(sa->sa_len)); #else /* XXX untested. */ sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(sizeof(struct sockaddr))); #endif } if (addr_sa == NULL) return (ISC_R_IGNORE); family = addr_sa->sa_family; if (family != AF_INET && family != AF_INET6) return (ISC_R_IGNORE); iter->current.af = family; get_addr(family, &iter->current.address, addr_sa, iter->current.name); if (mask_sa != NULL) get_addr(family, &iter->current.netmask, mask_sa, iter->current.name); if (dst_sa != NULL && (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) get_addr(family, &iter->current.dstaddress, dst_sa, iter->current.name); if (dst_sa != NULL && (iter->current.flags & INTERFACE_F_BROADCAST) != 0) get_addr(family, &iter->current.broadcast, dst_sa, iter->current.name); return (ISC_R_SUCCESS); } else { printf("warning: unexpected interface list message type\n"); return (ISC_R_IGNORE); } } /* * Step the iterator to the next interface. Unlike * isc_interfaceiter_next(), this may leave the iterator * positioned on an interface that will ultimately * be ignored. Return ISC_R_NOMORE if there are no more * interfaces, otherwise ISC_R_SUCCESS. */ static isc_result_t internal_next(isc_interfaceiter_t *iter) { struct ifa_msghdr *ifam; REQUIRE (iter->pos < (unsigned int) iter->bufused); ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); iter->pos += ifam->ifam_msglen; if (iter->pos >= iter->bufused) return (ISC_R_NOMORE); return (ISC_R_SUCCESS); } static void internal_destroy(isc_interfaceiter_t *iter) { UNUSED_ARG(iter); /* Unused. */ /* * Do nothing. */ } static void internal_first(isc_interfaceiter_t *iter) { iter->pos = 0; } /* end defined(HAVE_IFLIST_SYSCTL) && HAVE_IFLIST_SYSCTL */ #else /* * Obtain the list of network interfaces using the SIOCGLIFCONF ioctl. * See netintro(4). */ #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) #ifdef HAVE_STRUCT_IF_LADDRCONF #define lifc_len iflc_len #define lifc_buf iflc_buf #define lifc_req iflc_req #define LIFCONF if_laddrconf #else #define USE_LIFC_FAMILY 1 #define USE_LIFC_FLAGS 1 #define LIFCONF lifconf #endif #ifdef HAVE_STRUCT_IF_LADDRREQ #define lifr_addr iflr_addr #define lifr_name iflr_name #define lifr_dstaddr iflr_dstaddr #define lifr_broadaddr iflr_broadaddr #define lifr_flags iflr_flags #define lifr_index iflr_index #define ss_family sa_family #define LIFREQ if_laddrreq #else #define LIFREQ lifreq #endif #endif #define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'T') #define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) struct isc_interfaceiter { unsigned int magic; /* Magic number. */ isc_mem_t *mctx; int mode; int socket; struct ifconf ifc; void *buf; /* Buffer for sysctl data. */ unsigned int bufsize; /* Bytes allocated. */ unsigned int pos; /* Current offset in SIOCGIFCONF data */ #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) int socket6; struct LIFCONF lifc; void *buf6; /* Buffer for sysctl data. */ unsigned int bufsize6; /* Bytes allocated. */ unsigned int pos6; /* Current offset in SIOCGLIFCONF data */ isc_result_t result6; /* Last result code. */ bool first6; #endif #ifdef __linux FILE * proc; char entry[ISC_IF_INET6_SZ]; isc_result_t valid; #endif isc_interface_t current; /* Current interface data. */ isc_result_t result; /* Last result code. */ }; /*% * Size of buffer for SIOCGLIFCONF, in bytes. We assume no sane system * will have more than a megabyte of interface configuration data. */ #define IFCONF_BUFSIZE_INITIAL 4096 #define IFCONF_BUFSIZE_MAX 1048576 static int isc_ioctl(int fildes, int req, char *arg); static int isc_ioctl(int fildes, int req, char *arg) { int trys; int ret; for (trys = 0; trys < 3; trys++) { if ((ret = ioctl(fildes, req, arg)) < 0) { if (errno == EINTR) continue; } break; } return (ret); } static isc_result_t getbuf4(isc_interfaceiter_t *iter) { char strbuf[BUFSIZ]; iter->bufsize = IFCONF_BUFSIZE_INITIAL; for (;;) { iter->buf = emalloc(iter->bufsize); if (iter->buf == NULL) return (ISC_R_NOMEMORY); memset(&iter->ifc.ifc_len, 0, sizeof(iter->ifc.ifc_len)); iter->ifc.ifc_len = iter->bufsize; iter->ifc.ifc_buf = iter->buf; /* * Ignore the HP/UX warning about "integer overflow during * conversion". It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(iter->socket, SIOCGIFCONF, (char *)&iter->ifc) == -1) { if (errno != EINVAL) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("get interface " "configuration: %s", strbuf); goto unexpected; } /* * EINVAL. Retry with a bigger buffer. */ } else { /* * The ioctl succeeded. * Some OS's just return what will fit rather * than set EINVAL if the buffer is too small * to fit all the interfaces in. If * ifc.lifc_len is too near to the end of the * buffer we will grow it just in case and * retry. */ if (iter->ifc.ifc_len + 2 * sizeof(struct ifreq) < iter->bufsize) break; } if (iter->bufsize >= IFCONF_BUFSIZE_MAX) { UNEXPECTED_ERROR("get interface configuration: " "maximum buffer size exceeded"); goto unexpected; } free(iter->buf); iter->bufsize *= 2; } return (ISC_R_SUCCESS); unexpected: free(iter->buf); iter->buf = NULL; return (ISC_R_UNEXPECTED); } #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) static isc_result_t getbuf6(isc_interfaceiter_t *iter) { char strbuf[BUFSIZ]; isc_result_t result; iter->bufsize6 = IFCONF_BUFSIZE_INITIAL; for (;;) { iter->buf6 = emalloc(iter->bufsize6); if (iter->buf6 == NULL) return (ISC_R_NOMEMORY); memset(&iter->lifc, 0, sizeof(iter->lifc)); #ifdef USE_LIFC_FAMILY iter->lifc.lifc_family = AF_INET6; #endif #ifdef USE_LIFC_FLAGS iter->lifc.lifc_flags = 0; #endif iter->lifc.lifc_len = iter->bufsize6; iter->lifc.lifc_buf = iter->buf6; /* * Ignore the HP/UX warning about "integer overflow during * conversion". It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(iter->socket6, SIOCGLIFCONF, (char *)&iter->lifc) == -1) { if (errno != EINVAL) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("get interface " "configuration: %s", strbuf); result = ISC_R_UNEXPECTED; goto cleanup; } /* * EINVAL. Retry with a bigger buffer. */ } else { /* * The ioctl succeeded. * Some OS's just return what will fit rather * than set EINVAL if the buffer is too small * to fit all the interfaces in. If * ifc.ifc_len is too near to the end of the * buffer we will grow it just in case and * retry. */ if (iter->lifc.lifc_len + 2 * sizeof(struct LIFREQ) < iter->bufsize6) break; } if (iter->bufsize6 >= IFCONF_BUFSIZE_MAX) { UNEXPECTED_ERROR("get interface configuration: " "maximum buffer size exceeded"); result = ISC_R_UNEXPECTED; goto cleanup; } free(iter->buf6); iter->bufsize6 *= 2; } if (iter->lifc.lifc_len != 0) iter->mode = 6; return (ISC_R_SUCCESS); cleanup: free(iter->buf6); iter->buf6 = NULL; return (result); } #endif isc_result_t isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { isc_interfaceiter_t *iter; isc_result_t result; char strbuf[BUFSIZ]; REQUIRE(mctx != NULL); REQUIRE(iterp != NULL); REQUIRE(*iterp == NULL); iter = emalloc(sizeof(*iter)); if (iter == NULL) return (ISC_R_NOMEMORY); iter->mctx = mctx; iter->mode = 4; iter->buf = NULL; iter->pos = (unsigned int) -1; #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) iter->buf6 = NULL; iter->pos6 = (unsigned int) -1; iter->result6 = ISC_R_NOMORE; iter->socket6 = -1; iter->first6 = false; #endif /* * Get the interface configuration, allocating more memory if * necessary. */ #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) result = isc_net_probeipv6(); if (result == ISC_R_SUCCESS) { /* * Create an unbound datagram socket to do the SIOCGLIFCONF * ioctl on. HP/UX requires an AF_INET6 socket for * SIOCGLIFCONF to get IPv6 addresses. */ if ((iter->socket6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("making interface scan socket: %s", strbuf); result = ISC_R_UNEXPECTED; goto socket6_failure; } result = iter->result6 = getbuf6(iter); if (result != ISC_R_NOTIMPLEMENTED && result != ISC_R_SUCCESS) goto ioctl6_failure; } #endif if ((iter->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("making interface scan socket: %s", strbuf); result = ISC_R_UNEXPECTED; goto socket_failure; } result = getbuf4(iter); if (result != ISC_R_SUCCESS) goto ioctl_failure; /* * A newly created iterator has an undefined position * until isc_interfaceiter_first() is called. */ #ifdef __linux iter->proc = fopen("/proc/net/if_inet6", "r"); iter->valid = ISC_R_FAILURE; #endif iter->result = ISC_R_FAILURE; iter->magic = IFITER_MAGIC; *iterp = iter; return (ISC_R_SUCCESS); ioctl_failure: if (iter->buf != NULL) free(iter->buf); (void) close(iter->socket); socket_failure: #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) if (iter->buf6 != NULL) free(iter->buf6); ioctl6_failure: if (iter->socket6 != -1) (void) close(iter->socket6); socket6_failure: #endif free(iter); return (result); } /* * Get information about the current interface to iter->current. * If successful, return ISC_R_SUCCESS. * If the interface has an unsupported address family, or if * some operation on it fails, return ISC_R_IGNORE to make * the higher-level iterator code ignore it. */ static isc_result_t internal_current4(isc_interfaceiter_t *iter) { struct ifreq *ifrp; struct ifreq ifreq; int family; char strbuf[BUFSIZ]; #if !defined(HAVE_STRUCT_IF_LADDRREQ) && defined(SIOCGLIFADDR) struct lifreq lifreq; #else char sabuf[256]; #endif int i, bits, prefixlen; REQUIRE(VALID_IFITER(iter)); if (iter->ifc.ifc_len == 0 || iter->pos == (unsigned int)iter->ifc.ifc_len) { #ifdef __linux return (linux_if_inet6_current(iter)); #else return (ISC_R_NOMORE); #endif } INSIST( iter->pos < (unsigned int) iter->ifc.ifc_len); ifrp = (void *)((char *) iter->ifc.ifc_req + iter->pos); memset(&ifreq, 0, sizeof(ifreq)); memcpy(&ifreq, ifrp, sizeof(ifreq)); family = ifreq.ifr_addr.sa_family; if (family != AF_INET && family != AF_INET6) return (ISC_R_IGNORE); memset(&iter->current, 0, sizeof(iter->current)); iter->current.af = family; INSIST(sizeof(ifreq.ifr_name) <= sizeof(iter->current.name)); memset(iter->current.name, 0, sizeof(iter->current.name)); memcpy(iter->current.name, ifreq.ifr_name, sizeof(ifreq.ifr_name)); get_addr(family, &iter->current.address, (struct sockaddr *)&ifrp->ifr_addr, ifreq.ifr_name); /* * If the interface does not have a address ignore it. */ switch (family) { case AF_INET: if (iter->current.address.type.in.s_addr == htonl(INADDR_ANY)) return (ISC_R_IGNORE); break; case AF_INET6: if (memcmp(&iter->current.address.type.in6, &in6addr_any, sizeof(in6addr_any)) == 0) return (ISC_R_IGNORE); break; } /* * Get interface flags. */ iter->current.flags = 0; /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(iter->socket, SIOCGIFFLAGS, (char *) &ifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting interface flags: %s", ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } if ((ifreq.ifr_flags & IFF_UP) != 0) iter->current.flags |= INTERFACE_F_UP; #ifdef IFF_POINTOPOINT if ((ifreq.ifr_flags & IFF_POINTOPOINT) != 0) iter->current.flags |= INTERFACE_F_POINTTOPOINT; #endif if ((ifreq.ifr_flags & IFF_LOOPBACK) != 0) iter->current.flags |= INTERFACE_F_LOOPBACK; if ((ifreq.ifr_flags & IFF_BROADCAST) != 0) iter->current.flags |= INTERFACE_F_BROADCAST; #ifdef IFF_MULTICAST if ((ifreq.ifr_flags & IFF_MULTICAST) != 0) iter->current.flags |= INTERFACE_F_MULTICAST; #endif if (family == AF_INET) goto inet; memset(&lifreq, 0, sizeof(lifreq)); memcpy(lifreq.lifr_name, iter->current.name, sizeof(lifreq.lifr_name)); memcpy(&lifreq.lifr_addr, &iter->current.address.type.in6, sizeof(iter->current.address.type.in6)); if (isc_ioctl(iter->socket, SIOCGLIFADDR, &lifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting interface address: %s", ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } prefixlen = lifreq.lifr_addrlen; /* * Netmask already zeroed. */ iter->current.netmask.family = family; for (i = 0; i < 16; i++) { if (prefixlen > 8) { bits = 0; prefixlen -= 8; } else { bits = 8 - prefixlen; prefixlen = 0; } iter->current.netmask.type.in6.s6_addr[i] = (~0 << bits) & 0xff; } iter->current.ifindex = if_nametoindex(iter->current.name); return (ISC_R_SUCCESS); inet: if (family != AF_INET) return (ISC_R_IGNORE); #ifdef IFF_POINTOPOINT /* * If the interface is point-to-point, get the destination address. */ if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(iter->socket, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting destination address: %s", ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.dstaddress, (struct sockaddr *)&ifreq.ifr_dstaddr, ifreq.ifr_name); } #endif if ((iter->current.flags & INTERFACE_F_BROADCAST) != 0) { /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(iter->socket, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting broadcast address: %s", ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.broadcast, (struct sockaddr *)&ifreq.ifr_broadaddr, ifreq.ifr_name); } /* * Get the network mask. */ memset(&ifreq, 0, sizeof(ifreq)); memcpy(&ifreq, ifrp, sizeof(ifreq)); /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(iter->socket, SIOCGIFNETMASK, (char *)&ifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting netmask: %s", ifreq.ifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.netmask, (struct sockaddr *)&ifreq.ifr_addr, ifreq.ifr_name); iter->current.ifindex = if_nametoindex(iter->current.name); return (ISC_R_SUCCESS); } #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) static isc_result_t internal_current6(isc_interfaceiter_t *iter) { struct LIFREQ *ifrp; struct LIFREQ lifreq; int family; char strbuf[BUFSIZ]; int fd; REQUIRE(VALID_IFITER(iter)); if (iter->result6 != ISC_R_SUCCESS) return (iter->result6); REQUIRE(iter->pos6 < (unsigned int) iter->lifc.lifc_len); ifrp = (void *)((char *)iter->lifc.lifc_req + iter->pos6); memset(&lifreq, 0, sizeof(lifreq)); memcpy(&lifreq, ifrp, sizeof(lifreq)); family = lifreq.lifr_addr.ss_family; if (family != AF_INET && family != AF_INET6) return (ISC_R_IGNORE); memset(&iter->current, 0, sizeof(iter->current)); iter->current.af = family; INSIST(sizeof(lifreq.lifr_name) <= sizeof(iter->current.name)); memset(iter->current.name, 0, sizeof(iter->current.name)); memcpy(iter->current.name, lifreq.lifr_name, sizeof(lifreq.lifr_name)); get_addr(family, &iter->current.address, (struct sockaddr *)&lifreq.lifr_addr, lifreq.lifr_name); if (isc_netaddr_islinklocal(&iter->current.address)) isc_netaddr_setzone(&iter->current.address, (uint32_t)lifreq.lifr_index); /* * If the interface does not have a address ignore it. */ switch (family) { case AF_INET: if (iter->current.address.type.in.s_addr == htonl(INADDR_ANY)) return (ISC_R_IGNORE); break; case AF_INET6: if (memcmp(&iter->current.address.type.in6, &in6addr_any, sizeof(in6addr_any)) == 0) return (ISC_R_IGNORE); break; } /* * Get interface flags. */ iter->current.flags = 0; if (family == AF_INET6) fd = iter->socket6; else fd = iter->socket; /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(fd, SIOCGLIFFLAGS, (char *) &lifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting interface flags: %s", lifreq.lifr_name, strbuf); return (ISC_R_IGNORE); } if ((lifreq.lifr_flags & IFF_UP) != 0) iter->current.flags |= INTERFACE_F_UP; #ifdef IFF_POINTOPOINT if ((lifreq.lifr_flags & IFF_POINTOPOINT) != 0) iter->current.flags |= INTERFACE_F_POINTTOPOINT; #endif if ((lifreq.lifr_flags & IFF_LOOPBACK) != 0) iter->current.flags |= INTERFACE_F_LOOPBACK; if ((lifreq.lifr_flags & IFF_BROADCAST) != 0) { iter->current.flags |= INTERFACE_F_BROADCAST; } #ifdef IFF_MULTICAST if ((lifreq.lifr_flags & IFF_MULTICAST) != 0) { iter->current.flags |= INTERFACE_F_MULTICAST; } #endif #ifdef IFF_POINTOPOINT /* * If the interface is point-to-point, get the destination address. */ if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(fd, SIOCGLIFDSTADDR, (char *)&lifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting destination address: %s", lifreq.lifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.dstaddress, (struct sockaddr *)&lifreq.lifr_dstaddr, lifreq.lifr_name); } #endif #ifdef SIOCGLIFBRDADDR if ((iter->current.flags & INTERFACE_F_BROADCAST) != 0) { /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(iter->socket, SIOCGLIFBRDADDR, (char *)&lifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting broadcast address: %s", lifreq.lifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.broadcast, (struct sockaddr *)&lifreq.lifr_broadaddr, lifreq.lifr_name); } #endif /* SIOCGLIFBRDADDR */ /* * Get the network mask. Netmask already zeroed. */ memset(&lifreq, 0, sizeof(lifreq)); memcpy(&lifreq, ifrp, sizeof(lifreq)); #ifdef lifr_addrlen /* * Special case: if the system provides lifr_addrlen member, the * netmask of an IPv6 address can be derived from the length, since * an IPv6 address always has a contiguous mask. */ if (family == AF_INET6) { int i, bits; iter->current.netmask.family = family; for (i = 0; i < lifreq.lifr_addrlen; i += 8) { bits = lifreq.lifr_addrlen - i; bits = (bits < 8) ? (8 - bits) : 0; iter->current.netmask.type.in6.s6_addr[i / 8] = (~0 << bits) & 0xff; } iter->current.ifindex = if_nametoindex(iter->current.name); return (ISC_R_SUCCESS); } #endif /* * Ignore the HP/UX warning about "integer overflow during * conversion. It comes from its own macro definition, * and is really hard to shut up. */ if (isc_ioctl(fd, SIOCGLIFNETMASK, (char *)&lifreq) < 0) { strerror_r(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR("%s: getting netmask: %s", lifreq.lifr_name, strbuf); return (ISC_R_IGNORE); } get_addr(family, &iter->current.netmask, (struct sockaddr *)&lifreq.lifr_addr, lifreq.lifr_name); iter->current.ifindex = if_nametoindex(iter->current.name); return (ISC_R_SUCCESS); } #endif static isc_result_t internal_current(isc_interfaceiter_t *iter) { #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) if (iter->mode == 6) { iter->result6 = internal_current6(iter); if (iter->result6 != ISC_R_NOMORE) return (iter->result6); } #endif return (internal_current4(iter)); } /* * Step the iterator to the next interface. Unlike * isc_interfaceiter_next(), this may leave the iterator * positioned on an interface that will ultimately * be ignored. Return ISC_R_NOMORE if there are no more * interfaces, otherwise ISC_R_SUCCESS. */ static isc_result_t internal_next4(isc_interfaceiter_t *iter) { #ifdef ISC_PLATFORM_HAVESALEN struct ifreq *ifrp; #endif if (iter->pos < (unsigned int) iter->ifc.ifc_len) { #ifdef ISC_PLATFORM_HAVESALEN ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); if (ifrp->ifr_addr.sa_len > sizeof(struct sockaddr)) iter->pos += sizeof(ifrp->ifr_name) + ifrp->ifr_addr.sa_len; else #endif iter->pos += sizeof(struct ifreq); } else { INSIST(iter->pos == (unsigned int) iter->ifc.ifc_len); #ifdef __linux return (linux_if_inet6_next(iter)); #else return (ISC_R_NOMORE); #endif } return (ISC_R_SUCCESS); } #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) static isc_result_t internal_next6(isc_interfaceiter_t *iter) { #ifdef ISC_PLATFORM_HAVESALEN struct LIFREQ *ifrp; #endif if (iter->result6 != ISC_R_SUCCESS && iter->result6 != ISC_R_IGNORE) return (iter->result6); REQUIRE(iter->pos6 < (unsigned int) iter->lifc.lifc_len); #ifdef ISC_PLATFORM_HAVESALEN ifrp = (struct LIFREQ *)((char *) iter->lifc.lifc_req + iter->pos6); if (ifrp->lifr_addr.sa_len > sizeof(struct sockaddr)) iter->pos6 += sizeof(ifrp->lifr_name) + ifrp->lifr_addr.sa_len; else #endif iter->pos6 += sizeof(struct LIFREQ); if (iter->pos6 >= (unsigned int) iter->lifc.lifc_len) return (ISC_R_NOMORE); return (ISC_R_SUCCESS); } #endif static isc_result_t internal_next(isc_interfaceiter_t *iter) { #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) if (iter->mode == 6) { iter->result6 = internal_next6(iter); if (iter->result6 != ISC_R_NOMORE) return (iter->result6); if (iter->first6) { iter->first6 = false; return (ISC_R_SUCCESS); } } #endif return (internal_next4(iter)); } static void internal_destroy(isc_interfaceiter_t *iter) { (void) close(iter->socket); #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) if (iter->socket6 != -1) (void) close(iter->socket6); if (iter->buf6 != NULL) { free(iter->buf6); } #endif #ifdef __linux if (iter->proc != NULL) fclose(iter->proc); #endif } static void internal_first(isc_interfaceiter_t *iter) { iter->pos = 0; #if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) iter->pos6 = 0; if (iter->result6 == ISC_R_NOMORE) iter->result6 = ISC_R_SUCCESS; iter->first6 = true; #endif #ifdef __linux linux_if_inet6_first(iter); #endif } /* end of "ifiter_ioctl" */ #endif /* end of the big 3 way switch */ #ifdef __linux static void isc_netaddr_fromin6(isc_netaddr_t *netaddr, const struct in6_addr *ina6); static void isc_netaddr_fromin6(isc_netaddr_t *netaddr, const struct in6_addr *ina6) { memset(netaddr, 0, sizeof(*netaddr)); netaddr->family = AF_INET6; netaddr->type.in6 = *ina6; } /* * Returns #true if the address is a link local address. */ static bool isc_netaddr_islinklocal(isc_netaddr_t *na) __attribute__((pure)); static bool isc_netaddr_islinklocal(isc_netaddr_t *na) { switch (na->family) { case AF_INET: return (false); case AF_INET6: return (ISC_TF(IN6_IS_ADDR_LINKLOCAL(&na->type.in6))); default: return (false); } } static void linux_if_inet6_first(isc_interfaceiter_t *iter) { if (iter->proc != NULL) { rewind(iter->proc); (void)linux_if_inet6_next(iter); } else iter->valid = ISC_R_NOMORE; } static isc_result_t linux_if_inet6_next(isc_interfaceiter_t *iter) { if (iter->proc != NULL && fgets(iter->entry, sizeof(iter->entry), iter->proc) != NULL) iter->valid = ISC_R_SUCCESS; else iter->valid = ISC_R_NOMORE; return (iter->valid); } static isc_result_t linux_if_inet6_current(isc_interfaceiter_t *iter) { char address[33]; char name[IF_NAMESIZE+1]; struct in6_addr addr6; unsigned int ifindex; int prefix, scope, flags; int res; unsigned int i; if (iter->valid != ISC_R_SUCCESS) return (iter->valid); if (iter->proc == NULL) { /* isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR, "/proc/net/if_inet6:iter->proc == NULL"); */ return (ISC_R_FAILURE); } res = sscanf(iter->entry, "%32[a-f0-9] %x %x %x %x %16s\n", address, &ifindex, (unsigned int *)&prefix, (unsigned int *) &scope, (unsigned int *)&flags, name); if (res != 6) { /* isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR, "/proc/net/if_inet6:sscanf() -> %d (expected 6)", res); */ return (ISC_R_FAILURE); } if (strlen(address) != 32) { /* isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR, "/proc/net/if_inet6:strlen(%s) != 32", address); */ return (ISC_R_FAILURE); } /* ** Ignore DAD addresses -- ** we can't bind to them until they are resolved */ #ifdef IFA_F_TENTATIVE if (flags & IFA_F_TENTATIVE) return (ISC_R_IGNORE); #endif for (i = 0; i < 16; i++) { unsigned char byte; static const char hex[] = "0123456789abcdef"; byte = ((strchr(hex, address[i * 2]) - hex) << 4) | (strchr(hex, address[i * 2 + 1]) - hex); addr6.s6_addr[i] = byte; } iter->current.af = AF_INET6; iter->current.flags = INTERFACE_F_UP; isc_netaddr_fromin6(&iter->current.address, &addr6); iter->current.ifindex = ifindex; if (isc_netaddr_islinklocal(&iter->current.address)) { isc_netaddr_setzone(&iter->current.address, (uint32_t)ifindex); } for (i = 0; i < 16; i++) { if (prefix > 8) { addr6.s6_addr[i] = 0xff; prefix -= 8; } else { addr6.s6_addr[i] = (0xff << (8 - prefix)) & 0xff; prefix = 0; } } isc_netaddr_fromin6(&iter->current.netmask, &addr6); strlcpy(iter->current.name, name, sizeof(iter->current.name)); return (ISC_R_SUCCESS); } #endif /* * The remaining code is common to the sysctl and ioctl case. */ isc_result_t isc_interfaceiter_current(isc_interfaceiter_t *iter, isc_interface_t *ifdata) { REQUIRE(iter->result == ISC_R_SUCCESS); memcpy(ifdata, &iter->current, sizeof(*ifdata)); return (ISC_R_SUCCESS); } isc_result_t isc_interfaceiter_first(isc_interfaceiter_t *iter) { isc_result_t result; REQUIRE(VALID_IFITER(iter)); internal_first(iter); for (;;) { result = internal_current(iter); if (result != ISC_R_IGNORE) break; result = internal_next(iter); if (result != ISC_R_SUCCESS) break; } iter->result = result; return (result); } isc_result_t isc_interfaceiter_next(isc_interfaceiter_t *iter) { isc_result_t result; REQUIRE(VALID_IFITER(iter)); REQUIRE(iter->result == ISC_R_SUCCESS); for (;;) { result = internal_next(iter); if (result != ISC_R_SUCCESS) break; result = internal_current(iter); if (result != ISC_R_IGNORE) break; } iter->result = result; return (result); } void isc_interfaceiter_destroy(isc_interfaceiter_t **iterp) { isc_interfaceiter_t *iter; REQUIRE(iterp != NULL); iter = *iterp; REQUIRE(VALID_IFITER(iter)); internal_destroy(iter); if (iter->buf != NULL) free(iter->buf); iter->magic = 0; free(iter); *iterp = NULL; } ntpsec-1.1.0+dfsg1/libntp/systime.c0000644000175000017500000002753013252364117016765 0ustar rlaagerrlaager/* * systime -- routines to fiddle a UNIX clock. * * ATTENTION: Get approval from Dave Mills on all changes to this file! * */ #include "config.h" #include "ntp.h" #include "ntp_syslog.h" #include "ntp_stdlib.h" #include "timespecops.h" #include "ntp_calendar.h" #ifndef USE_COMPILETIME_PIVOT # define USE_COMPILETIME_PIVOT 1 #endif /* * These routines (get_systime, step_systime, adj_systime) implement an * interface between the system independent NTP clock and the Unix * system clock in various architectures and operating systems. Time is * a precious quantity in these routines and every effort is made to * minimize errors by unbiased rounding and amortizing adjustment * residues. * * In order to improve the apparent resolution, provide unbiased * rounding and most importantly ensure that the readings cannot be * predicted, the low-order unused portion of the time below the minimum * time to read the clock is filled with an unbiased random fuzz. * * The sys_tick variable specifies the system clock tick interval in * seconds, for stepping clocks, defined as those which return times * less than MINSTEP greater than the previous reading. For systems that * use a high-resolution counter such that each clock reading is always * at least MINSTEP greater than the prior, sys_tick is the time to read * the system clock. * * The sys_fuzz variable measures the minimum time to read the system * clock, regardless of its precision. When reading the system clock * using get_systime() after sys_tick and sys_fuzz have been determined, * ntpd ensures each unprocessed clock reading is no less than sys_fuzz * later than the prior unprocessed reading, and then fuzzes the bits * below sys_fuzz in the timestamp returned, ensuring each of its * resulting readings is strictly later than the previous. * * When slewing the system clock using adj_systime() (with the kernel * loop discipline unavailable or disabled), adjtime() offsets are * quantized to sys_tick, if sys_tick is greater than sys_fuzz, which * is to say if the OS presents a stepping clock. Otherwise, offsets * are quantized to the microsecond resolution of adjtime()'s timeval * input. The remaining correction sys_residual is carried into the * next adjtime() and meanwhile is also factored into get_systime() * readings. * * adj_systime() and step_systime() will behave sanely with these * variables not set, but the adjustments may be in larger steps. */ double sys_tick = 0; /* tick size or time to read (s) */ double sys_fuzz = 0; /* min. time to read the clock (s) */ bool trunc_os_clock; /* sys_tick > measured_tick */ time_stepped_callback step_callback; static doubletime_t sys_residual = 0; /* adjustment residue (s) */ static long sys_fuzz_nsec = 0; /* minimum time to read clock (ns) */ /* perlinger@ntp.org: As 'get_systime()' does its own check for clock * backstepping, this could probably become a local variable in * 'get_systime()' and the cruft associated with communicating via a * static value could be removed after the v4.2.8 release. */ static bool lamport_violated; /* clock was stepped back */ static void get_ostime (struct timespec *tsp); void set_sys_fuzz( double fuzz_val ) { sys_fuzz = fuzz_val; //INSIST(sys_fuzz >= 0); //INSIST(sys_fuzz <= 1.0); sys_fuzz_nsec = (long)(sys_fuzz * NS_PER_S + 0.5); } static void get_ostime( struct timespec * tsp ) { int rc; long ticks; rc = clock_gettime(CLOCK_REALTIME, tsp); if (rc < 0) { #ifndef __COVERITY__ msyslog(LOG_ERR, "TIME: read system clock failed: %m (%d)", errno); #endif /* __COVERITY__ */ exit(1); } if (trunc_os_clock) { ticks = (long)((tsp->tv_nsec * S_PER_NS) / sys_tick); tsp->tv_nsec = (long)(ticks * NS_PER_S * sys_tick); } } static void normalize_time (struct timespec, long, l_fp *); static void normalize_time( struct timespec ts, /* seconds and nanoseconds */ long randd, l_fp *now /* system time */ ) { static struct timespec ts_last; /* last sampled os time */ static struct timespec ts_prev; /* prior os time */ static l_fp lfp_prev; /* prior result */ static double dfuzz_prev; /* prior fuzz */ struct timespec ts_min; /* earliest permissible */ struct timespec ts_lam; /* lamport fictional increment */ struct timespec ts_prev_log; /* for msyslog only */ double dfuzz; double ddelta; l_fp result; l_fp lfpfuzz; l_fp lfpdelta; /* First check if there was a Lamport violation, that is, two * successive calls to 'get_ostime()' resulted in negative * time difference. Use a few milliseconds of permissible * tolerance -- being too sharp can hurt here. (This is intended * for the Win32 target, where the HPC interpolation might * introduce small steps backward. It should not be an issue on * systems where get_ostime() results in a true syscall.) */ if (cmp_tspec(add_tspec_ns(ts, 50000000), ts_last) < 0) lamport_violated = true; ts_last = ts; /* * After default_get_precision() has set a nonzero sys_fuzz, * ensure every reading of the OS clock advances by at least * sys_fuzz over the prior reading, thereby assuring each * fuzzed result is strictly later than the prior. Limit the * necessary fiction to 1 second. */ ts_min = add_tspec_ns(ts_prev, sys_fuzz_nsec); if (cmp_tspec(ts, ts_min) < 0) { ts_lam = sub_tspec(ts_min, ts); if (ts_lam.tv_sec > 0 && !lamport_violated) { msyslog(LOG_ERR, "CLOCK: get_systime Lamport advance exceeds one second (%.9f)", ts_lam.tv_sec + S_PER_NS * ts_lam.tv_nsec); exit(1); } if (!lamport_violated) ts = ts_min; } ts_prev_log = ts_prev; ts_prev = ts; /* convert from timespec to l_fp fixed-point */ result = tspec_stamp_to_lfp(ts); /* * Add in the fuzz. */ dfuzz = randd * 2. / FRAC * sys_fuzz; lfpfuzz = dtolfp(dfuzz); result += lfpfuzz; /* * Ensure result is strictly greater than prior result (ignoring * sys_residual's effect for now) once sys_fuzz has been * determined. */ if (lfp_prev != 0 && !lamport_violated) { if (!L_ISGTU(result, lfp_prev) && sys_fuzz > 0.) { msyslog(LOG_ERR, "CLOCK: ts_prev %ld s + %ld ns, ts_min %ld s + %ld ns", (long)ts_prev_log.tv_sec, ts_prev.tv_nsec, (long)ts_min.tv_sec, ts_min.tv_nsec); msyslog(LOG_ERR, "CLOCK: ts %ld s + %ld ns", (long)ts.tv_sec, ts.tv_nsec); msyslog(LOG_ERR, "CLOCK: sys_fuzz %ld nsec, prior fuzz %.9f", sys_fuzz_nsec, dfuzz_prev); msyslog(LOG_ERR, "CLOCK: this fuzz %.9f", dfuzz); lfpdelta = lfp_prev; lfpdelta -= result; ddelta = lfptod(lfpdelta); msyslog(LOG_ERR, "CLOCK: prev get_systime 0x%x.%08x is %.9f later than 0x%x.%08x", lfpuint(lfp_prev), lfpfrac(lfp_prev), ddelta, lfpuint(result), lfpfrac(result)); } } lfp_prev = result; dfuzz_prev = dfuzz; lamport_violated = false; *now = result; } /* * get_systime - return system time in NTP timestamp format. */ void get_systime( l_fp *now /* system time */ ) { struct timespec ts; /* seconds and nanoseconds */ get_ostime(&ts); normalize_time(ts, sys_fuzz > 0.0 ? ntp_random() : 0, now); } /* * adj_systime - adjust system time by the argument. */ bool /* true on okay, false on error */ adj_systime( double now, /* adjustment (s) */ int (*ladjtime)(const struct timeval *, struct timeval *) ) { struct timeval adjtv; /* new adjustment */ struct timeval oadjtv; /* residual adjustment */ double quant; /* quantize to multiples of */ doubletime_t dtemp; long ticks; bool isneg = false; /* * FIXME: With the legacy Windows port gone, this might be removable. * See also the related FIXME comment in ntpd/ntp_loopfilter.c. * * The Windows port adj_systime() depended on being called each * second even when there's no additional correction, to allow * emulation of adjtime() behavior on top of an API that simply * sets the current rate. This POSIX implementation needs to * ignore invocations with zero correction, otherwise ongoing * EVNT_NSET adjtime() can be aborted by a tiny adjtime() * triggered by sys_residual. */ if ( D_ISZERO_NS(now)) return true; /* * Most Unix adjtime() implementations adjust the system clock * in microsecond quanta, but some adjust in 10-ms quanta. We * carefully round the adjustment to the nearest quantum, then * adjust in quanta and keep the residue for later. */ dtemp = now + sys_residual; if (dtemp < 0) { isneg = true; dtemp = -dtemp; } adjtv.tv_sec = (long)dtemp; dtemp -= adjtv.tv_sec; if (sys_tick > sys_fuzz) quant = sys_tick; else quant = S_PER_US; ticks = (long)(dtemp / quant + .5); adjtv.tv_usec = (long)(ticks * quant * US_PER_S + .5); /* The rounding in the conversions could push us over the * limits: make sure the result is properly normalised! * note: sign comes later, all numbers non-negative here. */ if (adjtv.tv_usec >= US_PER_S) { adjtv.tv_sec += 1; adjtv.tv_usec -= US_PER_S; dtemp -= 1.; } /* set the new residual with leftover from correction */ sys_residual = dtemp - adjtv.tv_usec * S_PER_US; /* * Convert to signed seconds and microseconds for the Unix * adjtime() system call. Note we purposely lose the adjtime() * leftover. */ if (isneg) { adjtv.tv_sec = -adjtv.tv_sec; adjtv.tv_usec = -adjtv.tv_usec; sys_residual = -sys_residual; } if (adjtv.tv_sec != 0 || adjtv.tv_usec != 0) { if (ladjtime(&adjtv, &oadjtv) < 0) { msyslog(LOG_ERR, "CLOCK: adj_systime: %m"); return false; } } return true; } /* * step_systime - step the system clock. * * if your timespec has a 64 bit time_t then you are 2038 ready. * if your timespec has a 32 bit time_t, be sure to duck in 2038 */ bool step_systime( doubletime_t step, int (*settime)(struct timespec *) ) { time_t pivot; /* for ntp era unfolding */ struct timespec timets; struct calendar jd; l_fp fp_ofs, fp_sys; /* offset and target system time in FP */ /* * Get pivot time for NTP era unfolding. Since we don't step * very often, we can afford to do the whole calculation from * scratch. And we're not in the time-critical path yet. */ #if NTP_SIZEOF_TIME_T > 4 /* * This code makes sure the resulting time stamp for the new * system time is in the 2^32 seconds starting at 1970-01-01, * 00:00:00 UTC. */ pivot = 0x80000000; #if USE_COMPILETIME_PIVOT /* * Add the compile time minus 10 years to get a possible target * area of (compile time - 10 years) to (compile time + 126 * years). This should be sufficient for a given binary of * NTPD. */ if (ntpcal_get_build_date(&jd)) { jd.year -= 10; pivot += ntpcal_date_to_time(&jd); } else { msyslog(LOG_ERR, "CLOCK: step_systime: assume 1970-01-01 as build date"); } #else UNUSED_LOCAL(jd); #endif /* USE_COMPILETIME_PIVOT */ #else UNUSED_LOCAL(jd); /* This makes sure the resulting time stamp is on or after * 1969-12-31/23:59:59 UTC and gives us additional two years, * from the change of NTP era in 2036 to the UNIX rollover in * 2038. (Minus one second, but that won't hurt.) We *really* * need a longer 'time_t' after that! Or a different baseline, * but that would cause other serious trouble, too. */ pivot = 0x7FFFFFFF; #endif /* get the complete jump distance as l_fp */ fp_sys = dtolfp(sys_residual); fp_ofs = dtolfp(step); fp_ofs += fp_sys; /* ---> time-critical path starts ---> */ /* get the current time as l_fp (without fuzz) and as struct timespec */ get_ostime(&timets); fp_sys = tspec_stamp_to_lfp(timets); /* get the target time as l_fp */ fp_sys += fp_ofs; /* unfold the new system time */ timets = lfp_stamp_to_tspec(fp_sys, pivot); /* now set new system time */ if (settime(&timets) != 0) { msyslog(LOG_ERR, "CLOCK: step_systime: %m"); return false; } /* <--- time-critical path ended with call to the settime hook <--- */ msyslog(LOG_WARNING, "CLOCK: time stepped by %Lf", step); sys_residual = 0; lamport_violated = (step < 0); if (step_callback) (*step_callback)(); return true; } ntpsec-1.1.0+dfsg1/libntp/dolfptoa.c0000644000175000017500000000737313252364117017103 0ustar rlaagerrlaager/* * dolfptoa - do the grunge work of converting an l_fp number to decimal * * Warning: this conversion is lossy in the low-order bits of the fractional * part. It's good enough for statistics and logging, but do not expect * it to round-trip through atolfp(). 1444359386.1798776096, for example, may * dump as ...095 or ...097. */ #include "config.h" #include #include #include "ntp_fp.h" #include "lib_strbuf.h" #include "ntp_stdlib.h" char * dolfptoa( l_fp lfp, bool neg, short ndec, bool msec ) { uint32_t fpi = lfpuint(lfp); uint32_t fpv = lfpfrac(lfp); uint8_t *cp, *cpend, *cpdec; int dec; uint8_t cbuf[24]; char *buf, *bp; /* * Get a string buffer before starting */ buf = lib_getbuf(); /* * Zero the character buffer */ ZERO(cbuf); /* * Work on the integral part. This should work reasonable on * all machines with 32 bit arithmetic. Please note that 32 bits * can *always* be represented with at most 10 decimal digits, * including a possible rounding from the fractional part. */ cp = cpend = cpdec = &cbuf[10]; for (dec = cp - cbuf; dec > 0 && fpi != 0; dec--) { /* can add another digit */ uint32_t digit; digit = fpi; fpi /= 10U; digit -= (fpi << 3) + (fpi << 1); /* i*10 */ *--cp = (uint8_t)digit; } /* * Done that, now deal with the problem of the fraction. First * determine the number of decimal places. */ dec = ndec; if (dec < 0) dec = 0; if (msec) { dec += 3; cpdec += 3; } if (dec > (long)sizeof(cbuf) - (cpend - cbuf)) dec = (long)sizeof(cbuf) - (cpend - cbuf); /* * If there's a fraction to deal with, do so. */ for (/*NOP*/; dec > 0 && fpv != 0; dec--) { uint32_t digit, tmph, tmpl; /* FIXME - get rid of this ugly kludge! */ #define M_ADD(r_i, r_f, a_i, a_f) /* r += a */ \ do { \ uint32_t add_t = (r_f); \ (r_f) += (a_f); \ (r_i) += (a_i) + ((uint32_t)(r_f) < add_t); \ } while (false) #define M_LSHIFT(v_i, v_f) /* v <<= 1 */ \ do { \ (v_i) = ((uint32_t)(v_i) << 1) | ((uint32_t)(v_f) >> 31); \ (v_f) = ((uint32_t)(v_f) << 1); \ } while (false) /* * The scheme here is to multiply the fraction * (0.1234...) by ten. This moves a junk of BCD into * the units part. record that and iterate. * multiply by shift/add in two dwords. */ digit = 0; M_LSHIFT(digit, fpv); tmph = digit; tmpl = fpv; M_LSHIFT(digit, fpv); M_LSHIFT(digit, fpv); M_ADD(digit, fpv, tmph, tmpl); #undef M_ADD #undef M_SHIFT *cpend++ = (uint8_t)digit; } /* decide whether to round or simply extend by zeros */ if (dec > 0) { /* only '0' digits left -- just reposition end */ cpend += dec; } else { /* some bits remain in 'fpv'; do round */ uint8_t *tp = cpend; int carry = ((fpv & 0x80000000) != 0); for (dec = tp - cbuf; carry && dec > 0; dec--) { *--tp += 1; if (*tp == 10) *tp = 0; else carry = false; } if (tp < cp) /* rounding from 999 to 1000 or similar? */ cp = tp; } /* * We've now got the fraction in cbuf[], with cp pointing at * the first character, cpend pointing past the last, and * cpdec pointing at the first character past the decimal. * Remove leading zeros, then format the number into the * buffer. */ while (cp < cpdec && *cp == 0) cp++; if (cp >= cpdec) cp = cpdec - 1; bp = buf; if (neg) *bp++ = '-'; while (cp < cpend) { if (cp == cpdec) *bp++ = '.'; *bp++ = (char)(*cp++) + '0'; } *bp = '\0'; /* * Done! */ return buf; } char * mfptoa( l_fp lfp, short ndec ) { bool isneg = L_ISNEG(lfp); if (isneg) { L_NEG(lfp); } return dolfptoa(lfp, isneg, ndec, false); } char * mfptoms( l_fp lfp, short ndec ) { bool isneg = L_ISNEG(lfp); if (isneg) { L_NEG(lfp); } return dolfptoa(lfp, isneg, ndec, true); } ntpsec-1.1.0+dfsg1/libntp/statestr.c0000644000175000017500000002201313252364117017130 0ustar rlaagerrlaager/* * pretty printing of status information */ #include "config.h" #include #include #include "ntp_stdlib.h" #include "ntp.h" #include "lib_strbuf.h" #include "ntp_refclock.h" #include "ntp_control.h" # include "ntp_syscall.h" /* * Structure for turning various constants into a readable string. */ struct codestring { int code; const char * const string; }; /* * Leap status (leap) */ static const struct codestring leap_codes[] = { { LEAP_NOWARNING, "leap_none" }, { LEAP_ADDSECOND, "leap_add_sec" }, { LEAP_DELSECOND, "leap_del_sec" }, { LEAP_NOTINSYNC, "leap_alarm" }, { -1, "leap" } }; /* * Clock source status (sync) */ static const struct codestring sync_codes[] = { { CTL_SST_TS_UNSPEC, "sync_unspec" }, { CTL_SST_TS_ATOM, "sync_pps" }, { CTL_SST_TS_LF, "sync_lf_radio" }, { CTL_SST_TS_HF, "sync_hf_radio" }, { CTL_SST_TS_UHF, "sync_uhf_radio" }, { CTL_SST_TS_LOCAL, "sync_local" }, { CTL_SST_TS_NTP, "sync_ntp" }, { CTL_SST_TS_UDPTIME, "sync_other" }, { CTL_SST_TS_WRSTWTCH, "sync_wristwatch" }, { CTL_SST_TS_TELEPHONE, "sync_telephone" }, { -1, "sync" } }; /* * Peer selection status (sel) */ static const struct codestring select_codes[] = { { CTL_PST_SEL_REJECT, "sel_reject" }, { CTL_PST_SEL_SANE, "sel_falsetick" }, { CTL_PST_SEL_CORRECT, "sel_excess" }, { CTL_PST_SEL_SELCAND, "sel_outlier" }, { CTL_PST_SEL_SYNCCAND, "sel_candidate" }, { CTL_PST_SEL_EXCESS, "sel_backup" }, { CTL_PST_SEL_SYSPEER, "sel_sys.peer" }, { CTL_PST_SEL_PPS, "sel_pps.peer" }, { -1, "sel" } }; /* * Clock status (clk) */ static const struct codestring clock_codes[] = { { CTL_CLK_OKAY, "clk_unspec" }, { CTL_CLK_NOREPLY, "clk_no_reply" }, { CTL_CLK_BADFORMAT, "clk_bad_format" }, { CTL_CLK_FAULT, "clk_fault" }, { CTL_CLK_PROPAGATION, "clk_bad_signal" }, { CTL_CLK_BADDATE, "clk_bad_date" }, { CTL_CLK_BADTIME, "clk_bad_time" }, { -1, "clk" } }; #ifdef FLASH_CODES_UNUSED /* * Flash bits -- see ntpq.c tstflags & tstflagnames */ static const struct codestring flash_codes[] = { { BOGON1, "pkt_dup" }, { BOGON2, "pkt_bogus" }, { BOGON3, "pkt_unsync" }, { BOGON4, "pkt_denied" }, { BOGON5, "pkt_auth" }, { BOGON6, "pkt_stratum" }, { BOGON7, "pkt_header" }, { BOGON8, "pkt_autokey" }, { BOGON9, "pkt_crypto" }, { BOGON10, "peer_stratum" }, { BOGON11, "peer_dist" }, { BOGON12, "peer_loop" }, { BOGON13, "peer_unreach" }, { -1, "flash" } }; #endif /* * System events (sys) */ static const struct codestring sys_codes[] = { { EVNT_UNSPEC, "unspecified" }, { EVNT_NSET, "freq_not_set" }, { EVNT_FSET, "freq_set" }, { EVNT_SPIK, "spike_detect" }, { EVNT_FREQ, "freq_mode" }, { EVNT_SYNC, "clock_sync" }, { EVNT_SYSRESTART, "restart" }, { EVNT_SYSFAULT, "panic_stop" }, { EVNT_NOPEER, "no_sys_peer" }, { EVNT_ARMED, "leap_armed" }, { EVNT_DISARMED, "leap_disarmed" }, { EVNT_LEAP, "leap_event" }, { EVNT_CLOCKRESET, "clock_step" }, { EVNT_KERN, "kern" }, { EVNT_TAI, "TAI" }, { EVNT_LEAPVAL, "stale_leapsecond_values" }, { -1, "" } }; /* * Peer events (peer) */ static const struct codestring peer_codes[] = { { PEVNT_MOBIL & ~PEER_EVENT, "mobilize" }, { PEVNT_DEMOBIL & ~PEER_EVENT, "demobilize" }, { PEVNT_UNREACH & ~PEER_EVENT, "unreachable" }, { PEVNT_REACH & ~PEER_EVENT, "reachable" }, { PEVNT_RESTART & ~PEER_EVENT, "restart" }, { PEVNT_REPLY & ~PEER_EVENT, "no_reply" }, { PEVNT_RATE & ~PEER_EVENT, "rate_exceeded" }, { PEVNT_DENY & ~PEER_EVENT, "access_denied" }, { PEVNT_ARMED & ~PEER_EVENT, "leap_armed" }, { PEVNT_NEWPEER & ~PEER_EVENT, "sys_peer" }, { PEVNT_CLOCK & ~PEER_EVENT, "clock_event" }, { PEVNT_AUTH & ~PEER_EVENT, "bad_auth" }, { PEVNT_POPCORN & ~PEER_EVENT, "popcorn" }, { -1, "" } }; /* * Peer status bits */ static const struct codestring peer_st_bits[] = { { CTL_PST_CONFIG, "conf" }, { CTL_PST_AUTHENABLE, "authenb" }, { CTL_PST_AUTHENTIC, "auth" }, { CTL_PST_REACH, "reach" }, { CTL_PST_BCAST, "bcast" }, /* not used with getcode(), no terminating entry needed */ }; /* * Restriction match bits */ static const struct codestring res_match_bits[] = { { RESM_NTPONLY, "ntpport" }, { RESM_INTERFACE, "interface" }, { RESM_SOURCE, "source" }, /* not used with getcode(), no terminating entry needed */ }; /* * Restriction access bits */ static const struct codestring res_access_bits[] = { { RES_IGNORE, "ignore" }, { RES_DONTSERVE, "noserve" }, { RES_DONTTRUST, "notrust" }, { RES_NOQUERY, "noquery" }, { RES_NOMODIFY, "nomodify" }, { RES_NOPEER, "nopeer" }, { RES_LIMITED, "limited" }, { RES_VERSION, "version" }, { RES_KOD, "kod" }, { RES_FLAKE, "flake" }, /* not used with getcode(), no terminating entry needed */ }; /* * kernel discipline status bits */ static const struct codestring k_st_bits[] = { { STA_PLL, "pll" }, { STA_PPSFREQ, "ppsfreq" }, { STA_PPSTIME, "ppstime" }, { STA_FLL, "fll" }, { STA_INS, "ins" }, { STA_DEL, "del" }, { STA_UNSYNC, "unsync" }, { STA_FREQHOLD, "freqhold" }, { STA_PPSSIGNAL, "ppssignal" }, { STA_PPSJITTER, "ppsjitter" }, { STA_PPSWANDER, "ppswander" }, { STA_PPSERROR, "ppserror" }, { STA_CLOCKERR, "clockerr" }, # ifdef STA_NANO { STA_NANO, "nano" }, # endif # ifdef STA_MODE { STA_MODE, "mode=fll" }, # endif # ifdef STA_CLK { STA_CLK, "src=B" }, # endif /* not used with getcode(), no terminating entry needed */ }; /* Forwards */ static const char * getcode(int, const struct codestring *); static const char * getevents(int); static const char * peer_st_flags(uint8_t pst); /* * getcode - return string corresponding to code */ static const char * getcode( int code, const struct codestring * codetab ) { char * buf; while (codetab->code != -1) { if (codetab->code == code) return codetab->string; codetab++; } buf = lib_getbuf(); snprintf(buf, LIB_BUFLENGTH, "%s_%d", codetab->string, code); return buf; } /* * getevents - return a descriptive string for the event count */ static const char * getevents( int cnt ) { char * buf; if (cnt == 0) return "no events"; buf = lib_getbuf(); snprintf(buf, LIB_BUFLENGTH, "%d event%s", cnt, (1 == cnt) ? "" : "s"); return buf; } /* * decode_bitflags() * * returns a human-readable string with a keyword from tab for each bit * set in bits, separating multiple entries with text of sep2. */ static const char * decode_bitflags( int bits, const char * sep2, const struct codestring * tab, size_t tab_ct ) { const char * sep; char * buf; char * pch; char * lim; size_t b; int rc; int saved_errno; /* for use in DPRINT/TPRINT with %m */ saved_errno = errno; buf = lib_getbuf(); pch = buf; lim = buf + LIB_BUFLENGTH; sep = ""; for (b = 0; b < tab_ct; b++) { if (tab[b].code & bits) { rc = snprintf(pch, (size_t)(lim - pch), "%s%s", sep, tab[b].string); if (rc < 0) goto toosmall; pch += (unsigned int)rc; if (pch >= lim) goto toosmall; sep = sep2; } } return buf; toosmall: snprintf(buf, LIB_BUFLENGTH, "decode_bitflags(%s) can't decode 0x%x in %d bytes", (tab == peer_st_bits) ? "peer_st" : (tab == k_st_bits) ? "kern_st" : "", (unsigned)bits, (int)LIB_BUFLENGTH); errno = saved_errno; return buf; } static const char * peer_st_flags( uint8_t pst ) { return decode_bitflags(pst, ", ", peer_st_bits, COUNTOF(peer_st_bits)); } const char * res_match_flags( unsigned short mf ) { return decode_bitflags(mf, " ", res_match_bits, COUNTOF(res_match_bits)); } const char * res_access_flags( unsigned short af ) { return decode_bitflags(af, " ", res_access_bits, COUNTOF(res_access_bits)); } const char * k_st_flags( uint32_t st ) { return decode_bitflags((int)st, " ", k_st_bits, COUNTOF(k_st_bits)); } /* * statustoa - return a descriptive string for a peer status */ char * statustoa( int type, int st ) { char * cb; char * cc; uint8_t pst; cb = lib_getbuf(); switch (type) { case TYPE_SYS: snprintf(cb, LIB_BUFLENGTH, "%s, %s, %s, %s", getcode(CTL_SYS_LI(st), leap_codes), getcode(CTL_SYS_SOURCE(st), sync_codes), getevents(CTL_SYS_NEVNT(st)), getcode(CTL_SYS_EVENT(st), sys_codes)); break; case TYPE_PEER: pst = (uint8_t)CTL_PEER_STATVAL(st); snprintf(cb, LIB_BUFLENGTH, "%s, %s, %s", peer_st_flags(pst), getcode(pst & 0x7, select_codes), getevents(CTL_PEER_NEVNT(st))); if (CTL_PEER_EVENT(st) != EVNT_UNSPEC) { cc = cb + strlen(cb); snprintf(cc, (size_t)(LIB_BUFLENGTH - (cc - cb)), ", %s", getcode(CTL_PEER_EVENT(st), peer_codes)); } break; case TYPE_CLOCK: snprintf(cb, LIB_BUFLENGTH, "%s, %s", getevents(CTL_SYS_NEVNT(st)), getcode((st) & 0xf, clock_codes)); break; default: /* huh? */ break; } return cb; } const char * eventstr( int num ) { if (num & PEER_EVENT) return (getcode(num & ~PEER_EVENT, peer_codes)); else return (getcode(num, sys_codes)); } const char * ceventstr( int num ) { return getcode(num, clock_codes); } ntpsec-1.1.0+dfsg1/libntp/hextolfp.c0000644000175000017500000000252213252364117017113 0ustar rlaagerrlaager/* * hextolfp - convert an ascii hex string to an l_fp number */ #include "config.h" #include #include #include #include "ntp_fp.h" #include "ntp_stdlib.h" bool hextolfp( const char *str, l_fp *lfp ) { const char *cp; const char *cpstart; unsigned long dec_i; unsigned long dec_f; char *ind = NULL; static const char *digits = "0123456789abcdefABCDEF"; dec_i = dec_f = 0; cp = str; /* * We understand numbers of the form: * * [spaces]8_hex_digits[.]8_hex_digits[spaces|\n|\0] */ while (isspace((unsigned char)*cp)) cp++; cpstart = cp; while (*cp != '\0' && (cp - cpstart) < 8 && (ind = strchr(digits, *cp)) != NULL) { dec_i = dec_i << 4; /* multiply by 16 */ dec_i += (unsigned long)(((ind - digits) > 15) ? (ind - digits) - 6 : (ind - digits)); cp++; } if ((cp - cpstart) < 8 || ind == NULL) return false; if (*cp == '.') cp++; cpstart = cp; while (*cp != '\0' && (cp - cpstart) < 8 && (ind = strchr(digits, *cp)) != NULL) { dec_f = dec_f << 4; /* multiply by 16 */ dec_f += (unsigned long)(((ind - digits) > 15) ? (ind - digits) - 6 : (ind - digits)); cp++; } if ((cp - cpstart) < 8 || ind == NULL) return false; if (*cp != '\0' && !isspace((unsigned char)*cp)) return false; *lfp = lfpinit_u(dec_i, dec_f); return true; } ntpsec-1.1.0+dfsg1/libntp/decodenetnum.c0000644000175000017500000000722213252364117017736 0ustar rlaagerrlaager/* * decodenetnum - convert text IP address and port to sockaddr_u */ #include "config.h" #include #include #include #include #include #include #include #include "ntp_stdlib.h" #include "ntp_assert.h" /* This is a glibc thing, not standardized */ #ifndef NI_MAXSERV #define NI_MAXSERV 32 #endif /* * decodenetnum convert text IP address and port to sockaddr_u * * Returns false for failure, true for success. * * We accept: * IPv4 * IPv6 * [IPv6] * IPv4:port * [IPv6]:port * * The IP must be numeric but the port can be symbolic. * * return: 0 for success * negative numbers for error codes */ int decodenetnum( const char *num, sockaddr_u *netnum ) { struct addrinfo hints, *ai = NULL; const char *ip_start, *ip_end, *port_start, *temp; size_t numlen; bool have_brackets; int retcode = 0; char ip[INET6_ADDRSTRLEN]; ZERO(*netnum); /* don't return random data on fail */ /* check num not NULL before using it */ if ( NULL == num) { return -4001; } numlen = strlen(num); /* Quickly reject empty or impossibly long inputs. */ if(numlen == 0 || numlen > ((sizeof(ip) - 1) + (NI_MAXSERV - 1) + (3 /* "[]:" */))) { return -4002; } /* Is this a bracketed IPv6 address? */ have_brackets = ('[' == num[0]); if(have_brackets) { /* If it's formatted like [IPv6]:port, the port part comes after the "]:". */ if((temp = strstr(num, "]:")) != NULL) { ip_start = num + 1; ip_end = temp; port_start = temp + 2; } else if(num[numlen-1] == ']') { /* It's just [IPv6]. */ ip_start = num + 1; ip_end = ip_start + numlen - 1; port_start = NULL; } else { /* Anything else must be invalid. */ return -4003; } } /* No brackets. Searching backward, see if there's at least one * colon... */ else if((temp = strrchr(num, ':')) != NULL) { /* ...and then look for a second one, searching forward. */ if(strchr(num, ':') == temp) { /* Searching from both directions gave us the same result, so there's only one colon. What's after it is the port. */ ip_start = num; ip_end = temp; port_start = temp + 1; } else { /* Two colons and no brackets, so it has to be a bare IPv6 address */ ip_start = num; ip_end = ip_start + numlen; port_start = NULL; } } else { /* No colon, no brackets. */ ip_start = num; ip_end = ip_start + numlen; port_start = NULL; } /* Now we have ip_start pointing to the start of the IP, ip_end pointing past the end of the IP, and port_start either NULL or pointing to the start of the port. Check whether the IP is short enough to possibly be valid and if so copy it into ip. */ if ((ip_end - ip_start + 1) > (int)sizeof(ip)) { return -4004; } else { memcpy(ip, ip_start, (size_t)(ip_end - ip_start)); ip[ip_end - ip_start] = '\0'; } ZERO(hints); hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST; hints.ai_protocol = IPPROTO_UDP; /* One final validity check: only IPv6 addresses are allowed to * have brackets. */ hints.ai_family = have_brackets ? AF_INET6 : AF_UNSPEC; /* If we've gotten this far, then we still don't know that either the IP address or the port is well-formed, but at least they're unambiguously delimited from each other. Let getaddrinfo() perform all further validation. */ retcode = getaddrinfo(ip, port_start == NULL ? "ntp" : port_start, &hints, &ai); if(retcode) { return retcode; } INSIST(ai->ai_addrlen <= sizeof(*netnum)); if(netnum) { memcpy(netnum, ai->ai_addr, ai->ai_addrlen); } freeaddrinfo(ai); return 0; } ntpsec-1.1.0+dfsg1/libntp/isc_error.c0000644000175000017500000000204113252364117017245 0ustar rlaagerrlaager/* * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ /*! \file */ #include "config.h" #include #include #include "ntp_config.h" #include "ntp_syslog.h" #include "isc_error.h" #define MAX_UNEXPECTED_ERRORS 100 void isc_error_unexpected(const char *file, int line, const char *format, ...) { va_list args; char errbuf[256]; static int unexpected_error_cnt = 0; if (unexpected_error_cnt >= MAX_UNEXPECTED_ERRORS) return; /* avoid clutter in log */ va_start(args, format); msyslog(LOG_ERR, "ERR: %s:%d: unexpected error:", file, line); vsnprintf(errbuf, sizeof(errbuf), format, args); msyslog(LOG_ERR, "%s", errbuf); if (++unexpected_error_cnt == MAX_UNEXPECTED_ERRORS) msyslog(LOG_ERR, "ERR: Too many errors. Shutting up."); va_end(args); } ntpsec-1.1.0+dfsg1/libntp/lib_strbuf.c0000644000175000017500000000102113252364117017406 0ustar rlaagerrlaager/* * lib_strbuf - library string storage */ #include "config.h" #include "isc_netaddr.h" #include "ntp_fp.h" #include "ntp_stdlib.h" #include "lib_strbuf.h" /* * Storage declarations */ int debug; /* * Macro to get a pointer to the next buffer */ char *lib_getbuf(void) { static libbufstr lib_stringbuf[LIB_NUMBUF]; static int lib_nextbuf; char *bufp; ZERO(lib_stringbuf[lib_nextbuf]); (bufp) = &lib_stringbuf[lib_nextbuf++][0]; lib_nextbuf %= (int)COUNTOF(lib_stringbuf); return bufp; } ntpsec-1.1.0+dfsg1/libntp/clocktime.c0000644000175000017500000001277513252364117017247 0ustar rlaagerrlaager/* * clocktime - compute the NTP date from a day of year, hour, minute * and second. */ #include "config.h" #include "ntp_fp.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" /* * We check that the time be within CLOSETIME seconds of the receive * time stamp. This is about 4 hours, which hopefully should be wide * enough to collect most data, while close enough to keep things from * getting confused. */ #define CLOSETIME (4u*60u*60u) /* * Since we try to match years, the result of a full search will not * change when we are already less than a half year from the receive * time stamp. Since the length of a year is variable we use a * slightly narrower limit; this might require a full evaluation near * the edge, but will make sure we always get the correct result. */ #define NEARTIME (182u * SECSPERDAY) /* * local calendar helpers */ static int32_t ntp_to_year(uint32_t, time_t); static uint32_t year_to_ntp(int32_t); /* * Take a time spec given as year, day-of-year, hour, minute and second * (in GMT/UTC) and convert it to a NTP time stamp in * '*ts_ui'. There are two cases: ether the year is > 99, in which * case it is used, or it is < 99 in which case we ignore it and try * to deduce a year, * * The value will be in the range (rec_ui-0.5yrs) to * (rec_ui+0.5yrs). A hint for the current start-of-year will be * read from '*yearstart'. * * On return '*ts_ui' will always the best matching solution, and * '*yearstart' will receive the associated start-of-year. * * The function will tell if the result in 'ts_ui' is in CLOSETIME * (+/-4hrs) around the receive time by returning a non-zero value. * * Note: The function puts no constraints on the value ranges for the * time specification, but evaluates the effective seconds in * 32-bit arithmetic. */ int clocktime( int year , /* year */ int yday , /* day-of-year */ int hour , /* hour of day */ int minute , /* minute of hour */ int second , /* second of minute */ time_t pivot , /* pivot for time unfolding */ uint32_t rec_ui , /* recent timestamp to get year from */ uint32_t *yearstart, /* cached start-of-year, secs from NTP epoch */ uint32_t *ts_ui ) /* effective time stamp */ { uint32_t ystt[3]; /* year start */ uint32_t test[3]; /* result time stamp */ uint32_t diff[3]; /* abs difference to receive */ int32_t y, idx, min; uint32_t tmp; /* * Compute the offset into the year in seconds. Can't * be negative as yday is 1-origin. */ tmp = ((uint32_t)second + SECSPERMIN * ((uint32_t)minute + MINSPERHR * ((uint32_t)hour + HRSPERDAY * ((uint32_t)yday - 1)))); /* * Year > 1970 - from a 4-digit year stamp, must be greater * than POSIX epoch. Means we're not dependent on the pivot * value (derived from the packet receipt timestamp, and thus * ultimately from the system clock) to be correct. CLOSETIME * clipping to the receive time will *not* be applied in this * case. These two lines thus make it possible to recover from * a trashed or zeroed system clock. * * Warning: the hack in the NMEA driver that rectifies 4-digit * years from 2-digit ones has an expiration date in 2399. * After that this code will go badly wrong. */ if (year > 1970) { *yearstart = year_to_ntp(year); *ts_ui = *yearstart + tmp; return true; } /* * Year was too small to make sense, probably from a 2-digit * year stamp. * Based on the cached year start, do a first attempt. Be * happy and return if this gets us better than NEARTIME to * the receive time stamp. Do this only if the cached year * start is not zero, which will not happen after 1900 for the * next few thousand years. */ if (*yearstart) { /* -- get time stamp of potential solution */ test[0] = (uint32_t)(*yearstart) + (unsigned int)tmp; /* -- calc absolute difference to receive time */ diff[0] = test[0] - rec_ui; if (diff[0] >= 0x80000000u) diff[0] = ~diff[0] + 1; /* -- can't get closer if diff < NEARTIME */ if (diff[0] < NEARTIME) { *ts_ui = test[0]; return diff[0] < CLOSETIME; } } /* * Now the dance begins. Based on the receive time stamp and * the seconds offset in 'tmp', we make an educated guess * about the year to start with. This takes us on the spot * with a fuzz of +/-1 year. * * We calculate the effective timestamps for the three years * around the guess and select the entry with the minimum * absolute difference to the receive time stamp. */ y = ntp_to_year(rec_ui - tmp, pivot); for (idx = 0; idx < 3; idx++) { /* -- get year start of potential solution */ ystt[idx] = year_to_ntp(y + idx - 1); /* -- get time stamp of potential solution */ test[idx] = ystt[idx] + tmp; /* -- calc absolute difference to receive time */ diff[idx] = test[idx] - rec_ui; if (diff[idx] >= 0x80000000u) diff[idx] = ~diff[idx] + 1; } /* -*- assume current year fits best, then search best fit */ for (min = 1, idx = 0; idx < 3; idx++) if (diff[idx] < diff[min]) min = idx; /* -*- store results and update year start */ *ts_ui = test[min]; *yearstart = ystt[min]; /* -*- tell if we could get into CLOSETIME*/ return diff[min] < CLOSETIME; } static int32_t ntp_to_year( uint32_t ntp, time_t pivot) { time64_t t; ntpcal_split s; t = ntpcal_ntp_to_ntp(ntp, pivot); s = ntpcal_daysplit(t); s = ntpcal_split_eradays(s.hi + DAY_NTP_STARTS - 1, NULL); return s.hi + 1; } static uint32_t year_to_ntp( int32_t year) { uint32_t days; days = (uint32_t)ntpcal_days_in_years(year-1) - DAY_NTP_STARTS + 1; return days * SECSPERDAY; } ntpsec-1.1.0+dfsg1/libntp/README0000644000175000017500000000021213252364117015770 0ustar rlaagerrlaager= README file for directory libntp = This directory contains the sources for the NTP library used by most programs in this distribution. ntpsec-1.1.0+dfsg1/libntp/ntp_calendar.c0000644000175000017500000006135013252364117017720 0ustar rlaagerrlaager/* * ntp_calendar.c - calendar and helper functions * * Written by Juergen Perlinger for the NTP project. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: NTP * * There is more about these types and calculations in the internals tour * document distributed with the code. */ #include "config.h" #include #include "ntp_types.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" #include "ntp_fp.h" /* *--------------------------------------------------------------------- * replacing the 'time()' function * -------------------------------------------------------------------- */ static ntpcal_split ntpcal_days_in_months(int32_t /* months */); static int32_t ntpcal_edate_to_yeardays(int32_t, int32_t, int32_t); /* *--------------------------------------------------------------------- * Get the build date & time *--------------------------------------------------------------------- */ int ntpcal_get_build_date( struct calendar * jd ) { time_t epoch = (time_t)BUILD_EPOCH; struct tm *epoch_tm; ZERO(*jd); jd->year = 1970; jd->month = 1; jd->monthday = 1; epoch_tm = gmtime(&epoch); if ( NULL == epoch_tm ) { /* bad EPOCH */ return false; } /* good EPOCH */ jd->year = epoch_tm->tm_year + 1900; jd->yearday = epoch_tm->tm_yday + 1; jd->month = epoch_tm->tm_mon + 1; jd->monthday = epoch_tm->tm_mday; jd->hour = epoch_tm->tm_hour; jd->minute = epoch_tm->tm_min; jd->second = epoch_tm->tm_sec; jd->weekday = epoch_tm->tm_wday; #if 0 fprintf(stderr, "Build: %d %d %d %d %d %d %d %d\n", (int)jd->year, (int)jd->yearday, (int)jd->month, (int)jd->monthday, (int)jd->hour, (int)jd->minute, (int)jd->second, (int)jd->weekday); #endif return true; } /* *--------------------------------------------------------------------- * basic calendar stuff * -------------------------------------------------------------------- */ /* month table for a year starting with March,1st */ static const uint16_t shift_month_table[13] = { 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 }; /* month tables for years starting with January,1st; regular & leap */ static const uint16_t real_month_table[2][13] = { /* -*- table for regular years -*- */ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, /* -*- table for leap years -*- */ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; /* * Some notes on the terminology: * * We use the proleptic Gregorian calendar, which is the Gregorian * calendar extended in both directions ad infinitum. This totally * disregards the fact that this calendar was invented in 1582, and * was adopted at various dates over the world; sometimes even after * the start of the NTP epoch. * * Normally date parts are given as current cycles, while time parts * are given as elapsed cycles: * * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month, * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed. * * The basic calculations for this calendar implementation deal with * ELAPSED date units, which is the number of full years, full months * and full days before a date: 1970-01-01 would be (1969, 0, 0) in * that notation. * * To ease the numeric computations, month and day values outside the * normal range are acceptable: 2001-03-00 will be treated as the day * before 2001-03-01, 2000-13-32 will give the same result as * 2001-02-01 and so on. * * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die' * (day number). This is the number of days elapsed since 0000-12-31 * in the proleptic Gregorian calendar. The begin of the Christian Era * (0001-01-01) is RD(1). * * * Some notes on the implementation: * * Calendar algorithms thrive on the division operation, which is one of * the slowest numerical operations in any CPU. What saves us here from * abysmal performance is the fact that all divisions are divisions by * constant numbers, and most compilers can do this by a multiplication * operation. But this might not work when using the div/ldiv/lldiv * function family, because many compilers are not able to do inline * expansion of the code with following optimisation for the * constant-divider case. * * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which * are inherently target dependent. Nothing that could not be cured with * autoconf, but still a mess... * * Furthermore, we need floor division while C demands truncation to * zero, so additional steps are required to make sure the algorithms * work. * * For all this, all divisions by constant are coded manually, even when * there is a joined div/mod operation: The optimiser should sort that * out, if possible. * * Finally, the functions do not check for overflow conditions. This * is a sacrifice made for execution speed; since a 32-bit day counter * covers +/- 5,879,610 years, this should not pose a problem here. */ /* * ================================================================== * * General algorithmic stuff * * ================================================================== */ /* *--------------------------------------------------------------------- * Do a periodic extension of 'value' around 'pivot' with a period of * 'cycle'. * * The result 'res' is a number that holds to the following properties: * * 1) res MOD cycle == value MOD cycle * 2) pivot <= res < pivot + cycle * (replace />= for negative cycles) * * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which * is not the same as the '%' operator in C: C requires division to be * a truncated division, where remainder and dividend have the same * sign if the remainder is not zero, whereas floor division requires * divider and modulus to have the same sign for a non-zero modulus. * * This function has some useful applications: * * + let Y be a calendar year and V a truncated 2-digit year: then * periodic_extend(Y-50, V, 100) * is the closest expansion of the truncated year with respect to * the full year, that is a 4-digit year with a difference of less * than 50 years to the year Y. ("century unfolding") * * + let T be a UN*X time stamp and V be seconds-of-day: then * perodic_extend(T-43200, V, 86400) * is a time stamp that has the same seconds-of-day as the input * value, with an absolute difference to T of <= 12hrs. ("day * unfolding") * * + Wherever you have a truncated periodic value and a non-truncated * base value and you want to match them somehow... * * Basically, the function delivers 'pivot + (value - pivot) % cycle', * but the implementation takes some pains to avoid internal signed * integer overflows in the '(value - pivot) % cycle' part and adheres * to the floor division convention. * * If 64bit scalars where available on all intended platforms, writing a * version that uses 64 bit ops would be easy; writing a general * division routine for 64bit ops on a platform that can only do * 32/16bit divisions and is still performant is a bit more * difficult. Since most usecases can be coded in a way that does only * require the 32-bit version a 64bit version is NOT provided here. * --------------------------------------------------------------------- */ int32_t ntpcal_periodic_extend( int32_t pivot, int32_t value, int32_t cycle ) { uint32_t diff; bool cpl = false; /* modulo complement flag */ bool neg = false; /* sign change flag */ /* make the cycle positive and adjust the flags */ if (cycle < 0) { cycle = - cycle; neg = !neg; cpl = !cpl; } /* guard against div by zero or one */ if (cycle > 1) { /* * Get absolute difference as unsigned quantity and * the complement flag. This is done by always * subtracting the smaller value from the bigger * one. This implementation works only on a two's * complement machine! */ if (value >= pivot) { diff = (uint32_t)value - (uint32_t)pivot; } else { diff = (uint32_t)pivot - (uint32_t)value; cpl ^= 1; } diff %= (uint32_t)cycle; if (diff) { if (cpl) diff = (uint32_t)cycle - diff; if (neg) diff = ~diff + 1; pivot += (int32_t)diff; } } return pivot; } /* *------------------------------------------------------------------- * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X * scale with proper epoch unfolding around a given pivot or the current * system time. This function happily accepts negative pivot values as * timestamps befor 1970-01-01, so be aware of possible trouble on * platforms with 32bit 'time_t'! * * This is also a periodic extension, but since the cycle is 2^32 and * the shift is 2^31, we can do some *very* fast math without explicit * divisions. *------------------------------------------------------------------- */ time64_t ntpcal_ntp_to_time( uint32_t ntp, time_t pivot ) { time64_t res; settime64s(res, pivot); settime64u(res, time64u(res)-0x80000000); /* unshift of half range */ ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ ntp -= time64lo(res); /* cycle difference */ settime64u(res, time64u(res)+(uint64_t)ntp); /* get expanded time */ return res; } /* *------------------------------------------------------------------- * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP * scale with proper epoch unfolding around a given pivot or the current * system time. * * Note: The pivot must be given in the UN*X time domain! * * This is also a periodic extension, but since the cycle is 2^32 and * the shift is 2^31, we can do some *very* fast math without explicit * divisions. *------------------------------------------------------------------- */ time64_t ntpcal_ntp_to_ntp( uint32_t ntp, time_t pivot ) { time64_t res; settime64s(res, pivot); settime64u(res, time64u(res) - 0x80000000); /* unshift of half range */ settime64u(res, time64u(res) + (uint32_t)JAN_1970); /* warp into NTP domain */ ntp -= time64lo(res); /* cycle difference */ settime64u(res, time64u(res) + (uint64_t)ntp); /* get expanded time */ return res; } /* * ================================================================== * * Splitting values to composite entities * * ================================================================== */ /* *------------------------------------------------------------------- * Split a 64bit seconds value into elapsed days in 'res.hi' and * elapsed seconds since midnight in 'res.lo' using explicit floor * division. This function happily accepts negative time values as * timestamps before the respective epoch start. * ------------------------------------------------------------------- */ ntpcal_split ntpcal_daysplit( const time64_t ts ) { ntpcal_split res; /* manual floor division by SECSPERDAY */ res.hi = (int32_t)(time64s(ts) / SECSPERDAY); res.lo = (int32_t)(time64s(ts) % SECSPERDAY); if (res.lo < 0) { res.hi -= 1; res.lo += SECSPERDAY; } return res; } /* *------------------------------------------------------------------- * Split a 32bit seconds value into h/m/s and excessive days. This * function happily accepts negative time values as timestamps before * midnight. * ------------------------------------------------------------------- */ static int32_t priv_timesplit( int32_t split[3], int32_t ts ) { int32_t days = 0; /* make sure we have a positive offset into a day */ if (ts < 0 || ts >= SECSPERDAY) { days = ts / SECSPERDAY; ts = ts % SECSPERDAY; if (ts < 0) { days -= 1; ts += SECSPERDAY; } } /* get secs, mins, hours */ split[2] = (uint8_t)(ts % SECSPERMIN); ts /= SECSPERMIN; split[1] = (uint8_t)(ts % MINSPERHR); split[0] = (uint8_t)(ts / MINSPERHR); return days; } /* * --------------------------------------------------------------------- * Given the number of elapsed days in the calendar era, split this * number into the number of elapsed years in 'res.hi' and the number * of elapsed days of that year in 'res.lo'. * * if 'isleapyear' is not NULL, it will receive an integer that is 0 for * regular years and a non-zero value for leap years. *--------------------------------------------------------------------- */ ntpcal_split ntpcal_split_eradays( int32_t days, int32_t *isleapyear ) { ntpcal_split res; int32_t n400, n100, n004, n001, yday; /* calendar year cycles */ /* * Split off calendar cycles, using floor division in the first * step. After that first step, simple division does it because * all operands are positive; alas, we have to be aware of the * possible cycle overflows for 100 years and 1 year, caused by * the additional leap day. */ n400 = days / GREGORIAN_CYCLE_DAYS; yday = days % GREGORIAN_CYCLE_DAYS; if (yday < 0) { n400 -= 1; yday += GREGORIAN_CYCLE_DAYS; } n100 = yday / GREGORIAN_NORMAL_CENTURY_DAYS; yday = yday % GREGORIAN_NORMAL_CENTURY_DAYS; n004 = yday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; yday = yday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; n001 = yday / DAYSPERYEAR; yday = yday % DAYSPERYEAR; /* * check for leap cycle overflows and calculate the leap flag * if needed */ if ((n001 | n100) > 3) { /* hit last day of leap year */ n001 -= 1; yday += DAYSPERYEAR; if (isleapyear) *isleapyear = 1; } else if (isleapyear) *isleapyear = (n001 == 3) && ((n004 != 24) || (n100 == 3)); /* now merge the cycles to elapsed years, using horner scheme */ res.hi = ((4*n400 + n100)*25 + n004)*4 + n001; res.lo = yday; return res; } /* *--------------------------------------------------------------------- * Given a number of elapsed days in a year and a leap year indicator, * split the number of elapsed days into the number of elapsed months in * 'res.hi' and the number of elapsed days of that month in 'res.lo'. * * This function will fail and return {-1,-1} if the number of elapsed * days is not in the valid range! *--------------------------------------------------------------------- */ ntpcal_split ntpcal_split_yeardays( int32_t eyd, bool isleapyear ) { ntpcal_split res; const uint16_t *lt; /* month length table */ /* check leap year flag and select proper table */ lt = real_month_table[(isleapyear != 0)]; if (0 <= eyd && eyd < lt[12]) { /* get zero-based month by approximation & correction step */ res.hi = eyd >> 5; /* approx month; might be 1 too low */ if (lt[res.hi + 1] <= eyd) /* fixup approximative month value */ res.hi += 1; res.lo = eyd - lt[res.hi]; } else { res.lo = res.hi = -1; } return res; } /* *--------------------------------------------------------------------- * Convert a RD into the date part of a 'struct calendar'. * Returns -1 on calculation overflow. *--------------------------------------------------------------------- */ int ntpcal_rd_to_date( struct calendar *jd, int32_t rd ) { ntpcal_split split; int32_t leaps; int32_t retv; leaps = 0; retv = 0; /* Get day-of-week first. Since rd is signed, the remainder can * be in the range [-6..+6], but the assignment to an unsigned * variable maps the negative values to positive values >=7. * This makes the sign correction look strange, but adding 7 * causes the needed wrap-around into the desired value range of * zero to six, both inclusive. */ jd->weekday = rd % 7; if (jd->weekday >= 7) /* unsigned! */ jd->weekday += 7; split = ntpcal_split_eradays(rd - 1, &leaps); retv = (int)leaps; /* get year and day-of-year */ jd->year = (uint16_t)split.hi + 1; if (jd->year != split.hi + 1) { jd->year = 0; retv = -1; /* bletch. overflow trouble. */ } jd->yearday = (uint16_t)split.lo + 1; /* convert to month and mday */ split = ntpcal_split_yeardays(split.lo, leaps); jd->month = (uint8_t)split.hi + 1; jd->monthday = (uint8_t)split.lo + 1; return retv ? retv : leaps; } /* *--------------------------------------------------------------------- * Take a value of seconds since midnight and split it into hhmmss in a * 'struct calendar'. *--------------------------------------------------------------------- */ int32_t ntpcal_daysec_to_date( struct calendar *jd, int32_t sec ) { int32_t days; int ts[3]; days = priv_timesplit(ts, sec); jd->hour = (uint8_t)ts[0]; jd->minute = (uint8_t)ts[1]; jd->second = (uint8_t)ts[2]; return days; } /* *--------------------------------------------------------------------- * Take a UN*X time and convert to a calendar structure. *--------------------------------------------------------------------- */ int ntpcal_time_to_date( struct calendar *jd, const time64_t ts ) { ntpcal_split ds; ds = ntpcal_daysplit(ts); ds.hi += ntpcal_daysec_to_date(jd, ds.lo); ds.hi += DAY_UNIX_STARTS; return ntpcal_rd_to_date(jd, ds.hi); } /* * ================================================================== * * merging composite entities * * ================================================================== */ /* *--------------------------------------------------------------------- * Merge a number of days and a number of seconds into seconds, * expressed in 64 bits to avoid overflow. *--------------------------------------------------------------------- */ time64_t ntpcal_dayjoin( int32_t days, int32_t secs ) { time64_t res; settime64s(res, days); settime64s(res, time64s(res) * SECSPERDAY); settime64s(res, time64s(res) + secs); return res; } /* *--------------------------------------------------------------------- * Convert elapsed years in Era into elapsed days in Era. * * To accommodate for negative values of years, floor division would be * required for all division operations. This can be eased by first * splitting the years into full 400-year cycles and years in the * cycle. Only this operation must be coded as a full floor division; as * the years in the cycle is a non-negative number, all other divisions * can be regular truncated divisions. *--------------------------------------------------------------------- */ int32_t ntpcal_days_in_years( int32_t years ) { int32_t cycle; /* full gregorian cycle */ /* split off full calendar cycles, using floor division */ cycle = years / 400; years = years % 400; if (years < 0) { cycle -= 1; years += 400; } /* * Calculate days in cycle. years now is a non-negative number, * holding the number of years in the 400-year cycle. */ return cycle * GREGORIAN_CYCLE_DAYS + years * DAYSPERYEAR /* days inregular years */ + years / 4 /* 4 year leap rule */ - years / 100; /* 100 year leap rule */ /* the 400-year rule does not apply due to full-cycle split-off */ } /* *--------------------------------------------------------------------- * Convert a number of elapsed month in a year into elapsed days in year. * * The month will be normalized, and 'res.hi' will contain the * excessive years that must be considered when converting the years, * while 'res.lo' will contain the number of elapsed days since start * of the year. * * This code uses the shifted-month-approach to convert month to days, * because then there is no need to have explicit leap year * information. The slight disadvantage is that for most month values * the result is a negative value, and the year excess is one; the * conversion is then simply based on the start of the following year. *--------------------------------------------------------------------- */ static ntpcal_split ntpcal_days_in_months( int32_t m ) { ntpcal_split res; /* normalize month into range */ res.hi = 0; res.lo = m; if (res.lo < 0 || res.lo >= 12) { res.hi = res.lo / 12; res.lo = res.lo % 12; if (res.lo < 0) { res.hi -= 1; res.lo += 12; } } /* add 10 month for year starting with march */ if (res.lo < 2) res.lo += 10; else { res.hi += 1; res.lo -= 2; } /* get cummulated days in year with unshift */ res.lo = shift_month_table[res.lo] - 306; return res; } /* *--------------------------------------------------------------------- * Convert ELAPSED years/months/days of gregorian calendar to elapsed * days in Gregorian epoch. * * If you want to convert years and days-of-year, just give a month of * zero. *--------------------------------------------------------------------- */ int32_t ntpcal_edate_to_eradays( int32_t years, int32_t mons, int32_t mdays ) { ntpcal_split tmp; int32_t res; if (mons) { tmp = ntpcal_days_in_months(mons); res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo; } else res = ntpcal_days_in_years(years); res += mdays; return res; } /* *--------------------------------------------------------------------- * Convert ELAPSED years/months/days of gregorian calendar to elapsed * days in year. * * Note: This will give the true difference to the start of the given year, * even if months & days are off-scale. *--------------------------------------------------------------------- */ static int32_t ntpcal_edate_to_yeardays( int32_t years, int32_t mons, int32_t mdays ) { ntpcal_split tmp; if (0 <= mons && mons < 12) { years += 1; mdays += real_month_table[is_leapyear(years)][mons]; } else { tmp = ntpcal_days_in_months(mons); mdays += tmp.lo + ntpcal_days_in_years(years + tmp.hi) - ntpcal_days_in_years(years); } return mdays; } /* *--------------------------------------------------------------------- * Convert elapsed days and the hour/minute/second information into * total seconds. * * If 'isvalid' is not NULL, do a range check on the time specification * and tell if the time input is in the normal range, permitting for a * single leapsecond. *--------------------------------------------------------------------- */ int32_t ntpcal_etime_to_seconds( int32_t hours, int32_t minutes, int32_t seconds ) { int32_t res; res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds; return res; } /* *--------------------------------------------------------------------- * Convert the date part of a 'struct tm' (that is, year, month, * day-of-month) into the RD of that day. *--------------------------------------------------------------------- */ int32_t ntpcal_tm_to_rd( const struct tm *utm ) { return ntpcal_edate_to_eradays(utm->tm_year + 1899, utm->tm_mon, utm->tm_mday - 1) + 1; } /* *--------------------------------------------------------------------- * Convert the date part of a 'struct calendar' (that is, year, month, * day-of-month) into the RD of that day. *--------------------------------------------------------------------- */ int32_t ntpcal_date_to_rd( const struct calendar *jd ) { return ntpcal_edate_to_eradays((int32_t)jd->year - 1, (int32_t)jd->month - 1, (int32_t)jd->monthday - 1) + 1; } /* *--------------------------------------------------------------------- * take a 'struct calendar' and get the seconds-of-day from it. *--------------------------------------------------------------------- */ int32_t ntpcal_date_to_daysec( const struct calendar *jd ) { return ntpcal_etime_to_seconds(jd->hour, jd->minute, jd->second); } /* *--------------------------------------------------------------------- * take a 'struct tm' and get the seconds-of-day from it. *--------------------------------------------------------------------- */ int32_t ntpcal_tm_to_daysec( const struct tm *utm ) { return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, utm->tm_sec); } /* *--------------------------------------------------------------------- * take a 'struct calendar' and convert it to a 'time_t' *--------------------------------------------------------------------- */ time_t ntpcal_date_to_time( const struct calendar *jd ) { time64_t join; int32_t days, secs; days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS; secs = ntpcal_date_to_daysec(jd); join = ntpcal_dayjoin(days, secs); /* might truncate if time_t is 32 bits */ return (time_t)join; } int ntpcal_ntp64_to_date( struct calendar *jd, const time64_t ntp ) { ntpcal_split ds; ds = ntpcal_daysplit(ntp); ds.hi += ntpcal_daysec_to_date(jd, ds.lo); return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); } int ntpcal_ntp_to_date( struct calendar *jd, uint32_t ntp, const time_t piv ) { time64_t ntp64; /* * Unfold ntp time around current time into NTP domain. Split * into days and seconds, shift days into CE domain and * process the parts. */ ntp64 = ntpcal_ntp_to_ntp(ntp, piv); return ntpcal_ntp64_to_date(jd, ntp64); } /* * ymd2yd - compute the date in the year from y/m/d * * A thin wrapper around a more general calendar function. */ int ymd2yd( int y, int m, int d) { /* * convert y/m/d to elapsed calendar units, convert that to * elapsed days since the start of the given year and convert * back to unity-based day in year. * * This does no further error checking, since the underlying * function is assumed to work out how to handle the data. */ return ntpcal_edate_to_yeardays(y-1, m-1, d-1) + 1; } /* -*-EOF-*- */ ntpsec-1.1.0+dfsg1/libntp/ntp_random.c0000644000175000017500000000067313252364117017430 0ustar rlaagerrlaager/* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-4-clause */ #include #include #include "config.h" #include "ntp.h" #include "ntp_endian.h" int32_t ntp_random(void) { unsigned char rnd[sizeof(uint32_t)]; RAND_bytes(rnd, sizeof(rnd)); return (int32_t)ntp_be32dec(rnd); } ntpsec-1.1.0+dfsg1/libntp/macencrypt.c0000644000175000017500000001012613252364117017426 0ustar rlaagerrlaager/* * digest support for NTP */ #include "config.h" #include #include #include #include #include /* provides OpenSSL digest API */ #include #include "ntp_fp.h" #include "ntp_stdlib.h" #include "ntp.h" #ifndef EVP_MD_CTX_reset /* Slightly older version of OpenSSL */ /* Similar hack in ssl_init.c and attic/digest-timing.c */ #define EVP_MD_CTX_reset(ctx) EVP_MD_CTX_init(ctx) #endif /* Need one per thread. */ extern EVP_MD_CTX *digest_ctx; /* ctmemeq - test two blocks memory for equality without leaking * timing information. * * Return value: true if the two blocks of memory are equal, false * otherwise. * * TODO: find out if this is useful elsewhere and if so move * it to a more appropriate place and give it a prototype in a * header file. */ static bool ctmemeq(const void *s1, const void *s2, size_t n) { const uint8_t *a = s1; const uint8_t *b = s2; uint8_t accum = 0; for(size_t i = 0; i < n; i++) { accum |= a[i] ^ b[i]; } return accum == 0; } /* * mac_authencrypt - generate message digest * * Returns length of MAC including key ID and digest. */ int mac_authencrypt( int type, /* hash algorithm */ uint8_t *key, /* key pointer */ int key_size, /* key size */ uint32_t *pkt, /* packet pointer */ int length /* packet length */ ) { uint8_t digest[EVP_MAX_MD_SIZE]; unsigned int len; EVP_MD_CTX *ctx = digest_ctx; /* * Compute digest of key concatenated with packet. Note: the * key type and digest type have been verified when the key * was created. */ EVP_MD_CTX_reset(ctx); if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(type), NULL)) { msyslog(LOG_ERR, "MAC: encrypt: digest init failed"); return (0); } EVP_DigestUpdate(ctx, key, key_size); EVP_DigestUpdate(ctx, (uint8_t *)pkt, (unsigned int)length); EVP_DigestFinal_ex(ctx, digest, &len); if (MAX_BARE_DIGEST_LENGTH < len) len = MAX_BARE_DIGEST_LENGTH; memmove((uint8_t *)pkt + length + 4, digest, len); return (int)(len + 4); } /* * mac_authdecrypt - verify message authenticator * * Returns true if digest valid, false if invalid. */ bool mac_authdecrypt( int type, /* hash algorithm */ uint8_t *key, /* key pointer */ int key_size, /* key size */ uint32_t *pkt, /* packet pointer */ int length, /* packet length */ int size /* MAC size */ ) { uint8_t digest[EVP_MAX_MD_SIZE]; unsigned int len; EVP_MD_CTX *ctx = digest_ctx; /* * Compute digest of key concatenated with packet. Note: the * key type and digest type have been verified when the key * was created. */ EVP_MD_CTX_reset(ctx); if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(type), NULL)) { msyslog(LOG_ERR, "MAC: decrypt: digest init failed"); return false; } EVP_DigestUpdate(ctx, key, key_size); EVP_DigestUpdate(ctx, (uint8_t *)pkt, (unsigned int)length); EVP_DigestFinal_ex(ctx, digest, &len); if (MAX_BARE_DIGEST_LENGTH < len) len = MAX_BARE_DIGEST_LENGTH; if ((unsigned int)size != len + 4) { msyslog(LOG_ERR, "MAC: decrypt: MAC length error"); return false; } return ctmemeq(digest, (char *)pkt + length + 4, len); } /* * Calculate the reference id from the address. If it is an IPv4 * address, use it as is. If it is an IPv6 address, do a md5 on * it and use the top 4 bytes. (Note: NTP Classic claimed it used * the *bottom* 4 bytes, but that didn't match the code or the test.) * The result is in network byte order. */ uint32_t addr2refid(sockaddr_u *addr) { uint8_t digest[MD5_DIGEST_LENGTH]; uint32_t addr_refid; EVP_MD_CTX *ctx; unsigned int len; if (IS_IPV4(addr)) return (NSRCADR(addr)); ctx = EVP_MD_CTX_create(); #ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW /* MD5 is not used as a crypto hash here. */ EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); #endif if (!EVP_DigestInit_ex(ctx, EVP_md5(), NULL)) { msyslog(LOG_ERR, "MAC: MD5 init failed"); exit(1); } EVP_DigestUpdate(ctx, (uint8_t *)PSOCK_ADDR6(addr), sizeof(struct in6_addr)); EVP_DigestFinal_ex(ctx, digest, &len); EVP_MD_CTX_destroy(ctx); memcpy(&addr_refid, digest, sizeof(addr_refid)); return (addr_refid); } ntpsec-1.1.0+dfsg1/libntp/prettydate.c0000644000175000017500000001250513252364117017451 0ustar rlaagerrlaager/* * prettydate - convert a time stamp to something readable */ #include "config.h" #include #include #include "ntp_fp.h" #include "lib_strbuf.h" #include "ntp_stdlib.h" #include "ntp_calendar.h" #if NTP_SIZEOF_TIME_T < 4 # error sizeof(time_t) < 4 -- this will not work! #endif /* Helper function to handle possible wraparound of the ntp epoch. * * Works by periodic extension of the ntp time stamp in the UN*X epoch. * If the 'time_t' is 32 bit, use solar cycle warping to get the value * in a suitable range. Also uses solar cycle warping to work around * really buggy implementations of 'gmtime_r()' / 'localtime_r()' that * cannot work with a negative time value, that is, times before * 1970-01-01. (MSVCRT...) * * Apart from that we're assuming that the localtime/gmtime library * functions have been updated so that they work... * * An explanation: The julian calendar repeats ever 28 years, because * it's the LCM of 7 and 1461, the week and leap year cycles. This is * called a 'solar cycle'. The gregorian calendar does the same as * long as no centennial year (divisible by 100, but not 400) goes in * the way. So between 1901 and 2099 (inclusive) we can warp time * stamps by 28 years to make them suitable for localtime_r() and * gmtime_r() if we have trouble. Of course this will play hubbubb with * the DST zone switches, so we should do it only if necessary; but as * we NEED a proper conversion to dates via gmtime_r() we should try to * cope with as many idiosyncrasies as possible. * */ /* * solar cycle in unsigned secs and years, and the cycle limits. */ #define SOLAR_CYCLE_SECS (time_t)0x34AADC80 /* 7*1461*86400*/ #define SOLAR_CYCLE_YEARS 28 #define MINFOLD -3 #define MAXFOLD 3 static struct tm * get_struct_tm( const time64_t *stamp, struct tm *tmbuf) { struct tm *tm; int32_t folds = 0; time_t ts; int64_t tl; ts = tl = time64s(*stamp); /* * If there is chance of truncation, try to fix it. Let the * compiler find out if this can happen at all. */ while (ts != tl) { /* truncation? */ if (tl < 0) { if (--folds < MINFOLD) return NULL; tl += SOLAR_CYCLE_SECS; } else { if (++folds > MAXFOLD) return NULL; tl -= SOLAR_CYCLE_SECS; } ts = tl; /* next try... */ } /* * 'ts' should be a suitable value by now. Just go ahead, but * with care: * * There are some pathological implementations of 'gmtime_r()' * and 'localtime_r()' out there. No matter if we have 32-bit or * 64-bit 'time_t', try to fix this by solar cycle warping * again... * * At least the MSDN says that the (Microsoft) Windoze * versions of 'gmtime_r()' and 'localtime_r()' will bark on time * stamps < 0. * * ESR, 2017: Using localtime(3) for logging at all is bogus - * in particular, it is bad for reproducibility. */ while ((tm = gmtime_r(&ts, tmbuf)) == NULL) if (ts < 0) { if (--folds < MINFOLD) return NULL; ts += SOLAR_CYCLE_SECS; } else if (ts >= (time_t)SOLAR_CYCLE_SECS) { if (++folds > MAXFOLD) return NULL; ts -= SOLAR_CYCLE_SECS; } else return NULL; /* That's truly pathological! */ /* 'tm' surely not NULL here! */ if (folds != 0) { tm->tm_year += folds * SOLAR_CYCLE_YEARS; if (tm->tm_year <= 0 || tm->tm_year >= 200) return NULL; /* left warp range... can't help here! */ } return tm; } static time_t prettypivot; void set_prettydate_pivot(time_t pivot) { prettypivot = pivot; } static char * common_prettydate( const l_fp ts ) { static const char pfmt[] = "%08lx.%08lx %04d-%02d-%02dT%02d:%02d:%02d.%03u"; char *bp; struct tm *tm, tmbuf; unsigned int msec; uint32_t ntps; time64_t sec; bp = lib_getbuf(); /* get & fix milliseconds */ ntps = lfpuint(ts); msec = lfpfrac(ts) / 4294967; /* fract / (2 ** 32 / 1000) */ if (msec >= 1000u) { msec -= 1000u; ntps++; } sec = ntpcal_ntp_to_time(ntps, prettypivot); tm = get_struct_tm(&sec, &tmbuf); if (!tm) { /* * get a replacement, but always in UTC, using * ntpcal_time_to_date() */ struct calendar jd; ntpcal_time_to_date(&jd, sec); snprintf(bp, LIB_BUFLENGTH, pfmt, (unsigned long)lfpuint(ts), (unsigned long)lfpfrac(ts), jd.year, jd.month, jd.monthday, jd.hour, jd.minute, jd.second, msec); strncat(bp, "Z", LIB_BUFLENGTH); } else { snprintf(bp, LIB_BUFLENGTH, pfmt, (unsigned long)lfpuint(ts), (unsigned long)lfpfrac(ts), 1900 + tm->tm_year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, msec); strncat(bp, "Z", LIB_BUFLENGTH); } return bp; } char * prettydate( const l_fp ts ) { return common_prettydate(ts); } char * rfc3339date( const l_fp ts ) { return common_prettydate(ts) + 18; /* skip past hex time */ } /* * rfc3339time - prettyprint time stamp - POSIX epoch */ char * rfc3339time( time_t posix_stamp ) { char * buf; struct tm tm, *tm2; buf = lib_getbuf(); tm2 = gmtime_r(&posix_stamp, &tm); if (tm2 == NULL || tm.tm_year > 9999) snprintf(buf, LIB_BUFLENGTH, "rfc3339time: %ld: range error", (long)posix_stamp); // if (ntpcal_ntp_to_date(&tm, (uint32_t)ntp_stamp, NULL) < 0) // snprintf(buf, LIB_BUFLENGTH, "ntpcal_ntp_to_date: %ld: range error", // (long)ntp_stamp); else snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02dZ", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min); return buf; } /* end */ ntpsec-1.1.0+dfsg1/libntp/initnetwork.c0000644000175000017500000000056013252364117017637 0ustar rlaagerrlaager/* * lib_strbuf - library string storage */ #include "config.h" #include "isc_netaddr.h" #include "ntp_stdlib.h" /* * Storage declarations */ bool ipv4_works; bool ipv6_works; /* * initialization routine. Might be needed if the code is ROMized. */ void init_network(void) { ipv4_works = isc_net_probeipv4_bool(); ipv6_works = isc_net_probeipv6_bool(); } ntpsec-1.1.0+dfsg1/libntp/refidsmear.c0000644000175000017500000000144013252364117017401 0ustar rlaagerrlaager#include "config.h" #include "ntp.h" #include "ntp_fp.h" /* * we want to test a refid format of: * 254.x.y.x * * where x.y.z are 24 bits containing 2 (signed) integer bits * and 22 fractional bits. * */ uint32_t convertLFPToRefID(l_fp num) { uint32_t temp; /* round the input with the highest bit to shift out from the * fraction, then keep just two bits from the integral part. * * TODO: check for overflows; should we clamp/saturate or just * complain? */ setlfpfrac(num, lfpfrac(num) + 0x200); /* combine integral and fractional part to 24 bits */ temp = ((lfpuint(num) & 3) << 22) | (lfpfrac(num) >> 10); /* put in the leading 254.0.0.0 */ temp |= UINT32_C(0xFE000000); // printf("%03d %08x: ", (temp >> 24) & 0xFF, (temp & 0x00FFFFFF) ); return htonl(temp); } ntpsec-1.1.0+dfsg1/libntp/socket.c0000644000175000017500000000650713252364117016561 0ustar rlaagerrlaager/* * socket.c - low-level socket operations */ #include "config.h" #include #include "ntp.h" #include "ntp_io.h" #include "ntp_net.h" #include "ntp_debug.h" /* * on Unix systems the stdio library typically * makes use of file descriptors in the lower * integer range. stdio usually will make use * of the file descriptors in the range of * [0..FOPEN_MAX] * in order to keep this range clean, for socket * file descriptors we attempt to move them above * FOPEN_MAX. This is not as easy as it sounds as * FOPEN_MAX changes from implementation to implementation * and may exceed the current file descriptor limits. * We are using following strategy: * - keep a current socket fd boundary initialized with * max(0, min(sysconf(_SC_OPEN_MAX) - FD_CHUNK, FOPEN_MAX)) * - attempt to move the descriptor to the boundary or * above. * - if that fails and boundary > 0 set boundary * to min(0, socket_fd_boundary - FD_CHUNK) * -> retry * if failure and boundary == 0 return old fd * - on success close old fd return new fd * * effects: * - fds will be moved above the socket fd boundary * if at all possible. * - the socket boundary will be reduced until * allocation is possible or 0 is reached - at this * point the algrithm will be disabled */ SOCKET move_fd( SOCKET fd ) { #if defined(F_DUPFD) #ifdef OVERRIDE_FD_CHUNK #define FD_CHUNK OVERRIDE_FD_CHUNK #else #define FD_CHUNK 10 #endif /* * number of fds we would like to have for * stdio FILE* available. * we can pick a "low" number as our use of * FILE* is limited to log files and temporarily * to data and config files. Except for log files * we don't keep the other FILE* open beyond the * scope of the function that opened it. */ #ifdef OVERRIDE_FD_PREFERRED_SOCKBOUNDARY #define FD_PREFERRED_SOCKBOUNDARY OVERRIDE_FD_PREFERRED_SOCKBOUNDARY #else #define FD_PREFERRED_SOCKBOUNDARY 48 #endif static SOCKET socket_boundary = -1; SOCKET newfd; REQUIRE((int)fd >= 0); /* * check whether boundary has be set up * already */ if (socket_boundary == -1) { socket_boundary = max(0, min(sysconf(_SC_OPEN_MAX) - FD_CHUNK, min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY))); TPRINT(1, ("move_fd: estimated max descriptors: %d, " "initial socket boundary: %d\n", (int)sysconf(_SC_OPEN_MAX), socket_boundary)); } /* * Leave a space for stdio to work in. potentially moving the * socket_boundary lower until allocation succeeds. */ do { if (fd >= 0 && fd < socket_boundary) { /* inside reserved range: attempt to move fd */ newfd = fcntl(fd, F_DUPFD, socket_boundary); if (newfd != -1) { /* success: drop the old one - return the new one */ close(fd); return newfd; } } else { /* outside reserved range: no work - return the original one */ return fd; } socket_boundary = max(0, socket_boundary - FD_CHUNK); TPRINT(1, ("move_fd: selecting new socket boundary: %d\n", socket_boundary)); } while (socket_boundary > 0); #else REQUIRE((int)fd >= 0); #endif /* defined(F_DUPFD) */ return fd; } /* * make_socket_nonblocking() - set up descriptor to be non blocking */ void make_socket_nonblocking( SOCKET fd ) { /* * set non-blocking, */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { msyslog(LOG_ERR, "ERR: fcntl(O_NONBLOCK) fails on fd #%d: %m", fd); exit(1); } } ntpsec-1.1.0+dfsg1/libntp/getopt.c0000644000175000017500000001507413252364117016572 0ustar rlaagerrlaager#include #include "config.h" #include "ntp_machine.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_stdlib.h" #define no_argument 0 #define required_argument 1 /* const int optional_argument = 2; UNUSED */ char* ntp_optarg; static int ntp_optopt; /* The variable ntp_optind [...] shall be initialized to 1 by the system. */ int ntp_optind = 1; static int ntp_opterr; /* Implemented based on [1] and [2] for optional arguments. ntp_optopt is handled FreeBSD-style, per [3]. Other GNU and FreeBSD extensions are purely accidental. [1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html [2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html [3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE */ int ntp_getopt(int argc, char *const argv[], const char *optstring) { int optchar = -1; const char* optdecl = NULL; static char* optcursor = NULL; /* might not need to be static */ ntp_optarg = NULL; ntp_opterr = 0; ntp_optopt = 0; /* Unspecified, but we need it to avoid overrunning the argv bounds. */ if (ntp_optind >= argc) { optcursor = NULL; return -1; } /* If, when getopt() is called argv[ntp_optind] is a null pointer, getopt() shall return -1 without changing ntp_optind. */ if (argv[ntp_optind] == NULL) { optcursor = NULL; return -1; } /* If, when getopt() is called *argv[ntp_optind] is not the character '-', getopt() shall return -1 without changing ntp_optind. */ if (*argv[ntp_optind] != '-') { optcursor = NULL; return -1; } /* If, when getopt() is called argv[ntp_optind] points to the string "-", getopt() shall return -1 without changing ntp_optind. */ if (strcmp(argv[ntp_optind], "-") == 0) { optcursor = NULL; return -1; } /* If, when getopt() is called argv[ntp_optind] points to the string "--", getopt() shall return -1 after incrementing ntp_optind. */ if (strcmp(argv[ntp_optind], "--") == 0) { ++ntp_optind; optcursor = NULL; return -1; } if (optcursor == NULL || *optcursor == '\0') optcursor = argv[ntp_optind] + 1; optchar = *optcursor; /* FreeBSD: The variable ntp_optopt saves the last known option character returned by getopt(). */ ntp_optopt = optchar; /* The getopt() function shall return the next option character (if one is found) from argv that matches a character in optstring, if there is one that matches. */ optdecl = strchr(optstring, optchar); if (optdecl) { /* [I]f a character is followed by a colon, the option takes an argument. */ if (optdecl[1] == ':') { ntp_optarg = ++optcursor; if (*ntp_optarg == '\0') { /* GNU extension: Two colons mean an option takes an optional arg; if there is text in the current argv-element (i.e., in the same word as the option name itself, for example, "-oarg"), then it is returned in ntp_optarg, otherwise ntp_optarg is set to zero. */ if (optdecl[2] != ':') { /* If the option was the last character in the string pointed to by an element of argv, then ntp_optarg shall contain the next element of argv, and ntp_optind shall be incremented by 2. If the resulting value of ntp_optind is greater than argc, this indicates a missing option-argument, and getopt() shall return an error indication. Otherwise, ntp_optarg shall point to the string following the option character in that element of argv, and ntp_optind shall be incremented by 1. */ if (++ntp_optind < argc) { ntp_optarg = argv[ntp_optind]; } else { /* If it detects a missing option-argument, it shall return the colon character ( ':' ) if the first character of optstring was a colon, or a question-mark character ( '?' ) otherwise. */ ntp_optarg = NULL; optchar = (optstring[0] == ':') ? ':' : '?'; } } else { ntp_optarg = NULL; } } optcursor = NULL; } } else { /* If getopt() encounters an option character that is not contained in optstring, it shall return the question-mark ( '?' ) character. */ optchar = '?'; } if (optcursor == NULL || *++optcursor == '\0') ++ntp_optind; return optchar; } /* Implementation based on [1]. [1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html */ int ntp_getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex) { const struct option* o = longopts; const struct option* match = NULL; int num_matches = 0; size_t argument_name_length = 0; const char* current_argument = NULL; int retval = -1; ntp_optarg = NULL; ntp_optopt = 0; if (ntp_optind >= argc) return -1; if (strlen(argv[ntp_optind]) < 3 || strncmp(argv[ntp_optind], "--", 2) != 0) return ntp_getopt(argc, argv, optstring); /* It's an option; starts with -- and is longer than two chars. */ current_argument = argv[ntp_optind] + 2; argument_name_length = strcspn(current_argument, "="); for (; o->name; ++o) { if (strncmp(o->name, current_argument, argument_name_length) == 0) { match = o; ++num_matches; } } if (num_matches == 1) { /* If longindex is not NULL, it points to a variable which is set to the index of the long option relative to longopts. */ if (longindex) *longindex = (int)(match - longopts); /* If flag is NULL, then getopt_long() shall return val. Otherwise, getopt_long() returns 0, and flag shall point to a variable which shall be set to val if the option is found, but left unchanged if the option is not found. */ if (match->flag) *(match->flag) = match->val; retval = match->flag ? 0 : match->val; if (match->has_arg != no_argument) { ntp_optarg = strchr(argv[ntp_optind], '='); if (ntp_optarg != NULL) ++ntp_optarg; if (match->has_arg == required_argument) { /* Only scan the next argv for required arguments. Behavior is not specified, but has been observed with Ubuntu and Mac OSX. */ if (ntp_optarg == NULL && ++ntp_optind < argc) { ntp_optarg = argv[ntp_optind]; } if (ntp_optarg == NULL) retval = ':'; } } else if (strchr(argv[ntp_optind], '=')) { /* An argument was provided to a non-argument option. I haven't seen this specified explicitly, but both GNU and BSD-based implementations show this behavior. */ retval = '?'; } } else { /* Unknown option or ambiguous match. */ retval = '?'; } ++ntp_optind; return retval; } ntpsec-1.1.0+dfsg1/libntp/emalloc.c0000644000175000017500000000647213252364117016706 0ustar rlaagerrlaager/* * emalloc - return new memory obtained from the system. Belch if none. */ #include "config.h" #include "ntp_types.h" #include "ntp_malloc.h" #include "ntp_syslog.h" #include "ntp_stdlib.h" /* * When using the debug MS CRT allocator, each allocation stores the * callsite __FILE__ and __LINE__, which is then displayed at process * termination, to track down leaks. We don't want all of our * allocations to show up as coming from emalloc.c, so we preserve the * original callsite's source file and line using macros which pass * __FILE__ and __LINE__ as parameters to these routines. * Other debug malloc implementations can be used by defining * EREALLOC_IMPL() as ports/winnt/include/config.h does. */ void * ereallocz( void * ptr, size_t newsz, size_t priorsz, int zero_init #ifdef EREALLOC_CALLSITE /* ntp_malloc.h */ , const char * file, int line #endif ) { char * mem; size_t allocsz; if (0 == newsz) allocsz = 1; else allocsz = newsz; mem = EREALLOC_IMPL(ptr, allocsz, file, line); if (NULL == mem) { termlogit = true; #ifndef EREALLOC_CALLSITE msyslog(LOG_ERR, "ERR: fatal out of memory (%lu bytes)", (unsigned long)newsz); #else msyslog(LOG_ERR, "ERR: fatal out of memory %s line %d (%lu bytes)", file, line, (unsigned long)newsz); #endif exit(1); } if (zero_init && newsz > priorsz) memset(mem + priorsz, '\0', newsz - priorsz); return mem; } /* oreallocarray.c is licensed under the following: * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * oreallocarray( void *optr, size_t nmemb, size_t size #ifdef EREALLOC_CALLSITE /* ntp_malloc.h */ , const char * file, int line #endif ) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { #ifndef EREALLOC_CALLSITE msyslog(LOG_ERR, "ERR: fatal allocation size overflow"); #else msyslog(LOG_ERR, "ERR: fatal allocation size overflow %s line %d", file, line); #endif exit(1); } #ifndef EREALLOC_CALLSITE return ereallocz(optr, (size * nmemb), 0, false); #else return ereallocz(optr, (size * nmemb), 0, false, file, line); #endif } char * estrdup_impl( const char * str #ifdef EREALLOC_CALLSITE , const char * file, int line #endif ) { char * copy; size_t bytes; bytes = strlen(str) + 1; copy = ereallocz(NULL, bytes, 0, false #ifdef EREALLOC_CALLSITE , file, line #endif ); memcpy(copy, str, bytes); return copy; } ntpsec-1.1.0+dfsg1/libntp/netof.c0000644000175000017500000000160113252364117016372 0ustar rlaagerrlaager/* * netof6 - return the net address part of an IPv6 address * in a sockaddr_storage structure (zero out host part) * * except it does not really do that, it ASSumes /64 * * returns points to a 8 position static array. Each * position used in turn. * */ #include "config.h" #include #include #include "ntp_net.h" #include "ntp_stdlib.h" #include "ntp.h" sockaddr_u * netof6( sockaddr_u *hostaddr ) { static sockaddr_u netofbuf[8]; static int next_netofbuf; sockaddr_u * netaddr; netaddr = &netofbuf[next_netofbuf]; next_netofbuf = (next_netofbuf + 1) % (int)COUNTOF(netofbuf); memcpy(netaddr, hostaddr, sizeof(*netaddr)); if (IS_IPV6(netaddr)) /* assume the typical /64 subnet size */ memset(&NSRCADR6(netaddr)[8], '\0', 8); #ifdef DEBUG else { msyslog(LOG_ERR, "ERR: Not IPv6, AF %d", AF(netaddr)); exit(1); } #endif return netaddr; } ntpsec-1.1.0+dfsg1/libntp/ntp_dns.c0000644000175000017500000001000713252364117016724 0ustar rlaagerrlaager/* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: BSD-4-clause */ #include "config.h" #ifdef ENABLE_DNS_LOOKUP #include #include #include #include #ifdef HAVE_RES_INIT #include #include #include #endif #include "ntpd.h" #include "ntp_dns.h" /* Notes: Only one DNS lookup active at a time peer->srcadr holds IPv4/IPv6/UNSPEC flag peer->hmode holds DNS retry time (log 2) FLAG_DNS used in server case to indicate need DNS Server can't lookup again after finding an answer answer uses same peer slot, turns off FLAG_DNS srcadr+hmode changed by normal code server can't lookup again if answer stops responding Pool case makes new peer slots, pool slot unchanged so OK for another lookup */ static struct peer* active = NULL; /* busy flag */ static pthread_t worker; static int gai_rc; static struct addrinfo *answer; static void* dns_lookup(void* arg); bool dns_probe(struct peer* pp) { int rc; sigset_t block_mask, saved_sig_mask; const char * busy = ""; /* Comment out the next two lines to get (much) more * printout when we are busy. */ if (NULL != active) return false; if (NULL != active) busy = ", busy"; msyslog(LOG_INFO, "DNS: dns_probe: %s, cast_flags:%x, flags:%x%s", pp->hostname, pp->cast_flags, pp->cfg.flags, busy); if (NULL != active) /* normally redundant */ return false; active = pp; sigfillset(&block_mask); pthread_sigmask(SIG_BLOCK, &block_mask, &saved_sig_mask); rc = pthread_create(&worker, NULL, dns_lookup, pp); if (rc) { errno = rc; msyslog(LOG_ERR, "DNS: dns_probe: error from pthread_create: %s, %m", pp->hostname); return true; /* don't try again */ } pthread_sigmask(SIG_SETMASK, &saved_sig_mask, NULL); return true; }; void dns_check(void) { int rc; struct addrinfo *ai; DNS_Status status; msyslog(LOG_INFO, "DNS: dns_check: processing %s, %x, %x", active->hostname, active->cast_flags, (unsigned int)active->cfg.flags); rc = pthread_join(worker, NULL); if (0 != rc) { msyslog(LOG_ERR, "DNS: dns_check: join failed %m"); return; /* leaves active set */ } if (0 != gai_rc) { msyslog(LOG_INFO, "DNS: dns_check: DNS error: %d, %s", gai_rc, gai_strerror(gai_rc)); answer = NULL; } for (ai = answer; NULL != ai; ai = ai->ai_next) { sockaddr_u sockaddr; memcpy(&sockaddr, ai->ai_addr, ai->ai_addrlen); /* Both dns_take_pool and dns_take_server log something. */ // msyslog(LOG_INFO, "DNS: Take %s=>%s", // socktoa(ai->ai_addr), socktoa(&sockaddr)); if (active->cast_flags & MDF_POOL) dns_take_pool(active, &sockaddr); else dns_take_server(active, &sockaddr); } switch (gai_rc) { case 0: status = DNS_good; break; case EAI_AGAIN: status = DNS_temp; break; /* Treat all other errors as permanent. * Some values from man page weren't in headers. */ default: status = DNS_error; } dns_take_status(active, status); if (NULL != answer) freeaddrinfo(answer); active = NULL; }; /* Beware: no calls to msyslog from here. * It's not thread safe. * This is the only other thread in ntpd. */ static void* dns_lookup(void* arg) { struct peer *pp = (struct peer *) arg; struct addrinfo hints; #ifdef HAVE_RES_INIT /* Reload DNS servers from /etc/resolv.conf in case DHCP has updated it. * We only need to do this occasionally, but it's not expensive * and simpler to do it every time than it is to figure out when * to do it. */ res_init(); #endif ZERO(hints); hints.ai_protocol = IPPROTO_UDP; hints.ai_socktype = SOCK_DGRAM; hints.ai_family = AF(&pp->srcadr); gai_rc = getaddrinfo(pp->hostname, "ntp", &hints, &answer); kill(getpid(), SIGDNS); pthread_exit(NULL); /* Prevent compiler warning. * More portable than an attribute or directive */ return (void *)NULL; }; #endif /* ENABLE_DNS_LOOKUP */ ntpsec-1.1.0+dfsg1/libntp/numtoa.c0000644000175000017500000000166613252364117016575 0ustar rlaagerrlaager/* * numtoa - return asciized network numbers store in local array space */ #include "config.h" #include #include /* ntohl */ #include #include "ntp_fp.h" #include "lib_strbuf.h" #include "ntp_stdlib.h" char * numtoa( uint32_t num ) { uint32_t netnum; char *buf; netnum = ntohl(num); buf = lib_getbuf(); snprintf(buf, LIB_BUFLENGTH, "%lu.%lu.%lu.%lu", ((unsigned long)netnum >> 24) & 0xff, ((unsigned long)netnum >> 16) & 0xff, ((unsigned long)netnum >> 8) & 0xff, (unsigned long)netnum & 0xff); return buf; } /* Convert a refid & stratum to a string */ const char * refid_str( uint32_t refid, int stratum ) { char * text; size_t tlen; if (stratum > 1) return numtoa(refid); text = lib_getbuf(); text[0] = '.'; memcpy(&text[1], &refid, sizeof(refid)); text[1 + sizeof(refid)] = '\0'; tlen = strlen(text); text[tlen] = '.'; text[tlen + 1] = '\0'; return text; } ntpsec-1.1.0+dfsg1/libntp/authkeys.c0000644000175000017500000003002613252364117017117 0ustar rlaagerrlaager/* * authkeys.c - routines to manage the storage of authentication keys */ #include "config.h" #include #include #include #include "ntp.h" #include "ntpd.h" #include "ntp_lists.h" #include "ntp_malloc.h" #include "ntp_stdlib.h" /* * Structure to store keys in in the hash table. */ typedef struct savekey symkey; struct savekey { symkey * hlink; /* next in hash bucket */ DECL_DLIST_LINK(symkey, llink); /* for overall & free lists */ uint8_t * secret; /* shared secret */ keyid_t keyid; /* key identifier */ unsigned short type; /* OpenSSL digest NID */ unsigned short secretsize; /* secret octets */ unsigned short flags; /* KEY_ flags that wave */ }; /* define the payload region of symkey beyond the list pointers */ #define symkey_payload secret #define KEY_TRUSTED 0x001 /* this key is trusted */ #ifdef DEBUG typedef struct symkey_alloc_tag symkey_alloc; struct symkey_alloc_tag { symkey_alloc * link; void * mem; /* enable free() atexit */ }; symkey_alloc * authallocs; #endif /* DEBUG */ static inline unsigned short auth_log2(double x); static void auth_moremem (int); static void auth_resize_hashtable(void); static void allocsymkey(symkey **, keyid_t, unsigned short, unsigned short, unsigned short, uint8_t *); static void freesymkey(symkey *, symkey **); #ifdef DEBUG static void free_auth_mem(void); #endif static symkey key_listhead; /* list of all in-use keys */ /* * The hash table. This is indexed by the low order bits of the * keyid. This gets updated in auth_resize_hashtable */ #define KEYHASH(keyid) ((keyid) & authhashmask) #define INIT_AUTHHASHSIZE 64 static unsigned short authhashbuckets = INIT_AUTHHASHSIZE; static unsigned short authhashmask = INIT_AUTHHASHSIZE - 1; static symkey **key_hash; unsigned int authkeynotfound; /* keys not found */ unsigned int authkeylookups; /* calls to lookup keys */ unsigned int authnumkeys; /* number of active keys */ unsigned int authkeyuncached; /* cache misses */ static unsigned int authnokey; /* calls to encrypt with no key */ unsigned int authencryptions; /* calls to encrypt */ unsigned int authdecryptions; /* calls to decrypt */ /* * Storage for free symkey structures. We malloc() such things but * never free them. */ static symkey *authfreekeys; int authnumfreekeys; #define MEMINC 16 /* number of new free ones to get */ /* * The key cache. We cache the last key we looked at here. */ static keyid_t cache_keyid; /* key identifier */ static uint8_t *cache_secret; /* secret */ static unsigned short cache_secretsize; /* secret length */ static int cache_type; /* OpenSSL digest NID */ static unsigned short cache_flags; /* flags that wave */ /* * init_auth - initialize internal data */ void init_auth(void) { size_t newalloc; /* * Initialize hash table and free list */ newalloc = authhashbuckets * sizeof(key_hash[0]); key_hash = erealloc(key_hash, newalloc); memset(key_hash, '\0', newalloc); INIT_DLIST(key_listhead, llink); #ifdef DEBUG atexit(&free_auth_mem); #endif } /* * free_auth_mem - assist in leak detection by freeing all dynamic * allocations from this module. */ #ifdef DEBUG static void free_auth_mem(void) { symkey * sk; symkey_alloc * alloc; symkey_alloc * next_alloc; while (NULL != (sk = HEAD_DLIST(key_listhead, llink))) { freesymkey(sk, &key_hash[KEYHASH(sk->keyid)]); } free(key_hash); key_hash = NULL; cache_keyid = 0; cache_flags = 0; for (alloc = authallocs; alloc != NULL; alloc = next_alloc) { next_alloc = alloc->link; free(alloc->mem); } authfreekeys = NULL; authnumfreekeys = 0; } #endif /* DEBUG */ /* * auth_moremem - get some more free key structures */ static void auth_moremem( int keycount ) { symkey * sk; int i; #ifdef DEBUG void * base; symkey_alloc * allocrec; # define MOREMEM_EXTRA_ALLOC (sizeof(*allocrec)) #else # define MOREMEM_EXTRA_ALLOC (0) #endif i = (keycount > 0) ? keycount : MEMINC; sk = emalloc_zero((unsigned int)i * sizeof(*sk) + MOREMEM_EXTRA_ALLOC); #ifdef DEBUG base = sk; #endif authnumfreekeys += i; for (; i > 0; i--, sk++) { LINK_SLIST(authfreekeys, sk, llink.f); } #ifdef DEBUG allocrec = (void *)sk; allocrec->mem = base; LINK_SLIST(authallocs, allocrec, link); #endif } /* * auth_prealloc_symkeys */ void auth_prealloc_symkeys( int keycount ) { int allocated; int additional; allocated = (int)authnumkeys + authnumfreekeys; additional = keycount - allocated; if (additional > 0) auth_moremem(additional); auth_resize_hashtable(); } static inline unsigned short auth_log2(double x) { return (unsigned short)(log10(x) / log10(2)); } /* * auth_resize_hashtable * * Size hash table to average 4 or fewer entries per bucket initially, * within the bounds of at least 4 and no more than 15 bits for the hash * table index. Populate the hash table. */ static void auth_resize_hashtable(void) { unsigned int totalkeys; unsigned short hashbits; unsigned short hash; size_t newalloc; symkey * sk; totalkeys = authnumkeys + (unsigned int)authnumfreekeys; hashbits = auth_log2(totalkeys / 4.0) + 1; hashbits = max(4, hashbits); hashbits = min(15, hashbits); authhashbuckets = 1 << hashbits; authhashmask = authhashbuckets - 1; newalloc = authhashbuckets * sizeof(key_hash[0]); key_hash = erealloc(key_hash, newalloc); memset(key_hash, '\0', newalloc); ITER_DLIST_BEGIN(key_listhead, sk, llink, symkey) hash = KEYHASH(sk->keyid); LINK_SLIST(key_hash[hash], sk, hlink); ITER_DLIST_END() } /* * allocsymkey - common code to allocate and link in symkey * * secret must be allocated with a free-compatible allocator. It is * owned by the referring symkey structure, and will be free()d by * freesymkey(). */ static void allocsymkey( symkey ** bucket, keyid_t id, unsigned short flags, unsigned short type, unsigned short secretsize, uint8_t * secret ) { symkey * sk; if (authnumfreekeys < 1) auth_moremem(-1); UNLINK_HEAD_SLIST(sk, authfreekeys, llink.f); //ENSURE(sk != NULL); sk->keyid = id; sk->flags = flags; sk->type = type; sk->secretsize = secretsize; sk->secret = secret; LINK_SLIST(*bucket, sk, hlink); LINK_TAIL_DLIST(key_listhead, sk, llink); authnumfreekeys--; authnumkeys++; } /* * freesymkey - common code to remove a symkey and recycle its entry. */ static void freesymkey( symkey * sk, symkey ** bucket ) { symkey * unlinked; if (sk->secret != NULL) { memset(sk->secret, '\0', sk->secretsize); free(sk->secret); sk->secret = NULL; } UNLINK_SLIST(unlinked, *bucket, sk, hlink, symkey); //ENSURE(sk == unlinked); UNLINK_DLIST(sk, llink); memset((char *)sk + offsetof(symkey, symkey_payload), '\0', sizeof(*sk) - offsetof(symkey, symkey_payload)); LINK_SLIST(authfreekeys, sk, llink.f); authnumkeys--; authnumfreekeys++; } /* * authhavekey - return true and cache the key, if zero or both known * and trusted. */ bool authhavekey( keyid_t id ) { symkey * sk; authkeylookups++; if (0 == id || cache_keyid == id) { return true; } /* * Search the bin for the key. If found and the key type * is zero, somebody marked it trusted without specifying * a key or key type. In this case consider the key missing. */ authkeyuncached++; for (sk = key_hash[KEYHASH(id)]; sk != NULL; sk = sk->hlink) { if (id == sk->keyid) { if (0 == sk->type) { authkeynotfound++; return false; } break; } } /* * If the key is not found, or if it is found but not trusted, * the key is not considered found. */ if (NULL == sk) { authkeynotfound++; return false; } if (!(KEY_TRUSTED & sk->flags)) { authnokey++; return false; } /* * The key is found and trusted. Initialize the key cache. */ cache_keyid = sk->keyid; cache_type = sk->type; cache_flags = sk->flags; cache_secret = sk->secret; cache_secretsize = sk->secretsize; return true; } /* * authtrust - declare a key to be trusted/untrusted */ void authtrust( keyid_t id, bool trust ) { symkey ** bucket; symkey * sk; /* * Search bin for key; if it does not exist and is untrusted, * forget it. */ bucket = &key_hash[KEYHASH(id)]; for (sk = *bucket; sk != NULL; sk = sk->hlink) { if (id == sk->keyid) break; } if (!trust && NULL == sk) return; /* * There are two conditions remaining. Either it does not * exist and is to be trusted or it does exist and is or is * not to be trusted. */ if (sk != NULL) { if (cache_keyid == id) { cache_flags = 0; cache_keyid = 0; } /* * Key exists. If it is to be trusted, say so. */ if (trust) { sk->flags |= KEY_TRUSTED; return; } /* No longer trusted, return it to the free list. */ freesymkey(sk, bucket); return; } allocsymkey(bucket, id, KEY_TRUSTED, 0, 0, NULL); } /* * authistrusted - determine whether a key is trusted */ bool authistrusted( keyid_t keyno ) { symkey * sk; symkey ** bucket; if (keyno == cache_keyid) return !!(KEY_TRUSTED & cache_flags); authkeyuncached++; bucket = &key_hash[KEYHASH(keyno)]; for (sk = *bucket; sk != NULL; sk = sk->hlink) { if (keyno == sk->keyid) break; } if (NULL == sk || !(KEY_TRUSTED & sk->flags)) { authkeynotfound++; return false; } return true; } void mac_setkey( keyid_t keyno, int keytype, const uint8_t *key, size_t len ) { symkey ** bucket; uint8_t * secret; size_t secretsize; //ENSURE(keytype <= USHRT_MAX); //ENSURE(len < 4 * 1024); /* * See if we already have the key. If so just stick in the * new value. */ bucket = &key_hash[KEYHASH(keyno)]; for (symkey * sk = *bucket; sk != NULL; sk = sk->hlink) { if (keyno == sk->keyid) { sk->type = (unsigned short)keytype; secretsize = len; sk->secretsize = (unsigned short)secretsize; free(sk->secret); sk->secret = emalloc(secretsize); memcpy(sk->secret, key, secretsize); if (cache_keyid == keyno) { cache_flags = 0; cache_keyid = 0; } return; } } /* * Need to allocate new structure. Do it. */ secretsize = len; secret = emalloc(secretsize); memcpy(secret, key, secretsize); allocsymkey(bucket, keyno, 0, (unsigned short)keytype, (unsigned short)secretsize, secret); #ifdef DEBUG if (debug >= 4) { /* SPECIAL DEBUG */ printf("auth_setkey: key %d type %d len %d ", (int)keyno, keytype, (int)secretsize); for (size_t j = 0; j < secretsize; j++) printf("%02x", secret[j]); printf("\n"); } #endif } /* * auth_delkeys - delete untrusted keys, and clear all info * except the trusted bit of trusted keys, in * preparation for rereading the keys file. */ void auth_delkeys(void) { symkey * sk; ITER_DLIST_BEGIN(key_listhead, sk, llink, symkey) /* * Don't lose info as to which keys are trusted. */ if (KEY_TRUSTED & sk->flags) { if (sk->secret != NULL) { memset(sk->secret, '\0', sk->secretsize); free(sk->secret); sk->secret = NULL; } sk->secretsize = 0; } else { freesymkey(sk, &key_hash[KEYHASH(sk->keyid)]); } ITER_DLIST_END() } /* * authencrypt - generate message authenticator * * Returns length of authenticator field, zero if key not found. */ int authencrypt( keyid_t keyno, uint32_t * pkt, int length ) {\ /* * A zero key identifier means the sender has not verified * the last message was correctly authenticated. The MAC * consists of a single word with value zero. */ authencryptions++; pkt[length / 4] = htonl(keyno); if (0 == keyno) { return 4; } if (!authhavekey(keyno)) { return 0; } return mac_authencrypt(cache_type, cache_secret, cache_secretsize, pkt, length); } /* * authdecrypt - verify message authenticator * * Returns true if authenticator valid, false if invalid or not found. */ bool authdecrypt( keyid_t keyno, uint32_t * pkt, int length, int size ) { /* * A zero key identifier means the sender has not verified * the last message was correctly authenticated. For our * purpose this is an invalid authenticator. */ authdecryptions++; if (0 == keyno || !authhavekey(keyno) || size < 4) { return false; } return mac_authdecrypt(cache_type, cache_secret, cache_secretsize, pkt, length, size); } ntpsec-1.1.0+dfsg1/libntp/clockwork.c0000644000175000017500000000467213252364117017270 0ustar rlaagerrlaager/* * clockwork.c - the interface to the hardare clock */ #include "config.h" #include #include /* prerequisite on NetBSD */ #include #include "ntp.h" #include "ntp_machine.h" #include "ntp_syslog.h" #include "ntp_stdlib.h" #include "lib_strbuf.h" #include "ntp_debug.h" #include "ntp_syscall.h" #ifdef HAVE_SYS_TIMEX_H /* * ntp_adjtime at nanosecond precision. Hiding the units difference * here helps prevent loss-of-precision bugs elsewhere. We * deliberately don't merge STA_NANO into the status flags if it's * absent, however, this way callers can tell what accuracy they're * actually getting. * * Some versions of ntp_adjtime(2), notably the Linux one which is * implemented in terms of a local, unstandardized adjtimex(2), have a * time member that can be used to retrieve and increment * (ADJ_SETOFFSET) system time. If this were portable there would be * scaling of ntx->time.tv_usec in here for non-STA_NANO systems. It * isn't; NetBSD and FreeBSD don't have that time member. * * Problem: the Linux manual page for adjtimex(2) says the precision * member is microseconds and doesn't mention STA_NANO, but the legacy * ntptime code has a scaling expression in it that implies * nanoseconds if that flash bit is on. It is unknown under what * circumstances, if any, this was ever correct. */ int ntp_adjtime_ns(struct timex *ntx) { #ifdef STA_NANO static bool nanoseconds = false; static int callcount = 0; if (callcount++ == 0){ struct timex ztx; memset(&ztx, '\0', sizeof(ztx)); ntp_adjtime(&ztx); nanoseconds = (STA_NANO & ztx.status) != 0; } #endif #ifdef STA_NANO if (!nanoseconds) #endif ntx->offset /= 1000; int errval = ntp_adjtime(ntx); #ifdef STA_NANO nanoseconds = (STA_NANO & ntx->status) != 0; if (!nanoseconds) #endif { ntx->offset *= 1000; //ntx->precision *= 1000; ntx->jitter *= 1000; } return errval; } #endif /* HAVE_SYS_TIMEX_H */ int ntp_set_tod( struct timespec *tvs ) { int rc; int saved_errno; TPRINT(1, ("In ntp_set_tod\n")); #ifdef HAVE_CLOCK_SETTIME errno = 0; rc = clock_settime(CLOCK_REALTIME, tvs); saved_errno = errno; TPRINT(1, ("ntp_set_tod: clock_settime: %d %m\n", rc)); #else #error POSIX clock_settime(2) is required #endif /* HAVE_CLOCK_SETTIME */ errno = saved_errno; /* for %m below */ TPRINT(1, ("ntp_set_tod: Final result: clock_settime: %d %m\n", rc)); if (rc) errno = saved_errno; return rc; } ntpsec-1.1.0+dfsg1/libntp/timespecops.c0000644000175000017500000001535513252364117017625 0ustar rlaagerrlaager/* * timespecops.h -- calculations on 'struct timespec' values * * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. * * Rationale * --------- * * Doing basic arithmetic on a 'struct timespec' is not exceedingly * hard, but it requires tedious and repetitive code to keep the result * normalised. We consider a timespec normalised when the nanosecond * fraction is in the interval [0 .. 10^9[ ; there are multiple value * pairs of seconds and nanoseconds that denote the same time interval, * but the normalised representation is unique. No two different * intervals can have the same normalised representation. * * Another topic is the representation of negative time intervals. * There's more than one way to this, since both the seconds and the * nanoseconds of a timespec are signed values. IMHO, the easiest way is * to use a complement representation where the nanoseconds are still * normalised, no matter what the sign of the seconds value. This makes * normalisation easier, since the sign of the integer part is * irrelevant, and it removes several sign decision cases during the * calculations. * * As long as no signed integer overflow can occur with the nanosecond * part of the operands, all operations work as expected and produce a * normalised result. * * The exception to this are functions fix a '_fast' suffix, which do no * normalisation on input data and therefore expect the input data to be * normalised. * * Input and output operands may overlap; all input is consumed before * the output is written to. * * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: NTP */ #include "config.h" #include #include #include "timespecops.h" #include "ntp.h" #include "ntp_calendar.h" #include "timetoa.h" /* make sure nanoseconds are in nominal range */ struct timespec normalize_tspec( struct timespec x ) { #if NTP_SIZEOF_LONG > 4 /* * tv_nsec is of type 'long', and on a 64-bit machine using only * loops becomes prohibitive once the upper 32 bits get * involved. On the other hand, division by constant should be * fast enough; so we do a division of the nanoseconds in that * case. */ if (x.tv_nsec < 0 || x.tv_nsec >= NS_PER_S) { ldiv_t z = ldiv( x.tv_nsec, NS_PER_S); if (z.rem < 0) { z.quot--; z.rem += NS_PER_S; } x.tv_sec += z.quot; x.tv_nsec = z.rem; } #else /* since 10**9 is close to 2**32, we don't divide but do a * normalisation in a loop; this takes 3 steps max, and should * outperform a division even if the mul-by-inverse trick is * employed. */ if (x.tv_nsec < 0) do { x.tv_nsec += NS_PER_S; x.tv_sec--; } while (x.tv_nsec < 0); else if (x.tv_nsec >= NS_PER_S) do { x.tv_nsec -= NS_PER_S; x.tv_sec++; } while (x.tv_nsec >= NS_PER_S); #endif return x; } /* convert a double to a rounded and normalized timespec */ struct timespec d_to_tspec( double d ) { struct timespec x; double s = floor(d); x.tv_sec = (time_t) s; x.tv_nsec = (long) (((d - s) * NS_PER_S) + 0.5); return x; } /* x = a + b */ struct timespec add_tspec( struct timespec a, struct timespec b ) { struct timespec x; x = a; x.tv_sec += b.tv_sec; x.tv_nsec += b.tv_nsec; return normalize_tspec(x); } /* x = a + b, b is fraction only */ struct timespec add_tspec_ns( struct timespec a, long b ) { struct timespec x; x = a; x.tv_nsec += b; return normalize_tspec(x); } /* x = a - b */ struct timespec sub_tspec( struct timespec a, struct timespec b ) { struct timespec x; x = a; x.tv_sec -= b.tv_sec; x.tv_nsec -= b.tv_nsec; return normalize_tspec(x); } /* x = a - b, b is fraction only */ struct timespec sub_tspec_ns( struct timespec a, long b ) { struct timespec x; x = a; x.tv_nsec -= b; return normalize_tspec(x); } /* x = -a */ struct timespec neg_tspec( struct timespec a ) { struct timespec x; x.tv_sec = -a.tv_sec; x.tv_nsec = -a.tv_nsec; return normalize_tspec(x); } /* x = abs(a) */ struct timespec abs_tspec( struct timespec a ) { struct timespec c; c = normalize_tspec(a); if (c.tv_sec < 0) { if (c.tv_nsec != 0) { c.tv_sec = -c.tv_sec - 1; c.tv_nsec = NS_PER_S - c.tv_nsec; } else { c.tv_sec = -c.tv_sec; } } return c; } /* * compare previously-normalised a and b * return -1 / 0 / 1 if a < / == / > b */ int cmp_tspec( struct timespec a, struct timespec b ) { int r; r = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); if (0 == r) r = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); return r; } /* * compare possibly-denormal a and b * return -1 / 0 / 1 if a < / == / > b */ int cmp_tspec_denorm( struct timespec a, struct timespec b ) { return cmp_tspec(normalize_tspec(a), normalize_tspec(b)); } /* * test previously-normalised a * return -1 / 0 / 1 if a < / == / > 0 */ int test_tspec( struct timespec a ) { int r; r = (a.tv_sec > 0) - (a.tv_sec < 0); if (r == 0) r = (a.tv_nsec > 0); return r; } /* * test possibly-denormal a * return -1 / 0 / 1 if a < / == / > 0 */ int test_tspec_denorm( struct timespec a ) { return test_tspec(normalize_tspec(a)); } /* * convert to l_fp type, relative and absolute */ /* convert from timespec duration to l_fp duration */ l_fp tspec_intv_to_lfp( struct timespec x ) { struct timespec v = normalize_tspec(x); return lfpinit((int32_t)v.tv_sec, TVNTOF(v.tv_nsec)); } /* x must be UN*X epoch, output will be in NTP epoch */ l_fp tspec_stamp_to_lfp( struct timespec x ) { l_fp y; y = tspec_intv_to_lfp(x); bumplfpuint(y, JAN_1970); return y; } /* convert from l_fp type, relative signed/unsigned and absolute */ struct timespec lfp_intv_to_tspec( l_fp x ) { struct timespec out; l_fp absx; int neg; neg = L_ISNEG(x); absx = x; if (neg) { L_NEG(absx); } out.tv_nsec = FTOTVN(lfpfrac(absx)); out.tv_sec = lfpsint(absx); if (neg) { out.tv_sec = -out.tv_sec; out.tv_nsec = -out.tv_nsec; out = normalize_tspec(out); } return out; } struct timespec lfp_uintv_to_tspec( l_fp x ) { struct timespec out; out.tv_nsec = FTOTVN(lfpfrac(x)); out.tv_sec = lfpsint(x); return out; } /* * absolute (timestamp) conversion. Input is time in NTP epoch, output * is in UN*X epoch. The NTP time stamp will be expanded around the * pivot time p. */ struct timespec lfp_stamp_to_tspec( l_fp x, time_t p ) { struct timespec out; time64_t sec; sec = ntpcal_ntp_to_time(lfpuint(x), p); out.tv_nsec = FTOTVN(lfpfrac(x)); /* copying a time64_t to a time_t needs some care... */ #if NTP_SIZEOF_TIME_T <= 4 out.tv_sec = (time_t)time64lo(sec); #else out.tv_sec = (time_t)time64s(sec); #endif return out; } struct timespec tval_to_tspec( struct timeval x ) { struct timespec y; y.tv_sec = x.tv_sec; y.tv_nsec = x.tv_usec * 1000; return y; } /* end */ ntpsec-1.1.0+dfsg1/libntp/authreadkeys.c0000644000175000017500000001225513252364117017757 0ustar rlaagerrlaager/* * authreadkeys.c - routines to support the reading of the key file */ #include "config.h" #include #include #include "ntp.h" #include "ntp_syslog.h" #include "ntp_stdlib.h" #include "lib_strbuf.h" #include #include /* Forwards */ static char *nexttok (char **); /* * nexttok - basic internal tokenizing routine */ static char * nexttok( char **str ) { char *cp; char *starttok; cp = *str; /* * Space past white space */ while (*cp == ' ' || *cp == '\t') cp++; /* * Save this and space to end of token */ starttok = cp; while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && *cp != '#') cp++; /* * If token length is zero return an error, else set end of * token to zero and return start. */ if (starttok == cp) return NULL; if (*cp == ' ' || *cp == '\t') *cp++ = '\0'; else *cp = '\0'; *str = cp; return starttok; } static void check_digest_length( keyid_t keyno, int keytype, char *name) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int length = 0; EVP_MD_CTX *ctx; const EVP_MD *md; md = EVP_get_digestbynid(keytype); ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, md, NULL); EVP_DigestFinal_ex(ctx, digest, &length); EVP_MD_CTX_destroy(ctx); if (MAX_BARE_DIGEST_LENGTH < length) { msyslog(LOG_ERR, "AUTH: authreadkeys: digest for key %u, %s will be truncated.", keyno, name); } } /* * authreadkeys - (re)read keys from a file. */ bool authreadkeys( const char *file ) { FILE *fp; char *line; keyid_t keyno; int keytype; char buf[512]; /* lots of room for line */ uint8_t keystr[32]; /* Bug 2537 */ size_t len; size_t j; int keys = 0; /* * Open file. Complain and return if it can't be opened. */ fp = fopen(file, "r"); if (fp == NULL) { msyslog(LOG_ERR, "AUTH: authreadkeys: file %s: %m", file); return false; } ssl_init(); msyslog(LOG_ERR, "AUTH: authreadkeys: reading %s", file); /* * Remove all existing keys */ auth_delkeys(); /* * Now read lines from the file, looking for key entries */ while ((line = fgets(buf, sizeof buf, fp)) != NULL) { char *token = nexttok(&line); if (token == NULL) continue; /* * First is key number. See if it is okay. */ keyno = (keyid_t)atoi(token); if (keyno == 0) { msyslog(LOG_ERR, "AUTH: authreadkeys: cannot change key %s", token); continue; } if (keyno > NTP_MAXKEY) { msyslog(LOG_ERR, "AUTH: authreadkeys: key %s > %d reserved", token, NTP_MAXKEY); continue; } /* * Next is keytype. See if that is all right. */ token = nexttok(&line); if (token == NULL) { msyslog(LOG_ERR, "AUTH: authreadkeys: no key type for key %u", keyno); continue; } /* * The key type is the NID used by the message digest * algorithm. There are a number of inconsistencies in * the OpenSSL database. We attempt to discover them * here and prevent use of inconsistent data later. * * OpenSSL digest short names are capitalized, so uppercase the * digest name before passing to OBJ_sn2nid(). If it is not * recognized but begins with 'M' use NID_md5 to be consistent * with past behavior. */ char *upcased; char *pch; upcased = lib_getbuf(); strlcpy(upcased, token, LIB_BUFLENGTH); for (pch = upcased; '\0' != *pch; pch++) *pch = (char)toupper((unsigned char)*pch); keytype = OBJ_sn2nid(upcased); if ((NID_undef == keytype) && ('M' == upcased[0])) keytype = NID_md5; if (NID_undef == keytype) { msyslog(LOG_ERR, "AUTH: authreadkeys: invalid type for key %u, %s", keyno, token); continue; } if (EVP_get_digestbynid(keytype) == NULL) { msyslog(LOG_ERR, "AUTH: authreadkeys: no algorithm for key %u, %s", keyno, token); continue; } /* * Finally, get key and insert it. If it is longer than 20 * characters, it is a binary string encoded in hex; * otherwise, it is a text string of printable ASCII * characters. */ token = nexttok(&line); if (token == NULL) { msyslog(LOG_ERR, "AUTH: authreadkeys: no key for key %u", keyno); continue; } len = strlen(token); if (len <= 20) { /* Bug 2537 */ check_digest_length(keyno, keytype, upcased); mac_setkey(keyno, keytype, (uint8_t *)token, len); keys++; } else { char hex[] = "0123456789abcdef"; size_t jlim; jlim = len; if ((2*sizeof(keystr)) < jlim) { jlim = 2 * sizeof(keystr); msyslog(LOG_ERR, "AUTH: authreadkeys: key %u trucated to %u bytes", keyno, (unsigned int)jlim); } for (j = 0; j < jlim; j++) { char *ptr = strchr(hex, tolower((unsigned char)token[j])); if (ptr == NULL) break; /* abort decoding */ uint8_t temp = (uint8_t)(ptr - hex); if (j & 1) keystr[j / 2] |= temp; else keystr[j / 2] = (uint8_t)(temp << 4); } if (j < jlim) { msyslog(LOG_ERR, "AUTH: authreadkeys: invalid hex digit for key %u", keyno); continue; } check_digest_length(keyno, keytype, upcased); mac_setkey(keyno, keytype, keystr, jlim / 2); keys++; } } fclose(fp); msyslog(LOG_ERR, "AUTH: authreadkeys: added %d keys", keys); return true; } ntpsec-1.1.0+dfsg1/libntp/recvbuff.c0000644000175000017500000001055513252364117017071 0ustar rlaagerrlaager#include "config.h" #include #include "ntp_assert.h" #include "ntp_syslog.h" #include "ntp_stdlib.h" #include "ntp_lists.h" #include "recvbuff.h" /* * Memory allocation. */ static unsigned long full_recvbufs; /* recvbufs on full_recv_fifo */ static unsigned long free_recvbufs; /* recvbufs on free_recv_list */ static unsigned long total_recvbufs; /* total recvbufs currently in use */ static unsigned long lowater_adds; /* # of times we have added memory */ static unsigned long buffer_shortfall; /* # of missed free receive buffers between replenishments */ static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo; static recvbuf_t * free_recv_list; #ifdef DEBUG static void uninit_recvbuff(void); #endif unsigned long free_recvbuffs (void) { return free_recvbufs; } unsigned long full_recvbuffs (void) { return full_recvbufs; } unsigned long total_recvbuffs (void) { return total_recvbufs; } unsigned long lowater_additions(void) { return lowater_adds; } static inline void initialise_buffer(recvbuf_t *buff) { ZERO(*buff); } static void create_buffers(unsigned int nbufs) { recvbuf_t *bufp; unsigned int i, abuf; abuf = nbufs + buffer_shortfall; buffer_shortfall = 0; #ifndef DEBUG bufp = emalloc_zero(abuf * sizeof(*bufp)); #endif for (i = 0; i < abuf; i++) { #ifdef DEBUG /* * Allocate each buffer individually so they can be * free()d during ntpd shutdown on DEBUG builds to * keep them out of heap leak reports. */ bufp = emalloc_zero(sizeof(*bufp)); #endif LINK_SLIST(free_recv_list, bufp, link); bufp++; free_recvbufs++; total_recvbufs++; } lowater_adds++; /* coverity[leaked_storage] */ } void init_recvbuff(unsigned int nbufs) { /* * Init buffer free list and stat counters */ free_recvbufs = total_recvbufs = 0; full_recvbufs = lowater_adds = 0; create_buffers(nbufs); #ifdef DEBUG atexit(&uninit_recvbuff); #endif } #ifdef DEBUG static void uninit_recvbuff(void) { recvbuf_t *rbunlinked; for (;;) { UNLINK_FIFO(rbunlinked, full_recv_fifo, link); if (rbunlinked == NULL) break; free(rbunlinked); } for (;;) { UNLINK_HEAD_SLIST(rbunlinked, free_recv_list, link); if (rbunlinked == NULL) break; free(rbunlinked); } } #endif /* DEBUG */ /* * freerecvbuf - make a single recvbuf available for reuse */ void freerecvbuf(recvbuf_t *rb) { if (rb == NULL) { msyslog(LOG_ERR, "ERR: freerecvbuff received NULL buffer"); return; } rb->used--; if (rb->used != 0) msyslog(LOG_ERR, "ERR: ******** freerecvbuff non-zero usage: %d *******", rb->used); LINK_SLIST(free_recv_list, rb, link); free_recvbufs++; } void add_full_recv_buffer(recvbuf_t *rb) { if (rb == NULL) { msyslog(LOG_ERR, "ERR: add_full_recv_buffer received NULL buffer"); return; } LINK_FIFO(full_recv_fifo, rb, link); full_recvbufs++; } recvbuf_t * get_free_recv_buffer(void) { recvbuf_t *buffer; UNLINK_HEAD_SLIST(buffer, free_recv_list, link); if (buffer != NULL) { free_recvbufs--; initialise_buffer(buffer); buffer->used++; } else { buffer_shortfall++; } return buffer; } recvbuf_t * get_full_recv_buffer(void) { recvbuf_t * rbuf; /* * try to grab a full buffer */ UNLINK_FIFO(rbuf, full_recv_fifo, link); if (rbuf != NULL) full_recvbufs--; return rbuf; } /* * purge_recv_buffers_for_fd() - purges any previously-received input * from a given file descriptor. */ void purge_recv_buffers_for_fd( SOCKET fd ) { recvbuf_t *rbufp; recvbuf_t *next; recvbuf_t *punlinked; for (rbufp = HEAD_FIFO(full_recv_fifo); rbufp != NULL; rbufp = next) { next = rbufp->link; if (rbufp->fd == fd) { UNLINK_MID_FIFO(punlinked, full_recv_fifo, rbufp, link, recvbuf_t); INSIST(punlinked == rbufp); full_recvbufs--; freerecvbuf(rbufp); } } } /* * Checks to see if there are buffers to process */ bool has_full_recv_buffer(void) { if (HEAD_FIFO(full_recv_fifo) != NULL) return (true); else return (false); } #ifdef NTP_DEBUG_LISTS void check_gen_fifo_consistency(void *fifo) { gen_fifo *pf; gen_node *pthis; gen_node **pptail; pf = fifo; REQUIRE((NULL == pf->phead && NULL == pf->pptail) || (NULL != pf->phead && NULL != pf->pptail)); pptail = &pf->phead; for (pthis = pf->phead; pthis != NULL; pthis = pthis->link) if (NULL != pthis->link) pptail = &pthis->link; REQUIRE(NULL == pf->pptail || pptail == pf->pptail); } #endif /* NTP_DEBUG_LISTS */ ntpsec-1.1.0+dfsg1/libntp/assert.c0000644000175000017500000000702513252364117016566 0ustar rlaagerrlaager/* * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1997-2001 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ /*! \file */ #include "config.h" #include #include #include "ntp.h" #include "ntp_debug.h" #include "ntp_syslog.h" #include "ntp_assert.h" #include "isc_result.h" #ifdef USEBACKTRACE /* * Getting a back trace of a running process is tricky and highly platform * dependent. Our current approach is as follows: * 1. If the system library supports the "backtrace()" function, use it. * OS X support this starting at with SDK 10.5. glibc since version 2.1 * 2. Otherwise, if unwind.h exists then use the __Unwind_Backtrace() function. * This function is available on Linux, OS X, and FreeBSD. It is defined * in Linux Standard Base since version 4.1 * 3. Otherwise, tough luck. */ /* * The maximum number of stack frames to dump on assertion failure. */ #ifndef BACKTRACE_MAXFRAME #define BACKTRACE_MAXFRAME 128 #endif # ifdef HAVE_BACKTRACE_SYMBOLS_FD #include void backtrace_log(void) { int nptrs; void *buffer[BACKTRACE_MAXFRAME]; char **strings; nptrs = backtrace(buffer, BACKTRACE_MAXFRAME); strings = backtrace_symbols(buffer, nptrs); msyslog(LOG_ERR, "ERR: Stack trace:\n"); if (strings) { /* skip trace of this shim function */ for (int j = 1; j < nptrs; j++) msyslog(LOG_ERR, "ERR: %s\n", strings[j]); free(strings); } } # elif defined(HAVE__UNWIND_BACKTRACE) #include typedef struct { void **result; int max_depth; int skip_count; int count; } trace_arg_t; static _Unwind_Reason_Code btcallback(struct _Unwind_Context *uc, void *opq) { trace_arg_t *arg = (trace_arg_t *)opq; if (arg->skip_count > 0) arg->skip_count--; else arg->result[arg->count++] = (void *)_Unwind_GetIP(uc); if (arg->count == arg->max_depth) return (5); /* _URC_END_OF_STACK */ return (0); /* _URC_NO_REASON */ } void backtrace_log(void) { trace_arg_t arg; void *buffer[BACKTRACE_MAXFRAME]; int i; arg.skip_count = 1; arg.result = buffer; arg.max_depth = BACKTRACE_MAXFRAME; arg.count = 0; _Unwind_Backtrace(btcallback, &arg); msyslog(LOG_ERR, "ERR: Stack trace:\n"); /* skip trace of this shim function */ for (i = 1; i < arg.count; i++) { msyslog(LOG_ERR, "ERR: #%d %p in ??\n", i, buffer[i]); } } #endif /* HAVE__UNWIND_BACKTRACE */ #endif /* USEBACKTRACE */ /*% Type to Text */ static const char * assertion_typetotext(assertiontype_t type) { const char *result; /* * These strings have purposefully not been internationalized * because they are considered to essentially be keywords of * the ISC development environment. */ switch (type) { case assertiontype_require: result = "REQUIRE"; break; case assertiontype_ensure: result = "ENSURE"; break; case assertiontype_insist: result = "INSIST"; break; case assertiontype_invariant: result = "INVARIANT"; break; default: result = "(null)"; } return (result); } /* * assertion_failed - Redirect assertion failed output to msyslog(). */ void assertion_failed( const char *file, int line, assertiontype_t type, const char *cond ) { /* Is recursion an issue? */ termlogit = true; /* insist log to terminal */ msyslog(LOG_ERR, "ERR: %s:%d: %s(%s) failed", file, line, assertion_typetotext(type), cond); #ifndef BACKTRACE_DISABLED backtrace_log(); #endif msyslog(LOG_ERR, "ERR: exiting (due to assertion failure)"); abort(); } ntpsec-1.1.0+dfsg1/libntp/syssignal.c0000644000175000017500000000214013252364117017272 0ustar rlaagerrlaager# include "config.h" #include #include #include #include "ntp_syslog.h" #include "ntp_stdlib.h" # ifdef SA_RESTART # define Z_SA_RESTART SA_RESTART # else # define Z_SA_RESTART 0 # endif /* set an sa_handler */ void signal_no_reset( int sig, void (*func)(int) ) { int n; struct sigaction vec; ZERO(vec); sigemptyset(&vec.sa_mask); vec.sa_handler = func; /* Added for PPS clocks on Solaris 7 which get EINTR errors */ # ifdef SIGPOLL if (SIGPOLL == sig) vec.sa_flags = Z_SA_RESTART; # endif # ifdef SIGIO if (SIGIO == sig) vec.sa_flags = Z_SA_RESTART; # endif do n = sigaction(sig, &vec, NULL); while (-1 == n && EINTR == errno); if (-1 == n) { perror("ERROR: signal_no_reset() sigaction"); exit(1); } } /* set an sa_sigaction */ void signal_no_reset1( int sig, void (*func)(int, siginfo_t *, void *) ) { int n; struct sigaction vec; ZERO(vec); sigemptyset(&vec.sa_mask); vec.sa_sigaction = func; vec.sa_flags = SA_SIGINFO; n = sigaction(sig, &vec, NULL); if (-1 == n) { perror("ERROR: signal_no_reset1() sigaction"); exit(1); } } ntpsec-1.1.0+dfsg1/libntp/isc_net.c0000644000175000017500000001037413252364117016712 0ustar rlaagerrlaager/* * Copyright (C) 2004, 2005, 2007, 2008, 2012 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ #include "config.h" #include #include #if defined(HAVE_SYS_SYSCTL_H) #include #endif /* hack to ignore GCC Unused Result */ #define IGNORE(r) do{if(r){}}while(0) #include #include #include /* for struct sockaddr on *BSD */ #include /* Contractual promise. */ #include /* Contractual promise. */ #include "isc_netaddr.h" #include "isc_error.h" #include "isc_result.h" static isc_result_t ipv4_result = ISC_R_NOTFOUND; static isc_result_t ipv6_result = ISC_R_NOTFOUND; static isc_result_t ipv6only_result = ISC_R_NOTFOUND; static isc_result_t try_proto(int domain) { int s; isc_result_t result = ISC_R_SUCCESS; char strbuf[BUFSIZ]; s = socket(domain, SOCK_STREAM, 0); if (s == -1) { switch (errno) { #ifdef EAFNOSUPPORT case EAFNOSUPPORT: #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: #endif #ifdef EINVAL case EINVAL: #endif return (ISC_R_NOTFOUND); default: IGNORE(strerror_r(errno, strbuf, sizeof(strbuf))); UNEXPECTED_ERROR("socket() failed: %s", strbuf); return (ISC_R_UNEXPECTED); } } if (domain == PF_INET6) { struct sockaddr_in6 sin6; socklen_t len; /* * Check to see if IPv6 is broken, as is common on Linux. */ len = sizeof(sin6); if (getsockname(s, (struct sockaddr *)&sin6, &len) < 0) { /* isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, "retrieving the address of an IPv6 " "socket from the kernel failed."); isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, "IPv6 is not supported."); */ result = ISC_R_NOTFOUND; } else { if (len == sizeof(struct sockaddr_in6)) result = ISC_R_SUCCESS; else { /* isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, "IPv6 structures in kernel and " "user space do not match."); isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, "IPv6 is not supported."); */ result = ISC_R_NOTFOUND; } } } (void)close(s); return (result); } static void initialize(void) { static bool once = false; if (once) return; once = true; ipv4_result = try_proto(PF_INET); ipv6_result = try_proto(PF_INET6); } bool isc_net_probeipv4_bool(void) { initialize(); return (ISC_R_SUCCESS == ipv4_result); } bool isc_net_probeipv6_bool(void) { return (ISC_R_SUCCESS == isc_net_probeipv6()); } isc_result_t isc_net_probeipv6(void) { initialize(); return (ipv6_result); } static void initialize_ipv6only(void) { static bool once_ipv6only = false; #ifdef IPV6_V6ONLY int s, on; char strbuf[BUFSIZ]; #endif if (once_ipv6only) return; once_ipv6only = true; isc_result_t result; result = isc_net_probeipv6(); if (result != ISC_R_SUCCESS) { ipv6only_result = result; return; } #ifndef IPV6_V6ONLY ipv6only_result = ISC_R_NOTFOUND; return; #else /* check for TCP sockets */ s = socket(PF_INET6, SOCK_STREAM, 0); if (s == -1) { IGNORE(strerror_r(errno, strbuf, sizeof(strbuf))); UNEXPECTED_ERROR("socket() failed: %s", strbuf); ipv6only_result = ISC_R_UNEXPECTED; return; } on = 1; if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { ipv6only_result = ISC_R_NOTFOUND; goto close; } close(s); /* check for UDP sockets */ s = socket(PF_INET6, SOCK_DGRAM, 0); if (s == -1) { IGNORE(strerror_r(errno, strbuf, sizeof(strbuf))); UNEXPECTED_ERROR("socket() failed: %s", strbuf); ipv6only_result = ISC_R_UNEXPECTED; return; } on = 1; if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { ipv6only_result = ISC_R_NOTFOUND; goto close; } ipv6only_result = ISC_R_SUCCESS; close: close(s); return; #endif /* IPV6_V6ONLY */ } bool isc_net_probe_ipv6only_bool(void) { initialize_ipv6only(); return (ISC_R_SUCCESS == ipv6only_result); } ntpsec-1.1.0+dfsg1/libntp/msyslog.c0000644000175000017500000003077413252364117016771 0ustar rlaagerrlaager/* * msyslog - either send a message to the terminal or print it on * the standard output. * * Converted to use varargs, much better ... jks */ #include "config.h" #include #include #include #include "ntp.h" #include "ntp_debug.h" #include "ntp_syslog.h" #include "lib_strbuf.h" /* Use XSI-compliant strerror_r(3), prototype in string.h. * Don't try moving this further up, else hilarity will ensue... */ #undef _GNU_SOURCE #include /* start out with syslog and stderr, otherwise startup errors lost */ bool syslogit = true; /* log messages to syslog */ bool termlogit = true; /* duplicate to stdout/err */ bool termlogit_pid = true; bool msyslog_include_timestamp = true; static FILE * syslog_file; static char * syslog_fname; static char * syslog_abs_fname; /* libntp default ntp_syslogmask is all bits lit */ #define INIT_NTP_SYSLOGMASK ~(uint32_t)0 uint32_t ntp_syslogmask = INIT_NTP_SYSLOGMASK; extern char * progname; /* Declare the local functions */ static int mvfprintf(FILE *, const char *, va_list) NTP_PRINTF(2, 0); const char * humanlogtime(void); static void addto_syslog (int, const char *); #ifndef VSNPRINTF_PERCENT_M static void errno_to_str(int, char *, size_t); void format_errmsg (char *, size_t, const char *, int); /* format_errmsg() is under #ifndef VSNPRINTF_PERCENT_M above */ void format_errmsg( char * nfmt, size_t lennfmt, const char * fmt, int errval ) { char errmsg[256]; char c; char *n; const char *f; size_t len; n = nfmt; f = fmt; while ((c = *f++) != '\0' && n < (nfmt + lennfmt - 1)) { if (c != '%') { *n++ = c; continue; } if ((c = *f++) != 'm') { *n++ = '%'; if ('\0' == c) break; *n++ = c; continue; } errno_to_str(errval, errmsg, sizeof(errmsg)); len = strlen(errmsg); /* Make sure we have enough space for the error message */ if ((n + len) < (nfmt + lennfmt - 1)) { memcpy(n, errmsg, len); n += len; } } *n = '\0'; } /* * errno_to_str() - a thread-safe strerror() replacement. * Hides the varied signatures of strerror_r(). */ static void errno_to_str( int err, char * buf, size_t bufsiz ) { int rc; rc = strerror_r(err, buf, bufsiz); if (rc) snprintf(buf, bufsiz, "strerror_r(%d): errno %d", err, errno); } #endif /* VSNPRINTF_PERCENT_M */ /* We don't want to clutter up the log with the year and day of the week, etc.; just the minimal date and time. */ const char * humanlogtime(void) { char * bp; time_t cursec; struct tm tmbuf, *tm; cursec = time(NULL); tm = localtime_r(&cursec, &tmbuf); if (!tm) return "-- --- --:--:--"; bp = lib_getbuf(); #ifdef ENABLE_CLASSIC_MODE const char * const months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; snprintf(bp, LIB_BUFLENGTH, "%2d %s %02d:%02d:%02d", tm->tm_mday, months[tm->tm_mon], tm->tm_hour, tm->tm_min, tm->tm_sec); #else /* ISO 8601 is a better format, sort order equals time order */ snprintf(bp, LIB_BUFLENGTH, "%02d-%02dT%02d:%02d:%02d", tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); #endif /* ENABLE_CLASSIC_MODE */ return bp; } /* * addto_syslog() * This routine adds the contents of a buffer to the syslog or an * application-specific logfile. */ static void addto_syslog( int level, const char * msg ) { static char * prevcall_progname; static char * prog; const char nl[] = "\n"; const char empty[] = ""; FILE * term_file; bool log_to_term; bool log_to_file; int pid; const char * nl_or_empty; const char * human_time; /* setup program basename static var prog if needed */ if (progname != prevcall_progname) { prevcall_progname = progname; prog = strrchr(progname, DIR_SEP); if (prog != NULL) prog++; else prog = progname; } log_to_term = termlogit; log_to_file = false; if (syslogit) syslog(level, "%s", msg); else if (syslog_file != NULL) log_to_file = true; #if defined(DEBUG) && DEBUG if (debug > 0) /* SPECIAL DEBUG */ log_to_term = true; #endif if (!(log_to_file || log_to_term)) return; /* syslog() adds the timestamp, name, and pid */ if (msyslog_include_timestamp) human_time = humanlogtime(); else /* suppress gcc pot. uninit. warning */ human_time = NULL; if (termlogit_pid || log_to_file) pid = getpid(); else /* suppress gcc pot. uninit. warning */ pid = -1; /* syslog() adds trailing \n if not present */ if ('\n' != msg[strlen(msg) - 1]) nl_or_empty = nl; else nl_or_empty = empty; if (log_to_term) { term_file = (level <= LOG_ERR) ? stderr : stdout; if (msyslog_include_timestamp) fprintf(term_file, "%s ", human_time); if (termlogit_pid) fprintf(term_file, "%s[%d]: ", prog, pid); fprintf(term_file, "%s%s", msg, nl_or_empty); fflush(term_file); } if (log_to_file) { if (msyslog_include_timestamp) fprintf(syslog_file, "%s ", human_time); fprintf(syslog_file, "%s[%d]: %s%s", prog, pid, msg, nl_or_empty); fflush(syslog_file); } } int mvsnprintf( char * buf, size_t bufsiz, const char * fmt, va_list ap ) { #ifndef VSNPRINTF_PERCENT_M char nfmt[256]; #else const char * nfmt = fmt; #endif int errval; /* * Save the error value as soon as possible */ errval = errno; #ifndef VSNPRINTF_PERCENT_M format_errmsg(nfmt, sizeof(nfmt), fmt, errval); #else errno = errval; #endif return vsnprintf(buf, bufsiz, nfmt, ap); } static int mvfprintf( FILE * fp, const char * fmt, va_list ap ) { #ifndef VSNPRINTF_PERCENT_M char nfmt[256]; #else const char * nfmt = fmt; #endif int errval; /* * Save the error value as soon as possible */ errval = errno; #ifndef VSNPRINTF_PERCENT_M format_errmsg(nfmt, sizeof(nfmt), fmt, errval); #else errno = errval; #endif return vfprintf(fp, nfmt, ap); } int mprintf( const char * fmt, ... ) { va_list ap; int rc; va_start(ap, fmt); rc = mvfprintf(stdout, fmt, ap); va_end(ap); return rc; } void msyslog( int level, const char * fmt, ... ) { char buf[1024]; va_list ap; va_start(ap, fmt); mvsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); addto_syslog(level, buf); } /* * Initialize the logging * * Called once per process, including forked children. */ void init_logging( const char * name, uint32_t def_syslogmask, int is_daemon ) { static bool was_daemon; const char * cp; const char * pname; /* * ntpd defaults to only logging sync-category events, when * NLOG() is used to conditionalize. Other libntp clients * leave it alone so that all NLOG() conditionals will fire. * This presumes all bits lit in ntp_syslogmask can't be * configured via logconfig and all lit is thereby a sentinel * that ntp_syslogmask is still at its default from libntp, * keeping in mind this function is called in forked children * where it has already been called in the parent earlier. * Forked children pass 0 for def_syslogmask. */ if (INIT_NTP_SYSLOGMASK == ntp_syslogmask && 0 != def_syslogmask) ntp_syslogmask = def_syslogmask; /* set more via logconfig */ /* * Logging. This may actually work on the gizmo board. Find a name * to log with by using the basename */ cp = strrchr(name, DIR_SEP); if (NULL == cp) pname = name; else pname = 1 + cp; /* skip DIR_SEP */ progname = estrdup(pname); if (is_daemon) was_daemon = true; # ifndef LOG_DAEMON openlog(progname, LOG_PID); # else /* LOG_DAEMON */ # ifndef LOG_NTP # define LOG_NTP LOG_DAEMON # endif openlog(progname, LOG_PID | LOG_NDELAY, (was_daemon) ? LOG_NTP : 0); # ifdef DEBUG if (debug) /* SPECIAL DEBUG */ setlogmask(LOG_UPTO(LOG_DEBUG)); else # endif /* DEBUG */ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */ # endif /* LOG_DAEMON */ } /* * change_logfile() * * Used to change from syslog to a logfile, or from one logfile to * another, and to reopen logfiles after forking. On systems where * ntpd forks, deals with converting relative logfile paths to * absolute (root-based) because we reopen logfiles after the current * directory has changed. */ int change_logfile( const char * fname, bool leave_crumbs ) { FILE * new_file; const char * log_fname; char * abs_fname; char curdir[512]; size_t cd_octets; size_t octets; //REQUIRE(fname != NULL); log_fname = fname; /* * In a forked child of a parent which is logging to a file * instead of syslog, syslog_file will be NULL and both * syslog_fname and syslog_abs_fname will be non-NULL. * If we are given the same filename previously opened * and it's still open, there's nothing to do here. */ if (syslog_file != NULL && syslog_fname != NULL && 0 == strcmp(syslog_fname, log_fname)) return 0; if (0 == strcmp(log_fname, "stderr")) { new_file = stderr; abs_fname = estrdup(log_fname); } else if (0 == strcmp(log_fname, "stdout")) { new_file = stdout; abs_fname = estrdup(log_fname); } else { if (syslog_fname != NULL && 0 == strcmp(log_fname, syslog_fname)) log_fname = syslog_abs_fname; if (log_fname != syslog_abs_fname && DIR_SEP != log_fname[0] && 0 != strcmp(log_fname, "stderr") && 0 != strcmp(log_fname, "stdout") && NULL != getcwd(curdir, sizeof(curdir))) { cd_octets = strlen(curdir); /* trim any trailing '/' */ if (cd_octets > 1 && DIR_SEP == curdir[cd_octets - 1]) cd_octets--; octets = cd_octets; octets += 1; /* separator '/' */ octets += strlen(log_fname); octets += 1; /* NUL terminator */ abs_fname = emalloc(octets); snprintf(abs_fname, octets, "%.*s%c%s", (int)cd_octets, curdir, DIR_SEP, log_fname); } else abs_fname = estrdup(log_fname); TPRINT(1, ("attempting to open log %s\n", abs_fname)); new_file = fopen(abs_fname, "a"); } if (NULL == new_file) { free(abs_fname); return -1; } /* leave a pointer in the old log */ if (leave_crumbs && (syslogit || log_fname != syslog_abs_fname)) msyslog(LOG_NOTICE, "LOG: switching logging to file %s", abs_fname); if (syslog_file != NULL && syslog_file != stderr && syslog_file != stdout && fileno(syslog_file) != fileno(new_file)) fclose(syslog_file); syslog_file = new_file; if (log_fname == syslog_abs_fname) { free(abs_fname); } else { if (syslog_abs_fname != NULL && syslog_abs_fname != syslog_fname) free(syslog_abs_fname); if (syslog_fname != NULL) free(syslog_fname); syslog_fname = estrdup(log_fname); syslog_abs_fname = abs_fname; } syslogit = false; return 0; } /* * setup_logfile() * * Redirect logging to a file if requested with -l/--logfile or via * ntp.conf logfile directive. * * This routine is invoked three different times in the sequence of a * typical daemon ntpd with DNS lookups to do. First it is invoked in * the original ntpd process, then again in the daemon after closing * all descriptors. In both of those cases, ntp.conf has not been * processed, so only -l/--logfile will trigger logfile redirection in * those invocations. Finally, if DNS names are resolved, the worker * child invokes this routine after its fork and close of all * descriptors. In this case, ntp.conf has been processed and any * "logfile" directive needs to be honored in the child as well. */ void setup_logfile( const char * name ) { if (NULL == syslog_fname && NULL != name) { if (-1 == change_logfile(name, true)) msyslog(LOG_ERR, "LOG: Cannot open log file %s, %m", name); return ; } if (NULL == syslog_fname) return; if (-1 == change_logfile(syslog_fname, false)) msyslog(LOG_ERR, "LOG: Cannot reopen log file %s, %m", syslog_fname); } /* * reopen_logfile() * * reopen current logfile in case the old file has been renamed by logrotate * */ void reopen_logfile(void) { FILE * new_file; if (NULL == syslog_file) { return; /* no log file, no clutter */ } new_file = fopen(syslog_fname, "a"); if (NULL == new_file) { msyslog(LOG_ERR, "LOG: reopen_logfile: couldn't open %s %m", syslog_fname); return; } /* This is a hack to avoid cluttering the log if we would reuse * the same file all over again. * change_logfile compares filenos. That doesn't work. * Can't check for a new file using a length of 0 since * newsyslog on FreeBSD puts a "logfile turned over" message there. * This seems to work. */ if (ftell(syslog_file) == ftell(new_file)) { /* just for debugging */ msyslog(LOG_INFO, "LOG: reopen_logfile: same length, ignored"); fclose(new_file); return; } msyslog(LOG_INFO, "LOG: reopen_logfile: closing old file"); fclose(syslog_file); syslog_file = new_file; msyslog(LOG_INFO, "LOG: reopen_logfile: using %s", syslog_fname); } ntpsec-1.1.0+dfsg1/libntp/pymodule.c0000644000175000017500000001175613252364117017131 0ustar rlaagerrlaager/* * This file is Copyright (c) 2016 by the NTPsec project * SPDX-License-Identifier: BSD-2-clause * * Python binding for selected libntp library functions */ #include #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 "isc_error.h" #include "ntp_control.h" #include "python_compatibility.h" const char *progname = "libntpc"; /* * Client utility functions */ static PyObject * ntpc_setprogname(PyObject *self, PyObject *args) { char *s; UNUSED_ARG(self); if (!PyArg_ParseTuple(args, "s", &s)) return NULL; progname = strdup(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; Py_RETURN_NONE; } static PyObject * ntpc_statustoa(PyObject *self, PyObject *args) { int status1, status2; char *gs; UNUSED_ARG(self); if (!PyArg_ParseTuple(args, "ii", &status1, &status2)) return NULL; gs = statustoa(status1, status2); return Py_BuildValue("s", gs); } static PyObject * ntpc_prettydate(PyObject *self, PyObject *args) { char *s; l_fp ts; UNUSED_ARG(self); if (!PyArg_ParseTuple(args, "s", &s)) return NULL; if (hextolfp(s+2, &ts)) return Py_BuildValue("s", prettydate(ts)); else { PyErr_SetString(PyExc_ValueError, "ill-formed hex date"); return NULL; } } static PyObject * ntpc_lfptofloat(PyObject *self, PyObject *args) { char *s; l_fp ts; struct timespec tt; UNUSED_ARG(self); if (!PyArg_ParseTuple(args, "s", &s)) return NULL; if (!hextolfp(s+2, &ts)) { PyErr_SetString(PyExc_ValueError, "ill-formed hex date"); return NULL; } tt = lfp_stamp_to_tspec(ts, time(NULL)); return Py_BuildValue("d", tt.tv_sec + tt.tv_nsec * S_PER_NS); } static PyObject * ntpc_set_tod(PyObject *self, PyObject *args) { struct timespec ts; UNUSED_ARG(self); if (!PyArg_ParseTuple(args, "ii", &ts.tv_sec, &ts.tv_nsec)) return NULL; return Py_BuildValue("d", ntp_set_tod(&ts)); } static PyObject * ntpc_adj_systime(PyObject *self, PyObject *args) { double adjustment; UNUSED_ARG(self); if (!PyArg_ParseTuple(args, "d", &adjustment)) return NULL; return Py_BuildValue("d", adj_systime(adjustment, adjtime) ? 1 : 0); } static PyObject * ntpc_step_systime(PyObject *self, PyObject *args) { double adjustment; doubletime_t full_adjustment; UNUSED_ARG(self); /* * 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 * utilties if and when the time difference between the Unix epoch * and now exceeds the range of a double. */ if (!PyArg_ParseTuple(args, "d", &adjustment)) return NULL; full_adjustment = adjustment; return Py_BuildValue("d", step_systime(full_adjustment, ntp_set_tod)); } int32_t ntp_random(void) /* stub random function for get_systime() */ { return 0; } /* List of functions defined in the module */ static PyMethodDef ntpc_methods[] = { {"setprogname", ntpc_setprogname, METH_VARARGS, PyDoc_STR("Set program name for logging purposes.")}, {"statustoa", ntpc_statustoa, METH_VARARGS, PyDoc_STR("Status string display from peer status word.")}, {"prettydate", ntpc_prettydate, METH_VARARGS, PyDoc_STR("Convert a time stamp to something readable.")}, {"lfptofloat", ntpc_lfptofloat, METH_VARARGS, PyDoc_STR("NTP l_fp to Python-style float time.")}, {"set_tod", ntpc_set_tod, METH_VARARGS, PyDoc_STR("Set time to nanosecond precision.")}, {"adj_systime", ntpc_adj_systime, METH_VARARGS, PyDoc_STR("Adjust system time by slewing.")}, {"step_systime", ntpc_step_systime, METH_VARARGS, PyDoc_STR("Adjust system time by stepping.")}, {NULL, NULL, 0, NULL} /* sentinel */ }; PyDoc_STRVAR(module_doc, "Python wrapper for selected libntp C library routines.\n\ "); /* banishes pointless compiler warnings on various Python versions */ extern PyMODINIT_FUNC initntpc(void); extern PyMODINIT_FUNC PyInit_ntpc(void); // cppcheck-suppress unusedFunction NTPSEC_PY_MODULE_INIT(ntpc) { PyObject *m; /* Create the module and add the functions */ NTPSEC_PY_MODULE_DEF(m, "ntpc", module_doc, ntpc_methods) /* for statustoa() */ PyModule_AddIntConstant(m, "TYPE_SYS", TYPE_SYS); PyModule_AddIntConstant(m, "TYPE_PEER", TYPE_PEER); PyModule_AddIntConstant(m, "TYPE_CLOCK", TYPE_CLOCK); if (m == NULL) return NTPSEC_PY_MODULE_ERROR_VAL; return NTPSEC_PY_MODULE_SUCCESS_VAL(m); } ntpsec-1.1.0+dfsg1/libntp/python_compatibility.h0000644000175000017500000000235013252364117021540 0ustar rlaagerrlaager/* * python_compatibility.h -- macros for Python 2/3 compatibility * * This file is Copyright (c) 2016 by the NTPsec project * BSD terms apply: see the file COPYING in the distribution root for details. * * Definitions based on examples in "Supporting Python 3 - The Book Site" * http://python3porting.com/cextensions.html */ #ifndef GUARD_PYTHON_COMPATIBILITY_H #define GUARD_PYTHON_COMPATIBILITY_H #include #if PY_MAJOR_VERSION >= 3 #define NTPSEC_PY_MODULE_INIT(name) PyMODINIT_FUNC PyInit_##name(void) #define NTPSEC_PY_MODULE_DEF(mod, name, doc, methods) \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, NULL, NULL, NULL, NULL}; \ mod = PyModule_Create(&moduledef); #define NTPSEC_PY_MODULE_ERROR_VAL NULL #define NTPSEC_PY_MODULE_SUCCESS_VAL(val) val #define NTPSEC_PY_BYTE_FORMAT "y#" #else /* !Python 3 */ #define NTPSEC_PY_MODULE_INIT(name) PyMODINIT_FUNC init##name(void) #define NTPSEC_PY_MODULE_DEF(mod, name, doc, methods) \ mod = Py_InitModule3(name, methods, doc); #define NTPSEC_PY_MODULE_ERROR_VAL #define NTPSEC_PY_MODULE_SUCCESS_VAL(val) #define NTPSEC_PY_BYTE_FORMAT "s#" #endif /* !Python 3 */ #endif /* GUARD_PYTHON_COMPATIBILITY_H */ ntpsec-1.1.0+dfsg1/libntp/ntp_endian.c0000644000175000017500000000173013252364117017401 0ustar rlaagerrlaager/* ntp_endian.c - BSD-style big-endian encoding/decoding functions * * Copyright 2016 by the NTPsec project contributors * SPDX-License-Identifier: BSD-2-clause * * These are portable reimplementations of what BSD provides * in . */ #include #include "ntp_endian.h" uint16_t ntp_be16dec(const void *buf) { const uint8_t *b = (const uint8_t*)buf; return ((uint16_t)(b[0]) << 8) + (uint16_t)(b[1]); } uint32_t ntp_be32dec(const void *buf) { const uint8_t *b = (const uint8_t*)buf; return ((uint32_t)(b[0]) << 24) + ((uint32_t)(b[1]) << 16) + ((uint32_t)(b[2]) << 8) + (uint32_t)(b[3]); } uint64_t ntp_be64dec(const void *buf) { const uint8_t *b = (const uint8_t*)buf; return ((uint64_t)(b[0]) << 56) + ((uint64_t)(b[1]) << 48) + ((uint64_t)(b[2]) << 40) + ((uint64_t)(b[3]) << 32) + ((uint64_t)(b[4]) << 24) + ((uint64_t)(b[5]) << 16) + ((uint64_t)(b[6]) << 8) + (uint64_t)(b[7]); } ntpsec-1.1.0+dfsg1/libntp/ssl_init.c0000644000175000017500000000202613252364117017105 0ustar rlaagerrlaager/* * ssl_init.c Common OpenSSL initialization code for the various * programs which use it. * * Moved from ntpd/ntp_crypto.c crypto_setup() */ #include "config.h" #include "ntp_stdlib.h" #include #include #ifndef EVP_MD_CTX_new /* Slightly older version of OpenSSL */ /* Similar hack in macencrypt.c and attic/digest-timing.c */ #define EVP_MD_CTX_new() EVP_MD_CTX_create() #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) static void atexit_ssl_cleanup(void); #endif static bool ssl_init_done; EVP_MD_CTX *digest_ctx; void ssl_init(void) { if (ssl_init_done) return; #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) OpenSSL_add_all_digests(); atexit(&atexit_ssl_cleanup); #endif digest_ctx = EVP_MD_CTX_new(); ssl_init_done = true; } #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) static void atexit_ssl_cleanup(void) { if (!ssl_init_done) return; ssl_init_done = false; EVP_cleanup(); } #endif ntpsec-1.1.0+dfsg1/libntp/strl_obsd.c0000644000175000017500000000412613252364117017257 0ustar rlaagerrlaager/* * Why use strlcpy()/strlcat() instead of standard strncpy()/strncat()? * To reduce likelihood of bugs and avoid wasteful zero fills. See: * http://www.gratisoft.us/todd/papers/strlcpy.html */ /* * Copyright (c) 1998 Todd C. Miller * * Copyright 2015 by the NTPsec project contributors * SPDX-License-Identifier: ISC */ #include "config.h" /* + marks local changes */ #include #include #include "ntp_stdlib.h" /* + strlcpy, strlcat prototypes */ #ifndef HAVE_STRLCPY /* + */ /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return((size_t)(s - src - 1)); /* count does not include NUL */ } #endif /* + */ /* #include */ /* + */ /* #include */ /* + */ #ifndef HAVE_STRLCAT /* + */ /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = (size_t)(d - dst); n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (size_t)(s - src)); /* count does not include NUL */ } #endif /* + */ ntpsec-1.1.0+dfsg1/wscript0000644000175000017500000012275413252364117015256 0ustar rlaagerrlaagerfrom __future__ import print_function import itertools import os import shlex import sys import time from waflib import Build from waflib import Context from waflib import Scripting from waflib import Utils from waflib.Build import (BuildContext, CleanContext, InstallContext, StepContext, ListContext) from waflib.Context import BOTH from waflib.Errors import WafError from waflib.Logs import pprint from waflib.Tools import waf_unit_test # Avoid writing .pyc files in wafhelpers/ sys.dont_write_bytecode = True from wafhelpers.options import options_cmd from wafhelpers.probes \ import probe_header_with_prerequisites, probe_function_with_prerequisites from wafhelpers.test import test_write_log, test_print_log pprint.__doc__ = None APPNAME='ntpsec' out = "build" config = { "out": out, "OPT_STORE": {} } def help(ctx): "Be helpful, give a usage" print(''' Usage: waf build Build the project check Run tests configure Configure the project dist Create a release install Install the project loccount Show SLOC count of the source tree ''') def options(ctx): options_cmd(ctx, config) ctx.recurse("pylib") def configure(ctx): class oc(Build.BuildContext): cmd = 'oc' def exec_command(self, cmd, **kw): kw['output'] = BOTH try: err, out = self.cmd_and_log(cmd, **kw) except WafError as e: self.logger.debug('WafError') return e.returncode if (len(out) and any(word in out for word in ['err', 'err:', 'error', 'error:', 'ignored', 'illegal', 'unknown', 'unrecognized', 'warning'])): self.logger.debug('noooo %r' % out) return 1 if err: self.logger.debug('noooo %r' % err) return 1 return 0 def msg(str): pprint("YELLOW", str) def msg_setting(name, val): pprint("NORMAL", " %-30s: " % name, sep="") pprint("YELLOW", val) srcnode = ctx.srcnode.abspath() bldnode = ctx.bldnode.abspath() ctx.run_build_cls = 'check' ctx.load('waf', tooldir='wafhelpers/') ctx.load('waf_unit_test') ctx.load('pytest') ctx.load('gnu_dirs') with open("VERSION", "r") as f: ctx.env.NTPSEC_VERSION = f.read().split(" ")[0].strip() ctx.env.OPT_STORE = config["OPT_STORE"] opt_map = {} # Wipe out and override flags with those from the commandline for flag in ctx.env.OPT_STORE: if flag == "--undefine": for sym in ctx.env.OPT_STORE[flag]: ctx.undefine(sym) elif flag == "--define": for symval in ctx.env.OPT_STORE[flag]: (sym, val) = symval.split("=") try: ctx.define(sym, int(val)) except ValueError: ctx.define(sym, val) else: opt = flag.replace("--", "").upper() opt_map[opt] = ctx.env.OPT_STORE[flag] msg("--- Configuring host ---") ctx.setenv('host', ctx.env.derive()) ctx.load('compiler_c') ctx.start_msg('Checking compiler version') ctx.end_msg("%s" % ".".join(ctx.env.CC_VERSION)) # Ensure m4 is present, or bison will fail with SIGPIPE ctx.find_program('m4') ctx.load('bison') for opt in opt_map: ctx.env[opt] = opt_map[opt] if ctx.options.enable_rtems_trace: ctx.find_program("rtems-tld", var="BIN_RTEMS_TLD", path_list=[ctx.options.rtems_trace_path, '${BINDIR}']) ctx.env.RTEMS_TEST_ENABLE = True ctx.env.RTEMS_TEST_FLAGS = [ "-C", "%s/devel/trace/ntpsec-trace.ini" % srcnode, "-W", "%s/ntpsec-wrapper" % bldnode, "-P", "%s/devel/trace/" % srcnode, "-f", "-I%s" % bldnode, "-f", "-I%s/include/" % srcnode, ] # Not needed to build. Used by utility scripts. ctx.find_program("awk", var="BIN_AWK", mandatory=False) ctx.find_program("sh", var="BIN_SH", mandatory=False) # used to make man and html pages ctx.find_program("asciidoc", var="BIN_ASCIIDOC", mandatory=False) # make sure asciidoc is new enough. # based on check_python_version() from waf if ctx.env.BIN_ASCIIDOC: # https://lists.ntpsec.org/pipermail/devel/2016-July/001778.html asciidocminver = (8, 6, 0) # Get asciidoc version string cmd = ctx.env.BIN_ASCIIDOC + ['--version'] # example output: asciidoc 8.6.9 lines = ctx.cmd_and_log(cmd).split()[1].split(".") assert len(lines) == 3, "found %r lines, expected 3: %r" \ % (len(lines), lines) asciidocver_tuple = (int(lines[0]), int(lines[1]), int(lines[2])) # Compare asciidoc version with the minimum required result = (asciidocver_tuple >= asciidocminver) asciidocver_full = '.'.join(map(str, asciidocver_tuple[:3])) asciidocminver_str = '.'.join(map(str, asciidocminver)) ctx.msg('Checking for asciidoc version >= %s' % (asciidocminver_str,), asciidocver_full, color=result and 'GREEN' or 'YELLOW') if not result: del ctx.env.BIN_ASCIIDOC ctx.find_program("a2x", var="BIN_A2X", mandatory=False) ctx.find_program("xsltproc", var="BIN_XSLTPROC", mandatory=False) ctx.env.ENABLE_DOC = False if ctx.env.BIN_ASCIIDOC and ctx.env.BIN_XSLTPROC and ctx.env.BIN_A2X: ctx.env.ENABLE_DOC = True if ctx.options.enable_doc and not ctx.env.ENABLE_DOC: ctx.fatal("asciidoc and xsltproc are required in order " "to build documentation") elif ctx.options.enable_doc: ctx.env.ASCIIDOC_FLAGS = ["-f", "%s/docs/asciidoc.conf" % ctx.srcnode.abspath()] ctx.env.ENABLE_DOC_USER = ctx.options.enable_doc # XXX: conditionally build this with --disable-man? # Should it build without docs enabled? ctx.env.A2X_FLAGS = ["--format", "manpage", "--asciidoc-opts=--conf-file=%s/docs/asciidoc.conf" % ctx.srcnode.abspath()] if not ctx.options.enable_a2x_xmllint: ctx.env.A2X_FLAGS += ["--no-xmllint"] # Disable manpages within build() if ctx.options.disable_manpage: ctx.env.DISABLE_MANPAGE = True if ((os.path.exists(".git") and ctx.find_program("git", var="BIN_GIT", mandatory=False))): ctx.start_msg("DEVEL: Getting revision") cmd = shlex.split("git log -1 --format=%H") ctx.env.NTPSEC_REVISION = ctx.cmd_and_log(cmd).strip() ctx.end_msg(ctx.env.NTPSEC_REVISION) ctx.start_msg("Building version") ctx.env.NTPSEC_VERSION_STRING = ctx.env.NTPSEC_VERSION if ctx.env.NTPSEC_REVISION: ctx.env.NTPSEC_VERSION_STRING += "-%s" % ctx.env.NTPSEC_REVISION[:7] if ctx.options.build_version_tag: ctx.env.NTPSEC_VERSION_STRING += "-%s" % ctx.options.build_version_tag ctx.define("NTPSEC_VERSION_STRING", ctx.env.NTPSEC_VERSION_STRING) ctx.end_msg(ctx.env.NTPSEC_VERSION_STRING) # We require some things that C99 doesn't enable, like pthreads. # These flags get propagated to both the host and main parts of the build. # # _POSIX_C_SOURCE # If ==1, like _POSIX_SOURCE; # if >=2 add IEEE Std 1003.2; # if >=199309L, add IEEE Std 1003.1b-1993; # if >=199506L, add IEEE Std 1003.1c-1995; # if >=200112L, all of IEEE 1003.1-2004 # if >=200809L, all of IEEE 1003.1-2008 # # FIXME: We'd like this to be -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=600 # rather than -D_GNU_SOURCE, but that runs into problems in two places: # (1) The ISC net handling stuff, where struct in6_addr’ loses a member # named s6_addr32 that the macros need, and (2) three BSD functions # related to chroot jailing in the sandbox code. # # Note that _POSIX_C_SOURCE >= 199506L and _GNU_SOURCE both turn on # _POSIX_PTHREAD_SEMANTICS and _REENTRANT # ctx.env.CFLAGS = ["-std=c99", "-D_GNU_SOURCE"] + ctx.env.CFLAGS msg("--- Configuring main ---") ctx.setenv("main", ctx.env.derive()) from wafhelpers.check_sizeof import check_sizeof for opt in opt_map: ctx.env[opt] = opt_map[opt] if ctx.options.cross_compiler: ctx.env.ENABLE_CROSS = True ctx.start_msg("Using Cross compiler CC:") # ctx.get_cc_version(ctx.env.CC, gcc=True) ctx.end_msg(ctx.options.cross_compiler) ctx.env.CC = shlex.split(ctx.options.cross_compiler) ctx.env.LINK_CC = shlex.split(ctx.options.cross_compiler) if ctx.env["CROSS-CFLAGS"]: # Lexically split each part of the CFLAGS, then chain the lists iter = [shlex.split(x) for x in opt_map["CROSS-CFLAGS"]] ctx.env.CFLAGS = list(itertools.chain.from_iterable(iter)) if ctx.env["CROSS-LDFLAGS"]: # Lexically split each part of the LDFLAGS, then chain the lists iter = [shlex.split(x) for x in opt_map["CROSS-LDFLAGS"]] ctx.env.LDFLAGS = list(itertools.chain.from_iterable(iter)) if ctx.options.list: from wafhelpers.refclock import refclock_map print("ID Description") print("~~ ~~~~~~~~~~~") for id in refclock_map: print("%-5s %s" % (id, refclock_map[id]["descr"])) return # These are required by various refclocks # needs to be tested before CFLAGS are set if ctx.check_endianness() == "big": ctx.define("WORDS_BIGENDIAN", 1) if ctx.options.enable_leap_testing: ctx.define("ENABLE_LEAP_TESTING", 1, comment="Enable leap seconds on other than 1st of month.") # check for some libs first. some options, like stack protector, # may depend on some libs, like -lssp ctx.check_cc(lib="m", comment="Math library") ctx.check_cc(lib="rt", mandatory=False, comment="realtime library") ctx.check_cc(lib="execinfo", mandatory=False, comment="BSD backtrace library") ret = ctx.check_cc(lib="bsd", mandatory=False, comment="BSD compatibility library") if ret: ctx.env.LDFLAGS += ["-lbsd"] # -lssp and -lssp_nonshared may be needed by older gcc to # support "-fstack-protector-all" ret = ctx.check_cc(lib="ssp", mandatory=False, comment="libssp") if ret: ctx.env.LDFLAGS += ["-lssp"] ret = ctx.check_cc(lib="ssp_nonshared", mandatory=False, comment="libssp_nonshared") if ret: ctx.env.LDFLAGS += ["-lssp_nonshared"] cc_test_flags = [ ('f_stack_protector_all', '-fstack-protector-all'), ('PIC', '-fPIC'), ('PIE', '-pie -fPIE'), # this quiets most of macOS warnings on -fpie ('unused', '-Qunused-arguments'), # This is a useless warning on any architecture with a barrel # shifter, which includes Intel and ARM and basically # everything nowadays. Even so, we'd enable it out of # perfectionism, but the GCC directives that ought to be # useful for forcing structure alignment in order to suppress # it locally don't seem to be working quite right. #('w_cast_align', "-Wcast-align"), ('w_cast_qual', "-Wcast-qual"), ('w_disabled_optimization', "-Wdisabled-optimization"), ('w_float_equal', "-Wfloat-equal"), ('w_format', '-Wformat'), ('w_format_security', '-Wformat-security'), # fails on OpenBSD 6 ('w_format_signedness', '-Wformat-signedness'), ('w_implicit_function_declaration', "-Wimplicit-function-declaration"), ('w_init_self', '-Winit-self'), ('w_invalid_pch', '-Winvalid-pch'), ('w_missing_declarations', '-Wmissing-declarations'), ('w_multichar', '-Wmultichar'), ('w_packed', '-Wpacked'), ('w_pointer_arith', '-Wpointer-arith'), ('w_shadow', '-Wshadow'), # fails on clang ('w_suggest_attribute_noreturn', "-Wsuggest-attribute=noreturn"), ('w_write_strings', '-Wwrite-strings'), ] # Check which linker flags are supported ld_hardening_flags = [ ("z_now", "-Wl,-z,now"), # no deferred symbol resolution ] # we prepend our options to CFLAGS, this allows user provided # CFLAGS to override our computed CFLAGS if ctx.options.enable_debug_gdb: ctx.env.CFLAGS = ["-g"] + ctx.env.CFLAGS ctx.define("USEBACKTRACE", "1", quote=False) else: # not gdb debugging cc_test_flags += [ ('LTO', '-flto'), # link time optimization ] ld_hardening_flags += [ ('stripall', "-Wl,--strip-all"), # Strip binaries ] if ctx.options.enable_debug: ctx.define("DEBUG", 1, comment="Enable debug mode") ctx.env.BISONFLAGS += ["--debug"] if ctx.options.enable_warnings: # turn on some annoying warnings ctx.env.CFLAGS = [ # "-Wall", # for masochists # "-Waggregate-return", # breaks ldiv(), ntpcal_daysplit(), etc. # "-Wcast-align", # fails on RasPi, needs fixing. # "-Wbad-function-cast", # ntpd casts long<->double a lot # "-Wformat-nonliteral", # complains about a used feature "-Winline", # some OS have inline issues. # "-Wmissing-format-attribute", # false positives # "-Wnested-externs", # incompatible w/ Unity... # "-Wpadded", # duck... over 3k warnings # "-Wredundant-decls", # incompatible w/ Unity "-Wswitch-default", # warns on Bison bug ] + ctx.env.CFLAGS cc_test_flags += [ ('w_implicit_fallthru', "-Wimplicit-fallthrough=3"), # Fails on Solaris, OpenBSD 6, and RasPi # Complains about a Bison bug # Cannot be suppressed # ('w_sign_conversion', "-Wsign-conversion"), # fails on clang, lots of false positives and Unity complaints # ('w_suggest_attribute_const', "-Wsuggest-attribute=const"), # fails on clang, lot's of false positives and Unity complaints # ('w_suggest_attribute_pure', "-Wsuggest-attribute=pure"), ] ctx.env.CFLAGS = [ # -O1 will turn on -D_FORTIFY_SOURCE=2 for us "-O1", "-Wall", "-Wextra", "-Wmissing-prototypes", "-Wstrict-prototypes", "-Wundef", "-Wunused", ] + ctx.env.CFLAGS # gotta be tricky to test for -Wsuggest-attribute=const FRAGMENT = ''' int tmp; int main(int argc, char **argv) { (void)argc; (void)argv; tmp = argc; return argc; } ''' # check if C compiler supports some flags old_run_build_cls = ctx.run_build_cls ctx.run_build_cls = 'oc' for (name, ccflag) in cc_test_flags: ctx.check(cflags=ccflag, define_name='HAS_' + name, fragment=FRAGMENT, mandatory=False, msg='Checking if C compiler supports ' + ccflag, run_build_cls='oc') ctx.run_build_cls = old_run_build_cls if ctx.env.HAS_PIC: ctx.env.CFLAGS = ["-fPIC"] + ctx.env.CFLAGS if ctx.env.HAS_PIE: ctx.env.LINKFLAGS_NTPD += [ "-pie", ] ctx.env.CFLAGS_bin = ["-fPIE", "-pie"] + ctx.env.CFLAGS ld_hardening_flags += [ ('relro', "-Wl,-z,relro"), # hardening, marks some read only, ] if ctx.env.HAS_unused: ctx.env.CFLAGS = ['-Qunused-arguments'] + ctx.env.CFLAGS # XXX: -flto currently breaks link of ntpd if ctx.env.HAS_LTO and False: ctx.env.CFLAGS = ["-flto"] + ctx.env.CFLAGS # debug warnings that are not available with all compilers if ctx.env.HAS_w_implicit_fallthru: ctx.env.CFLAGS = ['-Wimplicit-fallthrough=3'] + ctx.env.CFLAGS if ctx.env.HAS_w_suggest_attribute_const: ctx.env.CFLAGS = ['-Wsuggest-attribute=const'] + ctx.env.CFLAGS if ctx.env.HAS_w_suggest_attribute_noreturn: ctx.env.CFLAGS = ['-Wsuggest-attribute=noreturn'] + ctx.env.CFLAGS if ctx.env.HAS_w_suggest_attribute_pure: ctx.env.CFLAGS = ['-Wsuggest-attribute=pure'] + ctx.env.CFLAGS if ctx.env.HAS_w_format_security: ctx.env.CFLAGS = ['-Wformat-security'] + ctx.env.CFLAGS if ctx.env.HAS_w_format_signedness: ctx.env.CFLAGS = ['-Wformat-signedness'] + ctx.env.CFLAGS # should be before other -Wformat-* in CFLAGS if ctx.env.HAS_w_format: ctx.env.CFLAGS = ['-Wformat'] + ctx.env.CFLAGS if ctx.env.HAS_w_float_equal: ctx.env.CFLAGS = ['-Wfloat-equal'] + ctx.env.CFLAGS if ctx.env.HAS_w_init_self: ctx.env.CFLAGS = ['-Winit-self'] + ctx.env.CFLAGS if ctx.env.HAS_w_write_strings: ctx.env.CFLAGS = ['-Wwrite-strings'] + ctx.env.CFLAGS if ctx.env.HAS_w_pointer_arith: ctx.env.CFLAGS = ['-Wpointer-arith'] + ctx.env.CFLAGS if ctx.env.HAS_w_invalid_pch: ctx.env.CFLAGS = ['-Winvalid-pch'] + ctx.env.CFLAGS if ctx.env.HAS_w_implicit_function_declaration: ctx.env.CFLAGS = ['-Wimplicit-function-declaration'] + ctx.env.CFLAGS if ctx.env.HAS_w_disabled_optimization: ctx.env.CFLAGS = ['-Wdisabled-optimization'] + ctx.env.CFLAGS #if ctx.env.HAS_w_cast_align: # ctx.env.CFLAGS = ['-Wcast-align'] + ctx.env.CFLAGS if ctx.env.HAS_w_missing_declarations: ctx.env.CFLAGS = ['-Wmissing-declarations'] + ctx.env.CFLAGS if ctx.env.HAS_w_cast_qual: ctx.env.CFLAGS = ['-Wcast-qual'] + ctx.env.CFLAGS if ctx.env.HAS_w_packed: ctx.env.CFLAGS = ['-Wpacked'] + ctx.env.CFLAGS if ctx.env.HAS_w_shadow: ctx.env.CFLAGS = ['-Wshadow'] + ctx.env.CFLAGS #if ctx.env.HAS_w_sign_conversion: # ctx.env.CFLAGS = ['-Wsign-conversion'] + ctx.env.CFLAGS if ctx.env.HAS_f_stack_protector_all: ctx.env.CFLAGS = ['-fstack-protector-all'] + ctx.env.CFLAGS # old gcc takes -z,relro, but then barfs if -fPIE available and used. # ("relro", "-Wl,-z,relro"), # marks some sections read only old_run_build_cls = ctx.run_build_cls ctx.run_build_cls = 'oc' for (name, ldflag) in ld_hardening_flags: ctx.check(define_name='HAS_' + name, fragment=FRAGMENT, ldflags=ldflag, mandatory=False, msg='Checking if linker supports ' + ldflag, run_build_cls='oc') if ctx.env['HAS_' + name]: ctx.env.LDFLAGS += [ldflag] ctx.run_build_cls = old_run_build_cls if ctx.env.CC_NAME == "sun": # we are sun, placeholder ctx.env.CFLAGS += [] elif ctx.env.CC_NAME == "clang": # used on macOS, FreeBSD, # FORTIFY needs LTO to work well if ctx.env.DEST_OS not in ["darwin", "freebsd"]: # -flto and friends breaks tests on macOS #ctx.env.CFLAGS = [ # "-flto" # "-fsanitize=cfi", # hardening # "-fsanitize=safe-stack", # hardening # ] + ctx.env.CFLAGS ctx.env.LDFLAGS += [ "-Wl,-z,relro", # hardening, marks some section read only, ] # else: # gcc, probably # Exclude Unity's support for printing floating point numbers since it triggers warnings # with -Wfloat-equal ctx.env.CFLAGS = ['-DUNITY_EXCLUDE_FLOAT_PRINT'] + ctx.env.CFLAGS # XXX: hack if ctx.env.DEST_OS in ["freebsd", "openbsd"]: ctx.env.PLATFORM_INCLUDES = ["/usr/local/include"] ctx.env.PLATFORM_LIBPATH = ["/usr/local/lib"] elif ctx.env.DEST_OS == "netbsd": ctx.env.PLATFORM_INCLUDES = ["/usr/pkg/include"] ctx.env.PLATFORM_LIBPATH = ["/usr/lib", "/usr/pkg/lib"] elif ctx.env.DEST_OS == "win32": ctx.load("msvc") elif ctx.env.DEST_OS == "darwin": # macports location if os.path.isdir("/opt/local/include"): ctx.env.PLATFORM_INCLUDES = ["/opt/local/include"] if os.path.isdir("/opt/local/lib"): ctx.env.PLATFORM_LIBPATH = ["/opt/local/lib"] # OS X needs this for IPv6 ctx.define("__APPLE_USE_RFC_3542", 1, comment="Needed for IPv6 support") elif ctx.env.DEST_OS == "sunos": # Declare compatibility with the POSIX.1-2001 standard, and any # headers/interfaces not in conflict with that standard ctx.define("_POSIX_C_SOURCE", "200112L", quote=False) ctx.define("__EXTENSIONS__", "1", quote=False) structures = ( ("struct if_laddrconf", ["sys/types.h", "net/if6.h"], False), ("struct if_laddrreq", ["sys/types.h", "net/if6.h"], False), ("struct timex", ["sys/time.h", "sys/timex.h"], True), ("struct ntptimeval", ["sys/time.h", "sys/timex.h"], False), ) for (s, h, r) in structures: ctx.check_cc(type_name=s, header_name=h, mandatory=r) # waf's SNIP_FIELD should likely include this header itself # This is needed on some systems to get size_t for following checks ctx.check_cc(auto_add_header_name=True, header_name="stddef.h", define_name="", # omit from config.h mandatory=False) structure_fields = ( ("struct timex", "time_tick", ["sys/time.h", "sys/timex.h"]), ("struct timex", "modes", ["sys/time.h", "sys/timex.h"]), ("struct ntptimeval", "time.tv_nsec", ["sys/time.h", "sys/timex.h"]), ("struct ntptimeval", "tai", ["sys/time.h", "sys/timex.h"]), # first in glibc 2.12 ) for (s, f, h) in structure_fields: ctx.check_cc(type_name=s, field_name=f, header_name=h, mandatory=False) # mostly used by timetoa.h and timespecops.h sizeofs = [ ("time.h", "time_t"), (None, "long"), ] for header, sizeof in sorted(sizeofs, key=lambda x: x[1:]): check_sizeof(ctx, header, sizeof) # These are helpful and don't break Linux or *BSD ctx.define("OPEN_BCAST_SOCKET", 1, comment="Whether to open a broadcast socket") # Check via pkg-config first, then fall back to a direct search if not ctx.check_cfg( package='libcrypto', uselib_store='CRYPTO', args=['libcrypto', '--cflags', '--libs'], msg="Checking for OpenSSL (via pkg-config)", define_name='', mandatory=False, ): openssl_headers = ( "openssl/evp.h", "openssl/rand.h", "openssl/objects.h", ) for hdr in openssl_headers: ctx.check_cc(header_name=hdr, includes=ctx.env.PLATFORM_INCLUDES) # FIXME! Ignoring the result... ctx.check_cc(msg="Checking for OpenSSL's crypto library", lib="crypto") # Optional functions. Do all function checks here, otherwise # we're likely to duplicate them. functions = ( ('_Unwind_Backtrace', ["unwind.h"]), ('adjtimex', ["sys/time.h", "sys/timex.h"]), ('backtrace_symbols_fd', ["execinfo.h"]), ('closefrom', ["stdlib.h"]), ('clock_gettime', ["time.h"], "RT"), ('clock_settime', ["time.h"], "RT"), ('ntp_adjtime', ["sys/time.h", "sys/timex.h"]), # BSD ('ntp_gettime', ["sys/time.h", "sys/timex.h"]), # BSD ('res_init', ["netinet/in.h", "arpa/nameser.h", "resolv.h"]), ('sched_setscheduler', ["sched.h"]), ('strlcpy', ["string.h"]), ('strlcat', ["string.h"]), ('timer_create', ["time.h"]) ) for ft in functions: if len(ft) == 2: probe_function_with_prerequisites(ctx, function=ft[0], prerequisites=ft[1]) else: probe_function_with_prerequisites(ctx, function=ft[0], prerequisites=ft[1], use=ft[2]) # check for BSD versions outside of libc if not ctx.get_define("HAVE_STRLCAT"): ret = probe_function_with_prerequisites(ctx, function='strlcat', prerequisites=['bsd/string.h']) if ret: ctx.define("HAVE_STRLCAT", 1, comment="Using bsd/strlcat") if not ctx.get_define("HAVE_STRLCPY"): ret = probe_function_with_prerequisites(ctx, function='strlcpy', prerequisites=['bsd/string.h']) if ret: ctx.define("HAVE_STRLCPY", 1, comment="Using bsd/strlcpy") # Nobody uses the symbol, but this seems like a good sanity check. ctx.check_cc(header_name="stdbool.h", mandatory=True, comment="Sanity check.") # This is a list of every optional include header in the # codebase that is guarded by a directly corresponding HAVE_*_H symbol. # # In some cases one HAVE symbol controls inclusion of more # than one header. In these cases only the one header name # matching the pattern of the HAVE_*_H symbol name is listed # here, so we can invert the relationship to generate tests # for all the symbols. # # Some of these are cruft from ancient big-iron systems and should # be removed. optional_headers = ( "alloca.h", ("arpa/nameser.h", ["sys/types.h"]), "dns_sd.h", # NetBSD, Apple, mDNS "bsd/string.h", # bsd emulation ("ifaddrs.h", ["sys/types.h"]), ("linux/if_addr.h", ["sys/socket.h"]), ("linux/rtnetlink.h", ["sys/socket.h"]), "linux/serial.h", "net/if6.h", ("net/route.h", ["sys/types.h", "sys/socket.h", "net/if.h"]), "netinfo/ni.h", # Apple "priv.h", # Solaris "semaphore.h", "stdatomic.h", "sys/clockctl.h", # NetBSD "sys/ioctl.h", "sys/modem.h", # Apple "sys/sockio.h", ("sys/sysctl.h", ["sys/types.h"]), ("timepps.h", ["inttypes.h"]), ("sys/timepps.h", ["inttypes.h", "sys/time.h"]), ("sys/timex.h", ["sys/time.h"]), ) for hdr in optional_headers: if isinstance(hdr, str): if ctx.check_cc(header_name=hdr, mandatory=False, comment="<%s> header" % hdr): continue else: (hdr, prereqs) = hdr if probe_header_with_prerequisites(ctx, hdr, prereqs): continue if os.path.exists("/usr/include/" + hdr): # Sanity check... print("Compilation check failed but include exists %s" % hdr) if ((ctx.get_define("HAVE_TIMEPPS_H") or ctx.get_define("HAVE_SYS_TIMEPPS_H"))): ctx.define("HAVE_PPSAPI", 1, comment="Enable the PPS API") # Check for Solaris capabilities if ctx.get_define("HAVE_PRIV_H") and ctx.env.DEST_OS == "sunos": ctx.define("HAVE_SOLARIS_PRIVS", 1, comment="Enable Solaris Privileges (Solaris only)") from wafhelpers.check_sockaddr import check_sockaddr check_sockaddr(ctx) # Check for Solaris's service configuration facility library ctx.check_cc(header_name="libscf.h", lib="scf", mandatory=False, uselib_store="SCF") # Some systems don't have sys/timex.h eg OS X, OpenBSD... if ctx.get_define("HAVE_SYS_TIMEX_H"): ctx.env.HEADER_SYS_TIMEX_H = True if ctx.options.refclocks: from wafhelpers.refclock import refclock_config refclock_config(ctx) # NetBSD (used to) need to recreate sockets on changed routing. # Perhaps it still does. If so, this should be set. The autoconf # build set it "if the OS clears cached routes when more specifics # become available". # ctx.define("OS_MISSES_SPECIFIC_ROUTE_UPDATES", 1) if ctx.options.enable_leap_smear: ctx.define("ENABLE_LEAP_SMEAR", 1, comment="Enable experimental leap smearing code") if ctx.options.enable_mssntp: ctx.define("ENABLE_MSSNTP", 1, comment="Enable MS-SNTP extensions " " https://msdn.microsoft.com/en-us/library/cc212930.aspx") if ctx.options.enable_lockclock: if ctx.env.REFCLOCK_LOCAL: ctx.define("ENABLE_LOCKCLOCK", 1, comment="Enable NIST 'lockclock'") else: import waflib.Errors raise waflib.Errors.WafError( "NIST 'lockclock' requires refclock 'local'") if not ctx.options.disable_droproot: ctx.define("ENABLE_DROPROOT", 1, comment="Drop root after initialising") if ctx.options.enable_early_droproot: ctx.define("ENABLE_EARLY_DROPROOT", 1, comment="Enable early drop root") if not ctx.options.disable_dns_lookup: ctx.define("ENABLE_DNS_LOOKUP", 1, comment="Enable DNS lookup of hostnames") # This is true under every Unix-like OS. ctx.define("HAVE_WORKING_FORK", 1, comment="Whether a working fork() exists") # SO_REUSEADDR socket option is needed to open a socket on an # interface when the port number is already in use on another # interface. Linux needs this, NetBSD does not, status on # other platforms is unknown. It is probably harmless to # have it on everywhere. ctx.define("NEED_REUSEADDR_FOR_IFADDRBIND", 1, comment="Whether SO_REUSEADDR is needed to open " "same sockets on alternate interfaces, required " "by Linux at least") from wafhelpers.check_vsprintfm import check_vsprintfm check_vsprintfm(ctx) # Check for directory separator if ctx.env.DEST_OS == "win32": sep = "\\" else: sep = "/" ctx.define("DIR_SEP", "'%s'" % sep, quote=False, comment="Directory separator used") if ctx.get_define("HAVE_SYS_SYSCTL_H"): ctx.define("HAVE_IFLIST_SYSCTL", 1, comment="Whether sysctl interface exists") # Header/library checks if not ctx.options.disable_droproot and ctx.env.DEST_OS == "linux": ctx.check_cc(header_name="sys/prctl.h", mandatory=False) ctx.check_cc(header_name="sys/capability.h", mandatory=False) ctx.check_cc(lib="cap", comment="Capability library", mandatory=False) if ((ctx.get_define("HAVE_SYS_CAPABILITY_H") and ctx.get_define("HAVE_SYS_PRCTL_H") and ctx.env.LIB_CAP)): ctx.define("HAVE_LINUX_CAPABILITY", 1) if ctx.options.enable_seccomp: if ctx.env.DEST_OS != "linux": ctx.fatal("seccomp is only supported on Linux") # Check via pkg-config first, then fall back to a direct search if not ctx.check_cfg( package='libseccomp', args=['--libs', '--cflags'], uselib_store='SECCOMP', define_name='HAVE_SECCOMP_H', mandatory=False ): ctx.check_cc(header_name="seccomp.h") ctx.check_cc(lib="seccomp") from wafhelpers.check_pthread import check_pthread_header_lib check_pthread_header_lib(ctx) if not ctx.options.disable_mdns_registration: from wafhelpers.check_mdns import check_mdns_header check_mdns_header(ctx) if not ctx.options.disable_dns_lookup: from wafhelpers.check_pthread import check_pthread_run check_pthread_run(ctx) if not ctx.options.disable_mdns_registration: from wafhelpers.check_mdns import check_mdns_run check_mdns_run(ctx) # Solaris needs -lsocket and -lnsl for socket code if ctx.env.DEST_OS == "sunos": ctx.check(features="c cshlib", lib="socket", mandatory=False) ctx.check(features="c cshlib", lib="nsl", mandatory=False) if ctx.options.enable_classic_mode: ctx.define("ENABLE_CLASSIC_MODE", 1) else: ctx.undefine("ENABLE_CLASSIC_MODE") if ctx.options.enable_debug_timing: ctx.define("ENABLE_DEBUG_TIMING", 1) else: ctx.undefine("ENABLE_DEBUG_TIMING") # Ugly hack to examine config symbols for sym in ctx.env.DEFINES: if sym.startswith("NTP_SIZEOF_TIME_T="): timesize = int(sym.split("=")[1]) if timesize < 8: msg("WARNING: This system has a 32-bit time_t.") msg("WARNING: Your ntpd will fail on 2038-01-19T03:14:07Z.") source_date_epoch = os.getenv('SOURCE_DATE_EPOCH', None) if ctx.options.build_epoch is not None: ctx.define("BUILD_EPOCH", ctx.options.build_epoch, comment="Using --build-epoch") elif source_date_epoch: if not source_date_epoch.isdigit(): msg("ERROR: malformed SOURCE_DATE_EPOCH") sys.exit(1) ctx.define("BUILD_EPOCH", int(source_date_epoch), comment="Using SOURCE_DATE_EPOCH") else: ctx.define("BUILD_EPOCH", int(time.time()), comment="Using default") # before write_config() droproot_type = "" if ctx.is_defined("HAVE_LINUX_CAPABILITY"): droproot_type = "Linux" elif ctx.is_defined("HAVE_SOLARIS_PRIVS"): droproot_type = "Solaris" elif ctx.is_defined("HAVE_SYS_CLOCKCTL_H"): droproot_type = "NetBSD" else: droproot_type = "None" # write_config() removes symbols ctx.start_msg("Writing configuration header:") ctx.write_config_header("config.h") ctx.end_msg("config.h", "PINK") def yesno(x): if x: return "Yes" return "No" msg("") msg("Build Options") msg_setting("CC", " ".join(ctx.env.CC)) msg_setting("CFLAGS", " ".join(ctx.env.CFLAGS)) msg_setting("LDFLAGS", " ".join(ctx.env.LDFLAGS)) msg_setting("LINKFLAGS_NTPD", " ".join(ctx.env.LINKFLAGS_NTPD)) msg_setting("PREFIX", ctx.env.PREFIX) msg_setting("Droproot Support", droproot_type) msg_setting("Debug Support", yesno(ctx.options.enable_debug)) msg_setting("Refclocks", ", ".join(ctx.env.REFCLOCK_LIST)) msg_setting("Build Manpages", yesno(ctx.env.ENABLE_DOC and not ctx.env.DISABLE_MANPAGE)) ctx.recurse("pylib") # Convert the Python directories to absolute paths. # This makes them behave the same as PREFIX. ctx.env.PYTHONDIR = os.path.abspath(ctx.env.PYTHONDIR) ctx.env.PYTHONARCHDIR = os.path.abspath(ctx.env.PYTHONARCHDIR) msg_setting("PYTHONDIR", ctx.env.PYTHONDIR) msg_setting("PYTHONARCHDIR", ctx.env.PYTHONARCHDIR) class check(BuildContext): cmd = 'check' variant = "main" def bin_test(ctx): """Run binary check, use after tests.""" from wafhelpers.bin_test import cmd_bin_test cmd_bin_test(ctx, config) # Borrowed from https://www.rtems.org/ variant_cmd = ( ("build", BuildContext), ("clean", CleanContext), ("install", InstallContext), ("step", StepContext), ("list", ListContext), # ("check", BuildContext) ) for v in ["host", "main"]: # the reason for creating these subclasses is just for __doc__ below... for cmd, cls in variant_cmd: class tmp(cls): __doc__ = "%s %s" % (cmd, v) cmd = "%s_%s" % (cmd, v) variant = v def init_handler(ctx): cmd = ctx.cmd if cmd == 'init_handler': cmd = 'build' def make_context(name): for x in Context.classes: if x.cmd == name and x.fun != 'init_handler': return x() ctx.fatal('No class for %r' % cmd) # By default we want to iterate over each variant. for v in ["host", "main"]: obj = make_context(cmd) obj.variant = v pprint("YELLOW", "--- %sing %s ---" % (cmd, v)) obj.execute() commands = ( ("install", "init_handler", None), ("uninstall", "init_handler", None), ("build", "init_handler", None), ("clean", "init_handler", None), ("list", "init_handler", None), ("step", "init_handler", None), ) for command, func, descr in commands: class tmp1(Context.Context): if descr: __doc__ = descr cmd = command fun = func if ((command in 'install uninstall build clean list step' )): execute = Scripting.autoconfigure(Context.Context.execute) # end borrowed code def afterparty(ctx): # Make magic links to support in-tree testing. # # The idea is that all directories where the Python tools live should # have an 'ntp' symlink so they can import Python modules from the pylib # directory. # # Note that this setup is applied to the build tree, not the # source tree. Only the build-tree copies of the programs are # expected to work. if ctx.cmd == 'clean': ctx.exec_command("rm -f ntpd/version.h ") for x in ("ntpclients", "tests/pylib",): # List used to be longer... path_build = ctx.bldnode.make_node("pylib") path_source = ctx.bldnode.make_node(x + "/ntp") relpath = ("../" * (x.count("/")+1)) + path_build.path_from(ctx.bldnode) if ctx.cmd in ('install', 'build'): if ((not path_source.exists() or os.readlink(path_source.abspath()) != relpath)): try: os.remove(path_source.abspath()) except OSError: pass os.symlink(relpath, path_source.abspath()) python_scripts = [ "ntpclients/ntploggps.py", "ntpclients/ntpdig.py", "ntpclients/ntpkeygen.py", "ntpclients/ntpmon.py", "ntpclients/ntpq.py", "ntpclients/ntpsweep.py", "ntpclients/ntptrace.py", "ntpclients/ntpviz.py", "ntpclients/ntpwait.py", "ntpclients/ntplogtemp.py", "ntpclients/ntpsnmpd.py", ] def build(ctx): ctx.load('waf', tooldir='wafhelpers/') ctx.load('asciidoc', tooldir='wafhelpers/') ctx.load('rtems_trace', tooldir='wafhelpers/') 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.env.ENABLE_DOC_USER: if ctx.variant != "main": ctx.recurse("docs") if ctx.variant == "host": ctx.recurse("ntpd") return if ctx.env.REFCLOCK_GENERIC or ctx.env.REFCLOCK_TRIMBLE: # required by the generic and Trimble refclocks ctx.recurse("libparse") ctx.recurse("libntp") ctx.recurse("ntpd") ctx.recurse("ntpfrob") ctx.recurse("ntptime") ctx.recurse("pylib") ctx.recurse("attic") ctx.recurse("tests") # Make sure the python scripts compile, but don't install them ctx( features="py", source=python_scripts, install_path=None, ) scripts = ["ntpclients/ntpleapfetch"] + python_scripts ctx( features="subst", source=scripts, target=[x.replace('.py', '') for x in scripts], chmod=Utils.O755, install_path='${BINDIR}', ) ctx.add_post_fun(afterparty) if ctx.cmd == 'clean': afterparty(ctx) ctx.manpage(1, "ntpclients/ntploggps-man.txt") ctx.manpage(1, "ntpclients/ntpdig-man.txt") ctx.manpage(1, "ntpclients/ntpmon-man.txt") ctx.manpage(1, "ntpclients/ntpq-man.txt") ctx.manpage(1, "ntpclients/ntpsweep-man.txt") ctx.manpage(1, "ntpclients/ntptrace-man.txt") ctx.manpage(1, "ntpclients/ntpviz-man.txt") ctx.manpage(1, "ntpclients/ntplogtemp-man.txt") ctx.manpage(8, "ntpclients/ntpkeygen-man.txt") ctx.manpage(8, "ntpclients/ntpleapfetch-man.txt") ctx.manpage(8, "ntpclients/ntpwait-man.txt") ctx.manpage(8, "ntpclients/ntpsnmpd-man.txt") # Skip running unit tests on a cross compile build if not ctx.env.ENABLE_CROSS: # Force re-running of tests. Same as 'waf --alltests' if ctx.cmd == "check": ctx.options.all_tests = True # Print log if -v is supplied if ctx.options.verbose: ctx.add_post_fun(test_print_log) # Write test log to a file ctx.add_post_fun(test_write_log) # Print a summary at the end ctx.add_post_fun(waf_unit_test.summary) ctx.add_post_fun(waf_unit_test.set_exit_code) else: pprint("YELLOW", "Unit test runner skipped on a cross-compiled build.") from waflib import Options Options.options.no_tests = True if ctx.cmd == "build": if "PYTHONPATH" in os.environ: print("--- PYTHONPATH is not set, " "loading the Python ntp library may be troublesome ---") # # Boot script setup # def systemdenable(ctx): "Enable boot time start with systemd. Must run as root." ctx.exec_command("cp etc/ntpd.service etc/ntp-wait.service " "/usr/lib/systemd/system/") def systemddisable(ctx): "Disable boot time start with systemd. Must run as root." ctx.exec_command("rm -f /usr/lib/systemd/system/ntpd.service " "/usr/lib/systemd/system/ntp-wait.service") # # Miscellaneous utility productions # def ifdex(ctx): "Get a report on configuration symbols not accounted for." ctx.exec_command("ifdex -X build/config.h -X devel/ifdex-ignores .") # See https://gitlab.com/esr/loccount def loccount(ctx): "Report the SLOC count of the source tree." ctx.exec_command("loccount -x=build .") def cxfreeze(ctx): "Create standalone binaries from Python scripts." ctx.exec_command("for prog in " + " ".join(python_scripts) + "; do cxfreeze $prog; done") def linkcheck(ctx): "Report references without anchors in the documentation." ctx.exec_command("devel/linkcheck docs/") # The following sets edit modes for GNU EMACS # Local Variables: # mode:python # End: # end ntpsec-1.1.0+dfsg1/buildprep0000755000175000017500000001355513252364117015552 0ustar rlaagerrlaager#!/bin/sh # # buildprep - prepare your system for an NTPsec source build. # # Use the -n option to dry-run this command, showing what would be done # without actually doing it # Set the defaults DRYRUN="no" NTPVIZ="no" DOC="no" # Loop through option flags for optflag in "$@" do case "$optflag" in -h|--help) cat </dev/null then installer=emerge install="$do $installer -q y" elif yum version 2>/dev/null then installer=yum install="$do $installer install" elif dnf --version >/dev/null 2>&1 then installer=dnf install="$do $installer install" elif apt-get --version >/dev/null 2>&1 then installer=apt install="$do apt-get install -y" elif zypper -h >/dev/null 2>&1 then # OpenSUSE prefers zypper over yast installer=zypper install="$do $installer install -y" elif yast -h >/dev/null 2>&1 then installer=yast install="$do $installer --install" else echo "# ERROR: Package manager unidentified - Unsupported operating system" exit 1 fi echo "# Your package installer is ${installer}." echo "" # In order to have a single point of truth about prerequisite package names, # these package name lists have been *removed* from INSTALL. # Build time vs. run time: # Build dependencies are marked. You'll need them all when building from a # repository copy; the unmarked (run-time) dependencies are information for # packagers. Under Gentoo, all dependencies are build dependencies. # Notes on optional packages: # libdnssd is optional for ntpd. Support for mDNS Service Discovery registration # libcap (under Linux) enables dropping root and is not strictly required. daemon () { # Prerequisites to build the daemon: bison, pps-tools, service libraries case $installer in apt) $install build-essential # Build environment $install bison libssl-dev # build $install libcap-dev libseccomp-dev # build $install libavahi-compat-libdnssd-dev # optional build $install libssl1.0.0 libcap2 pps-tools ;; emerge) # Build environment included! $install sys-libs/libcap sys-libs/libseccomp $install sys-devel/bison net-misc/pps-tools ;; yum|dnf) $do $installer groupinstall "Development Tools" # Build environment $install bison openssl-devel # build $install libcap-devel libseccomp-devel # build $install pps-tools-devel # build $install avahi-compat-libdns_sd-devel # optional build $install libcap openssl-libs pps-tools ;; yast) echo "# SLES versions 12 and earlier do not have pps-tools" $install basis-devel # Build environment $install libcap-devel libseccomp-devel # build $install openssl-devel # build $install libcap2 openssl-libs ;; zypper) $install -t pattern devel_basis # Build environment $install bison # build $install libcap-devel libseccomp-devel # build $install openssl-devel # build echo "# SLES versions 12 and earlier do not have pps-tools" $install pps-tools-devel # build $install pps-tools $install libcap2 openssl-libs ;; esac } tools () { # Prerequisites for the client Python tools: python extensions case $installer in apt) $install python-dev ;; yum|dnf|yast|zypper) $install python-devel ;; esac } ntpviz () { # Prerequisites to use ntpviz: gnuplot and liberation fonts case $installer in apt) distro=`lsb_release -i -s` if [ "$distro" = "Ubuntu" ] then echo "# Looks like an Ubuntu system" $install gnuplot5 else echo "# Looks like a generic Debian system" $install gnuplot fi $install fonts-liberation ;; emerge) $install sci-visualization/gnuplot $install media-fonts/liberation-fonts ;; yum|dnf) $install gnuplot $install liberation-fonts-common.noarch $install liberation-mono-fonts.noarch $install liberation-narrow-fonts.noarch $install liberation-sans-fonts.noarch $install liberation-serif-fonts.noarch ;; yast|zypper) $install gnuplot liberation-fonts ;; esac } doc () { # prerequisites to build documentation case $installer in apt) $install asciidoc ;; emerge) $install app-text/asciidoc ;; yum|dnf) echo "# Please check that your asciidoc is at least 8.6.0" echo "# You may need to enable EPEL repositories" $install asciidoc ;; yast|zypper) $install asciidoc ;; esac } # Main sequence daemon tools if [ "$NTPVIZ" = "yes" ] then ntpviz else echo "" echo "# Skipping ntpviz dependencies [--ntpviz]" fi if [ "$DOC" = "yes" ] then doc else echo "" echo "# Skipping documentation dependencies [--doc]" fi echo "" echo "# Done." ntpsec-1.1.0+dfsg1/pylib/0000775000175000017500000000000013252650651014747 5ustar rlaagerrlaagerntpsec-1.1.0+dfsg1/pylib/statfiles.py0000644000175000017500000002141213252364117017314 0ustar rlaagerrlaager# -*- coding: utf-8 -*- """ statfiles.py - class for digesting and plotting NTP logfiles Requires gnuplot and liberation fonts installed. """ # SPDX-License-Identifier: BSD-2-Clause from __future__ import print_function, division import calendar import glob import gzip import os import socket import sys import time class NTPStats: "Gather statistics for a specified NTP site" SecondsInDay = 24*60*60 DefaultPeriod = 7*24*60*60 # default 7 days, 604800 secs peermap = {} # cached result of peersplit() period = None starttime = None endtime = None sitename = '' @staticmethod def unixize(lines, starttime, endtime): "Extract first two fields, MJD and seconds past midnight." "convert timestamp (MJD & seconds past midnight) to Unix time" "Replace MJD+second with Unix time." # HOT LOOP! Do not change w/o profiling before and after lines1 = [] for line in lines: try: split = line.split() mjd = int(split[0]) second = float(split[1]) except: # unparseable, skip this line continue # warning: 32 bit overflows time = NTPStats.SecondsInDay * mjd + second - 3506716800 if starttime <= time <= endtime: # time as integer number milli seconds split[0] = int(time * 1000) # time as string split[1] = str(time) lines1.append(split) return lines1 @staticmethod def timestamp(line): "get Unix time from converted line." return float(line.split()[0]) @staticmethod def percentiles(percents, values): "Return given percentiles of a given row in a given set of entries." "assuming values are already split and sorted" ret = {} length = len(values) if 1 >= length: # uh, oh... if 1 == length: # just one data value, set all to that one value v = values[0] else: # no data, set all to zero v = 0 for perc in percents: ret["p" + str(perc)] = v else: for perc in percents: if perc == 100: ret["p100"] = values[length - 1] else: ret["p" + str(perc)] = values[int(length * (perc/100))] return ret @staticmethod def ip_label(key): "Produce appropriate label for an IP address." # If it's a new-style NTPsep clock label, pass it through, # Otherwise we expect it to be an IP address and the next guard fires if key[0].isdigit(): # TO BE REMOVED SOMEDAY # Clock address - only possible if we're looking at a logfile made # by NTP Classic or an NTPsec version configured with # --enable-classic-mode. Nasty that we have to emit a numeric # driver type here. if key.startswith("127.127."): (_, _, t, u) = key.split(".") return "REFCLOCK(type=%s,unit=%s)" % (t, u) # Ordinary IP address - replace with primary hostname. # Punt if the lookup fails. try: (hostname, _, _) = socket.gethostbyaddr(key) return hostname except socket.herror: pass return key # Someday, be smarter than this. def __init__(self, statsdir, sitename=None, period=None, starttime=None, endtime=None): "Grab content of logfiles, sorted by timestamp." if period is None: period = NTPStats.DefaultPeriod self.period = period # Default to one week before the latest date if endtime is None and starttime is None: endtime = int(time.time()) starttime = endtime - period elif starttime is None and endtime is not None: starttime = endtime - period elif starttime is not None and endtime is None: endtime = starttime + period self.starttime = starttime self.endtime = endtime self.sitename = sitename or os.path.basename(statsdir) if 'ntpstats' == self.sitename: # boring, use hostname self.sitename = socket.getfqdn() if not os.path.isdir(statsdir): # pragma: no cover sys.stderr.write("ntpviz: ERROR: %s is not a directory\n" % statsdir) raise SystemExit(1) self.clockstats = [] self.peerstats = [] self.loopstats = [] self.rawstats = [] self.temps = [] self.gpsd = [] for stem in ("clockstats", "peerstats", "loopstats", "rawstats", "temps", "gpsd"): lines = self.__load_stem(statsdir, stem) processed = self.__process_stem(stem, lines) setattr(self, stem, processed) def __load_stem(self, statsdir, stem): lines = [] try: pattern = os.path.join(statsdir, stem) if stem != "temps" and stem != "gpsd": pattern += "." for logpart in glob.glob(pattern + "*"): # skip files older than starttime if self.starttime > os.path.getmtime(logpart): continue if logpart.endswith("gz"): lines += gzip.open(logpart, 'rt').readlines() else: lines += open(logpart, 'r').readlines() except IOError: # pragma: no cover sys.stderr.write("ntpviz: WARNING: could not read %s\n" % logpart) pass return lines def __process_stem(self, stem, lines): lines1 = [] if stem == "temps" or stem == "gpsd": # temps and gpsd are already in UNIX time for line in lines: split = line.split() if 3 > len(split): # skip short lines continue try: t = float(split[0]) except: # ignore comment lines, lines with no time continue if self.starttime <= t <= self.endtime: # prefix with int milli sec. split.insert(0, int(t * 1000)) lines1.append(split) else: # Morph first fields into Unix time with fractional seconds # ut into nice dictionary of dictionary rows lines1 = NTPStats.unixize(lines, self.starttime, self.endtime) # Sort by datestamp # by default, a tuple sort()s on the 1st item, which is a nice # integer of milli seconds. This is faster than using # cmp= or key= lines1.sort() return lines1 def peersplit(self): "Return a dictionary mapping peerstats IPs to entry subsets." "This is very expensive, so cache the result" if len(self.peermap): return self.peermap for row in self.peerstats: try: ip = row[2] # peerstats field 2, refclock id if ip not in self.peermap: self.peermap[ip] = [] self.peermap[ip].append(row) except IndexError: # pragma: no cover # ignore corrupted rows pass return self.peermap def gpssplit(self): "Return a dictionary mapping gps sources to entry subsets." gpsmap = {} for row in self.gpsd: try: source = row[2] if source not in gpsmap: gpsmap[source] = [] gpsmap[source].append(row) except IndexError: # pragma: no cover # ignore corrupted rows pass return gpsmap def tempssplit(self): "Return a dictionary mapping temperature sources to entry subsets." tempsmap = {} for row in self.temps: try: source = row[2] if source not in tempsmap: tempsmap[source] = [] tempsmap[source].append(row) except IndexError: # pragma: no cover # ignore corrupted rows pass return tempsmap def iso_to_posix(s): "Accept timestamps in ISO 8661 format or numeric POSIX time. UTC only." if str(s).isdigit(): return int(s) else: t = time.strptime(s, "%Y-%m-%dT%H:%M:%S") # don't use time.mktime() as that is local tz return calendar.timegm(t) def posix_to_iso(t): "ISO 8601 string in UTC from Unix time." return time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(t)) # end ntpsec-1.1.0+dfsg1/pylib/agentx.py0000644000175000017500000005264513252364117016620 0ustar rlaagerrlaager#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function, division import select import time import sys try: import ntp.util import ntp.agentx_packet ax = ntp.agentx_packet except ImportError as e: sys.stderr.write( "AgentX: can't find Python AgentX Packet library.\n") sys.stderr.write("%s\n" % e) sys.exit(1) defaultTimeout = 30 pingTime = 60 class MIBControl: def __init__(self, oidTree, mibRoot=(), rangeSubid=0, upperBound=None, mibContext=None): self.oidTree = oidTree # contains callbacks for the MIB # The undo system is only for the last operation self.inSetP = False # Are we currently in the set procedure? self.setVarbinds = [] # Varbind of the current set operation self.setHandlers = [] # Handlers for commit/undo/cleanup of set self.setUndoData = [] # Previous values for undoing self.mibRoot = mibRoot self.rangeSubid = rangeSubid self.upperBound = upperBound self.context = mibContext def mib_rootOID(self): return self.mibRoot def mib_rangeSubid(self): return self.rangeSubid def mib_upperBound(self): return self.upperBound def mib_context(self): return self.context def getOID_core(self, nextP, searchoid, returnGenerator=False): gen = ax.walkMIBTree(self.oidTree, self.mibRoot) while True: try: oid, reader, writer = gen.next() if nextP is True: # GetNext # For getnext any OID greater than the start qualifies oidhit = (oid > searchoid) else: # Get # For get we need a *specific* OID oidhit = (oid.subids == searchoid.subids) if oidhit and (reader is not None): # We only return OIDs that have a minimal implementation # walkMIBTree handles the generation of dynamic trees if returnGenerator is True: return oid, reader, writer, gen else: return oid, reader, writer except StopIteration: # Couldn't find anything in the tree if returnGenerator is True: return None, None, None, None else: return None, None, None # These exist instead of just using getOID_core so semantics are clearer def getOID(self, searchoid, returnGenerator=False): "Get the requested OID" return self.getOID_core(False, searchoid, returnGenerator) def getNextOID(self, searchoid, returnGenerator=False): "Get the next lexicographical OID" return self.getOID_core(True, searchoid, returnGenerator) def getOIDsInRange(self, oidrange, firstOnly=False): "Get a list of every (optionally the first) OID in a range" oids = [] gen = ax.walkMIBTree(self.oidTree, self.mibRoot) # Find the first OID while True: try: oid, reader, writer = gen.next() if reader is None: continue # skip unimplemented OIDs elif oid.subids == oidrange.start.subids: # ok, found the start, do we need to skip it? if oidrange.start.include is True: oids.append((oid, reader, writer)) break else: continue elif oid > oidrange.start: # If we are here it means we hit the start but skipped oids.append((oid, reader, writer)) break except StopIteration: # Couldn't find *anything* return [] if firstOnly is True: return oids # Start filling in the rest of the range while True: try: oid, reader = gen.next() if reader is None: continue # skip unimplemented OIDs elif (oidrange.end is not None) and (oid >= oidrange.end): break # past the end of a bounded range else: oids.append((oid, reader, writer)) except StopIteration: break # We have run off the end of the MIB return oids class PacketControl: def __init__(self, sock, dbase, spinGap=0.001, timeout=defaultTimeout, logfp=None, debug=10000): self.log = (lambda txt, dbg: ntp.util.dolog(logfp, txt, debug, dbg)) # take a pre-made socket instead of making our own so that # PacketControl doesn't have to know or care about implementation self.socket = sock self.spinGap = spinGap # sleep() time on each loop # indexed on: (session_id, transaction_id, packet_id) # contains: (timeout, packet class) self.packetLog = {} # Sent packets kept until response is received self.loopCallback = None # called each loop in runforever mode self.database = dbase # class for handling data requests self.receivedData = "" # buffer for data from incomplete packets self.receivedPackets = [] # use as FIFO self.timeout = timeout self.sessionID = None # need this for all packets self.highestTransactionID = 0 # used for exchanges we start self.lastReception = None self.stillConnected = False # indexed on pdu code self.pduHandlers = {ax.PDU_GET: self.handle_GetPDU, ax.PDU_GET_NEXT: self.handle_GetNextPDU, ax.PDU_GET_BULK: self.handle_GetBulkPDU, ax.PDU_TEST_SET: self.handle_TestSetPDU, ax.PDU_COMMIT_SET: self.handle_CommitSetPDU, ax.PDU_UNDO_SET: self.handle_UndoSetPDU, ax.PDU_CLEANUP_SET: self.handle_CleanupSetPDU, ax.PDU_RESPONSE: self.handle_ResponsePDU} def mainloop(self, runforever): if self.stillConnected is not True: return False if runforever: while self.stillConnected is True: self._doloop() if self.loopCallback is not None: self.loopCallback(self) time.sleep(self.spinGap) else: self._doloop() return self.stillConnected def _doloop(self): # loop body split out to separate the one-shot/run-forever switches # from the actual logic self.packetEater() while len(self.receivedPackets) > 0: packet = self.receivedPackets.pop(0) if packet.sessionID != self.sessionID: self.log( "Received packet with incorrect session ID: %s" % packet, 3) resp = ax.ResponsePDU(True, packet.sessionID, packet.transactioID, packet.packetID, 0, ax.REPERR_NOT_OPEN, 0) self.sendPacket(resp, False) continue ptype = packet.pduType if ptype in self.pduHandlers: self.pduHandlers[ptype](packet) else: self.log("Dropping packet type %i, not implemented" % ptype, 2) self.checkResponses() if self.lastReception is not None: currentTime = time.time() if (currentTime - self.lastReception) > pingTime: self.sendPing() def initNewSession(self): self.log("Initializing new session...", 3) # We already have a connection, need to open a session. openpkt = ax.OpenPDU(True, 23, 0, 0, self.timeout, (), "NTPsec SNMP subagent") self.sendPacket(openpkt, False) response = self.waitForResponse(openpkt, True) self.sessionID = response.sessionID # Register the tree register = ax.RegisterPDU(True, self.sessionID, 1, 1, self.timeout, 1, self.database.mib_rootOID(), self.database.mib_rangeSubid(), self.database.mib_upperBound(), self.database.mib_context()) self.sendPacket(register, False) response = self.waitForResponse(register, True) self.stillConnected = True def waitForResponse(self, opkt, ignoreSID=False): "Wait for a response to a specific packet, dropping everything else" while True: self.packetEater() while len(self.receivedPackets) > 0: packet = self.receivedPackets.pop(0) if packet.__class__ != ax.ResponsePDU: continue haveit = (opkt.transactionID == packet.transactionID) and \ (opkt.packetID == packet.packetID) if ignoreSID is False: haveit = haveit and (opkt.sessionID == packet.sessionID) if haveit is True: self.log("Received waited for response", 4) return packet time.sleep(self.spinGap) def checkResponses(self): "Check for expected responses that have timed out" currentTime = time.time() for key in self.packetLog.keys(): expiration, originalPkt, callback = self.packetLog[key] if currentTime > expiration: if callback is not None: callback(None, originalPkt) del self.packetLog[key] def packetEater(self): "Slurps data from the input buffer and tries to parse packets from it" self.pollSocket() while True: datalen = len(self.receivedData) if datalen < 20: return None # We don't even have a packet header, bail try: pkt, extraData = ax.decode_packet(self.receivedData) self.receivedData = extraData self.receivedPackets.append(pkt) if pkt.transactionID > self.highestTransactionID: self.highestTransactionID = pkt.transactionID self.log("Received a full packet: %s" % repr(pkt), 4) except ax.ParseDataLengthError: return None # this happens if we don't have all of a packet except (ax.ParseVersionError, ax.ParsePDUTypeError, ax.ParseError) as e: if e.header["type"] != ax.PDU_RESPONSE: # Response errors are silently dropped, per RFC # Everything else sends an error response self.sendErrorResponse(e.header, ax.RSPERR_PARSE_ERROR, 0) # *Hopefully* the packet length was correct..... # if not, all packets will be scrambled. Maybe dump the # whole buffer if too many failures in a row? self.receivedData = e.remainingData def sendPacket(self, packet, expectsReply, replyTimeout=defaultTimeout, callback=None): encoded = packet.encode() self.log("Sending packet (with reply: %s): %s" % (expectsReply, repr(packet)), 4) self.socket.sendall(encoded) if expectsReply is True: index = (packet.sessionID, packet.transactionID, packet.packetID) self.packetLog[index] = (replyTimeout, packet, callback) def sendPing(self): # DUMMY packetID, does this need to change? or does the pktID only # count relative to a given transaction ID? tid = self.highestTransactionID + 5 # +5 to avoid collisions self.highestTransactionID = tid pkt = ax.PingPDU(True, self.sessionID, tid, 1) def callback(resp, orig): if resp is None: # Timed out. Need to restart the session. # Er, problem: Can't handle reconnect from inside PacketControl self.stillConnected = False self.sendPacket(pkt, True) def sendNotify(self, varbinds, context=None): # DUMMY packetID, does this need to change? or does the pktID only # count relative to a given transaction ID? tid = self.highestTransactionID + 5 # +5 to avoid collisions self.highestTransactionID = tid pkt = ax.NotifyPDU(True, self.sessionID, tid, 1, varbinds, context) def resendNotify(pkt, orig): if pkt is None: self.sendPacket(orig, True, callback=resendNotify) self.sendPacket(pkt, True, resendNotify) def sendErrorResponse(self, errorHeader, errorType, errorIndex): err = ax.ResponsePDU(errorHeader["flags"]["bigEndian"], errorHeader["session_id"], errorHeader["transaction_id"], errorHeader["packet_id"], 0, errorType, errorIndex) self.sendPacket(err, False) def pollSocket(self): "Reads all currently available data from the socket, non-blocking" data = "" while True: tmp = select.select([self.socket], [], [], 0)[0] if len(tmp) == 0: # No socket, means no data available break tmp = tmp[0] newdata = tmp.recv(4096) # Arbitrary value if len(newdata) > 0: self.log("Received data: %s" % repr(newdata), 5) data += newdata self.lastReception = time.time() else: break self.receivedData += data # ========================== # Packet handlers start here # ========================== def handle_GetPDU(self, packet): binds = [] for oidr in packet.oidranges: target = oidr.start oid, reader, _ = self.database.getOID(target) if (oid != target) or (reader is None): # This OID must not be implemented yet. binds.append(ax.Varbind(ax.VALUE_NO_SUCH_OBJECT, target)) else: vbind = reader(oid) if vbind is None: # No data avaliable. # I am not certain that this is the correct response # when no data is available. snmpwalk appears to stop # calling a particular sub-agent when it gets to a NULL. binds.append(ax.Varbind(ax.VALUE_NULL, target)) else: binds.append(vbind) # There should also be a situation that leads to noSuchInstance # but I do not understand the requirements for that # TODO: Need to implement genError resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, ax.ERR_NOERROR, 0, binds) self.sendPacket(resp, False) def handle_GetNextPDU(self, packet): binds = [] for oidr in packet.oidranges: while True: oids = self.database.getOIDsInRange(oidr, True) if len(oids) == 0: # Nothing found binds.append(ax.Varbind(ax.VALUE_END_OF_MIB_VIEW, oidr.start)) break else: oid, reader, _ = oids[0] vbind = reader(oid) if vbind is None: # No data available # Re-do search for this OID range, starting from just # after the current location oidr = ax.SearchRange(oid, oidr.end, False) continue else: binds.append(vbind) break # TODO: Need to implement genError resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, ax.ERR_NOERROR, 0, binds) self.sendPacket(resp, False) def handle_GetBulkPDU(self, packet): binds = [] nonreps = packet.oidranges[:packet.nonReps] repeats = packet.oidranges[packet.nonReps:] # Handle non-repeats for oidr in nonreps: oids = self.database.getOIDsInRange(oidr, True) if len(oids) == 0: # Nothing found binds.append(ax.Varbind(ax.VALUE_END_OF_MIB_VIEW, oidr.start)) else: oid, reader, _ = oids[0] binds.append(reader(oid)) # Handle repeaters for oidr in repeats: oids = self.database.getOIDsInRange(oidr) if len(oids) == 0: # Nothing found binds.append(ax.Varbind(ax.VALUE_END_OF_MIB_VIEW, oidr.start)) else: for oid, reader, _ in oids[:packet.maxReps]: binds.append(reader(oid)) resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, ax.ERR_NOERROR, 0, binds) self.sendPacket(resp, False) def handle_TestSetPDU(self, packet): # WIP / TODO # Be advised: MOST OF THE VALIDATION IS DUMMY CODE OR DOESN'T EXIST # According to the RFC this is one of the most demanding parts and # *has* to be gotten right if self.database.inSetP is True: pass # Is this an error? # if (inSetP is True) is an error these will go in an else block self.database.inSetP = True self.database.setVarbinds = [] self.database.setHandlers = [] self.database.setUndoData = [] error = None for bindIndex in range(len(packet.varbinds)): varbind = packet.varbinds[bindIndex] # Find an OID, then validate it oid, reader, writer = self.database.getOID(varbind.oid) if oid is None: # doesn't exist, can we create it? # DUMMY, assume we can't create anything error = ax.ERR_NO_ACCESS break elif writer is None: # exists, writing not implemented error = ax.ERR_NOT_WRITABLE break # Ok, we have an existing or new OID, assemble the orders # If we created a new bind undoData is None, must delete it undoData = reader(oid) error = writer("test", varbind) if error != ax.ERR_NOERROR: break self.database.setVarbinds.append(varbind) self.database.setHandlers.append(writer) self.database.setUndoData.append(undoData) if error != ax.ERR_NOERROR: resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, error, bindIndex) self.sendPacket(resp, False) for i in range(bindIndex): # Errored out, clear the successful ones self.database.setHandlers[i]("clear", self.database.setVarbinds[i]) self.database.inSetP = False else: resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, ax.ERR_NOERROR, 0) self.sendPacket(resp, False) def handle_CommitSetPDU(self, packet): if self.database.inSetP is False: pass # how to handle this? varbinds = self.database.setVarbinds handlers = self.database.setHandlers for i in range(len(varbinds)): error = handlers[i]("commit", varbinds[i]) if error != ax.ERR_NOERROR: break if error != ax.ERR_NOERROR: resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, error, i) else: resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, ax.ERR_NOERROR, 0) self.sendPacket(resp, False) def handle_UndoSetPDU(self, packet): varbinds = self.database.setVarbinds handlers = self.database.setHandlers undoData = self.database.setUndoData for i in range(len(varbinds)): error = handlers[i]("undo", varbinds[i], undoData[i]) if error != ax.ERR_NOERROR: break if error != ax.ERR_NOERROR: resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, error, i) else: resp = ax.ResponsePDU(True, self.sessionID, packet.transactionID, packet.packetID, 0, ax.ERR_NOERROR, 0) self.sendPacket(resp, False) def handle_CleanupSetPDU(self, packet): varbinds = self.database.setVarbinds handlers = self.database.setHandlers for i in range(len(varbinds)): handlers[i]("clean", varbinds[i]) self.database.inSetP = False def handle_ResponsePDU(self, packet): index = (packet.sessionID, packet.transactionID, packet.packetID) if index in self.packetLog: timeout, originalPkt, callback = self.packetLog[index] del self.packetLog[index] if callback is not None: callback(packet, originalPkt) else: # Ok, response with no associated packet. # Probably something that timed out. pass ntpsec-1.1.0+dfsg1/pylib/wscript0000644000175000017500000000366613252364117016375 0ustar rlaagerrlaagerfrom waflib import Utils # pylint: disable=import-error def options(opt): opt.load('python') def configure(conf): conf.load('python') conf.check_python_version((2, 6, 0)) conf.check_python_headers(features='pyext') # Extension-only, no embedded def build(ctx): srcnode = ctx.srcnode.make_node('pylib') bldnode = ctx.bldnode.make_node('pylib') target1 = bldnode.make_node('control.py') target2 = bldnode.make_node('magic.py') target3 = bldnode.make_node('version.py') target4 = ctx.srcnode.make_node('wafhelpers/.autorevision-cache') sources = srcnode.ant_glob('*.py') builds = [x.get_bld() for x in sources] # Remove generated files to ensure they are properly updated ctx.exec_command("rm -f %s" % target1.abspath()) ctx.exec_command("rm -f %s" % target2.abspath()) ctx.exec_command("rm -f %s" % target3.abspath()) # Make sure Python sees .py as well as .pyc/.pyo ctx( features="subst", source=sources, target=builds, ) ctx( before=['pyc', 'pyo'], cwd=srcnode, rule='${SRC} >${TGT}', source=["../wafhelpers/pythonize-header", "../include/ntp_control.h"], target=target1, ) ctx( before=['pyc', 'pyo'], cwd=srcnode, rule='${SRC} >${TGT}', source=["../wafhelpers/pythonize-header", "../include/ntp.h"], target=target2, ) ctx( before=['pyc', 'pyo'], cwd=srcnode, rule='VCS_EXTRA=`cat ${SRC[0]}` ../wafhelpers/autorevision.sh ' '-o ${TGT[1].abspath()} -e VERSION -t python >${TGT[0].abspath()}', source=["../VERSION", '../wafhelpers/autorevision.sh'], target=[target3, target4], ) # Force early creation of generated files ctx.add_group() ctx( features='py', source=builds+[target1, target2, target3], install_from=bldnode, install_path='${PYTHONDIR}/ntp' ) ntpsec-1.1.0+dfsg1/pylib/util.py0000644000175000017500000013602713252364117016304 0ustar rlaagerrlaager# -*- coding: utf-8 -*- # Common utility functions # SPDX-License-Identifier: BSD-2-clause from __future__ import print_function, division import collections import os import re import shutil import socket import sys import time import ntp.ntpc import ntp.version import ntp.magic import ntp.control # Old CTL_PST defines for version 2. OLD_CTL_PST_CONFIG = 0x80 OLD_CTL_PST_AUTHENABLE = 0x40 OLD_CTL_PST_AUTHENTIC = 0x20 OLD_CTL_PST_REACH = 0x10 OLD_CTL_PST_SANE = 0x08 OLD_CTL_PST_DISP = 0x04 OLD_CTL_PST_SEL_REJECT = 0 OLD_CTL_PST_SEL_SELCAND = 1 OLD_CTL_PST_SEL_SYNCCAND = 2 OLD_CTL_PST_SEL_SYSPEER = 3 # Units for formatting UNIT_NS = "ns" # nano second UNIT_US = u"µs" # micro second UNIT_MS = "ms" # milli second UNIT_S = "s" # second UNIT_KS = "ks" # kilo seconds UNITS_SEC = [UNIT_NS, UNIT_US, UNIT_MS, UNIT_S, UNIT_KS] UNIT_PPT = "ppt" # parts per trillion UNIT_PPB = "ppb" # parts per billion UNIT_PPM = "ppm" # parts per million UNIT_PPK = u"‰" # parts per thousand UNITS_PPX = [UNIT_PPT, UNIT_PPB, UNIT_PPM, UNIT_PPK] unitgroups = (UNITS_SEC, UNITS_PPX) # These two functions are not tested because they will muck up the module # for everything else, and they are simple. def check_unicode(): # pragma: no cover if "UTF-8" != sys.stdout.encoding: deunicode_units() return True # needed by ntpmon return False def deunicode_units(): # pragma: no cover "Under certain conditions it is not possible to force unicode output, " "this overwrites units that contain unicode with safe versions" global UNIT_US global UNIT_PPK # Replacement units new_us = "us" new_ppk = "ppk" # Replace units in unit groups UNITS_SEC[UNITS_SEC.index(UNIT_US)] = new_us UNITS_PPX[UNITS_PPX.index(UNIT_PPK)] = new_ppk # Replace the units themselves UNIT_US = new_us UNIT_PPK = new_ppk # Variables that have units S_VARS = ("tai", "poll") MS_VARS = ("rootdelay", "rootdisp", "rootdist", "offset", "sys_jitter", "clk_jitter", "leapsmearoffset", "authdelay", "koffset", "kmaxerr", "kesterr", "kprecis", "kppsjitter", "fuzz", "clk_wander_threshold", "tick", "in", "out", "bias", "delay", "jitter", "dispersion", "fudgetime1", "fudgetime2") PPM_VARS = ("frequency", "clk_wander") def dolog(logfp, text, debug, threshold): # debug is the current debug value # threshold is the trigger for the current log if logfp is None: return # can turn off logging by supplying a None file descriptior text = rfc3339(time.time()) + " " + text + "\n" if debug >= threshold: logfp.write(text) logfp.flush() # we don't want to lose an important log to a crash def safeargcast(arg, castfunc, errtext, usage): """Attempts to typecast an argument, prints and dies on failure. errtext must contain a %s for splicing in the argument, and be newline terminated.""" try: casted = castfunc(arg) except ValueError: sys.stderr.write(errtext % arg) sys.stderr.write(usage) raise SystemExit(1) return casted def stdversion(): "Returns the NTPsec version string in a standard format" return "ntpsec-%s+%s %s" % (ntp.version.VERSION, ntp.version.VCS_TICK, ntp.version.VCS_DATE) def rfc3339(t): "RFC 3339 string from Unix time, including fractional second." rep = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(t)) t = str(t) if "." in t: subsec = t.split(".", 1)[1] if int(subsec) > 0: rep += "." + subsec rep += "Z" return rep def deformatNTPTime(txt): txt = txt[2:] # Strip '0x' txt = "".join(txt.split(".")) # Strip '.' value = ntp.util.hexstr2octets(txt) return value def hexstr2octets(hexstr): if (len(hexstr) % 2) != 0: hexstr = hexstr[:-1] # slice off the last char values = [] for index in range(0, len(hexstr), 2): values.append(chr(int(hexstr[index:index+2], 16))) return "".join(values) def slicedata(data, slicepoint): "Breaks a sequence into two pieces at the slice point" return data[:slicepoint], data[slicepoint:] def portsplit(hostname): portsuffix = "" if hostname.count(":") == 1: # IPv4 with appended port (hostname, portsuffix) = hostname.split(":") portsuffix = ":" + portsuffix elif ']' in hostname: # IPv6 rbrak = hostname.rindex("]") if ":" in hostname[rbrak:]: portsep = hostname.rindex(":") portsuffix = hostname[portsep:] hostname = hostname[:portsep] hostname = hostname[1:-1] # Strip brackets return (hostname, portsuffix) def stringfilt(data): "Pretty print string of space separated numbers" parts = data.split() cooked = [] for part in parts: # These are expected to fit on a single 80-char line. # Accounting for other factors this leaves each number with # 7 chars + a space. fitted = fitinfield(part, 7) cooked.append(fitted) rendered = " ".join(cooked) return rendered def stringfiltcooker(data): "Cooks a filt* string of space separated numbers, expects milliseconds" parts = data.split() oomcount = {} minscale = -100000 # Keep track of the maxdownscale for each value # Find out what the 'natural' unit of each value is for part in parts: # Only care about OOMs, the real scaling happens later value, oom = scalestring(part) # Track the highest maxdownscale so we do not invent precision ds = maxdownscale(part) minscale = max(ds, minscale) oomcount[oom] = oomcount.get(oom, 0) + 1 # Find the most common unit mostcommon = 0 highestcount = 0 for key in oomcount.keys(): if key < minscale: continue # skip any scale that would result in making up data count = oomcount[key] if count > highestcount: mostcommon = key highestcount = count newunit = UNITS_SEC[mostcommon + UNITS_SEC.index(UNIT_MS)] # Shift all values to the new unit cooked = [] for part in parts: part = rescalestring(part, mostcommon) fitted = fitinfield(part, 7) cooked.append(fitted) rendered = " ".join(cooked) + " " + UNITS_SEC[mostcommon + \ UNITS_SEC.index(UNIT_MS)] return rendered def getunitgroup(unit): "Returns the unit group which contains a given unit" for group in unitgroups: if unit in group: return group def oomsbetweenunits(a, b): "Calculates how many orders of magnitude separate two units" group = getunitgroup(a) if b is None: # Caller is asking for the distance from the base unit return group.index(a) * 3 elif b in group: ia = group.index(a) ib = group.index(b) return abs((ia - ib) * 3) return None def breaknumberstring(value): "Breaks a number string into (aboveDecimal, belowDecimal, isNegative?)" if value[0] == "-": value = value[1:] negative = True else: negative = False if "." in value: above, below = value.split(".") else: above = value below = "" return (above, below, negative) def gluenumberstring(above, below, isnegative): "Glues together parts of a number string" if above == "": above = "0" if len(below) > 0: newvalue = ".".join((above, below)) else: newvalue = above if isnegative is True: newvalue = "-" + newvalue return newvalue def maxdownscale(value): "Maximum units a value can be scaled down without inventing data" if "." in value: digitcount = len(value.split(".")[1]) # Return a negative so it can be fed directly to a scaling function return -(digitcount // 3) else: # No decimals, the value is already at the maximum down-scale return 0 def rescalestring(value, unitsscaled): "Rescale a number string by a given number of units" whole, dec, negative = breaknumberstring(value) if unitsscaled == 0: # This may seem redundant, but glue forces certain formatting details value = gluenumberstring(whole, dec, negative) return value hilen = len(whole) lolen = len(dec) digitsmoved = abs(unitsscaled * 3) if unitsscaled > 0: # Scale to a larger unit, move decimal left if hilen < digitsmoved: # Scaling beyond the digits, pad it out. We can pad here # without making up digits that don't exist padcount = digitsmoved - hilen newwhole = "" newdec = ("0" * padcount) + whole + dec else: # Scaling in the digits, no need to pad choppoint = -digitsmoved newdec = whole[choppoint:] + dec newwhole = whole[:choppoint] elif unitsscaled < 0: # scale to a smaller unit, move decimal right if lolen < digitsmoved: # Scaling beyond the digits would force us to make up data # that doesn't exist. So fail. # The caller should have already caught this with maxdownscale() return None else: newwhole = whole + dec[:digitsmoved] newdec = dec[digitsmoved:] newwhole = newwhole.lstrip("0") newvalue = gluenumberstring(newwhole, newdec, negative) return newvalue def formatzero(value): "Scale a zero value for the unit with the highest available precision" scale = maxdownscale(value) newvalue = rescalestring(value, scale).lstrip("-") return (newvalue, scale) def scalestring(value): "Scales a number string to fit in the range 1.0-999.9" if isstringzero(value): return formatzero(value) whole, dec, negative = breaknumberstring(value) hilen = len(whole) if (hilen == 0) or isstringzero(whole): # Need to shift to smaller units i = 0 lolen = len(dec) while i < lolen: # need to find the actual digits if dec[i] != "0": break i += 1 lounits = (i // 3) + 1 # always need to shift one more unit movechars = lounits * 3 if lolen < movechars: # Not enough digits to scale all the way down. Inventing # digits is unacceptable, so scale down as much as we can. lounits = (i // 3) # "always", unless out of digits movechars = lounits * 3 newwhole = dec[:movechars].lstrip("0") newdec = dec[movechars:] unitsmoved = -lounits else: # Shift to larger units hiunits = hilen // 3 # How many we have, not how many to move hidigits = hilen % 3 if hidigits == 0: # full unit above the decimal hiunits -= 1 # the unit above the decimal doesn't count hidigits = 3 newwhole = whole[:hidigits] newdec = whole[hidigits:] + dec unitsmoved = hiunits newvalue = gluenumberstring(newwhole, newdec, negative) return (newvalue, unitsmoved) def fitinfield(value, fieldsize): "Attempt to fit value into a field, preserving as much data as possible" vallen = len(value) if fieldsize is None: newvalue = value elif vallen == fieldsize: # Goldilocks! newvalue = value elif vallen < fieldsize: # Extra room, pad it out pad = " " * (fieldsize - vallen) newvalue = pad + value else: # Insufficient room, round as few digits as possible if "." in value: # Ok, we *do* have decimals to crop diff = vallen - fieldsize declen = len(value.split(".")[1]) # length of decimals croplen = min(declen, diff) # Never round above the decimal point roundlen = declen - croplen # How many digits we round to newvalue = str(round(float(value), roundlen)) splitted = newvalue.split(".") # This should never fail declen = len(splitted[1]) if roundlen == 0: # if rounding all the decimals don't display .0 # but do display the point, to show that there is more beyond newvalue = splitted[0] + "." elif roundlen > declen: # some zeros have been cropped, fix that padcount = roundlen - declen newvalue = newvalue + ("0" * padcount) else: # No decimals, nothing we can crop newvalue = value return newvalue def cropprecision(value, ooms): "Crops digits below the maximum precision" if "." not in value: # No decimals, nothing to crop return value if ooms == 0: # We are at the baseunit, crop it all return value.split(".")[0] dstart = value.find(".") + 1 dsize = len(value) - dstart precision = min(ooms, dsize) cropcount = dsize - precision if cropcount > 0: value = value[:-cropcount] return value def isstringzero(value): "Detects whether a string is equal to zero" for i in value: if i not in ("-", ".", "0"): return False return True def unitrelativeto(unit, move): "Returns a unit at a different scale from the input unit" for group in unitgroups: if unit in group: if move is None: # asking for the base unit return group[0] else: index = group.index(unit) index += move # index of the new unit if 0 <= index < len(group): # found the new unit return group[index] else: # not in range return None return None # couldn't find anything def unitifyvar(value, varname, baseunit=None, width=8, unitSpace=False): "Call unitify() with the correct units for varname" if varname in S_VARS: start = UNIT_S elif varname in MS_VARS: start = UNIT_MS elif varname in PPM_VARS: start = UNIT_PPM else: return value return unitify(value, start, baseunit, width, unitSpace) def unitify(value, startingunit, baseunit=None, width=8, unitSpace=False): "Formats a numberstring with relevant units. Attemps to fit in width." if baseunit is None: baseunit = getunitgroup(startingunit)[0] ooms = oomsbetweenunits(startingunit, baseunit) if isstringzero(value): newvalue, unitsmoved = formatzero(value) else: newvalue = cropprecision(value, ooms) newvalue, unitsmoved = scalestring(newvalue) unitget = unitrelativeto(startingunit, unitsmoved) if unitSpace is True: spaceWidthAdjustment = 1 spacer = " " else: spaceWidthAdjustment = 0 spacer = "" if unitget is not None: # We have a unit if width is None: realwidth = None else: realwidth = width - (len(unitget) + spaceWidthAdjustment) newvalue = fitinfield(newvalue, realwidth) + spacer + unitget else: # don't have a replacement unit, use original newvalue = value + spacer + startingunit if width is None: newvalue = newvalue.strip() return newvalue def f8dot4(f): "Scaled floating point formatting to fit in 8 characters" if isinstance(f, str): # a string? pass it on as a signal return "%8s" % f if not isinstance(f, (int, float)): # huh? return " X" if str(float(f)).lower() == 'nan': # yes, this is a better test than math.isnan() # it also catches None, strings, etc. return " nan" fmt = "%8d" # xxxxxxxx or -xxxxxxx if f >= 0: if f < 1000.0: fmt = "%8.4f" # xxx.xxxx normal case elif f < 10000.0: fmt = "%8.3f" # xxxx.xxx elif f < 100000.0: fmt = "%8.2f" # xxxxx.xx elif f < 1000000.0: fmt = "%8.1f" # xxxxxx.x else: # negative number, account for minus sign if f > -100.0: fmt = "%8.4f" # -xx.xxxx normal case elif f > -1000.0: fmt = "%8.3f" # -xxx.xxx elif f > -10000.0: fmt = "%8.2f" # -xxxx.xx elif f > -100000.0: fmt = "%8.1f" # -xxxxx.x return fmt % f def f8dot3(f): "Scaled floating point formatting to fit in 8 characters" if isinstance(f, str): # a string? pass it on as a signal return "%8s" % f if not isinstance(f, (int, float)): # huh? return " X" if str(float(f)).lower() == 'nan': # yes, this is a better test than math.isnan() # it also catches None, strings, etc. return " nan" fmt = "%8d" # xxxxxxxx or -xxxxxxx if f >= 0: if f < 10000.0: fmt = "%8.3f" # xxxx.xxx normal case elif f < 100000.0: fmt = "%8.2f" # xxxxx.xx elif f < 1000000.0: fmt = "%8.1f" # xxxxxx.x else: # negative number, account for minus sign if f > -1000.0: fmt = "%8.3f" # -xxx.xxx normal case elif f > -10000.0: fmt = "%8.2f" # -xxxx.xx elif f > -100000.0: fmt = "%8.1f" # -xxxxx.x return fmt % f def monoclock(): "Try to get a monotonic clock value unaffected by NTP stepping." try: # Available in Python 3.3 and up. return time.monotonic() except AttributeError: return time.time() class Cache: "Simple time-based cache" def __init__(self, defaultTimeout=300): # 5 min default TTL self.defaultTimeout = defaultTimeout self._cache = {} def get(self, key): if key in self._cache: value, settime, ttl = self._cache[key] if settime >= monoclock() - ttl: return value else: # key expired, delete it del self._cache[key] return None else: return None def set(self, key, value, customTTL=None): ttl = customTTL if customTTL is not None else self.defaultTimeout self._cache[key] = (value, monoclock(), ttl) # A hack to avoid repeatedly hammering on DNS when ntpmon runs. canonicalization_cache = Cache() def canonicalize_dns(inhost, family=socket.AF_UNSPEC): "Canonicalize a hostname or numeric IP address." resname = canonicalization_cache.get(inhost) if resname is not None: return resname # Catch garbaged hostnames in corrupted Mode 6 responses m = re.match("([:.[\]]|\w)*", inhost) if not m: raise TypeError (hostname, portsuffix) = portsplit(inhost) try: ai = socket.getaddrinfo(hostname, None, family, 0, 0, socket.AI_CANONNAME) except socket.gaierror: return "DNSFAIL:%s" % hostname (family, socktype, proto, canonname, sockaddr) = ai[0] try: name = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD) result = name[0].lower() + portsuffix except socket.gaierror: # On OS X, canonname is empty for hosts without rDNS. # Fall back to the hostname. canonicalized = canonname or hostname result = canonicalized.lower() + portsuffix canonicalization_cache.set(inhost, result) return result TermSize = collections.namedtuple("TermSize", ["width", "height"]) # Python 2.x does not have the shutil.get_terminal_size function. # This conditional import is only needed by termsize() and should be kept # near it. It is not inside the function because the unit tests need to be # able to splice in a jig. if str is bytes: # We are on python 2.x import fcntl import termios import struct def termsize(): # pragma: no cover "Return the current terminal size." # Alternatives at http://stackoverflow.com/questions/566746 # The way this is used makes it not a big deal if the default is wrong. size = (80, 24) if os.isatty(1): if str is not bytes: # str is bytes means we are >py3.0, but this will still fail # on versions <3.3. We do not support those anyway. (w, h) = shutil.get_terminal_size((80, 24)) size = (w, h) else: try: # OK, Python version < 3.3, cope h, w, hp, wp = struct.unpack( 'HHHH', fcntl.ioctl(2, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0))) size = (w, h) except IOError: pass return TermSize(*size) class PeerStatusWord: "A peer status word from readstats(), dissected for display" def __init__(self, status, pktversion=ntp.magic.NTP_VERSION): # Event self.event = ntp.control.CTL_PEER_EVENT(status) # Event count self.event_count = ntp.control.CTL_PEER_NEVNT(status) statval = ntp.control.CTL_PEER_STATVAL(status) # Config if statval & ntp.control.CTL_PST_CONFIG: self.conf = "yes" else: self.conf = "no" # Reach if statval & ntp.control.CTL_PST_BCAST: self.reach = "none" elif statval & ntp.control.CTL_PST_REACH: self.reach = "yes" else: self.reach = "no" # Auth if (statval & ntp.control.CTL_PST_AUTHENABLE) == 0: self.auth = "none" elif statval & ntp.control.CTL_PST_AUTHENTIC: self.auth = "ok " else: self.auth = "bad" # Condition if pktversion > ntp.magic.NTP_OLDVERSION: seldict = { ntp.control.CTL_PST_SEL_REJECT: "reject", ntp.control.CTL_PST_SEL_SANE: "falsetick", ntp.control.CTL_PST_SEL_CORRECT: "excess", ntp.control.CTL_PST_SEL_SELCAND: "outlier", ntp.control.CTL_PST_SEL_SYNCCAND: "candidate", ntp.control.CTL_PST_SEL_EXCESS: "backup", ntp.control.CTL_PST_SEL_SYSPEER: "sys.peer", ntp.control.CTL_PST_SEL_PPS: "pps.peer", } self.condition = seldict[statval & 0x7] else: if (statval & 0x3) == OLD_CTL_PST_SEL_REJECT: if (statval & OLD_CTL_PST_SANE) == 0: self.condition = "insane" elif (statval & OLD_CTL_PST_DISP) == 0: self.condition = "hi_disp" else: self.condition = "" elif (statval & 0x3) == OLD_CTL_PST_SEL_SELCAND: self.condition = "sel_cand" elif (statval & 0x3) == OLD_CTL_PST_SEL_SYNCCAND: self.condition = "sync_cand" elif (statval & 0x3) == OLD_CTL_PST_SEL_SYSPEER: self.condition = "sys_peer" # Last Event event_dict = { ntp.magic.PEVNT_MOBIL: "mobilize", ntp.magic.PEVNT_DEMOBIL: "demobilize", ntp.magic.PEVNT_UNREACH: "unreachable", ntp.magic.PEVNT_REACH: "reachable", ntp.magic.PEVNT_RESTART: "restart", ntp.magic.PEVNT_REPLY: "no_reply", ntp.magic.PEVNT_RATE: "rate_exceeded", ntp.magic.PEVNT_DENY: "access_denied", ntp.magic.PEVNT_ARMED: "leap_armed", ntp.magic.PEVNT_NEWPEER: "sys_peer", ntp.magic.PEVNT_CLOCK: "clock_alarm", } self.last_event = event_dict.get(ntp.magic.PEER_EVENT | self.event, "") def __str__(self): return ("conf=%(conf)s, reach=%(reach)s, auth=%(auth)s, " "cond=%(condition)s, event=%(last_event)s ec=%(event_count)s" % self.__dict__) def cook(variables, showunits=False, sep=", "): "Cooked-mode variable display." width = ntp.util.termsize().width - 2 text = "" specials = ("filtdelay", "filtoffset", "filtdisp", "filterror") longestspecial = len(max(specials, key=len)) for (name, (value, rawvalue)) in variables.items(): if name in specials: # need special formatting for column alignment formatter = "%" + str(longestspecial) + "s =" item = formatter % name else: item = "%s=" % name if name in ("reftime", "clock", "org", "rec", "xmt"): item += ntp.ntpc.prettydate(value) elif name in ("srcadr", "peeradr", "dstadr", "refid"): # C ntpq cooked these in obscure ways. Since they # came up from the daemon as human-readable # strings this was probably a bad idea, but we'll # leave this case separated in case somebody thinks # re-cooking them is a good idea. item += value elif name == "leap": item += ("00", "01", "10", "11")[value] elif name == "reach": item += "%03lo" % value elif name in specials: if showunits: item += stringfiltcooker(value) else: item += "\t".join(value.split()) elif name == "flash": item += "%02x " % value if value == 0: item += "ok " else: # flasher bits tstflagnames = ( "pkt_dup", # BOGON1 "pkt_bogus", # BOGON2 "pkt_unsync", # BOGON3 "pkt_denied", # BOGON4 "pkt_auth", # BOGON5 "pkt_stratum", # BOGON6 "pkt_header", # BOGON7 "pkt_autokey", # BOGON8 "pkt_crypto", # BOGON9 "peer_stratum", # BOGON10 "peer_dist", # BOGON11 "peer_loop", # BOGON12 "peer_unreach" # BOGON13 ) for (i, n) in enumerate(tstflagnames): if (1 << i) & value: item += tstflagnames[i] + " " item = item[:-1] elif name in MS_VARS: # Note that this is *not* complete, there are definitely # missing variables here. # Completion cannot occur until all units are tracked down. if showunits: item += unitify(rawvalue, UNIT_MS, UNIT_NS, width=None) else: item += repr(value) elif name in S_VARS: if showunits: item += unitify(rawvalue, UNIT_S, UNIT_NS, width=None) else: item += repr(value) elif name in PPM_VARS: if showunits: item += unitify(rawvalue, UNIT_PPM, width=None) else: item += repr(value) else: item += repr(value) # add field separator item += sep # add newline so we don't overflow screen lastcount = 0 for c in text: if c == '\n': lastcount = 0 else: lastcount += 1 if lastcount + len(item) > width: text = text[:-1] + "\n" text += item text = text[:-2] + "\n" return text class PeerSummary: "Reusable report generator for peer statistics" def __init__(self, displaymode, pktversion, showhostnames, wideremote, showunits=False, termwidth=None, debug=0, logfp=sys.stderr): self.displaymode = displaymode # peers/apeers/opeers self.pktversion = pktversion # interpretation of flash bits self.showhostnames = showhostnames # If false, display numeric IPs self.showunits = showunits # If False show old style float self.wideremote = wideremote # show wide remote names? self.debug = debug self.logfp = logfp self.termwidth = termwidth # By default, the peer spreadsheet layout is designed so lines just # fit in 80 characters. This tells us how much extra horizontal space # we have available on a wider terminal emulator. self.horizontal_slack = min((termwidth or 80) - 80, 24) # Peer spreadsheet column widths. The reason we cap extra # width used at 24 is that on very wide displays, slamming the # non-hostname fields all the way to the right produces a huge # river that makes the entries difficult to read as wholes. # This choice caps the peername field width at that of the longest # possible IPV6 numeric address. self.namewidth = 15 + self.horizontal_slack self.refidwidth = 15 # Compute peer spreadsheet headers self.__remote = " remote ".ljust(self.namewidth) self.__common = "st t when poll reach delay offset " self.__header = None self.polls = [] @staticmethod def prettyinterval(diff): "Print an interval in natural time units." if not isinstance(diff, int) or diff <= 0: return '-' if diff <= 2048: return str(diff) diff = (diff + 29) / 60 if diff <= 300: return "%dm" % diff diff = (diff + 29) / 60 if diff <= 96: return "%dh" % diff diff = (diff + 11) / 24 return "%dd" % diff @staticmethod def high_truncate(hostname, maxlen): "Truncate on the left using leading _ to indicate 'more'." # Used for local IPv6 addresses, best distinguished by low bits if len(hostname) <= maxlen: return hostname else: return '-' + hostname[-maxlen+1:] @staticmethod def is_clock(variables): "Does a set of variables look like it returned from a clock?" return "srchost" in variables and '(' in variables["srchost"][0] def header(self): "Column headers for peer display" if self.displaymode == "apeers": self.__header = self.__remote + \ " refid assid ".ljust(self.refidwidth) + \ self.__common + "jitter" elif self.displaymode == "opeers": self.__header = self.__remote + \ " local ".ljust(self.refidwidth) + \ self.__common + " disp" else: self.__header = self.__remote + \ " refid ".ljust(self.refidwidth) + \ self.__common + "jitter" return self.__header def width(self): "Width of display" return 79 + self.horizontal_slack def summary(self, rstatus, variables, associd): "Peer status summary line." clock_name = '' dstadr_refid = "" dstport = 0 estdelay = '.' estdisp = '.' estjitter = '.' estoffset = '.' filtdelay = 0.0 filtdisp = 0.0 filtoffset = 0.0 flash = 0 have_jitter = False headway = 0 hmode = 0 hpoll = 0 keyid = 0 last_sync = None leap = 0 pmode = 0 ppoll = 0 precision = 0 ptype = '?' reach = 0 rec = None reftime = None rootdelay = 0.0 saw6 = False # x.6 floats for delay and friends srcadr = None srchost = None srcport = 0 stratum = 20 mode = 0 unreach = 0 xmt = 0 now = time.time() for item in variables.items(): if 2 != len(item) or 2 != len(item[1]): # bad item continue (name, (value, rawvalue)) = item if name == "delay": estdelay = rawvalue if self.showunits else value if len(rawvalue) > 6 and rawvalue[-7] == ".": saw6 = True elif name == "dstadr": # The C code tried to get a fallback ptype from this in case # the hmode field was not included if "local" in self.__header: dstadr_refid = rawvalue elif name == "dstport": # FIXME, dstport never used. dstport = value elif name == "filtdelay": # FIXME, filtdelay never used. filtdelay = value elif name == "filtdisp": # FIXME, filtdisp never used. filtdisp = value elif name == "filtoffset": # FIXME, filtoffset never used. filtoffset = value elif name == "flash": # FIXME, flash never used. flash = value elif name == "headway": # FIXME, headway never used. headway = value elif name == "hmode": hmode = value elif name == "hpoll": hpoll = value if hpoll < 0: hpoll = ntp.magic.NTP_MINPOLL elif name == "jitter": if "jitter" in self.__header: estjitter = rawvalue if self.showunits else value have_jitter = True elif name == "keyid": # FIXME, keyid never used. keyid = value elif name == "leap": # FIXME, leap never used. leap = value elif name == "offset": estoffset = rawvalue if self.showunits else value elif name == "pmode": # FIXME, pmode never used. pmode = value elif name == "ppoll": ppoll = value if ppoll < 0: ppoll = ntp.magic.NTP_MINPOLL elif name == "precision": # FIXME, precision never used. precision = value elif name == "reach": # Shipped as hex, displayed in octal reach = value elif name == "refid": # The C code for this looked crazily overelaborate. Best # guess is that it was designed to deal with formats that # no longer occur in this field. if "refid" in self.__header: dstadr_refid = rawvalue elif name == "rec": rec = value # l_fp timestamp last_sync = int(now - ntp.ntpc.lfptofloat(rec)) elif name == "reftime": reftime = value # l_fp timestamp last_sync = int(now - ntp.ntpc.lfptofloat(reftime)) elif name == "rootdelay": # FIXME, rootdelay never used. rootdelay = value # l_fp timestamp elif name == "rootdisp" or name == "dispersion": estdisp = rawvalue if self.showunits else value elif name in ("srcadr", "peeradr"): srcadr = value elif name == "srchost": srchost = value elif name == "srcport" or name == "peerport": # FIXME, srcport never used. srcport = value elif name == "stratum": stratum = value elif name == "mode": # FIXME, mode never used. mode = value elif name == "unreach": # FIXME, unreach never used. unreach = value elif name == "xmt": # FIXME, xmt never used. xmt = value else: # unknown name? # line = " name=%s " % (name) # debug # return line # debug continue if hmode == ntp.magic.MODE_BCLIENTX: # broadcastclient or multicastclient ptype = 'b' elif hmode == ntp.magic.MODE_BROADCAST: # broadcast or multicast server if srcadr.startswith("224."): # IANA multicast address prefix ptype = 'M' else: ptype = 'B' elif hmode == ntp.magic.MODE_CLIENT: if PeerSummary.is_clock(variables): ptype = 'l' # local refclock elif dstadr_refid == "POOL": ptype = 'p' # pool elif srcadr.startswith("224."): ptype = 'a' # manycastclient else: ptype = 'u' # unicast elif hmode == ntp.magic.MODE_ACTIVE: ptype = 's' # symmetric active elif hmode == ntp.magic.MODE_PASSIVE: ptype = 'S' # symmetric passive # # Got everything, format the line # line = "" poll_sec = 1 << min(ppoll, hpoll) self.polls.append(poll_sec) if self.pktversion > ntp.magic.NTP_OLDVERSION: c = " x.-+#*o"[ntp.control.CTL_PEER_STATVAL(rstatus) & 0x7] else: c = " .+*"[ntp.control.CTL_PEER_STATVAL(rstatus) & 0x3] # Source host or clockname or poolname or servername # After new DNS, 2017-Apr-17 # servers setup via numerical IP Address have only srcadr # servers setup via DNS have both srcadr and srchost # refclocks have both srcadr and srchost # pool has "0.0.0.0" (or "::") and srchost # slots setup via pool have only srcadr if srcadr is not None \ and srcadr != "0.0.0.0" \ and srcadr[:7] != "127.127" \ and srcadr != "::": if self.showhostnames: try: if self.debug: self.logfp.write("DNS lookup begins...\n") clock_name = canonicalize_dns(srcadr) if self.debug: self.logfp.write("DNS lookup ends.\n") except TypeError: # pragma: no cover return '' else: clock_name = srcadr else: clock_name = srchost if clock_name is None: if srcadr: clock_name = srcadr else: clock_name = "" if self.wideremote and len(clock_name) > self.namewidth: line += ("%c%s\n" % (c, clock_name)) line += (" " * (self.namewidth + 2)) else: line += ("%c%-*.*s " % (c, self.namewidth, self.namewidth, clock_name[:self.namewidth])) # Destination address, assoc ID or refid. assocwidth = 7 if self.displaymode == "apeers" else 0 if "." not in dstadr_refid and ":" not in dstadr_refid: dstadr_refid = "." + dstadr_refid + "." if assocwidth and len(dstadr_refid) >= self.refidwidth - assocwidth: visible = "..." else: visible = dstadr_refid line += self.high_truncate(visible, self.refidwidth) if self.displaymode == "apeers": line += (" " * (self.refidwidth - len(visible) - assocwidth + 1)) line += ("%-6d" % (associd)) else: line += (" " * (self.refidwidth - len(visible))) # The rest of the story if last_sync is None: last_sync = now jd = estjitter if have_jitter else estdisp line += ( " %2ld %c %4.4s %4.4s %3lo" % (stratum, ptype, PeerSummary.prettyinterval(last_sync), PeerSummary.prettyinterval(poll_sec), reach)) if saw6: if self.showunits: line += ( " %s %s %s" % (unitify(estdelay, UNIT_MS), unitify(estoffset, UNIT_MS), unitify(jd, UNIT_MS))) else: line += ( " %s %s %s" % (f8dot4(estdelay), f8dot4(estoffset), f8dot4(jd))) else: # old servers only have 3 digits of fraction # don't print a fake 4th digit if self.showunits: line += ( " %s %s %s" % (unitify(estdelay, UNIT_MS), unitify(estoffset, UNIT_MS), unitify(jd, UNIT_MS))) else: line += ( " %s %s %s" % (f8dot3(estdelay), f8dot3(estoffset), f8dot3(jd))) line += "\n" # for debugging both case # if srcadr != None and srchost != None: # line += "srcadr: %s, srchost: %s\n" % (srcadr, srchost) return line def intervals(self): "Return and flush the list of actual poll intervals." res = self.polls[:] self.polls = [] return res class MRUSummary: "Reusable class for MRU entry summary generation." def __init__(self, showhostnames, wideremote=False, debug=0, logfp=sys.stderr): self.debug = debug self.logfp = logfp self.now = None self.showhostnames = showhostnames # If false, display numeric IPs self.wideremote = wideremote header = " lstint avgint rstr r m v count rport remote address" def summary(self, entry): last = ntp.ntpc.lfptofloat(entry.last) if self.now: lstint = int(self.now - last + 0.5) stats = "%7d" % lstint else: # direct mode doesn't have a reference time MJD_1970 = 40587 # MJD for 1 Jan 1970, Unix epoch days, lstint = divmod(int(last), 86400) stats = "%5d %5d" % (days + MJD_1970, lstint) first = ntp.ntpc.lfptofloat(entry.first) active = float(last - first) if entry.ct == 1: favgint = 0 else: favgint = active / (entry.ct-1) avgint = int(favgint + 0.5) if 5.0 < favgint or 1 == entry.ct: stats += " %6d" % avgint elif 1.0 <= favgint: stats += " %6.2f" % favgint else: stats += " %6.3f" % favgint if entry.rs & ntp.magic.RES_KOD: rscode = 'K' elif entry.rs & ntp.magic.RES_LIMITED: rscode = 'L' else: rscode = '.' (ip, port) = portsplit(entry.addr) try: if not self.showhostnames: dns = ip else: dns = canonicalize_dns(ip) # Forward-confirm the returned DNS confirmed = canonicalization_cache.get(dns) if confirmed is None: confirmed = False try: ai = socket.getaddrinfo(dns, None) for (_, _, _, _, sockaddr) in ai: if sockaddr and sockaddr[0] == ip: confirmed = True break except socket.gaierror: pass canonicalization_cache.set(dns, confirmed) if not confirmed: dns = "%s (%s)" % (ip, dns) if not self.wideremote: # truncate for narrow display dns = dns[:40] stats += " %4hx %c %d %d %6d %5s %s" % \ (entry.rs, rscode, ntp.magic.PKT_MODE(entry.mv), ntp.magic.PKT_VERSION(entry.mv), entry.ct, port[1:], dns) return stats except ValueError: # This can happen when ntpd ships a corrupt varlist return '' class ReslistSummary: "Reusable class for reslist entry summary generation." header = """\ hits addr/prefix or addr mask restrictions """ width = 72 @staticmethod def __getPrefix(mask): if not mask: prefix = '' if ':' in mask: sep = ':' base = 16 else: sep = '.' base = 10 prefix = sum([bin(int(x, base)).count('1') for x in mask.split(sep) if x]) return '/' + str(prefix) def summary(self, variables): hits = variables.get("hits", "?") address = variables.get("addr", "?") mask = variables.get("mask", "?") if address == '?' or mask == '?': return '' address += ReslistSummary.__getPrefix(mask) flags = variables.get("flags", "?") # reslist responses are often corrupted s = "%10s %s\n %s\n" % (hits, address, flags) # Throw away corrupted entries. This is a shim - we really # want to make ntpd stop generating garbage for c in s: if not c.isalnum() and c not in "/.: \n": return '' return s class IfstatsSummary: "Reusable class for ifstats entry summary generation." header = """\ interface name send # address/broadcast drop flag received sent failed peers uptime """ width = 74 # Numbers are the fieldsize fields = {'name': '%-24.24s', 'flags': '%4x', 'rx': '%6d', 'tx': '%6d', 'txerr': '%6d', 'pc': '%5d', 'up': '%8d'} def summary(self, i, variables): formatted = {} try: # Format the fields for name in self.fields.keys(): value = variables.get(name, "?") if value == "?": fmt = value else: fmt = self.fields[name] % value formatted[name] = fmt enFlag = '.' if variables.get('en', False) else 'D' address = variables.get("addr", "?") bcast = variables.get("bcast") # Assemble the fields into a line s = ("%3u %s %s %s %s %s %s %s %s\n %s\n" % (i, formatted['name'], enFlag, formatted['flags'], formatted['rx'], formatted['tx'], formatted['txerr'], formatted['pc'], formatted['up'], address)) if bcast: s += " %s\n" % bcast except TypeError: # pragma: no cover # Can happen when ntpd ships a corrupted response return '' # FIXME, a brutal and slow way to check for invalid chars.. # maybe just strip non-printing chars? for c in s: if not c.isalnum() and c not in "/.:[] \%\n": return '' return s try: from collections import OrderedDict except ImportError: # pragma: no cover class OrderedDict(dict): "A stupid simple implementation in order to be back-portable to 2.6" # This can be simple because it doesn't need to be fast. # The programs that use it only have to run at human speed, # and the collections are small. def __init__(self, items=None): dict.__init__(self) self.__keys = [] if items: for (k, v) in items: self[k] = v def __setitem__(self, key, val): dict.__setitem__(self, key, val) self.__keys.append(key) def __delitem__(self, key): dict.__delitem__(self, key) self.__keys.remove(key) def keys(self): return self.__keys def items(self): return tuple([(k, self[k]) for k in self.__keys]) def __iter__(self): for key in self.__keys: yield key # end ntpsec-1.1.0+dfsg1/pylib/__init__.py0000644000175000017500000000053313252364117017056 0ustar rlaagerrlaager# -*- coding: utf-8 -*- # SPDX-License-Identifier: BSD-2-clause # # This code runs compatibly under Python 2 and 3.x for x >= 2. # Preserve this property! from __future__ import absolute_import # Ensure Python2 behaves like Python 3 api_major_version = 1 # bumped on incompatible changes api_minor_version = 0 # bumped on compatible changes ntpsec-1.1.0+dfsg1/pylib/README0000644000175000017500000000045113252364117015624 0ustar rlaagerrlaager= pylib README = This directory contains helper classes for NTP utilities written in Python. It should be installed under the name 'ntp'. The build recipe locally symlinks it under that name at other places in the tree so that various utilities can be run before the package has been installed. ntpsec-1.1.0+dfsg1/pylib/packet.py0000644000175000017500000021465713252364117016604 0ustar rlaagerrlaager# -*- coding: utf-8 -*- """ packet.py - definitions and classes for Python querying of NTP Freely translated from the old C ntpq code by ESR, with comments preserved. The idea was to cleanly separate ntpq-that-was into a thin front-end layer handling mainly command interpretation and a back-end that presents the take from ntpd as objects that can be re-used by other front ends. Other reusable pieces live in util.py. This code should be Python2-vs-Python-3 agnostic. Keep it that way! Here are some pictures to help make sense of this code. First, from RFC 5905, the general structure of an NTP packet (Figure 8): 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |LI | VN |Mode | Stratum | Poll | Precision | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Root Delay | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Root Dispersion | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Reference ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + Reference Timestamp (64) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + Origin Timestamp (64) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + Receive Timestamp (64) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + Transmit Timestamp (64) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | . . . Extension Field 1 (variable) . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | . . . Extension Field 2 (variable) . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key Identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | dgst (128) | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The fixed header is 48 bytes long. The simplest possible case of an NTP packet is the minimal SNTP request, a mode 3 packet with the Stratum and all following fields zeroed out to byte 47. How to interpret these fields: t_1, the origin timestamp, is the time according to the client at which the request was sent. t_2, the receive timestamp, is the time according to the server at which the request was received. t_3, the transmit timestamp, is the time according to the server at which the reply was sent. You also need t_4, the destination timestamp, is the time according to the client at which the reply was received. This is not in the reply packet, it's the packet receipt time collected by the client. The 'Reference timestamp' is an unused historical relic. It's supposed to be copied unchanged from upstream in the stratum hierarchy. Normal practice has been for Stratum 1 servers to fill it in with the raw timestamp from the most recent reference-clock. Theta is the thing we want to estimate: the offset between the server clock and the client clock. The sign convention is that theta is positive iff the server is ahead of the client. Theta is estimated by [(t_2-t_1)+(t_3-t_4)]/2. The accuracy of this estimate is predicated upon network latency being symmetrical. Delta is the network round trip time, i.e. (t_4-t_1)-(t_3-t_2). Here's how the terms work: (t_4-t_1) is the total time that the request was in flight, and (t_3-t_2) is time that the server spent processing it; when you subtract that out you're left with just network delays. Lambda nominally represents the maximum amount by which theta could be off. It's computed as delta/2 + epsilon. The delta/2 term usually dominates and represents the maximum amount by which network asymmetry could be throwing off the calculation. Epsilon is the sum of three other sources of error: rho_r: the (im)precision field from response packet, representing the server's inherent error in clock measurement. rho_s: the client's own (im)precision. PHI*(t_4-t_1): The amount by which the client's clock may plausibly have drifted while the packet was in flight. PHI is taken to be a constant of 15ppm. rho_r and rho_s are estimated by making back-to-back calls to clock_gettime() (or similar) and taking their difference. They're encoded on the wire as an eight-bit two's complement integer representing, to the nearest integer, log_2 of the value in seconds. If you look at the raw data, there are 3 unknowns: * transit time client to server * transit time server to client * clock offset but there are only two equations, so you can't solve it. NTP gets a 3rd equation by assuming the transit times are equal. That lets it solve for the clock offset. If you assume that both clocks are accurate which is reasonable if you have GPS at both ends, then you can easily solve for the transit times in each direction. The RFC 5905 diagram is slightly out of date in that the digest header assumes a 128-bit (16-octet) MD5 hash, but it is also possible for the field to be a 160-bit (20-octet) SHA1 hash. An extension field consists of a 16-bit network-order type field length, followed by a 16-bit network-order payload length in octets, followed by the payload (which must be padded to a 4-octet boundary). 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Type field | Payload length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Payload (variable) | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Here's what a Mode 6 packet looks like: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |LI | VN | 6 |R|E|M| Opcode | Sequence | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Status | Association ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Offset | Count | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | . . . Payload (variable) . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Key Identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | dgst (128) | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ In this case the fixed header is 24 bytes long. R = Response bit E = Error bit M = More bit. A Mode 6 packet cannot have extension fields. """ # SPDX-License-Identifier: BSD-2-clause from __future__ import print_function, division import getpass import hashlib import os import select import socket import string import struct import sys import time import ntp.control import ntp.magic import ntp.ntpc import ntp.util # 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. # # - 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: # pragma: no cover # Python 2 polystr = str polybytes = bytes polyord = ord polychr = str input = raw_input def string_escape(s): return s.decode('string_escape') def make_wrapper(fp): return fp else: # pragma: nocover # Python 3 import io 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 polyord(c): "Polymorphic ord() function" if isinstance(c, str): return ord(c) else: return c def polychr(c): "Polymorphic chr() function" if isinstance(c, int): return chr(c) else: return c 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_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) # Limit on packets in a single Mode 6 response. Increasing this value to # 96 will marginally speed "mrulist" operation on lossless networks # but it has been observed to cause loss on WiFi networks and with # an IPv6 go6.net tunnel over UDP. That loss causes the request # row limit to be cut in half, and it grows back very slowly to # ensure forward progress is made and loss isn't triggered too quickly # afterward. While the lossless case gains only marginally with # MAXFRAGS == 96, the lossy case is a lot slower due to the repeated # timeouts. Empirically, MAXFRAGS == 32 avoids most of the routine loss # on both the WiFi and UDP v6 tunnel tests and seems a good compromise. # This suggests some device in the path has a limit of 32 ~512 byte UDP # packets in queue. # Lowering MAXFRAGS may help with particularly lossy networks, but some # ntpq commands may rely on the longtime value of 24 implicitly, # assuming a single multipacket response will be large enough for any # needs. In contrast, the "mrulist" command is implemented as a series # of requests and multipacket responses to each. MAXFRAGS = 32 # Requests are automatically retried once, so total timeout with no # response is a bit over 2 * DEFTIMEOUT, or 10 seconds. At the other # extreme, a request eliciting 32 packets of responses each for some # reason nearly DEFSTIMEOUT seconds after the prior in that series, # with a single packet dropped, would take around 32 * DEFSTIMEOUT, or # 93 seconds to fail each of two times, or 186 seconds. # Some commands involve a series of requests, such as "peers" and # "mrulist", so the cumulative timeouts are even longer for those. DEFTIMEOUT = 5000 DEFSTIMEOUT = 3000 # The maximum keyid for authentication, keyid is a 16-bit field MAX_KEYID = 0xFFFF class Packet: "Encapsulate an NTP fragment" # The following two methods are copied from macros in includes/control.h @staticmethod def VN_MODE(v, m): return (((v & 7) << 3) | (m & 0x7)) @staticmethod def PKT_LI_VN_MODE(l, v, m): return (((l & 3) << 6) | Packet.VN_MODE(v, m)) def __init__(self, mode=ntp.magic.MODE_CLIENT, version=ntp.magic.NTP_VERSION, session=None): self.session = session # Where to get session context self.li_vn_mode = 0 # leap, version, mode (uint8_t) # Subclasses have variable fields here self.extension = b'' # extension data self.li_vn_mode = Packet.PKT_LI_VN_MODE(ntp.magic.LEAP_NOTINSYNC, version, mode) # These decorators will allow us to assign the extension Python 3 strings @property def extension(self): return self.__extension @extension.setter def extension(self, x): self.__extension = polybytes(x) def leap(self): return ("no-leap", "add-leap", "del-leap", "unsync")[ntp.magic.PKT_LEAP(self.li_vn_mode)] def version(self): return (self.li_vn_mode >> 3) & 0x7 def mode(self): return self.li_vn_mode & 0x7 class SyncException(BaseException): # pragma: no cover def __init__(self, message, errorcode=0): self.message = message self.errorcode = errorcode def __str__(self): return self.message class SyncPacket(Packet): "Mode 1-5 time-synchronization packet, including SNTP." format = "!BBBbIIIQQQQ" HEADER_LEN = 48 UNIX_EPOCH = 2208988800 # Midnight 1 Jan 1970 in secs since NTP epoch PHI = 15 * 1e-6 # 15ppm def __init__(self, data=''): Packet.__init__(self) self.status = 0 # status word for association (uint16_t) self.stratum = 0 self.poll = 0 self.precision = 0 self.root_delay = 0 self.root_dispersion = 0 self.refid = 0 self.reference_timestamp = 0 self.origin_timestamp = 0 self.receive_timestamp = 0 self.transmit_timestamp = 0 self.extension = '' self.extfields = [] self.mac = '' self.hostname = None self.resolved = None self.received = SyncPacket.posix_to_ntp(time.time()) self.trusted = True self.rescaled = False if data: self.analyze(polybytes(data)) def analyze(self, data): datalen = len(data) if datalen < SyncPacket.HEADER_LEN or (datalen & 3) != 0: raise SyncException("impossible packet length") (self.li_vn_mode, self.stratum, self.poll, self.precision, self.root_delay, self.root_dispersion, self.refid, self.reference_timestamp, self.origin_timestamp, self.receive_timestamp, self.transmit_timestamp) = struct.unpack( SyncPacket.format, data[:SyncPacket.HEADER_LEN]) self.extension = data[SyncPacket.HEADER_LEN:] # Parse the extension field if present. We figure out whether # an extension field is present by measuring the MAC size. If # the number of 4-octet words following the packet header is # 0, no MAC is present and the packet is not authenticated. If # 1, the packet is a crypto-NAK; if 3, the packet is # authenticated with DES; if 5, the packet is authenticated # with MD5; if 6, the packet is authenticated with SHA1. If 2 # or 4, the packet is a runt and discarded forthwith. If # greater than 6, an extension field is present, so we # subtract the length of the field and go around again. payload = self.extension # Keep extension intact for flatten() while len(payload) > 24: (ftype, flen) = struct.unpack("!II", payload[:8]) self.extfields.append((ftype, payload[8:8+flen])) payload = payload[8+flen:] if len(payload) == 4: # Crypto-NAK self.mac = payload elif len(payload) == 12: # DES raise SyncException("Unsupported DES authentication") elif len(payload) in (8, 16): raise SyncException("Packet is a runt") elif len(payload) in (20, 24): # MD5 or SHA1 self.mac = payload @staticmethod def ntp_to_posix(t): "Scale from NTP time to POSIX time" # Note: assumes we're in the same NTP era as the transmitter... return (t / (2**32)) - SyncPacket.UNIX_EPOCH @staticmethod def posix_to_ntp(t): "Scale from POSIX time to NTP time" # Note: assumes we're in the same NTP era as the transmitter... # This receives floats, can't use shifts return int((t + SyncPacket.UNIX_EPOCH) * 2**32) def posixize(self): "Rescale all timestamps to POSIX time." if not self.rescaled: self.rescaled = True self.root_delay >>= 16 self.root_dispersion >>= 16 self.reference_timestamp = SyncPacket.ntp_to_posix( self.reference_timestamp) self.origin_timestamp = SyncPacket.ntp_to_posix( self.origin_timestamp) self.receive_timestamp = SyncPacket.ntp_to_posix( self.receive_timestamp) self.transmit_timestamp = SyncPacket.ntp_to_posix( self.transmit_timestamp) self.received = SyncPacket.ntp_to_posix(self.received) def t1(self): return self.origin_timestamp def t2(self): return self.receive_timestamp def t3(self): return self.transmit_timestamp def t4(self): return self.received def delta(self): "Packet flight time" return (self.t4() - self.t1()) - (self.t3() - self.t2()) def epsilon(self): "Residual error due to clock imprecision." # FIXME: Include client imprecision. return SyncPacket.PHI * (self.t4() - self.t1()) + 2**self.precision def synchd(self): "Synchronization distance, estimates worst-case error in seconds" # This is "lambda" in NTP-speak, but that's a Python keyword return abs(self.delta()/2 + self.epsilon()) def adjust(self): "Adjustment implied by this packet - 'theta' in NTP-speak." return ((self.t2()-self.t1())+(self.t3()-self.t4()))/2 def flatten(self): "Flatten the packet into an octet sequence." body = struct.pack(SyncPacket.format, self.li_vn_mode, self.stratum, self.poll, self.precision, self.root_delay, self.root_dispersion, self.refid, self.reference_timestamp, self.origin_timestamp, self.receive_timestamp, self.transmit_timestamp) return body + self.extension def refid_octets(self): "Analyze refid into octets." return ((self.refid >> 24) & 0xff, (self.refid >> 16) & 0xff, (self.refid >> 8) & 0xff, self.refid & 0xff) def refid_as_string(self): "Sometimes it's a clock name or KOD type" return polystr(struct.pack(*(("BBBB",) + self.refid_octets()))) def refid_as_address(self): "Sometimes it's an IPV4 address." return polystr("%d.%d.%d.%d" % self.refid_octets()) def is_crypto_nak(self): return len(self.mac) == 4 def has_MD5(self): return len(self.mac) == 20 def has_SHA1(self): return len(self.mac) == 24 def __repr__(self): "Represent a posixized sync packet in an eyeball-friendly format." r = "> 8) & 0xff def end(self): return self.count + self.offset def stats(self): "Return statistics on a fragment." return "%5d %5d\t%3d octets\n" % (self.offset, self.end(), self.count) def analyze(self, rawdata): rawdata = polybytes(rawdata) (self.li_vn_mode, self.r_e_m_op, self.sequence, self.status, self.associd, self.offset, self.count) = struct.unpack(ControlPacket.format, rawdata[:ControlPacket.HEADER_LEN]) self.extension = rawdata[ControlPacket.HEADER_LEN:] return (self.sequence, self.status, self.associd, self.offset) def flatten(self): "Flatten the packet into an octet sequence." body = struct.pack(ControlPacket.format, self.li_vn_mode, self.r_e_m_op, self.sequence, self.status, self.associd, self.offset, self.count) return body + self.extension def send(self): self.session.sendpkt(self.flatten()) class Peer: "The information we have about an NTP peer." def __init__(self, session, associd, status): self.session = session self.associd = associd self.status = status self.variables = {} def readvars(self): self.variables = self.session.readvars() def __str__(self): return "" % (self.associd, self.status) __repr__ = __str__ SERR_BADFMT = "***Server reports a bad format request packet\n" SERR_PERMISSION = "***Server disallowed request (authentication?)\n" SERR_BADOP = "***Server reports a bad opcode in request\n" SERR_BADASSOC = "***Association ID {0} unknown to server\n" SERR_UNKNOWNVAR = "***A request variable unknown to the server\n" SERR_BADVALUE = "***Server indicates a request variable was bad\n" SERR_UNSPEC = "***Server returned an unspecified error\n" SERR_SOCKET = "***Socket error; probably ntpd is not running\n" SERR_TIMEOUT = "***Request timed out\n" SERR_INCOMPLETE = "***Response from server was incomplete\n" SERR_TOOMUCH = "***Buffer size exceeded for returned data\n" SERR_SELECT = "***Select call failed\n" SERR_NOHOST = "***No host open\n" SERR_BADLENGTH = "***Response length should have been a multiple of 4" SERR_BADKEY = "***Invalid key identifier" SERR_INVPASS = "***Invalid password" SERR_NOKEY = "***Key not found" SERR_BADNONCE = "***Unexpected nonce response format" SERR_BADPARAM = "***Unknown parameter '%s'" SERR_NOCRED = "***No credentials" SERR_SERVER = "***Server error code %s" SERR_STALL = "***No response, probably high-traffic server with low MRU limit" SERR_BADTAG = "***Bad MRU tag %s" SERR_BADSORT = "***Sort order %s is not implemented" SERR_NOTRUST = "***No trusted keys have been declared" def dump_hex_printable(xdata, outfp=sys.stdout): "Dump a packet in hex, in a familiar hex format" rowsize = 16 while len(xdata) > 0: # Slice one row off of our data linedata, xdata = ntp.util.slicedata(xdata, rowsize) # Output data in hex form linelen = len(linedata) line = "%02x " * linelen linedata = [polyord(x) for x in linedata] # Will need this later line %= tuple(linedata) if linelen < rowsize: # Pad out the line to keep columns neat line += " " * (rowsize - linelen) # Output printable data in string form linedata = [chr(x) if (32 <= x < 127) else "." for x in linedata] line += "".join(linedata) + "\n" outfp.write(line) class MRUEntry: "A traffic entry for an MRU list." def __init__(self): self.addr = None # text of IPv4 or IPv6 address and port self.last = None # timestamp of last receipt self.first = None # timestamp of first receipt self.ct = 0 # count of packets received self.mv = None # mode and version self.rs = None # restriction mask (RES_* bits) def avgint(self): last = ntp.ntpc.lfptofloat(self.last) first = ntp.ntpc.lfptofloat(self.first) return (last - first) / self.ct def sortaddr(self): addr = self.addr if addr[0] == '[': # IPv6 [n:n:n::n:n]:sock # or [n:n:n::n:n%x]:sock addr = addr[1:addr.find(']')] pct = addr.find('%') if pct > 0: # % for local IPv6 address on interface n addr = addr[:pct] return socket.inet_pton(socket.AF_INET6, addr) else: # IPv4 a.b.c.d:sock addr = addr[:addr.find(':')] # prefix with 0s so IPv6 sorts after IPv4 # Need 16 rather than 12 to catch ::1 return b'\0'*16 + socket.inet_pton(socket.AF_INET, addr) def __repr__(self): return "" class MRUList: "A sequence of address-timespan pairs returned by ntpd in one response." def __init__(self): self.entries = [] # A list of MRUEntry objects self.now = None # server timestamp marking end of operation def is_complete(self): "Is the server done shipping entries for this span?" return self.now is not None def __repr__(self): return "" % (self.entries, self.now) class ControlException(BaseException): def __init__(self, message, errorcode=0): self.message = message self.errorcode = errorcode def __str__(self): return self.message class ControlSession: "A session to a host" MRU_ROW_LIMIT = 256 server_errors = { ntp.control.CERR_UNSPEC: "UNSPEC", ntp.control.CERR_PERMISSION: "PERMISSION", ntp.control.CERR_BADFMT: "BADFMT", ntp.control.CERR_BADOP: "BADOP", ntp.control.CERR_BADASSOC: "BADASSOC", ntp.control.CERR_UNKNOWNVAR: "UNKNOWNVAR", ntp.control.CERR_BADVALUE: "BADVALUE", ntp.control.CERR_RESTRICT: "RESTRICT", } def __init__(self): self.debug = 0 self.ai_family = socket.AF_UNSPEC self.primary_timeout = DEFTIMEOUT # Timeout for first select self.secondary_timeout = DEFSTIMEOUT # Timeout for later selects # Packet version number we use self.pktversion = ntp.magic.NTP_OLDVERSION + 1 self.always_auth = False # Always send authenticated requests self.keytype = "MD5" self.keyid = None self.passwd = None self.auth = None self.hostname = None self.isnum = False self.sock = None self.port = 0 self.sequence = 0 self.response = "" self.rstatus = 0 self.ntpd_row_limit = ControlSession.MRU_ROW_LIMIT self.logfp = sys.stdout self.nonce_xmit = 0 def close(self): if self.sock: self.sock.close() self.sock = None def havehost(self): "Is the session connected to a host?" return self.sock is not None def __lookuphost(self, hname, fam): "Try different ways to interpret an address and family" if hname.startswith("["): hname = hname[1:-1] # First try to resolve it as an ip address and if that fails, # do a fullblown (DNS) lookup. That way we only use the dns # when it is needed and work around some implementations that # will return an "IPv4-mapped IPv6 address" address if you # give it an IPv4 address to lookup. def hinted_lookup(port, hints): return socket.getaddrinfo(hname, port, self.ai_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP, hints) try: return hinted_lookup(port="ntp", hints=socket.AI_NUMERICHOST) except socket.gaierror as e: ntp.util.dolog(self.logfp, "ntpq: numeric-mode lookup of %s failed, %s" % (hname, e.strerror), self.debug, 3) try: return hinted_lookup(port="ntp", hints=0) except socket.gaierror as e1: if self.logfp is not None: self.logfp.write("ntpq: standard-mode lookup " "of %s failed, %s\n" % (hname, e1.strerror)) # EAI_NODATA and AI_CANONNAME should both exist - they're in the # POSIX API. If this code throws AttributeErrors there is # probably a very old and broken socket layer in your Python # build. The C implementation had a second fallback mode that # removed AI_ADDRCONFIG if the first fallback raised BADFLAGS. fallback_hints = socket.AI_CANONNAME try: fallback_hints |= socket.AI_ADDRCONFIG except AttributeError: pass try: if hasattr(socket, "EAI_NODATA"): errlist = (socket.EAI_NONAME, socket.EAI_NODATA) else: errlist = (socket.EAI_NONAME,) if e1.errno in errlist: try: return hinted_lookup(port="ntp", hints=0) except socket.gaierror as e2: if self.logfp is not None: self.logfp.write("ntpq: ndp lookup failed, %s\n" % e2.strerror) except AttributeError: # pragma: no cover if self.logfp is not None: self.logfp.write( "ntpq: API error, missing socket attributes\n") return None def openhost(self, hname, fam=socket.AF_UNSPEC): "openhost - open a socket to a host" res = self.__lookuphost(hname, fam) if res is None: return False # C implementation didn't use multiple responses, so we don't either (family, socktype, protocol, canonname, sockaddr) = res[0] if canonname is None: self.hostname = socket.inet_ntop(sockaddr[0], family) self.isnum = True else: self.hostname = canonname or hname self.isnum = False ntp.util.dolog(self.logfp, "Opening host %s" % self.hostname, self.debug, 3) self.port = sockaddr[1] try: self.sock = socket.socket(family, socktype, protocol) except socket.error as e: raise ControlException("Error opening %s: %s [%d]" % (hname, e.strerror, e.errno)) try: self.sock.connect(sockaddr) except socket.error as e: raise ControlException("Error connecting to %s: %s [%d]" % (hname, e.strerror, e.errno)) return True def password(self): "Get a keyid and the password if we don't have one." if self.keyid is None: if self.auth is None: try: self.auth = Authenticator() except (OSError, IOError): pass if self.auth and self.hostname == "localhost": try: (self.keyid, self.keytype, self.passwd) \ = self.auth.control() return except ValueError: # There are no trusted keys. Barf. raise ControlException(SERR_NOTRUST) try: if os.isatty(0): key_id = int(input("Keyid: ")) else: key_id = 0 if key_id == 0 or key_id > MAX_KEYID: raise ControlException(SERR_BADKEY) except (SyntaxError, ValueError): # pragma: no cover raise ControlException(SERR_BADKEY) self.keyid = key_id if self.passwd is None: try: self.keytype, passwd = self.auth[self.keyid] except (IndexError, TypeError): passwd = getpass.getpass("%s Password: " % self.keytype) if passwd is None: raise ControlException(SERR_INVPASS) # If the password is longer then 20 chars we assume it is # hex encoded binary string. This assumption exists across all # of NTP. if len(passwd) > 20: passwd = ntp.util.hexstr2octets(passwd) self.passwd = passwd def sendpkt(self, xdata): "Send a packet to the host." while len(xdata) % 4: xdata += b"\x00" ntp.util.dolog(self.logfp, "Sending %d octets. seq=%d" % (len(xdata), self.sequence), self.debug, 3) try: self.sock.sendall(polybytes(xdata)) except socket.error: # On failure, we don't know how much data was actually received if self.logfp is not None: self.logfp.write("Write to %s failed\n" % self.hostname) return -1 if (self.debug >= 5) and (self.logfp is not None): # pragma: no cover # special, not replacing with dolog() self.logfp.write("Request packet:\n") dump_hex_printable(xdata, self.logfp) return 0 def sendrequest(self, opcode, associd, qdata, auth=False): "Ship an ntpq request packet to a server." if (self.debug >= 1) and (self.logfp is not None): # special, not replacing with dolog() if self.debug >= 3: self.logfp.write("\n") # extra space to help find clumps self.logfp.write("sendrequest: opcode=%d, associd=%d, qdata=%s\n" % (opcode, associd, qdata)) # Check to make sure the data will fit in one packet if len(qdata) > ntp.control.CTL_MAX_DATA_LEN: if self.logfp is not None: self.logfp.write("***Internal error! Data too large (%d)\n" % len(qdata)) return -1 # Assemble the packet pkt = ControlPacket(self, opcode, associd, qdata) self.sequence += 1 self.sequence %= 0x10000 # Has to fit in a struct H field pkt.sequence = self.sequence # If we have data, pad it out to a 32-bit boundary. # Do not include these in the payload count. if pkt.extension: pkt.extension = polybytes(pkt.extension) while ((ControlPacket.HEADER_LEN + len(pkt.extension)) & 3): pkt.extension += b"\x00" # If it isn't authenticated we can just send it. Otherwise # we're going to have to think about it a little. if not auth and not self.always_auth: return pkt.send() if self.keyid is None or self.passwd is None: raise ControlException(SERR_NOCRED) # Pad out packet to a multiple of 8 octets to be sure # receiver can handle it. Note: these pad bytes should # *not* be counted in the header count field. while ((ControlPacket.HEADER_LEN + len(pkt.extension)) & 7): pkt.extension += b"\x00" # Do the MAC compuation. mac = Authenticator.compute_mac(pkt.flatten(), self.keyid, self.keytype, self.passwd) if mac is None: raise ControlException(SERR_NOKEY) else: pkt.extension += polybytes(mac) return pkt.send() def getresponse(self, opcode, associd, timeo): "Get a response expected to match a given opcode and associd." # This is pretty tricky. We may get between 1 and MAXFRAG packets # back in response to the request. We peel the data out of # each packet and collect it in one long block. When the last # packet in the sequence is received we'll know how much data we # should have had. Note we use one long time out, should reconsider. fragments = [] self.response = '' seenlastfrag = False bail = 0 # TODO: refactor to simplify while retaining semantic info if self.logfp is not None: warn = self.logfp.write else: warn = (lambda x: x) warndbg = (lambda txt, th: ntp.util.dolog(self.logfp, txt, self.debug, th)) warndbg("Fragment collection begins", 1) # Loop until we have an error or a complete response. Nearly all # code paths to loop again use continue. while True: # Discarding various invalid packets can cause us to # loop more than MAXFRAGS times, but enforce a sane bound # on how long we're willing to spend here. bail += 1 if bail >= (2*MAXFRAGS): raise ControlException(SERR_TOOMUCH) if len(fragments) == 0: tvo = self.primary_timeout / 1000 else: tvo = self.secondary_timeout / 1000 warndbg("At %s, select with timeout %d begins" % (time.asctime(), tvo), 5) try: (rd, _, _) = select.select([self.sock], [], [], tvo) except select.error: raise ControlException(SERR_SELECT) warndbg("At %s, select with timeout %d ends" % (time.asctime(), tvo), 5) if not rd: # Timed out. Return what we have if len(fragments) == 0: if timeo: raise ControlException(SERR_TIMEOUT) if timeo: if (self.debug >= 1) and \ (self.logfp is not None): # pragma: no cover # special, not replacing with dolog() self.logfp.write( "ERR_INCOMPLETE: Received fragments:\n") for (i, frag) in enumerate(fragments): self.logfp.write("%d: %s" % (i+1, frag.stats())) self.logfp.write("last fragment %sreceived\n" % ("not ", "")[seenlastfrag]) raise ControlException(SERR_INCOMPLETE) warndbg("At %s, socket read begins" % time.asctime(), 4) try: rawdata = polybytes(self.sock.recv(4096)) except socket.error: # pragma: no cover # usually, errno 111: connection refused raise ControlException(SERR_SOCKET) warndbg("Received %d octets" % len(rawdata), 3) rpkt = ControlPacket(self) try: rpkt.analyze(rawdata) except struct.error: raise ControlException(SERR_UNSPEC) # Validate that packet header is sane, and the correct type valid = self.__validate_packet(rpkt, rawdata, opcode, associd) if valid is False: # pragma: no cover continue # Someday, perhaps, check authentication here # Clip off the MAC, if any rpkt.extension = rpkt.extension[:rpkt.count] if rpkt.count == 0 and rpkt.more(): warn("Received count of 0 in non-final fragment\n") continue if seenlastfrag and rpkt.more(): # pragma: no cover # I'n not sure this can be triggered without hitting another # error first. warn("Received second last fragment\n") continue # Find the most recent fragment with a not_earlier = [frag for frag in fragments if frag.offset >= rpkt.offset] if len(not_earlier): not_earlier = not_earlier[0] if not_earlier.offset == rpkt.offset: warn("duplicate %d octets at %d ignored, prior " " %d at %d\n" % (rpkt.count, rpkt.offset, not_earlier.count, not_earlier.offset)) continue if len(fragments) > 0: last = fragments[-1] if last.end() > rpkt.offset: warn("received frag at %d overlaps with %d octet " "frag at %d\n" % (rpkt.offset, last.count, last.offset)) continue if not_earlier and rpkt.end() > not_earlier.offset: warn("received %d octet frag at %d overlaps with " "frag at %d\n" % (rpkt.count, rpkt.offset, not_earlier.offset)) continue warndbg("Recording fragment %d, size = %d offset = %d, " " end = %d, more=%s" % (len(fragments)+1, rpkt.count, rpkt.offset, rpkt.end(), rpkt.more()), 3) # Passed all tests, insert it into the frag list. fragments.append(rpkt) fragments.sort(key=lambda frag: frag.offset) # Figure out if this was the last. # Record status info out of the last packet. if not rpkt.more(): seenlastfrag = True self.rstatus = rpkt.status # If we've seen the last fragment, look for holes in the sequence. # If there aren't any, we're done. if seenlastfrag and fragments[0].offset == 0: for f in range(1, len(fragments)): if fragments[f-1].end() != fragments[f].offset: warndbg("Hole in fragment sequence, %d of %d" % (f, len(fragments)), 1) break else: tempfraglist = [polystr(f.extension) for f in fragments] self.response = polybytes("".join(tempfraglist)) warndbg("Fragment collection ends. %d bytes " " in %d fragments" % (len(self.response), len(fragments)), 1) # special loggers, not replacing with dolog() if self.debug >= 5: # pragma: no cover warn("Response packet:\n") dump_hex_printable(self.response, self.logfp) elif self.debug >= 3: # pragma: no cover # FIXME: Garbage when retrieving assoc list (binary) warn("Response packet:\n%s\n" % repr(self.response)) elif self.debug >= 2: # pragma: no cover # FIXME: Garbage when retrieving assoc list (binary) eol = self.response.find("\n") firstline = self.response[:eol] warn("First line:\n%s\n" % repr(firstline)) return None break def __validate_packet(self, rpkt, rawdata, opcode, associd): # TODO: refactor to simplify while retaining semantic info if self.logfp is not None: warn = self.logfp.write else: warn = (lambda x: x) warndbg = (lambda txt, th: ntp.util.dolog(self.logfp, txt, self.debug, th)) if ((rpkt.version() > ntp.magic.NTP_VERSION) or (rpkt.version() < ntp.magic.NTP_OLDVERSION)): warndbg("Fragment received with version %d" % rpkt.version(), 1) return False if rpkt.mode() != ntp.magic.MODE_CONTROL: warndbg("Fragment received with mode %d" % rpkt.mode(), 1) return False if not rpkt.is_response(): warndbg("Received request, wanted response", 1) return False # Check opcode and sequence number for a match. # Could be old data getting to us. if rpkt.sequence != self.sequence: warndbg("Received sequence number %d, wanted %d" % (rpkt.sequence, self.sequence), 1) return False if rpkt.opcode() != opcode: warndbg("Received opcode %d, wanted %d" % (rpkt.opcode(), opcode), 1) return False # Check the error code. If non-zero, return it. if rpkt.is_error(): if rpkt.more(): warn("Error %d received on non-final fragment\n" % rpkt.errcode()) self.keyid = self.passwd = None raise ControlException( SERR_SERVER % ControlSession.server_errors[rpkt.errcode()], rpkt.errcode()) # Check the association ID to make sure it matches what we expect if rpkt.associd != associd: warn("Association ID %d doesn't match expected %d\n" % (rpkt.associd, associd)) # validate received payload size is padded to next 32-bit # boundary and no smaller than claimed by rpkt.count if len(rawdata) & 0x3: warn("Response fragment not padded, size = %d\n" % len(rawdata)) return False shouldbesize = (ControlPacket.HEADER_LEN + rpkt.count + 3) & ~3 if len(rawdata) < shouldbesize: warn("Response fragment claims %u octets payload, " "above %d received\n" % (rpkt.count, len(rawdata) - ControlPacket.HEADER_LEN)) raise ControlException(SERR_INCOMPLETE) return True def doquery(self, opcode, associd=0, qdata="", auth=False): "send a request and save the response" if not self.havehost(): raise ControlException(SERR_NOHOST) retry = True while True: # Ship the request self.sendrequest(opcode, associd, qdata, auth) # Get the response. try: res = self.getresponse(opcode, associd, not retry) except ControlException as e: if retry and e.message in (SERR_TIMEOUT, SERR_INCOMPLETE): retry = False continue else: raise e break # Return data on success return res def readstat(self, associd=0): "Read peer status, or throw an exception." self.doquery(opcode=ntp.control.CTL_OP_READSTAT, associd=associd) if len(self.response) % 4: raise ControlException(SERR_BADLENGTH) idlist = [] if associd == 0: for i in range(len(self.response)//4): data = self.response[4*i:4*i+4] (associd, status) = struct.unpack("!HH", data) idlist.append(Peer(self, associd, status)) idlist.sort(key=lambda a: a.associd) return idlist def __parse_varlist(self, raw=False): "Parse a response as a textual varlist." # Strip out NULs and binary garbage from text; # ntpd seems prone to generate these, especially # in reslist responses. kvpairs = [] instring = False response = "" self.response = polystr(self.response) for c in self.response: cord = polyord(c) if c == '"': response += c instring = not instring elif (instring is False) and (c == ","): # Separator between key=value pairs, done with this pair kvpairs.append(response.strip()) response = "" elif 0 < cord < 127: # if it isn't a special case or garbage, add it response += c if len(response) > 0: # The last item won't be caught by the loop kvpairs.append(response.strip()) items = [] for pair in kvpairs: if "=" in pair: key, value = ntp.util.slicedata(pair, pair.index("=")) value = value[1:] # Remove '=' else: key, value = pair, "" key, value = key.strip(), value.strip() # Start trying to cast to non-string types if value: try: castedvalue = int(value, 0) except ValueError: try: castedvalue = float(value) if (key == "delay") and (raw is False): # Hack for non-raw-mode to get precision items.append(("delay-s", value)) except ValueError: if (value[0] == '"') and (value[-1] == '"'): value = value[1:-1] castedvalue = value # str / unknown, stillneed casted else: # no value castedvalue = value if raw is True: items.append((key, (castedvalue, value))) else: items.append((key, castedvalue)) return ntp.util.OrderedDict(items) def readvar(self, associd=0, varlist=None, opcode=ntp.control.CTL_OP_READVAR, raw=False): "Read system vars from the host as a dict, or throw an exception." if varlist is None: qdata = "" else: qdata = ",".join(varlist) self.doquery(opcode, associd=associd, qdata=qdata) return self.__parse_varlist(raw) def config(self, configtext): "Send configuration text to the daemon. Return True if accepted." self.doquery(opcode=ntp.control.CTL_OP_CONFIGURE, qdata=configtext, auth=True) # Copes with an implementation error - ntpd uses putdata without # setting the size correctly. if not self.response: raise ControlException(SERR_PERMISSION) elif b"\x00" in self.response: self.response = self.response[:self.response.index(b"\x00")] self.response = self.response.rstrip() return self.response == polybytes("Config Succeeded") def fetch_nonce(self): """ Ask for, and get, a nonce that can be replayed. This combats source address spoofing """ for i in range(4): # retry 4 times self.doquery(opcode=ntp.control.CTL_OP_REQ_NONCE) self.nonce_xmit = time.time() if self.response.startswith(polybytes("nonce=")): return polystr(self.response.strip()) # maybe a delay between tries? # uh, oh, no nonce seen # this print probably never can be seen... if str is bytes: resp = self.response else: resp = self.response.decode() self.logfp.write("## Nonce expected: %s" % resp) raise ControlException(SERR_BADNONCE) def mrulist(self, variables=None, rawhook=None, direct=None): "Retrieve MRU list data" restarted_count = 0 cap_frags = True warndbg = (lambda txt, th: ntp.util.dolog(self.logfp, txt, self.debug, th)) sorter = None frags = MAXFRAGS self.slots = 0 if variables is None: variables = {} if variables: if "sort" in variables: sortkey = variables["sort"] del variables["sort"] # Slots are retrieved oldest first. # Slots are printed in reverse so the normal/no-sort # case prints youngest first. # That means sort functions are backwards. # Note lstint is backwards again (aka normal/forward) # since we really want to sort on now-last rather than last. sortdict = { # lstint ascending "lstint": lambda e: ntp.ntpc.lfptofloat(e.last), # lstint descending "-lstint": lambda e: -ntp.ntpc.lfptofloat(e.last), # avgint ascending "avgint": lambda e: -e.avgint(), # avgint descending "-avgint": lambda e: e.avgint(), # IPv4 asc. then IPv6 asc. "addr": lambda e: e.sortaddr(), # IPv6 desc. then IPv4 desc. "-addr": lambda e: e.sortaddr(), # hit count ascending "count": lambda e: -e.ct, # hit count descending "-count": lambda e: e.ct, } if sortkey == "lstint": sortkey = None # normal/default case, no need to sort if sortkey is not None: sorter = sortdict.get(sortkey) if sorter is None: raise ControlException(SERR_BADSORT % sortkey) for k in list(variables.keys()): if k in ("mincount", "resall", "resany", "kod", "limited", "maxlstint", "laddr", "recent", "sort", "frags", "limit"): continue else: raise ControlException(SERR_BADPARAM % k) if 'frags' in variables: frags = int(variables.get('frags')) del variables['frags'] if 'kod' in variables: variables['resany'] = variables.get('resany', 0) \ | ntp.magic.RES_KOD del variables['kod'] if 'limited' in variables: variables['resany'] = variables.get('resany', 0) \ | ntp.magic.RES_LIMITED del variables['limited'] nonce = self.fetch_nonce() span = MRUList() try: # Form the initial request limit = min(3 * MAXFRAGS, self.ntpd_row_limit) req_buf = "%s, frags=%d" % (nonce, frags) if variables: if 'resall' in variables: variables['resall'] = hex(variables['resall']) if 'resany' in variables: variables['resany'] = hex(variables['resany']) parms = ", " + ",".join([("%s=%s" % it) for it in list(variables.items())]) else: parms = "" req_buf += parms first_time_only = "recent=%s" % variables.get("recent") while True: # Request additions to the MRU list try: self.doquery(opcode=ntp.control.CTL_OP_READ_MRU, qdata=req_buf) recoverable_read_errors = False except ControlException as e: recoverable_read_errors = True if e.errorcode is None: raise e elif e.errorcode == ntp.control.CERR_UNKNOWNVAR: # None of the supplied prior entries match, so # toss them from our list and try again. warndbg("no overlap between prior entries and " "server MRU list", 1) restarted_count += 1 if restarted_count > 8: raise ControlException(SERR_STALL) warndbg("---> Restarting from the beginning, " "retry #%u" % restarted_count, 1) elif e.errorcode == ntp.control.CERR_BADVALUE: if cap_frags: cap_frags = False warndbg("Reverted to row limit from " "fragments limit.", 1) else: # ntpd has lower cap on row limit self.ntpd_row_limit -= 1 limit = min(limit, self.ntpd_row_limit) warndbg("Row limit reduced to %d following " "CERR_BADVALUE." % limit, 1) elif e.errorcode in (SERR_INCOMPLETE, SERR_TIMEOUT): # Reduce the number of rows/frags requested by # half to recover from lost response fragments. if cap_frags: frags = max(2, frags / 2) warndbg("Frag limit reduced to %d following " "incomplete response." % frags, 1) else: limit = max(2, limit / 2) warndbg("Row limit reduced to %d following " " incomplete response." % limit, 1) elif e.errorcode: raise e # Parse the response variables = self.__parse_varlist() # Comment from the C code: # This is a cheap cop-out implementation of rawmode # output for mrulist. A better approach would be to # dump similar output after the list is collected by # ntpq with a continuous sequence of indexes. This # cheap approach has indexes resetting to zero for # each query/response, and duplicates are not # coalesced. if rawhook: rawhook(variables) # Analyze the contents of this response into a span structure curidx = -1 mru = None for (tag, val) in variables.items(): warndbg("tag=%s, val=%s" % (tag, val), 4) if tag == "nonce": nonce = "%s=%s" % (tag, val) elif tag == "last.older": continue elif tag == "addr.older": continue if tag == "now": # finished marker span.now = ntp.ntpc.lfptofloat(val) continue elif tag == "last.newest": # more finished continue for prefix in ("addr", "last", "first", "ct", "mv", "rs"): if tag.startswith(prefix + "."): (member, idx) = tag.split(".") try: idx = int(idx) except ValueError: raise ControlException(SERR_BADTAG % tag) if idx != curidx: # This makes duplicates curidx = idx if mru: # Can't have partial slots on list # or printing crashes after ^C # Append full slot now span.entries.append(mru) mru = MRUEntry() self.slots += 1 setattr(mru, prefix, val) if mru: span.entries.append(mru) if direct is not None: direct(span.entries) # If we've seen the end sentinel on the span, break out if span.is_complete(): break # The C version of ntpq used to snooze for a bit # between MRU queries to let ntpd catch up with other # duties. It turns out this is quite a bad idea. Above # a certain traffic threshold, servers accumulate MRU records # faster than this protocol loop can capture them such that # you never get a complete span. The last thing you want to # do when trying to keep up with a high-traffic server is stall # in the read loop. # time.sleep(0.05) # If there were no errors, increase the number of rows # to a maximum of 3 * MAXFRAGS (the most packets ntpq # can handle in one response), on the assumption that # no less than 3 rows fit in each packet, capped at # our best guess at the server's row limit. if not recoverable_read_errors: if cap_frags: frags = min(MAXFRAGS, frags + 1) else: limit = min(3 * MAXFRAGS, self.ntpd_row_limit, max(limit + 1, limit * 33 / 32)) # Only ship 'recent' on the first request parms = parms.replace(first_time_only, "") # Prepare next query with as many address and last-seen # timestamps as will fit in a single packet. A new nonce # might be required. if time.time() - self.nonce_xmit >= ntp.control.NONCE_TIMEOUT: nonce = self.fetch_nonce() req_buf = "%s, %s=%d%s" % \ (nonce, "frags" if cap_frags else "limit", frags if cap_frags else limit, parms) for i in range(len(span.entries)): e = span.entries[len(span.entries) - i - 1] incr = ", addr.%d=%s, last.%d=%s" % (i, e.addr, i, e.last) if ((len(req_buf) + len(incr) >= ntp.control.CTL_MAX_DATA_LEN)): break else: req_buf += incr if direct is not None: span.entries = [] except KeyboardInterrupt: # pragma: no cover pass # We can test for interruption with is_complete() # C ntpq's code for stitching together spans was absurdly # overelaborate - all that dancing with last.older and # addr.older was, as far as I can tell, just pointless. # Much simpler to just run through the final list throwing # out every entry with an IP address that is duplicated # with a later most-recent-transmission time. addrdict = {} deletia = [] for (i, entry) in enumerate(span.entries): if entry.addr not in addrdict: addrdict[entry.addr] = [] addrdict[entry.addr].append((i, entry.last)) for addr in addrdict: deletia += sorted(addrdict[addr], key=lambda x: x[1])[:-1] deletia = [x[0] for x in deletia] deletia.sort(reverse=True) for i in deletia: span.entries.pop(i) # Sort for presentation if sorter: span.entries.sort(key=sorter) if sortkey == "addr": # I don't know how to feed a minus sign to text sort span.entries.reverse() return span def __ordlist(self, listtype): "Retrieve ordered-list data." self.doquery(opcode=ntp.control.CTL_OP_READ_ORDLIST_A, qdata=listtype, auth=True) stanzas = [] for (key, value) in self.__parse_varlist().items(): if key[-1].isdigit() and '.' in key: (stem, stanza) = key.split(".") stanza = int(stanza) if stanza > len(stanzas) - 1: for i in range(len(stanzas), stanza + 1): stanzas.append(ntp.util.OrderedDict()) stanzas[stanza][stem] = value return stanzas def reslist(self): "Retrieve reslist data." return self.__ordlist("addr_restrictions") def ifstats(self): "Retrieve ifstats data." return self.__ordlist("ifstats") class Authenticator: "MAC authentication manager for NTP packets." def __init__(self, keyfile=None): # We allow I/O and permission errors upward deliberately self.passwords = {} if keyfile is not None: for line in open(keyfile): if '#' in line: line = line[:line.index("#")] line = line.strip() if not line: continue (keyid, keytype, passwd) = line.split() self.passwords[int(keyid)] = (keytype, passwd) def __len__(self): return len(self.passwords) def __getitem__(self, keyid): return self.passwords.get(keyid) def control(self, keyid=None): "Get a keyid/passwd pair that is trusted on localhost" if keyid is not None: if keyid in self.passwords: return (keyid,) + self.passwords[keyid] else: return (keyid, None, None) for line in open("/etc/ntp.conf"): if line.startswith("control"): keyid = int(line.split()[1]) (keytype, passwd) = self.passwords[keyid] if passwd is None: raise ValueError if len(passwd) > 20: passwd = ntp.util.hexstr2octets(passwd) return (keyid, keytype, passwd) else: raise ValueError @staticmethod def compute_mac(payload, keyid, keytype, passwd): hasher = hashlib.new(keytype) hasher.update(polybytes(passwd)) hasher.update(payload) if hasher.digest_size == 0: return None else: return struct.pack("!I", keyid) + hasher.digest() @staticmethod def have_mac(packet): "Does this packet have a MAC?" # According to RFC 5909 7.5 the MAC is always present when an extension # field is present. Note: this crude test will fail on Mode 6 packets. # On those you have to go in and look at the count. return len(packet) > ntp.magic.LEN_PKT_NOMAC def verify_mac(self, packet): "Does the MAC on this packet verify according to credentials we have?" # FIXME: Someday, figure out how to handle SHA1? HASHLEN = 16 # Length of MD5 hash. payload = packet[:-HASHLEN-4] keyid = packet[-HASHLEN-4:-HASHLEN] mac = packet[-HASHLEN:] (keyid,) = struct.unpack("!I", keyid) if keyid not in self.passwords: return False (keytype, passwd) = self.passwords[keyid] hasher = hashlib.new(keytype) hasher.update(passwd) hasher.update(payload) return polybytes(hasher.digest()) == mac # end ntpsec-1.1.0+dfsg1/pylib/agentx_packet.py0000644000175000017500000014661513252364117020150 0ustar rlaagerrlaager# -*- coding: utf-8 -*- # Common utility functions # SPDX-License-Identifier: BSD-2-clause from __future__ import print_function import struct from ntp.util import slicedata master_encoding = 'latin-1' if str is bytes: # pragma: no cover # Python 2 polystr = str polybytes = bytes polyord = ord polychr = str input = raw_input def string_escape(s): return s.decode('string_escape') else: # pragma: no cover # Python 3 import io 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 polyord(c): "Polymorphic ord() function" if isinstance(c, str): return ord(c) else: return c def polychr(c): "Polymorphic chr() function" if isinstance(c, int): return chr(c) else: return c 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_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) internetPrefix = (1, 3, 6, 1) # Used by the prefix option of OID headers prefixCount = len(internetPrefix) # ========================================================================== # # Callables fall into the following categories: # Data type encoder/decoders # Packet classes/encoders # Packet body decoders # Glue/Utility/Misc functions # # To encode a packet, create an instance of that packet type's class, then # call the encode() method. # # To decode a packet, call the decode_packet function, which will select # the correct decoder for that packet type. # # ========================================================================== # ========================================================================== # # Error classes # # ========================================================================== class ParseError(Exception): def __init__(self, message, header=None, packetData="", remainingData=""): Exception.__init__(self) self.message = message self.header = header self.packetData = packetData self.remainingData = remainingData class ParseDataLengthError(ParseError): pass class ParseVersionError(ParseError): pass class ParsePDUTypeError(ParseError): pass # ========================================================================== # # Packet encoders / decoders # # Encoders are class methods which output a string version of the # packet, ready for transmission. # # Decoders take just the body sliced away from everything else, # and a dict containing the already parsed information from the header. # They return the relevant class instance for the packet in question, # they do not return extra data as they are never supposed to receive it. # # Decoders are not meant to be called directly by external code, # only by decode_packet(). # # ========================================================================== class AgentXPDU: def __init__(self, pduType, bigEndian, sID, tactID, pktID, hascontext=False, context=None): self.pduType = pduType self.bigEndian = bigEndian self.sessionID = sID self.transactionID = tactID self.packetID = pktID self.context = context self._hascontext = hascontext def packetVars(self): "Assembles a list of class variables that it is desirable to print" pktvars = {} names = dir(self) names.remove("context") for vname in names: if vname[0] == "_": # ignores specials, and what we want ignored continue var = getattr(self, vname) if callable(var): # ignores methods # *might* have to rethink this if contents get classified continue pktvars[vname] = var if self._hascontext is True: # context is always present, not always used pktvars["context"] = self.context return pktvars def __repr__(self): s = self.__class__.__name__ + "(" v = [] myvars = self.packetVars() keys = list(myvars.keys()) keys.sort() # they will always be in the same order: testable for name in keys: value = myvars[name] v.append("%s=%s" % (name, repr(value))) s += ", ".join(v) + ")" return s def __eq__(self, other): if not isinstance(other, self.__class__): return False if self.pduType != other.pduType: return False if self.bigEndian != other.bigEndian: return False if self.sessionID != other.sessionID: return False if self.transactionID != other.transactionID: return False if self.packetID != other.packetID: return False if self._hascontext != other._hascontext: return False if self._hascontext is True: if self.context != other.context: return False return True def __ne__(self, other): return not self.__eq__(other) def decode_OpenPDU(data, header): flags = header["flags"] temp, data = slicedata(data, 4) timeout = struct.unpack("Bxxx", polybytes(temp))[0] oid, data = decode_OID(data, header) description = decode_octetstr(data, header)[0] result = OpenPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], timeout, oid, description) return result class OpenPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, timeout, oid, description): AgentXPDU.__init__(self, PDU_OPEN, bigEndian, sID, tactID, pktID) self.timeout = timeout self.oid = classifyOID(oid) self.description = description def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.timeout != other.timeout: return False if self.oid != other.oid: return False if self.description != other.description: return False return True def encode(self): payload = struct.pack("Bxxx", self.timeout) payload += self.oid.encode(self.bigEndian) payload += encode_octetstr(self.bigEndian, self.description) header = encode_pduheader(self.pduType, False, False, False, False, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_ClosePDU(data, header): flags = header["flags"] reason = data[0] # Bxxx if isinstance(reason, str): reason = ord(reason) result = ClosePDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], reason) return result class ClosePDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, reason): AgentXPDU.__init__(self, PDU_CLOSE, bigEndian, sID, tactID, pktID) if reason not in definedReasons: # pragma: no cover raise ValueError("Close reason %s not in defined types" % reason) self.reason = reason def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.reason != other.reason: return False return True def encode(self): payload = struct.pack("Bxxx", self.reason) header = encode_pduheader(self.pduType, False, False, False, False, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_xRegisterPDU(data, header): flags = header["flags"] endianToken = getendian(flags["bigEndian"]) context, data = decode_context(data, header) temp, data = slicedata(data, 4) timeout, priority, rangeSubid = struct.unpack(endianToken + "BBBx", polybytes(temp)) oid, data = decode_OID(data, header) if rangeSubid != 0: temp, data = slicedata(data, 4) upperBound = struct.unpack(endianToken + "I", polybytes(temp))[0] else: upperBound = None if header["type"] == PDU_REGISTER: result = RegisterPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], timeout, priority, oid, rangeSubid, upperBound, context) else: result = UnregisterPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], priority, oid, rangeSubid, upperBound, context) return result class RegisterPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, timeout, priority, subtree, rangeSubid=0, upperBound=None, context=None): AgentXPDU.__init__(self, PDU_REGISTER, bigEndian, sID, tactID, pktID, True, context) self.timeout = timeout self.priority = priority self.subtree = classifyOID(subtree) self.rangeSubid = rangeSubid self.upperBound = upperBound self._instReg = True # so we don't have to write two encode()s def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if hasattr(self, "timeout"): # Relevant to UnregisterPDU subclass if self.timeout != other.timeout: return False if self.priority != other.priority: return False if self.subtree != other.subtree: return False if self.rangeSubid != other.rangeSubid: return False if self.upperBound != other.upperBound: return False return True def encode(self): endianToken = getendian(self.bigEndian) contextP, payload = encode_context(self.bigEndian, self.context) if self.pduType == PDU_REGISTER: payload += struct.pack(endianToken + "BBBx", self.timeout, self.priority, self.rangeSubid) else: payload += struct.pack(endianToken + "xBBx", self.priority, self.rangeSubid) payload += self.subtree.encode(self.bigEndian) if self.rangeSubid != 0: if self.upperBound is None: raise ValueError("upperBound must be set if rangeSubid is set") payload += struct.pack(endianToken + "I", self.upperBound) header = encode_pduheader(self.pduType, self._instReg, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) packet = header + payload return packet class UnregisterPDU(RegisterPDU): def __init__(self, bigEndian, sID, tactID, pktID, priority, subtree, rangeSubid=0, upperBound=None, context=None): RegisterPDU.__init__(self, bigEndian, sID, tactID, pktID, None, priority, subtree, rangeSubid, upperBound, context) self.pduType = PDU_UNREGISTER del self.timeout # Unregister doesn't have a timeout self._instReg = False def decode_xGetPDU(data, header): flags = header["flags"] context, data = decode_context(data, header) oidranges = decode_searchrange_list(data, header) if header["type"] == PDU_GET_NEXT: result = GetNextPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], oidranges, context) else: result = GetPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], oidranges, context) return result class GetPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, oidranges, context=None): AgentXPDU.__init__(self, PDU_GET, bigEndian, sID, tactID, pktID, True, context) self.oidranges = oidranges def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.oidranges != other.oidranges: return False return True def encode(self): contextP, payload = encode_context(self.bigEndian, self.context) payload += encode_searchrange_list(self.bigEndian, self.oidranges) header = encode_pduheader(self.pduType, False, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload class GetNextPDU(GetPDU): def __init__(self, bigEndian, sID, tactID, pktID, oidranges, context=None): GetPDU.__init__(self, bigEndian, sID, tactID, pktID, oidranges, context) self.pduType = PDU_GET_NEXT def decode_GetBulkPDU(data, header): flags = header["flags"] endianToken = getendian(flags["bigEndian"]) context, data = decode_context(data, header) temp, data = slicedata(data, 4) nonReps, maxReps = struct.unpack(endianToken + "HH", polybytes(temp)) oidranges = decode_searchrange_list(data, header) result = GetBulkPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], nonReps, maxReps, oidranges, context) return result class GetBulkPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, nonReps, maxReps, oidranges, context=None): AgentXPDU.__init__(self, PDU_GET_BULK, bigEndian, sID, tactID, pktID, True, context) self.nonReps = nonReps self.maxReps = maxReps self.oidranges = oidranges def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.nonReps != other.nonReps: return False if self.maxReps != other.maxReps: return False if self.oidranges != other.oidranges: return False return True def encode(self): endianToken = getendian(self.bigEndian) contextP, payload = encode_context(self.bigEndian, self.context) payload += struct.pack(endianToken + "HH", self.nonReps, self.maxReps) payload += encode_searchrange_list(self.bigEndian, self.oidranges) header = encode_pduheader(self.pduType, False, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_TestSetPDU(data, header): flags = header["flags"] context, data = decode_context(data, header) varbinds = decode_varbindlist(data, header) result = TestSetPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], varbinds, context) return result class TestSetPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, varbinds, context=None): AgentXPDU.__init__(self, PDU_TEST_SET, bigEndian, sID, tactID, pktID, True, context) self.varbinds = varbinds def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.varbinds != other.varbinds: return False return True def encode(self): contextP, payload = encode_context(self.bigEndian, self.context) payload += encode_varbindlist(self.bigEndian, self.varbinds) header = encode_pduheader(self.pduType, False, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_CommitSetPDU(data, header): flags = header["flags"] result = CommitSetPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"]) return result class CommitSetPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID): AgentXPDU.__init__(self, PDU_COMMIT_SET, bigEndian, sID, tactID, pktID) def encode(self): header = encode_pduheader(self.pduType, False, False, False, False, self.bigEndian, self.sessionID, self.transactionID, self.packetID, 0) return header def decode_UndoSetPDU(data, header): flags = header["flags"] result = UndoSetPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"]) return result class UndoSetPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID): AgentXPDU.__init__(self, PDU_UNDO_SET, bigEndian, sID, tactID, pktID) def encode(self): header = encode_pduheader(self.pduType, False, False, False, False, self.bigEndian, self.sessionID, self.transactionID, self.packetID, 0) return header def decode_CleanupSetPDU(data, header): flags = header["flags"] result = CleanupSetPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"]) return result class CleanupSetPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID): AgentXPDU.__init__(self, PDU_CLEANUP_SET, bigEndian, sID, tactID, pktID) def encode(self): header = encode_pduheader(self.pduType, False, False, False, False, self.bigEndian, self.sessionID, self.transactionID, self.packetID, 0) return header def decode_PingPDU(data, header): flags = header["flags"] context, data = decode_context(data, header) result = PingPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], context) return result class PingPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, context=None): AgentXPDU.__init__(self, PDU_PING, bigEndian, sID, tactID, pktID, True, context) def encode(self): contextP, payload = encode_context(self.bigEndian, self.context) header = encode_pduheader(self.pduType, False, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_NotifyPDU(data, header): flags = header["flags"] context, data = decode_context(data, header) varbinds = decode_varbindlist(data, header) result = NotifyPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], varbinds, context) return result class NotifyPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, varbinds, context=None): AgentXPDU.__init__(self, PDU_NOTIFY, bigEndian, sID, tactID, pktID, True, context) self.varbinds = varbinds def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.varbinds != other.varbinds: return False return True def encode(self): contextP, payload = encode_context(self.bigEndian, self.context) payload += encode_varbindlist(self.bigEndian, self.varbinds) header = encode_pduheader(self.pduType, False, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_xIndexAllocPDU(data, header): flags = header["flags"] context, data = decode_context(data, header) varbinds = decode_varbindlist(data, header) isalloc = (header["type"] == PDU_INDEX_ALLOC) pdu = IndexAllocPDU if isalloc else IndexDeallocPDU result = pdu(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], flags["newIndex"], flags["anyIndex"], varbinds, context) return result class IndexAllocPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, newIndex, anyIndex, varbinds, context=None): AgentXPDU.__init__(self, PDU_INDEX_ALLOC, bigEndian, sID, tactID, pktID, True, context) self.newIndex = newIndex self.anyIndex = anyIndex self.varbinds = varbinds def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.newIndex != other.newIndex: return False if self.anyIndex != other.anyIndex: return False if self.varbinds != other.varbinds: return False return True def encode(self): contextP, payload = encode_context(self.bigEndian, self.context) payload += encode_varbindlist(self.bigEndian, self.varbinds) header = encode_pduheader(self.pduType, False, self.newIndex, self.anyIndex, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload class IndexDeallocPDU(IndexAllocPDU): def __init__(self, bigEndian, sID, tactID, pktID, newIndex, anyIndex, varbinds, context=None): IndexAllocPDU.__init__(self, bigEndian, sID, tactID, pktID, newIndex, anyIndex, varbinds, context) self.pduType = PDU_INDEX_DEALLOC def decode_AddAgentCapsPDU(data, header): flags = header["flags"] context, data = decode_context(data, header) oid, data = decode_OID(data, header) descr = decode_octetstr(data, header)[0] result = AddAgentCapsPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], oid, descr, context) return result class AddAgentCapsPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, oid, description, context=None): AgentXPDU.__init__(self, PDU_ADD_AGENT_CAPS, bigEndian, sID, tactID, pktID, True, context) self.oid = classifyOID(oid) self.description = description def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.oid != other.oid: return False if self.description != other.description: return False return True def encode(self): contextP, payload = encode_context(self.bigEndian, self.context) payload += self.oid.encode(self.bigEndian) payload += encode_octetstr(self.bigEndian, self.description) header = encode_pduheader(self.pduType, False, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_RMAgentCapsPDU(data, header): flags = header["flags"] context, data = decode_context(data, header) oid, data = decode_OID(data, header) result = RMAgentCapsPDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], oid, context) return result class RMAgentCapsPDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, oid, context=None): AgentXPDU.__init__(self, PDU_RM_AGENT_CAPS, bigEndian, sID, tactID, pktID, True, context) self.oid = classifyOID(oid) def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.oid != other.oid: return False return True def encode(self): contextP, payload = encode_context(self.bigEndian, self.context) payload += self.oid.encode(self.bigEndian) header = encode_pduheader(self.pduType, False, False, False, contextP, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload def decode_ResponsePDU(data, header): flags = header["flags"] endianToken = getendian(flags["bigEndian"]) temp, data = slicedata(data, 8) sysUptime, resError, resIndex = struct.unpack(endianToken + "IHH", polybytes(temp)) if len(data) > 0: varbinds = decode_varbindlist(data, header) else: varbinds = None result = ResponsePDU(flags["bigEndian"], header["session_id"], header["transaction_id"], header["packet_id"], sysUptime, resError, resIndex, varbinds) return result class ResponsePDU(AgentXPDU): def __init__(self, bigEndian, sID, tactID, pktID, sysUptime, resError, resIndex, varbinds=None): AgentXPDU.__init__(self, PDU_RESPONSE, bigEndian, sID, tactID, pktID) self.sysUptime = sysUptime self.resError = resError self.resIndex = resIndex self.varbinds = varbinds def __eq__(self, other): if AgentXPDU.__eq__(self, other) is not True: return False if self.sysUptime != other.sysUptime: return False if self.resError != other.resError: return False if self.resIndex != other.resIndex: return False if self.varbinds != other.varbinds: return False return True def encode(self): endianToken = getendian(self.bigEndian) payload = struct.pack(endianToken + "IHH", self.sysUptime, self.resError, self.resIndex) if self.varbinds is not None: payload += encode_varbindlist(self.bigEndian, self.varbinds) header = encode_pduheader(self.pduType, False, False, False, False, self.bigEndian, self.sessionID, self.transactionID, self.packetID, len(payload)) return header + payload # ======================================================================== # # Data type encoders / decoders # # Encoders take data relevant to the type, return encoded string. # # Decoders take encoded string, return a tuple of (value, restOfData). # # ======================================================================== def classifyOID(oid): "Utility function to allow the user to send a bare tuple for some cases" if isinstance(oid, OID): return oid return OID(oid, False) def decode_OID(data, header): flags = header["flags"] # Need to split off the header to get the subid count header, data = slicedata(data, 4) n_subid, prefix, include = struct.unpack("BBBx", polybytes(header)) if prefix != 0: subids = internetPrefix + (prefix,) else: subids = () totalLen = len(subids) + n_subid if not (0 <= totalLen <= 128): raise ValueError("OID has too many subids") include = bool(include) # could be anything, force bool # Now have the count, need to split the subids from the rest of the packet byteCount = n_subid * 4 data, rest = slicedata(data, byteCount) endianToken = getendian(flags["bigEndian"]) formatString = endianToken + ("I" * n_subid) subids += struct.unpack(formatString, polybytes(data)) result = OID(subids, include) return (result, rest) class OID: def __init__(self, subids, include=False): self.subids = subids self.include = include self.sanity() def __eq__(self, other): if isinstance(other, OID) is False: return False if not (self.subids == other.subids): return False elif self.include != other.include: return False return True def __ne__(self, other): return not self.__eq__(other) def __lt__(self, other): return self.compareOID(other) == -1 def __le__(self, other): return self.compareOID(other) in (-1, 0) def __gt__(self, other): return self.compareOID(other) == 1 def __ge__(self, other): return self.compareOID(other) in (0, 1) def compareOID(self, other): if self.subids == other.subids: return 0 lself = len(self.subids) lother = len(other.subids) if lself > lother: x = other.subids y = self.subids lx = lother flipped = True else: x = self.subids y = other.subids lx = lself flipped = False for i in range(lx): if x[i] == y[i]: continue else: # this is the Py3 version of c = cmp(x[i], y[i]) c = (x[i] > y[i]) - (x[i] < y[i]) c = -c if flipped is True else c return c # Only reach this if shorter, and each index is equal if flipped is True: return 1 else: return -1 def __repr__(self): return "OID(%s, %s)" % (repr(self.subids), repr(self.include)) def sanity(self): if not isinstance(self.subids, (tuple, list)): raise TypeError if len(self.subids) > 128: raise ValueError def isNull(self): if (len(self.subids) == 0) and (self.include is False): return True return False def encode(self, bigEndian): subids = self.subids[:] # we may need to modify the copy numsubs = len(subids) if not (0 <= numsubs <= 128): # OIDs can have a maximum of 128 subs raise ValueError("OID has too many subids") if subids[:prefixCount] == internetPrefix: if numsubs > prefixCount: prefix = subids[prefixCount] # the number after the prefix subids = subids[prefixCount + 1:] # +1 to strip prefix arg else: # Have a prefix but nothing else, leave it as is. prefix = 0 else: prefix = 0 n_subid = len(subids) include = int(bool(self.include)) # force integer bool endianToken = getendian(bigEndian) body = struct.pack(endianToken + "BBBx", n_subid, prefix, include) for subid in subids: body += struct.pack(endianToken + "I", subid) return body def encode_octetstr(bigEndian, octets): numoctets = len(octets) endianToken = getendian(bigEndian) header = struct.pack(endianToken + "I", numoctets) pad = (numoctets % 4) if pad > 0: # Pad out the data to word boundary pad = 4 - pad pad = b"\x00" * pad if type(octets) is str: octets = polybytes(octets) data = header + octets + pad else: fmt = "B" * numoctets data = struct.pack(fmt, *octets) data = header + data + pad return data def decode_octetstr(data, header): flags = header["flags"] header, data = slicedata(data, 4) endianToken = getendian(flags["bigEndian"]) numoctets = struct.unpack(endianToken + "I", polybytes(header))[0] if len(data) < numoctets: raise ValueError("Octet string shorter than length") pad = numoctets % 4 if pad > 0: # Pad out the data to word boundary pad = 4 - pad return polystr(data[:numoctets]), data[numoctets + pad:] def sanity_octetstr(data): if isinstance(data, str): return if isinstance(data, (list, tuple)): for c in data: if not (0 <= c < 256): raise ValueError return raise TypeError def decode_Varbind(data, header): flags = header["flags"] bindheader, data = slicedata(data, 4) endianToken = getendian(flags["bigEndian"]) valType = struct.unpack(endianToken + "Hxx", polybytes(bindheader))[0] name, data = decode_OID(data, header) if valType not in definedValueTypes.keys(): raise ValueError("Value type %s not in defined types" % valType) handlers = definedValueTypes[valType] payload, data = handlers[2](data, header) result = Varbind(valType, name, payload) return result, data class Varbind: def __init__(self, vtype, oid, payload=None): self.valueType = vtype self.oid = classifyOID(oid) self.payload = payload # payload=None exists for Null types self.sanity() def __eq__(self, other): if self.valueType != other.valueType: return False if self.oid != other.oid: return False if self.payload != other.payload: return False return True def __ne__(self, other): return not self.__eq__(other) def __repr__(self): fmt = "Varbind(vtype=%s, oid=%s, payload=%s)" r = fmt % (repr(self.valueType), repr(self.oid), repr(self.payload)) return r def sanity(self): self.oid.sanity() vt = self.valueType if vt not in definedValueTypes.keys(): raise ValueError("Value type %s not in defined types" % vt) sanifyer, encoder, decoder = definedValueTypes[vt] if sanifyer is None: # it is a class self.payload.sanity() else: sanifyer(self.payload) def encode(self, bigEndian): endianToken = getendian(bigEndian) header = struct.pack(endianToken + "Hxx", self.valueType) name = self.oid.encode(bigEndian) handlers = definedValueTypes[self.valueType] sanifyer, encoder, decoder = handlers if sanifyer is None: # Classes have the .sanity() method data = header + name + self.payload.encode(bigEndian) else: # Non-classes have an associated sanity_() function data = header + name + encoder(bigEndian, self.payload) return data def encode_integer32(bigEndian, num): endianToken = getendian(bigEndian) return struct.pack(endianToken + "i", num) def decode_integer32(data, header): flags = header["flags"] endianToken = getendian(flags["bigEndian"]) num, data = slicedata(data, 4) num = struct.unpack(endianToken + "i", num)[0] return (num, data) def sanity_integer32(data): if type(data) is not int: raise TypeError def encode_unsigned32(bigEndian, num): endianToken = getendian(bigEndian) return struct.pack(endianToken + "I", num) def decode_unsigned32(data, header): flags = header["flags"] endianToken = getendian(flags["bigEndian"]) num, data = slicedata(data, 4) num = struct.unpack(endianToken + "I", num)[0] return (num, data) def sanity_unsigned32(data): # pragma: no cover if data != (data & 0xFFFFFFFF): raise ValueError def encode_nullvalue(bigEndian, data): return b"" def decode_nullvalue(data, header): return (None, data) def sanity_nullvalue(data): pass # Seriously? def encode_integer64(bigEndian, num): endianToken = getendian(bigEndian) return struct.pack(endianToken + "Q", num) def decode_integer64(data, header): flags = header["flags"] endianToken = getendian(flags["bigEndian"]) num, data = slicedata(data, 8) num = struct.unpack(endianToken + "Q", polybytes(num))[0] return (num, data) def sanity_integer64(data): if data != (data & 0xFFFFFFFFFFFFFFFF): raise ValueError def encode_ipaddr(bigEndian, octets): sanity_ipaddr(octets) return encode_octetstr(bigEndian, octets) def decode_ipaddr(data, header): addr, data = decode_octetstr(data, header) addr = struct.unpack("BBBB", polybytes(addr)) return addr, data def sanity_ipaddr(data): if len(data) not in (4, 16): raise ValueError def decode_SearchRange(data, header): startOID, data = decode_OID(data, header) endOID, data = decode_OID(data, header) result = SearchRange(startOID, endOID) return result, data class SearchRange: def __init__(self, start, end, include=None): self.start = classifyOID(start) self.end = classifyOID(end) self.end.include = False # sanify if include is not None: # if the user does not provide include it defaults to whatever # start is already set to self.start.include = include self.sanity() def __eq__(self, other): if self.start != other.start: return False if self.end != other.end: return False return True def __ne__(self, other): return not self.__eq__(other) def __repr__(self): r = "SearchRange(%s, %s)" % (repr(self.start), repr(self.end)) return r def sanity(self): self.start.sanity() self.end.sanity() if self.end.include is True: raise ValueError def encode(self, bigEndian): startOIDstr = self.start.encode(bigEndian) endOIDstr = self.end.encode(bigEndian) return startOIDstr + endOIDstr def encode_searchrange_list(bigEndian, searchranges): encoded = [] for sran in searchranges: encoded.append(sran.encode(bigEndian)) encoded = b"".join(encoded) return encoded def decode_searchrange_list(data, header): # Cannot handle extra data oidranges = [] while len(data) > 0: oids, data = decode_SearchRange(data, header) oidranges.append(oids) return tuple(oidranges) def encode_varbindlist(bigEndian, varbinds): payload = b"" for varbind in varbinds: payload += varbind.encode(bigEndian) return payload def decode_varbindlist(data, header): if len(data) > 0: varbinds = [] while len(data) > 0: vb, data = decode_Varbind(data, header) varbinds.append(vb) varbinds = tuple(varbinds) else: varbinds = None return varbinds def encode_flagbyte(flags): flagbyte = 0 flagbyte |= flags["instReg"] flagbyte |= flags["newIndex"] << 1 flagbyte |= flags["anyIndex"] << 2 flagbyte |= flags["contextP"] << 3 # nonDefaultContext flagbyte |= flags["bigEndian"] << 4 return flagbyte def decode_flagbyte(flags): flagDict = makeflags(bool(flags & 0x1), bool(flags & 0x2), bool(flags & 0x4), bool(flags & 0x8), bool(flags & 0x10)) return flagDict # ========================================= # Utilities, glue, and misc # ========================================= def makeflags(iR, nI, aI, cP, bE): return {"instReg": iR, "newIndex": nI, "anyIndex": aI, "contextP": cP, "bigEndian": bE} def getendian(bigEndian): return ">" if bigEndian is True else "<" def encode_pduheader(pduType, instanceRegistration, newIndex, anyIndex, nonDefaultContext, bigEndian, sessionID, transactionID, packetID, payloadLength): # version type flags reserved # sessionID # transactionID # packetID # payload_length if pduType not in definedPDUTypes: raise ValueError("PDU type %s not in defined types" % pduType) flags = encode_flagbyte(makeflags(instanceRegistration, newIndex, anyIndex, nonDefaultContext, bigEndian)) # Yes, this is a kluge, it is less problematic then the next best kluge endianToken = getendian(bigEndian) data = struct.pack(endianToken + "BBBxIIII", 1, pduType, flags, sessionID, transactionID, packetID, payloadLength) return data def decode_pduheader(data): # Endianness is controlled from the PDU header lineone, data = slicedata(data, 4) version, pduType, flags = struct.unpack(">BBBx", polybytes(lineone)) # Slice up the flags flagDict = decode_flagbyte(flags) # Chop the remaining parts of the header from the rest of the datastream # then parse them fmt = getendian(flagDict["bigEndian"]) + "IIII" linen, data = slicedata(data, 16) # 4 x 4-byte variables sID, tactionID, pktID, dataLen = struct.unpack(fmt, polybytes(linen)) result = {"version": version, "type": pduType, "flags": flagDict, "session_id": sID, "transaction_id": tactionID, "packet_id": pktID, "length": dataLen} return result def encode_context(bigEndian, context): if context is not None: contextP = True payload = encode_octetstr(bigEndian, context) else: contextP = False payload = b"" return (contextP, payload) def decode_context(data, header): flags = header["flags"] if flags["contextP"] is True: context, data = decode_octetstr(data, header) else: context = None return (context, data) def decode_packet(data): if len(data) < 20: raise ParseDataLengthError("Data too short for header") header, newData = slicedata(data, 20) header = decode_pduheader(header) if header["length"] > len(newData): raise ParseDataLengthError("Packet data too short", header) packetSlice, newData = slicedata(newData, header["length"]) if header["version"] != 1: raise ParseVersionError("Unknown packet version %i" % header["version"], header, packetSlice, newData) pktType = header["type"] if pktType not in definedPDUTypes.keys(): raise ParsePDUTypeError("PDU type %s not in defined types" % pktType, header, packetSlice, newData) decoder = definedPDUTypes[pktType] try: parsedPkt = decoder(packetSlice, header) except Exception: # pragma: no cover err = ParseError("Body parsing error", header, packetSlice, newData) raise err return parsedPkt, newData def walkMIBTree(tree, rootpath=()): # Tree node formats: # {"reader": , "writer": , "subids": {.blah.}} # {"reader": , "writer": , "subids": } # The "subids" function in dynamic nodes must return an MIB tree nodeStack = [] oidStack = [] current = tree currentKeys = list(current.keys()) currentKeys.sort() keyID = 0 while True: if keyID >= len(currentKeys): if len(nodeStack) > 0: # No more nodes this level, pop higher node current, currentKeys, keyID, key = nodeStack.pop() oidStack.pop() keyID += 1 continue else: # Out of tree, we are done return key = currentKeys[keyID] oid = OID(rootpath + tuple(oidStack) + (key,)) yield (oid, current[key].get("reader"), current[key].get("writer")) subs = current[key].get("subids") if subs is not None: # Push current node, move down a level nodeStack.append((current, currentKeys, keyID, key)) oidStack.append(key) if isinstance(subs, dict) is True: current = subs else: current = subs() # Tree generator function if current == {}: # no dynamic subids, pop current, currentKeys, keyID, key = nodeStack.pop() oidStack.pop() keyID += 1 continue currentKeys = list(current.keys()) currentKeys.sort() keyID = 0 key = currentKeys[keyID] continue keyID += 1 def bits2Bools(bitString, cropLength=None): bits = [] for octet in bitString: octet = ord(octet) bits.append(bool(octet & 0x80)) # Yes, these are backwards, that is bits.append(bool(octet & 0x40)) # how SNMP wants them. It does make bits.append(bool(octet & 0x20)) # sense if you think about it as a bits.append(bool(octet & 0x10)) # stream of bits instead of octets. bits.append(bool(octet & 0x08)) bits.append(bool(octet & 0x04)) # If you don't like it go yell at bits.append(bool(octet & 0x02)) # the SNMP designers. bits.append(bool(octet & 0x01)) if cropLength is not None: # used when a bitfield is not a multiple of 8 bits = bits[:cropLength] return bits def bools2Bits(bits): bitCounter = 0 octets = [] current = 0 for bit in bits: current += (int(bit) << (7 - bitCounter)) bitCounter += 1 if bitCounter >= 8: # end of byte bitCounter = 0 octets.append(chr(current)) current = 0 else: if bitCounter != 0: octets.append(chr(current)) octets = "".join(octets) return octets # Value types VALUE_INTEGER = 2 VALUE_OCTET_STR = 4 VALUE_NULL = 5 VALUE_OID = 6 VALUE_IP_ADDR = 64 VALUE_COUNTER32 = 65 VALUE_GAUGE32 = 66 VALUE_TIME_TICKS = 67 VALUE_OPAQUE = 68 VALUE_COUNTER64 = 70 VALUE_NO_SUCH_OBJECT = 128 VALUE_NO_SUCH_INSTANCE = 129 VALUE_END_OF_MIB_VIEW = 130 # Sub-tuple format: (sanityChecker, encoder/class, decoder) # if the sanityChecker is None it means the data type is held in # a class, which has it's own .sanity() method definedValueTypes = { # Used by the varbind functions VALUE_INTEGER: (sanity_integer32, encode_integer32, decode_integer32), VALUE_OCTET_STR: (sanity_octetstr, encode_octetstr, decode_octetstr), VALUE_NULL: (sanity_nullvalue, encode_nullvalue, decode_nullvalue), VALUE_OID: (None, OID, decode_OID), VALUE_IP_ADDR: (sanity_ipaddr, encode_ipaddr, decode_ipaddr), VALUE_COUNTER32: (sanity_unsigned32, encode_unsigned32, decode_unsigned32), VALUE_GAUGE32: (sanity_unsigned32, encode_unsigned32, decode_unsigned32), VALUE_TIME_TICKS: (sanity_unsigned32, encode_unsigned32, decode_unsigned32), VALUE_OPAQUE: (sanity_octetstr, encode_octetstr, decode_octetstr), VALUE_COUNTER64: (sanity_integer64, encode_integer64, decode_integer64), VALUE_NO_SUCH_OBJECT: (sanity_nullvalue, encode_nullvalue, decode_nullvalue), VALUE_NO_SUCH_INSTANCE: (sanity_nullvalue, encode_nullvalue, decode_nullvalue), VALUE_END_OF_MIB_VIEW: (sanity_nullvalue, encode_nullvalue, decode_nullvalue)} # PDU types PDU_OPEN = 1 PDU_CLOSE = 2 PDU_REGISTER = 3 PDU_UNREGISTER = 4 PDU_GET = 5 PDU_GET_NEXT = 6 PDU_GET_BULK = 7 PDU_TEST_SET = 8 PDU_COMMIT_SET = 9 PDU_UNDO_SET = 10 PDU_CLEANUP_SET = 11 PDU_NOTIFY = 12 PDU_PING = 13 PDU_INDEX_ALLOC = 14 PDU_INDEX_DEALLOC = 15 PDU_ADD_AGENT_CAPS = 16 PDU_RM_AGENT_CAPS = 17 PDU_RESPONSE = 18 definedPDUTypes = { # Used by the decode_packet function PDU_OPEN: decode_OpenPDU, PDU_CLOSE: decode_ClosePDU, PDU_REGISTER: decode_xRegisterPDU, PDU_UNREGISTER: decode_xRegisterPDU, PDU_GET: decode_xGetPDU, PDU_GET_NEXT: decode_xGetPDU, PDU_GET_BULK: decode_GetBulkPDU, PDU_TEST_SET: decode_TestSetPDU, PDU_COMMIT_SET: decode_CommitSetPDU, PDU_UNDO_SET: decode_UndoSetPDU, PDU_CLEANUP_SET: decode_CleanupSetPDU, PDU_NOTIFY: decode_NotifyPDU, PDU_PING: decode_PingPDU, PDU_INDEX_ALLOC: decode_xIndexAllocPDU, PDU_INDEX_DEALLOC: decode_xIndexAllocPDU, PDU_ADD_AGENT_CAPS: decode_AddAgentCapsPDU, PDU_RM_AGENT_CAPS: decode_RMAgentCapsPDU, PDU_RESPONSE: decode_ResponsePDU} # Closing reasons RSN_OTHER = 1 RSN_PARSE_ERROR = 2 RSN_PROTOCOL_ERROR = 3 RSN_TIMEOUT = 4 RSN_SHUTDOWN = 5 RSN_BY_MANAGER = 6 definedReasons = (RSN_OTHER, RSN_PARSE_ERROR, RSN_PROTOCOL_ERROR, RSN_TIMEOUT, RSN_SHUTDOWN, RSN_BY_MANAGER) # Error reasons ERR_NOERROR = 0 ERR_GENERR = 5 ERR_NO_ACCESS = 6 ERR_WRONG_TYPE = 7 ERR_WRONG_LEN = 8 ERR_WRONG_ENCODING = 9 ERR_WRONG_VALUE = 10 ERR_NO_CREATION = 11 ERR_INCONSISTENT_VALUE = 12 ERR_RESOURCE_UNAVAILABLE = 13 ERR_COMMIT_FAILED = 14 ERR_UNDO_FAILED = 15 ERR_NOT_WRITABLE = 17 ERR_INCONSISTENT_NAME = 18 definedErrors = (ERR_NOERROR, ERR_GENERR, ERR_NO_ACCESS, ERR_WRONG_TYPE, ERR_WRONG_LEN, ERR_WRONG_ENCODING, ERR_WRONG_VALUE, ERR_NO_CREATION, ERR_INCONSISTENT_VALUE, ERR_RESOURCE_UNAVAILABLE, ERR_NOT_WRITABLE, ERR_INCONSISTENT_NAME) RSPERR_NO_AGENTX = 0 RSPERR_OPEN_FAILED = 265 RSPERR_NOT_OPEN = 257 RSPERR_INDEX_WRONG_TYPE = 258 RSPERR_INDEX_ALREADY_ALLOCATED = 259 RSPERR_INDEX_NONE_AVAILABLE = 260 RSPERR_INDEX_NOT_ALLOCATED = 261 RSPERR_UNSUPPORTED_CONTEXT = 262 RSPERR_DUPLICATE_REGISTRATION = 263 RSPERR_UNKNOWN_REGISTRATION = 264 RSPERR_UNKNOWN_AGENT_CAPS = 265 RSPERR_PARSE_ERROR = 266 RSPERR_REQUEST_DENIED = 267 RSPERR_PROCESSING_ERROR = 268 responseErrors = (RSPERR_NO_AGENTX, RSPERR_OPEN_FAILED, RSPERR_NOT_OPEN, RSPERR_INDEX_WRONG_TYPE, RSPERR_INDEX_ALREADY_ALLOCATED, RSPERR_INDEX_NONE_AVAILABLE, RSPERR_INDEX_NOT_ALLOCATED, RSPERR_UNSUPPORTED_CONTEXT, RSPERR_DUPLICATE_REGISTRATION, RSPERR_UNKNOWN_REGISTRATION, RSPERR_UNKNOWN_AGENT_CAPS, RSPERR_PARSE_ERROR, RSPERR_REQUEST_DENIED, RSPERR_PROCESSING_ERROR)