chiark / gitweb /
Merge subdirmk 0.3
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 30 Dec 2019 13:14:45 +0000 (13:14 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 30 Dec 2019 13:14:45 +0000 (13:14 +0000)
189 files changed:
.dir-locals.el [new file with mode: 0644]
.gitignore
BUGS [new file with mode: 0644]
COPYING [new file with mode: 0644]
CREDITS [new file with mode: 0644]
DEVELOPER-CERTIFICATE [changed from file to symlink]
Dir.sd.mk [new file with mode: 0644]
INSTALL [new file with mode: 0644]
NEWS [new file with mode: 0644]
NOTES [new file with mode: 0644]
README
README.mac [new file with mode: 0644]
README.make-secnet-sites [new file with mode: 0644]
Suffix.sd.mk [new file with mode: 0644]
TODO [new file with mode: 0644]
ac_prog_cc_no_writeable_strings.m4 [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
aes.c [new file with mode: 0644]
aes.h [new file with mode: 0644]
argparseactionnoyes.py [new file with mode: 0644]
autogen.sh
comm-common.c [new file with mode: 0644]
comm-common.h [new file with mode: 0644]
common.make.in [new file with mode: 0644]
comprehensive-test [new file with mode: 0755]
conffile.c [new file with mode: 0644]
conffile.fl [new file with mode: 0644]
conffile.h [new file with mode: 0644]
conffile.y [new file with mode: 0644]
conffile_internal.h [new file with mode: 0644]
config.h.in [new file with mode: 0644]
configure [new file with mode: 0755]
configure.ac [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/default [new file with mode: 0644]
debian/init [new file with mode: 0644]
debian/rules [new file with mode: 0755]
dh.c [new file with mode: 0644]
eax-aes-test.c [new file with mode: 0644]
eax-aes-test.vectors [new file with mode: 0644]
eax-serpent-test.c [new file with mode: 0644]
eax-serpent-test.vectors [new file with mode: 0644]
eax-serpentbe-test.c [new file with mode: 0644]
eax-serpentbe-test.vectors [new file with mode: 0644]
eax-test.c [new file with mode: 0644]
eax-test.h [new file with mode: 0644]
eax.c [new file with mode: 0644]
example.conf [new file with mode: 0644]
hackypar.c [new file with mode: 0644]
hackypar.h [new file with mode: 0644]
hexdebug.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
ipaddr.c [new file with mode: 0644]
ipaddr.h [new file with mode: 0644]
ipaddrset-test.expected [new file with mode: 0644]
ipaddrset-test.py [new file with mode: 0755]
ipaddrset.py [new file with mode: 0644]
log.c [new file with mode: 0644]
magic.h [new file with mode: 0644]
make-secnet-sites [new file with mode: 0755]
md5.c [new file with mode: 0644]
md5.h [new file with mode: 0644]
modules.c [new file with mode: 0644]
msgcode-test.c [new file with mode: 0644]
mtest/Dir.sd.mk [new file with mode: 0644]
mtest/Ginside.sites [new file with mode: 0644]
mtest/Goutside.sites [new file with mode: 0644]
mtest/common.tcl [new file with mode: 0644]
mtest/delegations.sites [new file with mode: 0644]
mtest/e-basic.conf [new file with mode: 0644]
mtest/e-userv.sites [new file with mode: 0644]
mtest/header.sites [new file with mode: 0644]
mtest/t-basic [new file with mode: 0755]
mtest/t-prefix [new file with mode: 0755]
mtest/t-userv [new file with mode: 0755]
netlink.c [new file with mode: 0644]
netlink.h [new file with mode: 0644]
polypath-interface-monitor-linux [new file with mode: 0755]
polypath.c [new file with mode: 0644]
pretest-to-tested [new file with mode: 0755]
process.c [new file with mode: 0644]
process.h [new file with mode: 0644]
random.c [new file with mode: 0644]
resolver.c [new file with mode: 0644]
rsa.c [new file with mode: 0644]
secnet-wireshark.lua [new file with mode: 0644]
secnet.8 [new file with mode: 0644]
secnet.c [new file with mode: 0644]
secnet.h [new file with mode: 0644]
serpent.c [new file with mode: 0644]
serpent.h [new file with mode: 0644]
serpentbe.c [new file with mode: 0644]
serpentsboxes.h [new file with mode: 0644]
setup.mac [new file with mode: 0755]
sha1.c [new file with mode: 0644]
sha512.c [new file with mode: 0644]
sha512.h [new file with mode: 0644]
site.c [new file with mode: 0644]
slip.c [new file with mode: 0644]
stamp-h.in [new file with mode: 0644]
stest/Dir.sd.mk [new file with mode: 0644]
stest/common.tcl [new file with mode: 0644]
stest/t-basic-kex [new file with mode: 0755]
stest/t-dyni-kex [new file with mode: 0755]
stest/udp-preload.c [new file with mode: 0644]
subdirmk/.gitignore [new file with mode: 0644]
subdirmk/DEVELOPER-CERTIFICATE [new file with mode: 0644]
subdirmk/LGPL-2 [moved from LGPL-2 with 100% similarity]
subdirmk/README [new file with mode: 0644]
subdirmk/autogen.sh [new file with mode: 0755]
subdirmk/cdeps.sd.mk [moved from cdeps.sd.mk with 100% similarity]
subdirmk/clean.sd.mk [moved from clean.sd.mk with 100% similarity]
subdirmk/example/.gitignore [moved from example/.gitignore with 100% similarity]
subdirmk/example/DEVELOPER-CERTIFICATE [moved from example/DEVELOPER-CERTIFICATE with 100% similarity]
subdirmk/example/Dir.sd.mk [moved from example/Dir.sd.mk with 100% similarity]
subdirmk/example/Final.sd.mk [moved from example/Final.sd.mk with 100% similarity]
subdirmk/example/LGPL-2 [moved from example/LGPL-2 with 100% similarity]
subdirmk/example/Suffix.sd.mk [moved from example/Suffix.sd.mk with 100% similarity]
subdirmk/example/autogen.sh [moved from example/autogen.sh with 100% similarity]
subdirmk/example/configure.ac [moved from example/configure.ac with 100% similarity]
subdirmk/example/lib/Dir.sd.mk [moved from example/lib/Dir.sd.mk with 100% similarity]
subdirmk/example/lib/t/Dir.sd.mk [moved from example/lib/t/Dir.sd.mk with 100% similarity]
subdirmk/example/lib/t/toytest.c [moved from example/lib/t/toytest.c with 100% similarity]
subdirmk/example/lib/toylib.c [moved from example/lib/toylib.c with 100% similarity]
subdirmk/example/lib/toylib.h [moved from example/lib/toylib.h with 100% similarity]
subdirmk/example/src/Dir.sd.mk [moved from example/src/Dir.sd.mk with 100% similarity]
subdirmk/example/src/toy.c [moved from example/src/toy.c with 100% similarity]
subdirmk/example/subdirmk [moved from example/subdirmk with 100% similarity]
subdirmk/generate [moved from generate with 100% similarity]
subdirmk/regen.mk.in [moved from regen.mk.in with 100% similarity]
subdirmk/subdirmk.ac [moved from subdirmk.ac with 100% similarity]
subdirmk/tests/.gitignore [moved from tests/.gitignore with 100% similarity]
subdirmk/tests/advance-tested [moved from tests/advance-tested with 100% similarity]
subdirmk/tests/build-common [moved from tests/build-common with 100% similarity]
subdirmk/tests/check [moved from tests/check with 100% similarity]
subdirmk/tests/example/.gitignore [moved from tests/example/.gitignore with 100% similarity]
subdirmk/tests/example/check [moved from tests/example/check with 100% similarity]
subdirmk/tests/filter/.gitignore [moved from tests/filter/.gitignore with 100% similarity]
subdirmk/tests/filter/Dir.mk.expected [moved from tests/filter/Dir.mk.expected with 100% similarity]
subdirmk/tests/filter/Dir.sd.mk [moved from tests/filter/Dir.sd.mk with 100% similarity]
subdirmk/tests/filter/Final.mk.expected [moved from tests/filter/Final.mk.expected with 100% similarity]
subdirmk/tests/filter/Final.sd.mk [moved from tests/filter/Final.sd.mk with 100% similarity]
subdirmk/tests/filter/Prefix.sd.mk [moved from tests/filter/Prefix.sd.mk with 100% similarity]
subdirmk/tests/filter/Suffix.sd.mk [moved from tests/filter/Suffix.sd.mk with 100% similarity]
subdirmk/tests/filter/check [moved from tests/filter/check with 100% similarity]
subdirmk/tests/filter/extract-doctests [moved from tests/filter/extract-doctests with 100% similarity]
subdirmk/tests/filter/main.mk.expected [moved from tests/filter/main.mk.expected with 100% similarity]
subdirmk/tests/filter/stderr.expected [moved from tests/filter/stderr.expected with 100% similarity]
subdirmk/tests/filter/sub/Dir.mk.expected [moved from tests/filter/sub/Dir.mk.expected with 100% similarity]
subdirmk/tests/filter/sub/dir/Dir.mk.expected [moved from tests/filter/sub/dir/Dir.mk.expected with 100% similarity]
subdirmk/tests/filter/sub/dir/Dir.sd.mk [moved from tests/filter/sub/dir/Dir.sd.mk with 100% similarity]
subdirmk/tests/filter/update-expected [moved from tests/filter/update-expected with 100% similarity]
subdirmk/tests/intree/.gitignore [moved from tests/intree/.gitignore with 100% similarity]
subdirmk/tests/intree/check [moved from tests/intree/check with 100% similarity]
subdirmk/tests/make-release [moved from tests/make-release with 100% similarity]
subdirmk/tests/tests.mk [moved from tests/tests.mk with 100% similarity]
subdirmk/usual.mk.in [moved from usual.mk.in with 100% similarity]
test-common.sd.mk [new file with mode: 0644]
test-common.tcl [new file with mode: 0644]
test-example/Dir.sd.mk [new file with mode: 0644]
test-example/README [new file with mode: 0644]
test-example/bogus-setup-request.c [new file with mode: 0644]
test-example/common.conf [new file with mode: 0644]
test-example/fake-userv [new file with mode: 0755]
test-example/inside-polypath.conf [new file with mode: 0644]
test-example/inside.conf [new file with mode: 0644]
test-example/inside.key.b64 [new file with mode: 0644]
test-example/inside.key.pub [new file with mode: 0644]
test-example/memcheck.suppressions [new file with mode: 0644]
test-example/outside-random.conf [new file with mode: 0644]
test-example/outside-unshare.conf [new file with mode: 0644]
test-example/outside.conf [new file with mode: 0644]
test-example/outside.key.b64 [new file with mode: 0644]
test-example/outside.key.pub [new file with mode: 0644]
test-example/random-fake-userv [new file with mode: 0755]
test-example/sites [new file with mode: 0644]
transform-cbcmac.c [new file with mode: 0644]
transform-common.h [new file with mode: 0644]
transform-eax.c [new file with mode: 0644]
tun.c [new file with mode: 0644]
u64.h [new file with mode: 0644]
udp.c [new file with mode: 0644]
uk.org.greenend.secnet.plist [new file with mode: 0644]
unaligned.h [new file with mode: 0644]
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644 (file)
index 0000000..7ca463d
--- /dev/null
@@ -0,0 +1,8 @@
+;;; Directory Local Variables
+;;; See Info node `(emacs) Directory Variables' for more information.
+
+((c-mode
+  (c-basic-offset . 4))
+ (python-mode
+  (indent-tabs-mode . t)
+  (python-indent . 8)))
index 0b5bd469b5e8429736bde3106e8267878b86cb7a..31d92445fa85cf0d44b6465c2f4cffcf4b6ccced 100644 (file)
@@ -1,9 +1,50 @@
-# subdirmk - subdirmk/.gitignore
-#  Copyright 2019 Mark Wooding
-#  Copyright 2019 Ian Jackson
-# SPDX-License-Identifier: LGPL-2.0-or-later
-# There is NO WARRANTY.
-
-#----- subdirmk-generated -----
-/regen.mk
-/usual.mk
+*.o
+.*.d
+*.pyc
+conffile.tab.[ch]
+conffile.yy.[ch]
+/version.c
+/secnet
+/eax-*-test
+/eax-*-test.confirm
+/ipaddrset-test.new
+/ipaddrset.confirm
+
+/config.log
+/config.h
+/config.status
+/config.stamp
+/config.stamp.in
+Makefile
+/common.make
+/test-common.make
+
+msgcode-test
+msgcode-test.confirm
+
+autom4te.cache
+
+*~
+*.tmp
+TAGS
+
+.makefiles.stamp
+Dir.mk
+/main.mk
+/Final.mk
+
+debian/files
+debian/secnet.debhelper.log
+debian/*.debhelper
+debian/secnet
+debian/secnet.substvars
+*.xcodeproj
+/build
+
+test-example/*.key
+test-example/sites.conf
+test-example/bogus-setup-request
+build-stamp
+
+[sm]test/d-*
+stest/udp-preload.so
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..02a2de3
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,11 @@
+Known bugs in secnet
+
+(Complaints from Ian:)
+Your init.d script makes it hard to start secnet as non-root, too.
+
+secnet -jv has printed a large routing table full of stuff I wasn't
+interested in.
+
+Make explicit in the documentation that -n causes all log output to go
+to stderr.  Provide an option that is _really_ just "don't fork()" for
+people who want to run secnet from init.
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    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.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/CREDITS b/CREDITS
new file mode 100644 (file)
index 0000000..6cb5490
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,17 @@
+Stephen Early <steve@greenend.org.uk> - original author
+Ian Jackson <ijackson@chiark.greenend.org.uk> - current maintainer
+Mark Wooding <mdw@distorted.org.uk> - much useful stuff
+Ross Anderson, Eli Biham, Lars Knudsen - serpent
+Colin Plumb, Ian Jackson - MD5 implementation
+Steve Reid, James H. Brown, Saul Kravitz - SHA1 implementation
+Vincent Rijmen, Antoon Bosselaers, Paulo Barreto - Rijndael (AES) implementation
+Guido Draheim - ac_prog_cc_no_writeable_strings.m4
+Free Software Foundation and Scott G. Miller - SHA-512 implementation
+Free Software Foundation and Paul Eggert - u64.h
+Massachusetts Institute of Technology - install-sh
+
+Simon Tatham, Jonathan Amery, Ian Jackson - testing and debugging
+Simon Tatham - RSA signatures using Chinese Remainder Theorem
+Simon Tatham - endianness cleanups in transform.c
+Richard Kettlewell, Matthew Vernon, Peter Benie - assorted bugfixes
+"Omnifarious" and "btel" on Stackoverflow - python yes/no arg parsing
deleted file mode 100644 (file)
index 912d22ee9efecf5fb2a9cc972765a0efb23f82d3..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,38 +0,0 @@
-Developer Certificate of Origin
-Version 1.1
-
-Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
-1 Letterman Drive
-Suite D4700
-San Francisco, CA, 94129
-
-Everyone is permitted to copy and distribute verbatim copies of this
-license document, but changing it is not allowed.
-
-
-Developer's Certificate of Origin 1.1
-
-By making a contribution to this project, I certify that:
-
-(a) The contribution was created in whole or in part by me and I
-    have the right to submit it under the open source license
-    indicated in the file; or
-
-(b) The contribution is based upon previous work that, to the best
-    of my knowledge, is covered under an appropriate open source
-    license and I have the right under that license to submit that
-    work with modifications, whether created in whole or in part
-    by me, under the same open source license (unless I am
-    permitted to submit under a different license), as indicated
-    in the file; or
-
-(c) The contribution was provided directly to me by some other
-    person who certified (a), (b) or (c) and I have not modified
-    it.
-
-(d) I understand and agree that this project and the contribution
-    are public and that a record of the contribution (including all
-    personal information I submit with it, including my sign-off) is
-    maintained indefinitely and may be redistributed consistent with
-    this project or the open source license(s) involved.
-
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..d56384eea50f013b7ea783ea1e3537e6bde40fc9
--- /dev/null
@@ -0,0 +1 @@
+subdirmk/DEVELOPER-CERTIFICATE
\ No newline at end of file
diff --git a/Dir.sd.mk b/Dir.sd.mk
new file mode 100644 (file)
index 0000000..4111476
--- /dev/null
+++ b/Dir.sd.mk
@@ -0,0 +1,258 @@
+# Makefile for secnet
+#
+# This file is part of secnet.
+# See README for full list of copyright holders.
+#
+# secnet is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# secnet 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.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# version 3 along with secnet; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+
+.PHONY:        all clean realclean distclean dist install
+
+PACKAGE:=secnet
+VERSION=0.5.1
+
+VPATH:=@srcdir@
+srcdir:=@srcdir@
+include common.make
+
+INSTALL:=@INSTALL@
+INSTALL_PROGRAM:=@INSTALL_PROGRAM@
+INSTALL_SCRIPT:=@INSTALL_SCRIPT@
+INSTALL_DATA:=@INSTALL_DATA@
+
+prefix:=$(DESTDIR)@prefix@
+exec_prefix:=@exec_prefix@
+sbindir:=@sbindir@
+sysconfdir:=$(DESTDIR)@sysconfdir@
+datarootdir:=@datarootdir@
+transform:=@program_transform_name@
+mandir:=@mandir@
+
+ALL_CFLAGS:=@DEFS@ -I$(srcdir) -I. $(CFLAGS) $(EXTRA_CFLAGS)
+CPPFLAGS:=@CPPFLAGS@ -DDATAROOTDIR='"$(datarootdir)"' $(EXTRA_CPPFLAGS)
+LDFLAGS:=@LDFLAGS@ $(EXTRA_LDFLAGS)
+LDLIBS:=@LIBS@ $(EXTRA_LDLIBS)
+
+&:local+global OBJECTS TARGETS
+
+TARGETS:=secnet
+
+OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \
+       resolver.o random.o udp.o site.o transform-cbcmac.o transform-eax.o \
+       comm-common.o polypath.o \
+       netlink.o rsa.o dh.o serpent.o serpentbe.o \
+       md5.o sha512.o tun.o slip.o sha1.o ipaddr.o log.o \
+       process.o @LIBOBJS@ \
+       hackypar.o
+# version.o is handled specially below and in the link rule for secnet.
+
+PYMODULES := ipaddrset.py argparseactionnoyes.py
+
+TEST_OBJECTS:=eax-aes-test.o eax-serpent-test.o eax-serpentbe-test.o \
+               eax-test.o aes.o
+
+ifeq (version.o,$(MAKECMDGOALS))
+OBJECTS:=
+TEST_OBJECTS:=
+endif
+
+&OBJECTS += $(OBJECTS) $(TEST_OBJECTS)
+
+STALE_PYTHON_FILES=    $(foreach e, py pyc, \
+                       $(foreach p, /usr /usr/local, \
+                       $(foreach l, ipaddr, \
+                       $(DESTDIR)$p/share/secnet/$l.$e \
+                       )))
+
+%.c:   %.y
+
+%.yy.c:        %.fl
+       flex --header=$*.yy.h -o$@ $<
+
+%.tab.c %.tab.h:       %.y
+       bison -d -o $@ $<
+
+%.o: %.c conffile.yy.h
+       $(CC) $(CPPFLAGS) $(ALL_CFLAGS) $(CDEPS_CFLAGS) -c $< -o $@
+
+all::  $(TARGETS)
+
+${srcdir}/config.h.in: configure.ac
+       cd ${srcdir} && autoheader
+       touch $@
+
+MAKEFILE_TEMPLATES += config.h.in
+CONFIG_STATUS_OUTPUTS += config.h
+
+# Manual dependencies section
+conffile.yy.c: conffile.fl conffile.tab.c
+conffile.yy.h: conffile.yy.c
+conffile.tab.c:        conffile.y
+# End of manual dependencies section
+
+conffile.yy.o: ALL_CFLAGS += -Wno-sign-compare
+
+secnet:        $(OBJECTS)
+       $(MAKE) -f main.mk version.o # *.o $(filter-out %.o, $^)
+       $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $(OBJECTS) version.o $(LDLIBS)
+# We (always) regenerate the version, but only if we regenerate the
+# binary.  (This is necessary as the version string is can depend on
+# any of the source files, eg to see whether "+" is needed.)
+
+ifneq (,$(wildcard .git/HEAD))
+# If we have (eg) committed, relink and thus regenerate the version
+# with the new info from git describe.
+secnet: Makefile .git/HEAD $(wildcard $(shell sed -n 's#^ref: #.git/#p' .git/HEAD))
+secnet: $(wildcard .git/packed-refs)
+endif
+
+TESTDIRS=stest mtest
+
+&TARGETS_check = eax-aes-test.confirm eax-serpent-test.confirm \
+       eax-serpentbe-test.confirm ipaddrset.confirm
+
+&TARGETS_fullcheck += $(&TARGETS_check)
+&TARGETS_fullcheck += msgcode-test.confirm
+
+recheck:
+       rm -f $(&TARGETS_check)
+       rm -rf $(addsuffix /d-*, $(TESTDIRS))
+       $(MAKE) -f main.mk check
+
+.PHONY: FORCE
+version.c: FORCE
+       echo "#include \"secnet.h\"" >$@.new
+       @set -ex; if test -e .git && type -p git >/dev/null; then \
+               v=$$(git describe --match 'v*'); v=$${v#v}; \
+               if ! git diff --quiet HEAD; then v="$$v+"; fi; \
+       else \
+               v="$(VERSION)"; \
+       fi; \
+       echo "char version[]=\"secnet $$v\";" >>$@.new
+       mv -f $@.new $@
+
+eax-%-test: eax-%-test.o eax-test.o %.o
+       $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $^
+
+eax-%-test.confirm: eax-%-test eax-%-test.vectors
+       ./$< <$(srcdir)/eax-$*-test.vectors >$@.new
+       mv -f $@.new $@
+
+&CDEPS_OBJECTS += msgcode-test.o
+
+msgcode-test: msgcode-test.o
+       $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $^
+
+msgcode-test.confirm: msgcode-test
+       ./msgcode-test
+       touch $@
+
+ipaddrset.confirm: ipaddrset-test.py ipaddrset.py ipaddrset-test.expected
+       $(srcdir)/ipaddrset-test.py >ipaddrset-test.new
+       diff -u $(srcdir)/ipaddrset-test.expected ipaddrset-test.new
+       touch $@
+
+.PRECIOUS: eax-%-test
+
+installdirs:
+       $(INSTALL) -d $(prefix)/share/secnet $(sbindir)
+       $(INSTALL) -d $(mandir)/man8
+       $(INSTALL) -d $(datarootdir)/secnet
+
+install: installdirs
+       set -e; ok=true; for f in $(STALE_PYTHON_FILES); do \
+               if test -e $$f; then \
+                       echo >&\&2 "ERROR: $$f still exists "\
+                               "- try \`make install-force'"; \
+                       ok=false; \
+               fi; \
+       done; \
+       $$ok
+       $(INSTALL_PROGRAM) secnet $(sbindir)/`echo secnet|sed '$(transform)'`
+       $(INSTALL_PROGRAM) ${srcdir}/make-secnet-sites $(sbindir)/`echo make-secnet-sites|sed '$(transform)'`
+       set -e; for m in $(PYMODULES); do \
+               $(INSTALL_DATA) ${srcdir}/$$m $(prefix)/share/secnet/$$m; \
+               done
+       $(INSTALL_SCRIPT) ${srcdir}/polypath-interface-monitor-linux \
+               $(datarootdir)/secnet/.
+       $(INSTALL_DATA) ${srcdir}/secnet.8 $(mandir)/man8/secnet.8
+
+install-force:
+       rm -f $(STALE_PYTHON_FILES)
+       $(MAKE) -f main.mk install
+
+&CLEAN += .version.d
+&CLEAN += $(TARGETS) $(&TARGETS_check) $(&TARGETS_fullcheck)
+
+clean::
+       $(RM) -f *.o *.yy.[ch] *.tab.[ch] core version.c
+       $(RM) -f *.pyc *~ eax-*-test.confirm eax-*-test
+       $(RM) -rf __pycache__
+       $(RM) -f msgcode-test.confirm msgcode-test
+
+realclean::    clean
+       $(RM) -f *~ Makefile config.h \
+       config.log config.status config.cache \
+       config.stamp Makefile.bak
+
+distclean::    realclean
+
+include subdirmk/regen.mk
+
+&:warn !single-char-var
+# Release checklist:
+#
+#  0. Use this checklist from Dir.sd.mk
+#
+#  1. Check that the tree has what you want
+#
+#  2. Update changelog:
+#         gbp dch --since=<PREVIOUS VERSION>
+#     and then edit debian/changelog.
+#
+#  3. Update VERSION (in this file, above) and
+#     finalise debian/changelog (removing ~ from version) and commit.
+#
+#  4. Build source and binaries:
+#       dgit -wgf sbuild -A -c stretch -j8
+#
+#  5. dpkg -i on zealot just to check
+#       dpkg -i ~ian/things/Fvpn/bpd/secnet_${VERSION}_amd64.deb
+#
+#  6. run it on chiark
+#     check we can still ping davenant and chiark
+#
+#  7. Make git tag and source tarball signature:
+#       git-tag -u general -m "secnet $VERSION" -s v${VERSION//\~/_}
+#       gpg -u general --detach-sign ../bpd/secnet_$VERSION.tar.gz
+#
+#  8. Publish the branch and distriubtion files:
+#       git-push origin v${VERSION//\~/_} v${VERSION//\~/_}~0:master
+#       dcmd rsync -v ../bpd/secnet_${VERSION}_multi.changes chiark:/home/ianmdlvl/public-html/secnet/download/
+#
+#  9. Sort out html.  On chiark as user secnet:
+#       cd ~secnet/public-html/release/
+#       mkdir $VERSION
+#       cd $VERSION
+#       ln -s /home/ianmdlvl/public-html/secnet/download/secnet?$VERSION* .
+#       ln -sfn $VERSION ../current
+#
+# 10. write and post a release announcement
+#       cd ../bpd
+#       dcmd sha256sum secnet_${VERSION}_multi.changes
+#       ...
+#       gpg --clearsign ../release-announcement
+#       rsync -vP ../release-announcement.asc c:mail/d/
+#
+# 11. bump changelog version in master, to new version with ~
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..c9db174
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,188 @@
+INSTALLATION INSTRUCTIONS for SECNET
+
+USE AT YOUR OWN RISK.  THIS IS ALPHA TEST SOFTWARE.  I DO NOT
+GUARANTEE THAT THERE WILL BE PROTOCOL COMPATIBILITY BETWEEN DIFFERENT
+VERSIONS.
+
+* Preparation
+
+** System software support
+
+Ensure that you have libgmp3-dev and adns installed (and bison and
+flex, and for that matter gcc...).
+
+[On BSD install /usr/ports/devel/bison]
+
+If you intend to configure secnet to obtain packets from the kernel
+through userv-ipif, install and configure userv-ipif.  It is part of
+userv-utils, available from ftp.chiark.greenend.org.uk in
+/users/ian/userv
+
+If you intend to configure secnet to obtain packets from the kernel
+using the universal TUN/TAP driver, make sure it's configured in your
+kernel (it's under "network device support" in Linux-2.4) and that
+you've created the appropriate device files; see
+linux/Documentation/networking/tuntap.txt
+
+If you're using TUN/TAP on a platform other than Linux-2.4, see
+http://vtun.sourceforge.net/tun/
+
+You will probably be using the supplied `make-secnet-sites' program to
+generate your VPN's list of sites as a secnet configuration from a
+more-human-writeable form.
+
+** System and network configuration
+
+If you intend to start secnet as root, I suggest you create a userid
+for it to run as once it's ready to drop its privileges.  Example (on
+Debian):
+# adduser --system --no-create-home secnet
+
+If you're using the 'soft routes' feature (for some classes of mobile
+device) you'll have to run as root all the time, to enable secnet to
+add and remove routes from your kernel's routing table.  (This
+restriction may be relaxed later if someone writes a userv service to
+modify the routing table.)
+
+If you are joining an existing VPN, read that VPN's documentation now.
+It may supersede the next paragraph.
+
+In most configurations, you will need to allocate two IP addresses for
+use by secnet.  One will be for the tunnel interface on your tunnel
+endpoint machine (i.e. the address you see in 'ifconfig' when you look
+at the tunnel interface).  The other will be for secnet itself.  These
+addresses should probably be allocated from the range used by your
+internal network: if you do this, you should provide appropriate
+proxy-ARP on the internal network interface of the machine running
+secnet (eg. add an entry net/ipv4/conf/eth_whatever/proxy_arp = 1 to
+/etc/sysctl.conf on Debian systems and run sysctl -p).  Alternatively
+the addresses could be from some other range - this works well if the
+machine running secnet is the default route out of your network - but
+this requires more thought.
+
+http://www.ucam.org/cam-grin/ may be useful.
+
+* Installation
+
+If you installed the Debian package of secnet, skip to "If installing
+for the first time", below, and note that example.conf can be found in
+/usr/share/doc/secnet/examples.
+
+To install secnet do
+
+$ ./configure
+$ make
+# make install
+# mkdir /etc/secnet
+
+(Note: you may see the following warning while compiling
+conffile.tab.c; this is a bug in bison-1.28:
+/usr/share/bison/bison.simple: In function `yyparse':
+/usr/share/bison/bison.simple:285: warning: `yyval' might be used
+ uninitialized in this function
+
+You may if you wish apply the following patch to bison.simple:
+diff -pu -r1.28.0.1 -r1.28.0.3
+--- bison.s1    1999/08/30 19:23:24     1.28.0.1
++++ bison.s1    1999/08/30 21:15:18     1.28.0.3
+@@ -523,8 +523,14 @@ yydefault:
+ /* Do a reduction.  yyn is the number of a rule to reduce with.  */
+ yyreduce:
+   yylen = yyr2[yyn];
+-  if (yylen > 0)
+-    yyval = yyvsp[1-yylen]; /* implement default value of the action */
++
++  /* If yylen is nonzero, implement the default value of the action.
++     Otherwise, the following line sets yyval to the semantic value of
++     the lookahead token.  This behavior is undocumented and bison
++     users should not rely upon it.  Assigning to yyval
++     unconditionally makes the parser a bit smaller, and it avoids a
++     GCC warning that yyval may be used uninitialized.  */
++  yyval = yyvsp[1-yylen];
+ #if YYDEBUG != 0
+   if (yydebug)
+)
+
+Any other warnings or errors should be reported to
+steve@greenend.org.uk.
+
+If installing for the first time, do
+
+# cp example.conf /etc/secnet/secnet.conf
+# cd /etc/secnet
+# ssh-keygen -f key -t rsa1 -N ""
+
+(You may need ssh-keygen1, instead, which might be found in
+openssh-client-ssh1.)
+
+[On BSD use
+$ LDFLAGS="-L/usr/local/lib" ./configure
+$ gmake CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib"
+XXX this should eventually be worked out automatically by 'configure'.]
+
+Generate a site file fragment for your site (see your VPN's
+documentation, or see below), and submit it for inclusion in your
+VPN's 'sites' file.  Download the vpn-sites file to /etc/secnet/sites
+- MAKE SURE YOU GET AN AUTHENTIC COPY because the sites file contains
+public keys for all the sites in the VPN.  Use the make-secnet-sites
+program provided with the secnet distribution to convert the
+distributed sites file into one that can be included in a secnet
+configuration file:
+
+# make-secnet-sites /etc/secnet/sites /etc/secnet/sites.conf
+
+* Configuration
+
+Should be reasonably obvious - edit /etc/secnet/secnet.conf as
+prompted by the comments in example.conf.  XXX Fuller documentation of
+the configuration file format should be forthcoming in time.  Its
+syntax is described in the README file at the moment.
+
+* Constructing your site file fragment
+
+You need the following information:
+
+1. the name of your VPN.
+
+2. the name of your location(s).
+
+3. a short name for your site, eg. "sinister".  This is used to
+identify your site in the vpn-sites file, and should probably be the
+same as its hostname.
+
+4. the DNS name of the machine that will be the "front-end" for your
+secnet installation.  This will typically be the name of the gateway
+machine for your network, eg. sinister.dynamic.greenend.org.uk
+
+secnet does not actually have to run on this machine, as long as the
+machine can be configured to forward UDP packets to the machine that
+is running secnet.
+
+5. the port number used to contact secnet at your site.  This is the
+port number on the front-end machine, and does not necessarily have to
+match the port number on the machine running secnet.  If you want to
+use a privileged port number we suggest 410.  An appropriate
+unprivileged port number is 51396.
+
+6. the list of networks accessible at your site over the VPN.
+
+7. the public part of the RSA key you generated during installation
+(in /etc/secnet/key.pub if you followed the installation
+instructions).  This file contains three numbers and a comment on one
+line.
+
+If you are running secnet on a particularly slow machine, you may like
+to specify a larger value for the key setup retry timeout than the
+default, to prevent unnecessary retransmissions of key setup packets.
+See the notes in the example configuration file for more on this.
+
+The site file fragment should look something like this:
+
+vpn sgo
+location greenend
+contact steve@greenend.org.uk
+site sinister
+  networks 192.168.73.0/24 192.168.1.0/24 172.19.71.0/24
+  address sinister.dynamic.greenend.org.uk 51396
+  pubkey 1024 35 142982503......[lots more].....0611 steve@sinister
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..8094c3e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,331 @@
+* Planned for the future
+
+Please note that the 0.1 series of secnet releases is now 'maintenance
+only'; further development continues in secnet-0.2.
+
+Debconf support - if you are using the Debian packaged version and
+your secnet configuration is autogenerated using debconf then the
+upgrade to version 0.2.0 should just involve installing the package;
+an appropriate 0.2-style configuration file will be generated
+automatically.
+
+* New in version 0.1.18
+
+ipaddr.py now declares its character encoding; required by recent
+versions of Python
+
+* New in version 0.1.17
+
+autoconf updates for cross-compilation / more modern autoconf from
+Ross Younger <ross@crazyscot.com>
+
+MacOS X support from Richard Kettlewell <richard@sfere.greenend.org.uk>
+
+Makefile fix: Update bison pattern rule to indicate that both the
+.tab.c and .tab.h files are generated by the same command.
+
+i386 ip_csum implementation updated to work with modern gcc
+
+Rename global 'log' to 'slilog' to avoid conflict with gcc built-in
+log() function.
+
+* New in version 0.1.16
+
+XXX XXX PROTOCOL COMPATIBILITY IS BROKEN BETWEEN VERSION 0.1.16 AND
+XXX XXX ALL PREVIOUS VERSIONS.
+
+Bugfix: rsa.c private-key now works properly when you choose not to
+verify it.
+
+Bugfix: serpent key setup was only using the first 8 bytes of the key
+material. (Oops!)  Ian Jackson contributed a fix so the full 32 bytes
+are used, in big-endian mode.
+
+Debatable-bugfix: RSA operations now use PKCS1 v1.5-style padding
+
+"Hacky parallelism" contributed by Ian Jackson; this permits
+public-key operations to be performed in a subprocess during key
+exchange, to make secnet more usable on very slow machines.  This is
+not compiled in by default; if you find you need it (because key
+exchanges are taking more than a second or two) then add
+-DHACKY_PARALLEL to FLAGS in the Makefile.in and recompile.
+
+udp module updates from Peter Benie:
+  1) Handle the case where authbind-helper terminates with a signal
+  2) Cope with signals being delivered during waitpid
+  3) Add 'address' (optional) to the udp settings. This is an IP address
+     that the socket will be bound to.
+  4) Change the endianess of the arguments to authbind-helper.
+     sprintf("%04X") already translates from machine repesentation to most
+     significant octet first so htons reversed it again.
+
+All uses of alloca() expunged by Peter Benie.
+
+make-secnet-sites now supports configurations where each tunnel gets
+its own interface on the host, and the IP router code in secnet is
+disabled.  make-secnet-sites has been rewritten for clarity.  For
+information on how to configure secnet for one-interface-per-tunnel,
+see the example.conf file.
+
+* New in version 0.1.15
+
+Now terminates with an error when an "include" filename is not
+specified in the configuration file (thanks to RJK).
+
+RSA private key operations optimised using CRT. Thanks to SGT.
+
+Now compiles cleanly with -Wwrite-strings turned on in gcc.
+
+Anything sent to stderr once secnet has started running in the
+background is now redirected to the system/log facility.
+
+* New in version 0.1.14
+
+The --help and --version options now send their output to stdout.
+
+Bugfix: TUN flavour "BSD" no longer implies a BSD-style ifconfig and
+route command invocation.  Instead "ioctl"-style is used, which should
+work on both BSD and linux-2.2 systems.
+
+If no "networks" parameter is specified for a netlink device then it
+is assumed to be 0.0.0.0/0 rather than the empty set.  So, by default
+there is a default route from each netlink device to the host machine.
+The "networks" parameter can be used to implement a primitive
+firewall, restricting the destination addresses of packets received
+through tunnels; if a more complex firewall is required then implement
+it on the host.
+
+* New in version 0.1.13
+
+site.c code cleaned up; no externally visible changes
+
+secnet now calls setsid() after becoming a daemon.
+
+secnet now supports TUN on Solaris 2.5 and above (and possibly other
+STREAMS-based systems as well).
+
+The TUN code now tries to auto-detect the type of "TUN" in use
+(BSD-style, Linux-style or STREAMS-style).  If your configuration file
+specifies "tun-old" then it defaults to BSD-style; however, since
+"tun-old" will be removed in a future release, you should change your
+configuration file to specify "tun" and if there's a problem also
+specify the flavour in use.
+
+Example:
+netlink tun-old {
+       ...
+};
+should be rewritten as
+netlink tun {
+       flavour "bsd";
+       ...
+};
+
+The flavours currently defined are "bsd", "linux" and "streams".
+
+The TUN code can now be configured to configure interfaces and
+add/delete routes using one of several methods: invoking a
+"linux"-style ifconfig/route command, a "bsd"-style ifconfig/route
+command, "solaris-2.5"-style ifconfig/route command or calling ioctl()
+directly.  These methods can be selected using the "ifconfig-type" and
+"route-type" options.
+
+Example:
+netlink tun {
+       ifconfig-type "ioctl";
+       route-type "ioctl";
+       ...
+};
+
+The ioctl-based method is now the default for Linux systems.
+
+Magic numbers used within secnet are now collected in the header file
+"magic.h".
+
+netlink now uses ICMP type=0 code=13 for 'administratively prohibited'
+instead of code 9. See RFC1812 section 5.2.7.1.
+
+The UDP comm module now supports a proxy server, "udpforward".  This
+runs on a machine which is directly accessible by secnet and which can
+send packets to appropriate destinations.  It's useful when the proxy
+machine doesn't support source- and destination-NAT.  The proxy server
+is specified using the "proxy" key in the UDP module configuration;
+parameters are IP address (string) and port number.
+
+Bugfix: ipset_to_subnet_list() in ipaddr.c now believed to work in all
+cases, including 0.0.0.0/0
+
+* New in version 0.1.12
+
+IMPORTANT: fix calculation of 'now' in secnet.c; necessary for correct
+operation.
+
+(Only interesting for people building and modifying secnet by hand:
+the Makefile now works out most dependencies automatically.)
+
+The netlink code no longer produces an internal routing table sorted
+by netmask length.  Instead, netlink instances have a 'priority'; the
+table of routes is sorted by priority.  Devices like laptops that have
+tunnels that must sometimes 'mask' parts of other tunnels should be
+given higher priorities.  If a priority is not specified it is assumed
+to be zero.
+
+Example usage:
+site laptop { ...
+       link netlink {
+               route "192.168.73.74/31";
+               priority 10;
+       };
+};
+
+* New in version 0.1.11
+
+Lists of IP addresses in the configuration file can now include
+exclusions as well as inclusions.  For example, you can specify all
+the hosts on a subnet except one as follows:
+
+networks "192.168.73.0/24","!192.168.73.70";
+
+(If you were only allowed inclusions, you'd have to specify that like
+this:
+networks "192.168.73.71/32","192.168.73.68/31","192.168.73.64/30",
+       "192.168.73.72/29","192.168.73.80/28","192.168.73.96/27",
+       "192.168.73.0/26","192.168.73.128/25";
+)
+
+secnet now ensures that it invokes userv-ipif with a non-overlapping
+list of subnets.
+
+There is a new command-line option, --sites-key or -s, that enables
+the configuration file key that's checked to determine the list of
+active sites (default "sites") to be changed.  This enables a single
+configuration file to contain multiple cofigurations conveniently.
+
+NAKs are now sent when packets arrive that are not understood.  The
+tunnel code initiates a key setup if it sees a NAK.  Future
+developments should include configuration options that control this.
+
+The tunnel code notifies its peer when secnet is terminating, so the
+peer can close the session.
+
+The netlink "exclude-remote-networks" option has now been replaced by
+a "remote-networks" option; instead of specifying networks that no
+site may access, you specify the set of networks that remote sites are
+allowed to access. A sensible example: "192.168.0.0/16",
+"172.16.0.0/12", "10.0.0.0/8", "!your-local-network"
+
+* New in version 0.1.10
+
+WARNING: THIS VERSION MAKES A CHANGE TO THE CONFIGURATION FILE FORMAT
+THAT IS NOT BACKWARD COMPATIBLE.  However, in most configurations the
+change only affects the sites.conf file, which is generated by the
+make-secnet-sites script; after you regenerate your sites.conf using
+version 0.1.10, everything should continue to work.
+
+Netlink devices now interact slightly differently with the 'site'
+code.  When you invoke a netlink closure like 'tun' or 'userv-ipif',
+you get another closure back.  You then invoke this closure (usually
+in the site definitions) to specify things like routes and options.
+The result of this invocation should be used as the 'link' option in
+site configurations.
+
+All this really means is that instead of site configurations looking
+like this:
+
+foo {
+       name "foo";
+       networks "a", "b", "c";
+       etc.
+};
+
+...they look like this:
+
+foo {
+       name "foo";
+       link netlink { routes "a", "b", "c"; };
+       etc.
+};
+
+This change was made to enable the 'site' code to be completely free
+of any knowledge of the contents of the packets it transmits.  It
+should now be possible in the future to tunnel other protocols like
+IPv6, IPX, raw Ethernet frames, etc. without changing the 'site' code
+at all.
+
+Point-to-point netlink devices work slightly differently; when you
+apply the 'tun', 'userv-ipif', etc. closure and specify the
+ptp-address option, you must also specify the 'routes' option.  The
+result of this invocation should be passed directly to the 'link'
+option of the site configuration.  You can do things like this:
+
+sites site {
+       name "foo";
+       link tun {
+               networks "192.168.73.76/32";
+               local-address "192.168.73.76"; # IP address of interface
+               ptp-address "192.168.73.75"; # IP address of other end of link
+               routes "192.168.73.74/32";
+               mtu 1400;
+               buffer sysbuffer();
+       };
+       etc.
+};
+
+The route dump obtained by sending SIGUSR1 to secnet now includes
+packet counts.
+
+Point-to-point mode has now been tested.
+
+tun-old has now been tested, and the annoying 'untested' message has
+been removed.  Thanks to SGT and JDA.
+
+secnet now closes its stdin, stdout and stderr just after
+backgrounding.
+
+Bugfix: specifying network "0.0.0.0/0" (or "default") now works
+correctly.
+               
+* New in version 0.1.9
+
+The netlink code may now generate ICMP responses to ICMP messages that
+are not errors, eg. ICMP echo-request.  This makes Windows NT
+traceroute output look a little less strange.
+
+configure.in and config.h.bot now define uint32_t etc. even on systems
+without stdint.h and inttypes.h (needed for Solaris 2.5.1)
+
+GNU getopt is included for systems that lack it.
+
+We check for LOG_AUTHPRIV before trying to use it in log.c (Solaris
+2.5.1 doesn't have it.)
+
+Portable snprintf.c from http://www.ijs.si/software/snprintf/ is
+included for systems that lack snprintf/vsnprintf.
+
+make-secnet-sites.py renamed to make-secnet-sites and now installed in
+$prefix/sbin/make-secnet-sites; ipaddr.py library installed in
+$prefix/share/secnet/ipaddr.py.  make-secnet-sites searches
+/usr/local/share/secnet and /usr/share/secnet for ipaddr.py
+
+* New in version 0.1.8
+
+Netlink devices now support a 'point-to-point' mode.  In this mode the
+netlink device does not require an IP address; instead, the IP address
+of the other end of the tunnel is specified using the 'ptp-address'
+option.  Precisely one site must be configured to use the netlink
+device. (I haven't had a chance to test this because 0.1.8 turned into
+a 'quick' release to enable secnet to cope with the network problems
+affecting connections going via LINX on 2001-10-16.)
+
+The tunnel code in site.c now initiates a key setup if the
+reverse-transform function fails (wrong key, bad MAC, too much skew,
+etc.) - this should make secnet more reliable on dodgy links, which
+are much more common than links with active attackers...  (an attacker
+can now force a new key setup by replaying an old packet, but apart
+from minor denial of service on slow links or machines this won't
+achieve them much).  This should eventually be made configurable.
+
+The sequence number skew detection code in transform.c now only
+complains about 'reverse skew' - replays of packets that are too
+old. 'Forward skew' (gaps in the sequence numbers of received packets)
+is now tolerated silently, to cope with large amounts of packet loss.
diff --git a/NOTES b/NOTES
new file mode 100644 (file)
index 0000000..884ae30
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,380 @@
+* Design of new, multi-subnet secnet protocol
+
+Like the first (1995/6) version, we're tunnelling IP packets inside
+UDP packets. To defeat various restrictions which may be imposed on us
+by network providers (like the prohibition of incoming TCP
+connections) we're sticking with UDP for everything this time,
+including key setup. This means we have to handle retries, etc.
+
+Other new features include being able to deal with subnets hidden
+behind changing 'real' IP addresses, and the ability to choose
+algorithms and keys per pair of communicating sites.
+
+** Configuration and structure
+
+[The original plan]
+
+The network is made up from a number of 'sites'. These are collections
+of machines with private IP addresses. The new secnet code runs on
+machines which have interfaces on the private site network and some
+way of accessing the 'real' internet.
+
+Each end of a tunnel is identified by a name. Often it will be
+convenient for every gateway machine to use the same name for each
+tunnel endpoint, but this is not vital. Individual tunnels are
+identified by their two endpoint names.
+
+[The new plan]
+
+It appears that people want to be able to use secnet on mobile
+machines like laptops as well as to interconnect sites. In particular,
+they want to be able to use their laptop in three situations:
+
+1) connected to their internal LAN by a cable; no tunnel involved
+2) connected via wireless, using a tunnel to protect traffic
+3) connected to some other network, using a tunnel to access the
+internal LAN.
+
+They want the laptop to keep the same IP address all the time.
+
+Case (1) is simple.
+
+Case (2) requires that the laptop run a copy of secnet, and have a
+tunnel configured between it and the main internal LAN default
+gateway. secnet must support the concept of a 'soft' tunnel where it
+adds a route and causes the gateway to do proxy-ARP when the tunnel is
+up, and removes the route again when the tunnel is down.
+
+The usual prohibition of packets coming in from one tunnel and going
+out another must be relaxed in this case (in particular, the
+destination address of packets from these 'mobile station' tunnels may
+be another tunnel as well as the host).
+
+(Quick sanity check: if chiark's secnet address was in
+192.168.73.0/24, would this work properly? Yes, because there will be
+an explicit route to it, and proxy ARP will be done for it. Do we want
+packets from the chiark tunnel to be able to go out along other
+routes? No. So, spotting a 'local' address in a remote site's list of
+networks isn't sufficient to switch on routing for a site. We need an
+explicit option. NB packets may be routed if the source OR the
+destination is marked as allowing routing [otherwise packets couldn't
+get back from eg. chiark to a laptop at greenend]).
+
+[the even newer plan]
+
+secnet sites are configured to grant access to particular IP address
+ranges to the holder of a particular public key.  The key can certify
+other keys, which will then be permitted to use a subrange of the IP
+address range of the certifying key.
+
+This means that secnet won't know in advance (i.e. at configuration
+time) how many tunnels it might be required to support, so we have to
+be able to create them (and routes, and so on) on the fly.
+
+** VPN-level configuration
+
+At a high level we just want to be able to indicate which groups of
+users can claim ownership of which ranges of IP addresses. Assuming
+these users (or their representatives) all have accounts on a single
+machine, we can automate the submission of keys and other information
+to make up a 'sites' file for the entire VPN.
+
+The distributed 'sites' file should be in a more restricted format
+than the secnet configuration file, to prevent attackers who manage to
+distribute bogus sites files from taking over their victim's machines.
+
+The distributed 'sites' file is read one line at a time. Each line
+consists of a keyword followed by other information. It defines a
+number of VPNs; within each VPN it defines a number of locations;
+within each location it defines a number of sites. These VPNs,
+locations and sites are turned into a secnet.conf file fragment using
+a script.
+
+Some keywords are valid at any 'level' of the distributed 'sites'
+file, indicating defaults.
+
+The keywords are:
+
+vpn n: we are now declaring information to do with VPN 'n'. Must come first.
+
+location n: we are now declaring information for location 'n'.
+
+site n: we are now declaring information for site 'n'.
+endsite: we're finished declaring information for the current site
+
+restrict-nets a b c ...: restrict the allowable 'networks' for the current
+  level to those in this list.
+end-definitions: prevent definition of further vpns and locations, and
+  modification of defaults at VPN level
+
+dh x y: the current VPN uses the specified group; x=modulus, y=generator
+
+hash x: which hash function to use. Valid options are 'md5' and 'sha1'.
+
+admin n: administrator email address for current level
+
+key-lifetime n
+setup-retries n
+setup-timeout n
+wait-time n
+renegotiate-time n
+
+address a b: a=dnsname, b=port
+networks a b c ...
+pubkey x y z: x=keylen, y=encryption key, z=modulus
+mobile: declare this to be a 'mobile' site
+
+** Logging etc.
+
+There are several possible ways of running secnet:
+
+'reporting' only: --version, --help, etc. command line options and the
+--just-check-config mode.
+
+'normal' run: perform setup in the foreground, and then background.
+
+'failed' run: setup in the foreground, and terminate with an error
+before going to background.
+
+'reporting' modes should never output anything except to stdout/stderr.
+'normal' and 'failed' runs output to stdout/stderr before
+backgrounding, then thereafter output only to log destinations.
+
+** Protocols
+
+*** Protocol environment:
+
+Each gateway machine serves a particular, well-known set of private IP
+addresses (i.e. the agreement over which addresses it serves is
+outside the scope of this discussion). Each gateway machine has an IP
+address on the interconnecting network (usually the Internet), which
+may be dynamically allocated and may change at any point.
+
+Each gateway knows the RSA public keys of the other gateways with
+which it wishes to communicate. The mechanism by which this happens is
+outside the scope of this discussion. There exists a means by which
+each gateway can look up the probable IP address of any other.
+
+*** Protocol goals:
+
+The ultimate goal of the protocol is for the originating gateway
+machine to be able to forward packets from its section of the private
+network to the appropriate gateway machine for the destination
+machine, in such a way that it can be sure that the packets are being
+sent to the correct destination machine, the destination machine can
+be sure that the source of the packets is the originating gateway
+machine, and the contents of the packets cannot be understood other
+than by the two communicating gateways.
+
+XXX not sure about the address-change stuff; leave it out of the first
+version of the protocol. From experience, IP addresses seem to be
+quite stable so the feature doesn't gain us much.
+
+**** Protocol sub-goal 1: establish a shared key
+
+Definitions:
+
+A is the originating gateway machine name
+B is the destination gateway machine name
+A+ and B+ are the names with optional additional data, see below
+PK_A is the public RSA key of A
+PK_B is the public RSA key of B
+PK_A^-1 is the private RSA key of A
+PK_B^-1 is the private RSA key of B
+x is the fresh private DH key of A
+y is the fresh private DH key of B
+k is g^xy mod m
+g and m are generator and modulus for Diffie-Hellman
+nA is a nonce generated by A
+nB is a nonce generated by B
+iA is an index generated by A, to be used in packets sent from B to A
+iB is an index generated by B, to be used in packets sent from A to B
+i? is appropriate index for receiver
+
+Note that 'i' may be re-used from one session to the next, whereas 'n'
+is always fresh.
+
+The optional additional data after the sender's name consists of some
+initial subset of the following list of items:
+ * A 32-bit integer with a set of capability flags, representing the
+   abilities of the sender.
+ * In MSG3/MSG4: a 16-bit integer being the sender's MTU, or zero.
+   (In other messages: nothing.)  See below.
+ * More data which is yet to be defined and which must be ignored
+   by receivers.
+The optional additional data after the receiver's name is not
+currently used.  If any is seen, it must be ignored.
+
+Capability flag bits must be in one the following two categories:
+
+1. Early capability flags must be advertised in MSG1 or MSG2, as
+   applicable.  If MSG3 or MSG4 advertise any "early" capability bits,
+   MSG1 or MSG3 (as applicable) must have advertised them too.  Sadly,
+   advertising an early capability flag will produce MSG1s which are
+   not understood by versions of secnet which predate the capability
+   mechanism.
+
+2. Late capability flags are advertised in MSG2 or MSG3, as
+   applicable.  They may also appear in MSG1, but this is not
+   guaranteed.  MSG4 must advertise the same set as MSG2.
+
+Currently, the low 16 bits are allocated for negotiating bulk-crypto
+transforms.  Bits 8 to 15 are used by Secnet as default capability
+numbers for the various kinds of transform closures: bit 8 is for the
+original CBCMAC-based transform, and bit 9 for the new EAX transform;
+bits 10 to 15 are reserved for future expansion.  The the low eight bits
+are reserved for local use, e.g., to allow migration from one set of
+parameters for a particular transform to a different, incompatible set
+of parameters for the same transform.  Bit 31, if advertised by both
+ends, indicates that a mobile end gets priority in case of crossed MSG1.
+The remaining bits have not yet been assigned a purpose.
+
+Whether a capability number is early depends on its meaning, rather than
+being a static property of its number.  That said, the mobile-end-gets
+priority bit (31) is always sent as an `early' capability bit.
+
+
+MTU handling
+
+In older versions of secnet, secnet was not capable of fragmentation
+or sending ICMP Frag Needed.  Administrators were expected to configure
+consistent MTUs across the network.
+
+It is still the case in the current version that the MTUs need to be
+configured reasonably coherently across the network: the allocated
+buffer sizes must be sufficient to cope with packets from all other
+peers.
+
+However, provided the buffers are sufficient, all packets will be
+processed properly: a secnet receiving a packet larger than the
+applicable MTU for its delivery will either fragment it, or reject it
+with ICMP Frag Needed.
+
+The MTU additional data field allows secnet to advertise an MTU to the
+peer.  This allows the sending end to handle overlarge packets, before
+they are transmitted across the underlying public network.  This can
+therefore be used to work around underlying network braindamage
+affecting large packets.
+
+If the MTU additional data field is zero or not present, then the peer
+should use locally-configured MTU information (normally, its local
+netlink MTU) instead.
+
+If it is nonzero, the peer may send packets up to the advertised size
+(and if that size is bigger than the peer's administratively
+configured size, the advertiser promises that its buffers can handle
+such a large packet).
+
+A secnet instance should not assume that just because it has
+advertised an mtu which is lower than usual for the vpn, the peer will
+honour it, unless the administrator knows that the peers are
+sufficiently modern to understand the mtu advertisement option.  So
+secnet will still accept packets which exceed the link MTU (whether
+negotiated or assumed).
+
+
+Messages:
+
+1) A->B: i*,iA,msg1,A+,B+,nA
+
+i* must be encoded as 0.  (However, it is permitted for a site to use
+zero as its "index" for another site.)
+
+2) B->A: iA,iB,msg2,B+,A+,nB,nA
+
+(The order of B and A reverses in alternate messages so that the same
+code can be used to construct them...)
+
+3) A->B: {iB,iA,msg3,A+,B+,[chosen-transform],nA,nB,g^x mod m}_PK_A^-1
+
+If message 1 was a replay then A will not generate message 3, because
+it doesn't recognise nA.
+
+If message 2 was from an attacker then B will not generate message 4,
+because it doesn't recognise nB.
+
+4) B->A: {iA,iB,msg4,B+,A+,nB,nA,g^y mod m}_PK_B^-1
+
+At this point, A and B share a key, k. B must keep retransmitting
+message 4 until it receives a packet encrypted using key k.
+
+5) A: iB,iA,msg5,(ping/msg5)_k
+
+6) B: iA,iB,msg6,(pong/msg6)_k
+
+(Note that these are encrypted using the same transform that's used
+for normal traffic, so they include sequence number, MAC, etc.)
+
+The ping and pong messages can be used by either end of the tunnel at
+any time, but using msg0 as the unencrypted message type indicator.
+
+**** Protocol sub-goal 2: end the use of a shared key
+
+7) i?,i?,msg0,(end-session/msg7,A,B)_k
+
+This message can be sent by either party. Once sent, k can be
+forgotten. Once received and checked, k can be forgotten. No need to
+retransmit or confirm reception. It is suggested that this message be
+sent when a key times out, or the tunnel is forcibly terminated for
+some reason.
+
+**** Protocol sub-goal 3: send a packet
+
+8) i?,i?,msg0,(send-packet/msg9,packet)_k
+
+**** Other messages
+
+9) i?,i?,NAK (NAK is encoded as zero)
+
+If the link-layer can't work out what to do with a packet (session has
+gone away, etc.) it can transmit a NAK back to the sender.
+
+This can alert the sender to the situation where the sender has a key
+but the receiver doesn't (eg because it has been restarted).  The
+sender, on receiving the NAK, will try to initiate a key exchange.
+
+Forged (or overly delayed) NAKs can cause wasted resources due to
+spurious key exchange initiation, but there is a limit on this because
+of the key exchange retry timeout.
+
+10) i?,i?,msg8,A,B,nA,nB,msg?
+
+This is an obsolete form of NAK packet which is not sent by any even
+vaguely recent version of secnet.  (In fact, there is no evidence in
+the git history of it ever being sent.)
+
+This message number is reserved.
+
+11) *,*,PROD,A,B
+
+Sent in response to a NAK from B to A.  Requests that B initiates a
+key exchange with A, if B is willing and lacks a transport key for A.
+(If B doesn't have A's address configured, implicitly supplies A's
+public address.)
+
+This is necessary because if one end of a link (B) is restarted while
+a key exchange is in progress, the following bad state can persist:
+the non-restarted end (A) thinks that the key is still valid and keeps
+sending packets, but B either doesn't realise that a key exchange with
+A is necessary or (if A is a mobile site) doesn't know A's public IP
+address.
+
+Normally in these circumstances B would send NAKs to A, causing A to
+initiate a key exchange.  However if A and B were already in the
+middle of a key exchange then A will not want to try another one until
+the first one has timed out ("setup-time" x "setup-retries") and then
+the key exchange retry timeout ("wait-time") has elapsed.
+
+However if B's setup has timed out, B would be willing to participate
+in a key exchange initiated by A, if A could be induced to do so.
+This is the purpose of the PROD packet.
+
+We send no more PRODs than we would want to send data packets, to
+avoid a traffic amplification attack.  We also send them only in state
+WAIT, as in other states we wouldn't respond favourably.  And we only
+honour them if we don't already have a key.
+
+With PROD, the period of broken communication due to a key exchange
+interrupted by a restart is limited to the key exchange total
+retransmission timeout, rather than also including the key exchange
+retry timeout.
diff --git a/README b/README
index 3d18dc28294f0f2dfe87062cd8ca4660b3cd2219..ce3a4d70b44d4946462bf9f0c62718fd3a41463f 100644 (file)
--- a/README
+++ b/README
-subdirmk - assistance for non-recursive use of make
-===================================================
-
-Introduction
-------------
-
-Peter Miller's 1997 essay _Recursive Make Considered Harmful_
-persuasively argues that it is better to arrange to have a single
-make invocation with the project's complete dependency tree, rather
-than the currently conventional `$(MAKE) -C subdirectory' approach.
-
-However, actually writing a project's build system in a non-recursive
-style is not very ergonomic.  The main difficulties are:
-  - constantly having to write out long file and directory names
-  - the lack of a per-directory make variable namespace means
-    long make variables (or namespace clashes)
-  - it is difficult to arrange that one can cd to a subdirectory
-    and say `make all' and have something reasonable happen
-    (to wit, build an appropriate subset)
-
-`subdirmk' is an attempt to solve these problems (and it also slightly
-alleviates some of the boilerplate needed to support out-of-tree
-builds well, and helps a bit with metaprogramming and rule writing).
-
-Basic approach
---------------
-
-The developer is expected to write a makefile fragment, in each
-relevant subdirectory, called `Dir.sd.mk'.
-
-These fragments may contain ordinary make language.  Unqualified
-filenames are relative to the build toplevel, and all commands all run
-there.
-
-However, the sigil & is treated specially.  By and large, it refers to
-`the build directory corresponding to this .sd.mk file', etc.
-There are a variety of convenient constructions.
-
-The result is that to a large extent, the Dir.sd.mk has an easy way
-to namespace its "local" make variables, and an easy way to refer to
-its "local" filenames (and filenames in general).
-
-The Dir.sd.mk's are filtered, fed through autoconf in the usual way
-(for @..@-substitutions) and included by one autogenerated toplevel
-makefile.
-
-So all of the input is combined and passed to one make invocation.
-(A corollary is that there is no enforcement of the namespacing:
-discipline is required to prefix relevant variable names with &, etc.)
-
-Each subdirectory is also provided with an autogenerated `Makefile'
-which exists purely to capture ordinary make invocations and arrange
-for something suitable to happen.
-
-Where there are dependencies between subdirectories, each Dir.sd.mk
-can simply refer to files in other subdirectories directly.
-
-Substitution syntax
--------------------
-
-In general & expands to the subdirectory name when used for a
-filename, and to the subdirectory name with / replaced with _ for
-variable names.  (If your variables start with capital letters and
-your filenames with lowercase.  Otherwise, use &/ or &_.)
-
-Note that & is processed *even in makefile comments*.  The substitutor
-does not understand make syntax, or shell syntax, at all.  However,
-the substitution rules are chosen to work well with constructs which
-are common in makefiles.
-
-In the notation below, we suppose that the substitution is being in
-done in a subdirectory sub/dir of the source tree.  In the RH column
-we describe the expansion at the top level, which is often a special
-case (in general in variable names we call that TOP rather than the
-empty string).
-
-&CAPS          =>      sub_dir_CAPS                    or TOP_CAPS
-&lc            =>      sub/dir/lc                      or lc
-       Here CAPS is any ASCII letter A-Z and lc is a-z.
-       The assumption is that filenames are usually lowercase and
-       variables usually uppercase.  Otherwise, use another syntax:
-
-&/             =>      sub/dir/                        or nothing
-&_             =>      sub_dir_                        or TOP_
-&.             =>      sub/dir                         or .
-       (This implies that `&./' works roughly like `&/', although
-       it can produce a needless `./')
-
-&=             =>      sub_dir                         or TOP
-
-&^lc           =>      ${top_srcdir}/sub/dir/lc
-&^/            =>      ${top_srcdir}/sub/dir/
-&^.            =>      ${top_srcdir}/sub/dir
-
-&~lc           =>      ${top_srcdir}/lc
-&~/            =>      ${top_srcdir}/
-&~.            =>      ${top_srcdir}
-
-In general:
-    ^   pathname of this subdirectory in source tree
-    ~   pathname of top level of source tree
-    /  terminates the path escape } needed if next is
-    _   terminates the var escape  } not letter or space)
-    .   terminates path escape giving dir name (excluding /)
-    =  terminates var escape giving only prefix part (rarely needed)
-  lwsp  starts multi-word processing (see below)
-
-So pathname syntax is a subset of:
-    '&' [ '^' | '~' ] [ lc | '/' | '.' ]
-
-&&             =>      &&              for convenience in shell runes
-
-&\&            =>      &               general escaping mechanism
-&\$            =>      $               provided for $-doubling regimes
-&\NEWLINE                              eats the newline and vanishes
-
-&$VARIABLE     =>      ${sub_dir_VARIABLE}     or ${TOP_VARIABLE}
-       VARIABLE is ASCII starting with a letter and matching \w+
-
-& thing thing... &
-&^ thing thing... &
-&~ thing thing... &
-       Convenience syntax for prefixing multiple filenames.
-       Introduced by & followed by lwsp where lc could go.
-       Each lwsp-separated non-ws word is prefixed by &/ etc.
-        etc. respectively.  No other & escapes are recognised.
-       This processing continues until & preceded by lwsp,
-       or until EOL (the end of the line), or \ then EOL.
-
-&:<directive> <args>....
-       recognised at start of line only (possibly after lwsp)
-
-&:include filename             filename should usually be [&]foo.sd.mk
-&:-include filename            tolerate nonexistent file
-       RHS is &-expanded but filenames are relative to the top
-       srcdir.  This implies that unqualified names are like &~/
-       whereas &/ is like &^/.  &^ and &~ do not work here because
-       they expand to constructions involving literally
-       `$(top_srcdir)', but the RHS is not make-expanded.
-
-&!<lwsp>       disables & until EOL (and then disappears)
-
-&#     delete everything to end of line
-       (useful if the RHS contains unrecognised & constructions)
-
-&TARGETS_things
-       Handled specially.  If mentioned at the start of a line
-       (possibly following whitespace), declares that this
-       subdir ought to have a target `things'.  The rule will be
-               &/things:: $(&TARGETS_things)
-
-       You may extend it by adding more :: rules for the target,
-       but the preferred style is to do things like this:
-               &TARGETS_check += & test-passed.stamp
-
-       It is important to mention &TARGETS_things at least once in
-       the context of each applicable directory, because doing so
-       arranges that the *parent* will also have a `things' target
-       which recursively implies this directory's `things'.
-
-       Must be spelled exactly &TARGETS_things.  &_TARGETS_things,
-       for example, is not magic.  To make the target exist
-       without providing any prerequisites for it, write a line
-       containing just `&TARGETS_things +='.
-
-       `all' is extra special: every directory has an `all'
-       target, which corresponds to &TARGETS.
-
-&:warn [!]WARNTAG ...
-       Suppress (with !) or re-enable (without !) warnings tagged
-       WARNTAG (see section `Warnings', below).  The suppression list
-       is reset at the start of processing in each subdirectory.
-       Warnings that appear at the end of processing are controlled
-       by the final warning state after processing all the toplevel
-       input files (including Final.sd.mk).
-
-&:local+global [!][&]VARIABLE ...
-       Suppresses any warnings relating to forthcoming mentions
-       to VARIABLE or &VARIABLE, as applicable.  Scope ends at
-       the end of the current directory's Suffix.sd.mk.
-       Prefixing with ! removes [&]VARIABLE from the suppresion list.
-
-&:changequote NEWQUOTE
-       changes the escape sequence from & to literally NEWQUOTE
-       NEWQUOTE may be any series of of non-whitespace characters,
-       and is terminated by EOL or lwsp.  The whole line is
-       discarded.
-
-       After this, write NEWQUOTE instead of &, everywhere.
-       The effect is unscoped and lasts until the next setting,
-       or until the end of the current directory's Suffix.sd.mk.
-       It takes effect on &:include'd files too, so maybe set
-       it back before using &:include.
-
-       Notably
-               NEWQUOTENEWQUOTE        => NEWQUOTENEWQUOTE
-               NEWQUOTE\NEWQUOTE       => NEWQUOTE
-               NEWQUOTE\$              => $
-               NEWQUOTE:changequote &  set escape back to &
-
-
-Dollar doubling and macro assistance
-------------------------------------
-
-&$+            Starts dollar-doubling
-&$-            Stops dollar-doubling
-       Both are idempotent and local to the file or context.
-
-This is useful both for make macrology involving $(eval ...), and
-possibly for helping write complicated recipes involving shell
-variables, inline Perl code, etc.
-
-Sometimes we will show $'s being doubled inside another construct.
-This means the content of the construct is $-doubled: $-doubling is
-locally enabled, and restored afterwards.
-
-&:macro NAME   =>      define NAME
-STUFF $ THINGS ..      STUFF $$ THINGS
-&:endm         ..      endef
-       NAME is processed for &
-
-&${..$..} =>   ${eval ${call ..$$..}}
-       (matches { } pairs to find the end)
-       content is $-doubled (unless it contains &$- to turn that off)
-
-Together &:macro and &${...} provide a more reasonable macro facility
-than raw make.  They solve the problem that make expansions cannot
-directly generate multiple rules, variable, etc.; instead, `$(eval )'
-must be used, but that re-expands the argument, meaning that all the
-literal text must be $-doubled.  This applies to the macro text and to
-the arguments.  Also `$(eval $(call ...))' is an unfortunate syntax.
-Hence &:macro and &${...}.
-
-While dollar-doubling:
-- - - - - - - - - - -
-
-$      =>      $$      including $'s produced by other
-                        &-expansions not mentioned here
-
-&\$    =>      $
-&$(    =>      ${      (expands to { } so it is useable for shell too)
-&$NN   =>      ${NN}   where N are digits
-
-A few contexts do not support $-doubling, such as directive arguments
-or places where this might imply $-quadrupling.  (There is no way to
-get $-quadrupling.)
-
-
-Invocation, "recursive" per-directory targets
----------------------------------------------
-
-Arrangements are made so that when you run `make foo' in a
-subdirectory, it is like running the whole toplevel makefile, from the
-toplevel, as `make subdir/foo'.  If `subdir/foo' is a file that might
-be built, that builds it.
-
-But `foo' can also be a conventional target like `all'.
-
-Each subdirectory has its own `all' target.  For example a
-subdirectory `src' has a target `src/all'.  The rules for these are
-automatically generated from the settings of the per-directory
-&TARGETS variables.  &TARGETS is magic in this way.  (In
-src/Dir.sd.mk, &TARGETS of course refers to a make variable called
-src_TARGETS.)
-
-The `all' target in a parent directory is taken to imply the `all'
-targets in all of its subdirectories, recursively.  And in the
-autogenerated stub Makefiles, `all' is the default target.  So if you
-just type `make' in the toplevel, you are asking for `&all'
-(<subdir>/all) for every directory in the project.
-
-In a parallel build, the rules for all these various subdirectory
-targets may be in run in parallel: there is only one `make' invocation
-at a time.  There is no sequencing between subdirectories, only been
-individual targets (as specified according to their dependencies).
-
-You can define other per-directory recursive targets too: set the
-variable &TARGETS_zonk, or whatever (being sure to write &TARGETS_zonk
-at the start of a line).  This will create a src/zonk target (for
-appropriate value of src/).  Unlike `all', these other targets only
-exist in areas of the project where at least something mentions them.
-So for example, if &TARGETS_zonk is set in src but not lib, `make
-zonk' in lib will fail.  If you want to make a target exist
-everywhere, += it with nothing in Prefix.sd.mk or Suffix.sd.mk (see
-below).
-
-Prefix.sd.mk, Suffix.sd.mk, Final.sd.mk, inclusion
---------------------------------------------------
-
-The files Prefix.sd.mk and Suffix.sd.mk in the toplevel of the source
-are automatically processed before and after each individual
-directory's Dir.sd.mk, and the &-substituted contents therefore
-appear once for each subdirectory.
-
-This lets you do per-directory boilerplate.  Some useful boilerplate
-is already provided in subdirmk, for you to reference like this:
-  &:include subdirmk/cdeps.sd.mk
-  &:include subdirmk/clean.sd.mk
-For example you could put that in Suffix.sd.mk.
-
-The top-level Dir.sd.mk is the first makefile included after the
-autogenerated `main.mk' which merely has some basic settings and
-includes.  So if you want to get in early and set global variables,
-put them near the top of Dir.sd.mk.
-
-The file Final.sd.mk in the toplevel directory is processed and
-the result included after all the other files.  Its subdirmk
-filtering context inherits warning suppressions from the toplevel's
-Dir.sd.mk etc., but not anything else.
-
-subdirmk's filter script itself sets (only) these variables:
-  top_srcdir
-  abs_top_srcdir
-  SUBDIRMK_MAKEFILES
-  MAKEFILE_TEMPLATES
-You are likely to want to define $(PWD), and shorter names for
-top_srdir and abs_top_srcdir (we suggest $(src) and $(abs_src)).
-
-Warnings
---------
-
-subdirmk's `generate' program, which does the acual &-substitution,
-can produce some warnings about your .sd.mk files.  These can be
-suppressed with the &:warn directive.  The warning tags are:
-
-    local+global
-       The same VARNAME was used both with and without an & prefix.
-       This can be confusing.  Also, if you avoid this then you will
-       get a warning iff you accidentally leave off a needed &.
-       The generation of this warning depends on scanning your
-       makefile for things that look like variable references, which
-       subdirmk does not do completely perfectly.  Exciting make
-       syntax may evade this warning, or require suppressions.
-       (You can suppress this warning for a particular VARNAME with
-       the &:local+global directive.)
-
-    single-char-var
-       A variable expansion like $FBAR.  make's expansion rules
-       interpret this as $(F)BAR.  It's normally better to write
-       it this way, at least if the variable expansion is followed
-       by more letters.  Note that &$FOO works differently to
-       raw make: it expands to ${sub_dir_FOO}.
-
-    broken-var-ref
-        An attempt at variable expansion looking like $&...
-       You probably expected this to mean $(TOP_F)BAR but it
-       expands to $TOP_FBAR which make thinks means $(T)OP_FBAR.
-
-    unknown-warning
-       &:warn was used to try to enable a warning that this version
-       of subdirmk does not understand.  (Note that an attempt to
-       *dis*able an unknown warning is only reported if some other
-       warning was issued which might have been disabled.)
-
-
-Guides, hints, and further explanations
-=======================================
-
-Incorporating this into your project
-------------------------------------
-
-Use `git-subtree' to merge the subdirmk/ directory.  You may find it
-useful to symlink the DEVELOPER-CERTIFICATE file (git can store
-symlinks as symlinks - just `git add' the link).  And you probably
-want to mention the situation in your top-level COPYING and HACKING.
-
-Symlink autogen.sh into your project toplevel.
-
-In your configure.ac, say
+secnet - flexible VPN software
 
-  m4_include([subdirmk/subdirmk.ac])
-  SUBDIRMK_SUBDIRS([...list of subdirectories in relative syntax...])
-
-Write a Dir.sd.mk in each directory.  See the substitution syntax
-reference, above, and the example/ directory here.  The toplevel
-Dir.sd.mk should probably contain:
+* Copying
 
-  include subdirmk/usual.mk
-  include subdirmk/regen.mk
+secnet is
+  Copyright 1995-2003 Stephen Early <steve@greenend.org.uk>
+  Copyright 2002-2014 Ian Jackson <ijackson@chiark.greenend.org.uk>
+  Copyright 1991      Massachusetts Institute of Technology
+  Copyright 1998      Ross Anderson, Eli Biham, Lars Knudsen
+  Copyright 1993      Colin Plumb
+  Copyright 1998      James H. Brown, Steve Reid
+  Copyright 2000      Vincent Rijmen, Antoon Bosselaers, Paulo Barreto
+  Copyright 2001      Saul Kravitz
+  Copyright 2004      Fabrice Bellard
+  Copyright 2002      Guido Draheim
+  Copyright 2005-2010 Free Software Foundation, Inc.
+  Copyright 1995-2001 Jonathan Amery
+  Copyright 1995-2003 Peter Benie
+  Copyright 2011      Richard Kettlewell
+  Copyright 2012      Matthew Vernon
+  Copyright 2013-2019 Mark Wooding
+  Copyright 1995-2013 Simon Tatham
 
-Write a Suffix.sd.mk in the toplevel, if you want.  It should probably
-have:
+secnet is distributed under the terms of the GNU General Public
+License, version 3 or later.  Some individual files have more
+permissive licences; where this is the case, it is documented in the
+header comment for the files in question.
+
+secnet 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.  See the GNU General Public License
+for more details.
+
+The file COPYING contains a copy of the GNU GPL v3.
+
+
+* Introduction
+
+secnet allows large virtual private networks to be constructed
+spanning multiple separate sites.  It is designed for the case where a
+private network connecting many hosts is 'hidden' behind a single
+globally-routable IP address, but can also be applied in other
+circumstances.  It communicates entirely using UDP, and works well
+with gateways that implement network address translation.
+
+If you are installing secnet to join an existing VPN, you should read
+the 'INSTALL' file and your particular VPN's documentation now.  You
+may need to refer back to this file for information on the netlink and
+comm sections of the configuration file.
+
+If you are thinking about setting up a new VPN of any size (from one
+providing complete links between multiple sites to a simple
+laptop-to-host link), read the section in this file on 'Creating a
+VPN'.
+
+* Mailing lists and bug reporting
+
+There are two mailing lists associated with secnet: an 'announce' list
+and a 'discuss' list.  Their addresses are:
+http://www.chiark.greenend.org.uk/mailman/listinfo/secnet-announce
+http://www.chiark.greenend.org.uk/mailman/listinfo/secnet-discuss
+
+The -announce list receives one message per secnet release.  The
+-discuss list is for general discussion, including help with
+configuration, bug reports, feature requests, etc.
+
+Bug reports should be sent to <steve@greenend.org.uk>; they will be
+forwarded to the -discuss list by me.
+
+* Creating a VPN
+
+XXX TODO
+
+* secnet configuration file format
+
+By default secnet on linux reads /etc/secnet/secnet.conf.  The default
+may be different on other platforms.
+
+This file defines a dictionary (a mapping from keys to values) full of
+configuration information for secnet.  Two keys must be defined in
+this file for secnet to start.  One is "system", a dictionary
+containing systemwide control parameters.  The other is "sites", a
+list of all the sites that you intend to communicate with.
+
+The configuration file has a very simple syntax; keys are defined as
+follows:
+
+key definition;
+or
+key = definition;
+
+(the "=" is optional)
+
+Keys must match the following regular expression:
+[[:alpha:]_][[:alnum:]\-_]*
+
+i.e. the first character must be an alpha or an underscore, and the
+remaining characters may be alphanumeric, '-' or '_'.
+
+Keys can be defined to be a comma-separated list of any of the
+following types:
+
+  a boolean
+  a string, in quotes
+  a number, in decimal
+  a dictionary of definitions, enclosed in { }
+  a "closure", followed by arguments
+  a path to a key that already exists, to reference that definition
+
+Note that dictionaries can be nested: a key in one dictionary can
+refer to another dictionary. When secnet looks for a key in a
+particular directory and can't find it, it looks in the dictionary's
+lexical 'parents' in turn until it finds it (or fails to find it at
+all and stops with an error).
 
-  &:include subdirmk/cdeps.sd.mk
-  &:include subdirmk/clean.sd.mk
+Definitions can refer to previous definitions by naming them with a
+path.  Paths are key1/key2/key3... (starting from wherever we find
+key1, i.e. in the current dictionary or any of its parents), or
+alternatively /key1/key2/key3... (to start from the root).
+Definitions cannot refer to future definitions.
 
+Example:
 
-Hints
------
+a=1;
+b=2;
+c={ d=3; e=a; };
+f={ a=4; g=c; };
 
-You can convert your project incrementally.  Start with the top-level
-Makefile.in and rename it to Dir.sd.mk, and add the appropriate
-stuff to configure.ac, and fix everything up.  Leave the existing
-$(MAKE) -C for your existing subdirectories alone.  Then you can
-convert individual subdirectories, or classes of subdirectories, at
-your leisure.  (You must be /sure/ that each recursive (non-subdirmk)
-subdirectory will be entered only once at a time, but your existing
-recursive make descent system should already do that or you already
-have concurrency bugs.)
-
-Aside from this, be very wary of any invocation of $(MAKE) anywhere.
-This is a frequent source of concurrency bugs in recursive make build
-systems.  When combined with nonrecursive make it's all in the same
-directory and there is nothing stopping the different invocations
-ending up trying to make the same targets at the same time. That
-causes hideous racy lossage.  There are ways to get this to work
-reliably but it is advanced stuff.
-
-If you make syntax errors, or certain kinds of other errors, in your
-makefiles, you may find that just `make' is broken now and cannot get
-far enough to regenerate a working set of makefiles.  If this happens
-just rerun ./config.status by hand.
-
-If you go back and forth between different versions of your code you
-can sometimes find that `make' complains that one of your Dir.sd.mk
-files is missing: typically, if iot was used and therefore a
-dependency in some other version of your code.  If you run `make
-clean' (or `make realclean') these dependencies are suppressed, which
-will clear up the problem.
-
-
-Global definitions
-------------------
-
-If want to set global variables, such as CC, that should only be done
-once.  You can put them in your top-level Dir.sd.mk, or a separate
-file you `include' and declare using SUBDIRMK_MAKEFILES.
-
-If you need different settings of variables like CC for different
-subdirectories, you should probably do that with target-specific
-variable settings.  See the info node `(make) Target-specific'.
-
-
-Directory templates `.sd.mk' vs plain autoconf templates `.mk.in'
---------------------------------------------------------------------
-
-There are two kinds of template files.
-
- Filename                 .sd.mk                  .mk.in
-
- Processed by             &-substitution,         autoconf only
-                          then autoconf
-
- Instantiated             Usu. once per subdir    Once only
-
- Need to be mentioned     No, but Dir.sd.mk       All not in subdirmk/
- in configure.ac?         via SUBDIRMK_SUBDIRS    via SUBDIRMK_MAKEFILES
-
- How to include           `&:include foo.sd.mk'   `include foo.mk'
-                         in all relevant .sd.mk  in only one
-                          (but not needed for     Dir.sd.mk
-                           Prefix, Suffix, Final)
-
-If you `include subdirmk/regen.mk', dependency management and
-automatic regeneration for all of this template substitution, and for
-config.status etc. is done for you.
-
-
-Tables of file reference syntaxes
----------------------------------
-
-In a nonrecursive makefile supporting out of tree builds there are
-three separate important distinctions between different file
-locations:
-
- (i) In the build tree, or in the source tree ?
-
- (ii) In (or relative to) the subdirectory to which this Dir.sd.mk
-     relates, or relative to the project's top level ?
-
- (iii) Absolute or relative pathname ?  Usually relative pathnames
-     suffice.  Where an absolute pathname is needed, it can be built
-     out of &/ and an appropriate make variable such as $(PWD).
-
-Path construction &-expansions are built from the following:
-
-                      Relative paths in...
-                      build     source
-                                                       
-  This directory      &         &^
-  Top level           .         &~
-
-In more detail, with all the various options laid out:
-
-      Recommended     Relative paths in...   Absolute paths in...
-             for      build     source       build         source
-                                                       
-  This       lc       &file     &^file       $(PWD)/&file  $(abs_src)/&file
-  directory  any      &/file    &^/file      $(PWD)/&/file $(abs_src)/&/file
-             several  & f g h   &^ f g h     $(addprefix...)
-                                             
-  Top        lc       file      &~file
-  level      any      file      &~/file      $(PWD)/file   $(abs_src)/file
-             .mk.in   file      $(src)/file  $(PWD)/file   $(abs_src)/file
-             several  f g h     &~ f g h     $(addprefix...)
-
-(This assumes you have appropriate make variables src, PWD and
-abs_src.)
-
-
-Subdirectory and variable naming
---------------------------------
-
-The simple variable decoration scheme does not enforce a strict
-namespace distinction between parts of variable names which come from
-subdirectory names, and parts that mean something else.
-
-So it is a good idea to be a bit careful with your directory naming.
-`TOP', names that contain `_', and names that are similar to parts of
-make variables (whether conventional ones, or ones used in your
-project) are best avoided.
-
-If you name your variables in ALL CAPS and your subdirectories in
-lower case with `-' rather than `_', there will be no confusion.
-
-
-Legal information
-=================
-
-subdirmk is
- Copyright 2019 Mark Wooding
- Copyright 2019 Ian Jackson
-
-    subdirmk and its example is free software; you can redistribute it
-    and/or modify it under the terms of the GNU Library General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This 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.  See the GNU
-    Library General Public License for more details.
-
-    You should have received a copy of the GNU Library General Public
-    License along with this library as the file LGPL-2.
-    If not, see https://www.gnu.org/.
-
-Individual files generally contain the following tag in the copyright
-notice, instead of the full licence grant text:
-  SPDX-License-Identifier: LGPL-2.0-or-later
-As is conventional, this should be read as a licence grant.
-
-Contributions are accepted based on the git commit Signed-off-by
-convention, by which the contributors' certify their contributions
-according to the Developer Certificate of Origin version 1.1 - see
-the file DEVELOPER-CERTIFICATE.
-
-Where subdirmk is used by and incorporated into another project (eg
-via git subtree), the directory subdirmk/ is under GNU LGPL-2.0+, and
-the rest of the project are under that other project's licence(s).
-(The project's overall licence must be compatible with LGPL-2.0+.)
+The following paths are valid:
+a is 1
+b is 2
+c is a dictionary:
+ c/d is 3
+ c/e is 1
+f is a dictionary:
+ f/a is 4
+ f/g is a dictionary:
+  f/g/d is 3
+  f/g/e is 1
+
+Note that f/g/e is NOT 4.
+
+Elements that are lists are inserted into lists in definitions, not
+referenced by them (i.e. you can't have lists of lists).
+
+Some closures may be followed by an argument list in ( ), and may
+return any number of whatever type they like (including other
+closures).  Some types of closure (typically those returned from
+invokations of other closures) cannot be invoked.
+
+closure { definitions } is short for closure({definitions}).
+
+The main body of secnet, and all the additional modules, predefine
+some keys in the root dictionary.  The main ones are:
+
+  yes, true, True, TRUE, on:   the boolean value True
+  no, false, False, FALSE, off: the boolean value False
+  makelist:   turns a dictionary (arg1) into a list of definitions
+              (ignoring the keys)
+  readfile:   reads a file (arg1) and returns it as a string
+  map:        applies the closure specified as arg1 to each of the
+              remaining elements in the list in turn.  Returns a list
+              made up of the outputs of the closure.
+
+Keys defined by modules are described below, in the module
+documentation.
+
+Other configuration files can be included inline by writing "include
+filename" at the start of a line.
+
+After the configuration file is read, secnet looks for particular keys
+in configuration space to tell it what to do:
+
+ system: a dictionary which can contain the following keys:
+   log (log closure): a destination for system messages
+   userid (string): the userid for secnet to run as once it drops privileges
+   pidfile (string): where to store its PID
+   
+ sites: a list of closures of type 'site', which define other tunnel
+        endpoints that secnet will attempt to communicate with
+
+* secnet command line options
+
+Usage: secnet [OPTION]...
+
+  -f, --silent, --quiet   suppress error messages
+  -w, --nowarnings        suppress warnings
+  -v, --verbose           output extra diagnostics
+  -c, --config=filename   specify a configuration file
+  -j, --just-check-config stop after reading configfile
+  -n, --nodetach          do not run in background
+  -d, --debug=item,...    set debug options
+      --help              display this help and exit
+      --version           output version information and exit
+
+* secnet builtin modules
+
+** resolver
+
+Defines:
+  adns (closure => resolver closure)
+
+adns: dict argument
+  config (string): optional, a resolv.conf for ADNS to use
+
+** random
+
+Defines:
+  randomsrc (closure => randomsrc closure)
+
+randomsrc: string[,bool]
+  arg1: filename of random source
+  arg2: if True then source is blocking
+
+** udp
+
+Defines:
+  udp (closure => comm closure)
+
+udp: dict argument
+  address (string list): IPv6 or IPv4 addresses to listen and send on;
+   default is all local addresses
+  port (integer): UDP port to listen and send on; optional if you
+   don't need to have a stable address for your peers to talk to
+   (in which case your site ought probably to have `local-mobile true').
+  buffer (buffer closure): buffer for incoming packets
+  authbind (string): optional, path to authbind-helper program
+
+** polypath
+
+Defines:
+  polypath (closure => comm closure)
+
+polypath: dict argument
+  port (integer): UDP port to listen and send on
+  buffer (buffer closure): buffer for incoming packets
+  authbind (string): optional, path to authbind-helper program
+  max-interfaces (number): optional, max number of different interfaces to
+   use (also, maximum steady-state amount of packet multiplication);
+   interfaces marked with `@' do not count.
+  interfaces (string list): which interfaces to process; each entry is
+   optionally `!' or `+' or `@' followed by a glob pattern (which is
+   applied to a prospective interface using fnmatch with no flags).
+   `+' or nothing means to process normally. `!' means to ignore;
+   `@' means to use only in conjunction with dedicated-interface-addr.
+   If no list is specified, or the list ends with a `!' entry, a
+   default list is used/appended:
+   "!tun*","!tap*","!sl*","!userv*","!lo","@hippo*","*".
+   Patterns which do not start with `*' or an alphanumeric need to be
+   preceded by `!' or `+' or `@'.
+  monitor-command (string list): Program to use to monitor appearance
+   and disappearance of addresses on local network interfaces.  Should
+   produce lines of the form `+|-<ifname> 4|6 <addr>' where <addr> is
+   an address literal.  Each - line should relate to a previously
+   printed + line.  On startup, should produce a + line for each
+   currently existing address.  secnet does filtering so there is no
+   need to strip out tun interfaces, multicast addresses, and so on.
+   The command is run as the user secnet is started as (not the one
+   which secnet may drop privilege to due to the configured `userid').
+   The default depends on the operating system.
+  permit-loopback (boolean): Normally, loopback IPv6 and IPv4
+   addresses on local interfaces are disregarded, because such
+   interfaces are not interesting for communicating with distant
+   hosts.  Setting this option will ignore that check, which can be
+   useful for testing.  Setting this option also removes "!lo*" from
+   the default interface pattern list.
+
+When using this comm, packets are sent out of every active interface
+on the host (where possible).  It is important that interfaces created
+by secnet itself are not included!  secnet's default filter list tries
+to do this.
+
+This comm only makes sense for sites which are mobile.  That is, the
+site closures used with this comm should all have the `local-mobile'
+parameter set to `true'.  When the local site site is not marked
+mobile the address selection machinery might fixate on an unsuitable
+address.
+
+polypath takes site-specific informtion as passed to the `comm-info'
+site closure parameter.  The entries understood in the dictionary
+are:
+  dedicated-interface-addr (string): IPv4 or IPv6 address
+   literal.  Interfaces specified with `@' in `interfaces' will be
+   used for the corresponding site iff the interface local address
+   is this address.
+
+For an interface to work with polypath, it must either have a suitable
+default route, or be a point-to-point interface.  In the general case
+this might mean that the host would have to have multiple default
+routes.  However in practice the most useful configuration is two
+interfaces being (1) wifi (2) mobile internet.
+
+I have had success on Linux by using network-manager for wifi and
+invoking ppp directly for mobile internet.  ppp sets up a
+point-to-point link, and does not add a default route if there already
+is one.  network-manager always sets up a default route.  The result
+is that the wifi always has a default route (so is useable); ppp
+(being a point-to-point link) does not need one.
+
+The use of polypath requires that secnet be started with root
+privilege, to make the setsockopt(,,SO_BINDTODEVICE,) calls.  If the
+configuration specifies that secnet should drop privilege (see
+`userid' above), secnet will keep a special process around for this
+purpose; that process will handle local network interface changes but
+does not deal with any packets, key exchange, etc.
+
+polypath support is only available when secnet is built against an
+IPv6-capable version of adns (because it wants features in the newer
+adns).
+
+** log
+
+Defines:
+  logfile (closure => log closure)
+  syslog (closure => log closure)
+
+logfile: dict argument
+  filename (string): where to log to
+  class (string list): what type of messages to log
+    { "debug-config", M_DEBUG_CONFIG },
+    { "debug-phase", M_DEBUG_PHASE },
+    { "debug", M_DEBUG },
+    { "all-debug", M_DEBUG|M_DEBUG_PHASE|M_DEBUG_CONFIG },
+    { "info", M_INFO },
+    { "notice", M_NOTICE },
+    { "warning", M_WARNING },
+    { "error", M_ERROR },
+    { "security", M_SECURITY },
+    { "fatal", M_FATAL },
+    { "default", M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
+    { "verbose", M_INFO|M_NOTICE|M_WARNING|M_ERROR|M_SECURITY|M_FATAL },
+    { "quiet", M_FATAL }
+
+logfile will close and reopen its file upon receipt of SIGHUP.
+
+syslog: dict argument
+  ident (string): include this string in every log message
+  facility (string): facility to log as
+    { "authpriv", LOG_AUTHPRIV },
+    { "cron", LOG_CRON },
+    { "daemon", LOG_DAEMON },
+    { "kern", LOG_KERN },
+    { "local0", LOG_LOCAL0 },
+    { "local1", LOG_LOCAL1 },
+    { "local2", LOG_LOCAL2 },
+    { "local3", LOG_LOCAL3 },
+    { "local4", LOG_LOCAL4 },
+    { "local5", LOG_LOCAL5 },
+    { "local6", LOG_LOCAL6 },
+    { "local7", LOG_LOCAL7 },
+    { "lpr", LOG_LPR },
+    { "mail", LOG_MAIL },
+    { "news", LOG_NEWS },
+    { "syslog", LOG_SYSLOG },
+    { "user", LOG_USER },
+    { "uucp", LOG_UUCP }
+
+** util
+
+Defines:
+  sysbuffer (closure => buffer closure)
+
+sysbuffer: integer[,dict]
+  arg1: buffer length
+  arg2: options:
+    lockdown (boolean): if True, mlock() the buffer
+
+** site
+
+Defines:
+  site (closure => site closure)
+
+site: dict argument
+  local-name (string): this site's name for itself
+  name (string): the name of the site's peer
+  link (netlink closure)
+  comm (one or more comm closures): if there is more than one, the
+   first one will be used for any key setups initiated by us using the
+   configured address.  Others are only used if our peer talks to
+   them.
+  resolver (resolver closure)
+  random (randomsrc closure)
+  local-key (rsaprivkey closure)
+  address (string list): optional, DNS name(s) used to find our peer;
+    address literals are supported too if enclosed in `[' `]'.
+  port (integer): mandatory if 'address' is specified: the port used
+    to contact our peer
+  key (rsapubkey closure): our peer's public key
+  transform (transform closure): how to mangle packets sent between sites
+  dh (dh closure)
+  hash (hash closure)
+  key-lifetime (integer): max lifetime of a session key, in ms
+    [one hour; mobile: 2 days]
+  setup-retries (integer): max number of times to transmit a key negotiation
+    packet [5; mobile: 30]
+  setup-timeout (integer): time between retransmissions of key negotiation
+    packets, in ms [2000; mobile: 1000]
+  wait-time (integer): after failed key setup, wait roughly this long
+    (in ms) before allowing another attempt [20000; mobile: 10000]
+    Actual wait time is randomly chosen between ~0.5x and ~1.5x this.
+  renegotiate-time (integer): if we see traffic on the link after this time
+    then renegotiate another session key immediately (in ms)
+    [half key-lifetime, or key-lifetime minus 5 mins (mobile: 12 hours),
+     whichever is longer].
+  keepalive (bool): if True then attempt always to keep a valid session key.
+    [false]
+  log-events (string list): types of events to log for this site
+    unexpected: unexpected key setup packets (may be late retransmissions)
+    setup-init: start of attempt to setup a session key
+    setup-timeout: failure of attempt to setup a session key, through timeout
+    activate-key: activation of a new session key
+    timeout-key: deletion of current session key through age
+    security: anything potentially suspicious
+    state-change: steps in the key setup protocol
+    packet-drop: whenever we throw away an outgoing packet
+    dump-packets: every key setup packet we see
+    errors: failure of name resolution, internal errors
+    peer-addrs: changes to sets of peer addresses (interesting for mobile peers)
+    all: everything (too much!)
+  mobile (bool): if True then peer is "mobile" ie we assume it may
+    change its apparent IP address and port number without either it
+    or us being aware of the change; so, we remember the last several
+    port/addr pairs we've seen and send packets to all of them
+    (subject to a timeout).  We maintain one set of addresses for key
+    setup exchanges, and another for data traffic. Two communicating
+    peers must not each regard the other as mobile, or all the traffic
+    in each direction will be triplicated (strictly, transmitted
+    mobile-peers-max times) and anyway two peers whose public contact
+    address may suddenly change couldn't communicate reliably because
+    their contact addresses might both change at once. [false]
+  mobile-peers-max (integer): Maximum number of peer port/addr pairs we
+    remember and send to.  Must be at least 1 and no more than 5.
+    [4 if any address is configured, otherwise 3]
+  static-peers-max (integer): Maximum number of peer port/addr pairs
+    we can try for a static site.  Must be at least 1 and no more
+    than 5.  [4 or 3, as above]
+  mobile-peer-expiry (integer): For "mobile" peers only, the length
+    of time (in seconds) for which we will keep sending to multiple
+    address/ports from which we have not seen incoming traffic. [120]
+  local-mobile (bool): if True then other peers have been told we are
+    "mobile".  This should be True iff the peers' site configurations
+    for us have "mobile True" (and if we find a site configuration for
+    ourselves in the config, we insist on this).  The effect is to
+    check that there are no links both ends of which are allegedly
+    mobile (which is not supported, so those links are ignored) and
+    to change some of the tuning parameter defaults. [false]
+  mtu-target (integer): Desired value of the inter-site MTU for this
+    peering.  This value will be advertised to the peer (which ought
+    to affect incoming packets), and if the peer advertises an MTU its
+    value will be combined with this setting to compute the inter-site
+    MTU.  (secnet will still accept packets which exceed the
+    (negotiated or assumed) inter-site MTU.)  Setting a lower
+    inter-site MTU can be used to try to restrict the sizes of the
+    packets sent over the underlying public network (e.g. to work
+    around network braindamage).  It is not normally useful to set a
+    larger value for mtu-target than the VPN's general MTU (which
+    should be reflected in the local private interface MTU, ie the mtu
+    parameter to netlink).  If this parameter is not set, or is set
+    to 0, the default is to use the local private link mtu.
+  comm-info (dict): Information for the comm, used when this site
+    wants to transmit.  If the comm does not support this, it is
+    ignored.
+
+Links involving mobile peers have some different tuning parameter
+default values, which are generally more aggressive about retrying key
+setup but more relaxed about using old keys.  These are noted with
+"mobile:", above, and apply whether the mobile peer is local or
+remote.
+
+** transform-eax
+
+Defines:
+   eax-serpent (closure => transform closure)
+
+** transform-cbcmac
+
+Defines:
+  serpent256-cbc (closure => transform closure)
+
+** netlink
+
+Defines:
+  null-netlink (closure => closure or netlink closure)
+
+null-netlink: dict argument
+  name (string): name for netlink device, used in log messages
+  networks (string list): networks on the host side of the netlink device
+  remote-networks (string list): networks that may be claimed
+    by the remote site using this netlink device
+  local-address (string): IP address of host's tunnel interface
+  secnet-address (string): IP address of this netlink device
+  ptp-address (string): IP address of the other end of a point-to-point link
+  mtu (integer): MTU of host's tunnel interface
+
+Only one of secnet-address or ptp-address may be specified.  If
+point-to-point mode is in use then the "routes" option must also be
+specified, and netlink returns a netlink closure that should be used
+directly with the "link" option to the site closure.  If
+point-to-point mode is not in use then netlink returns a closure that
+may be invoked using a dict argument with the following keys to yield
+a netlink closure:
+  routes (string list): networks reachable down the tunnel attached to
+    this instance of netlink
+  options (string list):
+    allow-route: allow packets coming from this tunnel to be routed to
+      other tunnels as well as the host (used for mobile devices like laptops)
+    soft: remove these routes from the host's routing table when
+      the tunnel link quality is zero
+  mtu (integer): MTU of host's tunnel interface
+
+Netlink will dump its current routing table to the system/log on
+receipt of SIGUSR1.
+
+** slip
+
+Defines:
+  userv-ipif (closure => netlink closure)
+
+userv-ipif: dict argument
+  userv-path (string): optional, where to find userv ["userv"]
+  service-user (string): optional, username for userv-ipif service ["root"]
+  service-name (string): optional, name of userv-ipif service ["ipif"]
+  buffer (buffer closure): buffer for assembly of host->secnet packets
+ plus generic netlink options, as for 'null-netlink'
+
+** tun
+
+Defines:
+  tun (closure => netlink closure) [only on linux-2.4]
+  tun-old (closure => netlink closure)
+
+tun: dict argument
+  flavour (string): optional, type of TUN interface to use
+    ("guess","linux","bsd","streams")
+  device (string): optional, path of TUN/TAP device file ["/dev/net/tun"]
+  interface (string): optional, name of tunnel network interface
+  ifconfig-path (string): optional, path to ifconfig command
+  route-path (string): optional, path to route command
+  ifconfig-type (string): optional, how to perform ifconfig
+  route-type (string): optional, how to add and remove routes
+   types are: "guess", "ioctl", "bsd", "linux", "solaris-2.5"
+  buffer (buffer closure): buffer for host->secnet packets
+ plus generic netlink options, as for 'null-netlink'
+
+I recommend you don't specify the 'interface' option unless you're
+doing something that requires the interface name to be constant.
+
+** rsa
+
+Defines:
+  rsa-private (closure => rsaprivkey closure)
+  rsa-public (closure => rsapubkey closure)
+
+rsa-private: string[,bool]
+  arg1: filename of SSH private key file (version 1, no password)
+  arg2: whether to check that the key is usable [default True]
+
+rsa-public: string,string
+  arg1: encryption key (decimal)
+  arg2: modulus (decimal)
+
+** dh
+
+Defines:
+  diffie-hellman (closure => dh closure)
+
+diffie-hellman: string,string[,bool]
+  arg1: modulus (hex)
+  arg2: generator (hex)
+  arg3: whether to check that the modulus is prime [default True]
+
+** md5
+
+Defines:
+  md5 (hash closure)
+
+** sha1
+
+Defines:
+  sha1 (hash closure)
+
+** conffile
+
+Defines:
+  makelist (dictionary => list of definitions)
+  readfile (string => string)
+  map (closure,list => list)
+
+makelist: dictionary
+  returns a list consisting of the definitions in the dictionary. The keys
+  are discarded.
+
+readfile: string
+  reads the named file and returns its contents as a string
+
+map:
+  applies the closure specified as arg1 to each of the elements in the list.
+  Returns a list made up of the outputs of the closure.
diff --git a/README.mac b/README.mac
new file mode 100644 (file)
index 0000000..c84a84f
--- /dev/null
@@ -0,0 +1,83 @@
+How to install secnet on a Fink-equipped OS X system:
+  - Install GMP:
+    fink install gmp
+  - Download and install ADNS:
+    ./configure --disable-dynamic
+    make
+    sudo make install
+  - Build secnet:
+    ./configure
+    make
+    sudo make install
+  - Install tuntap for OSX from http://tuntaposx.sourceforge.net/
+  - Create /etc/secnet/{key,secnet.conf,sites.conf} as usual
+  - If you don't want secnet 'always on', edit
+    uk.org.greenend.secnet.plist and remove *both* these two lines:
+        <key>RunAtLoad</key>
+        <true/>
+  - Create the 'secnet' user and install the job configuration:
+    ./setup.mac.
+
+To start secnet:
+  sudo launchctl start uk.org.greenend.secnet
+
+To stop secnet:
+  sudo launchctl stop uk.org.greenend.secnet
+
+To uninstall:
+  sudo launchctl unload /Library/LaunchDaemons/uk.org.greenend.secnet.plist
+  sudo rm -f /Library/LaunchDaemons/uk.org.greenend.secnet.plist
+
+If you need to enable IP forwarding:
+  sudo sysctl -w net.inet.ip.forwarding=1
+
+(Note that on a Mac, you need to enable IP forwarding if you want to
+route to addresses on one interface via another; i.e. if you expect to
+be able to reach an address on en0 with a packet delivered through
+tun0, IP forwarding must be turned on.)
+
+How to import secnet into XCode 3.2:
+
+- Set up build directories as follows:
+    $ mkdir build/Debug build/Release
+    $ cd build/Debug
+    $ ~/src/secnet/configure CFLAGS="-g -O0"
+    $ cd ../Release
+    $ ~/src/secnet/configure
+    $ cd ../..
+  (Replace ~/src/secnet with the *absolute* path to your secnet tree -
+  XCode cannot map the relative paths in errors to the source files
+  otherwise.)
+- Start XCode
+- Menubar -> File -> New Project
+  - Choose the Mac OS X -> Other -> External Build System template
+  - Choose the *parent* of the secnet directory and call the project
+    secnet
+    - OK the overwrite (it won't overwrite anything that matters)
+    - This creates 'build' and 'secnet.xcodeproj' directories in your
+      secnet tree.
+- Right-click Groups & Files -> secnet -> Add -> Existing files and
+  select all the *.c, *.h, *.y and *.fl files.
+  - Omit the following files:
+    - *.yy.[ch]       \
+    - *.tab.[ch]      | generated during build
+    - version.c       |
+    - config.h        /
+    - snprintf.[ch]   - unnecessary on OSX
+  - Sort by 'kind' may make this easier
+  - Leave 'Copy items...' unchecked
+  - Add To Targets should have 'secnet' checked
+  - For conffile.fl, right click Get Info -> General, and set File
+    Type to sourcecode.lex.
+- Under Groups & Files -> secnet, select all source files and right
+  click Get Info -> General, and set:
+  - Tab Width to 8
+  - Indent Width to 4
+  - Check Editor uses tabs
+- Double click click Groups & Files -> Targets secnet
+  - Add '-C $TARGET_BUILD_DIR' to the start of the arguments.
+
+You should now be able to build both debug and release configurations
+using âŒ˜B.
+
+Richard Kettlewell 2011-07-23
diff --git a/README.make-secnet-sites b/README.make-secnet-sites
new file mode 100644 (file)
index 0000000..ea767e9
--- /dev/null
@@ -0,0 +1,235 @@
+USAGE
+
+       make-secnet-sites [-P PREFIX] [IN [OUT]]
+       make-secnet-sites -u HEADER GRPDIR SITESFILE GROUP
+
+       The `-P' option sets the PREFIX string, mentioned below in
+       `OUTPUT STRUCTURE'; the default is empty.
+
+       In the former mode, `make-secnet-sites' reads a single input
+       file from IN (defaulting to standard input), and writes a Secnet
+       configuration fragment to OUT (defaulting to standard output).
+
+       In the latter, `make-secnet-sites' expects to have been invoked
+       via GNU Userv.  It verifies that GROUP is listed in the
+       `USERV_GROUP' environment variable.  It then processes the
+       HEADER input, which should say `end-defintions' somewhere, to
+       enable restrictions, and then user input on standard input.  If
+       the combination of the two is acceptable, it writes a copy of
+       the user input to the file `GRPDIR/RGROUP' (the `R' is literal)
+       preceded by a comment logging the time and the value of the
+       `USERV_USER' environment variable, and writes a file named
+       SITESFILE consisting of the concatenation of:
+
+         * a header comment logging the time and the value of the
+           `USERV_USER' environment variable, and a reminder that this
+           is `make-secnet-sites' input;
+
+         * the HEADER, with any `include' lines replaced by the files
+           they include; and
+
+         * each of the `GRPDIR/R*' files, in some arbitrary order.
+
+       This SITESFILE can later be processed in the former mode to
+       produce Secnet configuration.
+
+
+INPUT SYNTAX
+
+       The input files have a simple line-based syntax.  Blank lines,
+       and lines beginning with a `#' character, are ignored.  Other
+       lines consist of a keyword followed by arguments, and separated
+       by horizontal whitespace.  There is no quoting, and it is not
+       possible to include horizontal whitespace in an argument.
+
+       An input file describes a number of virtual private networks
+       (`VPNs').  Each VPN consists of a number of locations, and each
+       location consists of a number of sites, thus forming (together
+       with the root) a fixed four-level hierarchy.  The root, VPNs,
+       locations, and sites can each have a number of properties
+       attached to them: each level in the hierarchy has a different
+       set of permissable properties.
+
+       Most keywords define properties on a `current' item in the
+       hierarchy.  Some change which item is current, possibly creating
+       a new item.  A few are special.
+
+       First, the navigation keywords.
+
+       vpn NAME
+               Switch to the VPN called NAME, which is a direct child
+               of the root, creating it if necessary.  Subsequent
+               properties, up until the next navigation keyword, are
+               attached directly to the VPN.
+
+               A VPN item becomes a dictionary named `NAME' within the
+               `PREFIXvpn-data' dictionary in the generated output.
+
+       location NAME [GROUP]
+               Switch to the location called NAME, which is a direct
+               child of the most recently mentioned VPN, creating it if
+               necessary.  The GROUP name may be omitted (and is anyway
+               ignored) if the location already exists.  It is an error
+               if there is no current VPN.  Subsequent properties, up
+               until the next navigation keyword, are attached directly
+               to the location.
+
+               A location item becomes a dictionary named `NAME' within
+               its parent VPN's dictionary in the generated output.
+
+       site NAME
+               Switch to the site called NAME, which is a direct
+               child of the most recently mentioned location, creating
+               it if necessary.  It is an error if there is no current
+               location.  Subsequent properties, up until the next
+               navigation keyword, are attached directly to the site.
+
+               A location item becomes a dictionary named `NAME' within
+               its parent location's dictionary in the generated
+               output.
+
+       Now, the special keywords.
+
+       include FILE
+               Read lines from FILE, as if they'd appeared at this
+               point in the input.  If the FILE name is relative, it is
+               interpreted relative to the directory containing the
+               most recently opened file.  (This seems to be a bug.)
+
+               The `include' keyword is only permitted before the
+               `end-defintions' marker in a HEADER file processed using
+               the `-u' option.
+
+       end-definitions
+               After this keyword, the following restrictions apply.
+
+                 * The `include' keyword can no longer be used.
+
+                 * It is not permitted to define new VPNs and
+                   locations.
+
+                 * It is not permitted to append new items to root,
+                   VPN, and location properties which are already
+                   defined.  (Assigning new properties is permitted.)
+
+                 * It is not permitted to define new VPN-level
+                   properties.
+
+       Finally, the properties.
+
+       Usually, if a property has already been defined on an item, then
+       it is an error to try to redefine it.  But some properties are
+       list-like: the values are accumulated into a single list.
+
+       Mostly, properties are written to corresponding assignments in
+       the generated Secnet configuration file, .  The entries below
+       describe how properties are translated into assignments.
+
+       contact EMAIL
+               Becomes a `Contact address' comment in the output.
+               Acceptable at all levels; required separately at VPN and
+               location levels.
+
+       dh P G
+               Assigns a Diffie--Hellman closure to the `dh' key,
+               constructed as `diffie-hellman(P, G)'. Acceptable at all
+               levels; required at site level.
+
+       hash HASH-NAME
+               Assigns the HASH-NAME to the `hash' key.  The HASH-NAME
+               must be one of `md5' or `sha1', and the corresponding
+               hash closure is used.  Acceptable at all levels;
+               required at site level.
+
+       key-lifetime INT
+       setup-timeout INT
+       setup-retries INT
+       wait-time INT
+       renegotiate-time INT
+               Assign integers to the like-named key.  Acceptable at
+               all levels.
+               
+       restrict-nets NETWORK NETWORK ...
+               This item and its descendents may only define `networks'
+               and `peer' properties with addresses within the listed
+               NETWORKs, each of which has the form IPADDR/MASK, where
+               the IPADDR is an IPv4 address in dotted-quad form, and
+               the MASK is either a netmask in dotted-quad form or a
+               prefix length.  Becomes a comment n the output.
+               Acceptable at all levels.
+
+       networks NETWORK NETWORK ...
+               Assigns a list of NETWORKs to the `routes' key in a
+               netlink application (see below).  See `restrict-nets'
+               for the syntax of a NETWORK.  Acceptable only at site
+               level; required at site level.
+
+       address HOSTNAME PORT
+               Assigns HOSTNAME to the `address' key and PORT (an
+               integer) to the `port' key.  Acceptable only at site
+               level.  May be omitted for mobile sites.
+
+       peer IPADDR
+               Assigns IPADDR to the `ptp-address' key in a netlink
+               application (see below).  IPADDR must be an IPv4 address
+               in dotted-quad form.  Acceptable only at site level;
+               required at site level.
+
+       pubkey HUNOZ E N
+               Assigns a public-key closure to the `key' key,
+               constructed as `rsa-public(E, N)'.  The argument HUNOZ
+               must be an integer, but is otherwise ignored; it's
+               conventionally the length of N in bits.  Acceptable only
+               at site level; required at site level.
+
+       mobile BOOL
+               Assigns BOOL to the `mobile' key.  Acceptable only at
+               site level, but optional.
+
+
+OUTPUT STRUCTURE
+
+       The program produces a Secnet configuration fragment with the
+       structure described below, suitable for inclusion using the
+       `include' keyword.
+
+               PREFIXvpn-data {
+                 VPN {
+                   # Contact email address: EMAIL
+                   [ # restrict-nets: NETWORKS ]
+                   [ VPN-PROPERTIES ]
+                   LOCATION {
+                     # Contact email address: EMAIL
+                     [ # restrict-nets: NETWORKS ]
+                     [ LOCATION-PROPERTIES ]
+                     SITE {
+                       [ # Contact email address: EMAIL ]
+                       [ # restrict-nets: NETWORKS ]
+                       name "VPN/LOCATION/NAME";
+                       SITE-PROPERTIES
+                       link netlink {
+                         routes NETWORK ...;
+                         ptp-address IPADDR;
+                       };
+                     };
+                     [ MORE SITES ... ]
+                   };
+                   [ MORE LOCATIONS ... ]
+                 };
+                 [ MORE VPNS ... ]
+               };
+
+               PREFIXvpn {
+                 VPN {
+                   LOCATION PREFIXvpn-data/VPN/LOCATION/SITE, ...;
+                   [ MORE LOCATIONS ]
+                   all-sites LOCATION, ...;
+                 };
+               };
+
+               PREFIXall-sites PREFIXvpn/VPN/all-sites, ...;
+
+       Note in particular the implicit dependency on a pure closure
+       named `netlink' used to set the `link' key in each site
+       definition.  Usually, this will be constructed by a partial
+       application of the built-in `userv-ipif' or `tun' closures.
diff --git a/Suffix.sd.mk b/Suffix.sd.mk
new file mode 100644 (file)
index 0000000..5daff5e
--- /dev/null
@@ -0,0 +1,5 @@
+&TARGETS_check +=
+&TARGETS_fullcheck +=
+
+&:include subdirmk/cdeps.sd.mk
+&:include subdirmk/clean.sd.mk
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..2d5e447
--- /dev/null
+++ b/TODO
@@ -0,0 +1,34 @@
+dh.c: change format to binary from decimal string (without introducing
+endianness problems)
+
+netlink.c: test the 'allow_route' option properly.
+Add fragmentation code.  Check that we comply with RFC1812.
+
+random.c: test properly
+
+resolver.c: ought to return a list of addresses for each address; the
+site code ought to remember them and try contacting them in turn.
+
+rsa.c: check padding type, change format to binary from decimal string
+(without introducing endianness problems)
+
+site.c: Abandon key exchanges when a bad packet is received. Modify
+protocol to include version fields, as described in the NOTES
+file. Implement keepalive mode. Make policy about when to initiate key
+exchanges more configurable (how many NAKs / bad reverse-transforms
+does it take to prompt a key exchange?)
+
+slip.c: restart userv-ipif to cope with soft routes? Restart it if it
+fails in use?
+
+transform.c: separate the transforms into multiple parts, which can
+then be combined in the configuration file.  Will allow the user to
+plug in different block ciphers, invent an authenticity-only mode,
+etc. (similar to udptunnel)
+
+udp.c: option for path-MTU discovery (once fragmentation support is
+implemented in netlink)
+
+
+global:
+consider using liboop for the event loop
diff --git a/ac_prog_cc_no_writeable_strings.m4 b/ac_prog_cc_no_writeable_strings.m4
new file mode 100644 (file)
index 0000000..ee7aa5d
--- /dev/null
@@ -0,0 +1,143 @@
+dnl @synopsis AC_PROG_CC_NO_WRITEABLE_STRINGS(substvar [,hard])
+dnl
+dnl Try to find a compiler option that warns when a stringliteral is
+dnl used in a place that could potentially modify the address. This
+dnl should warn on giving an stringliteral to a function that asks of
+dnl a non-const-modified char-pointer.
+dnl
+dnl The sanity check is done by looking at string.h which has a set
+dnl of strcpy definitions that should be defined with const-modifiers
+dnl to not emit a warning in all so many places.
+dnl
+dnl Currently this macro knows about GCC.
+dnl hopefully will evolve to use:    Solaris C compiler,
+dnl Digital Unix C compiler, C for AIX Compiler, HP-UX C compiler,
+dnl and IRIX C compiler.
+dnl
+dnl @version $Id: ac_prog_cc_no_writeable_strings.m4,v 1.1 2002/02/20 16:18:18 steve Exp $
+dnl @author Guido Draheim <guidod@gmx.de>
+
+dnl [This appears to be a previous version of
+dnl  ax_cflags_no_writable_strings.m4 which is nowadays to be found in
+dnl  the Autoconf Archive.  It was imported there on 2007-02-14
+dnl  in commit 16aee45643e593e2833e4dff19df7b5f14267a79 where the file
+dnl  has a GPLv2 permission notice.  Therefore I feel justified in
+dnl  adding the copyright permission notice below: -iwj]
+dnl
+dnl  This file is Free Software.  It has been copied into secnet.
+dnl
+dnl  Copyright 2002 Guido Draheim
+dnl
+dnl  You may redistribute secnet as a whole and/or modify it under the
+dnl  terms of the GNU General Public License as published by the Free
+dnl  Software Foundation; either version 3, or (at your option) any
+dnl  later version.
+dnl
+dnl  You may redistribute this file and/or modify it under the terms of
+dnl  the GNU General Public License as published by the Free Software
+dnl  Foundation; either version 2, or (at your option) any later
+dnl  version.
+dnl
+dnl  This software is distributed in the hope that it will be useful,
+dnl  but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl  GNU General Public License for more details.
+dnl
+dnl  You should have received a copy of the GNU General Public License
+dnl  along with this software; if not, see
+dnl  https://www.gnu.org/licenses/gpl.html.
+
+AC_DEFUN([AC_PROG_CC_NO_WRITEABLE_STRINGS], [
+  pushdef([CV],ac_cv_prog_cc_no_writeable_strings)dnl
+  hard=$2
+  if test -z "$hard"; then
+    msg="C to warn about writing to stringliterals"
+  else
+    msg="C to prohibit any write to stringliterals"
+  fi
+  AC_CACHE_CHECK($msg, CV, [
+  cat > conftest.c <<EOF
+#include <string.h>
+int main (void)
+{
+   char test[[16]];
+   if (strcpy (test, "test")) return 0;
+   return 1;
+}
+EOF
+  dnl GCC
+  if test "$GCC" = "yes"; 
+  then
+       if test -z "$hard"; then
+           CV="-Wwrite-strings"
+        else
+            CV="-fno-writable-strings -Wwrite-strings"
+        fi
+
+        if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then
+            CV="suppressed: string.h"
+        fi
+
+  dnl Solaris C compiler
+  elif  $CC -flags 2>&1 | grep "Xc.*strict ANSI C" > /dev/null 2>&1 &&
+       $CC -c -xstrconst conftest.c > /dev/null 2>&1 &&
+       test -f conftest.o 
+  then
+        # strings go into readonly segment
+       CV="-xstrconst"
+
+       rm conftest.o
+        if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then
+             CV="suppressed: string.h"
+        fi
+  
+  dnl HP-UX C compiler
+  elif  $CC > /dev/null 2>&1 &&
+       $CC -c +ESlit conftest.c > /dev/null 2>&1 &&
+       test -f conftest.o 
+  then
+       # strings go into readonly segment
+       CV="+ESlit"
+       
+       rm conftest.o
+        if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then
+             CV="suppressed: string.h"
+        fi
+
+  dnl Digital Unix C compiler
+  elif ! $CC > /dev/null 2>&1 &&
+       $CC -c -readonly_strings conftest.c > /dev/null 2>&1 &&
+       test -f conftest.o
+  then 
+       # strings go into readonly segment
+       CV="-readonly_strings"
+       
+       rm conftest.o
+        if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then
+             CV="suppressed: string.h"
+        fi
+
+  dnl C for AIX Compiler
+
+  dnl IRIX C compiler
+       # -use_readonly_const is the default for IRIX C, 
+       # puts them into .rodata, but they are copied later.
+       # need to be "-G0 -rdatashared" for strictmode but
+       # I am not sure what effect that has really.
+
+  fi
+  rm -f conftest.*
+  ])
+  if test -z "[$]$1" ; then
+    if test -n "$CV" ; then
+      case "$CV" in
+        suppressed*) $1="" ;; # known but suppressed
+        *)  $1="$CV" ;;
+      esac
+    fi
+  fi
+  AC_SUBST($1)
+  popdef([CV])dnl
+])
+
+
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..970fb7d
--- /dev/null
@@ -0,0 +1,35 @@
+# aclocal.m4 - package-specific macros for autoconf
+
+dnl This file is part of secnet.
+dnl See README for full list of copyright holders.
+dnl
+dnl secnet is free software; you can redistribute it and/or modify it
+dnl under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 3 of the License, or
+dnl (at your option) any later version.
+dnl 
+dnl secnet is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+dnl General Public License for more details.
+dnl 
+dnl You should have received a copy of the GNU General Public License
+dnl version 3 along with secnet; if not, see
+dnl https://www.gnu.org/licenses/gpl.html.
+
+dnl This next macro came from adns.git,
+dnl (d8fa191ed7774818862febd6ade774cb7e149ab9).
+define(ADNS_C_GETFUNC,[
+ AC_CHECK_FUNC([$1],,[
+  AC_CHECK_LIB([$2],[$1],[$3],[
+    AC_MSG_ERROR([cannot find library function $1])
+  ])
+ ])
+])
+
+define(SECNET_C_GETFUNC,[
+ ADNS_C_GETFUNC($1,$2,[
+  LIBS="-l$2 $LIBS";
+  AC_MSG_WARN([$1 is in lib$2, urgh.  Must use -l$2.])
+ ])
+])
diff --git a/aes.c b/aes.c
new file mode 100644 (file)
index 0000000..d82c81d
--- /dev/null
+++ b/aes.c
@@ -0,0 +1,1350 @@
+/*
+ * aes.c - implementation of Rijndael
+ */
+/*
+ * This file is Free Software.  It has been modified to as part of its
+ * incorporation into secnet.
+ *
+ * Copyright 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto
+ * Copyright 2004 Fabrice Bellard
+ * Copyright 2013 Ian Jackson
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the permissive licence shown below.
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+/*
+ * Integrated in QEMU by Fabrice Bellard from the OpenSSL project.
+ *
+ * Copied to the secnet tree by Ian Jackson from the upstream qemu git
+ * tree revision 55616505876d6683130076b810a27c7889321560
+ * and modified only to remove the include of qemu-common.h.
+ *
+ * (The changes by various qemu contributors between
+ * e4d4fe3c34cdd6e26f9b9975efec7d1e81ad00b6, where this file appeared
+ * in qemu in a commit by Fabrice Bellard, and 55616505 are too
+ * trivial to attract copyright, which is just as well because some of
+ * the commits are lacking a S-o-b.)
+ */
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS OR CONTRIBUTORS 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.
+ */
+#include "aes.h"
+
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+/* This controls loop-unrolling in aes_core.c */
+#undef FULL_UNROLL
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+static const u32 Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+    0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+    0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+    0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+    0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+    0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+    0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+    0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+    0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+    0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+    0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+    0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+    0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+    0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+    0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+    0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+    0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+    0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+    0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+    0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+    0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+    0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+    0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+    0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+    0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+    0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+    0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+    0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+    0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+    0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+    0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+    0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+    0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+    0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+    0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+    0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+    0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+    0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+    0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+    0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+    0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+    0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+    0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+    0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+    0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+    0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+    0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+    0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+    0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+    0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+    0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+    0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+    0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+    0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+    0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+    0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+    0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+    0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+    0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+    0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+    0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+    0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+    0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+    0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+    0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+    0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+    0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+    0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+    0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+    0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+    0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+    0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+    0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+    0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+    0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+    0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+    0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+    0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+    0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+    0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+    0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+    0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+    0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+    0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+    0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+    0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+    0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+    0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+    0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+    0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+    0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+    0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+    0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+    0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+    0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+    0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+    0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+    0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+    0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+    0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+    0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+    0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+    0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+    0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+    0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+    0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+    0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+    0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+    0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+    0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+    0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+    0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+    0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+    0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+    0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+    0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+    0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+    0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+    0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+    0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+    0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+    0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+    0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+    0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+    0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+    0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+    0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+    0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+    0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+       0x01000000, 0x02000000, 0x04000000, 0x08000000,
+       0x10000000, 0x20000000, 0x40000000, 0x80000000,
+       0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+                       AES_KEY *key) {
+
+       u32 *rk;
+       int i = 0;
+       u32 temp;
+
+       if (!userKey || !key)
+               return -1;
+       if (bits != 128 && bits != 192 && bits != 256)
+               return -2;
+
+       rk = key->rd_key;
+
+       if (bits==128)
+               key->rounds = 10;
+       else if (bits==192)
+               key->rounds = 12;
+       else
+               key->rounds = 14;
+
+       rk[0] = GETU32(userKey     );
+       rk[1] = GETU32(userKey +  4);
+       rk[2] = GETU32(userKey +  8);
+       rk[3] = GETU32(userKey + 12);
+       if (bits == 128) {
+               while (1) {
+                       temp  = rk[3];
+                       rk[4] = rk[0] ^
+                               (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+                               (Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+                               (Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+                               (Te4[(temp >> 24)       ] & 0x000000ff) ^
+                               rcon[i];
+                       rk[5] = rk[1] ^ rk[4];
+                       rk[6] = rk[2] ^ rk[5];
+                       rk[7] = rk[3] ^ rk[6];
+                       if (++i == 10) {
+                               return 0;
+                       }
+                       rk += 4;
+               }
+       }
+       rk[4] = GETU32(userKey + 16);
+       rk[5] = GETU32(userKey + 20);
+       if (bits == 192) {
+               while (1) {
+                       temp = rk[ 5];
+                       rk[ 6] = rk[ 0] ^
+                               (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+                               (Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+                               (Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+                               (Te4[(temp >> 24)       ] & 0x000000ff) ^
+                               rcon[i];
+                       rk[ 7] = rk[ 1] ^ rk[ 6];
+                       rk[ 8] = rk[ 2] ^ rk[ 7];
+                       rk[ 9] = rk[ 3] ^ rk[ 8];
+                       if (++i == 8) {
+                               return 0;
+                       }
+                       rk[10] = rk[ 4] ^ rk[ 9];
+                       rk[11] = rk[ 5] ^ rk[10];
+                       rk += 6;
+               }
+       }
+       rk[6] = GETU32(userKey + 24);
+       rk[7] = GETU32(userKey + 28);
+       if (bits == 256) {
+               while (1) {
+                       temp = rk[ 7];
+                       rk[ 8] = rk[ 0] ^
+                               (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+                               (Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+                               (Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+                               (Te4[(temp >> 24)       ] & 0x000000ff) ^
+                               rcon[i];
+                       rk[ 9] = rk[ 1] ^ rk[ 8];
+                       rk[10] = rk[ 2] ^ rk[ 9];
+                       rk[11] = rk[ 3] ^ rk[10];
+                       if (++i == 7) {
+                               return 0;
+                       }
+                       temp = rk[11];
+                       rk[12] = rk[ 4] ^
+                               (Te4[(temp >> 24)       ] & 0xff000000) ^
+                               (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+                               (Te4[(temp >>  8) & 0xff] & 0x0000ff00) ^
+                               (Te4[(temp      ) & 0xff] & 0x000000ff);
+                       rk[13] = rk[ 5] ^ rk[12];
+                       rk[14] = rk[ 6] ^ rk[13];
+                       rk[15] = rk[ 7] ^ rk[14];
+
+                       rk += 8;
+               }
+       }
+       return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+                        AES_KEY *key) {
+
+        u32 *rk;
+       int i, j, status;
+       u32 temp;
+
+       /* first, start with an encryption schedule */
+       status = AES_set_encrypt_key(userKey, bits, key);
+       if (status < 0)
+               return status;
+
+       rk = key->rd_key;
+
+       /* invert the order of the round keys: */
+       for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+               temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+               temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+               temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+               temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+       }
+       /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+       for (i = 1; i < (key->rounds); i++) {
+               rk += 4;
+               rk[0] =
+                       Td0[Te4[(rk[0] >> 24)       ] & 0xff] ^
+                       Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+                       Td2[Te4[(rk[0] >>  8) & 0xff] & 0xff] ^
+                       Td3[Te4[(rk[0]      ) & 0xff] & 0xff];
+               rk[1] =
+                       Td0[Te4[(rk[1] >> 24)       ] & 0xff] ^
+                       Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+                       Td2[Te4[(rk[1] >>  8) & 0xff] & 0xff] ^
+                       Td3[Te4[(rk[1]      ) & 0xff] & 0xff];
+               rk[2] =
+                       Td0[Te4[(rk[2] >> 24)       ] & 0xff] ^
+                       Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+                       Td2[Te4[(rk[2] >>  8) & 0xff] & 0xff] ^
+                       Td3[Te4[(rk[2]      ) & 0xff] & 0xff];
+               rk[3] =
+                       Td0[Te4[(rk[3] >> 24)       ] & 0xff] ^
+                       Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+                       Td2[Te4[(rk[3] >>  8) & 0xff] & 0xff] ^
+                       Td3[Te4[(rk[3]      ) & 0xff] & 0xff];
+       }
+       return 0;
+}
+
+#ifndef AES_ASM
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+                const AES_KEY *key) {
+
+       const u32 *rk;
+       u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+       int r;
+#endif /* ?FULL_UNROLL */
+
+       assert(in && out && key);
+       rk = key->rd_key;
+
+       /*
+        * map byte array block to cipher state
+        * and add initial round key:
+        */
+       s0 = GETU32(in     ) ^ rk[0];
+       s1 = GETU32(in +  4) ^ rk[1];
+       s2 = GETU32(in +  8) ^ rk[2];
+       s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+       /* round 1: */
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+       /* round 2: */
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+       /* round 3: */
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+       /* round 4: */
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+       /* round 5: */
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+       /* round 6: */
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+       /* round 7: */
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+       /* round 8: */
+       s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+       s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+       s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+       s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+       /* round 9: */
+       t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+       t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+       t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+       t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+        s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+        s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+        s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+        t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+        t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+        t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+            s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+            s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+            s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+            t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+            t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+            t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+        }
+    }
+    rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Te0[(s0 >> 24)       ] ^
+            Te1[(s1 >> 16) & 0xff] ^
+            Te2[(s2 >>  8) & 0xff] ^
+            Te3[(s3      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Te0[(s1 >> 24)       ] ^
+            Te1[(s2 >> 16) & 0xff] ^
+            Te2[(s3 >>  8) & 0xff] ^
+            Te3[(s0      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Te0[(s2 >> 24)       ] ^
+            Te1[(s3 >> 16) & 0xff] ^
+            Te2[(s0 >>  8) & 0xff] ^
+            Te3[(s1      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Te0[(s3 >> 24)       ] ^
+            Te1[(s0 >> 16) & 0xff] ^
+            Te2[(s1 >>  8) & 0xff] ^
+            Te3[(s2      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Te0[(t0 >> 24)       ] ^
+            Te1[(t1 >> 16) & 0xff] ^
+            Te2[(t2 >>  8) & 0xff] ^
+            Te3[(t3      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Te0[(t1 >> 24)       ] ^
+            Te1[(t2 >> 16) & 0xff] ^
+            Te2[(t3 >>  8) & 0xff] ^
+            Te3[(t0      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Te0[(t2 >> 24)       ] ^
+            Te1[(t3 >> 16) & 0xff] ^
+            Te2[(t0 >>  8) & 0xff] ^
+            Te3[(t1      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Te0[(t3 >> 24)       ] ^
+            Te1[(t0 >> 16) & 0xff] ^
+            Te2[(t1 >>  8) & 0xff] ^
+            Te3[(t2      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+        * apply last round and
+        * map cipher state to byte array block:
+        */
+       s0 =
+               (Te4[(t0 >> 24)       ] & 0xff000000) ^
+               (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+               (Te4[(t2 >>  8) & 0xff] & 0x0000ff00) ^
+               (Te4[(t3      ) & 0xff] & 0x000000ff) ^
+               rk[0];
+       PUTU32(out     , s0);
+       s1 =
+               (Te4[(t1 >> 24)       ] & 0xff000000) ^
+               (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+               (Te4[(t3 >>  8) & 0xff] & 0x0000ff00) ^
+               (Te4[(t0      ) & 0xff] & 0x000000ff) ^
+               rk[1];
+       PUTU32(out +  4, s1);
+       s2 =
+               (Te4[(t2 >> 24)       ] & 0xff000000) ^
+               (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+               (Te4[(t0 >>  8) & 0xff] & 0x0000ff00) ^
+               (Te4[(t1      ) & 0xff] & 0x000000ff) ^
+               rk[2];
+       PUTU32(out +  8, s2);
+       s3 =
+               (Te4[(t3 >> 24)       ] & 0xff000000) ^
+               (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+               (Te4[(t1 >>  8) & 0xff] & 0x0000ff00) ^
+               (Te4[(t2      ) & 0xff] & 0x000000ff) ^
+               rk[3];
+       PUTU32(out + 12, s3);
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+                const AES_KEY *key) {
+
+       const u32 *rk;
+       u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+       int r;
+#endif /* ?FULL_UNROLL */
+
+       assert(in && out && key);
+       rk = key->rd_key;
+
+       /*
+        * map byte array block to cipher state
+        * and add initial round key:
+        */
+    s0 = GETU32(in     ) ^ rk[0];
+    s1 = GETU32(in +  4) ^ rk[1];
+    s2 = GETU32(in +  8) ^ rk[2];
+    s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+    /* round 1: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+    /* round 2: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+    /* round 3: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+    /* round 4: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+    /* round 5: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+    /* round 6: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+    /* round 7: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+    /* round 8: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+    /* round 9: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+        s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+        s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+        s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+        t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+        t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+        t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+            s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+            s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+            s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+            t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+            t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+            t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+        }
+    }
+       rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Td0[(s0 >> 24)       ] ^
+            Td1[(s3 >> 16) & 0xff] ^
+            Td2[(s2 >>  8) & 0xff] ^
+            Td3[(s1      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Td0[(s1 >> 24)       ] ^
+            Td1[(s0 >> 16) & 0xff] ^
+            Td2[(s3 >>  8) & 0xff] ^
+            Td3[(s2      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Td0[(s2 >> 24)       ] ^
+            Td1[(s1 >> 16) & 0xff] ^
+            Td2[(s0 >>  8) & 0xff] ^
+            Td3[(s3      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Td0[(s3 >> 24)       ] ^
+            Td1[(s2 >> 16) & 0xff] ^
+            Td2[(s1 >>  8) & 0xff] ^
+            Td3[(s0      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Td0[(t0 >> 24)       ] ^
+            Td1[(t3 >> 16) & 0xff] ^
+            Td2[(t2 >>  8) & 0xff] ^
+            Td3[(t1      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Td0[(t1 >> 24)       ] ^
+            Td1[(t0 >> 16) & 0xff] ^
+            Td2[(t3 >>  8) & 0xff] ^
+            Td3[(t2      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Td0[(t2 >> 24)       ] ^
+            Td1[(t1 >> 16) & 0xff] ^
+            Td2[(t0 >>  8) & 0xff] ^
+            Td3[(t3      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Td0[(t3 >> 24)       ] ^
+            Td1[(t2 >> 16) & 0xff] ^
+            Td2[(t1 >>  8) & 0xff] ^
+            Td3[(t0      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+        * apply last round and
+        * map cipher state to byte array block:
+        */
+       s0 =
+               (Td4[(t0 >> 24)       ] & 0xff000000) ^
+               (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+               (Td4[(t2 >>  8) & 0xff] & 0x0000ff00) ^
+               (Td4[(t1      ) & 0xff] & 0x000000ff) ^
+               rk[0];
+       PUTU32(out     , s0);
+       s1 =
+               (Td4[(t1 >> 24)       ] & 0xff000000) ^
+               (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+               (Td4[(t3 >>  8) & 0xff] & 0x0000ff00) ^
+               (Td4[(t2      ) & 0xff] & 0x000000ff) ^
+               rk[1];
+       PUTU32(out +  4, s1);
+       s2 =
+               (Td4[(t2 >> 24)       ] & 0xff000000) ^
+               (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+               (Td4[(t0 >>  8) & 0xff] & 0x0000ff00) ^
+               (Td4[(t3      ) & 0xff] & 0x000000ff) ^
+               rk[2];
+       PUTU32(out +  8, s2);
+       s3 =
+               (Td4[(t3 >> 24)       ] & 0xff000000) ^
+               (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+               (Td4[(t1 >>  8) & 0xff] & 0x0000ff00) ^
+               (Td4[(t0      ) & 0xff] & 0x000000ff) ^
+               rk[3];
+       PUTU32(out + 12, s3);
+}
+
+#endif /* AES_ASM */
+
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                    const unsigned long length, const AES_KEY *key,
+                    unsigned char *ivec, const int enc)
+{
+
+       unsigned long n;
+       unsigned long len = length;
+       unsigned char tmp[AES_BLOCK_SIZE];
+
+       assert(in && out && key && ivec);
+
+       if (enc) {
+               while (len >= AES_BLOCK_SIZE) {
+                       for(n=0; n < AES_BLOCK_SIZE; ++n)
+                               tmp[n] = in[n] ^ ivec[n];
+                       AES_encrypt(tmp, out, key);
+                       memcpy(ivec, out, AES_BLOCK_SIZE);
+                       len -= AES_BLOCK_SIZE;
+                       in += AES_BLOCK_SIZE;
+                       out += AES_BLOCK_SIZE;
+               }
+               if (len) {
+                       for(n=0; n < len; ++n)
+                               tmp[n] = in[n] ^ ivec[n];
+                       for(n=len; n < AES_BLOCK_SIZE; ++n)
+                               tmp[n] = ivec[n];
+                       AES_encrypt(tmp, tmp, key);
+                       memcpy(out, tmp, AES_BLOCK_SIZE);
+                       memcpy(ivec, tmp, AES_BLOCK_SIZE);
+               }
+       } else {
+               while (len >= AES_BLOCK_SIZE) {
+                       memcpy(tmp, in, AES_BLOCK_SIZE);
+                       AES_decrypt(in, out, key);
+                       for(n=0; n < AES_BLOCK_SIZE; ++n)
+                               out[n] ^= ivec[n];
+                       memcpy(ivec, tmp, AES_BLOCK_SIZE);
+                       len -= AES_BLOCK_SIZE;
+                       in += AES_BLOCK_SIZE;
+                       out += AES_BLOCK_SIZE;
+               }
+               if (len) {
+                       memcpy(tmp, in, AES_BLOCK_SIZE);
+                       AES_decrypt(tmp, tmp, key);
+                       for(n=0; n < len; ++n)
+                               out[n] = tmp[n] ^ ivec[n];
+                       memcpy(ivec, tmp, AES_BLOCK_SIZE);
+               }
+       }
+}
diff --git a/aes.h b/aes.h
new file mode 100644 (file)
index 0000000..23259f6
--- /dev/null
+++ b/aes.h
@@ -0,0 +1,103 @@
+/*
+ * aes.h - Header file declaring AES functions.
+ */
+/*
+ * This file is Free Software.  It has been modified to as part of its
+ * incorporation into secnet.
+ *
+ * Copyright 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto
+ * Copyright 2004 Fabrice Bellard
+ * Copyright 2013 Ian Jackson
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the permissive licence shown below.
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+/*
+  * Copied from the upstream qemu git tree revision
+  *   55616505876d6683130076b810a27c7889321560
+  * but was introduced there by Fabrice Bellard in
+  *   e4d4fe3c34cdd6e26f9b9975efec7d1e81ad00b6
+  *   AES crypto support
+  *   git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1036 \
+  *     c046a42c-6fe2-441c-8c8c-71466251a162
+  *
+  * Modified by Ian Jackson to change the guard #define from
+  * QEMU_AES_H to AES_H and to add some needed system #include's.
+  *
+  * The header file doesn't appear to have a separate copyright notice
+  * but is clearly a lightly edited (by Bellard) version of code from
+  * Rijmen, Bosselaers and Barreto.
+  *
+  * The original is from rijndael-alg-fst.c, with this copyright
+  * notice:
+  *
+  *   rijndael-alg-fst.c
+  *   
+  *   @version 3.0 (December 2000)
+  *   
+  *   Optimised ANSI C code for the Rijndael cipher (now AES)
+  *   
+  *   @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+  *   @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+  *   @author Paulo Barreto <paulo.barreto@terra.com.br>
+  *   
+  *   This code is hereby placed in the public domain.
+  *   
+  *   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS OR CONTRIBUTORS 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.
+  *
+  */
+
+#ifndef AES_H
+#define AES_H
+
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#define AES_MAXNR 14
+#define AES_BLOCK_SIZE 16
+
+struct aes_key_st {
+    uint32_t rd_key[4 *(AES_MAXNR + 1)];
+    int rounds;
+};
+typedef struct aes_key_st AES_KEY;
+
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+       AES_KEY *key);
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+       AES_KEY *key);
+
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+       const AES_KEY *key);
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+       const AES_KEY *key);
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+                    const unsigned long length, const AES_KEY *key,
+                    unsigned char *ivec, const int enc);
+
+#endif /* AES_H */
diff --git a/argparseactionnoyes.py b/argparseactionnoyes.py
new file mode 100644 (file)
index 0000000..a7eef77
--- /dev/null
@@ -0,0 +1,37 @@
+# Python argparse --[no-]foo options
+#
+# Copyright 2012 "Omnifarious" (a user on StackOverFlow)
+# Copyright 2013 "btel" (a user on StackOverFlow)
+#
+# https://stackoverflow.com/questions/9234258/in-python-argparse-is-it-possible-to-have-paired-no-something-something-arg/20422915#20422915
+#
+# CC-BY-SA 4.0
+# by virtue of
+# https://stackoverflow.com/legal/terms-of-service#licensing
+# which says everything is CC-BY-SA and has a link to v4.0
+# (And which is therefore compatible with secnet's GPLv3+)
+#
+# all retrieved 4.11.2019
+
+import argparse
+
+class ActionNoYes(argparse.Action):
+    def __init__(self, option_strings, dest, default=None, required=False, help=None):
+
+        if default is None:
+            raise ValueError('You must provide a default with Yes/No action')
+        if len(option_strings)!=1:
+            raise ValueError('Only single argument is allowed with YesNo action')
+        opt = option_strings[0]
+        if not opt.startswith('--'):
+            raise ValueError('Yes/No arguments must be prefixed with --')
+
+        opt = opt[2:]
+        opts = ['--' + opt, '--no-' + opt]
+        super(ActionNoYes, self).__init__(opts, dest, nargs=0, const=None, 
+                                          default=default, required=required, help=help)
+    def __call__(self, parser, namespace, values, option_strings=None):
+        if option_strings.startswith('--no-'):
+            setattr(namespace, self.dest, False)
+        else:
+            setattr(namespace, self.dest, True)
index 528cfbdab0aeead00ca489c11f2a4ce2de82522e..dddafaca8f72de31a21969dd510fd52942afa824 100755 (executable)
@@ -1,8 +1,22 @@
 #!/bin/sh
-# subdirmk, autogen.sh (conventional autoconf invocation script)
-#  Copyright 2019 Ian Jackson
-# SPDX-License-Identifier: LGPL-2.0-or-later
-# There is NO WARRANTY.
+#
+# This file is part of secnet.
+# See README for full list of copyright holders.
+#
+# secnet is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# secnet 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.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# version 3 along with secnet; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+
 set -e
-cd ${0%/*}
 autoconf
+autoheader
diff --git a/comm-common.c b/comm-common.c
new file mode 100644 (file)
index 0000000..48041cd
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include "comm-common.h"
+
+struct comm_clientinfo *comm_clientinfo_ignore(void *state, dict_t *dict,
+                                              struct cloc cloc)
+{
+    return 0;
+}
+
+void comm_request_notify(void *commst, void *nst, comm_notify_fn *fn)
+{
+    struct commcommon *st=commst;
+    struct comm_notify_entry *n;
+    
+    NEW(n);
+    n->fn=fn;
+    n->state=nst;
+    LIST_INSERT_HEAD(&st->notify, n, entry);
+}
+
+void comm_release_notify(void *commst, void *nst, comm_notify_fn *fn)
+{
+    struct commcommon *st=commst;
+    struct comm_notify_entry *n, *t;
+
+    /* XXX untested */
+    LIST_FOREACH_SAFE(n, &st->notify, entry, t) {
+       if (n->state==nst && n->fn==fn) {
+          LIST_REMOVE(n, entry);
+          free(n);
+       }
+    }
+}
+
+bool_t comm_notify(struct commcommon *cc,
+                  struct buffer_if *buf, const struct comm_addr *ca)
+{
+    struct comm_notify_list *notify = &cc->notify;
+    struct comm_notify_entry *n;
+
+    priomsg_reset(&cc->why_unwanted);
+
+    LIST_FOREACH(n, notify, entry) {
+       if (n->fn(n->state, buf, ca, &cc->why_unwanted)) {
+           return True;
+       }
+    }
+    return False;
+}
+
+void comm_apply(struct commcommon *cc, void *st)
+{
+    assert(cc==st);
+    cc->cl.type=CL_COMM;
+    cc->cl.apply=NULL;
+    cc->cl.interface=&cc->ops;
+    cc->ops.st=cc;
+    cc->ops.request_notify=comm_request_notify;
+    cc->ops.release_notify=comm_release_notify;
+    LIST_INIT(&cc->notify);
+    cc->rbuf=NULL;
+    priomsg_new(&cc->why_unwanted, MAX_NAK_MSG);
+}
diff --git a/comm-common.h b/comm-common.h
new file mode 100644 (file)
index 0000000..45340e4
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef COMM_COMMON_H
+#define COMM_COMMON_H
+
+#include "secnet.h"
+#include "util.h"
+
+/*----- for all comms -----*/
+
+struct comm_notify_entry {
+    comm_notify_fn *fn;
+    void *state;
+    LIST_ENTRY(comm_notify_entry) entry;
+};
+LIST_HEAD(comm_notify_list, comm_notify_entry) notify;
+
+struct commcommon { /* must be first so that void* is comm_common* */
+    closure_t cl;
+    struct comm_if ops;
+    struct cloc loc;
+    struct comm_notify_list notify;
+    struct buffer_if *rbuf;
+    struct priomsg why_unwanted;
+};
+
+struct comm_clientinfo *comm_clientinfo_ignore(void *state, dict_t*,
+                                              struct cloc cloc);
+void comm_request_notify(void *commst, void *nst, comm_notify_fn *fn);
+void comm_release_notify(void *commst, void *nst, comm_notify_fn *fn);
+
+bool_t comm_notify(struct commcommon*, struct buffer_if *buf,
+                  const struct comm_addr *ca);
+  /* Either: returns True, with message delivered and buffer freed.
+   * Or: False, if no-one wanted it - buffer still allocd'd;
+   *     in that case, cc->why_unwanted has info
+   * Ie, roughly like comm_notify_fn. */
+
+void comm_apply(struct commcommon *cc, void *st);
+
+#define COMM_APPLY(st,cc,prefix,desc,loc)              \
+    NEW(st);                                           \
+    (cc)->loc=loc;                                     \
+    (cc)->cl.description=desc;                         \
+    (cc)->ops.clientinfo=comm_clientinfo_ignore;       \
+    (cc)->ops.sendmsg=prefix##sendmsg;                 \
+    (cc)->ops.addr_to_string=prefix##addr_to_string;   \
+    comm_apply((cc),(st))
+   /* void COMM_APPLY(SOMETHING *st, struct commcommon *FUNCTIONOF(st),
+    *                 prefix, "DESC", struct cloc loc);
+    *   // Expects in scope: prefix##sendmsg, prefix##addr_to_string.
+    */
+
+#define COMM_APPLY_STANDARD(st,cc,desc,args)                           \
+    item_t *item=list_elem(args,0);                                    \
+    if (!item || item->type!=t_dict) {                                 \
+       cfgfatal((cc)->loc,desc,"first argument must be a dictionary\n"); \
+    }                                                                  \
+    dict_t *d=item->data.dict;                                         \
+    (cc)->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,desc,(cc)->loc)
+    /* void COMM_APPLY_STANDARD(SOMETHING *st, struct commcommon *cc,
+     *                          const char *desc, list_t *args);
+     *   // Declares:
+     *   //    item_t *item = <undefined>;
+     *   //    dict_t *dict = <construction dictionary argument>;
+     */
+
+/*----- for udp-based comms -----*/
+
+#define UDP_MAX_SOCKETS 3 /* 2 ought to do really */
+
+#define MAX_AF MAX_RAW(AF_INET6,AF_INET)
+
+struct udpsock {
+    union iaddr addr;
+    int fd;
+    bool_t experienced[/*0=recv,1=send*/2][MAX_AF+1][/*success?*/2];
+};
+
+struct udpsocks {
+    int n_socks;
+    struct udpsock socks[UDP_MAX_SOCKETS];
+    /* private for udp_socks_* */
+    struct udpcommon *uc; /* link to parent, for cfg, notify list, etc. */
+    struct poll_interest *interest;
+    const char *desc;
+};
+
+struct udpcommon {
+    struct commcommon cc;
+    int port;
+    string_t authbind;
+    bool_t use_proxy;
+    union iaddr proxy;
+};
+
+bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us,
+                      int failmsgclass);
+  /* Caller should have filled in ->addr.  Fills in us->fd,
+     ->experienced; updates ->addr.  Logs any errors with lg_[v]perror. */
+bool_t udp_import_socket(struct udpcommon *uc, struct udpsock *us,
+                        int failmsgclass, int fd);
+  /* Like udp_make_socket, but caller provides fd.  fd is not closed
+     on error */
+
+void udp_destroy_socket(struct udpcommon *uc, struct udpsock *us);
+  /* Idempotent.  No errors are possible. */
+
+const char *af_name(int af);
+void udp_sock_experienced(struct log_if *lg, struct udpcommon *uc,
+                         struct udpsocks *socks, struct udpsock *us,
+                         const union iaddr *dest, int af /* 0 means any */,
+                         int r, int errnoval);
+
+void udp_socks_register(struct udpcommon *uc, struct udpsocks *socks,
+                       const char *desc);
+void udp_socks_deregister(struct udpcommon *uc, struct udpsocks *socks);
+void udp_socks_childpersist(struct udpcommon *uc, struct udpsocks *socks);
+
+#define UDP_APPLY_STANDARD(st,uc,desc)                                 \
+    (uc)->use_proxy=False;                                             \
+    (uc)->authbind=dict_read_string(d,"authbind",False,"udp",(uc)->cc.loc); \
+    (uc)->port=dict_read_number(d,"port",False,"udp",(uc)->cc.loc,0)
+    /* void UDP_APPLY_STANDARD(SOMETHING *st, struct udpcommon *uc,
+     *                         const char *desc);
+     *   // Expects in scope:  dict_t *d=...;   as from COMM_APPLY_STANDARD
+     */
+
+#endif /*COMM_COMMON_H*/
diff --git a/common.make.in b/common.make.in
new file mode 100644 (file)
index 0000000..7c073b1
--- /dev/null
@@ -0,0 +1,35 @@
+# common makefile settings for secnet
+#
+# This file is part of secnet.
+# See README for full list of copyright holders.
+#
+# secnet is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# secnet 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.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# version 3 along with secnet; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+
+@SET_MAKE@
+
+topbuilddir:=@abs_top_builddir@
+src:=@top_srcdir@
+
+SHELL:=/bin/sh
+CC:=@CC@
+
+CFLAGS:=-Wall @WRITESTRINGS@ @CFLAGS@ -Werror \
+       -W -Wno-unused -Wno-unused-parameter \
+       -Wno-pointer-sign -Wstrict-prototypes -Wmissing-prototypes \
+       -Wmissing-declarations -Wnested-externs -Wredundant-decls \
+       -Wpointer-arith -Wformat=2 -Winit-self \
+       -Wswitch-enum -Wunused-variable -Wunused-function -Wbad-function-cast \
+       -Wno-strict-aliasing -fno-strict-aliasing \
+       -Wno-bool-operation -Wno-stringop-truncation
diff --git a/comprehensive-test b/comprehensive-test
new file mode 100755 (executable)
index 0000000..d8cf07d
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/bash
+set -e
+set -o pipefail
+
+oot_rel=oot-rel.tmp~
+oot_abs=$(cd .. && pwd)/oot-comprehensive-test.tmp~
+
+nproc=$(nproc || echo 1)
+mflags=-j$nproc
+
+for arg in "$@"; do
+    case "$arg" in
+       --oot-abs=*) oot_abs=${arg%*=} ;;
+       *) echo >&2 "unknown arg/option $1"; exit 1;;
+    esac
+done
+
+x () { echo >&2 "x $*"; "$@"; }
+
+srcdir=$(pwd)
+
+build_and_test () {
+    cd "$srcdir"
+    x git clean -xdff
+    if [ "x$1" != x. ]; then
+       rm -rf "$1"
+       mkdir "$1"
+    fi
+    x ./autogen.sh
+    x cd "$1"
+    x "$srcdir/configure" CFLAGS='-O0 -g'
+    x make $mflags all check
+    for t in mtest/check stest/check; do
+       x make $mflags clean
+       x make $mflags $t
+    done
+    x make $mflags clean
+    if [ "x$1" != x. ]; then
+        find -type f
+     else
+        git-ls-files -o
+    fi | perl -ne '
+       s{^\./}{};
+       s{^}{/};
+        next if m{^/ct-files$};
+        next if m{^/autom4te\.cache/};
+        next if m{/Makefile$};
+       next if m{\.mk$};
+       next if m{^/common\.make$};
+       next if m{^/(?:config|\.makefiles)\.stamp$};
+       next if m{^/config\.(?:log|status|h)$};
+        warn "clean in '"$1"' missed $_";
+        $bad=1;
+        END { exit $bad; }
+    '
+    cd "$srcdir"
+}
+
+build_and_test .
+build_and_test "$oot_rel"
+build_and_test "$oot_abs"
+
+echo "----- $0 ok -----"
diff --git a/conffile.c b/conffile.c
new file mode 100644 (file)
index 0000000..c03c1d8
--- /dev/null
@@ -0,0 +1,843 @@
+/* conffile.c - process the configuration file */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+/* #define DUMP_PARSE_TREE */
+
+#include "secnet.h"
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include "conffile.h"
+#include "conffile_internal.h"
+#include "conffile.yy.h"
+#include "util.h"
+#include "ipaddr.h"
+
+static struct cloc no_loc={"none",0};
+
+struct atomlist {
+    struct atomlist *next;
+    atom_t a;
+};
+
+struct entry {
+    struct entry *next;
+    atom_t key;
+    list_t *val;
+};
+
+struct searchlist {
+    struct dict *d;
+    struct searchlist *next;
+};
+
+struct dict {
+    struct dict *parent;
+    struct searchlist *search;
+    struct entry *entries;
+    int32_t size;
+};
+
+static struct atomlist *atoms=NULL;
+
+static void process_alist(dict_t *context, struct p_node *c);
+static list_t *process_invocation(dict_t *context, struct p_node *i);
+
+static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key)
+{
+    struct entry *i;
+    for (i=dict->entries; i; i=i->next) {
+       if (key==i->key) return i->val;
+    }
+    return NULL;
+}
+
+static list_t *dict_ilookup(dict_t *dict, atom_t key)
+{
+    dict_t *d;
+    list_t *v;
+
+    v=dict_ilookup_primitive(dict, key);
+    if (v) return v;
+    /* Check dictionaries in search path */
+/* XXX */
+    /* Check lexical parents */
+    for (d=dict; d; d=d->parent) {
+       v=dict_ilookup_primitive(d, key);
+       if (v) return v;
+    }
+    return NULL;
+}
+
+static void dict_iadd(dict_t *dict, atom_t key, list_t *val)
+{
+    struct entry *e;
+    if (dict_ilookup_primitive(dict, key)) {
+       fatal("duplicate key \"%s\" in dictionary",key);
+    }
+    NEW(e);
+    e->next=dict->entries;
+    e->key=key;
+    e->val=val;
+    dict->entries=e;
+    dict->size++;
+}
+
+/***** Functions beyond this point are private to the config system *****/
+
+static dict_t *dict_new(dict_t *parent)
+{
+    dict_t *d;
+
+    NEW(d);
+    d->parent=parent;
+    d->search=NULL;
+    d->entries=NULL;
+    d->size=0;
+    return d;
+}
+
+static struct p_node *node_copy(struct p_node *n)
+{
+    struct p_node *r;
+    NEW(r);
+    *r=*n;
+    return r;
+}
+
+static struct p_node *list_reverse(struct p_node *list)
+{
+    struct p_node *rl=NULL, *i, *n;
+
+    for (i=list; i; i=i->r) {
+       n=node_copy(i);
+       n->r=rl;
+       rl=n;
+    }
+    return rl;
+}
+
+/* Since we use left-recursion in the parser for efficiency, sequences
+   end up "backwards" in the parse tree. Rather than have complicated
+   code for, eg. processing assignments in the right order, we reverse
+   these sequences here. */
+static void ptree_mangle(struct p_node *t)
+{
+    if (!t) return;
+    ptree_mangle(t->l);
+    ptree_mangle(t->r);
+    switch (t->type) {
+    case T_DICT:
+       ASSERT(!t->l || t->l->type==T_ALIST);
+       ASSERT(!t->r || t->r->type==T_LISTITEM);
+       t->l=list_reverse(t->l);
+       t->r=list_reverse(t->r);
+       break;
+    case T_ASSIGNMENT:
+       ASSERT(t->l->type==T_KEY);
+       ASSERT(t->r->type==T_LISTITEM);
+       t->r=list_reverse(t->r);
+       break;
+    case T_ABSPATH:
+    case T_RELPATH:
+       ASSERT(t->l==NULL);
+       ASSERT(t->r->type==T_PATHELEM);
+       t->r=list_reverse(t->r);
+       break;
+    case T_EXEC:
+       ASSERT(t->l);
+       ASSERT(t->r==NULL || t->r->type==T_LISTITEM);
+       t->r=list_reverse(t->r);
+       break;
+    }
+}
+
+#ifdef DUMP_PARSE_TREE
+/* Convert a node type to a string, for parse tree dump */
+static const char *ntype(uint32_t type)
+{
+    switch(type) {
+    case T_STRING:     return "T_STRING";
+    case T_NUMBER:     return "T_NUMBER";
+    case T_KEY:        return "T_KEY";
+    case T_ASSIGNMENT: return "T_ASSIGNMENT";
+    case T_LISTITEM:   return "T_LISTITEM";
+    case T_EXEC:       return "T_EXEC";
+    case T_PATHELEM:   return "T_PATHELEM";
+    case T_ABSPATH:    return "T_ABSPATH";
+    case T_RELPATH:    return "T_RELPATH";
+    case T_DICT:       return "T_DICT";
+    case T_ALIST:      return "T_ALIST";
+    case T_ERROR:      return "T_ERROR";
+    }
+    return "**unknown**";
+}
+
+static void ptree_indent(int amount)
+{
+    int i;
+    for (i=0; i<amount; i++) printf("  . ");
+}
+
+static void ptree_dump(struct p_node *n, int d)
+{
+    if (!n) {
+       printf("NULL\n");
+       return;
+    }
+    
+    if (T_IS_PRIMITIVE(n->type)) {
+       switch(n->type) {
+       case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n",
+                             n->data.string,n->loc.file,n->loc.line); break;
+       case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n",
+                             n->data.number, n->loc.file,n->loc.line); break;
+       case T_KEY:    printf("T_KEY:    %s (%s line %d)\n",
+                             n->data.key, n->loc.file,n->loc.line); break;
+       default:       printf("**unknown primitive type**\n"); break;
+       }
+    } else {
+       assert(d<10000);
+       printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line);
+       ptree_indent(d);
+       printf("  |-"); ptree_dump(n->l, d+1);
+       ptree_indent(d);
+       printf("  +-"); ptree_dump(n->r, d+1);
+    }
+}
+
+#endif /* DUMP_PARSE_TREE */
+
+static dict_t *dict_find_root(dict_t *d)
+{
+    dict_t *i;
+
+    for (i=d; i->parent; i=i->parent);
+    return i;
+}
+
+static list_t *dict_lookup_path(dict_t *context, struct p_node *p)
+{
+    dict_t *i;
+    list_t *l;
+
+    ASSERT(p->type==T_PATHELEM);
+    ASSERT(p->l->type==T_KEY);
+    l=dict_ilookup(context, p->l->data.key);
+    if (!l) {
+       cfgfatal(p->loc,"conffile","can't find key %s\n",
+                p->l->data.key);
+    }
+
+    while (p->r) {
+       if (l->item->type != t_dict) {
+           cfgfatal(p->loc,"conffile","path element \"%s\" "
+                    "is not a dictionary\n",p->l->data.key);
+       }
+       i=l->item->data.dict; /* First thing in list */
+
+       p=p->r;
+       l=dict_ilookup_primitive(i, p->l->data.key);
+       if (!l) {
+           cfgfatal(p->loc,"conffile","can't find key %s\n",
+                    p->l->data.key);
+       }
+    }
+    return l;
+}
+
+static item_t *new_item(enum types type, struct cloc loc)
+{
+    item_t *i;
+
+    NEW(i);
+    i->type=type;
+    i->loc=loc;
+    return i;
+}
+
+static list_t *process_item(dict_t *context, struct p_node *i)
+{
+    item_t *item=NULL;
+
+    switch (i->type) {
+    case T_STRING:
+       item=new_item(t_string, i->loc);
+       item->data.string=i->data.string; /* XXX maybe strcpy */
+       break;
+    case T_NUMBER:
+       item=new_item(t_number, i->loc);
+       item->data.number=i->data.number;
+       break;
+    case T_ABSPATH:
+       context=dict_find_root(context);
+       /* falls through */
+    case T_RELPATH:
+       return dict_lookup_path(context, i->r);
+       /* returns immediately */
+       break;
+    case T_DICT:
+       item=new_item(t_dict, i->loc);
+       item->data.dict=dict_new(context);
+/* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */
+       process_alist(item->data.dict, i->l);
+       break;
+    case T_EXEC:
+       return process_invocation(context, i);
+       /* returns immediately */
+       break;
+    default:
+#ifdef DUMP_PARSE_TREE
+       ptree_dump(i,0);
+       fatal("process_item: invalid node type for a list item (%s)",
+             ntype(i->type));
+#else
+       fatal("process_item: list item has invalid node type %d - recompile "
+             "with DUMP_PARSE_TREE defined in conffile.c for more "
+             "detailed debug output",i->type);
+#endif /* DUMP_PARSE_TREE */
+       break;
+    }
+    return list_append(NULL,item);
+}
+
+static list_t *process_ilist(dict_t *context, struct p_node *l)
+{
+    struct p_node *i;
+    list_t *r;
+
+    ASSERT(!l || l->type==T_LISTITEM);
+
+    r=list_new();
+
+    for (i=l; i; i=i->r) {
+       r=list_append_list(r,process_item(context,i->l));
+    }
+    return r;
+}
+       
+static list_t *process_invocation(dict_t *context, struct p_node *i)
+{
+    list_t *cll;
+    item_t *cl;
+    list_t *args;
+
+    ASSERT(i->type==T_EXEC);
+    ASSERT(i->r==NULL || i->r->type==T_LISTITEM);
+    cll=process_item(context,i->l);
+    cl=cll->item;
+    if (cl->type != t_closure) {
+       cfgfatal(i->l->loc,"conffile","only closures can be invoked\n");
+    }
+    if (!cl->data.closure->apply) {
+       cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n");
+    }
+    args=process_ilist(context, i->r);
+    return cl->data.closure->apply(cl->data.closure, i->loc, context, args);
+}
+
+static void process_alist(dict_t *context, struct p_node *c)
+{
+    struct p_node *i;
+    atom_t k;
+    list_t *l;
+
+    if (!c) return; /* NULL assignment lists are valid (empty dictionary) */
+
+    ASSERT(c->type==T_ALIST);
+    if (c->type!=T_ALIST) {
+       fatal("invalid node type in assignment list");
+    }
+
+    for (i=c; i; i=i->r) {
+       ASSERT(i->l && i->l->type==T_ASSIGNMENT);
+       ASSERT(i->l->l->type==T_KEY);
+       ASSERT(i->l->r->type==T_LISTITEM);
+       k=i->l->l->data.key;
+       l=process_ilist(context, i->l->r);
+       dict_iadd(context, k, l);
+    }
+}
+
+/* Take a list of items; turn any dictionaries in this list into lists */
+static list_t *makelist(closure_t *self, struct cloc loc,
+                       dict_t *context, list_t *args)
+{
+    list_t *r=NULL, *i;
+    struct entry *e;
+    
+    for (i=args; i; i=i->next) {
+       if (i->item->type==t_dict) {
+           /* Convert */
+           for (e=i->item->data.dict->entries; e; e=e->next) {
+               r=list_append_list(r, e->val);
+           }
+       } else {
+           r=list_append_list(r, list_append(NULL,i->item));
+       }
+    }
+    return r;
+}
+
+/* Take a list consisting of a closure and some other things. Apply the
+   closure to the other things, and return the resulting list */
+static list_t *map(closure_t *self, struct cloc loc, dict_t *context,
+                  list_t *args)
+{
+    list_t *r=NULL, *al;
+    item_t *ci;
+    closure_t *cl;
+    list_t se;
+    
+    ci=list_elem(args,0);
+    if (ci && ci->type==t_closure) {
+       cl=ci->data.closure;
+       if (!cl->apply) {
+           cfgfatal(loc,"map","closure cannot be applied\n");
+       }
+       for (al=args->next; al; al=al->next) {
+           /* Construct a single-element list */
+           se.next=NULL;
+           se.item=al->item;
+           /* Invoke the closure, append its result to the output */
+           r=list_append_list(r,cl->apply(cl,loc,context,&se));
+       }
+    } else {
+       cfgfatal(loc,"map","you must supply a closure as the "
+                "first argument\n");
+    }
+    return r;
+}
+
+/* Read a file and turn it into a string */
+static list_t *readfile(closure_t *self, struct cloc loc,
+                       dict_t *context, list_t *args)
+{
+    FILE *f;
+    string_t filename;
+    long length;
+    item_t *r;
+
+    r=list_elem(args,0);
+    if (!r) {
+       cfgfatal(loc,"readfile","you must supply a filename\n");
+    }
+    if (r->type!=t_string) {
+       cfgfatal(loc,"readfile","filename must be a string\n");
+    }
+    filename=r->data.string;
+    f=fopen(filename,"rb");
+    if (!f) {
+       fatal_perror("readfile (%s:%d): cannot open file \"%s\"",
+                    loc.file,loc.line, filename);
+    }
+    if (fseek(f, 0, SEEK_END)!=0) {
+       fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line);
+    }
+    length=ftell(f);
+    if (length<0) {
+       fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line);
+    }
+    if (fseek(f, 0, SEEK_SET)!=0) {
+       fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line);
+    }
+    r=new_item(t_string,loc);
+    r->data.string=safe_malloc(length+1,"readfile");
+    if (fread(r->data.string,length,1,f)!=1) {
+       (ferror(f) ? fatal_perror : fatal)
+           ("readfile (%s:%d): fread: could not read all of file",
+            loc.file,loc.line);
+    }
+    r->data.string[length]=0;
+    if (fclose(f)!=0) {
+       fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line);
+    }
+    return list_append(NULL,r);
+}
+    
+static dict_t *process_config(struct p_node *c)
+{
+    dict_t *root;
+    dict_t *context;
+    item_t *i;
+    list_t *false;
+    list_t *true;
+
+    root=dict_new(NULL);
+    context=root;
+
+    /* Predefined keys for boolean values */
+    /* "nowise" and "verily" have the advantage of being the same
+       length, so they line up nicely...  thanks VKC and SGT (who also
+       point out that "mayhap" is a good "maybe" value as well) */
+    i=new_item(t_bool,no_loc);
+    i->data.bool=False;
+    false=list_append(NULL,i);
+    i=new_item(t_bool,no_loc);
+    i->data.bool=True;
+    true=list_append(NULL,i);
+    dict_add(root,"false",false);
+    dict_add(root,"False",false);
+    dict_add(root,"FALSE",false);
+    dict_add(root,"no",false);
+    dict_add(root,"No",false);
+    dict_add(root,"NO",false);
+    dict_add(root,"nowise",false);
+    dict_add(root,"Nowise",false);
+    dict_add(root,"NOWISE",false);
+    dict_add(root,"true",true);
+    dict_add(root,"True",true);
+    dict_add(root,"TRUE",true);
+    dict_add(root,"yes",true);
+    dict_add(root,"Yes",true);
+    dict_add(root,"YES",true);
+    dict_add(root,"verily",true);
+    dict_add(root,"Verily",true);
+    dict_add(root,"VERILY",true);
+
+    add_closure(root,"makelist",makelist);
+    add_closure(root,"readfile",readfile);
+    add_closure(root,"map",map);
+
+    init_builtin_modules(root);
+
+    process_alist(context, c);
+
+    return root;
+}
+
+/***** Externally accessible functions */
+
+atom_t intern(cstring_t s)
+{
+    struct atomlist *i;
+
+    for (i=atoms; i; i=i->next) {
+       if (strcmp(i->a, s)==0) break;
+    }
+
+    if (!i) {
+       /* Did't find it; create a new one */
+       NEW(i);
+       i->a=safe_strdup(s,"intern: alloc string");
+       i->next=atoms;
+       atoms=i;
+    }
+    return i->a;
+}
+
+list_t *dict_lookup(dict_t *dict, cstring_t key)
+{
+    return dict_ilookup(dict, intern(key));
+}
+
+list_t *dict_lookup_primitive(dict_t *dict, cstring_t key)
+{
+    return dict_ilookup_primitive(dict, intern(key));
+}
+
+void dict_add(dict_t *dict, cstring_t key, list_t *val)
+{
+    dict_iadd(dict,intern(key),val);
+}
+
+cstring_t *dict_keys(dict_t *dict)
+{
+    atom_t *r, *j;
+    struct entry *i;
+    NEW_ARY(r,dict->size+1);
+    for (i=dict->entries, j=r; i; i=i->next, j++) {
+       *j=i->key;
+    }
+    *j=NULL;
+    return r;
+}
+
+
+/* List-related functions */
+
+list_t *list_new(void)
+{
+    return NULL;
+}
+
+int32_t list_length(const list_t *a)
+{
+    int32_t l=0;
+    const list_t *i;
+    for (i=a; i; i=i->next) { assert(l < INT_MAX); l++; }
+    return l;
+}
+
+static list_t *list_copy(list_t *a)
+{
+    list_t *r, *i, *b, *l;
+
+    if (!a) return NULL;
+    l=NULL;
+    r=NULL;
+    for (i=a; i; i=i->next) {
+       NEW(b);
+       if (l) l->next=b; else r=b;
+       l=b;
+       b->item=i->item;
+       b->next=NULL;
+    }
+    return r;
+}
+
+list_t *list_append_list(list_t *a, list_t *b)
+{
+    list_t *i;
+
+    b=list_copy(b);
+    if (!a) return b;
+    for (i=a; i->next; i=i->next);
+    i->next=b;
+    return a;
+}
+
+list_t *list_append(list_t *list, item_t *item)
+{
+    list_t *l;
+
+    NEW(l);
+    l->item=item;
+    l->next=NULL;
+
+    return list_append_list(list,l);
+}
+
+item_t *list_elem(list_t *l, int32_t index)
+{
+    if (!l) return NULL;
+    if (index==0) return l->item;
+    return list_elem(l->next, index-1);
+}
+
+list_t *new_closure(closure_t *cl)
+{
+    item_t *i;
+
+    i=new_item(t_closure,no_loc);
+    i->data.closure=cl;
+    return list_append(NULL,i);
+}
+
+void add_closure(dict_t *dict, cstring_t name, apply_fn apply)
+{
+    closure_t *c;
+    NEW(c);
+    c->description=name;
+    c->type=CL_PURE;
+    c->apply=apply;
+    c->interface=NULL;
+
+    dict_add(dict,name,new_closure(c));
+}
+
+void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
+                bool_t fail_if_invalid, cstring_t desc, struct cloc loc)
+{
+    item_t *i;
+    closure_t *cl;
+
+    i = dict_find_item(dict,name,fail_if_invalid,desc,loc);
+    if (i->type!=t_closure) {
+       if (!fail_if_invalid) return NULL;
+       cfgfatal(loc,desc,"\"%s\" must be a closure\n",name);
+    }
+    cl=i->data.closure;
+    if (cl->type!=type) {
+       if (!fail_if_invalid) return NULL;
+       cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",name);
+    }
+    return cl->interface;
+}
+
+/* Convenience functions for modules reading configuration dictionaries */
+item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
+                      cstring_t desc, struct cloc loc)
+{
+    list_t *l;
+    item_t *i;
+
+    l=dict_lookup(dict,key);
+    if (!l) {
+       if (!required) return NULL;
+       cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
+    }
+    if(list_length(l) != 1)
+       cfgfatal(loc,desc,"parameter \"%s\" has wrong number of values",key);
+    i=list_elem(l,0);
+    return i;
+}
+
+string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
+                         cstring_t desc, struct cloc loc)
+{
+    item_t *i;
+    string_t r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return NULL;
+    if (i->type!=t_string) {
+       cfgfatal(loc,desc,"\"%s\" must be a string\n",key);
+    }
+    if (strlen(i->data.string) > INT_MAX/10) {
+       cfgfatal(loc,desc,"\"%s\" is unreasonably long\n",key);
+    }
+    r=i->data.string;
+    return r;
+}
+
+const char **dict_read_string_array(dict_t *dict, cstring_t key,
+                                   bool_t required, cstring_t desc,
+                                   struct cloc loc, const char *const *def)
+{
+    list_t *l;
+    const char **ra, **rap;
+
+    l=dict_lookup(dict,key);
+    if (!l) {
+       if (!required) return (const char**)def;
+       cfgfatal(loc,desc,"required string list \"%s\" not found\n",key);
+    }
+
+    int32_t ll=list_length(l);
+    NEW_ARY(ra, ll+1);
+    for (rap=ra; l; l=l->next,rap++) {
+       item_t *it=l->item;
+       if (it->type!=t_string)
+           cfgfatal(it->loc,desc,"\"%s\" entry must be a string\n",key);
+       *rap=it->data.string;
+    }
+    *rap=0;
+    return ra;
+}
+
+uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
+                         cstring_t desc, struct cloc loc, uint32_t def)
+{
+    item_t *i;
+    uint32_t r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return def;
+    if (i->type!=t_number) {
+       cfgfatal(loc,desc,"\"%s\" must be a number\n",key);
+    }
+    if (i->data.number >= 0x80000000) {
+        cfgfatal(loc,desc,"\"%s\" must fit into a 32-bit signed integer\n",key);
+    }
+    r=i->data.number;
+    return r;
+}
+
+bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
+                     cstring_t desc, struct cloc loc, bool_t def)
+{
+    item_t *i;
+    bool_t r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return def;
+    if (i->type!=t_bool) {
+       cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key);
+    }
+    r=i->data.bool;
+    return r;
+}
+
+dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required,
+                      cstring_t desc, struct cloc loc)
+{
+    item_t *i;
+    dict_t *r;
+
+    i=dict_find_item(dict,key,required,desc,loc);
+    if (!i) return NULL;
+    if (i->type!=t_dict) {
+       cfgfatal(loc,desc,"\"%s\" must be a dictionary\n",key);
+    }
+    r=i->data.dict;
+    return r;
+}
+
+uint32_t string_to_word(cstring_t s, struct cloc loc,
+                       struct flagstr *f, cstring_t desc)
+{
+    struct flagstr *j;
+    for (j=f; j->name; j++)
+       if (strcmp(s,j->name)==0)
+           return j->value;
+    cfgfatal(loc,desc,"option \"%s\" not known\n",s);
+    return 0;
+}
+
+uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc)
+{
+    list_t *i;
+    uint32_t r=0;
+    struct flagstr *j;
+
+    for (i=l; i; i=i->next) {
+       if (i->item->type!=t_string) {
+           cfgfatal(i->item->loc,desc,"all elements of list must be "
+                    "strings\n");
+       }
+       for (j=f; j->name; j++)
+           r|=string_to_word(i->item->data.string,i->item->loc,f,desc);
+    }
+    return r;
+}
+
+dict_t *read_conffile(const char *name)
+{
+    FILE *conffile;
+    struct p_node *config;
+
+    if (strcmp(name,"-")==0) {
+       conffile=stdin;
+    } else {
+       conffile=fopen(name,"r");
+       if (!conffile)
+           fatal_perror("Cannot open configuration file \"%s\"",name);
+    }
+    config_lineno=1;
+    config_file=name;
+    config=parse_conffile(conffile);
+    fclose(conffile);
+
+#ifdef DUMP_PARSE_TREE
+    printf("*** config file parse tree BEFORE MANGLE\n");
+    ptree_dump(config,0);
+#endif /* DUMP_PARSE_TREE */
+    /* The root of the configuration is a T_ALIST, which needs reversing
+       before we mangle because it isn't the child of a T_DICT. */
+    config=list_reverse(config);
+    ptree_mangle(config);
+#ifdef DUMP_PARSE_TREE
+    printf("\n\n*** config file parse tree AFTER MANGLE\n");
+    ptree_dump(config,0);
+#endif /* DUMP_PARSE_TREE */
+    return process_config(config);
+}
diff --git a/conffile.fl b/conffile.fl
new file mode 100644 (file)
index 0000000..6b0259c
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+/* the "incl" state is used for picking up the name of an include file */
+%x incl
+
+%option nounput
+%option noinput
+%option never-interactive
+%option noyywrap
+
+%{
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "conffile_internal.h"
+#include "conffile.tab.h"
+#include "util.h"
+
+#define YY_NO_UNPUT
+
+#define YY_INPUT(buf,result,max_size)                                  \
+do{                                                                    \
+       (result)= fread((buf),1,(max_size),yyin);                       \
+       if (ferror(yyin))                                               \
+               fatal_perror("Error reading configuration file (%s)",   \
+                            config_file);                              \
+}while(0)
+
+#define MAX_INCLUDE_DEPTH 10
+struct include_stack_item {
+       YY_BUFFER_STATE bst;
+       int lineno;
+       cstring_t file;
+};
+struct include_stack_item include_stack[MAX_INCLUDE_DEPTH];
+int include_stack_ptr=0;
+
+int config_lineno=0;
+cstring_t config_file="xxx";
+
+static struct p_node *leafnode(uint32_t type)
+{
+       struct p_node *r;
+
+       NEW(r);
+       r->type=type;
+       r->loc.file=config_file;
+       r->loc.line=config_lineno;
+       r->l=NULL; r->r=NULL;
+       return r;
+}
+
+static struct p_node *keynode(atom_t key)
+{
+       struct p_node *r;
+       r=leafnode(T_KEY);
+       r->data.key=intern(key);
+       return r;
+}
+
+static struct p_node *stringnode(string_t string)
+{
+       struct p_node *r;
+       r=leafnode(T_STRING);
+       string++;
+       string[strlen(string)-1]=0;
+       r->data.string=safe_strdup(string,"stringnode");
+       return r;
+}
+
+static struct p_node *numnode(string_t number)
+{
+       struct p_node *r;
+       unsigned long n;
+       r=leafnode(T_NUMBER);
+       errno = 0;
+       n = strtoul(number, NULL, 10);
+       /* The caller is expected to only give us [0-9]+,
+        * so we skip some of the usual syntax checking. */
+       r->data.number=n;
+       /* Give a consistent error message for any kind of
+        * out-of-range condition */
+       if(errno == ERANGE || n != r->data.number) {
+           Message(M_FATAL,"config file %s line %d: '%s' is too big\n",
+                   config_file, config_lineno, number);
+           exit(1);
+       }
+       if(errno) {
+           Message(M_FATAL,"config file %s line %d: '%s': %s\n",
+                   config_file, config_lineno, number, strerror(errno));
+           exit(1);
+       }
+       return r;
+}
+
+%}
+
+%%
+include                        BEGIN(incl);
+<incl>[ \t]*           /* eat the whitespace */
+<incl>[^ \t\n]+                { /* got the include filename */
+       if (include_stack_ptr >= MAX_INCLUDE_DEPTH) {
+               fatal("Configuration file includes nested too deeply");
+       }
+       include_stack[include_stack_ptr].bst=YY_CURRENT_BUFFER;
+       include_stack[include_stack_ptr].lineno=config_lineno;
+       include_stack[include_stack_ptr].file=config_file;
+       include_stack_ptr++;
+       yyin=fopen(yytext,"r");
+       if (!yyin) {
+               fatal("Can't open included file %s",yytext);
+       }
+       config_lineno=1;
+       config_file=safe_strdup(yytext,"conffile.fl/include");
+       yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+       BEGIN(INITIAL);
+       }
+<incl>\n               { /* include with no filename */
+       Message(M_FATAL,"config file %s line %d: %s\n",config_file,
+               config_lineno,"``include'' requires a filename");
+       BEGIN(INITIAL);
+       assert(config_lineno < INT_MAX);
+       ++config_lineno;
+       ++yynerrs;
+}
+
+<<EOF>>                {
+       if (--include_stack_ptr < 0) {
+               yyterminate();
+               }
+       else {
+               fclose(yyin);
+               yy_delete_buffer(YY_CURRENT_BUFFER);
+               yy_switch_to_buffer(include_stack[include_stack_ptr].bst);
+               config_lineno=include_stack[include_stack_ptr].lineno;
+               config_file=include_stack[include_stack_ptr].file;
+       }
+       }
+\"[^\"]*\"             yylval=stringnode(yytext); return TOK_STRING;
+
+[[:alpha:]_][[:alnum:]\-_]*    yylval=keynode(yytext); return TOK_KEY;
+
+[[:digit:]]+           yylval=numnode(yytext); return TOK_NUMBER;
+
+       /* Eat comments */
+\#.*\n                 config_lineno++;
+       /* Count lines */
+\n                     config_lineno++;
+       /* Eat whitespace */
+[[:blank:]\j]
+
+       /* Return all unclaimed single characters to the parser */
+.                      return *yytext;
diff --git a/conffile.h b/conffile.h
new file mode 100644 (file)
index 0000000..c4834b1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef conffile_h
+#define conffile_h
+
+#include "secnet.h"
+
+extern dict_t *read_conffile(const char *conffile);
+
+#endif /* conffile_h */
diff --git a/conffile.y b/conffile.y
new file mode 100644 (file)
index 0000000..9f32cfd
--- /dev/null
@@ -0,0 +1,113 @@
+%token TOK_STRING
+%token TOK_NUMBER
+%token TOK_KEY
+
+%start input
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+/* Bison stupidly redeclares malloc/free unless they are #defined
+ * (or a bunch of madder conditions) */
+#ifndef malloc
+# define malloc malloc
+# define free free
+#endif
+#include "secnet.h"
+#include "conffile_internal.h"
+#include "conffile.yy.h"
+#include "util.h"
+#define YYERROR_VERBOSE
+
+static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r);
+
+static struct p_node *result;
+
+static void yyerror(const char *s);
+
+%}
+
+%%
+
+input:           assignments { result = $1; $$=result; }
+               ;
+
+assignments:     assignments assignment { $$=node(T_ALIST, $2, $1); }
+               | assignment { $$=node(T_ALIST, $1, NULL); }
+               ;
+
+searchpath:      /* empty */ { $$ = NULL; }
+               | '<' list '>' { $$ = $2; }
+               ;
+
+dict:            searchpath '{' assignments '}'
+       { $$ = node(T_DICT, $3, $1); }
+               | searchpath '{' '}' { $$ = node(T_DICT, NULL, $1); }
+               ;
+
+path:            '/' pathelements { $$ = node(T_ABSPATH, NULL, $2); }
+               | pathelements { $$ = node(T_RELPATH, NULL, $1); }
+               ;
+
+pathelements:    pathelements '/' TOK_KEY { $$ = node(T_PATHELEM, $3, $1); }
+               | TOK_KEY { $$ = node(T_PATHELEM, $1, NULL); }
+               ;
+
+exec:            item '(' list ')' { $$ = node(T_EXEC, $1, $3); }
+               | item '(' ')' { $$ = node(T_EXEC, $1, NULL); }
+               | item dict
+       { $$ = node(T_EXEC, $1, node(T_LISTITEM, $2, NULL)); }
+               ;
+
+list:            list ',' item { $$ = node(T_LISTITEM, $3, $1); }
+               | item { $$ = node(T_LISTITEM, $1, NULL); }
+               ;
+
+assignment:      TOK_KEY '=' list ';' { $$ = node(T_ASSIGNMENT, $1, $3); }
+               | TOK_KEY list ';' { $$ = node(T_ASSIGNMENT, $1, $2); }
+               | error ';' { $$ = node(T_ERROR, NULL, NULL); }
+               | error '}' { $$ = node(T_ERROR, NULL, NULL); }
+               | error ')' { $$ = node(T_ERROR, NULL, NULL); }
+               ;
+
+item:            TOK_STRING
+               | TOK_NUMBER
+               | path
+               | dict
+               | exec
+               ;
+
+%%
+
+static void yyerror(const char *s)
+{
+       Message(M_FATAL,"config file %s line %d: %s\n",config_file,
+               config_lineno,s);
+}
+
+struct p_node *parse_conffile(FILE *conffile)
+{
+       yyin=conffile;
+       if (yyparse()!=0) {
+               fatal("Configuration file parsing failed\n");
+       }
+       if (yynerrs>0) {
+               fatal("%d error%s encountered in configuration file\n",
+               yynerrs,yynerrs==1?"":"s");
+       }
+       return result;
+}
+
+static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r)
+{
+       struct p_node *rv;
+
+       NEW(rv);
+       rv->type=type;
+       rv->loc.file=config_file;
+       rv->loc.line=config_lineno;
+       rv->l=l;
+       rv->r=r;
+       return rv;
+}
diff --git a/conffile_internal.h b/conffile_internal.h
new file mode 100644 (file)
index 0000000..c2127ab
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef conffile_internal_h
+#define conffile_internal_h
+
+#include <stdio.h>
+#include "secnet.h"
+
+typedef cstring_t atom_t;
+
+/* Parse tree for configuration file */
+
+#define YYSTYPE struct p_node *
+
+#define T_STRING 1
+#define T_NUMBER 2
+#define T_KEY    3
+#define T_ASSIGNMENT 10
+#define T_LISTITEM   11
+#define T_EXEC       12
+#define T_PATHELEM   13
+#define T_ABSPATH    14
+#define T_RELPATH    15
+#define T_DICT       16
+#define T_ALIST      17
+#define T_ERROR      20
+
+#define T_IS_PRIMITIVE(NTYPE) ((NTYPE) < T_ASSIGNMENT)
+
+struct p_node {
+    uint32_t type;
+    struct cloc loc;
+    union {
+       atom_t key;
+       string_t string;
+       uint32_t number;
+    } data;
+    struct p_node *l;
+    struct p_node *r;
+};
+
+extern cstring_t config_file;
+extern int config_lineno;
+extern int yynerrs;
+
+/* Keys in dictionaries are 'atoms', which are constructed from strings
+   using this call. Atoms may be compared using '=='. */
+extern atom_t intern(cstring_t string);
+
+extern struct p_node *parse_conffile(FILE *conffile);
+
+#endif /* conffile_internal_h */
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..a59a02c
--- /dev/null
@@ -0,0 +1,183 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 to use IPv6 support in system and adns */
+#undef CONFIG_IPV6
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `adns' library (-ladns). */
+#undef HAVE_LIBADNS
+
+/* Define to 1 if you have the `gmp' library (-lgmp). */
+#undef HAVE_LIBGMP
+
+/* Define to 1 if you have the `gmp2' library (-lgmp2). */
+#undef HAVE_LIBGMP2
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define to 1 if you have the <linux/if_tun.h> header file. */
+#undef HAVE_LINUX_IF_TUN_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#undef HAVE_NET_IF_H
+
+/* Define to 1 if you have the <net/if_tun.h> header file. */
+#undef HAVE_NET_IF_TUN_H
+
+/* Define to 1 if you have the <net/route.h> header file. */
+#undef HAVE_NET_ROUTE_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <stropts.h> header file. */
+#undef HAVE_STROPTS_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+#undef HAVE_SYS_SOCKIO_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* The size of `unsigned char', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_CHAR
+
+/* The size of `unsigned int', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_INT
+
+/* The size of `unsigned long', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_LONG
+
+/* The size of `unsigned long long', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_LONG_LONG
+
+/* The size of `unsigned short', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_SHORT
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
+
+
+/* -*- c -*- */
+
+/* These used to be in config.h.bot, but are now in configure.in. */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+#if SIZEOF_UNSIGNED_LONG_LONG==8
+typedef unsigned long long uint64_t;
+typedef long long int64_t;
+#elif SIZEOF_UNSIGNED_LONG==8
+typedef unsigned long uint64_t;
+typedef long int64_t;
+#else
+#error I do not know what to use for a uint64_t.
+#endif
+
+/* Give us an unsigned 32-bit data type. */
+#if SIZEOF_UNSIGNED_LONG==4
+typedef unsigned long uint32_t;
+typedef long int32_t;
+#elif SIZEOF_UNSIGNED_INT==4
+typedef unsigned int uint32_t;
+typedef int int32_t;
+#else
+#error I do not know what to use for a uint32_t.
+#endif
+
+/* An unsigned 16-bit data type. */
+#if SIZEOF_UNSIGNED_INT==2
+typedef unsigned int uint16_t;
+typedef int int16_t;
+#elif SIZEOF_UNSIGNED_SHORT==2
+typedef unsigned short uint16_t;
+typedef short int16_t;
+#else
+#error I do not know what to use for a uint16_t.
+#endif
+
+/* An unsigned 8-bit data type */
+#if SIZEOF_UNSIGNED_CHAR==1
+typedef unsigned char uint8_t;
+#else
+#error I do not know what to use for a uint8_t.
+#endif
+#endif
+#endif
+
+#ifdef __GNUC__
+#define NORETURN(_x) void _x __attribute__ ((noreturn))
+#define FORMAT(_a,_b,_c) __attribute__ ((format (_a,_b,_c)))
+#else
+#define NORETURN(_x) _x
+#define FORMAT(_a,_b,_c)
+#endif
+
+#endif /* _CONFIG_H */
+
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..b069360
--- /dev/null
+++ b/configure
@@ -0,0 +1,6046 @@
+#! /bin/sh
+# From configure.ac Id: configure.in.
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for secnet 0.1.18+.
+#
+# Report bugs to <secnet@chiark.greenend.org.uk>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: secnet@chiark.greenend.org.uk about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='secnet'
+PACKAGE_TARNAME='secnet'
+PACKAGE_VERSION='0.1.18+'
+PACKAGE_STRING='secnet 0.1.18+'
+PACKAGE_BUGREPORT='secnet@chiark.greenend.org.uk'
+PACKAGE_URL=''
+
+ac_unique_file="secnet.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+WRITESTRINGS
+EGREP
+GREP
+CPP
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+SET_MAKE
+FINK
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL
+_SUBDIRMK_MAKEFILES'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_hacky_parallel
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures secnet 0.1.18+ to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/secnet]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of secnet 0.1.18+:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-hacky-parallel parallelise slow cryptography (default is no)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <secnet@chiark.greenend.org.uk>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+secnet configure 0.1.18+
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## -------------------------------------------- ##
+## Report this to secnet@chiark.greenend.org.uk ##
+## -------------------------------------------- ##"
+     ) | sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid; break
+else
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+                       if test $ac_lo -le $ac_mid; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_lo=$ac_mid; break
+else
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+                       if test $ac_mid -le $ac_hi; then
+                         ac_lo= ac_hi=
+                         break
+                       fi
+                       as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+else
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_hi=$ac_mid
+else
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+       return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
+# ---------------------------------------------
+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+# accordingly.
+ac_fn_c_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  as_decl_name=`echo $2|sed 's/ *(.*//'`
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+#ifndef $as_decl_name
+#ifdef __cplusplus
+  (void) $as_decl_use;
+#else
+  (void) $as_decl_name;
+#endif
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_decl
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by secnet $as_me 0.1.18+, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+
+  ac_config_files="$ac_config_files main.mk:main.mk.tmp Dir.mk:Dir.mk.tmp Final.mk:Final.mk.tmp"
+
+
+
+  _SUBDIRMK_MAKEFILES="$_SUBDIRMK_MAKEFILES subdirmk/regen.mk"
+  ac_config_files="$ac_config_files subdirmk/regen.mk:subdirmk/regen.mk.in"
+
+
+  _SUBDIRMK_MAKEFILES="$_SUBDIRMK_MAKEFILES subdirmk/usual.mk"
+  ac_config_files="$ac_config_files subdirmk/usual.mk:subdirmk/usual.mk.in"
+
+
+
+
+
+  subdirmk_subdirs="$subdirmk_subdirs 'test-example/'"
+  ac_config_files="$ac_config_files test-example/Dir.mk:test-example/Dir.mk.tmp"
+
+
+  subdirmk_subdirs="$subdirmk_subdirs 'mtest/'"
+  ac_config_files="$ac_config_files mtest/Dir.mk:mtest/Dir.mk.tmp"
+
+
+  subdirmk_subdirs="$subdirmk_subdirs 'stest/'"
+  ac_config_files="$ac_config_files stest/Dir.mk:stest/Dir.mk.tmp"
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# If fink is on the path then it is assumed we should use it.
+# Extract the first word of "fink", so it can be a program name with args.
+set dummy fink; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_FINK+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $FINK in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_FINK="$FINK" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_FINK="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+FINK=$ac_cv_path_FINK
+if test -n "$FINK"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FINK" >&5
+$as_echo "$FINK" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+if test "x$FINK" != x; then
+  finkdir=`echo $FINK|sed 's,/[^/]*/[^/]*$,,'`
+  CPPFLAGS="-I$finkdir/include ${CPPFLAGS}"
+  LDFLAGS="-L$finkdir/lib ${LDFLAGS}"
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+       @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           rm -rf conftest.one conftest.two conftest.dir
+           echo one > conftest.one
+           echo two > conftest.two
+           mkdir conftest.dir
+           if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+             test -s conftest.one && test -s conftest.two &&
+             test -s conftest.dir/conftest.one &&
+             test -s conftest.dir/conftest.two
+           then
+             ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+             break 3
+           fi
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in stdint.h inttypes.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in net/if.h net/route.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in sys/socket.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_socket_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_SOCKET_H 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in linux/if_tun.h
+do :
+  ac_fn_c_check_header_compile "$LINENO" "linux/if_tun.h" "ac_cv_header_linux_if_tun_h" "#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+"
+if test "x$ac_cv_header_linux_if_tun_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LINUX_IF_TUN_H 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in stropts.h sys/sockio.h net/if_tun.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if ${ac_cv_c_bigendian+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_c_bigendian=unknown
+    # See if we're dealing with a universal compiler.
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifndef __APPLE_CC__
+              not a universal capable compiler
+            #endif
+            typedef int dummy;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+       # Check for potential -arch flags.  It is not universal unless
+       # there are at least two -arch flags with different values.
+       ac_arch=
+       ac_prev=
+       for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+        if test -n "$ac_prev"; then
+          case $ac_word in
+            i?86 | x86_64 | ppc | ppc64)
+              if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+                ac_arch=$ac_word
+              else
+                ac_cv_c_bigendian=universal
+                break
+              fi
+              ;;
+          esac
+          ac_prev=
+        elif test "x$ac_word" = "x-arch"; then
+          ac_prev=arch
+        fi
+       done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if sys/param.h defines the BYTE_ORDER macro.
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+            #include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+                    && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+                    && LITTLE_ENDIAN)
+             bogus endian macros
+            #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+               #include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+                not big endian
+               #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+             bogus endian macros
+            #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  # It does; now see whether it defined to _BIG_ENDIAN or not.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+                not big endian
+               #endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_c_bigendian=yes
+else
+  ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    fi
+    if test $ac_cv_c_bigendian = unknown; then
+      # Compile a test program.
+      if test "$cross_compiling" = yes; then :
+  # Try to guess by grepping values from an object file.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+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;
+
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+             ac_cv_c_bigendian=yes
+           fi
+           if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+             if test "$ac_cv_c_bigendian" = unknown; then
+               ac_cv_c_bigendian=no
+             else
+               # finding both strings is unlikely to happen, but who knows?
+               ac_cv_c_bigendian=unknown
+             fi
+           fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+
+            /* Are we little or big endian?  From Harbison&Steele.  */
+            union
+            {
+              long int l;
+              char c[sizeof (long int)];
+            } u;
+            u.l = 1;
+            return u.c[sizeof (long int) - 1] == 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  ac_cv_c_bigendian=no
+else
+  ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+   yes)
+     $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+   no)
+      ;; #(
+   universal)
+
+$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+     ;; #(
+   *)
+     as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long long" >&5
+$as_echo_n "checking size of unsigned long long... " >&6; }
+if ${ac_cv_sizeof_unsigned_long_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long long))" "ac_cv_sizeof_unsigned_long_long"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned_long_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned long long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_unsigned_long_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_LONG_LONG $ac_cv_sizeof_unsigned_long_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
+$as_echo_n "checking size of unsigned long... " >&6; }
+if ${ac_cv_sizeof_unsigned_long+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned_long" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned long)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_unsigned_long=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned_int" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_unsigned_int=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned short" >&5
+$as_echo_n "checking size of unsigned short... " >&6; }
+if ${ac_cv_sizeof_unsigned_short+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned short))" "ac_cv_sizeof_unsigned_short"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned_short" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned short)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_unsigned_short=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_short" >&5
+$as_echo "$ac_cv_sizeof_unsigned_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned char" >&5
+$as_echo_n "checking size of unsigned char... " >&6; }
+if ${ac_cv_sizeof_unsigned_char+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned char))" "ac_cv_sizeof_unsigned_char"        "$ac_includes_default"; then :
+
+else
+  if test "$ac_cv_type_unsigned_char" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned char)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_unsigned_char=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_char" >&5
+$as_echo "$ac_cv_sizeof_unsigned_char" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_CHAR $ac_cv_sizeof_unsigned_char
+_ACEOF
+
+
+
+    hard=
+  if test -z "$hard"; then
+    msg="C to warn about writing to stringliterals"
+  else
+    msg="C to prohibit any write to stringliterals"
+  fi
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking $msg" >&5
+$as_echo_n "checking $msg... " >&6; }
+if ${ac_cv_prog_cc_no_writeable_strings+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+  cat > conftest.c <<EOF
+#include <string.h>
+int main (void)
+{
+   char test[16];
+   if (strcpy (test, "test")) return 0;
+   return 1;
+}
+EOF
+    if test "$GCC" = "yes";
+  then
+       if test -z "$hard"; then
+           ac_cv_prog_cc_no_writeable_strings="-Wwrite-strings"
+        else
+            ac_cv_prog_cc_no_writeable_strings="-fno-writable-strings -Wwrite-strings"
+        fi
+
+        if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then
+            ac_cv_prog_cc_no_writeable_strings="suppressed: string.h"
+        fi
+
+    elif  $CC -flags 2>&1 | grep "Xc.*strict ANSI C" > /dev/null 2>&1 &&
+       $CC -c -xstrconst conftest.c > /dev/null 2>&1 &&
+       test -f conftest.o
+  then
+        # strings go into readonly segment
+       ac_cv_prog_cc_no_writeable_strings="-xstrconst"
+
+       rm conftest.o
+        if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then
+             ac_cv_prog_cc_no_writeable_strings="suppressed: string.h"
+        fi
+
+    elif  $CC > /dev/null 2>&1 &&
+       $CC -c +ESlit conftest.c > /dev/null 2>&1 &&
+       test -f conftest.o
+  then
+       # strings go into readonly segment
+       ac_cv_prog_cc_no_writeable_strings="+ESlit"
+
+       rm conftest.o
+        if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then
+             ac_cv_prog_cc_no_writeable_strings="suppressed: string.h"
+        fi
+
+    elif ! $CC > /dev/null 2>&1 &&
+       $CC -c -readonly_strings conftest.c > /dev/null 2>&1 &&
+       test -f conftest.o
+  then
+       # strings go into readonly segment
+       ac_cv_prog_cc_no_writeable_strings="-readonly_strings"
+
+       rm conftest.o
+        if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then
+             ac_cv_prog_cc_no_writeable_strings="suppressed: string.h"
+        fi
+
+
+       # -use_readonly_const is the default for IRIX C,
+       # puts them into .rodata, but they are copied later.
+       # need to be "-G0 -rdatashared" for strictmode but
+       # I am not sure what effect that has really.
+
+  fi
+  rm -f conftest.*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_no_writeable_strings" >&5
+$as_echo "$ac_cv_prog_cc_no_writeable_strings" >&6; }
+  if test -z "$WRITESTRINGS" ; then
+    if test -n "$ac_cv_prog_cc_no_writeable_strings" ; then
+      case "$ac_cv_prog_cc_no_writeable_strings" in
+        suppressed*) WRITESTRINGS="" ;; # known but suppressed
+        *)  WRITESTRINGS="$ac_cv_prog_cc_no_writeable_strings" ;;
+      esac
+    fi
+  fi
+
+
+
+# Check whether --enable-hacky-parallel was given.
+if test "${enable_hacky_parallel+set}" = set; then :
+  enableval=$enable_hacky_parallel;
+  case "$enableval" in
+  n|0|no) ;;
+  y|1|yes) CFLAGS="$CFLAGS -DHACKY_PARALLEL" ;;
+  *) ;;
+  esac
+
+fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mpz_init_set_str in -lgmp" >&5
+$as_echo_n "checking for mpz_init_set_str in -lgmp... " >&6; }
+if ${ac_cv_lib_gmp_mpz_init_set_str+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgmp  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mpz_init_set_str ();
+int
+main ()
+{
+return mpz_init_set_str ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_gmp_mpz_init_set_str=yes
+else
+  ac_cv_lib_gmp_mpz_init_set_str=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp_mpz_init_set_str" >&5
+$as_echo "$ac_cv_lib_gmp_mpz_init_set_str" >&6; }
+if test "x$ac_cv_lib_gmp_mpz_init_set_str" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBGMP 1
+_ACEOF
+
+  LIBS="-lgmp $LIBS"
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for mpz_init_set_str in -lgmp2" >&5
+$as_echo_n "checking for mpz_init_set_str in -lgmp2... " >&6; }
+if ${ac_cv_lib_gmp2_mpz_init_set_str+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgmp2  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mpz_init_set_str ();
+int
+main ()
+{
+return mpz_init_set_str ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_gmp2_mpz_init_set_str=yes
+else
+  ac_cv_lib_gmp2_mpz_init_set_str=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp2_mpz_init_set_str" >&5
+$as_echo "$ac_cv_lib_gmp2_mpz_init_set_str" >&6; }
+if test "x$ac_cv_lib_gmp2_mpz_init_set_str" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBGMP2 1
+_ACEOF
+
+  LIBS="-lgmp2 $LIBS"
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __gmpz_init_set_str in -lgmp" >&5
+$as_echo_n "checking for __gmpz_init_set_str in -lgmp... " >&6; }
+if ${ac_cv_lib_gmp___gmpz_init_set_str+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgmp  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char __gmpz_init_set_str ();
+int
+main ()
+{
+return __gmpz_init_set_str ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_gmp___gmpz_init_set_str=yes
+else
+  ac_cv_lib_gmp___gmpz_init_set_str=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp___gmpz_init_set_str" >&5
+$as_echo "$ac_cv_lib_gmp___gmpz_init_set_str" >&6; }
+if test "x$ac_cv_lib_gmp___gmpz_init_set_str" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBGMP 1
+_ACEOF
+
+  LIBS="-lgmp $LIBS"
+
+fi
+
+ac_fn_c_check_header_mongrel "$LINENO" "gmp.h" "ac_cv_header_gmp_h" "$ac_includes_default"
+if test "x$ac_cv_header_gmp_h" = xyes; then :
+
+else
+  as_fn_error $? "gmp.h not found" "$LINENO" 5
+fi
+
+
+
+
+ ac_fn_c_check_func "$LINENO" "inet_ntoa" "ac_cv_func_inet_ntoa"
+if test "x$ac_cv_func_inet_ntoa" = xyes; then :
+
+else
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnsl" >&5
+$as_echo_n "checking for inet_ntoa in -lnsl... " >&6; }
+if ${ac_cv_lib_nsl_inet_ntoa+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_ntoa ();
+int
+main ()
+{
+return inet_ntoa ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_nsl_inet_ntoa=yes
+else
+  ac_cv_lib_nsl_inet_ntoa=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_inet_ntoa" >&5
+$as_echo "$ac_cv_lib_nsl_inet_ntoa" >&6; }
+if test "x$ac_cv_lib_nsl_inet_ntoa" = xyes; then :
+
+  LIBS="-lnsl $LIBS";
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: inet_ntoa is in libnsl, urgh.  Must use -lnsl." >&5
+$as_echo "$as_me: WARNING: inet_ntoa is in libnsl, urgh.  Must use -lnsl." >&2;}
+
+else
+
+    as_fn_error $? "cannot find library function inet_ntoa" "$LINENO" 5
+
+fi
+
+
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5
+$as_echo_n "checking for socket in -lsocket... " >&6; }
+if ${ac_cv_lib_socket_socket+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_socket_socket=yes
+else
+  ac_cv_lib_socket_socket=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5
+$as_echo "$ac_cv_lib_socket_socket" >&6; }
+if test "x$ac_cv_lib_socket_socket" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSOCKET 1
+_ACEOF
+
+  LIBS="-lsocket $LIBS"
+
+fi
+
+
+
+ ac_fn_c_check_func "$LINENO" "inet_aton" "ac_cv_func_inet_aton"
+if test "x$ac_cv_func_inet_aton" = xyes; then :
+
+else
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_aton in -lresolv" >&5
+$as_echo_n "checking for inet_aton in -lresolv... " >&6; }
+if ${ac_cv_lib_resolv_inet_aton+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lresolv  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_aton ();
+int
+main ()
+{
+return inet_aton ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_resolv_inet_aton=yes
+else
+  ac_cv_lib_resolv_inet_aton=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_inet_aton" >&5
+$as_echo "$ac_cv_lib_resolv_inet_aton" >&6; }
+if test "x$ac_cv_lib_resolv_inet_aton" = xyes; then :
+
+  LIBS="-lresolv $LIBS";
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: inet_aton is in libresolv, urgh.  Must use -lresolv." >&5
+$as_echo "$as_me: WARNING: inet_aton is in libresolv, urgh.  Must use -lresolv." >&2;}
+
+else
+
+    as_fn_error $? "cannot find library function inet_aton" "$LINENO" 5
+
+fi
+
+
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for adns_init in -ladns" >&5
+$as_echo_n "checking for adns_init in -ladns... " >&6; }
+if ${ac_cv_lib_adns_adns_init+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ladns  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char adns_init ();
+int
+main ()
+{
+return adns_init ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_adns_adns_init=yes
+else
+  ac_cv_lib_adns_adns_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_adns_adns_init" >&5
+$as_echo "$ac_cv_lib_adns_adns_init" >&6; }
+if test "x$ac_cv_lib_adns_adns_init" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBADNS 1
+_ACEOF
+
+  LIBS="-ladns $LIBS"
+
+fi
+
+ac_fn_c_check_header_mongrel "$LINENO" "adns.h" "ac_cv_header_adns_h" "$ac_includes_default"
+if test "x$ac_cv_header_adns_h" = xyes; then :
+
+else
+  as_fn_error $? "adns.h not found" "$LINENO" 5
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: Checking requirements for IPv6 support..." >&5
+$as_echo "$as_me: Checking requirements for IPv6 support..." >&6;}
+enable_ipv6=true
+
+ac_fn_c_check_decl "$LINENO" "AF_INET6" "ac_cv_have_decl_AF_INET6" "#include <netinet/in.h>
+"
+if test "x$ac_cv_have_decl_AF_INET6" = xyes; then :
+
+else
+  enable_ipv6=false
+fi
+
+ac_fn_c_check_func "$LINENO" "adns_addr2text" "ac_cv_func_adns_addr2text"
+if test "x$ac_cv_func_adns_addr2text" = xyes; then :
+
+else
+  enable_ipv6=false
+fi
+
+if $enable_ipv6; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: Enabling IPv6 support" >&5
+$as_echo "$as_me: Enabling IPv6 support" >&6;}
+
+$as_echo "#define CONFIG_IPV6 1" >>confdefs.h
+
+else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling IPv6 support" >&5
+$as_echo "$as_me: WARNING: Disabling IPv6 support" >&2;}
+fi
+
+
+
+  _SUBDIRMK_MAKEFILES="$_SUBDIRMK_MAKEFILES common.make"
+  ac_config_files="$ac_config_files common.make:common.make.in"
+
+
+
+ac_config_commands="$ac_config_commands default"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by secnet $as_me 0.1.18+, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <secnet@chiark.greenend.org.uk>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+secnet config.status 0.1.18+
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+
+     '$srcdir'/subdirmk/generate --srcdir='$srcdir' $subdirmk_subdirs
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "main.mk") CONFIG_FILES="$CONFIG_FILES main.mk:main.mk.tmp" ;;
+    "Dir.mk") CONFIG_FILES="$CONFIG_FILES Dir.mk:Dir.mk.tmp" ;;
+    "Final.mk") CONFIG_FILES="$CONFIG_FILES Final.mk:Final.mk.tmp" ;;
+    "subdirmk/regen.mk") CONFIG_FILES="$CONFIG_FILES subdirmk/regen.mk:subdirmk/regen.mk.in" ;;
+    "subdirmk/usual.mk") CONFIG_FILES="$CONFIG_FILES subdirmk/usual.mk:subdirmk/usual.mk.in" ;;
+    "test-example/Dir.mk") CONFIG_FILES="$CONFIG_FILES test-example/Dir.mk:test-example/Dir.mk.tmp" ;;
+    "mtest/Dir.mk") CONFIG_FILES="$CONFIG_FILES mtest/Dir.mk:mtest/Dir.mk.tmp" ;;
+    "stest/Dir.mk") CONFIG_FILES="$CONFIG_FILES stest/Dir.mk:stest/Dir.mk.tmp" ;;
+    "common.make") CONFIG_FILES="$CONFIG_FILES common.make:common.make.in" ;;
+    "default") CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+  :C)  { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "default":C) echo timestamp >config.stamp ;;
+
+  esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+
+
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..8f8fab3
--- /dev/null
@@ -0,0 +1,173 @@
+dnl Process this file with autoconf to produce a configure script.
+
+dnl This file is part of secnet.
+dnl See README for full list of copyright holders.
+dnl
+dnl secnet is free software; you can redistribute it and/or modify it
+dnl under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 3 of the License, or
+dnl (at your option) any later version.
+dnl 
+dnl secnet is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+dnl General Public License for more details.
+dnl 
+dnl You should have received a copy of the GNU General Public License
+dnl version 3 along with secnet; if not, see
+dnl https://www.gnu.org/licenses/gpl.html.
+
+sinclude(ac_prog_cc_no_writeable_strings.m4)
+
+m4_include(subdirmk/subdirmk.ac)
+
+AC_INIT(secnet,0.1.18+,secnet@chiark.greenend.org.uk)
+AC_CONFIG_SRCDIR(secnet.c)
+AC_CONFIG_HEADER(config.h)
+
+SUBDIRMK_SUBDIRS([test-example mtest stest])
+
+AC_PREREQ(2.50)
+AC_REVISION($Id: configure.in,v 1.4 2002/09/09 22:05:02 steve Exp $)
+
+AC_LANG_C
+
+# If fink is on the path then it is assumed we should use it.
+AC_PATH_PROG([FINK],[fink])
+if test "x$FINK" != x; then
+  finkdir=`echo $FINK|sed 's,/[[^/]]*/[[^/]]*$,,'`
+  CPPFLAGS="-I$finkdir/include ${CPPFLAGS}"
+  LDFLAGS="-L$finkdir/lib ${LDFLAGS}"
+fi
+
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_STDC_HEADERS
+AC_CHECK_HEADERS([stdint.h inttypes.h])
+AC_CHECK_HEADERS([net/if.h net/route.h])
+AC_CHECK_HEADERS([sys/socket.h])
+AC_CHECK_HEADERS([linux/if_tun.h], [], [], 
+[#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+])
+AC_CHECK_HEADERS([stropts.h sys/sockio.h net/if_tun.h])
+AC_C_BIGENDIAN
+AC_CHECK_SIZEOF(unsigned long long)
+AC_CHECK_SIZEOF(unsigned long)
+AC_CHECK_SIZEOF(unsigned int)
+AC_CHECK_SIZEOF(unsigned short)
+AC_CHECK_SIZEOF(unsigned char)
+AC_PROG_CC_NO_WRITEABLE_STRINGS(WRITESTRINGS)
+
+AC_ARG_ENABLE(hacky-parallel,
+  [AS_HELP_STRING([--enable-hacky-parallel],
+                  [parallelise slow cryptography (default is no)])], [
+  case "$enableval" in
+  n|0|no) ;;
+  y|1|yes) CFLAGS="$CFLAGS -DHACKY_PARALLEL" ;;
+  *) ;;
+  esac
+])
+
+AC_DEFUN([REQUIRE_HEADER],[AC_CHECK_HEADER($1,,[AC_MSG_ERROR($1 not found)])])
+
+dnl the order in which libraries is checked is important
+dnl eg. adns on Solaris 2.5.1 depends on -lnsl and -lsocket
+AC_CHECK_LIB(gmp,mpz_init_set_str)
+AC_CHECK_LIB(gmp2,mpz_init_set_str)
+AC_CHECK_LIB(gmp,__gmpz_init_set_str)
+REQUIRE_HEADER([gmp.h])
+dnl Would love to barf if no gmp was found, but how to test? Requiring the header will do for now.
+SECNET_C_GETFUNC(inet_ntoa,nsl)
+AC_CHECK_LIB(socket,socket)
+SECNET_C_GETFUNC(inet_aton,resolv)
+AC_CHECK_LIB(adns,adns_init)
+REQUIRE_HEADER([adns.h])
+
+AC_MSG_NOTICE([Checking requirements for IPv6 support...])
+enable_ipv6=true
+m4_define(NO_IPV6,[enable_ipv6=false])
+AC_CHECK_DECL(AF_INET6,        [],[NO_IPV6],[#include <netinet/in.h>])
+AC_CHECK_FUNC(adns_addr2text,  [],[NO_IPV6])
+if $enable_ipv6; then
+    AC_MSG_NOTICE([Enabling IPv6 support])
+    AC_DEFINE(CONFIG_IPV6, 1,
+              [Define to 1 to use IPv6 support in system and adns])
+else
+    AC_MSG_WARN([Disabling IPv6 support])
+fi
+
+SUBDIRMK_MAKEFILES(common.make)
+
+AC_OUTPUT(,
+          echo timestamp >config.stamp)
+
+AH_TOP([
+#ifndef _CONFIG_H
+#define _CONFIG_H
+])
+
+AH_BOTTOM([
+/* -*- c -*- */
+
+/* These used to be in config.h.bot, but are now in configure.in. */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+#if SIZEOF_UNSIGNED_LONG_LONG==8
+typedef unsigned long long uint64_t;
+typedef long long int64_t;
+#elif SIZEOF_UNSIGNED_LONG==8
+typedef unsigned long uint64_t;
+typedef long int64_t;
+#else
+#error I do not know what to use for a uint64_t.
+#endif
+
+/* Give us an unsigned 32-bit data type. */
+#if SIZEOF_UNSIGNED_LONG==4
+typedef unsigned long uint32_t;
+typedef long int32_t;
+#elif SIZEOF_UNSIGNED_INT==4
+typedef unsigned int uint32_t;
+typedef int int32_t;
+#else
+#error I do not know what to use for a uint32_t.
+#endif
+
+/* An unsigned 16-bit data type. */
+#if SIZEOF_UNSIGNED_INT==2
+typedef unsigned int uint16_t;
+typedef int int16_t;
+#elif SIZEOF_UNSIGNED_SHORT==2
+typedef unsigned short uint16_t;
+typedef short int16_t;
+#else
+#error I do not know what to use for a uint16_t.
+#endif
+
+/* An unsigned 8-bit data type */
+#if SIZEOF_UNSIGNED_CHAR==1
+typedef unsigned char uint8_t;
+#else
+#error I do not know what to use for a uint8_t.
+#endif
+#endif
+#endif
+
+#ifdef __GNUC__
+#define NORETURN(_x) void _x __attribute__ ((noreturn))
+#define FORMAT(_a,_b,_c) __attribute__ ((format (_a,_b,_c)))
+#else
+#define NORETURN(_x) _x
+#define FORMAT(_a,_b,_c)
+#endif
+
+#endif /* _CONFIG_H */
+])
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..7d9c4eb
--- /dev/null
@@ -0,0 +1,426 @@
+secnet (0.5.2~) unstable; urgency=medium
+
+  * 
+
+ --
+
+secnet (0.5.1) unstable; urgency=medium
+
+  POTENTIALLY INCOMPATIBLE CHANGE.  Some security implications.
+
+  * make-secnet-sites: Prefix names when writing secnet sites.conf file.
+
+    make-secnet-sites must copy names (vpn, location and site names) from
+    the input sites file (which is not wholly trusted) to the secnet
+    config file.  Prior to this release, naming a location or site the
+    same as a secnet predefined name could generate a broken sites.conf
+    which secnet would reject.  (With the existing featureset,
+    malfunctions other than rejection, eg privilege escalation, are not
+    possible.)
+
+    make-secnet-sites now adds a prefix to these names when writing
+    sites.conf.  This will not affect configurations which use the
+    make-secnet-sites-provided `all-sites' key, as is usual.  Other
+    configurations will break unless the references in the static part of
+    the config are adjusted.
+
+    Previous behaviour can be restored with the --no-conf-key-prefix
+    option.  (Planned future enhancements to secnet are likely to make use
+    of that option, with untrusted input, dangerously insecure.)
+
+  other changes to make-secnet-sites:
+  * Fix argument parsing.  Fixes a regression affecting -P in 0.5.0,
+    and also fixes new facilities introduced in 0.5.0.
+  * Sort the properties on output (and adjust the test case expected
+    outputs).  Tests now pass on (at least) Python 2.7.13, 3.5.3, 3.7.5.
+  * Delete some unused code.
+
+  secnet:
+  * Change one idiom to avoid a warning from GCC9.  No functional change.
+
+  build system - MAJOR CHANGES:
+  * Fix out-of-tree builds.  (Broken in 0.5.0)
+  * Replace recursive make with use of the new subdirmk system.
+    This represents a fairly comprehensive overhaul of the makefiles.
+    Several bugs (esp. involving dependencies between files in different
+    directories) are fixed.
+  * Drop `make check' from `make all'.  (Otherwise there is no way
+    to ask for `all' without `check'.)
+  * Suppress two unhelpful new compiler warnings from GCC9.
+  * Release checklist update.
+
+  documentation:
+  * Creit Mark Wooding properly in CREDITS.
+  * Include DEVELOPER-CERTIFICATE.
+
+  tests:
+  * Locations now have different names to sites.
+  * Somewhat better debugging output from mtest.
+  * Do not run msgcode-test except with `make fullcheck'.
+  * Other minor bugfixes and improvments.
+  * stest: Suppress unhelpful -Wno-unused-result (needed for stretch).
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Fri, 22 Nov 2019 23:13:14 +0000
+
+secnet (0.5.0) unstable; urgency=medium
+
+  make-secnet-sites SECURITY FIX:
+  * Do not blindly trust inputs; instead, check the syntax for sanity.
+    Previous releases can be induced to run arbitrary code as the user
+    invoking secnet (which might be root), if a secnet sites.conf is used
+    that was generated from an untrustworthy sites file.
+  * The userv invocation mode of make-secnet-sites seems to have been safe
+    in itself, but it previously allowed hazardous data to be propagated
+    into the master sites file.  This is now prevented too.
+
+  make-secnet-sites overhaul work:
+  * make-secnet-sites is now in the common subset of Python2 and Python3.
+    The #! is python3 now, but it works with Python2.7 too.
+    It will probably *not* work with old versions of Python2.
+  * We no longer depend on the obsolete `ipaddr' library.  We use
+    `ipaddress' now.  And this is onlo a Recommends in the .deb.
+  * Ad-hoc argument parser been replaced with `argparse'.
+    There should be no change to existing working invocations.
+  * Bad address syntax error does not wrongly mention IPv6 scopes.
+  * Minor refactoring to support forthcoming work.  [Mark Wooding]
+
+  other bugfixes, improvements and changes to secnet itself:
+  * Better logging of why we are sending NAK messages.
+  * Correctly use the verified copy of the peer remote capabilities
+    from MSG3.  (Bug is not a vulnerability.)    [Mark Wooding]
+  * Significant internal rearrangements and refactorings, to support
+    forthcoming key management work.  [Mark Wooding and Ian Jackson]
+
+  build system etc.:
+  * Completely overhaul release checklist; drop dist target.
+  * Remove dependency on `libfl.a'.  [Mark Wooding]
+  * polypath.c: Fix missing include of <limits.h>.  [Mark Wooding]
+  * Add a Wireshark dissector `secnet-wireshark.lua'.  It is not
+    installed anywhere right now.  [Mark Wooding]
+
+  documentation:
+  * Improve documentation of capability negotiation in NOTES, secnet(8)
+    and magic.h.  [Mark Wooding]
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 24 Oct 2019 19:11:54 +0100
+
+secnet (0.4.5) unstable; urgency=medium
+
+  * INSTALL: Mention that rsa key generation might need ssh-keygen1.
+  * mobile: Fix negotiation bug with mixed old/new secnets and
+    simultaneous key setup attempts by each end.  [Mark Wooding]
+  * Makefile.in: Support installation from a `VPATH' build.  [Mark Wooding]
+  * Portability fixes for clang.  [Mark Wooding]
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sat, 21 Sep 2019 12:04:31 +0100
+
+secnet (0.4.4) unstable; urgency=medium
+
+  Security fix:
+  * make-secnet-sites: Don't allow setting new VPN-level properties
+    when restricted.  This could allow denial of service by
+    users with delegated authorisation.  [Mark Wooding]
+
+  Bugfixes for poor network environments:
+  * polypath: cope properly with asymmetric routing, by correcting
+    the handling of late duplicated packets etc.   Protocol is now
+    incompatible with secnet prior to 0.3.0 when either end is mobile.
+  * Randomise key setup retry time.
+
+  Other bugfixes:
+  * rsa and cbcmac: Fix configuration error messages.  [Mark Wooding]
+  * Handle IPv4 addresses properly (ie, not foolishly byte-swapped),
+    when IPv6 is not available.  [Mark Wooding]
+  * Better logging (and less foolish debug), especially about whether
+    key is set up, and about crossed key setup attempts.
+  * Internal refactoring and fixes.  [Ian Jackson and Mark Wooding]
+
+  Build system and portability:
+  * configure: rerun autogen.sh with autoconf 2.69-10
+  * Avoid memset(0,0,0) wrt st->sharedsecret.  (Fixes compiler warning;
+    in theory might cause miscompilation.)  [Mark Wooding]
+
+  Documentation:
+  * README.make-secnet-sites: new documentation file.  [Mark Wooding]
+  * NOTES: Describe current allocation of capability bits.  [Mark Wooding]
+  * NOTES: tiny fix tot protocol description.
+  * secnet(8): Delete wrong information about dh groups.  [Mark Wooding]
+
+  Administrivia:
+  * Fix erroneous GPL3+ licence notices "version d or later" (!)
+  * .dir-locals.el: Settings for Python code.  [Mark Wooding]
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 08 Sep 2019 22:53:14 +0100
+
+secnet (0.4.3) unstable; urgency=low
+
+  Security improvement:
+  * Use `mpz_powm_sec' for modexps.
+
+  Enhancements:
+  * Implement comm-info and dedicated-interface-addr feature, for
+    benefit of hippotat.
+  * Implement `keepalive' site option, to try to keep link always up.
+
+  Build etc. fixes:
+  * #include <limits.h> (fixes the build on jessie).
+  * Tolerate building from a git checkout, but with git not installed.
+    (This can happen in chroots.)
+  * Turn off -Wsign-compare for bison output.
+  * Makefile.in: Fix `check-ipaddrset' rule to get reference from
+    $(srcdir).  (Makes out-of-tree builds work properly.)
+  * Release checklist fixes.
+  * Burn version numbers 0.4.1 and 0.4.2 due to errors in release prep.
+
+  Bugfixes:
+  * When printing messages about dropping IPv6, do not print anything
+    about ihl.  (Check the IP version field first!)
+  * When turning on debug, turn on verbose too.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sat, 25 Nov 2017 13:36:41 +0000
+
+secnet (0.4.0) unstable; urgency=low
+
+  Debugging improvements:
+  * Packet-level debugging from site notes errors from transmit.
+  * Report when transport peers updated as a result of transmit.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sat, 28 Feb 2015 15:03:00 +0000
+
+secnet (0.4.0~beta2) unstable; urgency=low
+
+  Polypath bugfixes:
+  * Ignore IPv6 Unique Local unicast addresses.
+  * Skip "tentative" IPv6 local addresses.
+  * Improve logging and debug output.
+
+  Portability fix:
+  * Build where size_t is not compatible with int.
+
+  Build system and packaging fixes:
+  * Makefile: support DESTDIR.
+  * debian/rules: set DESTDIR (not prefix).
+  * debian/rules: Support dpkg-buildflags.
+  * Install ipaddrset.py and secnet.8 with correct permissions.
+  * Fix check for <linux/if_tun.h> and git rid of our copy.
+  * Use -lresolv only if inet_aton is not found otherwise.
+  * Use -lnsl only if inet_ntoa is not found otherwise.
+  * debian/rules: Provide build-arch and build-indep targets.
+  * debian/rules: Do not run build for *-indep (!)
+  * Makefile.in: Putative dual (backport and not) release build process doc.
+
+  Copyright updates:
+  * Update to GPLv3.  Add missing copyright notices and credits.
+  * Get rid of old FSF street address; use URL instead.
+  * Remove obsolete LICENCE.txt (which was for snprintf reimplementation).
+  * Remove obsolete references to Cendio (for old ipaddr.py).
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 28 Dec 2014 17:14:10 +0000
+
+secnet (0.4.0~beta1) unstable; urgency=low
+
+  New features:
+  * Support transport over IPv6.  (We do not yet carry IPv6 in the private
+    network.)  IPv6 support depends on IPv6-capable adns (adns 1.5.x).
+  * New polypath comm, which can duplicate packets so as to send them via
+    multiple routes over the public network, for increased
+    reliability/performance (but increased cost).  Currently Linux-only
+    but should be fairly easy to port.
+  * Support multiple public addresses for peers.
+  * Discard previously-received packets (by default).
+
+  Logging improvements:
+  * Report (each first) transmission and reception success and failure.
+  * Log reason for DNS reolution failure.
+  * Log unexpected kinds of death from userv.
+  * Log authbind exit status as errno value (if appropriate).
+
+  Configuration adjustments:
+  * Adjust default number of mobile peer addresses to store when a peer
+    public address is also configured.
+  * Make specifying peer public port optional.  This avoids making special
+    arrangements to bind to a port for in mobile sites with no public
+    stable address.
+
+  Bugfixes:
+  * Hackypar children will die if they get a terminating signal.
+  * Fix signal dispositions inherited by secnet's child processes.
+  * Fix off-by-one error which prevented setting transport-peers-max to 5.
+
+  Test, build and internal improvements:
+  * Use conventional IP address handling library ipaddr.py.
+  * Provide a fuzzer for the slip decoder.
+  * Build system improvements.
+  * Many source code cleanups.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 26 Oct 2014 15:28:31 +0000
+
+secnet (0.3.4) unstable; urgency=low
+
+  SECURITY FIX:
+  * The previous security fix to buffer handling was entirely wrong.  This
+    one is better.  Thanks to Simon Tatham for the report and the patch.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Mon, 22 Sep 2014 16:16:11 +0100
+
+secnet (0.3.3) unstable; urgency=high
+
+  SECURITY FIXES:
+  * Pass correct size argument to recvfrom.  This is a serious security
+    problem which may be exploitable from outside the VPN.
+  * Fix a memory leak in some error logging.
+
+  Other related fixes:
+  * Two other latent bugs in buffer length handling found and fixed.
+  * Non-critical stylistic improvements to buffer length handling, to make
+    the code clearer and to assist audit.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Fri, 19 Sep 2014 23:50:45 +0100
+
+secnet (0.3.3~beta1) unstable; urgency=low
+
+  Installation compatibility fix:
+  * In make-secnet-sites, always use our own ipaddr.py even if the
+    incompatible modern ipaddr.py is installed (eg via python-ipaddr.deb).
+    (Future versions of secnet are going to need that Python module to be
+    installed.)
+
+  For links involving mobile sites:
+  * Use source of NAK packets as hint for peer transport address.
+  * When initiating rekey, make use of data transport peer addresses.
+
+  Build fix:
+  * Provide clean target in test-example/Makefile.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Fri, 19 Sep 2014 00:11:44 +0100
+
+secnet (0.3.2) unstable; urgency=low
+
+  * Release of 0.3.2.  No code changes since 0.3.1~beta1.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 26 Jun 2014 20:27:58 +0100
+
+secnet (0.3.2~beta1) unstable; urgency=low
+
+  For links involving mobile sites:
+  * SECURITY: Properly update peer address array when it is full.
+  * Do name-resolution on peer-initiated key setup too, when we are mobile
+    (and other name-resolution improvements).
+
+  Other minor improvements:
+  * Log peer addresses on key exchange timeout.
+  * When printing version (eg during startup), use value from git-describe
+    and thus include git commit id where applicable.
+  * Updates to release checklist in Makefile.in.
+  * Use C99 _Bool for bool_t.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Fri, 06 Jun 2014 01:17:54 +0100
+
+secnet (0.3.1) unstable; urgency=low
+
+  * Release of 0.3.1.  No code changes since 0.3.1~beta3.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 15 May 2014 01:08:30 +0100
+
+secnet (0.3.1~beta3) unstable; urgency=low
+
+  * Build fixes for non-i386 architectures and gcc 4.8.2.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 08 May 2014 19:53:43 +0100
+
+secnet (0.3.1~beta2) unstable; urgency=low
+
+  Fix relating to new fragmentation / ICMP functionality:
+  * Generate ICMP packets correctly in point-to-point configurations.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sat, 03 May 2014 18:58:09 +0100
+
+secnet (0.3.1~beta1) unstable; urgency=low
+
+  Security fixes (vulnerabilities are to inside attackers only):
+  * SECURITY: Fixes to MTU and fragmentation handling.
+  * SECURITY: Correctly set "unused" ICMP header field.
+  * SECURITY: Fix IP length check not to crash on very short packets.
+
+  New feature:
+  * Make the inter-site MTU configurable, and negotiate it with the peer.
+
+  Bugfixes etc.:
+  * Fix netlink SEGV on clientless netlinks (i.e. configuration error).
+  * Fix formatting error in p-t-p startup message.
+  * Do not send ICMP errors in response to unknown incoming ICMP.
+  * Fix formatting error in secnet.8 manpage.
+  * Internal code rearrangements and improvements.
+
+  Packaging improvements:
+  * Updates to release checklist in Makefile.in.
+  * Additions to the test-example suite.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 01 May 2014 19:02:56 +0100
+
+secnet (0.3.0) unstable; urgency=low
+
+  * Release of 0.3.0.  No code changes since 0.3.0~beta3.
+  * Update release checklist.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 01 Sep 2013 20:27:48 +0100
+
+secnet (0.3.0~beta3) unstable; urgency=low
+
+  * New upstream version.
+   - Stability bugfix: properly initialise site's scratch buffer.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Mon, 05 Aug 2013 11:54:09 +0100
+
+secnet (0.3.0~beta2) unstable; urgency=low
+
+  * New upstream version.
+   - SECURITY FIX: RSA public modulus and exponent buffer overflow.
+   - SECURITY FIX: Use constant-time memcmp for message authentication.
+   - SECURITY FIX: Provide a new transform, eax-serpent, to replace cbcmac.
+   - SECURITY FIX: No longer send NAKs for NAKs, avoiding NAK storm.
+   - SECURITY FIX: Fix site name checking when site name A is prefix of B.
+   - SECURITY FIX: Safely reject too-short IP packets.
+   - Better robustness for mobile sites (proper user of NAKs, new PROD msg).
+   - Better robustness against SLIP decoding errors.
+   - Fix bugs which caused routes to sometimes not be advertised.
+   - Protocol capability negotiation mechanism.
+   - Improvements and fixes to protocol and usage documentation.
+   - Other bugfixes and code tidying up.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 25 Jul 2013 18:26:01 +0100
+
+secnet (0.3.0~beta1) unstable; urgency=low
+
+  * New upstream version.
+   - SECURITY FIX: avoid crashes (or buffer overrun) on short packets.
+   - Bugfixes relating to packet loss during key exchange.
+   - Bugfixes relating to link up/down status.
+   - Bugfixes relating to logging.
+   - make-secnet-sites made more sophisticated to support two vpns on chiark.
+   - Documentation improvements.
+   - Build system improvements.
+  * Debian packaging improvements:
+   - Native package.
+   - Maintainer / uploaders.
+   - init script requires $remove_fs since we're in /usr.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 12 Jul 2012 20:18:16 +0100
+
+secnet (0.2.1-1) unstable; urgency=low
+
+  * New upstream version.  (authbind endianness fix)
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 11 Dec 2011 13:14:57 +0000
+
+secnet (0.2.0-1) unstable; urgency=low
+
+  * New upstream version.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sat, 10 Dec 2011 22:44:41 +0000
+
+secnet (0.1.18-1) unstable; urgency=low
+
+  * New upstream version.
+
+ -- Stephen Early <steve@greenend.org.uk>  Tue,  18 Mar 2008 17:45:00 +0000
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..45dfe28
--- /dev/null
@@ -0,0 +1,17 @@
+Source: secnet
+Section: net
+Priority: extra
+Maintainer: Ian Jackson <ijackson@chiark.greenend.org.uk>
+Uploaders: Stephen Early <steve@greenend.org.uk>,
+           Richard Kettlewell <rjk@terraraq.org.uk>
+Build-Depends: debhelper (>= 5), libgmp3-dev, libadns1-dev, bison, flex,
+               libbsd-dev, python3, tclx, tcl, libtcl-chiark-1
+Standards-Version: 3.0.1
+
+Package: secnet
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Recommends: python3
+Description: VPN software for distributed networks
+ secnet allows multiple private networks, each 'hidden' behind a single
+ globally-routable IP address, to be bridged together.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..243dbd0
--- /dev/null
@@ -0,0 +1,33 @@
+This is secnet packages for Debian GNU/Linux systems and derivatives.
+
+secnet is
+  Copyright 1995-2003 Stephen Early <steve@greenend.org.uk>
+  Copyright 2002-2014 Ian Jackson <ijackson@chiark.greenend.org.uk>
+  Copyright 1991      Massachusetts Institute of Technology
+  Copyright 1998      Ross Anderson, Eli Biham, Lars Knudsen
+  Copyright 1993      Colin Plumb
+  Copyright 1998      James H. Brown, Steve Reid
+  Copyright 2000      Vincent Rijmen, Antoon Bosselaers, Paulo Barreto
+  Copyright 2001      Saul Kravitz
+  Copyright 2004      Fabrice Bellard
+  Copyright 2002      Guido Draheim
+  Copyright 2005-2010 Free Software Foundation, Inc.
+  Copyright 1995-2001 Jonathan Amery
+  Copyright 1995-2003 Peter Benie
+  Copyright 2011      Richard Kettlewell
+  Copyright 2012      Matthew Vernon
+  Copyright 2013      Mark Wooding
+  Copyright 1995-2013 Simon Tatham
+  Copyright 2012,2013 "Omnifarious" and "btel" on Stackoverflow
+
+The Debian package, additionally, is:
+  Copyright 2001      Joey Hess
+
+secnet is distributed under the terms of the GNU General Public
+License, version 3 or later.  A copy of this licence can be found on
+your Debian system in /usr/share/common-licenses/GPL-3.
+
+secnet 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.  See the GNU General Public License
+for more details.
diff --git a/debian/default b/debian/default
new file mode 100644 (file)
index 0000000..63f2c5b
--- /dev/null
@@ -0,0 +1 @@
+RUN_SECNET=no
diff --git a/debian/init b/debian/init
new file mode 100644 (file)
index 0000000..8df268b
--- /dev/null
@@ -0,0 +1,84 @@
+#! /bin/sh
+# /etc/init.d/secnet
+#
+# skeleton     example file to build /etc/init.d/ scripts.
+#              This file should be used to construct scripts for /etc/init.d.
+#
+#              Written by Miquel van Smoorenburg <miquels@cistron.nl>.
+#              Modified for Debian GNU/Linux
+#              by Ian Murdock <imurdock@gnu.ai.mit.edu>.
+#
+# Version:     @(#)skeleton  1.8  03-Mar-1998  miquels@cistron.nl
+#
+
+### BEGIN INIT INFO
+# Provides:          secnet
+# Required-Start:    $network $local_fs $remote_fs
+# Required-Stop:     $network $local_fs $remote_fs
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: Start and stop secnet
+# Description:       secnet is a VPN server.
+### END INIT INFO
+
+set -e
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/secnet
+NAME=secnet
+DESC="VPN server"
+
+test -f $DAEMON || exit 0
+test -f /etc/secnet/secnet.conf || exit 0
+test -f /etc/default/secnet && . /etc/default/secnet
+
+[ "X$RUN_SECNET" = "Xyes" ] || exit 0
+
+case "$1" in
+  start)
+       echo -n "Starting $DESC: "
+       start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
+               --exec $DAEMON
+       echo "$NAME."
+       ;;
+  stop)
+       echo -n "Stopping $DESC: "
+       start-stop-daemon --stop --quiet --oknodo --pidfile \
+           /var/run/$NAME.pid --exec $DAEMON
+       echo "$NAME."
+       ;;
+  #reload)
+       #
+       #       If the daemon can reload its config files on the fly
+       #       for example by sending it SIGHUP, do it here.
+       #
+       #       If the daemon responds to changes in its config file
+       #       directly anyway, make this a do-nothing entry.
+       #
+       # echo "Reloading $DESC configuration files."
+       # start-stop-daemon --stop --signal 1 --quiet --pidfile \
+       #       /var/run/$NAME.pid --exec $DAEMON
+  #;;
+  restart|force-reload)
+       #
+       #       If the "reload" option is implemented, move the "force-reload"
+       #       option to the "reload" entry above. If not, "force-reload" is
+       #       just the same as "restart".
+       #
+       echo -n "Restarting $DESC: "
+       start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
+               --exec $DAEMON
+       sleep 1
+       start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
+               --exec $DAEMON
+       echo "$NAME."
+       ;;
+  *)
+       N=/etc/init.d/$NAME
+       # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
+       echo "Usage: $N {start|stop|restart|force-reload}" >&2
+       exit 1
+       ;;
+esac
+
+exit 0
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..f94e752
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# This file is public domain software, originally written by Joey Hess. 
+# Modified for secnet by Stephen Early <steve@greenend.org.uk>
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+#  This file is Free Software.  It has been incorporated into, and
+#  extensively modified, for secnet.
+#
+#  Copyright 2001      Joey Hess
+#  Copyright 2011-2014 Ian Jackson
+#
+#  You may redistribute this file (and the other source files in the
+#  debian/ subdirectory) freely - the copyrightholders declare that
+#  they wish these files to be in the public domain.
+#
+#  You may redistribute secnet as a whole and/or modify it under the
+#  terms of the GNU General Public License as published by the Free
+#  Software Foundation; either version 3, or (at your option) any
+#  later version.
+#
+#  This software 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.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this software; if not, see
+#  https://www.gnu.org/licenses/gpl.html.
+
+export EXTRA_CFLAGS= $(shell dpkg-buildflags --get CPPFLAGS) \
+                    $(shell dpkg-buildflags --get CFLAGS)
+export EXTRA_LDFLAGS=$(shell dpkg-buildflags --get LDFLAGS)
+
+build build-arch: build-stamp
+build-stamp:
+       dh_testdir
+
+       # Add here commands to compile the package.
+       ./configure --prefix=/usr --sysconfdir=/etc && $(MAKE)
+
+       touch build-stamp
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp
+
+       # Add here commands to clean up after the build process.
+       -$(MAKE) realclean
+
+       dh_clean
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+
+       # Add here commands to install the package into debian/<packagename>
+       $(MAKE) DESTDIR=`pwd`/debian/`dh_listpackages` install
+
+# Build architecture-independent files here.
+build-indep binary-indep:
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+#      dh_installdebconf       
+       dh_installdocs INSTALL README NOTES TODO NEWS BUGS CREDITS
+       dh_installexamples example.conf
+#      dh_installmenu
+#      dh_installlogrotate
+#      dh_installemacsen
+#      dh_installpam
+#      dh_installmime
+       dh_installinit
+#      dh_installcron
+       dh_installman
+#      dh_installinfo
+       dh_installchangelogs
+       dh_link
+       dh_strip
+       dh_compress
+       dh_fixperms
+#      dh_makeshlibs
+       dh_installdeb
+#      dh_perl
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/dh.c b/dh.c
new file mode 100644 (file)
index 0000000..0616a43
--- /dev/null
+++ b/dh.c
@@ -0,0 +1,167 @@
+/*
+ * dh.c
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ *
+ * Copyright 1995-2003 Stephen Early
+ * Copyright 2002-2014 Ian Jackson
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include <stdio.h>
+#include <gmp.h>
+#include <limits.h>
+
+#include "secnet.h"
+#include "util.h"
+
+struct dh {
+    closure_t cl;
+    struct dh_if ops;
+    struct cloc loc;
+    MP_INT p,g; /* prime modulus and generator */
+};
+
+static string_t dh_makepublic(void *sst, uint8_t *secret, int32_t secretlen)
+{
+    struct dh *st=sst;
+    string_t r;
+    MP_INT a, b; /* a is secret key, b is public key */
+
+    mpz_init(&a);
+    mpz_init(&b);
+
+    read_mpbin(&a, secret, secretlen);
+
+    mpz_powm_sec(&b, &st->g, &a, &st->p);
+
+    r=write_mpstring(&b);
+
+    mpz_clear(&a);
+    mpz_clear(&b);
+    return r;
+}
+
+static dh_makeshared_fn dh_makeshared;
+static void dh_makeshared(void *sst, uint8_t *secret, int32_t secretlen,
+                         cstring_t rempublic, uint8_t *sharedsecret,
+                         int32_t buflen)
+{
+    struct dh *st=sst;
+    MP_INT a, b, c;
+
+    mpz_init(&a);
+    mpz_init(&b);
+    mpz_init(&c);
+
+    read_mpbin(&a, secret, secretlen);
+    mpz_set_str(&b, rempublic, 16);
+
+    mpz_powm_sec(&c, &b, &a, &st->p);
+
+    write_mpbin(&c,sharedsecret,buflen);
+
+    mpz_clear(&a);
+    mpz_clear(&b);
+    mpz_clear(&c);
+}
+
+static list_t *dh_apply(closure_t *self, struct cloc loc, dict_t *context,
+                       list_t *args)
+{
+    struct dh *st;
+    string_t p,g;
+    item_t *i;
+
+    NEW(st);
+    st->cl.description="dh";
+    st->cl.type=CL_DH;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.makepublic=dh_makepublic;
+    st->ops.makeshared=dh_makeshared;
+    st->loc=loc;
+    /* We have two string arguments: the first is the modulus, and the
+       second is the generator. Both are in hex. */
+    i=list_elem(args,0);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"diffie-hellman","first argument must be a "
+                    "string\n");
+       }
+       p=i->data.string;
+       if (mpz_init_set_str(&st->p,p,16)!=0) {
+           cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number "
+                    "string\n",p);
+       }
+    } else {
+       cfgfatal(loc,"diffie-hellman","you must provide a prime modulus\n");
+    }
+    
+    i=list_elem(args,1);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"diffie-hellman","second argument must be a "
+                    "string\n");
+       }
+       g=i->data.string;
+       if (mpz_init_set_str(&st->g,g,16)!=0) {
+           cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number "
+                    "string\n",g);
+       }
+    } else {
+       cfgfatal(loc,"diffie-hellman","you must provide a generator\n");
+    }
+
+    i=list_elem(args,2);
+    if (i && i->type==t_bool && i->data.bool==False) {
+       Message(M_INFO,"diffie-hellman (%s:%d): skipping modulus "
+               "primality check\n",loc.file,loc.line);
+    } else {
+       /* Test that the modulus is really prime */
+       if (mpz_probab_prime_p(&st->p,5)==0) {
+           cfgfatal(loc,"diffie-hellman","modulus must be a prime\n");
+       }
+    }
+
+    size_t sz=mpz_sizeinbase(&st->p,2)/8;
+    if (sz>INT_MAX) {
+       cfgfatal(loc,"diffie-hellman","modulus far too large\n");
+    }
+    if (mpz_cmp(&st->g,&st->p) >= 0) {
+       cfgfatal(loc,"diffie-hellman","generator must be less than modulus\n");
+    }
+
+    st->ops.len=sz;
+
+    st->ops.ceil_len=(mpz_sizeinbase(&st->p,2)+7)/8;
+    /* According to the docs, mpz_sizeinbase(,256) is allowed to return
+     * an answer which is 1 too large.  But mpz_sizeinbase(,2) isn't. */
+
+    return new_closure(&st->cl);
+}
+
+void dh_module(dict_t *dict)
+{
+    add_closure(dict,"diffie-hellman",dh_apply);
+}
diff --git a/eax-aes-test.c b/eax-aes-test.c
new file mode 100644 (file)
index 0000000..1838553
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * eax-aes-test.c: test harness glue for EAX-AES (EAX-Rijndael)
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ *
+ * Copyright 2013 Ian Jackson
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+/*
+ * The corresponding test vector file is eax-aes-test.vectors.  It was
+ * copied out of the AES (Rijndael) paper.  I don't believe it is a
+ * creative work that attracts copyright.  -iwj.
+ */
+
+#include "eax-test.h"
+#include "aes.h"
+
+#define BLOCK_SIZE AES_BLOCK_SIZE
+static AES_KEY key;
+
+EAX_SOME_TEST;
+
+void eaxtest_blockcipher_key_setup(const uint8_t *keydata, uint8_t bytes)
+{
+    AES_set_encrypt_key(keydata, bytes*8, &key);
+}
+
+static void BLOCK_ENCRYPT(uint8_t dst[BLOCK_SIZE],
+                         const uint8_t src[BLOCK_SIZE])
+{
+    AES_encrypt((const void*)src, (void*)dst, &key);
+}
+
+#include "eax.c"
diff --git a/eax-aes-test.vectors b/eax-aes-test.vectors
new file mode 100644 (file)
index 0000000..f6bf3f4
--- /dev/null
@@ -0,0 +1,59 @@
+MSG:
+KEY:    233952DEE4D5ED5F9B9C6D6FF80FF478
+NONCE:  62EC67F9C3A4A407FCB2A8C49031A8B3
+HEADER: 6BFB914FD07EAE6B
+CIPHER: E037830E8389F27B025A2D6527E79D01
+
+MSG:    F7FB
+KEY:    91945D3F4DCBEE0BF45EF52255F095A4
+NONCE:  BECAF043B0A23D843194BA972C66DEBD
+HEADER: FA3BFD4806EB53FA
+CIPHER: 19DD5C4C9331049D0BDAB0277408F67967E5
+
+MSG:    1A47CB4933
+KEY:    01F74AD64077F2E704C0F60ADA3DD523
+NONCE:  70C3DB4F0D26368400A10ED05D2BFF5E
+HEADER: 234A3463C1264AC6
+CIPHER: D851D5BAE03A59F238A23E39199DC9266626C40F80
+
+MSG:    481C9E39B1
+KEY:    D07CF6CBB7F313BDDE66B727AFD3C5E8
+NONCE:  8408DFFF3C1A2B1292DC199E46B7D617
+HEADER: 33CCE2EABFF5A79D
+CIPHER: 632A9D131AD4C168A4225D8E1FF755939974A7BEDE
+
+MSG:    40D0C07DA5E4
+KEY:    35B6D0580005BBC12B0587124557D2C2
+NONCE:  FDB6B06676EEDC5C61D74276E1F8E816
+HEADER: AEB96EAEBE2970E9
+CIPHER: 071DFE16C675CB0677E536F73AFE6A14B74EE49844DD
+
+MSG:    4DE3B35C3FC039245BD1FB7D
+KEY:    BD8E6E11475E60B268784C38C62FEB22
+NONCE:  6EAC5C93072D8E8513F750935E46DA1B
+HEADER: D4482D1CA78DCE0F
+CIPHER: 835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F
+
+MSG:    8B0A79306C9CE7ED99DAE4F87F8DD61636
+KEY:    7C77D6E813BED5AC98BAA417477A2E7D
+NONCE:  1A8C98DCD73D38393B2BF1569DEEFC19
+HEADER: 65D2017990D62528
+CIPHER: 02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2
+
+MSG:    1BDA122BCE8A8DBAF1877D962B8592DD2D56
+KEY:    5FFF20CAFAB119CA2FC73549E20F5B0D
+NONCE:  DDE59B97D722156D4D9AFF2BC7559826
+HEADER: 54B9F04E6A09189A
+CIPHER: 2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A
+
+MSG:    6CF36720872B8513F6EAB1A8A44438D5EF11
+KEY:    A4A4782BCFFD3EC5E7EF6D8C34A56123
+NONCE:  B781FCF2F75FA5A8DE97A9CA48E522EC
+HEADER: 899A175897561D7E
+CIPHER: 0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700
+
+MSG:    CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7
+KEY:    8395FCF1E95BEBD697BD010BC766AAC3
+NONCE:  22E7ADD93CFC6393C57EC0B3C17D6B44
+HEADER: 126735FCC320D25A
+CIPHER: CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E
diff --git a/eax-serpent-test.c b/eax-serpent-test.c
new file mode 100644 (file)
index 0000000..9c7133b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * eax-serpent-test.c: test harness glue for EAX-Serpent
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ *
+ * Copyright 2013 Ian Jackson
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+/*
+ * The corresponding test vector files are eax-serpent-test.vectors
+ * and eax-serpentbe-test.vectors.  eax-serpent-test.vectors was
+ * provided by Mark Wooding and eax-serpentbe-test.vectors was
+ * generated by this file (in its guise as eax-serpentbe-test).  I
+ * don't believe these test vecctors are creative works that attract
+ * copyright.  -iwj.
+ */
+
+#include "eax-test.h"
+#include "serpent.h"
+
+#define BLOCK_SIZE 16
+static struct keyInstance key;
+
+EAX_SOME_TEST;
+
+void eaxtest_blockcipher_key_setup(const uint8_t *keydata, uint8_t bytes)
+{
+    serpent_makekey(&key, bytes*8, keydata);
+}
+
+static void BLOCK_ENCRYPT(uint8_t dst[BLOCK_SIZE],
+                         const uint8_t src[BLOCK_SIZE])
+{
+    serpent_encrypt(&key, src, dst);
+}
+
+#include "eax.c"
diff --git a/eax-serpent-test.vectors b/eax-serpent-test.vectors
new file mode 100644 (file)
index 0000000..e016783
--- /dev/null
@@ -0,0 +1,59 @@
+MSG: 
+KEY: 233952DEE4D5ED5F9B9C6D6FF80FF478
+NONCE: 62EC67F9C3A4A407FCB2A8C49031A8B3
+HEADER: 6BFB914FD07EAE6B
+CIPHER: 1271EC1E68330EB461A96D3A3A7A2707
+
+MSG: F7FB
+KEY: 91945D3F4DCBEE0BF45EF52255F095A4
+NONCE: BECAF043B0A23D843194BA972C66DEBD
+HEADER: FA3BFD4806EB53FA
+CIPHER: 1C7367D3DB493A1F7B054ECECA2A2CF37EE6
+
+MSG: 1A47CB4933
+KEY: 01F74AD64077F2E704C0F60ADA3DD523
+NONCE: 70C3DB4F0D26368400A10ED05D2BFF5E
+HEADER: 234A3463C1264AC6
+CIPHER: 2439712B59B13982351BA05B25BB2BD3B95DF62D73
+
+MSG: 481C9E39B1
+KEY: D07CF6CBB7F313BDDE66B727AFD3C5E8
+NONCE: 8408DFFF3C1A2B1292DC199E46B7D617
+HEADER: 33CCE2EABFF5A79D
+CIPHER: F1D718884BE94B29E143A264B54E283CA9E439C90D
+
+MSG: 40D0C07DA5E4
+KEY: 35B6D0580005BBC12B0587124557D2C2
+NONCE: FDB6B06676EEDC5C61D74276E1F8E816
+HEADER: AEB96EAEBE2970E9
+CIPHER: 5936DB85DF31199BA3556A5D5EFF1964A6BEFEA0D950
+
+MSG: 4DE3B35C3FC039245BD1FB7D
+KEY: BD8E6E11475E60B268784C38C62FEB22
+NONCE: 6EAC5C93072D8E8513F750935E46DA1B
+HEADER: D4482D1CA78DCE0F
+CIPHER: 7A3A7997EE349B57152CC43F723903A85B09D86456315AC0D9180724
+
+MSG: 8B0A79306C9CE7ED99DAE4F87F8DD61636
+KEY: 7C77D6E813BED5AC98BAA417477A2E7D
+NONCE: 1A8C98DCD73D38393B2BF1569DEEFC19
+HEADER: 65D2017990D62528
+CIPHER: 73548FFAF45D2617EB25AD1DFFA18420836D48394D5EF2CD2E0E30CDD2F4C52D96
+
+MSG: 1BDA122BCE8A8DBAF1877D962B8592DD2D56
+KEY: 5FFF20CAFAB119CA2FC73549E20F5B0D
+NONCE: DDE59B97D722156D4D9AFF2BC7559826
+HEADER: 54B9F04E6A09189A
+CIPHER: E8BD1C6FE47DF149A141CE813B0C1239542EC4CBF7B3968388D631E6F4FFE86E14E7
+
+MSG: 6CF36720872B8513F6EAB1A8A44438D5EF11
+KEY: A4A4782BCFFD3EC5E7EF6D8C34A56123
+NONCE: B781FCF2F75FA5A8DE97A9CA48E522EC
+HEADER: 899A175897561D7E
+CIPHER: E4A9D72847D437B85F10B7DAA46F1E00E3509AF0B97961C39DFBB70170B6C4CADBC1
+
+MSG: CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7
+KEY: 8395FCF1E95BEBD697BD010BC766AAC3
+NONCE: 22E7ADD93CFC6393C57EC0B3C17D6B44
+HEADER: 126735FCC320D25A
+CIPHER: 83D69403EAE9386B679DAEAAD2951465F8DDF9BE1AFFAD1C5FEF072F8B48BD58C07FEE3D83
diff --git a/eax-serpentbe-test.c b/eax-serpentbe-test.c
new file mode 100644 (file)
index 0000000..ce5b3dc
--- /dev/null
@@ -0,0 +1,9 @@
+#include "eax-test.h"
+#include "serpent.h"
+/* multiple-inclusion protection means that serpent.h's inclusion
+ * by eax-serpent-test.c is suppressed, so we don't get useless
+ * duplicate declarations of serpentbe_makekey and serpentbe_encrypt
+ */
+#define serpent_makekey serpentbe_makekey
+#define serpent_encrypt serpentbe_encrypt
+#include "eax-serpent-test.c"
diff --git a/eax-serpentbe-test.vectors b/eax-serpentbe-test.vectors
new file mode 100644 (file)
index 0000000..d7e4e6e
--- /dev/null
@@ -0,0 +1,59 @@
+MSG:
+KEY:    233952DEE4D5ED5F9B9C6D6FF80FF478
+NONCE:  62EC67F9C3A4A407FCB2A8C49031A8B3
+HEADER: 6BFB914FD07EAE6B
+CIPHER: E667316E3FC40DF234575E203EA06EA0
+
+MSG:    F7FB
+KEY:    91945D3F4DCBEE0BF45EF52255F095A4
+NONCE:  BECAF043B0A23D843194BA972C66DEBD
+HEADER: FA3BFD4806EB53FA
+CIPHER: D6D0A45C2A76EEB6AD20C3DB5CE100A2AEC4
+
+MSG:    1A47CB4933
+KEY:    01F74AD64077F2E704C0F60ADA3DD523
+NONCE:  70C3DB4F0D26368400A10ED05D2BFF5E
+HEADER: 234A3463C1264AC6
+CIPHER: FD8218F8987B7CCEBE4D521F4374D40B2F85794B31
+
+MSG:    481C9E39B1
+KEY:    D07CF6CBB7F313BDDE66B727AFD3C5E8
+NONCE:  8408DFFF3C1A2B1292DC199E46B7D617
+HEADER: 33CCE2EABFF5A79D
+CIPHER: 529951EDB28B9557667E88ED360EB51256DEC0F056
+
+MSG:    40D0C07DA5E4
+KEY:    35B6D0580005BBC12B0587124557D2C2
+NONCE:  FDB6B06676EEDC5C61D74276E1F8E816
+HEADER: AEB96EAEBE2970E9
+CIPHER: F46A8BFED3B22A6E4388659FF1C39B3D49AAD8ADEA74
+
+MSG:    4DE3B35C3FC039245BD1FB7D
+KEY:    BD8E6E11475E60B268784C38C62FEB22
+NONCE:  6EAC5C93072D8E8513F750935E46DA1B
+HEADER: D4482D1CA78DCE0F
+CIPHER: C3C7281CE5790F14D4CD666E8494D4911D528548F200014C32B86719
+
+MSG:    8B0A79306C9CE7ED99DAE4F87F8DD61636
+KEY:    7C77D6E813BED5AC98BAA417477A2E7D
+NONCE:  1A8C98DCD73D38393B2BF1569DEEFC19
+HEADER: 65D2017990D62528
+CIPHER: 1DD9A93ACD19F0C3FB7A3B431DFCFB96D2B899FA2285AEC7DCA504AF75B97A58A3
+
+MSG:    1BDA122BCE8A8DBAF1877D962B8592DD2D56
+KEY:    5FFF20CAFAB119CA2FC73549E20F5B0D
+NONCE:  DDE59B97D722156D4D9AFF2BC7559826
+HEADER: 54B9F04E6A09189A
+CIPHER: 2BA4C477F2927210ADCA26DF72E2BEF81BF3D6B03160E7BFE7FD3EB57D255D66713F
+
+MSG:    6CF36720872B8513F6EAB1A8A44438D5EF11
+KEY:    A4A4782BCFFD3EC5E7EF6D8C34A56123
+NONCE:  B781FCF2F75FA5A8DE97A9CA48E522EC
+HEADER: 899A175897561D7E
+CIPHER: A17D854BA33FDBDF0BA86ADC9152D40B4EA01E1A8FAB1E0A80B19E73784219B29446
+
+MSG:    CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7
+KEY:    8395FCF1E95BEBD697BD010BC766AAC3
+NONCE:  22E7ADD93CFC6393C57EC0B3C17D6B44
+HEADER: 126735FCC320D25A
+CIPHER: 9F30626B590A0A6E2F6CE0ED8835031654B3FCCF311BD7A6C6089AF1C7373D22CB80D4AFEE
diff --git a/eax-test.c b/eax-test.c
new file mode 100644 (file)
index 0000000..ea63fdf
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * eax-test.c: test harness for EAX, implementation
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ *
+ * Copyright 2013 Ian Jackson
+ * Copyright 2013 Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+/*
+ * usages:
+ *   ./eax-foo-test <eax-foo-test.vectors
+ *      runs the test vectors, regenerates the file on stdout
+ *   grep -v CIPHER <eax-foo-test.vectors | ./eax-foo-test
+ *      generates output with CIPHER lines reinserted
+ * All errors result in calls to abort().
+ */
+
+#include "eax-test.h"
+
+struct valbuf {
+    _Bool got;
+    uint8_t v[1024];
+    size_t len;
+};
+#define V(vb) ((vb).v), ((vb).len)
+
+static struct valbuf msg, key, nonce, header, cipher, ourcipher, returnplain;
+static size_t tau;
+
+static void trydecrypt(_Bool expected)
+{
+    _Bool actual = eax_decrypt(-1, V(nonce), V(header), V(ourcipher), tau,
+                              returnplain.v);
+    assert(actual == expected);
+    if (actual) {
+       returnplain.len = ourcipher.len - tau;
+       assert(returnplain.len == msg.len);
+       assert(!memcmp(returnplain.v, msg.v, msg.len));
+    }
+}
+
+static void negtest(struct valbuf *perturb)
+{
+    unsigned delta = 0x04;
+    size_t i;
+    for (i=0; i<perturb->len; i++) {
+       perturb->v[i] ^= delta;
+       trydecrypt(0);
+       perturb->v[i] ^= delta;
+    }
+}
+
+static void something(void)
+{
+    if (!msg.got) return;
+    assert(key.got);
+    assert(nonce.got);
+    assert(header.got);
+    eaxtest_blockcipher_key_setup(V(key));
+    eax_setup(-1);
+    if (cipher.got) {
+       assert(cipher.len > msg.len);
+       tau = cipher.len - msg.len;
+       assert(tau <= blocksize);
+    } else {
+       assert(msg.len + blocksize < sizeof(ourcipher.v));
+       tau = blocksize;
+    }
+    ourcipher.len = msg.len + tau;
+    eax_encrypt(-1, V(nonce), V(header), V(msg), tau, ourcipher.v);
+    if (cipher.got) {
+       assert(ourcipher.len == cipher.len);
+       assert(!memcmp(ourcipher.v, cipher.v, cipher.len));
+       trydecrypt(1);
+       negtest(&ourcipher);
+       negtest(&header);
+    } else {
+       size_t i;
+       printf("CIPHER: ");
+       for (i=0; i<ourcipher.len; i++)
+           printf("%02X", ourcipher.v[i]);
+       putchar('\n');
+    }
+    msg.got=key.got=nonce.got=header.got=0;
+}
+
+static int getputchar(void)
+{
+    int c = getchar();
+    assert(c != EOF);
+    putchar(c);
+    return c;
+}
+
+int main(int argc, const char *const *argv)
+{
+    struct valbuf *cv;
+
+    assert(argc==1);
+
+    for (;;) {
+       int c = getchar();
+       switch (c) {
+       case 'M':  something();  cv = &msg;     putchar(c);  break;
+       case 'K':                cv = &key;     putchar(c);  break;
+       case 'N':                cv = &nonce;   putchar(c);  break;
+       case 'H':                cv = &header;  putchar(c);  break;
+       case 'C':                cv = &cipher;  putchar(c);  break;
+       case '\n':                              putchar(c);  continue;
+       case EOF:  something();  exit(0);
+       default:   assert(!"unexpected input");
+       }
+       cv->got = 1;
+       cv->len = 0;
+       for (;;) {
+           c = getputchar();
+           if (c == ':') break;
+           assert(isalpha(c));
+       }
+       for (;;) {
+           char hbuf[3], *ep;
+           c = getputchar();
+           if (c == '\n') break;
+           if (isspace(c)) continue;
+           assert(isprint(c));
+           hbuf[0] = c;
+           c = getputchar();
+           assert(isprint(c));
+           hbuf[1] = c;
+           hbuf[2] = 0;
+           assert(cv->len < sizeof(cv->v));
+           cv->v[cv->len++] = strtoul(hbuf,&ep,16);
+           assert(!*ep);
+       }
+    }
+    assert(!ferror(stdin));
+    assert(feof(stdin));
+    assert(!ferror(stdout));
+    assert(!fflush(stdout));
+    return 0;
+}
diff --git a/eax-test.h b/eax-test.h
new file mode 100644 (file)
index 0000000..f6eb201
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * eax-test.c: test harness for EAX, common declarations
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ * See README for full list of copyright holders.
+ *
+ * Copyright 2013 Ian Jackson
+ * Copyright 2013 Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+
+#ifndef EAX_TEST_H
+#define EAX_TEST_H
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#define INFO                int dummy_info
+#define I                   dummy_info
+#define EAX_ENTRYPOINT_DECL /* empty */
+
+#define EAX_DECLARATIONS_ONLY
+#include "eax.c"
+#undef EAX_DECLARATIONS_ONLY
+
+void eaxtest_blockcipher_key_setup(const uint8_t *keydata, uint8_t bytes);
+
+#define consttime_memeq(s1,s2,sz) (!memcmp((s1),(s2),(sz)))
+    /* fine for running test vectors */
+
+extern const size_t blocksize;
+
+#define EAX_SOME_TEST                                          \
+    const size_t blocksize = BLOCK_SIZE;                       \
+    static uint8_t INFO_B[BLOCK_SIZE], INFO_P[BLOCK_SIZE]
+
+#endif /* EAX_TEST_H */
diff --git a/eax.c b/eax.c
new file mode 100644 (file)
index 0000000..a32baac
--- /dev/null
+++ b/eax.c
@@ -0,0 +1,346 @@
+/*
+ * eax.c: implementation of the EAX authenticated encryption block cipher mode
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ *
+ * Copyright 2013 Ian Jackson
+ * Copyright 2013 Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+/*
+ * This file is designed to be #included into another .c file which
+ * sets up the environment.  It will declare or define three
+ * functions, eax_setup, eax_encrypt and eax_decrypt.
+ *
+ * Manifest constants which are expected to be defined:
+ *
+ *  INFO       One or more formal parameter definitions.
+ *             Used in all relevant function declarations.  Typically
+ *             the application will use this for its context pointer,
+ *             key schedule structure, etc.
+ *
+ *  I          Corresponding actual parameters for chaining onto
+ *             sub-functions declared to take INFO parameters
+ *
+ *  EAX_ENTRYPOINT_DECL
+ *             Declarator decoration for the three entry points.
+ *             Typically either "static" or the empty string;
+ *
+ *  EAX_DECLARATIONS_ONLY
+ *             Tested with #ifdef, so should be #defined to 1, or left
+ *             undefined.  If defined, #including eax.c will produces
+ *             only the function prototypes for the three entrypoints.
+ *             Otherwise it will produce the implementation.
+ *
+ *  BLOCK_SIZE
+ *             Constant expresion of integer type.
+ *
+ *  void BLOCK_ENCRYPT(uint8_t dst[n], const uint8_t src[n]);
+ *
+ *             Function to encrypt with the selected key.  The block
+ *             cipher's key schedule (ie, the key) to be used is
+ *             implicit; uses of BLOCK_ENCRYPT always occur in a
+ *             context where the parameters defined by INFO are
+ *             available.
+ *
+ *             So in a real application which wants to use more than
+ *             one key at a time, BLOCK_ENCRYPT must be a macro which
+ *             accesses the block cipher's key schedule via one of the
+ *             INFO parameters.
+ *
+ *             BLOCK_ENCRYPT must tolerate dst==src.
+ *
+ *             EAX does not need to use the block cipher's decryption
+ *             function.
+ *
+ *  uint8_t INFO_B[n], INFO_P[n];
+ *
+ *             Buffers used by the algorithm; they are written by
+ *             eax_setup and used by eax_encrypt and eax_decrypt.
+ *
+ *             That is, effectively they are the part of the key
+ *             schedule specific to EAX.
+ *
+ *             An application which wants to use more than one key at
+ *             a time must define these as macros which refer to
+ *             key-specific variables via one of the INFO parameters.
+ *
+ *  int consttime_memeq(const void *s1, const void *s2, size_t n);
+ *
+ *             Like !memcmp(s1,s2,n) but takes the same amount of time
+ *             no matter where the discrepancy is.  Result must be
+ *             a canonicalised boolean.
+ *
+ * The entrypoints which are then defined are:
+ *
+ *  void eax_setup(INFO)
+ *
+ *      Does the EAX-specific part of the key setup.  The block
+ *      cipher key schedule must already have been done, as
+ *      eax_setup uses BLOCK_ENCRYPT.
+ *
+ *  void eax_encrypt(INFO, const uint8_t nonce[nonce_len], size_t nonce_len,
+ *                         const uint8_t h[h_len], size_t h_len,
+ *                         const uint8_t m[m_len], size_t m_len,
+ *                         uint8_t tau,
+ *                         uint8_t ct[m_len+tau])
+ *
+ *      Does a single EAX authenticated encryption, providing
+ *      confidentiality and integrity to the message m[0..m_len-1].
+ *
+ *      The output message is longer than m by tau bytes and is stored
+ *      in the array ct which must be big enough.
+ *
+ *      nonce must never be repeated with the same key or the security
+ *      of the system is destroyed, but it does not need to be secret.
+ *      It is OK to transmit the nonce with the message along with the
+ *      ciphertext and have the receiver recover it.
+ *
+ *      h is the "header" - it is some extra data which should be
+ *      covered by the authentication, but not the encryption.  The
+ *      output message does not contain a representation of h: it is
+ *      expected to be transmitted separately (perhaps even in a
+ *      different format).  (If h_len==0, h may be a NULL pointer.)
+ *
+ *      tau is the tag length - that is, the length of the message
+ *      authentication code.  It should be chosen for the right
+ *      tradeoff between message expansion and security (resistence to
+ *      forgery).  It must be no longer than the block cipher block
+ *      size.
+ *
+ *      For any particular key.  the tag length should be fixed.  (The
+ *      tag length should NOT be encoded into the packet along with
+ *      the ciphertext and extracted by the receiver!  Rather, the
+ *      receiver must know what tag length to expect.)
+ *
+ *      It is permissible for ct==m, or for the arrays to be disjoint.
+ *      They must not overlap more subtly.
+ *
+ *  _Bool eax_decrypt(INFO, const uint8_t nonce[nonce_len], size_t nonce_len,
+ *                          const uint8_t h[h_len], size_t h_len,
+ *                          const uint8_t ct[ct_len], size_t ct_len,
+ *                          uint8_t tau,
+ *                          uint8_t m[ct_len-tau])
+ *
+ *      Does a single EAX authenticated decryption.
+ *
+ *      On successful return, the plaintext message is written to m
+ *      and eax_decrypt returns true.  The length of the plaintext
+ *      message is always ct_len-tau.
+ *
+ *      If the message did not decrypt correctly, returns false.
+ *      (There is no further indication of the nature of the error.)
+ *      In this case the buffer m may contain arbitrary contents which
+ *      should not be revealed to attackers.
+ *
+ *      nonce, h, tau are as above.
+ *
+ *      It is permissible to call eax_decrypt with an input message
+ *      which is too short (i.e. shorter than tau) (notwithstanding
+ *      the notation m[ct_len-tau] in the faux prototype above).
+ *      In this case it will return false without touching m.
+ *
+ *      As with eax_decrypt, ct==m is permissible.
+ */
+
+/***** IMPLEMENTATION *****/
+
+/*
+ * We use the notation from the EAX paper, mostly.
+ * (We write xscr for "x in fancy mathsy curly script".)
+ *
+ * See:
+ *  Mihir Bellare, Phillip Rogaway, and David Wagner
+ *
+ *  _The EAX Mode of Operation
+ *   (A Two-Pass Authenticated Encryption Scheme
+ *   Optimized for Simplicity and Efficiency)_
+ *
+ * Preliminary version in:
+ *   Fast Software Encryption (FSE) 2004. Lecture Notes in Computer Science,
+ *   vol. ??, pp. ??--??.
+ *
+ * Full version at:
+ *  http://www.cs.ucdavis.edu/~rogaway/papers/eax.html
+ */
+/*
+ * In general, all functions tolerate their destination arrays being
+ * the same pointer to their source arrays, or totally distinct.
+ * (Just like BLOCK_ENCRYPT and the public eax entrypoints.)
+ * They must not overlap in more subtle ways.
+ */
+
+#define n ((size_t)BLOCK_SIZE)
+
+#ifndef EAX_DECLARATIONS_ONLY
+
+static void xor_block(uint8_t *dst, const uint8_t *a, const uint8_t *b,
+                      size_t l)
+    /* simple block xor */
+{
+    while (l--)
+        *dst++ = *a++ ^ *b++;
+}
+
+static void increment(uint8_t *value)
+    /* value is a single block; incremented (BE) mod 256^n */
+{
+    uint8_t *p;
+    for (p=value+n; p>value; )
+        if ((*--p)++) break;
+}
+
+static void alg_ctr(INFO, uint8_t *c, const uint8_t *nscr,
+                    const uint8_t *m, size_t m_len)
+{
+    uint8_t blocknonce[n], cipher[n];
+    size_t in;
+
+    memcpy(blocknonce, nscr, n);
+    for (in=0; in<m_len; in+=n) {
+        BLOCK_ENCRYPT(cipher,blocknonce);
+        increment(blocknonce);
+        size_t now = m_len-in < n ? m_len-in : n;
+        xor_block(c+in, m+in, cipher, now);
+    }
+}
+
+static void alg_omac_t_k(INFO, uint8_t *mac_out, uint8_t t,
+                         const uint8_t *m, size_t m_len)
+{
+    /* Initial tweak. */
+    memset(mac_out, 0, n-1);
+    mac_out[n-1] = t;
+
+    /* All of the whole blocks. */
+    size_t in=0;
+    for (; in+n <= m_len; in+=n) {
+        BLOCK_ENCRYPT(mac_out, mac_out);
+        xor_block(mac_out, mac_out, m+in, n);
+    }
+
+    /* The last partial block, if there is one. */
+    assert(in <= m_len);
+    size_t remain = m_len - in;
+    if (!remain)
+        xor_block(mac_out, mac_out, INFO_B, n);
+    else {
+        BLOCK_ENCRYPT(mac_out, mac_out);
+        xor_block(mac_out, mac_out, m+in, remain);
+        mac_out[remain] ^= 0x80;
+        xor_block(mac_out, mac_out, INFO_P, n);
+    }
+
+    /* Final block-cipher application. */
+    BLOCK_ENCRYPT(mac_out, mac_out);
+}
+
+/*
+ * Constant-time multiply-by-x in F = GF(2^128), using the EAX representation
+ * F = GF(2)[x]/(x^128 + x^7 + x^2 + x + 1).
+ *
+ * The input vector V consists of the input polynomial L = a_127 x^127 +
+ * ... + a_1 x + a_0; specifically, the byte v[15 - i] contains a_{8i+7}
+ * x^{8i+7} + ... + a_{8i} x^{8i}.  The output vector O will contain L x on
+ * exit, using the same encoding.
+ *
+ * It is fine if O = V, or the two vectors are disjoint; Bad Things will
+ * happen if they overlap in some more complicated way.
+ */
+static void consttime_curious_multiply(INFO, uint8_t *o, const uint8_t *v)
+{
+#define POLY 0x87u
+
+  unsigned m = ~((v[0] >> 7) - 1u) & POLY;
+  unsigned i, mm;
+
+  for (i = n - 1; i < n; i--) {
+    mm = (v[i] >> 7) & 1u;
+    o[i] = (v[i] << 1) ^ m;
+    m = mm;
+  }
+
+#undef POLY
+}
+
+#endif /* not EAX_DECLARATIONS_ONLY */
+
+EAX_ENTRYPOINT_DECL
+void eax_setup(INFO)
+#ifndef EAX_DECLARATIONS_ONLY
+{
+    uint8_t work[n];
+    memset(work,0,n);
+    BLOCK_ENCRYPT(work,work);
+    consttime_curious_multiply(I, INFO_B, work);
+    consttime_curious_multiply(I, INFO_P, INFO_B);
+}
+#endif /* not EAX_DECLARATIONS_ONLY */
+;
+
+EAX_ENTRYPOINT_DECL
+void eax_encrypt(INFO,
+                 const uint8_t *nonce, size_t nonce_len,
+                 const uint8_t *h, size_t h_len,
+                 const uint8_t *m, size_t m_len, uint8_t tau, uint8_t *ct)
+#ifndef EAX_DECLARATIONS_ONLY
+{
+    assert(tau <= n);
+    uint8_t nscr[n], hscr[n], cscr[n];
+    alg_omac_t_k(I, nscr, 0, nonce,nonce_len);
+    alg_omac_t_k(I, hscr, 1, h,h_len);
+    alg_ctr(I, ct, nscr, m, m_len);
+    alg_omac_t_k(I, cscr, 2, ct, m_len);
+    uint8_t *t = ct + m_len;
+    xor_block(t, nscr, cscr, tau);
+    xor_block(t, t, hscr, tau);
+}
+#endif /* not EAX_DECLARATIONS_ONLY */
+;
+
+EAX_ENTRYPOINT_DECL
+_Bool eax_decrypt(INFO,
+                  const uint8_t *nonce, size_t nonce_len,
+                  const uint8_t *h, size_t h_len,
+                  const uint8_t *ct, size_t ct_len, uint8_t tau, uint8_t *m)
+#ifndef EAX_DECLARATIONS_ONLY
+{
+    assert(tau <= n);
+    const uint8_t *t;
+    uint8_t nscr[n], hscr[n], cscr[n], tprime[tau];
+    if (ct_len < tau) return 0;
+    size_t m_len = ct_len - tau;
+    t = ct + m_len;
+    alg_omac_t_k(I, nscr, 0, nonce,nonce_len);
+    alg_omac_t_k(I, hscr, 1, h,h_len);
+    alg_omac_t_k(I, cscr, 2, ct,m_len);
+    xor_block(tprime, nscr, cscr, tau);
+    xor_block(tprime, tprime, hscr, tau);
+    if (!consttime_memeq(tprime, t, tau)) return 0;
+    alg_ctr(I, m, nscr, ct, m_len);
+    return 1;
+}
+#endif /* not EAX_DECLARATIONS_ONLY */
+;
+
+#undef n
diff --git a/example.conf b/example.conf
new file mode 100644 (file)
index 0000000..f1d8758
--- /dev/null
@@ -0,0 +1,178 @@
+# secnet example configuration file
+
+# Log facility
+# If you use this unaltered you should consider providing automatic log
+# rotation for /var/log/secnet.  secnet will close and re-open its logfiles
+# when it receives SIGHUP.
+log logfile {
+       filename "/var/log/secnet";
+       class "info","notice","warning","error","security","fatal";
+       # There are some useful message classes that could replace
+       # this list:
+       #  'default' -> warning,error,security,fatal
+       #  'verbose' -> info,notice,default
+       #  'quiet'   -> fatal
+};
+
+# Alternatively you could log through syslog:
+# log syslog {
+#      ident "secnet";
+#      facility "local0";
+# };
+
+
+# Systemwide configuration (all other configuration is per-site):
+# log          a log facility for program messages
+# userid       who we try to run as after setup
+# pidfile
+system {
+       # Note that you should not specify 'userid' here unless secnet
+       # is being invoked as root.
+       userid "secnet";
+       pidfile "/var/run/secnet.pid";
+};
+
+# Parameters for each remote site (arguments to the site() closure):
+#  things we configure locally
+# buffer                buffer for constructing/sending/receiving packets
+# netlink              user/kernel netlink device for this tunnel
+# comm                 UDP communication
+# resolver             resolver to use for name lookups
+# log                  a log destination for this connection
+# log-events           string list: which events we log
+# random                a source of randomness
+
+#  our local configuration visible to the outside world
+# local-name           string: how we identify ourselves to them
+# local-key            our own private RSA key
+# local-port           port number we listen on
+
+#  their configuration visible to us
+# name                 string: how they identify themselves
+# address              string: use with resolver to find their IP address
+# networks             string list: their networks for us
+# key                  the remote site's RSA public key
+# port                 port we send to to contact remote site
+
+#  things both ends must agree on
+# transform             routine for bulk encryption
+# dh                   Diffie-Hellman parameters
+# hash                 secure hash function
+
+#  things both ends ought to agree on, but don't have to
+# key-lifetime          max session key lifetime, in milliseconds
+# setup-retries         max retransmits of a key setup packet
+# setup-timeout         wait between retransmits of key setup packets, in ms
+# wait-time             wait between unsuccessful key setup attempts, in ms
+# renegotiate-time      set up a new key if we see any traffic after this time
+
+# Defaults that may be overridden on a per-site basis:
+setup-retries 10;
+setup-timeout 2000;
+
+# Use the universal TUN/TAP driver to get packets to and from the kernel,
+# through a single interface.  secnet will act as a router; it requires
+# its own IP address which is specified below (you'll see it on traceroute,
+# etc. for routes that go via tunnels).  If you don't want secnet to act
+# as a router, and instead want a separate kernel network interface per
+# tunnel, then see the alternative configuration below
+
+# If you want to use userv-ipif to manage interfaces then replace the
+# word "tun" with "userv-ipif".
+netlink tun {
+       name "netlink-tun"; # Printed in log messages from this netlink
+#      interface "tun0"; # You may set your own interface name if you wish;
+               # if you don't one will be chosen for you.
+#      device "/dev/net/tun";
+
+       local-address "192.168.x.x"; # IP address of host's tunnel interface
+       secnet-address "192.168.x.x"; # IP address of this secnet
+
+       # Tunnels are only allowed to use these networks; attempts to
+       # claim IP addresses in any other ranges is a configuration error
+       remote-networks "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8";
+
+       # MTU of the tunnel interface. Should be kept under the path-MTU
+       # (by at least 60 bytes) between this secnet and its peers for
+       # optimum performance.
+       mtu 1400;
+
+       # This buffer is used to pass incoming packets onto the 'site'
+       # module. It should be at least as big as the MTU plus 60 bytes.
+       # Buffers can sometimes be shared between netlink devices - see
+       # full documentation for more details. (XXX TODO)
+       buffer sysbuffer(2048);
+};
+
+# This alternative configuration allows you to create one kernel network
+# interface per tunnel. IT WILL ONLY WORK WITH "tun" - IT WILL NOT
+# WORK WITH "userv-ipif".  This is because "tun" can share a single
+# buffer between multiple network interfaces, but userv-ipif can't.
+# To use userv-ipif in this style, process the sites.conf file so that
+# each "netlink" section contains a "buffer sysbuffer(2048);" line.
+#netlink tun;
+#local-address "192.168.x.x"; # Address of local interfaces - all the same
+#mtu 1400;
+#buffer sysbuffer(2048);
+
+
+# This defines the port that this instance of secnet will listen on, and
+# originate packets on. It does not _have_ to correspond to the advertised
+# port for your site: you may be doing network address translation, for
+# example. You need to arrange that any UDP packets sent to the advertised
+# host and port for your site end up on this machine at the port you
+# specify here.
+comm udp {
+       port 410;
+       buffer sysbuffer(4096);
+};
+
+# The resolver is used to look up IP addresses from the DNS names provided
+# in the sites file. You may specify an alternative resolv.conf for
+# ADNS here if you wish.
+resolver adns {
+#      config=readfile("/etc/secnet/adns.conf");
+};
+
+# log is defined earlier - we share it with the system
+log-events "setup-init","setup-timeout","activate-key","timeout-key","errors",
+       "security";
+
+# A source of random bits for nonces and session keys. The 'no' specifies
+# that it's non-blocking. XXX 'yes' isn't implemented yet.
+random randomfile("/dev/urandom",no);
+
+# If you're using the make-secnet-sites script then your local-name
+# will be of the form "vpnname/location/site" eg. "sgo/greenend/sinister"
+local-name "your-site-name";
+local-key rsa-private("/etc/secnet/key");
+
+# On dodgy links you may want to specify a higher maximum sequence number skew
+transform eax-serpent, serpent256-cbc;
+
+include /etc/secnet/sites.conf
+
+# The /etc/secnet/sites file contains information on all reachable sites;
+# if the site you want to communicate with isn't listed, you should get
+# a newer version. MAKE SURE YOU GET AN AUTHENTIC COPY OF THE FILE - it
+# contains public keys for all sites.
+
+# If you want to communicate with all the VPN sites, you can use something
+# like the following:
+
+sites map(site,vpn/example/all-sites);
+
+# If you only want to communicate with a subset of the VPN sites, list
+# them explicitly:
+
+# sites map(site,
+#      vpn-data/example/location1/site1,
+#      vpn-data/example/location2/site1,
+#      vpn-data/example/location2/site2);
+
+# If you want to communicate with a subset of locations, try the following:
+
+# sites map(site,vpn/example/location1,vpn/example/location2);
+
+# This file is placed in the public domain (insofar as possible.)
+# Authors:  Stephen Early, Ian Jackson
diff --git a/hackypar.c b/hackypar.c
new file mode 100644 (file)
index 0000000..25e41ea
--- /dev/null
@@ -0,0 +1,145 @@
+/* Hacky parallelism */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#define _GNU_SOURCE
+
+#include "secnet.h"
+#include "util.h"
+#include "hackypar.h"
+
+#ifdef HACKY_PARALLEL
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <assert.h>
+#include <sys/wait.h>
+
+#define HASHSIZE 16
+#define CACHESIZE 16
+
+typedef enum { hp_idle, hp_compute, hp_deferring, hp_fail } HPState;
+
+static HPState state;
+static pid_t child;
+
+static void checkchild(void)
+{
+    int r, status;
+  
+    if (!child) return;
+
+    r= waitpid(child,&status,WNOHANG); if (!r) return;
+    if (r==-1) {
+       Message(M_ERR,"hacky_par: waitpid: %s\n",strerror(errno));
+       return;
+    }
+    child= 0;
+  
+    if (WIFSIGNALED(status)) {
+       Message(M_ERR,"hacky_par: signaled! %s\n",strsignal(WTERMSIG(status)));
+    } else if (!WIFEXITED(status)) {
+       Message(M_ERR,"hacky_par: unexpected status! %d\n", r);
+    }
+}
+
+static HPState start(void)
+{
+    assert(!child);
+
+    child= fork();
+    if (child == -1) {
+       Message(M_ERR,"hacky_par: fork failed: %s\n",strerror(errno));
+       return hp_fail;
+    }
+
+    if (!child) { /* we are the child */
+       afterfork();
+       return hp_compute;
+    }
+
+    Message(M_INFO,"hacky_par: started, punting\n");
+    return hp_deferring;
+}
+
+int hacky_par_start_failnow(void)
+{
+    state= hp_idle;
+    checkchild();
+    if (child) {
+       state= hp_deferring;
+       Message(M_INFO,"hacky_par: busy, punting\n");
+       return 1;
+    }
+    return 0;
+}
+
+int hacky_par_mid_failnow(void)
+{
+    state= start();
+    return state != hp_compute;
+}
+
+bool_t (*packy_par_gen)(struct site *st);
+
+void hacky_par_end(int *ok,
+                  int32_t retries, int32_t timeout,
+                  bool_t (*send_msg)(struct site *st), struct site *st)
+{
+    int i;
+  
+    switch (state) {
+    case hp_deferring:
+       assert(!*ok);
+       *ok= 1;
+       return;
+    case hp_fail:
+       assert(!*ok);
+       return;
+    case hp_idle:
+       return;
+    case hp_compute:
+       if (!ok) {
+           Message(M_ERR,"hacky_par: compute failed\n");
+           _exit(2);
+       }
+       Message(M_INFO,"hacky_par: got result, sending\n");
+       for (i=1; i<retries; i++) {
+           sleep((timeout + 999)/1000);
+           if (!send_msg(st)) {
+               Message(M_ERR,"hacky_par: retry failed\n");
+               _exit(1);
+           }
+       }
+       _exit(0);
+    }
+}
+
+#else /*!HACKY_PARALLEL*/
+
+int hacky_par_start_failnow(void) { return 0; }
+int hacky_par_mid_failnow(void) { return 0; }
+void hacky_par_end(int *ok,
+                  int32_t retries, int32_t timeout,
+                  bool_t (*send_msg)(struct site *st), struct site *st) { }
+
+#endif /*HACKY_PARALLEL...else*/
diff --git a/hackypar.h b/hackypar.h
new file mode 100644 (file)
index 0000000..2f378d3
--- /dev/null
@@ -0,0 +1,34 @@
+/* Hacky parallelism
+ * We fork, and return false !
+ */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef hackympzpar_h
+#define hackympzpar_h
+
+struct site;
+
+int hacky_par_start_failnow(void);
+int hacky_par_mid_failnow(void);
+void hacky_par_end(int *ok,
+                  int32_t retries, int32_t timeout,
+                  bool_t (*send_msg)(struct site *st), struct site *st);
+
+#endif /* hackympzpar_h */
diff --git a/hexdebug.h b/hexdebug.h
new file mode 100644 (file)
index 0000000..3f4787c
--- /dev/null
@@ -0,0 +1,39 @@
+/* This file is Free Software.  It was written for secnet.
+ *
+ * Authored 2013      Ian Jackson
+ *
+ * You may redistribute this file freely - the copyrightholders and
+ * authors declare that they wish these files to be in the public
+ * domain; or alternatively (at your option) that you may deal with
+ * them according to the `CC0 1.0 Universal' licence.
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef HEXDEBUG_H
+#define HEXDEBUG_H
+
+#include <stdio.h>
+#include <sys/types.h>
+
+static inline void hexdebug(FILE *file, const void *buffer, size_t len)
+{
+    const uint8_t *array=buffer;
+    size_t i;
+    for (i=0; i<len; i++)
+       fprintf(file,"%02x",array[i]);
+}
+
+#endif /*HEXDEBUG_H*/
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..e843669
--- /dev/null
@@ -0,0 +1,250 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/ipaddr.c b/ipaddr.c
new file mode 100644 (file)
index 0000000..d12af55
--- /dev/null
+++ b/ipaddr.c
@@ -0,0 +1,467 @@
+/* The 'ipset' data structure and related algorithms in this file were
+   inspired by the 'ipaddr.py' library from Cendio Systems AB. */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include <limits.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "ipaddr.h"
+#include "util.h"
+
+#define DEFAULT_ALLOC 2
+#define EXTEND_ALLOC_BY 4
+
+struct subnet_list *subnet_list_new(void)
+{
+    struct subnet_list *r;
+    NEW(r);
+    r->entries=0;
+    r->alloc=DEFAULT_ALLOC;
+    NEW_ARY(r->list,r->alloc);
+    return r;
+}
+
+void subnet_list_free(struct subnet_list *a)
+{
+    if (a->list) free(a->list);
+    free(a);
+}
+
+static void subnet_list_set_len(struct subnet_list *a, int32_t l)
+{
+    int32_t na;
+
+    if (l>a->alloc) {
+       assert(a->alloc < INT_MAX-EXTEND_ALLOC_BY);
+       na=a->alloc+EXTEND_ALLOC_BY;
+       REALLOC_ARY(a->list,na);
+       a->alloc=na;
+    }
+    a->entries=l;
+}
+
+void subnet_list_append(struct subnet_list *a, uint32_t prefix, int len)
+{
+    struct subnet *sn;
+    assert(a->entries < INT_MAX);
+    subnet_list_set_len(a,a->entries+1);
+    sn=&a->list[a->entries-1];
+    sn->prefix=prefix;
+    sn->len=len;
+    sn->mask=len?(0xffffffff << (32-len)):0;
+}
+
+struct ipset *ipset_new(void)
+{
+    struct ipset *r;
+    NEW(r);
+    r->l=0;
+    r->a=DEFAULT_ALLOC;
+    NEW_ARY(r->d,r->a);
+    return r;
+}
+
+void ipset_free(struct ipset *a)
+{
+    if (a->d) free(a->d);
+    free(a);
+}
+
+#ifdef DEBUG
+static void ipset_dump(struct ipset *a, string_t name)
+{
+    int32_t i;
+
+    printf("%s: ",name);
+    for (i=0; i<a->l; i++) {
+       printf("[%08x-%08x] ",a->d[i].a,a->d[i].b);
+    }
+    printf("\n");
+}
+#endif
+
+struct ipset *ipset_from_subnet(struct subnet s)
+{
+    struct ipset *r;
+
+    r=ipset_new();
+    r->l=1;
+    r->d[0].a=s.prefix;
+    r->d[0].b=s.prefix | (~s.mask);
+    return r;
+}
+
+struct ipset *ipset_from_subnet_list(struct subnet_list *l)
+{
+    struct ipset *r, *a, *b;
+    int32_t i;
+
+    r=ipset_new();
+    for (i=0; i<l->entries; i++) {
+       a=ipset_from_subnet(l->list[i]);
+       b=ipset_union(r,a);
+       ipset_free(a);
+       ipset_free(r);
+       r=b;
+    }
+    return r;
+}
+
+static void ipset_set_len(struct ipset *a, int32_t l)
+{
+    int32_t na;
+
+    if (l>a->a) {
+       assert(a->a < INT_MAX-EXTEND_ALLOC_BY);
+       na=a->a+EXTEND_ALLOC_BY;
+       REALLOC_ARY(a->d,na);
+       a->a=na;
+    }
+    a->l=l;
+}
+
+static void ipset_append_range(struct ipset *a, struct iprange r)
+{
+    ipset_set_len(a,a->l+1);
+    a->d[a->l-1]=r;
+}
+
+struct ipset *ipset_union(struct ipset *a, struct ipset *b)
+{
+    struct ipset *c;
+    struct iprange r;
+    int32_t ia,ib;
+
+    c=ipset_new();
+    ia=0; ib=0;
+    while (ia<a->l || ib<b->l) {
+       if (ia<a->l)
+           if (ib<b->l)
+               if (a->d[ia].a < b->d[ib].a)
+                   r=a->d[ia++];
+               else
+                   r=b->d[ib++];
+           else
+               r=a->d[ia++];
+       else
+           r=b->d[ib++];
+
+       if (c->l==0)
+           ipset_append_range(c,r);
+       else if (r.a <= c->d[c->l-1].b+1)
+           /* Extends (or is consumed by) the last range */
+           c->d[c->l-1].b=MAX(c->d[c->l-1].b, r.b);
+       else
+           ipset_append_range(c,r);
+    }
+    return c;
+}
+
+struct ipset *ipset_intersection(struct ipset *a, struct ipset *b)
+{
+    struct ipset *r;
+    struct iprange ra, rb;
+    int32_t ia,ib;
+
+    r=ipset_new();
+    ia=0; ib=0;
+
+    while (ia<a->l && ib<b->l) {
+       ra=a->d[ia];
+       rb=b->d[ib];
+       if (ra.b < rb.a)
+           /* The first entry of a doesn't overlap with any part of b */
+           ia++;
+       else if (ra.a > rb.b)
+           /* The first entry of b doesn't overlap with any part of a */
+           ib++;
+       else {
+           /* Trim away any leading edges */
+           if (ra.a < rb.a)
+               /* a starts before b */
+               ra.a=rb.a;
+           else if (ra.a > rb.a)
+               /* b starts before a */
+               rb.a=ra.a;
+
+           /* Now the ranges start at the same point */
+           if (ra.b == rb.b) {
+               /* The ranges are equal */
+               ipset_append_range(r,ra);
+               ia++;
+               ib++;
+           } else if (ra.b < rb.b) {
+               /* a is the smaller range */
+               ipset_append_range(r,ra);
+               ia++;
+           } else {
+               /* y is the smaller range */
+               ipset_append_range(r,rb);
+               ib++;
+           }
+       }
+    }
+    return r;
+}
+
+struct ipset *ipset_complement(struct ipset *a)
+{
+    struct ipset *r;
+    struct iprange n;
+    int64_t pre;
+    int32_t i;
+    uint32_t lo,hi;
+
+    r=ipset_new();
+    pre=-1;
+    for (i=0; i<a->l; i++) {
+       lo=a->d[i].a;
+       hi=a->d[i].b;
+       if (lo!=0) {
+           n.a=pre+1;
+           n.b=lo-1;
+           ipset_append_range(r,n);
+       }
+       pre=hi;
+    }
+    if (pre!=0xffffffff) {
+       n.a=pre+1;
+       n.b=0xffffffff;
+       ipset_append_range(r,n);
+    }
+    return r;
+}
+
+/* Return a-b */
+struct ipset *ipset_subtract(struct ipset *a, struct ipset *b)
+{
+    struct ipset *c, *r;
+    c=ipset_complement(b);
+    r=ipset_intersection(a,c);
+    ipset_free(c);
+    return r;
+}
+
+bool_t ipset_is_empty(struct ipset *a)
+{
+   return (a->l==0);
+}
+
+bool_t ipset_contains_addr(struct ipset *a, uint32_t addr)
+{
+    int32_t i;
+    struct iprange r;
+
+    for (i=0; i<a->l; i++) {
+       r=a->d[i];
+       if (addr>=r.a && addr<=r.b) return True;
+       if (addr<r.a) return False;
+    }
+    return False;
+}
+
+/* sub is a subset of super if it does not intersect with the complement 
+   of super */
+bool_t ipset_is_subset(struct ipset *super, struct ipset *sub)
+{
+    struct ipset *superc;
+    struct ipset *inter;
+    bool_t empty;
+
+    superc=ipset_complement(super);
+    inter=ipset_intersection(superc,sub);
+    empty=ipset_is_empty(inter);
+    ipset_free(inter);
+    ipset_free(superc);
+    return empty;
+}
+
+struct subnet_list *ipset_to_subnet_list(struct ipset *is)
+{
+    struct subnet_list *r;
+    int64_t a,b,lobit,himask,lomask;
+    int bits;
+    int32_t i;
+
+    r=subnet_list_new();
+    for (i=0; i<is->l; i++) {
+       a=is->d[i].a;
+       b=is->d[i].b;
+
+       lomask=1;
+       lobit=1;
+       himask=0xfffffffe;
+       bits=32;
+       while (a<=b) {
+           if ((a & lomask) != 0) {
+               subnet_list_append(r,a,bits);
+               a=a+lobit;
+           } else if ((b & lomask) != lomask) {
+               subnet_list_append(r,b&himask,bits);
+               b=b-lobit;
+           } else {
+               lomask = (lomask << 1) | 1;
+               lobit = (lobit << 1);
+               himask = himask ^ lobit;
+               bits = bits - 1;
+               ASSERT(bits>=0);
+           }
+       }
+    }
+    /* Sort the list? */
+    return r;
+}
+
+#define IPADDR_BUFLEN 20
+
+static char *ipaddr_getbuf(void)
+{
+    SBUF_DEFINE(16, IPADDR_BUFLEN);
+    return SBUF;
+}
+
+/* The string buffer must be at least 16 bytes long */
+string_t ipaddr_to_string(uint32_t addr)
+{
+    uint8_t a,b,c,d;
+    string_t s;
+
+    s=ipaddr_getbuf();
+    a=addr>>24;
+    b=addr>>16;
+    c=addr>>8;
+    d=addr;
+    snprintf(s, 16, "%d.%d.%d.%d", a, b, c, d);
+    return s;
+}
+
+string_t subnet_to_string(struct subnet sn)
+{
+    uint32_t addr=sn.prefix;
+    uint8_t a,b,c,d;
+    string_t s;
+
+    s=ipaddr_getbuf();
+    a=addr>>24;
+    b=addr>>16;
+    c=addr>>8;
+    d=addr;
+    snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, sn.len);
+    return s;
+}
+
+static struct subnet string_item_to_subnet(item_t *i, cstring_t desc,
+                                          bool_t *invert)
+{
+    struct subnet s;
+    uint32_t a, b, c, d, n;
+    int match;
+    cstring_t in;
+
+    *invert=False;
+
+    /* i is not guaranteed to be a string */
+    if (i->type!=t_string) {
+       cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
+    }
+    in=i->data.string;
+
+    if (strcmp(in,"default")==0) {
+       s.prefix=0;
+       s.mask=0;
+       s.len=0;
+       return s;
+    }
+
+    if (*in=='!') {
+       *invert=True;
+       in++;
+    }
+    /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
+       NOT optional. The subnet mask is optional; if missing it is assumed
+       to be /32. */
+    match=sscanf(in,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
+    if (match<4) {
+       cfgfatal(i->loc,desc,"\"%s\" is not a valid "
+                "subnet specification\n",in);
+    }
+    if (match<5) {
+       n=32;
+    }
+    if (a>255 || b>255 || c>255 || d>255 || n>32) {
+       cfgfatal(i->loc,desc,"\"%s\": range error\n",in);
+    }
+    s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
+    s.mask=n?(~0UL << (32-n)):0;
+    s.len=n;
+    if (s.prefix & ~s.mask) {
+       cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
+                "in mask\n",in);
+    }
+    return s;
+}
+
+uint32_t string_item_to_ipaddr(const item_t *i, cstring_t desc)
+{
+    uint32_t a, b, c, d;
+    int match;
+
+    /* i is not guaranteed to be a string */
+    if (i->type!=t_string) {
+       cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
+    }
+
+    match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
+    if (match<4) {
+       cfgfatal(i->loc,desc,"\"%s\" is not a valid "
+                "IP address\n",i->data.string);
+    }
+    if (a>255 || b>255 || c>255 || d>255) {
+       cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
+    }
+    return (a<<24)|(b<<16)|(c<<8)|(d);
+}
+
+struct ipset *string_list_to_ipset(list_t *l, struct cloc loc,
+                                  cstring_t module, cstring_t param)
+{
+    struct ipset *r, *n, *isn;
+    uint32_t e,i;
+    item_t *item;
+    bool_t inv;
+
+    r=ipset_new();
+    e=list_length(l);
+    for (i=0; i<e; i++) {
+       item=list_elem(l,i);
+       isn=ipset_from_subnet(string_item_to_subnet(item,param,&inv));
+       if (inv) {
+           n=ipset_subtract(r,isn);
+       } else {
+           n=ipset_union(r,isn);
+       }
+       ipset_free(r);
+       ipset_free(isn);
+       r=n;
+    }
+    return r;
+}
diff --git a/ipaddr.h b/ipaddr.h
new file mode 100644 (file)
index 0000000..d8bc9fa
--- /dev/null
+++ b/ipaddr.h
@@ -0,0 +1,76 @@
+/* Useful functions for dealing with collections of IP addresses */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef ipaddr_h
+#define ipaddr_h
+
+struct subnet {
+    uint32_t prefix;
+    uint32_t mask;
+    int len;
+};
+
+struct subnet_list {
+    int32_t entries;
+    int32_t alloc;
+    struct subnet *list;
+};
+
+struct iprange {
+    uint32_t a,b;
+};
+
+struct ipset {
+    int32_t l; /* Number of entries in list */
+    int32_t a; /* Allocated space in list */
+    struct iprange *d;
+};
+
+extern struct subnet_list *subnet_list_new(void);
+extern void subnet_list_free(struct subnet_list *a);
+extern void subnet_list_append(struct subnet_list *a, uint32_t prefix, int len);
+
+static inline bool_t subnet_match(struct subnet s, uint32_t address)
+{
+    return (s.prefix==(address&s.mask));
+}
+
+extern struct ipset *ipset_new(void);
+extern void ipset_free(struct ipset *a);
+extern struct ipset *ipset_from_subnet(struct subnet s);
+extern struct ipset *ipset_from_subnet_list(struct subnet_list *l);
+extern struct ipset *ipset_union(struct ipset *a, struct ipset *b);
+extern struct ipset *ipset_intersection(struct ipset *a, struct ipset *b);
+extern struct ipset *ipset_complement(struct ipset *a);
+extern struct ipset *ipset_subtract(struct ipset *a, struct ipset *b);
+extern bool_t ipset_is_empty(struct ipset *a);
+extern bool_t ipset_contains_addr(struct ipset *a, uint32_t addr);
+extern bool_t ipset_is_subset(struct ipset *super, struct ipset *sub);
+extern struct subnet_list *ipset_to_subnet_list(struct ipset *is);
+
+extern string_t ipaddr_to_string(uint32_t addr);
+extern string_t subnet_to_string(struct subnet sn);
+
+extern struct ipset *string_list_to_ipset(list_t *l,struct cloc loc,
+                                         cstring_t module, cstring_t param);
+                                         
+extern uint32_t string_item_to_ipaddr(const item_t *i, cstring_t desc);
+
+#endif /* ipaddr_h */
diff --git a/ipaddrset-test.expected b/ipaddrset-test.expected
new file mode 100644 (file)
index 0000000..103050d
--- /dev/null
@@ -0,0 +1,23 @@
+s = -
+2001:23:24::/48,172.18.45.0/24
+t = 172.18.45.192/28,172.31.80.8/32
+False
+False
+172.18.44.0/23
+False
+False
+172.18.45.6/32
+True
+False
+172.18.45.0/24
+True
+False
+a = ::/0,0.0.0.0/0
+True
+True
+^
+172.18.45.192/28
+172.18.45.192/28
+u
+2001:23:24::/48,172.18.45.0/24,172.31.80.8/32
+2001:23:24::/48,172.18.45.0/24,172.31.80.8/32
diff --git a/ipaddrset-test.py b/ipaddrset-test.py
new file mode 100755 (executable)
index 0000000..16258f0
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/python3
+
+# This file is Free Software.  It was originally written for secnet.
+#
+# Copyright 2014 Ian Jackson
+#
+# You may redistribute secnet as a whole and/or modify it under the
+# terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 3, or (at your option) any
+# later version.
+#
+# You may redistribute this fileand/or modify it under the terms of
+# the GNU General Public License as published by the Free Software
+# Foundation; either version 2, or (at your option) any later version.
+#
+# This software 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.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+#
+# The corresponding test vector file ise ipaddrset-test.expected.  I
+# don't believe it is a creative work that attracts copyright.  -iwj.
+
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import ipaddress
+from ipaddress import ip_network, ip_address
+
+import ipaddrset
+from ipaddrset import IPAddressSet
+
+v4a=ip_address('172.18.45.6')
+
+s=IPAddressSet()
+print('s =', s)
+s.append([ip_network('172.18.45.0/24')])
+s.append([ip_network('2001:23:24::/48')])
+print(s)
+
+t=IPAddressSet(map(ip_network,['172.31.80.8/32','172.18.45.192/28']))
+print('t =', t)
+print(t <= s)
+print(t == s)
+
+for n1s in ['172.18.44.0/23','172.18.45.6/32','172.18.45.0/24']:
+    n1=ip_network(n1s)
+    print(n1)
+    print(s.contains(n1))
+    print(t.contains(n1))
+
+n=s.networks()[0]
+
+a=ipaddrset.complete_set()
+print('a =', a)
+print(a >= s)
+print(a >= t)
+
+print('^')
+print(s.intersection(t))
+print(t.intersection(s))
+
+print('u')
+print(s.union(t))
+print(t.union(s))
diff --git a/ipaddrset.py b/ipaddrset.py
new file mode 100644 (file)
index 0000000..38d4571
--- /dev/null
@@ -0,0 +1,160 @@
+"""IP address set manipulation, built on top of ipaddress.py"""
+
+# This file is Free Software.  It was originally written for secnet.
+#
+# Copyright 2014 Ian Jackson
+#
+# You may redistribute secnet as a whole and/or modify it under the
+# terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 3, or (at your option) any
+# later version.
+#
+# You may redistribute this file and/or modify it under the terms of
+# the GNU General Public License as published by the Free Software
+# Foundation; either version 2, or (at your option) any later version.
+# Note however that this version of ipaddrset.py uses the Python
+# ipaddr library from Google, which is licenced only under the Apache
+# Licence, version 2.0, which is only compatible with the GNU GPL v3
+# (or perhaps later versions), and not with the GNU GPL v2.
+#
+# This software 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.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this software; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+
+import ipaddress
+
+_vsns = [6,4]
+
+class IPAddressSet:
+       "A set of IP addresses"
+
+       # constructors
+       def __init__(self,l=[]):
+               "New set contains each IP*Network in the sequence l"
+               self._v = {}
+               for v in _vsns:
+                       self._v[v] = [ ]
+               self.append(l)
+
+       # housekeeping and representation
+       def _compact(self):
+               for v in _vsns:
+                       self._v[v] = list(
+                               ipaddress.collapse_addresses(self._v[v]))
+       def __repr__(self):
+               return "IPAddressSet(%s)" % self.networks()
+       def str(self,comma=",",none="-"):
+               "Human-readable string with controllable delimiters"
+               if self:
+                       return comma.join(map(str, self.networks()))
+               else:
+                       return none
+       def __str__(self):
+               return self.str()
+
+       # mutators
+       def append(self,l):
+               "Appends each IP*Network in the sequence l to self"
+               self._append(l)
+               self._compact()
+
+       def _append(self,l):
+               "Appends each IP*Network in the sequence l to self"
+               for a in l:
+                       self._v[a.version].append(a)
+
+       # enquirers including standard comparisons
+       def __bool__(self):
+               for v in _vsns:
+                       if self._v[v]:
+                               return True
+               return False
+       __nonzero__=__bool__ # for python2
+
+       def __eq__(self,other):
+               for v in _vsns:
+                       if self._v[v] != other._v[v]:
+                               return False
+               return True
+       def __ne__(self,other): return not self.__eq__(other)
+       def __ge__(self,other):
+               """True iff self completely contains IPAddressSet other"""
+               for o in other:
+                       if not self._contains_net(o):
+                               return False
+               return True
+       def __le__(self,other): return other.__ge__(self)
+       def __gt__(self,other): return self!=other and other.__ge__(self)
+       def __lt__(self,other): return other.__gt__(self)
+
+       def __cmp__(self,other):
+               if self==other: return 0
+               if self>=other: return +1
+               if self<=other: return -1
+               return NotImplemented
+
+       def __iter__(self):
+               "Iterates over minimal list of distinct IPNetworks in this set"
+               for v in _vsns:
+                       for i in self._v[v]:
+                               yield i
+
+       def networks(self):
+               "Returns miminal list of distinct IPNetworks in this set"
+               return [i for i in self]
+
+       # set operations
+       def intersection(self,other):
+               "Returns the intersection; does not modify self"
+               r = IPAddressSet()
+               for v in _vsns:
+                       for i in self._v[v]:
+                               for j in other._v[v]:
+                                       if i.overlaps(j):
+                                               if i.prefixlen > j.prefixlen:
+                                                       r._append([i])
+                                               else:
+                                                       r._append([j])
+               return r
+       def union(self,other):
+               "Returns the union; does not modify self"
+               r = IPAddressSet()
+               r._append(self.networks())
+               r._append(other.networks())
+               r._compact()
+               return r
+
+       def _contains_net(self,n):
+               """True iff self completely contains IPNetwork n"""
+               for i in self:
+                       if i.overlaps(n) and n.prefixlen >= i.prefixlen:
+                               return True
+               return False
+
+       def contains(self,thing):
+               """Returns True iff self completely contains thing.
+                  thing may be an IPNetwork or an IPAddressSet"""
+               try:
+                       v = [thing.version]
+               except KeyError:
+                       v = None
+               if v:
+                       return self._contains_net(ipaddress.ip_network(thing))
+               else:
+                       return self.__ge__(thing)
+
+def complete_set():
+       "Returns a set containing all addresses"
+       s=IPAddressSet()
+       for v in _vsns:
+               if v==6: a=ipaddress.IPv6Address(0)
+               elif v==4: a=ipaddress.IPv4Address(0)
+               else: raise "internal error"
+               n=ipaddress.ip_network("%s/0" % a)
+               s.append([n])
+       return s
diff --git a/log.c b/log.c
new file mode 100644 (file)
index 0000000..d5669ca
--- /dev/null
+++ b/log.c
@@ -0,0 +1,654 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+#include "secnet.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <syslog.h>
+#include <assert.h>
+#include <unistd.h>
+#include "process.h"
+#include "util.h"
+
+bool_t secnet_is_daemon=False;
+uint32_t message_level=M_WARNING|M_ERR|M_SECURITY|M_FATAL;
+struct log_if *system_log=NULL;
+
+FORMAT(printf,2,0)
+static void vMessageFallback(uint32_t class, const char *message, va_list args)
+{
+    FILE *dest=stdout;
+    /* Messages go to stdout/stderr */
+    if (class & message_level) {
+       if (class&M_FATAL || class&M_ERR || class&M_WARNING) {
+           dest=stderr;
+       }
+       vfprintf(dest,message,args);
+    }
+}
+
+FORMAT(printf,2,0)
+static void vMessage(uint32_t class, const char *message, va_list args)
+{
+
+    if (system_log) {
+       /* Messages go to the system log interface */
+       vslilog_part(system_log, class, message, args);
+    } else {
+       vMessageFallback(class,message,args);
+    }
+}  
+
+void Message(uint32_t class, const char *message, ...)
+{
+    va_list ap;
+
+    va_start(ap,message);
+    vMessage(class,message,ap);
+    va_end(ap);
+}
+
+FORMAT(printf,2,3)
+static void MessageFallback(uint32_t class, const char *message, ...)
+{
+    va_list ap;
+
+    va_start(ap,message);
+    vMessageFallback(class,message,ap);
+    va_end(ap);
+}
+
+static NORETURN(vfatal(int status, bool_t perror, const char *message,
+                      va_list args));
+
+FORMAT(printf,3,0)
+static void vfatal(int status, bool_t perror, const char *message,
+                  va_list args)
+{
+    int err;
+
+    err=errno;
+
+    enter_phase(PHASE_SHUTDOWN);
+    Message(M_FATAL, "secnet fatal error: ");
+    vMessage(M_FATAL, message, args);
+    if (perror)
+       Message(M_FATAL, ": %s\n",strerror(err));
+    else
+       Message(M_FATAL, "\n");
+    exit(status);
+}
+
+void fatal(const char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(current_phase,False,message,args);
+    va_end(args);
+}
+
+void fatal_status(int status, const char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(status,False,message,args);
+    va_end(args);
+}
+
+void fatal_perror(const char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(current_phase,True,message,args);
+    va_end(args);
+}
+
+void fatal_perror_status(int status, const char *message, ...)
+{
+    va_list args;
+    va_start(args,message);
+    vfatal(status,True,message,args);
+    va_end(args);
+}
+
+void vcfgfatal_maybefile(FILE *maybe_f /* or 0 */, struct cloc loc,
+                        cstring_t facility, const char *message, va_list args)
+{
+    enter_phase(PHASE_SHUTDOWN);
+
+    if (maybe_f && ferror(maybe_f)) {
+       assert(loc.file);
+       Message(M_FATAL, "error reading config file (%s, %s): %s",
+               facility, loc.file, strerror(errno));
+    } else if (maybe_f && feof(maybe_f)) {
+       assert(loc.file);
+       Message(M_FATAL, "unexpected end of config file (%s, %s)",
+               facility, loc.file);
+    } else if (loc.file && loc.line) {
+       Message(M_FATAL, "config error (%s, %s:%d): ",facility,loc.file,
+               loc.line);
+    } else if (!loc.file && loc.line) {
+       Message(M_FATAL, "config error (%s, line %d): ",facility,loc.line);
+    } else {
+       Message(M_FATAL, "config error (%s): ",facility);
+    }
+    
+    vMessage(M_FATAL,message,args);
+    exit(current_phase);
+}
+
+void cfgfatal_maybefile(FILE *maybe_f, struct cloc loc, cstring_t facility,
+                       const char *message, ...)
+{
+    va_list args;
+
+    va_start(args,message);
+    vcfgfatal_maybefile(maybe_f,loc,facility,message,args);
+    va_end(args);
+}    
+
+void cfgfatal(struct cloc loc, cstring_t facility, const char *message, ...)
+{
+    va_list args;
+
+    va_start(args,message);
+    vcfgfatal_maybefile(0,loc,facility,message,args);
+    va_end(args);
+}
+
+void cfgfile_postreadcheck(struct cloc loc, FILE *f)
+{
+    assert(loc.file);
+    if (ferror(f)) {
+       Message(M_FATAL, "error reading config file (%s): %s\n",
+               loc.file, strerror(errno));
+       exit(current_phase);
+    } else if (feof(f)) {
+       Message(M_FATAL, "unexpected end of config file (%s)\n", loc.file);
+       exit(current_phase);
+    }
+}
+
+/* Take a list of log closures and merge them */
+struct loglist {
+    struct log_if *l;
+    struct loglist *next;
+};
+
+FORMAT(printf, 3, 0)
+static void log_vmulti(void *sst, int class, const char *message, va_list args)
+{
+    struct loglist *st=sst, *i;
+
+    if (secnet_is_daemon) {
+       for (i=st; i; i=i->next) {
+           vslilog(i->l,class,message,args);
+       }
+    } else {
+       vMessage(class,message,args);
+       Message(class,"\n");
+    }
+}
+
+FORMAT(printf, 6, 0)
+void lg_vperror(struct log_if *lg, const char *desc, struct cloc *loc,
+               int class, int errnoval, const char *fmt, va_list al)
+{
+    int status=current_phase;
+    int esave=errno;
+
+    if (!lg)
+       lg=system_log;
+
+    if (class & M_FATAL)
+       enter_phase(PHASE_SHUTDOWN);
+
+    slilog_part(lg,class,"%s",desc);
+    if (loc)
+       slilog_part(lg,class," (%s:%d)",loc->file,loc->line);
+    slilog_part(lg,class,": ");
+    vslilog_part(lg,class,fmt,al);
+    if (errnoval)
+       slilog_part(lg,class,": %s",strerror(errnoval));
+    slilog_part(lg,class,"\n");
+
+    if (class & M_FATAL)
+       exit(status);
+
+    errno=esave;
+}
+
+void lg_perror(struct log_if *lg, const char *desc, struct cloc *loc,
+              int class, int errnoval, const char *fmt, ...)
+{
+    va_list al;
+    va_start(al,fmt);
+    lg_vperror(lg,desc,loc,class,errnoval,fmt,al);
+    va_end(al);
+}
+
+void lg_exitstatus(struct log_if *lg, const char *desc, struct cloc *loc,
+                  int class, int status, const char *progname)
+{
+    if (!status)
+       lg_perror(lg,desc,loc,class,0,"%s exited",progname);
+    else if (WIFEXITED(status))
+       lg_perror(lg,desc,loc,class,0,"%s exited with error exit status %d",
+                 progname,WEXITSTATUS(status));
+    else if (WIFSIGNALED(status))
+       lg_perror(lg,desc,loc,class,0,"%s died due to fatal signal %s (%d)%s",
+                 progname,strsignal(WTERMSIG(status)),WTERMSIG(status),
+                 WCOREDUMP(status)?" (core dumped)":"");
+    else
+       lg_perror(lg,desc,loc,class,0,"%s died with unknown wait status %d",
+                 progname,status);
+}
+
+struct log_if *init_log(list_t *ll)
+{
+    int i=0;
+    item_t *item;
+    closure_t *cl;
+    struct loglist *l=NULL, *n;
+    struct log_if *r;
+
+    if (list_length(ll)==1) {
+       item=list_elem(ll,0);
+       cl=item->data.closure;
+       if (cl->type!=CL_LOG) {
+           cfgfatal(item->loc,"init_log","closure is not a logger");
+       }
+       return cl->interface;
+    }
+    while ((item=list_elem(ll,i++))) {
+       if (item->type!=t_closure) {
+           cfgfatal(item->loc,"init_log","item is not a closure");
+       }
+       cl=item->data.closure;
+       if (cl->type!=CL_LOG) {
+           cfgfatal(item->loc,"init_log","closure is not a logger");
+       }
+       NEW(n);
+       n->l=cl->interface;
+       n->next=l;
+       l=n;
+    }
+    if (!l) {
+       fatal("init_log: no log");
+    }
+    NEW(r);
+    r->st=l;
+    r->vlogfn=log_vmulti;
+    r->buff[0]=0;
+    return r;
+}
+
+struct logfile {
+    closure_t cl;
+    struct log_if ops;
+    struct cloc loc;
+    string_t logfile;
+    uint32_t level;
+    FILE *f;
+    bool_t forked;
+};
+
+static cstring_t months[]={
+    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
+
+FORMAT(printf, 3, 0)
+static void logfile_vlog(void *sst, int class, const char *message,
+                        va_list args)
+{
+    struct logfile *st=sst;
+    time_t t;
+    struct tm *tm;
+    char pidbuf[20];
+
+    if (st->forked) {
+       pid_t us=getpid();
+       snprintf(pidbuf,sizeof(pidbuf),"[%ld] ",(long)us);
+    } else {
+       pidbuf[0]=0;
+    }
+
+    if (secnet_is_daemon && st->f) {
+       if (class&st->level) {
+           t=time(NULL);
+           tm=localtime(&t);
+           fprintf(st->f,"%s %2d %02d:%02d:%02d %s",
+                   months[tm->tm_mon],tm->tm_mday,tm->tm_hour,tm->tm_min,
+                   tm->tm_sec,
+                   pidbuf);
+           vfprintf(st->f,message,args);
+           fprintf(st->f,"\n");
+           fflush(st->f);
+       }
+    } else {
+       if (pidbuf[0]) MessageFallback(class,"%s",pidbuf);
+       vMessageFallback(class,message,args);
+       MessageFallback(class,"\n");
+    }
+}
+
+FORMAT(printf,3,4)
+static void logfile_log(void *state, int class, const char *message, ...)
+{
+    va_list ap;
+
+    va_start(ap,message);
+    logfile_vlog(state,class,message,ap);
+    va_end(ap);
+}
+
+static void logfile_hup_notify(void *sst, int signum)
+{
+    struct logfile *st=sst;
+    FILE *f;
+    f=fopen(st->logfile,"a");
+    if (!f) {
+       logfile_log(st,M_FATAL,"received SIGHUP, but could not reopen "
+                   "logfile: %s",strerror(errno));
+    } else {
+       fclose(st->f);
+       st->f=f;
+       logfile_log(st,M_INFO,"received SIGHUP");
+    }
+}
+
+static void logfile_phase_hook(void *sst, uint32_t new_phase)
+{
+    struct logfile *st=sst;
+    FILE *f;
+
+    if (background) {
+       f=fopen(st->logfile,"a");
+       if (!f) fatal_perror("logfile (%s:%d): cannot open \"%s\"",
+                            st->loc.file,st->loc.line,st->logfile);
+       st->f=f;
+       request_signal_notification(SIGHUP, logfile_hup_notify,st);
+    }
+}
+
+static void logfile_childpersist_hook(void *sst, uint32_t new_phase)
+{
+    struct logfile *st=sst;
+    st->forked=1;
+}
+
+static struct flagstr message_class_table[]={
+    { "debug-config", M_DEBUG_CONFIG },
+    { "debug-phase", M_DEBUG_PHASE },
+    { "debug", M_DEBUG },
+    { "all-debug", M_DEBUG|M_DEBUG_PHASE|M_DEBUG_CONFIG },
+    { "info", M_INFO },
+    { "notice", M_NOTICE },
+    { "warning", M_WARNING },
+    { "error", M_ERR },
+    { "security", M_SECURITY },
+    { "fatal", M_FATAL },
+    { "default", M_WARNING|M_ERR|M_SECURITY|M_FATAL },
+    { "verbose", M_INFO|M_NOTICE|M_WARNING|M_ERR|M_SECURITY|M_FATAL },
+    { "quiet", M_FATAL },
+    { NULL, 0 }
+};
+
+static list_t *logfile_apply(closure_t *self, struct cloc loc, dict_t *context,
+                            list_t *args)
+{
+    struct logfile *st;
+    item_t *item;
+    dict_t *dict;
+
+    /* We should defer opening the logfile until the getresources
+       phase.  We should defer writing into the logfile until after we
+       become a daemon. */
+    
+    NEW(st);
+    st->cl.description="logfile";
+    st->cl.type=CL_LOG;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.vlogfn=logfile_vlog;
+    st->ops.buff[0]=0;
+    st->loc=loc;
+    st->f=NULL;
+    st->forked=0;
+
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict) {
+       cfgfatal(loc,"logfile","argument must be a dictionary\n");
+    }
+    dict=item->data.dict;
+
+    st->logfile=dict_read_string(dict,"filename",True,"logfile",loc);
+    st->level=string_list_to_word(dict_lookup(dict,"class"),
+                                      message_class_table,"logfile");
+
+    add_hook(PHASE_GETRESOURCES,logfile_phase_hook,st);
+    add_hook(PHASE_CHILDPERSIST,logfile_childpersist_hook,st);
+
+    return new_closure(&st->cl);
+}
+
+struct syslog {
+    closure_t cl;
+    struct log_if ops;
+    string_t ident;
+    int facility;
+    bool_t open;
+};
+
+static int msgclass_to_syslogpriority(uint32_t m)
+{
+    switch (m) {
+    case M_DEBUG_CONFIG: return LOG_DEBUG;
+    case M_DEBUG_PHASE: return LOG_DEBUG;
+    case M_DEBUG: return LOG_DEBUG;
+    case M_INFO: return LOG_INFO;
+    case M_NOTICE: return LOG_NOTICE;
+    case M_WARNING: return LOG_WARNING;
+    case M_ERR: return LOG_ERR;
+    case M_SECURITY: return LOG_CRIT;
+    case M_FATAL: return LOG_EMERG;
+    default: return LOG_NOTICE;
+    }
+}
+    
+static void syslog_vlog(void *sst, int class, const char *message,
+                        va_list args)
+    FORMAT(printf,3,0);
+static void syslog_vlog(void *sst, int class, const char *message,
+                        va_list args)
+{
+    struct syslog *st=sst;
+
+    if (st->open)
+       vsyslog(msgclass_to_syslogpriority(class),message,args);
+    else {
+       vMessageFallback(class,message,args);
+       MessageFallback(class,"\n");
+    }
+}
+
+static struct flagstr syslog_facility_table[]={
+#ifdef LOG_AUTH
+    { "auth", LOG_AUTH },
+#endif
+#ifdef LOG_AUTHPRIV
+    { "authpriv", LOG_AUTHPRIV },
+#endif
+    { "cron", LOG_CRON },
+    { "daemon", LOG_DAEMON },
+    { "kern", LOG_KERN },
+    { "local0", LOG_LOCAL0 },
+    { "local1", LOG_LOCAL1 },
+    { "local2", LOG_LOCAL2 },
+    { "local3", LOG_LOCAL3 },
+    { "local4", LOG_LOCAL4 },
+    { "local5", LOG_LOCAL5 },
+    { "local6", LOG_LOCAL6 },
+    { "local7", LOG_LOCAL7 },
+    { "lpr", LOG_LPR },
+    { "mail", LOG_MAIL },
+    { "news", LOG_NEWS },
+    { "syslog", LOG_SYSLOG },
+    { "user", LOG_USER },
+    { "uucp", LOG_UUCP },
+    { NULL, 0 }
+};
+
+static void syslog_phase_hook(void *sst, uint32_t newphase)
+{
+    struct syslog *st=sst;
+
+    if (background) {
+       openlog(st->ident,
+               newphase==PHASE_CHILDPERSIST ? LOG_PID : 0,
+               st->facility);
+       st->open=True;
+    }
+}
+
+static list_t *syslog_apply(closure_t *self, struct cloc loc, dict_t *context,
+                           list_t *args)
+{
+    struct syslog *st;
+    dict_t *d;
+    item_t *item;
+    string_t facstr;
+
+    NEW(st);
+    st->cl.description="syslog";
+    st->cl.type=CL_LOG;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.vlogfn=syslog_vlog;
+    st->ops.buff[0]=0;
+
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"syslog","parameter must be a dictionary\n");
+    d=item->data.dict;
+
+    st->ident=dict_read_string(d, "ident", False, "syslog", loc);
+    facstr=dict_read_string(d, "facility", True, "syslog", loc);
+    st->facility=string_to_word(facstr,loc,
+                               syslog_facility_table,"syslog");
+    st->open=False;
+    add_hook(PHASE_GETRESOURCES,syslog_phase_hook,st);
+    add_hook(PHASE_CHILDPERSIST,syslog_phase_hook,st);
+
+    return new_closure(&st->cl);
+}    
+
+/* Read from a fd and output to a log.  This is a quick hack to
+   support logging stderr, and needs code adding to tidy up before it
+   can be used for anything else. */
+#define FDLOG_BUFSIZE 1024
+struct fdlog {
+    struct log_if *log;
+    int fd;
+    cstring_t prefix;
+    string_t buffer;
+    int i;
+    bool_t finished;
+};
+
+static int log_from_fd_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                                 int *timeout_io)
+{
+    struct fdlog *st=sst;
+    if (!st->finished) {
+       BEFOREPOLL_WANT_FDS(1);
+       fds[0].fd=st->fd;
+       fds[0].events=POLLIN;
+    } else {
+       BEFOREPOLL_WANT_FDS(0);
+    }
+    return 0;
+}
+
+static void log_from_fd_afterpoll(void *sst, struct pollfd *fds, int nfds)
+{
+    struct fdlog *st=sst;
+    int r,remain,i;
+
+    if (nfds==0) return;
+    if (fds[0].revents&POLLERR) {
+       st->finished=True;
+    }
+    if (fds[0].revents&POLLIN) {
+       remain=FDLOG_BUFSIZE-st->i-1;
+       if (remain<=0) {
+           st->buffer[FDLOG_BUFSIZE-1]=0;
+           slilog(st->log,M_WARNING,"%s: overlong line: %s",
+                        st->prefix,st->buffer);
+           st->i=0;
+           remain=FDLOG_BUFSIZE-1;
+       }
+       r=read(st->fd,st->buffer+st->i,remain);
+       if (r>0) {
+           st->i+=r;
+           for (i=0; i<st->i; i++) {
+               if (st->buffer[i]=='\n') {
+                   st->buffer[i]=0;
+                   slilog(st->log,M_INFO,"%s: %s",
+                                st->prefix,st->buffer);
+                   i++;
+                   memmove(st->buffer,st->buffer+i,st->i-i);
+                   st->i-=i;
+                   i=-1;
+               }
+           }
+       } else if (errno==EINTR || iswouldblock(errno)) {
+       } else {
+           Message(M_WARNING,"log_from_fd: %s\n",strerror(errno));
+           st->finished=True;
+       }
+    }
+}
+               
+void log_from_fd(int fd, cstring_t prefix, struct log_if *log)
+{
+    struct fdlog *st;
+
+    NEW(st);
+    st->log=log;
+    st->fd=fd;
+    st->prefix=prefix;
+    st->buffer=safe_malloc(FDLOG_BUFSIZE,"log_from_fd");
+    st->i=0;
+    st->finished=False;
+
+    setnonblock(st->fd);
+
+    register_for_poll(st,log_from_fd_beforepoll,log_from_fd_afterpoll,
+                     prefix);
+}
+
+void log_module(dict_t *dict)
+{
+    add_closure(dict,"logfile",logfile_apply);
+    add_closure(dict,"syslog",syslog_apply);
+}
diff --git a/magic.h b/magic.h
new file mode 100644 (file)
index 0000000..15d8498
--- /dev/null
+++ b/magic.h
@@ -0,0 +1,115 @@
+/* Magic numbers used within secnet */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef magic_h
+#define magic_h
+
+/* Encode a pair of 16 bit major and minor codes as a single 32-bit label.
+ * The encoding is strange for historical reasons.  Suppose that the nibbles
+ * of the major number are (from high to low) a, b, c, d, and the minor
+ * number has nibbles w, x, y, z.  (Here, a, b, c, d are variables, not hex
+ * digits.)  We scramble them to form a message label as follows.
+ *
+ *     0 d 0 d 0 d 0 d
+ *     0 0 0 a b c 0 0
+ *     z 0 0 0 0 0 z 0
+ *     w x y 0 0 0 0 0
+ *     ---------------
+ *     f g h i j k l m
+ *
+ * and calculate the nibbles f, g, ..., m of the message label (higher
+ * significance on the left) by XORing the columns.  It can be shown that
+ * this is invertible using linear algebra in GF(16), but but it's easier to
+ * notice that d = m, z = l, c = k XOR d, b = j, a = i XOR d, y = h,
+ * x = g XOR d, and w = f XOR z.
+ *
+ * Encoding in the forward direction, from a major/minor pair to a label, is
+ * (almost?) always done on constants, so its performance is fairly
+ * unimportant.  There is a compatibility constraint on the patterns produced
+ * with a = b = c = w = x = y = 0.  Subject to that, I wanted to find an
+ * invertible GF(16)-linear transformation which would let me recover the
+ * major and minor numbers with relatively little calculation.
+ */
+
+#define MSGCODE(major, minor)                                          \
+       ((((uint32_t)(major)&0x0000000fu) <<  0) ^                      \
+        (((uint32_t)(major)&0x0000000fu) <<  8) ^                      \
+        (((uint32_t)(major)&0x0000000fu) << 16) ^                      \
+        (((uint32_t)(major)&0x0000000fu) << 24) ^                      \
+        (((uint32_t)(major)&0x0000fff0u) <<  4) ^                      \
+        (((uint32_t)(minor)&0x0000000fu) <<  4) ^                      \
+        (((uint32_t)(minor)&0x0000000fu) << 28) ^                      \
+        (((uint32_t)(minor)&0x0000fff0u) << 16))
+
+/* Extract major and minor codes from a 32-bit message label. */
+#define MSGMAJOR(label)                                                        \
+       ((((uint32_t)(label)&0x0000000fu) <<  0) ^                      \
+        (((uint32_t)(label)&0x0000000fu) <<  4) ^                      \
+        (((uint32_t)(label)&0x0000000fu) << 12) ^                      \
+        (((uint32_t)(label)&0x000fff00u) >>  4))
+#define MSGMINOR(label)                                                        \
+       ((((uint32_t)(label)&0x000000ffu) <<  8) ^                      \
+        (((uint32_t)(label)&0x000000f0u) >>  4) ^                      \
+        (((uint32_t)(label)&0xfff00000u) >> 16))
+
+#define LABEL_NAK      MSGCODE(     0, 0)
+#define LABEL_MSG0     MSGCODE(0x2020, 0) /* ! */
+#define LABEL_MSG1     MSGCODE(     1, 0)
+#define LABEL_MSG2     MSGCODE(     2, 0)
+#define LABEL_MSG3     MSGCODE(     3, 0)
+#define LABEL_MSG3BIS  MSGCODE(     3, 1)
+#define LABEL_MSG4     MSGCODE(     4, 0)
+#define LABEL_MSG5     MSGCODE(     5, 0)
+#define LABEL_MSG6     MSGCODE(     6, 0)
+#define LABEL_MSG7     MSGCODE(     7, 0)
+#define LABEL_MSG8     MSGCODE(     8, 0)
+#define LABEL_MSG9     MSGCODE(     9, 0)
+#define LABEL_PROD     MSGCODE(    10, 0)
+
+/*
+ * The capability mask is a set of bits, one for each optional feature
+ * supported.  The capability numbers for transforms are set in the
+ * configuration (and should correspond between the two sites), although
+ * there are sensible defaults.
+ *
+ * Advertising a nonzero capability mask promises that the receiver
+ * understands LABEL_MSG3BIS messages, which contain an additional byte
+ * specifying the transform capability number actually chosen by the MSG3
+ * sender.
+ *
+ * Aside from that, an empty bitmask is treated the same as
+ *  1u<<CAPAB_BIT_ANCIENTTRANSFORM
+ */
+
+/* uses of the 32-bit capability bitmap */
+#define CAPAB_TRANSFORM_MASK  0x0000ffff
+#define CAPAB_PRIORITY_MOBILE 0x80000000 /* mobile site has MSG1 priority */
+/* remaining bits are unused */
+
+/* bit indices, 0 is ls bit */
+#define CAPAB_BIT_USER_MIN              0
+#define CAPAB_BIT_USER_MAX              7
+#define CAPAB_BIT_SERPENT256CBC         8
+#define CAPAB_BIT_EAXSERPENT            9
+#define CAPAB_BIT_MAX                  15
+
+#define CAPAB_BIT_ANCIENTTRANSFORM CAPAB_BIT_SERPENT256CBC
+
+#endif /* magic_h */
diff --git a/make-secnet-sites b/make-secnet-sites
new file mode 100755 (executable)
index 0000000..c26cab0
--- /dev/null
@@ -0,0 +1,780 @@
+#! /usr/bin/env python3
+#
+# This file is part of secnet.
+# See README for full list of copyright holders.
+#
+# secnet is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# secnet 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.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# version 3 along with secnet; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+
+"""VPN sites file manipulation.
+
+This program enables VPN site descriptions to be submitted for
+inclusion in a central database, and allows the resulting database to
+be turned into a secnet configuration file.
+
+A database file can be turned into a secnet configuration file simply:
+make-secnet-sites.py [infile [outfile]]
+
+It would be wise to run secnet with the "--just-check-config" option
+before installing the output on a live system.
+
+The program expects to be invoked via userv to manage the database; it
+relies on the USERV_USER and USERV_GROUP environment variables. The
+command line arguments for this invocation are:
+
+make-secnet-sites.py -u header-filename groupfiles-directory output-file \
+  group
+
+All but the last argument are expected to be set by userv; the 'group'
+argument is provided by the user. A suitable userv configuration file
+fragment is:
+
+reset
+no-disconnect-hup
+no-suppress-args
+cd ~/secnet/sites-test/
+execute ~/secnet/make-secnet-sites.py -u vpnheader groupfiles sites
+
+This program is part of secnet.
+
+"""
+
+from __future__ import print_function
+from __future__ import unicode_literals
+from builtins import int
+
+import string
+import time
+import sys
+import os
+import getopt
+import re
+import argparse
+import math
+
+import ipaddress
+
+# entry 0 is "near the executable", or maybe from PYTHONPATH=.,
+# which we don't want to preempt
+sys.path.insert(1,"/usr/local/share/secnet")
+sys.path.insert(1,"/usr/share/secnet")
+import ipaddrset
+
+from argparseactionnoyes import ActionNoYes
+
+VERSION="0.1.18"
+
+from sys import version_info
+if version_info.major == 2:  # for python2
+    import codecs
+    sys.stdin = codecs.getreader('utf-8')(sys.stdin)
+    sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
+    import io
+    open=lambda f,m='r': io.open(f,m,encoding='utf-8')
+
+max={'rsa_bits':8200,'name':33,'dh_bits':8200}
+
+class Tainted:
+       def __init__(self,s,tline=None,tfile=None):
+               self._s=s
+               self._ok=None
+               self._line=line if tline is None else tline
+               self._file=file if tfile is None else tfile
+       def __eq__(self,e):
+               return self._s==e
+       def __ne__(self,e):
+               # for Python2
+               return not self.__eq__(e)
+       def __str__(self):
+               raise RuntimeError('direct use of Tainted value')
+       def __repr__(self):
+               return 'Tainted(%s)' % repr(self._s)
+
+       def _bad(self,what,why):
+               assert(self._ok is not True)
+               self._ok=False
+               complain('bad parameter: %s: %s' % (what, why))
+               return self
+
+       def _max_ok(self,what,maxlen):
+               if len(self._s) > maxlen:
+                       self._bad(what,'too long (max %d)' % maxlen)
+               return self
+
+       def _re_ok(self,bad,what,maxlen=None):
+               if maxlen is None: maxlen=max[what]
+               self._max_ok(what,maxlen)
+               if self._ok is False: return self
+               if bad.search(self._s): return self._bad(what,'bad syntax')
+               return self
+
+       def _rtnval(self, is_ok, ifgood, ifbad=''):
+               if is_ok:
+                       assert(self._ok is not False)
+                       self._ok=True
+                       return ifgood
+               else:
+                       assert(self._ok is not True)
+                       self._ok=False
+                       return ifbad
+
+       def _rtn(self, is_ok, ifbad=''):
+               return self._rtnval(is_ok, self._s, ifbad)
+
+       def raw(self):
+               return self._s
+       def raw_mark_ok(self):
+               # caller promises to throw if syntax was dangeorus
+               return self._rtn(True)
+
+       def output(self):
+               if self._ok is False: return ''
+               if self._ok is True: return self._s
+               print('%s:%d: unchecked/unknown additional data "%s"' %
+                     (self._file,self._line,self._s),
+                     file=sys.stderr)
+               sys.exit(1)
+
+       bad_name=re.compile(r'^[^a-zA-Z]|[^-_0-9a-zA-Z]')
+       # secnet accepts _ at start of names, but we reserve that
+       bad_name_counter=0
+       def name(self):
+               ok=self._re_ok(Tainted.bad_name,'name')
+               return self._rtn(ok,
+                                '_line%d_%s' % (self._line, id(self)))
+
+       def keyword(self):
+               ok=self._s in keywords or self._s in levels
+               if not ok:
+                       complain('unknown keyword %s' % self._s)
+               return self._rtn(ok)
+
+       bad_hex=re.compile(r'[^0-9a-fA-F]')
+       def bignum_16(self,kind,what):
+               maxlen=(max[kind+'_bits']+3)/4
+               ok=self._re_ok(Tainted.bad_hex,what,maxlen)
+               return self._rtn(ok)
+
+       bad_num=re.compile(r'[^0-9]')
+       def bignum_10(self,kind,what):
+               maxlen=math.ceil(max[kind+'_bits'] / math.log10(2))
+               ok=self._re_ok(Tainted.bad_num,what,maxlen)
+               return self._rtn(ok)
+
+       def number(self,minn,maxx,what='number'):
+               # not for bignums
+               ok=self._re_ok(Tainted.bad_num,what,10)
+               if ok:
+                       v=int(self._s)
+                       if v<minn or v>maxx:
+                               ok=self._bad(what,'out of range %d..%d'
+                                            % (minn,maxx))
+               return self._rtnval(ok,v,minn)
+
+       bad_host=re.compile(r'[^-\][_.:0-9a-zA-Z]')
+       # We permit _ so we can refer to special non-host domains
+       # which have A and AAAA RRs.  This is a crude check and we may
+       # still produce config files with syntactically invalid
+       # domains or addresses, but that is OK.
+       def host(self):
+               ok=self._re_ok(Tainted.bad_host,'host/address',255)
+               return self._rtn(ok)
+
+       bad_email=re.compile(r'[^-._0-9a-z@!$%^&*=+~/]')
+       # ^ This does not accept all valid email addresses.  That's
+       # not really possible with this input syntax.  It accepts
+       # all ones that don't require quoting anywhere in email
+       # protocols (and also accepts some invalid ones).
+       def email(self):
+               ok=self._re_ok(Tainted.bad_email,'email address',1023)
+               return self._rtn(ok)
+
+       bad_groupname=re.compile(r'^[^_A-Za-z]|[^-+_0-9A-Za-z]')
+       def groupname(self):
+               ok=self._re_ok(Tainted.bad_groupname,'group name',64)
+               return self._rtn(ok)
+
+def parse_args():
+       global service
+       global inputfile
+       global header
+       global groupfiledir
+       global sitesfile
+       global group
+       global user
+       global of
+       global prefix
+       global key_prefix
+
+       ap = argparse.ArgumentParser(description='process secnet sites files')
+       ap.add_argument('--userv', '-u', action='store_true',
+                       help='userv service fragment update mode')
+       ap.add_argument('--conf-key-prefix', action=ActionNoYes,
+                       default=True,
+                help='prefix conf file key names derived from sites data')
+       ap.add_argument('--prefix', '-P', nargs=1,
+                       help='set prefix')
+       ap.add_argument('arg',nargs=argparse.REMAINDER)
+       av = ap.parse_args()
+       #print(repr(av), file=sys.stderr)
+       service = 1 if av.userv else 0
+       prefix = '' if av.prefix is None else av.prefix[0]
+       key_prefix = av.conf_key_prefix
+       if service:
+               if len(av.arg)!=4:
+                       print("Wrong number of arguments")
+                       sys.exit(1)
+               (header, groupfiledir, sitesfile, group) = av.arg
+               group = Tainted(group,0,'command line')
+               # untrusted argument from caller
+               if "USERV_USER" not in os.environ:
+                       print("Environment variable USERV_USER not found")
+                       sys.exit(1)
+               user=os.environ["USERV_USER"]
+               # Check that group is in USERV_GROUP
+               if "USERV_GROUP" not in os.environ:
+                       print("Environment variable USERV_GROUP not found")
+                       sys.exit(1)
+               ugs=os.environ["USERV_GROUP"]
+               ok=0
+               for i in ugs.split():
+                       if group==i: ok=1
+               if not ok:
+                       print("caller not in group %s"%group)
+                       sys.exit(1)
+       else:
+               if len(av.arg)>3:
+                       print("Too many arguments")
+                       sys.exit(1)
+               (inputfile, outputfile) = (av.arg + [None]*2)[0:2]
+               if outputfile is None: of=sys.stdout
+               else: of=open(outputfile,'w')
+
+parse_args()
+
+# Classes describing possible datatypes in the configuration file
+
+class basetype:
+       "Common protocol for configuration types."
+       def add(self,obj,w):
+               complain("%s %s already has property %s defined"%
+                       (obj.type,obj.name,w[0].raw()))
+
+class conflist:
+       "A list of some kind of configuration type."
+       def __init__(self,subtype,w):
+               self.subtype=subtype
+               self.list=[subtype(w)]
+       def add(self,obj,w):
+               self.list.append(self.subtype(w))
+       def __str__(self):
+               return ', '.join(map(str, self.list))
+def listof(subtype):
+       return lambda w: conflist(subtype, w)
+
+class single_ipaddr (basetype):
+       "An IP address"
+       def __init__(self,w):
+               self.addr=ipaddress.ip_address(w[1].raw_mark_ok())
+       def __str__(self):
+               return '"%s"'%self.addr
+
+class networks (basetype):
+       "A set of IP addresses specified as a list of networks"
+       def __init__(self,w):
+               self.set=ipaddrset.IPAddressSet()
+               for i in w[1:]:
+                       x=ipaddress.ip_network(i.raw_mark_ok(),strict=True)
+                       self.set.append([x])
+       def __str__(self):
+               return ",".join(map((lambda n: '"%s"'%n), self.set.networks()))
+
+class dhgroup (basetype):
+       "A Diffie-Hellman group"
+       def __init__(self,w):
+               self.mod=w[1].bignum_16('dh','dh mod')
+               self.gen=w[2].bignum_16('dh','dh gen')
+       def __str__(self):
+               return 'diffie-hellman("%s","%s")'%(self.mod,self.gen)
+
+class hash (basetype):
+       "A choice of hash function"
+       def __init__(self,w):
+               hname=w[1]
+               self.ht=hname.raw()
+               if (self.ht!='md5' and self.ht!='sha1'):
+                       complain("unknown hash type %s"%(self.ht))
+                       self.ht=None
+               else:
+                       hname.raw_mark_ok()
+       def __str__(self):
+               return '%s'%(self.ht)
+
+class email (basetype):
+       "An email address"
+       def __init__(self,w):
+               self.addr=w[1].email()
+       def __str__(self):
+               return '<%s>'%(self.addr)
+
+class boolean (basetype):
+       "A boolean"
+       def __init__(self,w):
+               v=w[1]
+               if re.match('[TtYy1]',v.raw()):
+                       self.b=True
+                       v.raw_mark_ok()
+               elif re.match('[FfNn0]',v.raw()):
+                       self.b=False
+                       v.raw_mark_ok()
+               else:
+                       complain("invalid boolean value");
+       def __str__(self):
+               return ['False','True'][self.b]
+
+class num (basetype):
+       "A decimal number"
+       def __init__(self,w):
+               self.n=w[1].number(0,0x7fffffff)
+       def __str__(self):
+               return '%d'%(self.n)
+
+class address (basetype):
+       "A DNS name and UDP port number"
+       def __init__(self,w):
+               self.adr=w[1].host()
+               self.port=w[2].number(1,65536,'port')
+       def __str__(self):
+               return '"%s"; port %d'%(self.adr,self.port)
+
+class rsakey (basetype):
+       "An RSA public key"
+       def __init__(self,w):
+               self.l=w[1].number(0,max['rsa_bits'],'rsa len')
+               self.e=w[2].bignum_10('rsa','rsa e')
+               self.n=w[3].bignum_10('rsa','rsa n')
+               if len(w) >= 5: w[4].email()
+       def __str__(self):
+               return 'rsa-public("%s","%s")'%(self.e,self.n)
+
+# Possible properties of configuration nodes
+keywords={
+ 'contact':(email,"Contact address"),
+ 'dh':(dhgroup,"Diffie-Hellman group"),
+ 'hash':(hash,"Hash function"),
+ 'key-lifetime':(num,"Maximum key lifetime (ms)"),
+ 'setup-timeout':(num,"Key setup timeout (ms)"),
+ 'setup-retries':(num,"Maximum key setup packet retries"),
+ 'wait-time':(num,"Time to wait after unsuccessful key setup (ms)"),
+ 'renegotiate-time':(num,"Time after key setup to begin renegotiation (ms)"),
+ 'restrict-nets':(networks,"Allowable networks"),
+ 'networks':(networks,"Claimed networks"),
+ 'pubkey':(rsakey,"RSA public site key"),
+ 'peer':(single_ipaddr,"Tunnel peer IP address"),
+ 'address':(address,"External contact address and port"),
+ 'mobile':(boolean,"Site is mobile"),
+}
+
+def sp(name,value):
+       "Simply output a property - the default case"
+       return "%s %s;\n"%(name,value)
+
+# All levels support these properties
+global_properties={
+       'contact':(lambda name,value:"# Contact email address: %s\n"%(value)),
+       'dh':sp,
+       'hash':sp,
+       'key-lifetime':sp,
+       'setup-timeout':sp,
+       'setup-retries':sp,
+       'wait-time':sp,
+       'renegotiate-time':sp,
+       'restrict-nets':(lambda name,value:"# restrict-nets %s\n"%value),
+}
+
+class level:
+       "A level in the configuration hierarchy"
+       depth=0
+       leaf=0
+       allow_properties={}
+       require_properties={}
+       def __init__(self,w):
+               self.type=w[0].keyword()
+               self.name=w[1].name()
+               self.properties={}
+               self.children={}
+       def indent(self,w,t):
+               w.write("                 "[:t])
+       def prop_out(self,n):
+               return self.allow_properties[n](n,str(self.properties[n]))
+       def output_props(self,w,ind):
+               for i in sorted(self.properties.keys()):
+                       if self.allow_properties[i]:
+                               self.indent(w,ind)
+                               w.write("%s"%self.prop_out(i))
+       def kname(self):
+               return ((self.type[0].upper() if key_prefix else '')
+                       + self.name)
+       def output_data(self,w,path):
+               ind = 2*len(path)
+               self.indent(w,ind)
+               w.write("%s {\n"%(self.kname()))
+               self.output_props(w,ind+2)
+               if self.depth==1: w.write("\n");
+               for k in sorted(self.children.keys()):
+                       c=self.children[k]
+                       c.output_data(w,path+(c,))
+               self.indent(w,ind)
+               w.write("};\n")
+
+class vpnlevel(level):
+       "VPN level in the configuration hierarchy"
+       depth=1
+       leaf=0
+       type="vpn"
+       allow_properties=global_properties.copy()
+       require_properties={
+        'contact':"VPN admin contact address"
+       }
+       def __init__(self,w):
+               level.__init__(self,w)
+       def output_vpnflat(self,w,path):
+               "Output flattened list of site names for this VPN"
+               ind=2*(len(path)+1)
+               self.indent(w,ind)
+               w.write("%s {\n"%(self.kname()))
+               for i in self.children.keys():
+                       self.children[i].output_vpnflat(w,path+(self,))
+               w.write("\n")
+               self.indent(w,ind+2)
+               w.write("all-sites %s;\n"%
+                       ','.join(map(lambda i: i.kname(),
+                                    self.children.values())))
+               self.indent(w,ind)
+               w.write("};\n")
+
+class locationlevel(level):
+       "Location level in the configuration hierarchy"
+       depth=2
+       leaf=0
+       type="location"
+       allow_properties=global_properties.copy()
+       require_properties={
+        'contact':"Location admin contact address",
+       }
+       def __init__(self,w):
+               level.__init__(self,w)
+               self.group=w[2].groupname()
+       def output_vpnflat(self,w,path):
+               ind=2*(len(path)+1)
+               self.indent(w,ind)
+               # The "path=path,self=self" abomination below exists because
+               # Python didn't support nested_scopes until version 2.1
+               #
+               #"/"+self.name+"/"+i
+               w.write("%s %s;\n"%(self.kname(),','.join(
+                       map(lambda x,path=path,self=self:
+                           '/'.join([prefix+"vpn-data"] + list(map(
+                                   lambda i: i.kname(),
+                                   path+(self,x)))),
+                           self.children.values()))))
+
+class sitelevel(level):
+       "Site level (i.e. a leafnode) in the configuration hierarchy"
+       depth=3
+       leaf=1
+       type="site"
+       allow_properties=global_properties.copy()
+       allow_properties.update({
+        'address':sp,
+        'networks':None,
+        'peer':None,
+        'pubkey':(lambda n,v:"key %s;\n"%v),
+        'mobile':sp,
+       })
+       require_properties={
+        'dh':"Diffie-Hellman group",
+        'contact':"Site admin contact address",
+        'networks':"Networks claimed by the site",
+        'hash':"hash function",
+        'peer':"Gateway address of the site",
+        'pubkey':"RSA public key of the site",
+       }
+       def __init__(self,w):
+               level.__init__(self,w)
+       def output_data(self,w,path):
+               ind=2*len(path)
+               np='/'.join(map(lambda i: i.name, path))
+               self.indent(w,ind)
+               w.write("%s {\n"%(self.kname()))
+               self.indent(w,ind+2)
+               w.write("name \"%s\";\n"%(np,))
+               self.output_props(w,ind+2)
+               self.indent(w,ind+2)
+               w.write("link netlink {\n");
+               self.indent(w,ind+4)
+               w.write("routes %s;\n"%str(self.properties["networks"]))
+               self.indent(w,ind+4)
+               w.write("ptp-address %s;\n"%str(self.properties["peer"]))
+               self.indent(w,ind+2)
+               w.write("};\n")
+               self.indent(w,ind)
+               w.write("};\n")
+
+# Levels in the configuration file
+# (depth,properties)
+levels={'vpn':vpnlevel, 'location':locationlevel, 'site':sitelevel}
+
+def complain(msg):
+       "Complain about a particular input line"
+       global complaints
+       print(("%s line %d: "%(file,line))+msg)
+       complaints=complaints+1
+def moan(msg):
+       "Complain about something in general"
+       global complaints
+       print(msg);
+       complaints=complaints+1
+
+class UntaintedRoot():
+       def __init__(self,s): self._s=s
+       def name(self): return self._s
+       def keyword(self): return self._s
+
+root=level([UntaintedRoot(x) for x in ['root','root']])
+# All vpns are children of this node
+obstack=[root]
+allow_defs=0   # Level above which new definitions are permitted
+
+def set_property(obj,w):
+       "Set a property on a configuration node"
+       prop=w[0]
+       if prop.raw() in obj.properties:
+               obj.properties[prop.raw_mark_ok()].add(obj,w)
+       else:
+               obj.properties[prop.raw()]=keywords[prop.raw_mark_ok()][0](w)
+
+
+def pline(il,allow_include=False):
+       "Process a configuration file line"
+       global allow_defs, obstack, root
+       w=il.rstrip('\n').split()
+       if len(w)==0: return ['']
+       w=list([Tainted(x) for x in w])
+       keyword=w[0]
+       current=obstack[len(obstack)-1]
+       copyout=lambda: ['    '*len(obstack) +
+                       ' '.join([ww.output() for ww in w]) +
+                       '\n']
+       if keyword=='end-definitions':
+               keyword.raw_mark_ok()
+               allow_defs=sitelevel.depth
+               obstack=[root]
+               return copyout()
+       if keyword=='include':
+               if not allow_include:
+                       complain("include not permitted here")
+                       return []
+               if len(w) != 2:
+                       complain("include requires one argument")
+                       return []
+               newfile=os.path.join(os.path.dirname(file),w[1].raw_mark_ok())
+               # ^ user of "include" is trusted so raw_mark_ok is good
+               return pfilepath(newfile,allow_include=allow_include)
+       if keyword.raw() in levels:
+               # We may go up any number of levels, but only down by one
+               newdepth=levels[keyword.raw_mark_ok()].depth
+               currentdepth=len(obstack) # actually +1...
+               if newdepth<=currentdepth:
+                       obstack=obstack[:newdepth]
+               if newdepth>currentdepth:
+                       complain("May not go from level %d to level %d"%
+                               (currentdepth-1,newdepth))
+               # See if it's a new one (and whether that's permitted)
+               # or an existing one
+               current=obstack[len(obstack)-1]
+               tname=w[1].name()
+               if tname in current.children:
+                       # Not new
+                       current=current.children[tname]
+                       if service and group and current.depth==2:
+                               if group!=current.group:
+                                       complain("Incorrect group!")
+                               w[2].groupname()
+               else:
+                       # New
+                       # Ignore depth check for now
+                       nl=levels[keyword.raw()](w)
+                       if nl.depth<allow_defs:
+                               complain("New definitions not allowed at "
+                                       "level %d"%nl.depth)
+                               # we risk crashing if we continue
+                               sys.exit(1)
+                       current.children[tname]=nl
+                       current=nl
+               obstack.append(current)
+               return copyout()
+       if keyword.raw() not in current.allow_properties:
+               complain("Property %s not allowed at %s level"%
+                       (keyword.raw(),current.type))
+               return []
+       elif current.depth == vpnlevel.depth < allow_defs:
+               complain("Not allowed to set VPN properties here")
+               return []
+       else:
+               set_property(current,w)
+               return copyout()
+
+       complain("unknown keyword '%s'"%(keyword.raw()))
+
+def pfilepath(pathname,allow_include=False):
+       f=open(pathname)
+       outlines=pfile(pathname,f.readlines(),allow_include=allow_include)
+       f.close()
+       return outlines
+
+def pfile(name,lines,allow_include=False):
+       "Process a file"
+       global file,line
+       file=name
+       line=0
+       outlines=[]
+       for i in lines:
+               line=line+1
+               if (i[0]=='#'): continue
+               outlines += pline(i,allow_include=allow_include)
+       return outlines
+
+def outputsites(w):
+       "Output include file for secnet configuration"
+       w.write("# secnet sites file autogenerated by make-secnet-sites "
+               +"version %s\n"%VERSION)
+       w.write("# %s\n"%time.asctime(time.localtime(time.time())))
+       w.write("# Command line: %s\n\n"%' '.join(sys.argv))
+
+       # Raw VPN data section of file
+       w.write(prefix+"vpn-data {\n")
+       for i in root.children.values():
+               i.output_data(w,(i,))
+       w.write("};\n")
+
+       # Per-VPN flattened lists
+       w.write(prefix+"vpn {\n")
+       for i in root.children.values():
+               i.output_vpnflat(w,())
+       w.write("};\n")
+
+       # Flattened list of sites
+       w.write(prefix+"all-sites %s;\n"%",".join(
+               map(lambda x:"%svpn/%s/all-sites"%(prefix,x.kname()),
+                       root.children.values())))
+
+line=0
+file=None
+complaints=0
+
+# Sanity check section
+# Delete nodes where leaf=0 that have no children
+
+def live(n):
+       "Number of leafnodes below node n"
+       if n.leaf: return 1
+       for i in n.children.keys():
+               if live(n.children[i]): return 1
+       return 0
+def delempty(n):
+       "Delete nodes that have no leafnode children"
+       for i in list(n.children.keys()):
+               delempty(n.children[i])
+               if not live(n.children[i]):
+                       del n.children[i]
+
+# Check that all constraints are met (as far as I can tell
+# restrict-nets/networks/peer are the only special cases)
+
+def checkconstraints(n,p,ra):
+       new_p=p.copy()
+       new_p.update(n.properties)
+       for i in n.require_properties.keys():
+               if i not in new_p:
+                       moan("%s %s is missing property %s"%
+                               (n.type,n.name,i))
+       for i in new_p.keys():
+               if i not in n.allow_properties:
+                       moan("%s %s has forbidden property %s"%
+                               (n.type,n.name,i))
+       # Check address range restrictions
+       if "restrict-nets" in n.properties:
+               new_ra=ra.intersection(n.properties["restrict-nets"].set)
+       else:
+               new_ra=ra
+       if "networks" in n.properties:
+               if not n.properties["networks"].set <= new_ra:
+                       moan("%s %s networks out of bounds"%(n.type,n.name))
+               if "peer" in n.properties:
+                       if not n.properties["networks"].set.contains(
+                               n.properties["peer"].addr):
+                               moan("%s %s peer not in networks"%(n.type,n.name))
+       for i in n.children.keys():
+               checkconstraints(n.children[i],new_p,new_ra)
+
+if service:
+       headerinput=pfilepath(header,allow_include=True)
+       userinput=sys.stdin.readlines()
+       pfile("user input",userinput)
+else:
+       if inputfile is None:
+               pfile("stdin",sys.stdin.readlines())
+       else:
+               pfilepath(inputfile)
+
+delempty(root)
+checkconstraints(root,{},ipaddrset.complete_set())
+
+if complaints>0:
+       if complaints==1: print("There was 1 problem.")
+       else: print("There were %d problems."%(complaints))
+       sys.exit(1)
+complaints=None # arranges to crash if we complain later
+
+if service:
+       # Put the user's input into their group file, and rebuild the main
+       # sites file
+       f=open(groupfiledir+"/T"+group.groupname(),'w')
+       f.write("# Section submitted by user %s, %s\n"%
+               (user,time.asctime(time.localtime(time.time()))))
+       f.write("# Checked by make-secnet-sites version %s\n\n"%VERSION)
+       for i in userinput: f.write(i)
+       f.write("\n")
+       f.close()
+       os.rename(groupfiledir+"/T"+group.groupname(),
+                 groupfiledir+"/R"+group.groupname())
+       f=open(sitesfile+"-tmp",'w')
+       f.write("# sites file autogenerated by make-secnet-sites\n")
+       f.write("# generated %s, invoked by %s\n"%
+               (time.asctime(time.localtime(time.time())),user))
+       f.write("# use make-secnet-sites to turn this file into a\n")
+       f.write("# valid /etc/secnet/sites.conf file\n\n")
+       for i in headerinput: f.write(i)
+       files=os.listdir(groupfiledir)
+       for i in files:
+               if i[0]=='R':
+                       j=open(groupfiledir+"/"+i)
+                       f.write(j.read())
+                       j.close()
+       f.write("# end of sites file\n")
+       f.close()
+       os.rename(sitesfile+"-tmp",sitesfile)
+else:
+       outputsites(of)
diff --git a/md5.c b/md5.c
new file mode 100644 (file)
index 0000000..ca9ddb1
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,297 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ * [I interpet this as a blanket permision -iwj.]
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h' header
+ * definitions; now uses stuff from dpkg's config.h.
+ *  - Ian Jackson <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#include "secnet.h"
+#include "util.h"
+#include <string.h>            /* for memcpy() */
+#include "md5.h"
+
+#ifdef WORDS_BIGENDIAN
+static void
+byteSwap(uint32_t *buf, int words)
+{
+       md5byte *p = (md5byte *)buf;
+
+       do {
+               *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 |
+                       ((unsigned)p[1] << 8 | p[0]);
+               p += 4;
+       } while (--words);
+}
+#else
+#define byteSwap(buf,words)
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void
+MD5Init(struct MD5Context *ctx)
+{
+       ctx->buf[0] = 0x67452301;
+       ctx->buf[1] = 0xefcdab89;
+       ctx->buf[2] = 0x98badcfe;
+       ctx->buf[3] = 0x10325476;
+
+       ctx->bytes[0] = 0;
+       ctx->bytes[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void
+MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len)
+{
+       uint32_t t;
+
+       /* Update byte count */
+
+       t = ctx->bytes[0];
+       if ((ctx->bytes[0] = t + len) < t)
+               ctx->bytes[1]++;        /* Carry from low to high */
+
+       t = 64 - (t & 0x3f);    /* Space available in ctx->in (at least 1) */
+       if (t > len) {
+               memcpy((md5byte *)ctx->in + 64 - t, buf, len);
+               return;
+       }
+       /* First chunk is an odd size */
+       memcpy((md5byte *)ctx->in + 64 - t, buf, t);
+       byteSwap(ctx->in, 16);
+       MD5Transform(ctx->buf, ctx->in);
+       buf += t;
+       len -= t;
+
+       /* Process data in 64-byte chunks */
+       while (len >= 64) {
+               memcpy(ctx->in, buf, 64);
+               byteSwap(ctx->in, 16);
+               MD5Transform(ctx->buf, ctx->in);
+               buf += 64;
+               len -= 64;
+       }
+
+       /* Handle any remaining bytes of data. */
+       memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void
+MD5Final(md5byte digest[16], struct MD5Context *ctx)
+{
+       int count = ctx->bytes[0] & 0x3f;       /* Number of bytes in ctx->in */
+       md5byte *p = (md5byte *)ctx->in + count;
+
+       /* Set the first char of padding to 0x80.  There is always room. */
+       *p++ = 0x80;
+
+       /* Bytes of padding needed to make 56 bytes (-8..55) */
+       count = 56 - 1 - count;
+
+       if (count < 0) {        /* Padding forces an extra block */
+               memset(p, 0, count + 8);
+               byteSwap(ctx->in, 16);
+               MD5Transform(ctx->buf, ctx->in);
+               p = (md5byte *)ctx->in;
+               count = 56;
+       }
+       memset(p, 0, count);
+       byteSwap(ctx->in, 14);
+
+       /* Append length in bits and transform */
+       ctx->in[14] = ctx->bytes[0] << 3;
+       ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29;
+       MD5Transform(ctx->buf, ctx->in);
+
+       byteSwap(ctx->buf, 4);
+       memcpy(digest, ctx->buf, 16);
+       memset(ctx, 0, sizeof *ctx);    /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f,w,x,y,z,in,s) \
+        (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x)
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+       register uint32_t a, b, c, d;
+
+       a = buf[0];
+       b = buf[1];
+       c = buf[2];
+       d = buf[3];
+
+       MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+       MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+       MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+       MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+       MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+       MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+       MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+       MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+       MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+       MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+       MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+       MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+       MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+       MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+       MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+       MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+       MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+       MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+       MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+       MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+       MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+       MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+       MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+       MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+       MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+       MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+       MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+       MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+       MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+       MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+       MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+       MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+       MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+       MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+       MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+       MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+       MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+       MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+       MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+       MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+       MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+       MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+       MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+       MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+       MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+       MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+       MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+       MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+       MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+       MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+       MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+       MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+       MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+       MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+       MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+       MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+       MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+       MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+       MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+       MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+       MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+       MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+       MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+       MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+       buf[0] += a;
+       buf[1] += b;
+       buf[2] += c;
+       buf[3] += d;
+}
+
+#endif
+
+static void md5_init(void *sst)
+{
+    struct MD5Context *ctx=sst;
+
+    MD5Init(ctx);
+}
+
+static void md5_update(void *sst, const void *buf, int32_t len)
+{
+    struct MD5Context *ctx=sst;
+
+    MD5Update(ctx,buf,len);
+}
+
+static void md5_final(void *sst, uint8_t *digest)
+{
+    struct MD5Context *ctx=sst;
+
+    MD5Final(digest,ctx);
+}
+
+struct md5 {
+    closure_t cl;
+    struct hash_if ops;
+};
+
+void md5_module(dict_t *dict)
+{
+    struct md5 *st;
+    cstring_t testinput="12345\n";
+    uint8_t expected[16]=
+       {0xd5,0x77,0x27,0x3f,0xf8,0x85,0xc3,0xf8,
+        0x4d,0xad,0xb8,0x57,0x8b,0xb4,0x13,0x99};
+    uint8_t digest[16];
+    int i;
+
+    NEW(st);
+    st->cl.description="md5";
+    st->cl.type=CL_HASH;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.hlen=16;
+    st->ops.slen=sizeof(struct MD5Context);
+    st->ops.init=md5_init;
+    st->ops.update=md5_update;
+    st->ops.final=md5_final;
+
+    dict_add(dict,"md5",new_closure(&st->cl));
+
+    hash_hash(&st->ops,testinput,strlen(testinput),digest);
+    for (i=0; i<16; i++) {
+       if (digest[i]!=expected[i]) {
+           fatal("md5 module failed self-test");
+       }
+    }
+}
diff --git a/md5.h b/md5.h
new file mode 100644 (file)
index 0000000..b5441d4
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,41 @@
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ * [I interpet this as a blanket permision -iwj.]
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ * Changed so as no longer to depend on Colin Plumb's `usual.h'
+ * header definitions; now uses stuff from dpkg's config.h
+ *  - Ian Jackson <ijackson@nyx.cs.du.edu>.
+ * Still in the public domain.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define md5byte unsigned char
+
+struct MD5Context {
+       uint32_t buf[4];
+       uint32_t bytes[2];
+       uint32_t in[16];
+};
+
+static void MD5Init(struct MD5Context *context);
+static void MD5Update(struct MD5Context *context,
+                     md5byte const *buf, unsigned len);
+static void MD5Final(unsigned char digest[16], struct MD5Context *context);
+static void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+#endif /* !MD5_H */
diff --git a/modules.c b/modules.c
new file mode 100644 (file)
index 0000000..24c1459
--- /dev/null
+++ b/modules.c
@@ -0,0 +1,40 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+
+void init_builtin_modules(dict_t *dict)
+{
+    resolver_module(dict);
+    random_module(dict);
+    udp_module(dict);
+    polypath_module(dict);
+    util_module(dict);
+    site_module(dict);
+    transform_eax_module(dict);
+    transform_cbcmac_module(dict);
+    netlink_module(dict);
+    rsa_module(dict);
+    dh_module(dict);
+    md5_module(dict);
+    slip_module(dict);
+    tun_module(dict);
+    sha1_module(dict);
+    log_module(dict);
+}
diff --git a/msgcode-test.c b/msgcode-test.c
new file mode 100644 (file)
index 0000000..401bf6f
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * msgcode-test.c: check that the new message encoding is correct
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ *
+ * Copyright 2017 Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "magic.h"
+
+#define OLD_LABEL_NAK     0x00000000
+#define OLD_LABEL_MSG0    0x00020200
+#define OLD_LABEL_MSG1    0x01010101
+#define OLD_LABEL_MSG2    0x02020202
+#define OLD_LABEL_MSG3    0x03030303
+#define OLD_LABEL_MSG3BIS 0x13030313
+#define OLD_LABEL_MSG4    0x04040404
+#define OLD_LABEL_MSG5    0x05050505
+#define OLD_LABEL_MSG6    0x06060606
+#define OLD_LABEL_MSG7    0x07070707
+#define OLD_LABEL_MSG8    0x08080808
+#define OLD_LABEL_MSG9    0x09090909
+#define OLD_LABEL_PROD    0x0a0a0a0a
+
+static void check_labels(const char *what, uint32_t new, uint32_t old)
+{
+    if (old != new) {
+       printf("mismatch for %s: %08"PRIx32" (new) /= %08"PRIx32" (old)\n",
+              what, new, old);
+       exit(2);
+    }
+}
+
+int main(void)
+{
+    unsigned i, j;
+    uint32_t m, r, s;
+
+#define CHECK(label) check_labels(#label, LABEL_##label, OLD_LABEL_##label)
+    CHECK(NAK);
+    CHECK(MSG0);
+    CHECK(MSG1);
+    CHECK(MSG2);
+    CHECK(MSG3);
+    CHECK(MSG3BIS);
+    CHECK(MSG4);
+    CHECK(MSG5);
+    CHECK(MSG6);
+    CHECK(MSG7);
+    CHECK(MSG8);
+    CHECK(MSG9);
+    CHECK(PROD);
+#undef CHECK
+    for (i = 0; i < 65536; i++) {
+       for (j = 0; j < 65536; j++) {
+           m = MSGCODE(i, j);
+           r = MSGMAJOR(m); s = MSGMINOR(m);
+           if (r != i || s != j) {
+               printf("roundtrip fail: %04x %04x -> %08"PRIx32" "
+                      "-> %08"PRIx32" %08"PRIx32"\n",
+                      i, j, m, r, s);
+               exit(2);
+           }
+       }
+    }
+
+    return (0);
+}
diff --git a/mtest/Dir.sd.mk b/mtest/Dir.sd.mk
new file mode 100644 (file)
index 0000000..bdb872f
--- /dev/null
@@ -0,0 +1,8 @@
+
+&DEPS += &~/make-secnet-sites
+&DEPS += &~/ipaddrset.py
+&DEPS += &^/common.tcl
+
+&:include test-common.sd.mk
+
+&check:: &check-real
diff --git a/mtest/Ginside.sites b/mtest/Ginside.sites
new file mode 100644 (file)
index 0000000..3348266
--- /dev/null
@@ -0,0 +1,8 @@
+vpn test-example
+location inside root
+site inside
+  networks 172.18.232.8/30
+  peer 172.18.232.9
+  address [127.0.0.1] 16910
+  mobile True
+  pubkey 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com
diff --git a/mtest/Goutside.sites b/mtest/Goutside.sites
new file mode 100644 (file)
index 0000000..c85f5f8
--- /dev/null
@@ -0,0 +1,7 @@
+vpn test-example
+location outside root
+site outside
+  networks 172.18.232.0/30
+  peer 172.18.232.1
+  address [::1] 16900
+  pubkey 1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com
diff --git a/mtest/common.tcl b/mtest/common.tcl
new file mode 100644 (file)
index 0000000..b6fde3e
--- /dev/null
@@ -0,0 +1,42 @@
+source test-common.tcl
+
+proc mss-program {} {
+    global env
+    set l ./make-secnet-sites
+    if {![catch { set py $env(MTEST_PYTHON) }]} {
+       set l [concat $py $l]
+    }
+    return $l
+}
+
+proc run-mss-userv {user group args} {
+    eval [list exec env USERV_USER=$user USERV_GROUP=$group] \
+        [mss-program] \
+        $args
+}
+
+proc run-mss {args} { eval [list exec] [mss-program] $args }
+
+proc diff {a b seddery {sedderyb X}} {
+    if {![string compare $sedderyb X]} { set sedderyb $seddery }
+    puts "$a $b $seddery $sedderyb"
+    exec bash -c "
+       diff -u <( <$a $seddery  ) \\
+               <( <$b $sedderyb )
+    "
+}
+
+proc diff-output {expected got suffix} {
+    global seddery
+    global tmp
+    diff mtest/$expected$suffix $tmp/$got$suffix $seddery
+}
+
+file mkdir $tmp/groupfiles
+
+set env(PYTHONHASHSEED) 0
+set env(PYTHONBYTECODEBASE) 0
+
+set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' }
+
+prefix_some_path PYTHONPATH .
diff --git a/mtest/delegations.sites b/mtest/delegations.sites
new file mode 100644 (file)
index 0000000..f6632e2
--- /dev/null
@@ -0,0 +1,5 @@
+location outside Goutside
+restrict-nets 172.18.232.0/29
+
+location inside Ginside
+restrict-nets 172.18.232.8/29
diff --git a/mtest/e-basic.conf b/mtest/e-basic.conf
new file mode 100644 (file)
index 0000000..9a4fde8
--- /dev/null
@@ -0,0 +1,48 @@
+# secnet sites file autogenerated by make-secnet-sites version 0.1.18
+# Tue Nov 19 01:02:50 2019
+# Command line: ./make-secnet-sites --no-conf-key-prefix test-example/sites ./mtest/d-basic/out.conf
+
+vpn-data {
+  test-example {
+    # Contact email address: <devnull@example.com>
+    dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2");
+    hash sha1;
+    key-lifetime 72000000;
+    # restrict-nets "172.18.232.0/28"
+    setup-retries 5;
+    setup-timeout 2000;
+
+    in {
+      inside {
+        name "test-example/in/inside";
+        address "[127.0.0.1]"; port 16910;
+        mobile True;
+        key rsa-public("65537","130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669");
+        link netlink {
+          routes "172.18.232.8/29";
+          ptp-address "172.18.232.9";
+        };
+      };
+    };
+    out {
+      outside {
+        name "test-example/out/outside";
+        address "[::1]"; port 16900;
+        key rsa-public("65537","129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941");
+        link netlink {
+          routes "172.18.232.0/29";
+          ptp-address "172.18.232.1";
+        };
+      };
+    };
+  };
+};
+vpn {
+  test-example {
+    out vpn-data/test-example/out/outside;
+    in vpn-data/test-example/in/inside;
+
+    all-sites out,in;
+  };
+};
+all-sites vpn/test-example/all-sites;
diff --git a/mtest/e-userv.sites b/mtest/e-userv.sites
new file mode 100644 (file)
index 0000000..e9108a6
--- /dev/null
@@ -0,0 +1,35 @@
+# sites file autogenerated by make-secnet-sites
+# generated Sun Oct 20 13:21:06 2019, invoked by Uuser
+# use make-secnet-sites to turn this file into a
+# valid /etc/secnet/sites.conf file
+
+vpn test-example
+contact header@example.com
+dh 8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3 2
+hash sha1
+key-lifetime 72000000
+restrict-nets 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
+setup-timeout 2000
+setup-retries 5
+
+location outside Goutside
+restrict-nets 172.18.232.0/29
+
+location inside Ginside
+restrict-nets 172.18.232.8/29
+
+end-definitions
+
+# Section submitted by user Uuser, Sun Oct 20 13:21:06 2019
+# Checked by make-secnet-sites version 0.1.18
+
+vpn test-example
+location inside root
+site inside
+  networks 172.18.232.8/30
+  peer 172.18.232.9
+  address [127.0.0.1] 16910
+  mobile True
+  pubkey 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com
+
+# end of sites file
diff --git a/mtest/header.sites b/mtest/header.sites
new file mode 100644 (file)
index 0000000..f4b3481
--- /dev/null
@@ -0,0 +1,13 @@
+vpn test-example
+contact header@example.com
+dh 8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3 2
+hash sha1
+key-lifetime 72000000
+restrict-nets 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
+setup-timeout 2000
+setup-retries 5
+
+include delegations.sites
+
+end-definitions
+
diff --git a/mtest/t-basic b/mtest/t-basic
new file mode 100755 (executable)
index 0000000..ad754c1
--- /dev/null
@@ -0,0 +1,8 @@
+#! /usr/bin/tclsh
+
+source mtest/common.tcl
+
+run-mss --no-conf-key-prefix test-example/sites $tmp/out.conf
+
+set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' }
+diff  mtest/e-basic.conf $tmp/out.conf $seddery
diff --git a/mtest/t-prefix b/mtest/t-prefix
new file mode 100755 (executable)
index 0000000..2db65e3
--- /dev/null
@@ -0,0 +1,10 @@
+#! /usr/bin/tclsh
+
+source mtest/common.tcl
+
+run-mss -Ppprefix --no-conf-key-prefix test-example/sites $tmp/out.conf
+
+set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' }
+diff  mtest/e-basic.conf $tmp/out.conf \
+    "sed -e 's/vpn/pprefixvpn/g; s/^all-sites/pprefix&/' | $seddery" \
+    $seddery
diff --git a/mtest/t-userv b/mtest/t-userv
new file mode 100755 (executable)
index 0000000..5d159bd
--- /dev/null
@@ -0,0 +1,37 @@
+#! /usr/bin/tclsh
+
+source mtest/common.tcl
+
+#----- success test -----
+
+set good [list Uuser Ginside -u \
+          mtest/header.sites $tmp/groupfiles $tmp/out.sites Ginside \
+         < mtest/Ginside.sites]
+
+eval run-mss-userv $good
+
+diff-output e-userv out .sites
+
+#----- argument parser does not look for args beyond header -----
+
+set env(LC_MESSAGES) C
+
+set try [lreplace $good 4 4 --misparse-test]
+
+if {![catch {
+    eval run-mss-userv $try
+} emsg]} {
+    error "should have failed"
+} else {
+    switch -glob $emsg {
+       {*unrecognized arguments: --misparse-test*} {
+           error "misparsed!"
+       }
+       {*No such file or directory: '--misparse-test/TGinside'*} {
+       }
+       * {
+           error "huh ? $emsg"
+       }
+    }
+}
+
diff --git a/netlink.c b/netlink.c
new file mode 100644 (file)
index 0000000..7add6d7
--- /dev/null
+++ b/netlink.c
@@ -0,0 +1,1345 @@
+/* User-kernel network link */
+
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+/* See RFCs 791, 792, 1123 and 1812 */
+
+/* The netlink device is actually a router.  Tunnels are unnumbered
+   point-to-point lines (RFC1812 section 2.2.7); the router has a
+   single address (the 'router-id'). */
+
+/* This is where we currently have the anti-spoofing paranoia - before
+   sending a packet to the kernel we check that the tunnel it came
+   over could reasonably have produced it. */
+
+
+/* Points to note from RFC1812 (which may require changes in this
+   file):
+
+3.3.4 Maximum Transmission Unit - MTU
+
+   The MTU of each logical interface MUST be configurable within the
+   range of legal MTUs for the interface.
+
+   Many Link Layer protocols define a maximum frame size that may be
+   sent.  In such cases, a router MUST NOT allow an MTU to be set which
+   would allow sending of frames larger than those allowed by the Link
+   Layer protocol.  However, a router SHOULD be willing to receive a
+   packet as large as the maximum frame size even if that is larger than
+   the MTU.
+
+4.2.1  A router SHOULD count datagrams discarded.
+
+4.2.2.1 Source route options - we probably should implement processing
+of source routes, even though mostly the security policy will prevent
+their use.
+
+5.3.13.4 Source Route Options
+
+   A router MUST implement support for source route options in forwarded
+   packets.  A router MAY implement a configuration option that, when
+   enabled, causes all source-routed packets to be discarded.  However,
+   such an option MUST NOT be enabled by default.
+
+5.3.13.5 Record Route Option
+
+   Routers MUST support the Record Route option in forwarded packets.
+
+   A router MAY provide a configuration option that, if enabled, will
+   cause the router to ignore (i.e., pass through unchanged) Record
+   Route options in forwarded packets.  If provided, such an option MUST
+   default to enabling the record-route.  This option should not affect
+   the processing of Record Route options in datagrams received by the
+   router itself (in particular, Record Route options in ICMP echo
+   requests will still be processed according to Section [4.3.3.6]).
+
+5.3.13.6 Timestamp Option
+
+   Routers MUST support the timestamp option in forwarded packets.  A
+   timestamp value MUST follow the rules given [INTRO:2].
+
+   If the flags field = 3 (timestamp and prespecified address), the
+   router MUST add its timestamp if the next prespecified address
+   matches any of the router's IP addresses.  It is not necessary that
+   the prespecified address be either the address of the interface on
+   which the packet arrived or the address of the interface over which
+   it will be sent.
+
+
+4.2.2.7 Fragmentation: RFC 791 Section 3.2
+
+   Fragmentation, as described in [INTERNET:1], MUST be supported by a
+   router.
+
+4.2.2.8 Reassembly: RFC 791 Section 3.2
+
+   As specified in the corresponding section of [INTRO:2], a router MUST
+   support reassembly of datagrams that it delivers to itself.
+
+4.2.2.9 Time to Live: RFC 791 Section 3.2
+
+   Note in particular that a router MUST NOT check the TTL of a packet
+   except when forwarding it.
+
+   A router MUST NOT discard a datagram just because it was received
+   with TTL equal to zero or one; if it is to the router and otherwise
+   valid, the router MUST attempt to receive it.
+
+   On messages the router originates, the IP layer MUST provide a means
+   for the transport layer to set the TTL field of every datagram that
+   is sent.  When a fixed TTL value is used, it MUST be configurable.
+
+
+8.1 The Simple Network Management Protocol - SNMP
+8.1.1 SNMP Protocol Elements
+
+   Routers MUST be manageable by SNMP [MGT:3].  The SNMP MUST operate
+   using UDP/IP as its transport and network protocols.
+
+
+*/
+
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include "secnet.h"
+#include "util.h"
+#include "ipaddr.h"
+#include "netlink.h"
+#include "process.h"
+
+#ifdef NETLINK_DEBUG
+#define MDEBUG(...) Message(M_DEBUG, __VA_ARGS__)
+#else /* !NETLINK_DEBUG */
+#define MDEBUG(...) ((void)0)
+#endif /* !NETLINK_DEBUG */
+
+#define ICMP_TYPE_ECHO_REPLY             0
+
+#define ICMP_TYPE_UNREACHABLE            3
+#define ICMP_CODE_NET_UNREACHABLE        0
+#define ICMP_CODE_PROTOCOL_UNREACHABLE   2
+#define ICMP_CODE_FRAGMENTATION_REQUIRED 4
+#define ICMP_CODE_NET_PROHIBITED        13
+
+#define ICMP_TYPE_ECHO_REQUEST           8
+
+#define ICMP_TYPE_TIME_EXCEEDED         11
+#define ICMP_CODE_TTL_EXCEEDED           0
+
+/* Generic IP checksum routine */
+static inline uint16_t ip_csum(const uint8_t *iph,int32_t count)
+{
+    register uint32_t sum=0;
+
+    while (count>1) {
+       sum+=ntohs(*(uint16_t *)iph);
+       iph+=2;
+       count-=2;
+    }
+    if(count>0)
+       sum+=*(uint8_t *)iph;
+    while (sum>>16)
+       sum=(sum&0xffff)+(sum>>16);
+    return htons(~sum);
+}
+
+#ifdef i386
+/*
+ *      This is a version of ip_compute_csum() optimized for IP headers,
+ *      which always checksum on 4 octet boundaries.
+ *
+ *      By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
+ *      Arnt Gulbrandsen.
+ */
+static inline uint16_t ip_fast_csum(const uint8_t *iph, int32_t ihl) {
+    uint32_t sum;
+
+    __asm__ __volatile__(
+            "movl (%1), %0      ;\n"
+            "subl $4, %2        ;\n"
+            "jbe 2f             ;\n"
+            "addl 4(%1), %0     ;\n"
+            "adcl 8(%1), %0     ;\n"
+            "adcl 12(%1), %0    ;\n"
+"1:         adcl 16(%1), %0     ;\n"
+            "lea 4(%1), %1      ;\n"
+            "decl %2            ;\n"
+            "jne 1b             ;\n"
+            "adcl $0, %0        ;\n"
+            "movl %0, %2        ;\n"
+            "shrl $16, %0       ;\n"
+            "addw %w2, %w0      ;\n"
+            "adcl $0, %0        ;\n"
+            "notl %0            ;\n"
+"2:                             ;\n"
+        /* Since the input registers which are loaded with iph and ipl
+           are modified, we must also specify them as outputs, or gcc
+           will assume they contain their original values. */
+        : "=r" (sum), "=r" (iph), "=r" (ihl)
+        : "1" (iph), "2" (ihl)
+       : "memory");
+    return sum;
+}
+#else
+static inline uint16_t ip_fast_csum(const uint8_t *iph, int32_t ihl)
+{
+    assert(ihl < INT_MAX/4);
+    return ip_csum(iph,ihl*4);
+}
+#endif
+
+struct iphdr {
+#if defined (WORDS_BIGENDIAN)
+    uint8_t    version:4,
+              ihl:4;
+#else
+    uint8_t    ihl:4,
+              version:4;
+#endif
+    uint8_t    tos;
+    uint16_t   tot_len;
+    uint16_t   id;
+    uint16_t   frag;
+#define IPHDR_FRAG_OFF  ((uint16_t)0x1fff)
+#define IPHDR_FRAG_MORE ((uint16_t)0x2000)
+#define IPHDR_FRAG_DONT ((uint16_t)0x4000)
+/*                 reserved        0x8000 */
+    uint8_t    ttl;
+    uint8_t    protocol;
+    uint16_t   check;
+    uint32_t   saddr;
+    uint32_t   daddr;
+    /* The options start here. */
+};
+
+struct icmphdr {
+    struct iphdr iph;
+    uint8_t type;
+    uint8_t code;
+    uint16_t check;
+    union icmpinfofield {
+       uint32_t unused;
+       struct {
+           uint8_t pointer;
+           uint8_t unused1;
+           uint16_t unused2;
+       } pprob;
+       uint32_t gwaddr;
+       struct {
+           uint16_t id;
+           uint16_t seq;
+       } echo;
+       struct {
+           uint16_t unused;
+           uint16_t mtu;
+       } fragneeded;
+    } d;
+};
+
+static const union icmpinfofield icmp_noinfo;
+    
+static void netlink_client_deliver(struct netlink *st,
+                                  struct netlink_client *client,
+                                  uint32_t source, uint32_t dest,
+                                  struct buffer_if *buf);
+static void netlink_host_deliver(struct netlink *st,
+                                struct netlink_client *sender,
+                                uint32_t source, uint32_t dest,
+                                struct buffer_if *buf);
+
+static const char *sender_name(struct netlink_client *sender /* or NULL */)
+{
+    return sender?sender->name:"(local)";
+}
+
+static void netlink_packet_deliver(struct netlink *st,
+                                  struct netlink_client *client,
+                                  struct buffer_if *buf);
+
+/* XXX RFC1812 4.3.2.5:
+   All other ICMP error messages (Destination Unreachable,
+   Redirect, Time Exceeded, and Parameter Problem) SHOULD have their
+   precedence value set to 6 (INTERNETWORK CONTROL) or 7 (NETWORK
+   CONTROL).  The IP Precedence value for these error messages MAY be
+   settable.
+   */
+static struct icmphdr *netlink_icmp_tmpl(struct netlink *st,
+                                        uint32_t source, uint32_t dest,
+                                        uint16_t len)
+{
+    struct icmphdr *h;
+
+    BUF_ALLOC(&st->icmp,"netlink_icmp_tmpl");
+    buffer_init(&st->icmp,calculate_max_start_pad());
+    h=buf_append(&st->icmp,sizeof(*h));
+
+    h->iph.version=4;
+    h->iph.ihl=5;
+    h->iph.tos=0;
+    h->iph.tot_len=htons(len+(h->iph.ihl*4)+8);
+    h->iph.id=0;
+    h->iph.frag=0;
+    h->iph.ttl=255; /* XXX should be configurable */
+    h->iph.protocol=1;
+    h->iph.saddr=htonl(source);
+    h->iph.daddr=htonl(dest);
+    h->iph.check=0;
+    h->iph.check=ip_fast_csum((uint8_t *)&h->iph,h->iph.ihl);
+    h->check=0;
+    h->d.unused=0;
+
+    return h;
+}
+
+/* Fill in the ICMP checksum field correctly */
+static void netlink_icmp_csum(struct icmphdr *h)
+{
+    int32_t len;
+
+    len=ntohs(h->iph.tot_len)-(4*h->iph.ihl);
+    h->check=0;
+    h->check=ip_csum(&h->type,len);
+}
+
+/* RFC1122:
+ *       An ICMP error message MUST NOT be sent as the result of
+ *       receiving:
+ *
+ *       *    an ICMP error message, or
+ *
+ *       *    a datagram destined to an IP broadcast or IP multicast
+ *            address, or
+ *
+ *       *    a datagram sent as a link-layer broadcast, or
+ *
+ *       *    a non-initial fragment, or
+ *
+ *       *    a datagram whose source address does not define a single
+ *            host -- e.g., a zero address, a loopback address, a
+ *            broadcast address, a multicast address, or a Class E
+ *            address.
+ */
+static bool_t netlink_icmp_may_reply(struct buffer_if *buf)
+{
+    struct iphdr *iph;
+    struct icmphdr *icmph;
+    uint32_t source;
+
+    if (buf->size < (int)sizeof(struct icmphdr)) return False;
+    iph=(struct iphdr *)buf->start;
+    icmph=(struct icmphdr *)buf->start;
+    if (iph->protocol==1) {
+       switch(icmph->type) {
+           /* Based on http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-types
+            * as retrieved Thu, 20 Mar 2014 00:16:44 +0000.
+            * Deprecated, reserved, unassigned and experimental
+            * options are treated as not safe to reply to.
+            */
+       case 0: /* Echo Reply */
+       case 8: /* Echo */
+       case 13: /* Timestamp */
+       case 14: /* Timestamp Reply */
+           return True;
+       default:
+           return False;
+       }
+    }
+    /* How do we spot broadcast destination addresses? */
+    if (ntohs(iph->frag)&IPHDR_FRAG_OFF) return False;
+    source=ntohl(iph->saddr);
+    if (source==0) return False;
+    if ((source&0xff000000)==0x7f000000) return False;
+    /* How do we spot broadcast source addresses? */
+    if ((source&0xf0000000)==0xe0000000) return False; /* Multicast */
+    if ((source&0xf0000000)==0xf0000000) return False; /* Class E */
+    return True;
+}
+
+/* How much of the original IP packet do we include in its ICMP
+   response? The header plus up to 64 bits. */
+
+/* XXX TODO RFC1812:
+4.3.2.3 Original Message Header
+
+   Historically, every ICMP error message has included the Internet
+   header and at least the first 8 data bytes of the datagram that
+   triggered the error.  This is no longer adequate, due to the use of
+   IP-in-IP tunneling and other technologies.  Therefore, the ICMP
+   datagram SHOULD contain as much of the original datagram as possible
+   without the length of the ICMP datagram exceeding 576 bytes.  The
+   returned IP header (and user data) MUST be identical to that which
+   was received, except that the router is not required to undo any
+   modifications to the IP header that are normally performed in
+   forwarding that were performed before the error was detected (e.g.,
+   decrementing the TTL, or updating options).  Note that the
+   requirements of Section [4.3.3.5] supersede this requirement in some
+   cases (i.e., for a Parameter Problem message, if the problem is in a
+   modified field, the router must undo the modification).  See Section
+   [4.3.3.5]).
+   */
+static uint16_t netlink_icmp_reply_len(struct buffer_if *buf)
+{
+    if (buf->size < (int)sizeof(struct iphdr)) return 0;
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    uint16_t hlen,plen;
+
+    hlen=iph->ihl*4;
+    /* We include the first 8 bytes of the packet data, provided they exist */
+    hlen+=8;
+    plen=ntohs(iph->tot_len);
+    return MIN(hlen,plen);
+}
+
+/* client indicates where the packet we're constructing a response to
+   comes from. NULL indicates the host. */
+static void netlink_icmp_simple(struct netlink *st,
+                               struct netlink_client *origsender,
+                               struct buffer_if *buf,
+                               uint8_t type, uint8_t code,
+                               union icmpinfofield info)
+{
+    struct icmphdr *h;
+    uint16_t len;
+
+    if (netlink_icmp_may_reply(buf)) {
+       struct iphdr *iph=(struct iphdr *)buf->start;
+
+       uint32_t icmpdest = ntohl(iph->saddr);
+       uint32_t icmpsource;
+       const char *icmpsourcedebugprefix;
+       if (!st->ptp) {
+           icmpsource=st->secnet_address;
+           icmpsourcedebugprefix="";
+       } else if (origsender) {
+           /* was from peer, send reply as if from host */
+           icmpsource=st->local_address;
+           icmpsourcedebugprefix="L!";
+       } else {
+           /* was from host, send reply as if from peer */
+           icmpsource=st->secnet_address; /* actually, peer address */
+           icmpsourcedebugprefix="P!";
+       }
+       MDEBUG("%s: generating ICMP re %s[%s]->[%s]:"
+              " from %s%s type=%u code=%u\n",
+              st->name, sender_name(origsender),
+              ipaddr_to_string(ntohl(iph->saddr)),
+              ipaddr_to_string(ntohl(iph->daddr)),
+              icmpsourcedebugprefix,
+              ipaddr_to_string(icmpsource),
+              type, code);
+
+       len=netlink_icmp_reply_len(buf);
+       h=netlink_icmp_tmpl(st,icmpsource,icmpdest,len);
+       h->type=type; h->code=code; h->d=info;
+       BUF_ADD_BYTES(append,&st->icmp,buf->start,len);
+       netlink_icmp_csum(h);
+
+       if (!st->ptp) {
+           netlink_packet_deliver(st,NULL,&st->icmp);
+       } else if (origsender) {
+           netlink_client_deliver(st,origsender,icmpsource,icmpdest,&st->icmp);
+       } else {
+           netlink_host_deliver(st,NULL,icmpsource,icmpdest,&st->icmp);
+       }
+       BUF_ASSERT_FREE(&st->icmp);
+    }
+}
+
+/*
+ * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the
+ * checksum.
+ * RFC1812: 4.2.2.5 MUST discard messages containing invalid checksums.
+ *
+ * Is the datagram acceptable?
+ *
+ * 1. Length at least the size of an ip header
+ * 2. Version of 4
+ * 3. Checksums correctly.
+ * 4. Doesn't have a bogus length
+ */
+static bool_t netlink_check(struct netlink *st, struct buffer_if *buf,
+                           char *errmsgbuf, int errmsgbuflen)
+{
+#define BAD(...) do{                                   \
+       snprintf(errmsgbuf,errmsgbuflen,__VA_ARGS__);   \
+       return False;                                   \
+    }while(0)
+
+    if (buf->size < (int)sizeof(struct iphdr)) BAD("len %"PRIu32"",buf->size);
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    int32_t len;
+
+    if (iph->version != 4) BAD("version %u",iph->version);
+    if (iph->ihl < 5) BAD("ihl %u",iph->ihl);
+    if (buf->size < iph->ihl*4) BAD("size %"PRId32"<%u*4",buf->size,iph->ihl);
+    if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) BAD("csum");
+    len=ntohs(iph->tot_len);
+    /* There should be no padding */
+    if (buf->size!=len) BAD("len %"PRId32"!=%"PRId32,buf->size,len);
+    if (len<(iph->ihl<<2)) BAD("len %"PRId32"<(%u<<2)",len,iph->ihl);
+    /* XXX check that there's no source route specified */
+    return True;
+
+#undef BAD
+}
+
+static const char *fragment_filter_header(uint8_t *base, long *hlp)
+{
+    const int fixedhl = sizeof(struct iphdr);
+    long hl = *hlp;
+    const uint8_t *ipend = base + hl;
+    uint8_t *op = base + fixedhl;
+    const uint8_t *ip = op;
+
+    while (ip < ipend) {
+       uint8_t opt = ip[0];
+       int remain = ipend - ip;
+       if (opt == 0x00) /* End of Options List */ break;
+       if (opt == 0x01) /* No Operation */ continue;
+       if (remain < 2) return "IPv4 options truncated at length";
+       int optlen = ip[1];
+       if (remain < optlen) return "IPv4 options truncated in option";
+       if (opt & 0x80) /* copy */ {
+           memmove(op, ip, optlen);
+           op += optlen;
+       }
+       ip += optlen;
+    }
+    while ((hl = (op - base)) & 0x3)
+       *op++ = 0x00 /* End of Option List */;
+    ((struct iphdr*)base)->ihl = hl >> 2;
+    *hlp = hl;
+
+    return 0;
+}
+
+/* Fragment or send ICMP Fragmentation Needed */
+static void netlink_maybe_fragment(struct netlink *st,
+                                  struct netlink_client *sender,
+                                  netlink_deliver_fn *deliver,
+                                  void *deliver_dst,
+                                  const char *delivery_name,
+                                  int32_t mtu,
+                                  uint32_t source, uint32_t dest,
+                                  struct buffer_if *buf)
+{
+    struct iphdr *iph=(struct iphdr*)buf->start;
+    long hl = iph->ihl*4;
+    const char *ssource = ipaddr_to_string(source);
+
+    if (buf->size <= mtu) {
+       deliver(deliver_dst, buf);
+       return;
+    }
+
+    MDEBUG("%s: fragmenting %s->%s org.size=%"PRId32"\n",
+          st->name, ssource, delivery_name, buf->size);
+
+#define BADFRAG(m, ...)                                        \
+       Message(M_WARNING,                              \
+               "%s: fragmenting packet from source %s" \
+               " for transmission via %s: " m "\n",    \
+               st->name, ssource, delivery_name,       \
+               ## __VA_ARGS__);
+
+    unsigned orig_frag = ntohs(iph->frag);
+
+    if (orig_frag&IPHDR_FRAG_DONT) {
+       union icmpinfofield info =
+           { .fragneeded = { .unused = 0, .mtu = htons(mtu) } };
+       netlink_icmp_simple(st,sender,buf,
+                           ICMP_TYPE_UNREACHABLE,
+                           ICMP_CODE_FRAGMENTATION_REQUIRED,
+                           info);
+       BUF_FREE(buf);
+       return;
+    }
+    if (mtu < hl + 8) {
+       BADFRAG("mtu %"PRId32" too small", mtu);
+       BUF_FREE(buf);
+       return;
+    }
+
+    /* we (ab)use the icmp buffer to stash the original packet */
+    struct buffer_if *orig = &st->icmp;
+    BUF_ALLOC(orig,"netlink_client_deliver fragment orig");
+    buffer_copy(orig,buf);
+    BUF_FREE(buf);
+
+    const uint8_t *startindata = orig->start + hl;
+    const uint8_t *indata =      startindata;
+    const uint8_t *endindata =   orig->start + orig->size;
+    _Bool filtered = 0;
+
+    for (;;) {
+       /* compute our fragment offset */
+       long dataoffset = indata - startindata
+           + (orig_frag & IPHDR_FRAG_OFF)*8;
+       assert(!(dataoffset & 7));
+       if (dataoffset > IPHDR_FRAG_OFF*8) {
+           BADFRAG("ultimate fragment offset out of range");
+           break;
+       }
+
+       BUF_ALLOC(buf,"netlink_client_deliver fragment frag");
+       buffer_init(buf,calculate_max_start_pad());
+
+       /* copy header (possibly filtered); will adjust in a bit */
+       struct iphdr *fragh = buf_append(buf, hl);
+       memcpy(fragh, orig->start, hl);
+
+       /* decide how much payload to copy and copy it */
+       long avail = mtu - hl;
+       long remain = endindata - indata;
+       long use = avail < remain ? (avail & ~(long)7) : remain;
+       BUF_ADD_BYTES(append, buf, indata, use);
+       indata += use;
+
+       _Bool last_frag = indata >= endindata;
+
+       /* adjust the header */
+       fragh->tot_len = htons(buf->size);
+       fragh->frag =
+           htons((orig_frag & ~IPHDR_FRAG_OFF) |
+                 (last_frag ? 0 : IPHDR_FRAG_MORE) |
+                 (dataoffset >> 3));
+       fragh->check = 0;
+       fragh->check = ip_fast_csum((const void*)fragh, fragh->ihl);
+
+       /* actually send it */
+       deliver(deliver_dst, buf);
+       if (last_frag)
+           break;
+
+       /* after copying the header for the first frag,
+        * we filter the header for the remaining frags */
+       if (!filtered++) {
+           const char *bad = fragment_filter_header(orig->start, &hl);
+           if (bad) { BADFRAG("%s", bad); break; }
+       }
+    }
+
+    BUF_FREE(orig);
+
+#undef BADFRAG
+}
+
+/* Deliver a packet _to_ client; used after we have decided
+ * what to do with it (and just to check that the client has
+ * actually registered a delivery function with us). */
+static void netlink_client_deliver(struct netlink *st,
+                                  struct netlink_client *client,
+                                  uint32_t source, uint32_t dest,
+                                  struct buffer_if *buf)
+{
+    if (!client->deliver) {
+       string_t s,d;
+       s=ipaddr_to_string(source);
+       d=ipaddr_to_string(dest);
+       Message(M_ERR,"%s: dropping %s->%s, client not registered\n",
+               st->name,s,d);
+       BUF_FREE(buf);
+       return;
+    }
+    netlink_maybe_fragment(st,NULL, client->deliver,client->dst,client->name,
+                          client->mtu, source,dest,buf);
+    client->outcount++;
+}
+
+/* Deliver a packet to the host; used after we have decided that that
+ * is what to do with it. */
+static void netlink_host_deliver(struct netlink *st,
+                                struct netlink_client *sender,
+                                uint32_t source, uint32_t dest,
+                                struct buffer_if *buf)
+{
+    netlink_maybe_fragment(st,sender, st->deliver_to_host,st->dst,"(host)",
+                          st->mtu, source,dest,buf);
+    st->outcount++;
+}
+
+/* Deliver a packet. "sender"==NULL for packets from the host and packets
+   generated internally in secnet.  */
+static void netlink_packet_deliver(struct netlink *st,
+                                  struct netlink_client *sender,
+                                  struct buffer_if *buf)
+{
+    if (buf->size < (int)sizeof(struct iphdr)) {
+       Message(M_ERR,"%s: trying to deliver a too-short packet"
+               " from %s!\n",st->name, sender_name(sender));
+       BUF_FREE(buf);
+       return;
+    }
+
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    uint32_t dest=ntohl(iph->daddr);
+    uint32_t source=ntohl(iph->saddr);
+    uint32_t best_quality;
+    bool_t allow_route=False;
+    bool_t found_allowed=False;
+    int best_match;
+    int i;
+
+    BUF_ASSERT_USED(buf);
+
+    if (dest==st->secnet_address) {
+       Message(M_ERR,"%s: trying to deliver a packet to myself!\n",st->name);
+       BUF_FREE(buf);
+       return;
+    }
+    
+    /* Packets from the host (sender==NULL) may always be routed.  Packets
+       from clients with the allow_route option will also be routed. */
+    if (!sender || (sender && (sender->options & OPT_ALLOWROUTE)))
+       allow_route=True;
+
+    /* If !allow_route, we check the routing table anyway, and if
+       there's a suitable route with OPT_ALLOWROUTE set we use it.  If
+       there's a suitable route, but none with OPT_ALLOWROUTE set then
+       we generate ICMP 'communication with destination network
+       administratively prohibited'. */
+
+    best_quality=0;
+    best_match=-1;
+    for (i=0; i<st->n_clients; i++) {
+       if (st->routes[i]->up &&
+           ipset_contains_addr(st->routes[i]->networks,dest)) {
+           /* It's an available route to the correct destination. But is
+              it better than the one we already have? */
+
+           /* If we have already found an allowed route then we don't
+              bother looking at routes we're not allowed to use.  If
+              we don't yet have an allowed route we'll consider any.  */
+           if (!allow_route && found_allowed) {
+               if (!(st->routes[i]->options&OPT_ALLOWROUTE)) continue;
+           }
+           
+           if (st->routes[i]->link_quality>best_quality
+               || best_quality==0) {
+               best_quality=st->routes[i]->link_quality;
+               best_match=i;
+               if (st->routes[i]->options&OPT_ALLOWROUTE)
+                   found_allowed=True;
+               /* If quality isn't perfect we may wish to
+                  consider kicking the tunnel with a 0-length
+                  packet to prompt it to perform a key setup.
+                  Then it'll eventually decide it's up or
+                  down. */
+               /* If quality is perfect and we're allowed to use the
+                  route we don't need to search any more. */
+               if (best_quality>=MAXIMUM_LINK_QUALITY && 
+                   (allow_route || found_allowed)) break;
+           }
+       }
+    }
+    if (best_match==-1) {
+       /* The packet's not going down a tunnel.  It might (ought to)
+          be for the host.   */
+       if (ipset_contains_addr(st->networks,dest)) {
+           netlink_host_deliver(st,sender,source,dest,buf);
+           BUF_ASSERT_FREE(buf);
+       } else {
+           string_t s,d;
+           s=ipaddr_to_string(source);
+           d=ipaddr_to_string(dest);
+           Message(M_DEBUG,"%s: don't know where to deliver packet "
+                   "(s=%s, d=%s)\n", st->name, s, d);
+           netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE,
+                               ICMP_CODE_NET_UNREACHABLE, icmp_noinfo);
+           BUF_FREE(buf);
+       }
+    } else {
+       if (!allow_route &&
+           !(st->routes[best_match]->options&OPT_ALLOWROUTE)) {
+           string_t s,d;
+           s=ipaddr_to_string(source);
+           d=ipaddr_to_string(dest);
+           /* We have a usable route but aren't allowed to use it.
+              Generate ICMP destination unreachable: communication
+              with destination network administratively prohibited */
+           Message(M_NOTICE,"%s: denied forwarding for packet (s=%s, d=%s)\n",
+                   st->name,s,d);
+                   
+           netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE,
+                               ICMP_CODE_NET_PROHIBITED, icmp_noinfo);
+           BUF_FREE(buf);
+       } else {
+           if (best_quality>0) {
+               netlink_client_deliver(st,st->routes[best_match],
+                                      source,dest,buf);
+               BUF_ASSERT_FREE(buf);
+           } else {
+               /* Generate ICMP destination unreachable */
+               netlink_icmp_simple(st,sender,buf,
+                                   ICMP_TYPE_UNREACHABLE,
+                                   ICMP_CODE_NET_UNREACHABLE,
+                                   icmp_noinfo);
+               BUF_FREE(buf);
+           }
+       }
+    }
+    BUF_ASSERT_FREE(buf);
+}
+
+static void netlink_packet_forward(struct netlink *st, 
+                                  struct netlink_client *sender,
+                                  struct buffer_if *buf)
+{
+    if (buf->size < (int)sizeof(struct iphdr)) return;
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    
+    BUF_ASSERT_USED(buf);
+
+    /* Packet has already been checked */
+    if (iph->ttl<=1) {
+       /* Generate ICMP time exceeded */
+       netlink_icmp_simple(st,sender,buf,ICMP_TYPE_TIME_EXCEEDED,
+                           ICMP_CODE_TTL_EXCEEDED,icmp_noinfo);
+       BUF_FREE(buf);
+       return;
+    }
+    iph->ttl--;
+    iph->check=0;
+    iph->check=ip_fast_csum((uint8_t *)iph,iph->ihl);
+
+    netlink_packet_deliver(st,sender,buf);
+    BUF_ASSERT_FREE(buf);
+}
+
+/* Deal with packets addressed explicitly to us */
+static void netlink_packet_local(struct netlink *st,
+                                struct netlink_client *sender,
+                                struct buffer_if *buf)
+{
+    struct icmphdr *h;
+
+    st->localcount++;
+
+    if (buf->size < (int)sizeof(struct icmphdr)) {
+       Message(M_WARNING,"%s: short packet addressed to secnet; "
+               "ignoring it\n",st->name);
+       BUF_FREE(buf);
+       return;
+    }
+    h=(struct icmphdr *)buf->start;
+
+    unsigned fraginfo = ntohs(h->iph.frag);
+    if ((fraginfo&(IPHDR_FRAG_OFF|IPHDR_FRAG_MORE))!=0) {
+       if (!(fraginfo & IPHDR_FRAG_OFF))
+           /* report only for first fragment */
+           Message(M_WARNING,"%s: fragmented packet addressed to secnet; "
+                   "ignoring it\n",st->name);
+       BUF_FREE(buf);
+       return;
+    }
+
+    if (h->iph.protocol==1) {
+       /* It's ICMP */
+       if (h->type==ICMP_TYPE_ECHO_REQUEST && h->code==0) {
+           /* ICMP echo-request. Special case: we re-use the buffer
+              to construct the reply. */
+           h->type=ICMP_TYPE_ECHO_REPLY;
+           h->iph.daddr=h->iph.saddr;
+           h->iph.saddr=htonl(st->secnet_address);
+           h->iph.ttl=255;
+           h->iph.check=0;
+           h->iph.check=ip_fast_csum((uint8_t *)h,h->iph.ihl);
+           netlink_icmp_csum(h);
+           netlink_packet_deliver(st,NULL,buf);
+           return;
+       }
+       Message(M_WARNING,"%s: unknown incoming ICMP\n",st->name);
+    } else {
+       /* Send ICMP protocol unreachable */
+       netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE,
+                           ICMP_CODE_PROTOCOL_UNREACHABLE,icmp_noinfo);
+       BUF_FREE(buf);
+       return;
+    }
+
+    BUF_FREE(buf);
+}
+
+/* If cid==NULL packet is from host, otherwise cid specifies which tunnel 
+   it came from. */
+static void netlink_incoming(struct netlink *st, struct netlink_client *sender,
+                            struct buffer_if *buf)
+{
+    uint32_t source,dest;
+    struct iphdr *iph;
+    char errmsgbuf[50];
+    const char *sourcedesc=sender?sender->name:"host";
+
+    BUF_ASSERT_USED(buf);
+
+    if (!netlink_check(st,buf,errmsgbuf,sizeof(errmsgbuf))) {
+       Message(M_WARNING,"%s: bad IP packet from %s: %s\n",
+               st->name,sourcedesc,
+               errmsgbuf);
+       BUF_FREE(buf);
+       return;
+    }
+    assert(buf->size >= (int)sizeof(struct iphdr));
+    iph=(struct iphdr *)buf->start;
+
+    source=ntohl(iph->saddr);
+    dest=ntohl(iph->daddr);
+
+    /* Check source. If we don't like the source, there's no point
+       generating ICMP because we won't know how to get it to the
+       source of the packet. */
+    if (sender) {
+       /* Check that the packet source is appropriate for the tunnel
+          it came down */
+       if (!ipset_contains_addr(sender->networks,source)) {
+           string_t s,d;
+           s=ipaddr_to_string(source);
+           d=ipaddr_to_string(dest);
+           Message(M_WARNING,"%s: packet from tunnel %s with bad "
+                   "source address (s=%s,d=%s)\n",st->name,sender->name,s,d);
+           BUF_FREE(buf);
+           return;
+       }
+    } else {
+       /* Check that the packet originates in our configured local
+          network, and hasn't been forwarded from elsewhere or
+          generated with the wrong source address */
+       if (!ipset_contains_addr(st->networks,source)) {
+           string_t s,d;
+           s=ipaddr_to_string(source);
+           d=ipaddr_to_string(dest);
+           Message(M_WARNING,"%s: outgoing packet with bad source address "
+                   "(s=%s,d=%s)\n",st->name,s,d);
+           BUF_FREE(buf);
+           return;
+       }
+    }
+
+    /* If this is a point-to-point device we don't examine the
+       destination address at all; we blindly send it down our
+       one-and-only registered tunnel, or to the host, depending on
+       where it came from.  It's up to external software to check
+       address validity and generate ICMP, etc. */
+    if (st->ptp) {
+       if (sender) {
+           netlink_host_deliver(st,sender,source,dest,buf);
+       } else {
+           netlink_client_deliver(st,st->clients,source,dest,buf);
+       }
+       BUF_ASSERT_FREE(buf);
+       return;
+    }
+
+    /* st->secnet_address needs checking before matching destination
+       addresses */
+    if (dest==st->secnet_address) {
+       netlink_packet_local(st,sender,buf);
+       BUF_ASSERT_FREE(buf);
+       return;
+    }
+    netlink_packet_forward(st,sender,buf);
+    BUF_ASSERT_FREE(buf);
+}
+
+static void netlink_inst_incoming(void *sst, struct buffer_if *buf)
+{
+    struct netlink_client *c=sst;
+    struct netlink *st=c->nst;
+
+    netlink_incoming(st,c,buf);
+}
+
+static void netlink_dev_incoming(void *sst, struct buffer_if *buf)
+{
+    struct netlink *st=sst;
+
+    netlink_incoming(st,NULL,buf);
+}
+
+static void netlink_set_quality(void *sst, uint32_t quality)
+{
+    struct netlink_client *c=sst;
+    struct netlink *st=c->nst;
+
+    c->link_quality=quality;
+    c->up=(c->link_quality==LINK_QUALITY_DOWN)?False:True;
+    if (c->options&OPT_SOFTROUTE) {
+       st->set_routes(st->dst,c);
+    }
+}
+
+static void netlink_output_subnets(struct netlink *st, uint32_t loglevel,
+                                  struct subnet_list *snets)
+{
+    int32_t i;
+    string_t net;
+
+    for (i=0; i<snets->entries; i++) {
+       net=subnet_to_string(snets->list[i]);
+       Message(loglevel,"%s ",net);
+    }
+}
+
+static void netlink_dump_routes(struct netlink *st, bool_t requested)
+{
+    int i;
+    string_t net;
+    uint32_t c=M_INFO;
+
+    if (requested) c=M_WARNING;
+    if (st->ptp) {
+       net=ipaddr_to_string(st->secnet_address);
+       Message(c,"%s: point-to-point (remote end is %s); routes: ",
+               st->name, net);
+       netlink_output_subnets(st,c,st->clients->subnets);
+       Message(c,"\n");
+    } else {
+       Message(c,"%s: routing table:\n",st->name);
+       for (i=0; i<st->n_clients; i++) {
+           netlink_output_subnets(st,c,st->routes[i]->subnets);
+           Message(c,"-> tunnel %s (%s,mtu %d,%s routes,%s,"
+                   "quality %d,use %d,pri %lu)\n",
+                   st->routes[i]->name,
+                   st->routes[i]->up?"up":"down",
+                   st->routes[i]->mtu,
+                   st->routes[i]->options&OPT_SOFTROUTE?"soft":"hard",
+                   st->routes[i]->options&OPT_ALLOWROUTE?"free":"restricted",
+                   st->routes[i]->link_quality,
+                   st->routes[i]->outcount,
+                   (unsigned long)st->routes[i]->priority);
+       }
+       net=ipaddr_to_string(st->secnet_address);
+       Message(c,"%s/32 -> netlink \"%s\" (use %d)\n",
+               net,st->name,st->localcount);
+       for (i=0; i<st->subnets->entries; i++) {
+           net=subnet_to_string(st->subnets->list[i]);
+           Message(c,"%s ",net);
+       }
+       if (i>0)
+           Message(c,"-> host (use %d)\n",st->outcount);
+    }
+}
+
+/* ap is a pointer to a member of the routes array */
+static int netlink_compare_client_priority(const void *ap, const void *bp)
+{
+    const struct netlink_client *const*a=ap;
+    const struct netlink_client *const*b=bp;
+
+    if ((*a)->priority==(*b)->priority) return 0;
+    if ((*a)->priority<(*b)->priority) return 1;
+    return -1;
+}
+
+static void netlink_phase_hook(void *sst, uint32_t new_phase)
+{
+    struct netlink *st=sst;
+    struct netlink_client *c;
+    int32_t i;
+
+    /* All the networks serviced by the various tunnels should now
+     * have been registered.  We build a routing table by sorting the
+     * clients by priority.  */
+    NEW_ARY(st->routes,st->n_clients);
+    /* Fill the table */
+    i=0;
+    for (c=st->clients; c; c=c->next) {
+       assert(i<INT_MAX);
+       st->routes[i++]=c;
+    }
+    /* Sort the table in descending order of priority */
+    qsort(st->routes,st->n_clients,sizeof(*st->routes),
+         netlink_compare_client_priority);
+
+    netlink_dump_routes(st,False);
+}
+
+static void netlink_signal_handler(void *sst, int signum)
+{
+    struct netlink *st=sst;
+    Message(M_INFO,"%s: route dump requested by SIGUSR1\n",st->name);
+    netlink_dump_routes(st,True);
+}
+
+static void netlink_inst_set_mtu(void *sst, int32_t new_mtu)
+{
+    struct netlink_client *c=sst;
+
+    c->mtu=new_mtu;
+}
+
+static void netlink_inst_reg(void *sst, netlink_deliver_fn *deliver, 
+                            void *dst, uint32_t *localmtu_r)
+{
+    struct netlink_client *c=sst;
+    struct netlink *st=c->nst;
+
+    c->deliver=deliver;
+    c->dst=dst;
+
+    if (localmtu_r)
+       *localmtu_r=st->mtu;
+}
+
+static struct flagstr netlink_option_table[]={
+    { "soft", OPT_SOFTROUTE },
+    { "allow-route", OPT_ALLOWROUTE },
+    { NULL, 0}
+};
+/* This is the routine that gets called when the closure that's
+   returned by an invocation of a netlink device closure (eg. tun,
+   userv-ipif) is invoked.  It's used to create routes and pass in
+   information about them; the closure it returns is used by site
+   code.  */
+static closure_t *netlink_inst_create(struct netlink *st,
+                                     struct cloc loc, dict_t *dict)
+{
+    struct netlink_client *c;
+    string_t name;
+    struct ipset *networks;
+    uint32_t options,priority;
+    int32_t mtu;
+    list_t *l;
+
+    name=dict_read_string(dict, "name", True, st->name, loc);
+
+    l=dict_lookup(dict,"routes");
+    if (!l)
+       cfgfatal(loc,st->name,"required parameter \"routes\" not found\n");
+    networks=string_list_to_ipset(l,loc,st->name,"routes");
+    options=string_list_to_word(dict_lookup(dict,"options"),
+                               netlink_option_table,st->name);
+
+    priority=dict_read_number(dict,"priority",False,st->name,loc,0);
+    mtu=dict_read_number(dict,"mtu",False,st->name,loc,0);
+
+    if ((options&OPT_SOFTROUTE) && !st->set_routes) {
+       cfgfatal(loc,st->name,"this netlink device does not support "
+                "soft routes.\n");
+       return NULL;
+    }
+
+    if (options&OPT_SOFTROUTE) {
+       /* XXX for now we assume that soft routes require root privilege;
+          this may not always be true. The device driver can tell us. */
+       require_root_privileges=True;
+       require_root_privileges_explanation="netlink: soft routes";
+       if (st->ptp) {
+           cfgfatal(loc,st->name,"point-to-point netlinks do not support "
+                    "soft routes.\n");
+           return NULL;
+       }
+    }
+
+    /* Check that nets are a subset of st->remote_networks;
+       refuse to register if they are not. */
+    if (!ipset_is_subset(st->remote_networks,networks)) {
+       cfgfatal(loc,st->name,"routes are not allowed\n");
+       return NULL;
+    }
+
+    NEW(c);
+    c->cl.description=name;
+    c->cl.type=CL_NETLINK;
+    c->cl.apply=NULL;
+    c->cl.interface=&c->ops;
+    c->ops.st=c;
+    c->ops.reg=netlink_inst_reg;
+    c->ops.deliver=netlink_inst_incoming;
+    c->ops.set_quality=netlink_set_quality;
+    c->ops.set_mtu=netlink_inst_set_mtu;
+    c->nst=st;
+
+    c->networks=networks;
+    c->subnets=ipset_to_subnet_list(networks);
+    c->priority=priority;
+    c->deliver=NULL;
+    c->dst=NULL;
+    c->name=name;
+    c->link_quality=LINK_QUALITY_UNUSED;
+    c->mtu=mtu?mtu:st->mtu;
+    c->options=options;
+    c->outcount=0;
+    c->up=False;
+    c->kup=False;
+    c->next=st->clients;
+    st->clients=c;
+    assert(st->n_clients < INT_MAX);
+    st->n_clients++;
+
+    return &c->cl;
+}
+
+static list_t *netlink_inst_apply(closure_t *self, struct cloc loc,
+                                 dict_t *context, list_t *args)
+{
+    struct netlink *st=self->interface;
+
+    dict_t *dict;
+    item_t *item;
+    closure_t *cl;
+
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict) {
+       cfgfatal(loc,st->name,"must have a dictionary argument\n");
+    }
+    dict=item->data.dict;
+
+    cl=netlink_inst_create(st,loc,dict);
+
+    return new_closure(cl);
+}
+
+netlink_deliver_fn *netlink_init(struct netlink *st,
+                                void *dst, struct cloc loc,
+                                dict_t *dict, cstring_t description,
+                                netlink_route_fn *set_routes,
+                                netlink_deliver_fn *to_host)
+{
+    item_t *sa, *ptpa;
+    list_t *l;
+
+    st->dst=dst;
+    st->cl.description=description;
+    st->cl.type=CL_PURE;
+    st->cl.apply=netlink_inst_apply;
+    st->cl.interface=st;
+    st->clients=NULL;
+    st->routes=NULL;
+    st->n_clients=0;
+    st->set_routes=set_routes;
+    st->deliver_to_host=to_host;
+
+    st->name=dict_read_string(dict,"name",False,description,loc);
+    if (!st->name) st->name=description;
+    l=dict_lookup(dict,"networks");
+    if (l) 
+       st->networks=string_list_to_ipset(l,loc,st->name,"networks");
+    else {
+       struct ipset *empty;
+       empty=ipset_new();
+       st->networks=ipset_complement(empty);
+       ipset_free(empty);
+    }
+    l=dict_lookup(dict,"remote-networks");
+    if (l) {
+       st->remote_networks=string_list_to_ipset(l,loc,st->name,
+                                                "remote-networks");
+    } else {
+       struct ipset *empty;
+       empty=ipset_new();
+       st->remote_networks=ipset_complement(empty);
+       ipset_free(empty);
+    }
+    st->local_address=string_item_to_ipaddr(
+       dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
+
+    sa=dict_find_item(dict,"secnet-address",False,"netlink",loc);
+    ptpa=dict_find_item(dict,"ptp-address",False,"netlink",loc);
+    if (sa && ptpa) {
+       cfgfatal(loc,st->name,"you may not specify secnet-address and "
+                "ptp-address in the same netlink device\n");
+    }
+    if (!(sa || ptpa)) {
+       cfgfatal(loc,st->name,"you must specify secnet-address or "
+                "ptp-address for this netlink device\n");
+    }
+    if (sa) {
+       st->secnet_address=string_item_to_ipaddr(sa,"netlink");
+       st->ptp=False;
+    } else {
+       st->secnet_address=string_item_to_ipaddr(ptpa,"netlink");
+       st->ptp=True;
+    }
+    /* To be strictly correct we could subtract secnet_address from
+       networks here.  It shouldn't make any practical difference,
+       though, and will make the route dump look complicated... */
+    st->subnets=ipset_to_subnet_list(st->networks);
+    st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
+    buffer_new(&st->icmp,MAX(ICMP_BUFSIZE,st->mtu));
+    st->outcount=0;
+    st->localcount=0;
+
+    add_hook(PHASE_SETUP,netlink_phase_hook,st);
+    request_signal_notification(SIGUSR1, netlink_signal_handler, st);
+
+    /* If we're point-to-point then we return a CL_NETLINK directly,
+       rather than a CL_NETLINK_OLD or pure closure (depending on
+       compatibility).  This CL_NETLINK is for our one and only
+       client.  Our cl.apply function is NULL. */
+    if (st->ptp) {
+       closure_t *cl;
+       cl=netlink_inst_create(st,loc,dict);
+       st->cl=*cl;
+    }
+    return netlink_dev_incoming;
+}
+
+/* No connection to the kernel at all... */
+
+struct null {
+    struct netlink nl;
+};
+
+static bool_t null_set_route(void *sst, struct netlink_client *routes)
+{
+    struct null *st=sst;
+
+    if (routes->up!=routes->kup) {
+       Message(M_INFO,"%s: setting routes for tunnel %s to state %s\n",
+               st->nl.name,routes->name,
+               routes->up?"up":"down");
+       routes->kup=routes->up;
+       return True;
+    }
+    return False;
+}
+           
+static void null_deliver(void *sst, struct buffer_if *buf)
+{
+    return;
+}
+
+static list_t *null_apply(closure_t *self, struct cloc loc, dict_t *context,
+                         list_t *args)
+{
+    struct null *st;
+    item_t *item;
+    dict_t *dict;
+
+    NEW(st);
+
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"null-netlink","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+
+    netlink_init(&st->nl,st,loc,dict,"null-netlink",null_set_route,
+                null_deliver);
+
+    return new_closure(&st->nl.cl);
+}
+
+void netlink_module(dict_t *dict)
+{
+    add_closure(dict,"null-netlink",null_apply);
+}
diff --git a/netlink.h b/netlink.h
new file mode 100644 (file)
index 0000000..93b556d
--- /dev/null
+++ b/netlink.h
@@ -0,0 +1,91 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef netlink_h
+#define netlink_h
+
+#include "ipaddr.h"
+
+#define DEFAULT_BUFSIZE 2048
+#define DEFAULT_MTU 1000
+#define ICMP_BUFSIZE 1024
+
+struct netlink;
+
+struct netlink_client {
+    closure_t cl;
+    struct netlink_if ops;
+    struct netlink *nst;
+    struct ipset *networks;
+    struct subnet_list *subnets; /* Same information as 'networks' */
+    uint32_t priority; /* Higher priority clients have their networks
+                         checked first during routing.  This allows
+                         things like laptops to supersede whole
+                         networks. */
+    netlink_deliver_fn *deliver;
+    void *dst;
+    string_t name;
+    uint32_t link_quality;
+    int32_t mtu;
+    uint32_t options;
+    uint32_t outcount;
+    bool_t up; /* Should these routes exist in the kernel? */
+    bool_t kup; /* Do these routes exist in the kernel? */
+    struct netlink_client *next;
+};
+
+/* options field in 'struct netlink_client' */
+#define OPT_SOFTROUTE   1
+#define OPT_ALLOWROUTE  2
+
+typedef bool_t netlink_route_fn(void *cst, struct netlink_client *routes);
+
+/* Netlink provides one function to the device driver, to call to deliver
+   a packet from the device. The device driver provides one function to
+   netlink, for it to call to deliver a packet to the device. */
+
+struct netlink {
+    closure_t cl;
+    void *dst; /* Pointer to host interface state */
+    cstring_t name;
+    struct ipset *networks; /* Local networks */
+    struct subnet_list *subnets; /* Same as networks, for display */
+    struct ipset *remote_networks; /* Allowable remote networks */
+    uint32_t local_address; /* host interface address */
+    uint32_t secnet_address; /* our own address, or the address of the
+                               other end of a point-to-point link */
+    bool_t ptp;
+    int32_t mtu;
+    struct netlink_client *clients; /* Linked list of clients */
+    struct netlink_client **routes; /* Array of clients, sorted by priority */
+    int32_t n_clients;
+    netlink_deliver_fn *deliver_to_host; /* Provided by driver */
+    netlink_route_fn *set_routes; /* Provided by driver */
+    struct buffer_if icmp; /* Buffer for assembly of outgoing ICMP */
+    uint32_t outcount; /* Packets sent to host */
+    uint32_t localcount; /* Packets sent to secnet */
+};
+
+extern netlink_deliver_fn *netlink_init(struct netlink *st,
+                                       void *dst, struct cloc loc,
+                                       dict_t *dict, cstring_t description,
+                                       netlink_route_fn *set_routes,
+                                       netlink_deliver_fn *to_host);
+
+#endif /* netlink_h */
diff --git a/polypath-interface-monitor-linux b/polypath-interface-monitor-linux
new file mode 100755 (executable)
index 0000000..3ecae59
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/perl -w
+
+# This file is part of secnet.
+# See README for full list of copyright holders.
+#
+# secnet is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# secnet 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.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# version 3 along with secnet; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+
+use strict;
+use IO::Handle;
+
+my $us = $0;
+$us =~ s{.*/}{};
+
+open DEBUG, ">/dev/null" or die $!;
+
+if (@ARGV && $ARGV[0] eq '-D') {
+    shift @ARGV;
+    open DEBUG, ">&STDERR" or die $!;
+}
+
+die "$us: no arguments permitted\n" if @ARGV;
+
+our ($monh,$monchild);
+
+our %reported;
+#  no entry: not reported, does not exist
+#  /ry+/: reported, entry exists
+# during processing only:
+#  /r/: reported, may not still exist
+#  /y+/: not reported, entry exists
+
+sub killmonitor () {
+    return unless $monchild;
+    kill 9, $monchild
+       or warn "$us: cannot kill monitor child [$monchild]: $!\n";
+    $monchild=undef;
+    close $monh;
+}
+
+END { killmonitor(); }
+
+my $restart;
+
+for (;;) {
+    my $o;
+    eval {
+       if (!$monh) {
+           killmonitor();
+           $monh = new IO::File;
+           $monchild = open $monh, "-|", qw(ip -o monitor addr)
+               or die "spawn monitor: $!\n";
+           sleep(1) if $restart++;
+       } else {
+           my $discard;
+           my $got = sysread $monh, $discard, 4096;
+           die "read monitor: $!\n" unless defined $got;
+           die "monitor failed\n" unless $got;
+       }
+       $_='r' foreach values %reported;
+       print DEBUG "#########################################\n";
+       foreach my $ip (qw(4 6)) {
+           print DEBUG "###### $ip:\n";
+           my $addrh = new IO::File;
+           open $addrh, "-|", qw(ip -o), "-$ip", qw(addr show)
+               or die "spawn addr $ip show: $!\n";
+           my $afstr = $ip==4 ? 'inet' : $ip==6 ? 'inet6' : die;
+           while (<$addrh>) {
+               print DEBUG "#$_";
+               if (m{^\d+\:\s*(\S+)\s+$afstr\s+([0-9a-z.:]+)(?:/\d+)?\s}) {
+                   my $rhs=$'; #';
+                   my $outline = "$ip $1 $2";
+                   # "ip -o addr show" has a ridiculous output format.  In
+                   # particular, it mixes output keywords which introduce
+                   # values with ones which don't, and there seems to be
+                   # no way to tell without knowing all the possible
+                   # keywords.  We hope that before the \ there is nothing
+                   # which contains arbitrary text (specifically, which
+                   # might be `tentative' other than to specify IPv6
+                   # tentativeness).  We have to do this for IPv6 only
+                   # because in the IPv4 output, the interface name
+                   # appears here!
+                   next if $ip==6 && $rhs=~m{[^\\]* tentative\s};
+                   $reported{$outline} .= "y";
+               } else {
+                   chomp;
+                   warn "unexpected output from addr $ip show: $_\n";
+               }
+           }
+           my $r = close $addrh;
+           die "addr $ip show failed $!\n" unless $r;
+           $o = '';
+       }
+       foreach my $k (keys %reported) {
+           local $_ = $reported{$k};
+           if (m/^r$/) {
+               $o .= "-$k\n";
+               delete $reported{$k};
+           } elsif (m/^y/) {
+               $o .= "+$k\n";
+           }
+       }
+    };
+    if ($@) {
+       print STDERR "$us: $@";
+       sleep 5;
+       next;
+    }
+    print $o or die $!;
+    STDOUT->flush or die $!;
+}
diff --git a/polypath.c b/polypath.c
new file mode 100644 (file)
index 0000000..64284a3
--- /dev/null
@@ -0,0 +1,938 @@
+/* polypath
+ * send/receive module for secnet
+ * for multi-route setups */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include "util.h"
+#include "unaligned.h"
+#include "comm-common.h"
+
+#include <adns.h>
+#include <ctype.h>
+#include <limits.h>
+
+#ifdef CONFIG_IPV6
+
+static comm_sendmsg_fn polypath_sendmsg;
+
+struct interf {
+    char *name; /* from malloc */
+    struct udpsocks socks;
+    bool_t experienced_xmit_noaf[MAX_AF+1];
+    LIST_ENTRY(interf) entry;
+};
+
+struct polypath {
+    struct udpcommon uc;
+    int max_interfs;
+    const char *const *ifname_pats;
+    const char *const *monitor_command;
+    bool_t permit_loopback;
+    LIST_HEAD(interf_list, interf) interfs_general;
+    struct interf_list interfs_dedicated;
+    struct buffer_if lbuf;
+    int monitor_fd;
+    pid_t monitor_pid;
+    int privsep_incoming_fd;
+    int privsep_ipcsock_fd;
+};
+
+struct comm_clientinfo {
+    union iaddr dedicated; /* might be AF_UNSPEC */
+};
+
+static void polypath_phase_shutdown(void *sst, uint32_t newphase);
+
+#define LG 0, st->uc.cc.cl.description, &st->uc.cc.loc
+
+static const char *const default_loopback_ifname_pats[] = {
+    "!lo", 0
+};
+static const char *const default_ifname_pats[] = {
+    "!tun*","!tap*","!sl*","!userv*", "@hippo*", "*", 0
+};
+
+static const char *const default_monitor_command[] = {
+#if __linux__
+    DATAROOTDIR "/secnet/" "polypath-interface-monitor-linux", 0
+#else
+    0
+#endif
+};
+
+static const char *polypath_addr_to_string(void *commst,
+                                          const struct comm_addr *ca)
+{
+    static char sbuf[100];
+
+    snprintf(sbuf, sizeof(sbuf), "polypath:%s",
+            iaddr_to_string(&ca->ia));
+    return sbuf;
+}
+
+static bool_t ifname_search_pats(struct polypath *st, struct cloc loc,
+                                const char *ifname, char *want_io,
+                                const char *const *pats) {
+    /* Returns True iff we found a list entry, in which case *want_io
+     * is set to the sense of that entry.  Otherwise *want_io is set
+     * to the sense of the last entry, or unchanged if there were no pats. */
+    if (!pats)
+       return False;
+    const char *const *pati;
+    for (pati=pats; *pati; pati++) {
+       const char *pat=*pati;
+       if (*pat=='!' || *pat=='+' || *pat=='@') { *want_io=*pat; pat++; }
+       else if (*pat=='*' || isalnum((unsigned char)*pat)) { *want_io='+'; }
+       else cfgfatal(loc,"polypath","invalid interface name pattern `%s'",pat);
+       int match=fnmatch(pat,ifname,0);
+       if (match==0) return True;
+       if (match!=FNM_NOMATCH)
+           cfgfatal(loc,"polypath","fnmatch failed! (pattern `%s')",pat);
+    }
+    return False;
+}
+
+static char ifname_wanted(struct polypath *st, struct cloc loc,
+                         const char *ifname) {
+    char want='!'; /* pretend an empty cfg ends with !<doesn'tmatch> */
+    if (ifname_search_pats(st,loc,ifname,&want, st->ifname_pats))
+       return want;
+    if (want!='!') /* last pattern was positive, do not search default */
+       return '!';
+    if (!st->permit_loopback &&
+       ifname_search_pats(st,loc,ifname,&want, default_loopback_ifname_pats))
+       return want;
+    if (ifname_search_pats(st,loc,ifname,&want, default_ifname_pats))
+       return want;
+    abort();
+}
+
+static struct comm_clientinfo *polypath_clientinfo(void *state,
+                                    dict_t *dict, struct cloc cloc) {
+    struct comm_clientinfo *clientinfo;
+
+    NEW(clientinfo);
+    FILLZERO(*clientinfo);
+    clientinfo->dedicated.sa.sa_family=AF_UNSPEC;
+
+    item_t *item = dict_find_item(dict,"dedicated-interface-addr",
+                                 False,"polypath",cloc);
+    if (item) string_item_to_iaddr(item,0,&clientinfo->dedicated,
+                                  "polypath");
+    return clientinfo;
+}
+    
+static int polypath_beforepoll(void *state, struct pollfd *fds, int *nfds_io,
+                              int *timeout_io)
+{
+    struct polypath *st=state;
+    BEFOREPOLL_WANT_FDS(1);
+    fds[0].fd=st->monitor_fd;
+    fds[0].events=POLLIN;
+    return 0;
+}
+
+static inline bool_t matches32(uint32_t word, uint32_t prefix, int prefixlen)
+{
+    assert(prefixlen>0);
+    assert(prefixlen<=32);
+    uint32_t mask = ~(((uint32_t)1 << (32-prefixlen)) - 1);
+    assert(!(prefix & ~mask));
+    return (word & mask) == prefix;
+}
+
+/* These macros expect
+ *    bad_fn_type *const bad;
+ *    void *badctx;
+ * and
+ *   out:
+ */
+#define BAD(m)     do{ bad(st,badctx,M_WARNING,m,0);  goto out; }while(0)
+#define BADE(m,ev) do{ bad(st,badctx,M_WARNING,m,ev); goto out; }while(0)
+typedef void bad_fn_type(struct polypath *st, void *badctx,
+                        int mclass, const char* m, int ev);
+
+typedef void polypath_ppml_callback_type(struct polypath *st,
+          bad_fn_type *bad, void *badctx,
+          bool_t add, char want,
+          const char *ifname, const char *ifaddr,
+          const union iaddr *ia, int fd /* -1 if none yet */);
+
+struct ppml_bad_ctx {
+    const char *orgl;
+    char *undospace;
+};
+
+static void ppml_bad(struct polypath *st, void *badctx,
+                    int mclass, const char *m, int ev)
+{
+    struct ppml_bad_ctx *bc=badctx;
+    if (bc->undospace)
+       *(bc->undospace)=' ';
+    lg_perror(LG,mclass,ev,
+             "error processing polypath state change: %s"
+             " (while processing `%s')",
+             m,bc->orgl);
+}
+
+static void polypath_process_monitor_line(struct polypath *st, char *orgl,
+                                      polypath_ppml_callback_type *callback)
+    /* always calls callback with fd==-1 */
+{
+    struct udpcommon *uc=&st->uc;
+    char *l=orgl;
+    bad_fn_type (*const bad)=ppml_bad;
+    struct ppml_bad_ctx badctx[1]={{
+           .orgl=orgl,
+           .undospace=0
+       }};
+
+    bool_t add;
+    int c=*l++;
+    if (c=='+') add=True;
+    else if (c=='-') add=False;
+    else BAD("bad +/-");
+
+    int proto;
+    c=*l++;
+    if (c=='4') proto=AF_INET;
+    else if (c=='6') proto=AF_INET6;
+    else BAD("bad proto");
+
+    char *space=strchr(l,' ');
+    if (!space) BAD("no first space");
+    const char *ifname=space+1;
+
+    space=strchr(ifname,' ');
+    if (!space) BAD("no second space");
+    const char *ifaddr=space+1;
+    *space=0;
+    badctx->undospace=space;
+
+    union iaddr ia;
+    FILLZERO(ia);
+    socklen_t salen=sizeof(ia);
+    int r=adns_text2addr(ifaddr,uc->port, adns_qf_addrlit_ipv4_quadonly,
+                        &ia.sa, &salen);
+    assert(r!=ENOSPC);
+    if (r) BADE("adns_text2addr",r);
+    if (ia.sa.sa_family!=proto) BAD("address family mismatch");
+
+#define DONT(m) do{                                                    \
+       if (add)                                                        \
+           lg_perror(LG,M_INFO,0,"ignoring %s [%s]: %s",ifname,ifaddr,m); \
+       goto out;                                                       \
+    }while(0)
+
+    char want=ifname_wanted(st,st->uc.cc.loc,ifname);
+    if (want=='!') DONT("unwanted interface name");
+
+    switch (ia.sa.sa_family) {
+    case AF_INET6: {
+       const struct in6_addr *i6=&ia.sin6.sin6_addr;
+#define DONTKIND(X,m) \
+       if (IN6_IS_ADDR_##X(i6)) DONT("IPv6 address is " m)
+       DONTKIND(UNSPECIFIED, "unspecified");
+       DONTKIND(MULTICAST  , "multicast"  );
+       DONTKIND(LINKLOCAL  , "link local" );
+       DONTKIND(SITELOCAL  , "site local" );
+       DONTKIND(V4MAPPED   , "v4-mapped"  );
+       if (!st->permit_loopback)
+           DONTKIND(LOOPBACK   , "loopback"   );
+#undef DONTKIND
+#define DONTMASK(w7x,w6x,prefixlen,m)                                  \
+       if (matches32(get_uint32(i6->s6_addr),                          \
+                      ((uint32_t)0x##w7x << 16) | (uint32_t)0x##w6x,   \
+                      prefixlen))                                      \
+           DONT("IPv6 address is " m)
+        DONTMASK( 100,   0,  8, "Discard-Only (RFC6666)");
+       DONTMASK(2001,   0, 23, "in IETF protocol block (RFC2928)");
+       DONTMASK(fc00,   0,  7, "Uniqe Local unicast (RFC4193)");
+#undef DONTMASK
+       break;
+    }
+    case AF_INET: {
+       const uint32_t i4=htonl(ia.sin.sin_addr.s_addr);
+       if (i4==INADDR_ANY) DONT("IPv4 address is any/unspecified");
+       if (i4==INADDR_BROADCAST) DONT("IPv4 address is all hosts broadcast");
+#define DONTMASK(b3,b2,b1,b0,prefixlen,m) do{                          \
+           const uint8_t prefixbytes[4] = { (b3),(b2),(b1),(b0) };     \
+           if (matches32(i4,get_uint32(prefixbytes),prefixlen))        \
+               DONT("IPv4 address is " m);                             \
+       }while(0)
+       DONTMASK(169,254,0,0, 16, "link local");
+       DONTMASK(224,  0,0,0,  4, "multicast");
+       DONTMASK(192,  0,0,0, 24, "in IETF protocol block (RFC6890)");
+       DONTMASK(240,  0,0,0,  4, "in reserved addressing block (RFC1112)");
+       if (!st->permit_loopback)
+           DONTMASK(127,  0,0,0,  8, "loopback");
+#undef DONTMASK
+       break;
+    }
+    default:
+       abort();
+    }
+
+#undef DONT
+
+    /* OK, process it */
+    callback(st, bad,badctx, add,want, ifname,ifaddr,&ia,-1);
+
+ out:;
+}
+
+static void dump_pria(struct polypath *st, struct interf_list *interfs,
+                     const char *ifname, char want)
+{
+#ifdef POLYPATH_DEBUG
+    struct interf *interf;
+    if (ifname)
+       lg_perror(LG,M_DEBUG,0, "polypath record ifaddr `%s' (%c)",
+                 ifname, want);
+    LIST_FOREACH(interf, interfs, entry) {
+       lg_perror(LG,M_DEBUG,0, "  polypath interface `%s', nsocks=%d",
+                 interf->name, interf->socks.n_socks);
+       int i;
+       for (i=0; i<interf->socks.n_socks; i++) {
+           struct udpsock *us=&interf->socks.socks[i];
+           lg_perror(LG,M_DEBUG,0, "    polypath sock fd=%d addr=%s",
+                     us->fd, iaddr_to_string(&us->addr));
+       }
+    }
+#endif
+}
+
+static bool_t polypath_make_socket(struct polypath *st,
+                                  bad_fn_type *bad, void *badctx,
+                                  struct udpsock *us, const char *ifname)
+    /* on error exit has called bad; might leave us->fd as -1 */
+{
+    assert(us->fd==-1);
+
+    bool_t ok=udp_make_socket(&st->uc,us,M_WARNING);
+    if (!ok) BAD("unable to set up socket");
+    int r=setsockopt(us->fd,SOL_SOCKET,SO_BINDTODEVICE,
+                    ifname,strlen(ifname)+1);
+    if (r) BADE("setsockopt(,,SO_BINDTODEVICE,)",errno);
+    return True;
+
+ out:
+    return False;
+}
+
+static void polypath_record_ifaddr(struct polypath *st,
+                                  bad_fn_type *bad, void *badctx,
+                                  bool_t add, char want,
+                                  const char *ifname,
+                                  const char *ifaddr,
+                                  const union iaddr *ia, int fd)
+{
+    struct udpcommon *uc=&st->uc;
+    struct interf *interf=0;
+    int max_interfs;
+    struct udpsock *us=0;
+
+    struct interf_list *interfs;
+    switch (want) {
+    case '+': interfs=&st->interfs_general; max_interfs=st->max_interfs; break;
+    case '@': interfs=&st->interfs_dedicated; max_interfs=INT_MAX; break;
+    default:   fatal("polypath: got bad want (%#x, %s)", want, ifname);
+    }
+
+    dump_pria(st,interfs,ifname,want);
+
+    int n_ifs=0;
+    LIST_FOREACH(interf,interfs,entry) {
+       if (!strcmp(interf->name,ifname))
+           goto found_interf;
+       n_ifs++;
+    }
+    /* not found */
+    if (n_ifs==max_interfs) BAD("too many interfaces");
+    interf=malloc(sizeof(*interf));
+    if (!interf) BADE("malloc for new interface",errno);
+    interf->name=0;
+    interf->socks.n_socks=0;
+    FILLZERO(interf->experienced_xmit_noaf);
+    LIST_INSERT_HEAD(interfs,interf,entry);
+    interf->name=strdup(ifname);
+    udp_socks_register(&st->uc,&interf->socks,interf->name);
+    if (!interf->name) BADE("strdup interface name",errno);
+ found_interf:
+
+    if (add) {
+       if (interf->socks.n_socks == UDP_MAX_SOCKETS)
+           BAD("too many addresses on this interface");
+       struct udpsock *us=&interf->socks.socks[interf->socks.n_socks];
+       us->fd=-1;
+       COPY_OBJ(us->addr,*ia);
+       if (fd<0) {
+           bool_t ok=polypath_make_socket(st,bad,badctx, us,ifname);
+           if (!ok) goto out;
+       } else {
+           bool_t ok=udp_import_socket(uc,us,M_WARNING,fd);
+           if (!ok) goto out;
+           fd=-1;
+       }
+       interf->socks.n_socks++;
+       lg_perror(LG,M_INFO,0,"using %s %s",ifname,
+                 iaddr_to_string(&us->addr));
+       us=0; /* do not destroy this socket during `out' */
+    } else {
+       int i;
+       for (i=0; i<interf->socks.n_socks; i++)
+           if (iaddr_equal(&interf->socks.socks[i].addr,ia,True))
+               goto address_remove_found;
+       bad(st,badctx,M_DEBUG,"address to remove not found",0);
+       goto out;
+    address_remove_found:
+       lg_perror(LG,M_INFO,0,"removed %s %s",ifname,
+                 iaddr_to_string(&interf->socks.socks[i].addr));
+       udp_destroy_socket(&st->uc,&interf->socks.socks[i]);
+       interf->socks.socks[i]=
+           interf->socks.socks[--interf->socks.n_socks];
+    }
+
+ out:
+    if (us)
+       udp_destroy_socket(uc,us);
+    if (fd>=0)
+       close(fd);
+    if (interf && !interf->socks.n_socks) {
+       udp_socks_deregister(&st->uc,&interf->socks);
+       LIST_REMOVE(interf,entry);
+       free(interf->name);
+       free(interf);
+    }
+
+    dump_pria(st,interfs,0,0);
+}
+
+static void subproc_problem(struct polypath *st,
+                           enum async_linebuf_result alr, const char *emsg)
+{
+    int status;
+    assert(st->monitor_pid);
+
+    pid_t gotpid=waitpid(st->monitor_pid,&status,WNOHANG);
+    if (gotpid==st->monitor_pid) {
+       st->monitor_pid=0;
+       lg_exitstatus(LG,M_FATAL,status,"interface monitor");
+    } else if (gotpid<0)
+       lg_perror(LG,M_ERR,errno,"unable to reap interface monitor");
+    else
+       assert(gotpid==0);
+
+    if (alr==async_linebuf_eof)
+       lg_perror(LG,M_FATAL,0,"unexpected EOF from interface monitor");
+    else
+       lg_perror(LG,M_FATAL,0,"bad output from interface monitor: %s",emsg);
+    assert(!"not reached");
+}
+
+/* Used in non-privsep case, and in privsep child */
+static void afterpoll_monitor(struct polypath *st, struct pollfd *fd,
+                             polypath_ppml_callback_type *callback)
+{
+    enum async_linebuf_result alr;
+    const char *emsg;
+    
+    while ((alr=async_linebuf_read(fd,&st->lbuf,&emsg)) == async_linebuf_ok)
+       polypath_process_monitor_line(st,st->lbuf.base,callback);
+
+    if (alr==async_linebuf_nothing)
+       return;
+
+    subproc_problem(st,alr,emsg);
+}
+
+/* Used in non-privsep case only - glue for secnet main loop */
+static void polypath_afterpoll_monitor(void *state, struct pollfd *fds,
+                                      int nfds)
+{
+    struct polypath *st=state;
+    if (nfds<1) return;
+    afterpoll_monitor(st,fds,polypath_record_ifaddr);
+}
+
+/* Actual udp packet sending work */
+
+static void polypath_sendmsg_interf(struct polypath *st,
+                                   struct interf *interf,
+                                   struct buffer_if *buf,
+                                   const struct comm_addr *dest,
+                                   const union iaddr *dedicated,
+                                   bool_t *allreasonable)
+{
+    int i;
+    int af=dest->ia.sa.sa_family;
+    bool_t wanted=False, attempted=False, reasonable=False;
+
+    for (i=0; i<interf->socks.n_socks; i++) {
+       struct udpsock *us=&interf->socks.socks[i];
+       if (dedicated && !iaddr_equal(dedicated, &us->addr, True))
+           continue;
+       wanted=True;
+       if (af != us->addr.sa.sa_family)
+           continue;
+       attempted=True;
+       int r=sendto(us->fd,buf->start,buf->size,
+                    0,&dest->ia.sa,iaddr_socklen(&dest->ia));
+       udp_sock_experienced(0,&st->uc,&interf->socks,us,
+                            &dest->ia,af, r,errno);
+       if (r>=0) {
+           reasonable=True;
+           break;
+       }
+       if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH))
+           reasonable=True;
+       lg_perror(LG,M_DEBUG,errno,"%s [%s] xmit %"PRIu32" bytes to %s",
+                 interf->name,iaddr_to_string(&us->addr),
+                 buf->size,iaddr_to_string(&dest->ia));
+    }
+    if (!wanted)
+       return;
+
+    if (!attempted)
+       if (!interf->experienced_xmit_noaf[af]++)
+           lg_perror(LG,M_WARNING,0,
+                     "%s has no suitable address to transmit %s",
+                     interf->name, af_name(af));
+
+    *allreasonable &= reasonable;
+}
+
+static bool_t polypath_sendmsg(void *commst, struct buffer_if *buf,
+                         const struct comm_addr *dest,
+                         struct comm_clientinfo *clientinfo)
+{
+    struct polypath *st=commst;
+    struct interf *interf;
+    bool_t allreasonable=True;
+
+    LIST_FOREACH(interf,&st->interfs_general,entry) {
+       polypath_sendmsg_interf(st,interf,buf,dest,
+                               0, &allreasonable);
+    }
+    if (clientinfo && clientinfo->dedicated.sa.sa_family != AF_UNSPEC) {
+       LIST_FOREACH(interf,&st->interfs_dedicated,entry) {
+           polypath_sendmsg_interf(st,interf,buf,dest,
+                                   &clientinfo->dedicated, &allreasonable);
+       }
+    }
+    return allreasonable;
+}
+
+/* Non-privsep: called in (sole) child.  Privsep: in grandchild. */
+static void child_monitor(struct polypath *st, int childfd)
+{
+    dup2(childfd,1);
+    execvp(st->monitor_command[0],(char**)st->monitor_command);
+    fprintf(stderr,"secnet: cannot execute %s: %s\n",
+           st->monitor_command[0], strerror(errno));
+    exit(-1);
+}
+
+/* General utility function. */
+static void start_subproc(struct polypath *st, void (*make_fdpair)(int[2]),
+                         void (*child)(struct polypath *st, int childfd),
+                         const char *desc)
+{
+    int pfds[2];
+
+    assert(!st->monitor_pid);
+    assert(st->monitor_fd<0);
+
+    make_fdpair(pfds);
+
+    pid_t pid=fork();
+    if (!pid) {
+       afterfork();
+       close(pfds[0]);
+       child(st,pfds[1]);
+       abort();
+    }
+    if (pid<0)
+       fatal_perror("%s: failed to fork for interface monitoring",
+                    st->uc.cc.cl.description);
+
+    close(pfds[1]);
+    st->monitor_pid=pid;
+    st->monitor_fd=pfds[0];
+    setnonblock(st->monitor_fd);
+
+    lg_perror(LG,M_NOTICE,0, "%s: spawning %s [pid %ld]",
+             st->uc.cc.cl.description, desc, (long)st->monitor_pid);
+}
+
+/* Non-privsep only: glue for forking the monitor, from the main loop */
+static void polypath_phase_startmonitor(void *sst, uint32_t newphase)
+{
+    struct polypath *st=sst;
+    start_subproc(st,pipe_cloexec,child_monitor,
+                 "interface monitor (no privsep)");
+    register_for_poll(st,polypath_beforepoll,
+                     polypath_afterpoll_monitor,"polypath");
+}
+
+/*----- Privsep-only: -----*/
+
+/*
+ * We use two subprocesses, a child and a grandchild.  These are
+ * forked before secnet drops privilege.
+ *
+ * The grandchild is the same interface monitor helper script as used
+ * in the non-privsep case.  But its lines are read by the child
+ * instead of by the main secnet.  The child is responsible for
+ * creating the actual socket (binding it, etc.).  Each socket is
+ * passed to secnet proper via fd passing, along with a data message
+ * describing the interface name and address.  The child does not
+ * retain a list of current interfaces and addresses - it trusts the
+ * interface monitor to get that right.  secnet proper still maintains
+ * that data structure.
+ *
+ * The result is that much of the non-privsep code can be reused, but
+ * just plumbed together differently.
+ *
+ * The child does not retain the socket after passing it on.
+ * Interface removals are handled similarly but without any fd.
+ *
+ * The result is that the configuration's limits on which interfaces
+ * and ports secnet may use are enforced by the privileged child.
+ */
+
+struct privsep_mdata {
+    bool_t add;
+    char ifname[100];
+    union iaddr ia;
+    char want; /* `+' or `@' */
+};
+
+static void papp_bad(struct polypath *st, void *badctx,
+                    int mclass, const char *m, int ev)
+{
+    const struct privsep_mdata *mdata=(const void*)st->lbuf.start;
+    const char *addr_str=badctx;
+
+    lg_perror(LG,mclass,ev,
+             "error processing polypath address change %s %s [%s]: %s",
+             mdata->add ? "+" : "-",
+             mdata->ifname, addr_str, m);
+}
+
+static void polypath_afterpoll_privsep(void *state, struct pollfd *fds,
+                                      int nfds)
+/* In secnet proper; receives messages from child. */
+{
+    struct polypath *st=state;
+
+    if (nfds<1) return;
+
+    int revents=fds[0].revents;
+
+    const char *badbit=pollbadbit(revents);
+    if (badbit) subproc_problem(st,async_linebuf_broken,badbit);
+
+    if (!(revents & POLLIN)) return;
+
+    for (;;) {
+       if (st->lbuf.size==sizeof(struct privsep_mdata)) {
+           const struct privsep_mdata *mdata=(const void*)st->lbuf.start;
+           if (mdata->add && st->privsep_incoming_fd<0)
+               fatal("polypath (privsep): got add message data but no fd");
+           if (!mdata->add && st->privsep_incoming_fd>=0)
+               fatal("polypath (privsep): got remove message data with fd");
+           if (!memchr(mdata->ifname,0,sizeof(mdata->ifname)))
+               fatal("polypath (privsep): got ifname with no terminating nul");
+           int af=mdata->ia.sa.sa_family;
+           if (!(af==AF_INET6 || af==AF_INET))
+               fatal("polypath (privsep): got message data but bad AF %d",af);
+           const char *addr_str=iaddr_to_string(&mdata->ia);
+           polypath_record_ifaddr(st,papp_bad,(void*)addr_str,
+                                  mdata->add,mdata->want,
+                                  mdata->ifname,addr_str,
+                                  &mdata->ia, st->privsep_incoming_fd);
+           st->privsep_incoming_fd=-1;
+           st->lbuf.size=0;
+       }
+       struct msghdr msg;
+       int fd;
+       size_t cmsgdatalen=sizeof(fd);
+       char cmsg_control_buf[CMSG_SPACE(cmsgdatalen)];
+       struct iovec iov;
+
+       FILLZERO(msg);
+       msg.msg_iov=&iov;
+       msg.msg_iovlen=1;
+
+       iov.iov_base=st->lbuf.start+st->lbuf.size;
+       iov.iov_len=sizeof(struct privsep_mdata)-st->lbuf.size;
+
+       if (st->privsep_incoming_fd<0) {
+           msg.msg_control=cmsg_control_buf;
+           msg.msg_controllen=sizeof(cmsg_control_buf);
+       }
+
+       ssize_t got=recvmsg(st->monitor_fd,&msg,0);
+       if (got<0) {
+           if (errno==EINTR) continue;
+           if (iswouldblock(errno)) break;
+           fatal_perror("polypath (privsep): recvmsg failed");
+       }
+       if (got==0)
+           subproc_problem(st,async_linebuf_eof,0);
+
+       st->lbuf.size+=got;
+
+       if (msg.msg_controllen) {
+           size_t cmsgdatalen=sizeof(st->privsep_incoming_fd);
+           struct cmsghdr *h=CMSG_FIRSTHDR(&msg);
+           if (!(st->privsep_incoming_fd==-1 &&
+                 h &&
+                 h->cmsg_level==SOL_SOCKET &&
+                 h->cmsg_type==SCM_RIGHTS &&
+                 h->cmsg_len==CMSG_LEN(cmsgdatalen) &&
+                 !CMSG_NXTHDR(&msg,h)))
+               subproc_problem(st,async_linebuf_broken,"bad cmsg");
+           memcpy(&st->privsep_incoming_fd,CMSG_DATA(h),cmsgdatalen);
+           assert(st->privsep_incoming_fd>=0);
+       }
+
+    }
+}
+
+static void privsep_handle_ifaddr(struct polypath *st,
+                                  bad_fn_type *bad, void *badctx,
+                                  bool_t add, char want,
+                                  const char *ifname,
+                                  const char *ifaddr,
+                                  const union iaddr *ia, int fd_dummy)
+/* In child: handles discovered wanted interfaces, making sockets
+   and sending them to secnet proper. */
+{
+    struct msghdr msg;
+    struct iovec iov;
+    struct udpsock us={ .fd=-1 };
+    size_t cmsgdatalen=sizeof(us.fd);
+    char cmsg_control_buf[CMSG_SPACE(cmsgdatalen)];
+
+    assert(fd_dummy==-1);
+
+    struct privsep_mdata mdata;
+    FILLZERO(mdata);
+    mdata.add=add;
+
+    size_t l=strlen(ifname);
+    if (l>=sizeof(mdata.ifname)) BAD("interface name too long");
+    strcpy(mdata.ifname,ifname);
+    mdata.want=want;
+
+    COPY_OBJ(mdata.ia,*ia);
+
+    iov.iov_base=&mdata;
+    iov.iov_len =sizeof(mdata);
+
+    FILLZERO(msg);
+    msg.msg_iov=&iov;
+    msg.msg_iovlen=1;
+
+    if (add) {
+       COPY_OBJ(us.addr,*ia);
+       bool_t ok=polypath_make_socket(st,bad,badctx,&us,ifname);
+       if (!ok) goto out;
+
+       msg.msg_control=cmsg_control_buf;
+       msg.msg_controllen=sizeof(cmsg_control_buf);
+
+       struct cmsghdr *h=CMSG_FIRSTHDR(&msg);
+       h->cmsg_level=SOL_SOCKET;
+       h->cmsg_type =SCM_RIGHTS;
+       h->cmsg_len  =CMSG_LEN(cmsgdatalen);
+       memcpy(CMSG_DATA(h),&us.fd,cmsgdatalen);
+    }
+
+    while (iov.iov_len) {
+       ssize_t got=sendmsg(st->privsep_ipcsock_fd,&msg,0);
+       if (got<0) {
+           if (errno!=EINTR) fatal_perror("polypath privsep sendmsg");
+           got=0;
+       } else {
+           assert(got>0);
+           assert((size_t)got<=iov.iov_len);
+       }
+       iov.iov_base=(char*)iov.iov_base+got;
+       iov.iov_len-=got;
+       msg.msg_control=0;
+       msg.msg_controllen=0;
+    }
+
+ out:
+    if (us.fd>=0) close(us.fd);
+}
+
+static void child_privsep(struct polypath *st, int ipcsockfd)
+/* Privsep child main loop. */
+{
+    struct pollfd fds[2];
+
+    enter_phase(PHASE_CHILDPERSIST);
+
+    st->privsep_ipcsock_fd=ipcsockfd;
+    start_subproc(st,pipe_cloexec,child_monitor,
+                 "interface monitor (grandchild)");
+    for (;;) {
+       int nfds=1;
+       int r=polypath_beforepoll(st,fds,&nfds,0);
+       assert(nfds==1);
+       assert(!r);
+
+       fds[1].fd=st->privsep_ipcsock_fd;
+       fds[1].events=POLLIN;
+
+       r=poll(fds,ARRAY_SIZE(fds),-1);
+
+       if (r<0) {
+           if (errno==EINTR) continue;
+           fatal_perror("polypath privsep poll");
+       }
+       if (fds[1].revents) {
+           if (fds[1].revents & (POLLHUP|POLLIN)) {
+               polypath_phase_shutdown(st,PHASE_SHUTDOWN);
+               exit(0);
+           }
+           fatal("polypath privsep poll parent socket revents=%#x",
+                 fds[1].revents);
+       }
+       if (fds[0].revents & POLLNVAL)
+           fatal("polypath privsep poll child socket POLLNVAL");
+       afterpoll_monitor(st,fds,privsep_handle_ifaddr);
+    }
+}
+
+static void privsep_socketpair(int *fd)
+{
+    int r=socketpair(AF_UNIX,SOCK_STREAM,0,fd);
+    if (r) fatal_perror("socketpair(AF_UNIX,SOCK_STREAM,,)");
+    setcloexec(fd[0]);
+    setcloexec(fd[1]);
+}
+
+static void polypath_phase_startprivsep(void *sst, uint32_t newphase)
+{
+    struct polypath *st=sst;
+
+    if (!will_droppriv()) {
+       add_hook(PHASE_RUN,          polypath_phase_startmonitor,st);
+       return;
+    }
+
+    start_subproc(st,privsep_socketpair,child_privsep,
+                 "socket generator (privsep interface handler)");
+
+    BUF_FREE(&st->lbuf);
+    buffer_destroy(&st->lbuf);
+    buffer_new(&st->lbuf,sizeof(struct privsep_mdata));
+    BUF_ALLOC(&st->lbuf,"polypath mdata buf");
+    st->privsep_incoming_fd=-1;
+
+    register_for_poll(st,polypath_beforepoll,
+                     polypath_afterpoll_privsep,"polypath");
+}
+
+static void polypath_phase_shutdown(void *sst, uint32_t newphase)
+{
+    struct polypath *st=sst;
+    if (st->monitor_pid) {
+       assert(st->monitor_pid>0);
+       kill(st->monitor_pid,SIGTERM);
+    }
+}
+
+static void polypath_phase_childpersist(void *sst, uint32_t newphase)
+{
+    struct polypath *st=sst;
+    struct interf *interf;
+
+    LIST_FOREACH(interf,&st->interfs_general,entry)
+       udp_socks_childpersist(&st->uc,&interf->socks);
+    LIST_FOREACH(interf,&st->interfs_dedicated,entry)
+       udp_socks_childpersist(&st->uc,&interf->socks);
+}
+
+#undef BAD
+#undef BADE
+
+/*----- generic closure and module setup -----*/
+
+static list_t *polypath_apply(closure_t *self, struct cloc loc,
+                             dict_t *context, list_t *args)
+{
+    struct polypath *st;
+
+    COMM_APPLY(st,&st->uc.cc,polypath_,"polypath",loc);
+    COMM_APPLY_STANDARD(st,&st->uc.cc,"polypath",args);
+    UDP_APPLY_STANDARD(st,&st->uc,"polypath");
+
+    st->uc.cc.ops.clientinfo = polypath_clientinfo;
+
+    struct udpcommon *uc=&st->uc;
+    struct commcommon *cc=&uc->cc;
+
+    st->max_interfs=dict_read_number(d,"max-interfaces",False,"polypath",loc,3);
+
+    st->ifname_pats=dict_read_string_array(d,"interfaces",False,"polypath",
+                                          cc->loc,0);
+    st->permit_loopback=0; /* ifname_wanted reads this */
+    ifname_wanted(st,st->uc.cc.loc," "); /* try to check each pattern */
+
+    st->monitor_command=dict_read_string_array(d,"monitor-command",False,
+                               "polypath",cc->loc, default_monitor_command);
+    if (!st->monitor_command[0])
+       cfgfatal(loc,"polypath","no polypath interface monitor-command"
+                " (polypath unsupported on this platform?)\n");
+
+    st->permit_loopback=dict_read_bool(d,"permit-loopback",False,
+                                      "polypath",cc->loc,False);
+
+    LIST_INIT(&st->interfs_general);
+    LIST_INIT(&st->interfs_dedicated);
+    buffer_new(&st->lbuf,ADNS_ADDR2TEXT_BUFLEN+100);
+    BUF_ALLOC(&st->lbuf,"polypath lbuf");
+
+    st->monitor_fd=-1;
+    st->monitor_pid=0;
+
+    add_hook(PHASE_GETRESOURCES, polypath_phase_startprivsep,st);
+
+    add_hook(PHASE_SHUTDOWN,    polypath_phase_shutdown,    st);
+    add_hook(PHASE_CHILDPERSIST,polypath_phase_childpersist,st);
+
+    return new_closure(&cc->cl);
+}
+
+#endif /* CONFIG_IPV6 */
+
+void polypath_module(dict_t *dict)
+{
+#ifdef CONFIG_IPV6
+    add_closure(dict,"polypath",polypath_apply);
+#endif /* CONFIG_IPV6 */
+}
diff --git a/pretest-to-tested b/pretest-to-tested
new file mode 100755 (executable)
index 0000000..7166486
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# best to run this in a git-worktree
+# example runes in main tree:
+#  git-branch -f pretest && git-branch -f tested `git-merge-base HEAD tested` && git-checkout wip
+
+set -e
+while true; do
+    next=$(git-rev-list --reverse tested..pretest | head -n1)
+    if [ "x$next" = x ]; then break; fi
+    git checkout "$next"
+    ./comprehensive-test
+    git push . HEAD:tested
+done
diff --git a/process.c b/process.c
new file mode 100644 (file)
index 0000000..3444308
--- /dev/null
+++ b/process.c
@@ -0,0 +1,349 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#define _GNU_SOURCE
+#include "secnet.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+#include "process.h"
+
+/* Process handling - subprocesses, signals, etc. */
+
+static bool_t signal_handling=False;
+static sigset_t emptyset, fullset;
+static sigset_t registered,pending;
+
+struct child {
+    pid_t pid;
+    cstring_t desc;
+    process_callback_fn *cb;
+    void *cst;
+    bool_t finished;
+    struct child *next;
+};
+
+static struct child *children=NULL;
+
+struct signotify {
+    int signum;
+    signal_notify_fn *notify;
+    void *cst;
+    struct signotify *next;
+};
+
+static struct signotify *sigs=NULL;
+
+static int spw,spr; /* file descriptors for signal notification pipe */
+
+/* Long-lived subprocesses can only be started once we've started
+   signal processing so that we can catch SIGCHLD for them and report
+   their exit status using the callback function.  We block SIGCHLD
+   until signal processing has begun. */
+pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
+                void *est, void *cst, cstring_t desc)
+{
+    struct child *c;
+    pid_t p;
+
+    NEW(c);
+    c->desc=desc;
+    c->cb=cb;
+    c->cst=cst;
+
+    if (!signal_handling) {
+       fatal("makesubproc called before signal handling started");
+    }
+    p=fork();
+    if (p==0) {
+       /* Child process */
+       afterfork();
+       entry(est);
+       abort();
+    } else if (p==-1) {
+       fatal_perror("makesubproc (%s): fork",desc);
+    }
+    c->pid=p;
+    c->finished=False;
+    c->next=children;
+    children=c;
+    return p;
+}
+
+static signal_notify_fn sigchld_handler;
+static void sigchld_handler(void *st, int signum)
+{
+    struct child *i,*n,**p;
+    struct work {
+       pid_t pid;
+       process_callback_fn *cb;
+       void *cst;
+       int status;
+       struct work *next;
+    };
+    struct work *w=NULL, *nw;
+    pid_t rv;
+    int status;
+
+    for (i=children; i; i=i->next) {
+       rv=waitpid(i->pid,&status,WNOHANG);
+       if (rv==-1) {
+           fatal_perror("sigchld_handler: waitpid");
+       }
+       if (rv==i->pid) {
+           i->finished=True;
+           
+           NEW(nw);
+           nw->pid=i->pid;
+           nw->cb=i->cb;
+           nw->cst=i->cst;
+           nw->status=status;
+           nw->next=w;
+           w=nw;
+       }
+    }
+
+    /* Remove all the finished tasks from the list of children */
+    for (i=children, p=&children; i; i=n) {
+       n=i->next;
+       if (i->finished) {
+           free(i);
+           *p=n;
+       } else {
+           p=&i->next;
+       }
+    }
+
+    /* Notify as appropriate, then free the list */
+    while (w) {
+       w->cb(w->cst,w->pid,w->status);
+       nw=w;
+       w=w->next;
+       free(nw);
+    }
+}
+
+int sys_cmd(const char *path, const char *arg, ...)
+{
+    va_list ap;
+    int rv, rc;
+    pid_t c;
+
+    c=fork();
+    if (c) {
+       /* Parent -> wait for child */
+       do {
+           rc = waitpid(c,&rv,0);
+       } while(rc < 0 && errno == EINTR);
+       if (rc < 0)
+           fatal_perror("sys_cmd: waitpid for %s", path);
+       if (rc != c) /* OS has gone mad */
+           fatal("sys_cmd: waitpid for %s returned wrong process ID!",
+                 path);
+       if (rv) {
+           /* If the command failed report its exit status */
+           lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path);
+       }
+    } else if (c==0) {
+       char *args[100];
+       int i;
+       /* Child -> exec command */
+       /* Really we ought to strcpy() the arguments into the args array,
+          since the arguments are const char *.  Since we'll exit anyway
+          if the execvp() fails this seems somewhat pointless, and
+          increases the chance of the child process failing before it
+          gets to exec(). */
+       afterfork();
+       va_start(ap,arg);
+       args[0]=(char *)arg; /* program name */
+       i=1;
+       while ((args[i++]=va_arg(ap,char *)));
+       execvp(path,args);
+       fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
+       _exit(1);
+    } else {
+       /* Error */
+       fatal_perror("sys_cmd(%s,%s,...)", path, arg);
+    }
+
+    return rv;
+}
+
+static beforepoll_fn signal_beforepoll;
+static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io,
+                            int *timeout_io)
+{
+    BEFOREPOLL_WANT_FDS(1);
+    fds[0].fd=spr;
+    fds[0].events=POLLIN;
+    return 0;
+}
+
+/* Bodge to work around Ubuntu's strict header files */
+static void discard(int anything) {}
+
+static afterpoll_fn signal_afterpoll;
+static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
+{
+    uint8_t buf[16];
+    struct signotify *n;
+    sigset_t todo,old;
+
+    if (nfds && (fds->revents & POLLIN)) {
+       discard(read(spr,buf,16));
+                         /* We don't actually care what we read; as
+                            long as there was at least one byte
+                            (which there was) we'll pick up the
+                            signals in the pending set */
+       
+       /* We reset 'pending' before processing any of the signals
+          that were pending so that we don't miss any signals that
+          are delivered partway-through processing (all we assume
+          about signal notification routines is that they handle all
+          the work available at their _start_ and only optionally any
+          work that arrives part-way through their execution). */
+       sigprocmask(SIG_SETMASK,&fullset,&old);
+       todo=pending;
+       sigemptyset(&pending);
+       sigprocmask(SIG_SETMASK,&old,NULL);
+       
+       for (n=sigs; n; n=n->next)
+           if (sigismember(&todo,n->signum))
+               n->notify(n->cst,n->signum);
+    }
+}
+
+void afterfork(void)
+{
+    struct signotify *n;
+    sigset_t done;
+    struct sigaction sa;
+
+    clear_phase_hooks(PHASE_SHUTDOWN);
+    /* Prevents calls to fatal() etc. in the child from running off
+       and doing a lot of unhelpful things */
+
+    sigemptyset(&done);
+    for (n=sigs; n; n=n->next)
+       if (!sigismember(&done,n->signum)) {
+           sigaddset(&done,n->signum);
+           sa.sa_handler=SIG_DFL;
+           sa.sa_mask=emptyset;
+           sa.sa_flags=0;
+           sigaction(n->signum,&sa,NULL);
+       }
+
+    sigemptyset(&emptyset);
+    sigprocmask(SIG_SETMASK,&emptyset,NULL);
+}
+
+void childpersist_closefd_hook(void *fd_vp, uint32_t newphase)
+{
+    int *fd_p=fd_vp;
+    int fd=*fd_p;
+    if (fd<0) return;
+    *fd_p=-1;
+    setnonblock(fd); /* in case close() might block */
+    close(fd); /* discard errors - we don't care, in the child */
+}
+
+static void signal_handler(int signum)
+{
+    int saved_errno;
+    uint8_t thing=0;
+    sigaddset(&pending,signum);
+    /* XXX the write() may set errno, which can make the main program fail.
+       However, signal handlers aren't allowed to modify anything which
+       is not of type sig_atomic_t.  The world is broken. */
+    /* I have decided to save and restore errno anyway; on most
+       architectures on which secnet can run modifications to errno
+       will be atomic, and it seems to be the lesser of the two
+       evils. */
+    saved_errno=errno;
+    discard(write(spw,&thing,1));
+                         /* We don't care if this fails (i.e. the pipe
+                           is full) because the service routine will
+                           spot the pending signal anyway */
+    errno=saved_errno;
+}
+
+static void register_signal_handler(struct signotify *s)
+{
+    struct sigaction sa;
+    int rv;
+
+    if (!signal_handling) return;
+
+    if (sigismember(&registered,s->signum)) return;
+    sigaddset(&registered,s->signum);
+
+    sa.sa_handler=signal_handler;
+    sa.sa_mask=fullset;
+    sa.sa_flags=0;
+    rv=sigaction(s->signum,&sa,NULL);
+    if (rv!=0) {
+       fatal_perror("register_signal_handler: sigaction(%d)",s->signum);
+    }
+}
+
+void request_signal_notification(int signum, signal_notify_fn *notify,
+                                void *cst)
+{
+    struct signotify *s;
+    sigset_t old;
+
+    NEW(s);
+    s->signum=signum;
+    s->notify=notify;
+    s->cst=cst;
+    s->next=sigs;
+    sigprocmask(SIG_SETMASK,&fullset,&old);
+    sigs=s;
+    register_signal_handler(s);
+    sigprocmask(SIG_SETMASK,&old,NULL);
+}
+
+void start_signal_handling(void)
+{
+    int p[2];
+    struct signotify *i;
+
+    sigemptyset(&emptyset);
+    sigfillset(&fullset);
+    sigemptyset(&registered);
+    sigemptyset(&pending);
+
+    pipe_cloexec(p);
+    spw=p[1];
+    spr=p[0];
+    setnonblock(spw);
+    setnonblock(spr);
+
+    register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal");
+    signal_handling=True;
+
+    /* Register signal handlers for all the signals we're interested in */
+    for (i=sigs; i; i=i->next) {
+       register_signal_handler(i);
+    }
+
+    request_signal_notification(SIGCHLD,sigchld_handler,NULL);
+}
diff --git a/process.h b/process.h
new file mode 100644 (file)
index 0000000..99681b0
--- /dev/null
+++ b/process.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef process_h
+#define process_h
+
+#include <signal.h>
+#include <sys/wait.h>
+
+typedef void process_callback_fn(void *cst, pid_t pid, int status);
+typedef void process_entry_fn(void *cst);
+typedef void signal_notify_fn(void *cst, int signum);
+
+extern pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb,
+                       void *est, void *cbst, cstring_t desc);
+
+extern void request_signal_notification(int signum, signal_notify_fn *notify,
+                                       void *cst);
+
+#endif /* process_h */
diff --git a/random.c b/random.c
new file mode 100644 (file)
index 0000000..323fffd
--- /dev/null
+++ b/random.c
@@ -0,0 +1,102 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+
+struct rgen_data {
+    closure_t cl;
+    struct random_if ops;
+    struct cloc loc;
+    int fd;
+};
+
+static random_fn random_generate;
+static void random_generate(void *data, int32_t bytes, uint8_t *buff)
+{
+    struct rgen_data *st=data;
+    int r;
+
+    r= read(st->fd,buff,bytes);
+
+    assert(r == bytes);
+    /* This is totally crap error checking, but callers of
+     * this function do not check the return value and dealing
+     * with failure of this everywhere would be very inconvenient.
+     */
+}
+
+static list_t *random_apply(closure_t *self, struct cloc loc,
+                           dict_t *context, list_t *args)
+{
+    struct rgen_data *st;
+    item_t *arg1, *arg2;
+    string_t filename=NULL;
+
+    NEW(st);
+
+    st->cl.description="randomsource";
+    st->cl.type=CL_RANDOMSRC;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.blocking=False;
+    st->ops.generate=random_generate;
+    st->loc=loc;
+
+    arg1=list_elem(args,0);
+    arg2=list_elem(args,1);
+
+    if (!arg1) {
+       cfgfatal(loc,"randomsource","requires a filename\n");
+    }
+    if (arg1->type != t_string) {
+       cfgfatal(arg1->loc,"randomsource",
+                "filename (arg1) must be a string\n");
+    }
+    filename=arg1->data.string;
+
+    if (arg2) {
+       if (arg2->type != t_bool) {
+           cfgfatal(arg2->loc,"randomsource",
+                    "blocking parameter (arg2) must be bool\n");
+       }
+       st->ops.blocking=arg2->data.bool;
+    }
+
+    if (!filename) {
+       cfgfatal(loc,"randomsource","requires a filename\n");
+    }
+    st->fd=open(filename,O_RDONLY);
+    if (st->fd<0) {
+       fatal_perror("randomsource (%s:%d): cannot open %s",arg1->loc.file,
+                    arg1->loc.line,filename);
+    }
+    return new_closure(&st->cl);
+}
+
+void random_module(dict_t *d)
+{
+    add_closure(d,"randomfile",random_apply);
+}
diff --git a/resolver.c b/resolver.c
new file mode 100644 (file)
index 0000000..3a6ad5a
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+/* Name resolution using adns */
+
+#include <errno.h>
+#include "secnet.h"
+#include "util.h"
+#ifndef HAVE_LIBADNS
+#error secnet requires ADNS version 1.0 or above
+#endif
+#include <adns.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+
+struct adns {
+    closure_t cl;
+    struct resolver_if ops;
+    struct cloc loc;
+    adns_state ast;
+};
+
+struct query {
+    void *cst;
+    const char *name;
+    int port;
+    struct comm_if *comm;
+    resolve_answer_fn *answer;
+    adns_query query;
+};
+
+static resolve_request_fn resolve_request;
+static bool_t resolve_request(void *sst, cstring_t name,
+                             int port, struct comm_if *comm,
+                             resolve_answer_fn *cb, void *cst)
+{
+    struct adns *st=sst;
+    struct query *q;
+    int rv;
+    const int maxlitlen=
+#ifdef CONFIG_IPV6
+       ADNS_ADDR2TEXT_BUFLEN*2
+#else
+       50
+#endif
+       ;
+    ssize_t l=strlen(name);
+    if (name[0]=='[' && l<maxlitlen && l>2 && name[l-1]==']') {
+       char trimmed[maxlitlen+1];
+       memcpy(trimmed,name+1,l-2);
+       trimmed[l-2]=0;
+       struct comm_addr ca;
+       ca.comm=comm;
+       ca.ix=-1;
+#ifdef CONFIG_IPV6
+       socklen_t salen=sizeof(ca.ia);
+       rv=adns_text2addr(trimmed, port, adns_qf_addrlit_ipv4_quadonly,
+                         &ca.ia.sa, &salen);
+       assert(rv!=ENOSPC);
+       if (rv) {
+           char msg[250];
+           snprintf(msg,sizeof(msg),"invalid address literal: %s",
+                    strerror(rv));
+           msg[sizeof(msg)-1]=0;
+           cb(cst,0,0,0,name,msg);
+       } else {
+           cb(cst,&ca,1,1,name,0);
+       }
+#else
+       ca.ia.sin.sin_family=AF_INET;
+       ca.ia.sin.sin_port=htons(port);
+       if (inet_aton(trimmed,&ca.ia.sin.sin_addr))
+           cb(cst,&ca,1,1,name,0);
+       else
+           cb(cst,0,0,0,name,"invalid IP address");
+#endif
+       return True;
+    }
+
+    NEW(q);
+    q->cst=cst;
+    q->comm=comm;
+    q->port=port;
+    q->name=name;
+    q->answer=cb;
+
+    rv=adns_submit(st->ast, name, adns_r_addr, 0, q, &q->query);
+    if (rv) {
+        Message(M_WARNING,
+               "resolver: failed to submit lookup for %s: %s",name,
+               adns_strerror(rv));
+       free(q);
+       return False;
+    }
+
+    return True;
+}
+
+static int resolver_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                              int *timeout_io)
+{
+    struct adns *st=sst;
+    return adns_beforepoll(st->ast, fds, nfds_io, timeout_io, tv_now);
+}
+
+static void resolver_afterpoll(void *sst, struct pollfd *fds, int nfds)
+{
+    struct adns *st=sst;
+    adns_query aq;
+    adns_answer *ans;
+    void *qp;
+    struct query *q;
+    int rv;
+
+    adns_afterpoll(st->ast, fds, nfds, tv_now);
+
+    while (True) {
+       aq=NULL;
+       rv=adns_check(st->ast, &aq, &ans, &qp);
+       if (rv==0) {
+           q=qp;
+           if (ans->status!=adns_s_ok) {
+               q->answer(q->cst,NULL,0,0,q->name,adns_strerror(ans->status));
+               free(q);
+               free(ans);
+           } else {
+               int rslot, wslot, total;
+               int ca_len=MIN(ans->nrrs,MAX_PEER_ADDRS);
+               struct comm_addr ca_buf[ca_len];
+               for (rslot=0, wslot=0, total=0;
+                    rslot<ans->nrrs;
+                    rslot++) {
+                   total++;
+                   if (!(wslot<ca_len)) continue;
+                   adns_rr_addr *ra=&ans->rrs.addr[rslot];
+                   struct comm_addr *ca=&ca_buf[wslot];
+                   ca->comm=q->comm;
+                   ca->ix=-1;
+                   assert(ra->len <= (int)sizeof(ca->ia));
+                   memcpy(&ca->ia,&ra->addr,ra->len);
+                   switch (ra->addr.sa.sa_family) {
+                   case AF_INET:
+                       assert(ra->len == sizeof(ca->ia.sin));
+                       ca->ia.sin.sin_port=htons(q->port);
+                       break;
+#ifdef CONFIG_IPV6
+                   case AF_INET6:
+                       assert(ra->len == sizeof(ca->ia.sin6));
+                       ca->ia.sin6.sin6_port=htons(q->port);
+                       break;
+#endif /*CONFIG_IPV6*/
+                   default:
+                       /* silently skip unexpected AFs from adns */
+                       continue;
+                   }
+                   wslot++;
+               }
+               q->answer(q->cst,ca_buf,wslot,total,q->name,0);
+               free(q);
+               free(ans);
+           }
+       } else if (rv==EAGAIN || rv==ESRCH) {
+           break;
+       } else {
+           fatal("resolver_afterpoll: adns_check() returned %d",rv);
+       }
+    }
+
+    return;
+}
+
+/* Initialise adns, using parameters supplied */
+static list_t *adnsresolver_apply(closure_t *self, struct cloc loc,
+                                 dict_t *context, list_t *args)
+{
+    struct adns *st;
+    dict_t *d;
+    item_t *i;
+    string_t conf;
+
+    NEW(st);
+    st->cl.description="adns";
+    st->cl.type=CL_RESOLVER;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->loc=loc;
+    st->ops.st=st;
+    st->ops.request=resolve_request;
+
+    i=list_elem(args,0);
+    if (!i || i->type!=t_dict) {
+       cfgfatal(st->loc,"adns","first argument must be a dictionary\n");
+    }
+    d=i->data.dict;
+    conf=dict_read_string(d,"config",False,"adns",loc);
+
+    if (conf) {
+       if (adns_init_strcfg(&st->ast, 0, 0, conf)) {
+           fatal_perror("Failed to initialise ADNS");
+       }
+    } else {
+       if (adns_init(&st->ast, 0, 0)) {
+           fatal_perror("Failed to initialise ADNS");
+       }
+    }
+
+    register_for_poll(st, resolver_beforepoll, resolver_afterpoll,
+                     "resolver");
+
+    return new_closure(&st->cl);
+}
+
+void resolver_module(dict_t *dict)
+{
+    add_closure(dict,"adns",adnsresolver_apply);
+}
diff --git a/rsa.c b/rsa.c
new file mode 100644 (file)
index 0000000..28a4c1b
--- /dev/null
+++ b/rsa.c
@@ -0,0 +1,593 @@
+/*
+ * rsa.c: implementation of RSA with PKCS#1 padding
+ */
+/*
+ * This file is Free Software.  It was originally written for secnet.
+ *
+ * Copyright 1995-2003 Stephen Early
+ * Copyright 2002-2014 Ian Jackson
+ * Copyright 2001      Simon Tatham
+ * Copyright 2013      Mark Wooding
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <gmp.h>
+#include "secnet.h"
+#include "util.h"
+#include "unaligned.h"
+
+#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n"
+
+#define mpp(s,n) do { char *p = mpz_get_str(NULL,16,n); printf("%s 0x%sL\n", s, p); free(p); } while (0)
+
+struct rsacommon {
+    struct hash_if *hashi;
+    uint8_t *hashbuf;
+};
+
+struct rsapriv {
+    closure_t cl;
+    struct sigprivkey_if ops;
+    struct cloc loc;
+    struct rsacommon common;
+    MP_INT n;
+    MP_INT p, dp;
+    MP_INT q, dq;
+    MP_INT w;
+};
+struct rsapub {
+    closure_t cl;
+    struct sigpubkey_if ops;
+    struct cloc loc;
+    struct rsacommon common;
+    MP_INT e;
+    MP_INT n;
+};
+/* Sign data. NB data must be smaller than modulus */
+
+#define RSA_MAX_MODBYTES 2048
+/* The largest modulus I've seen is 15360 bits, which works out at 1920
+ * bytes.  Using keys this big is quite implausible, but it doesn't cost us
+ * much to support them.
+ */
+
+static const char *hexchars="0123456789abcdef";
+
+static void rsa_sethash(struct rsacommon *c, struct hash_if *hash)
+{
+    free(c->hashbuf);
+    c->hashbuf=safe_malloc(hash->hlen, "generate_msg");
+    c->hashi=hash;
+}
+static void rsa_pub_sethash(void *sst, struct hash_if *hash)
+{
+    struct rsapub *st=sst;
+    rsa_sethash(&st->common, hash);
+}
+static void rsa_priv_sethash(void *sst, struct hash_if *hash)
+{
+    struct rsapriv *st=sst;
+    rsa_sethash(&st->common, hash);
+}
+static void rsa_hash(struct rsacommon *c, const uint8_t *buf, int32_t len)
+{
+    hash_hash(c->hashi,buf,len,c->hashbuf);
+}
+
+static void emsa_pkcs1(MP_INT *n, MP_INT *m,
+                      const uint8_t *data, int32_t datalen)
+{
+    char buff[2*RSA_MAX_MODBYTES + 1];
+    int msize, i;
+
+    /* RSA PKCS#1 v1.5 signature padding:
+     *
+     * <------------ msize hex digits ---------->
+     *
+     * 00 01 ff ff .... ff ff 00 vv vv vv .... vv
+     *
+     *                           <--- datalen -->
+     *                                 bytes
+     *                         = datalen*2 hex digits
+     *
+     * NB that according to PKCS#1 v1.5 we're supposed to include a
+     * hash function OID in the data.  We don't do that (because we
+     * don't have the hash function OID to hand here), thus violating
+     * the spec in a way that affects interop but not security.
+     *
+     * -iwj 17.9.2002
+     */
+
+    msize=mpz_sizeinbase(n, 16);
+
+    if (datalen*2+6>=msize) {
+       fatal("rsa_sign: message too big");
+    }
+
+    strcpy(buff,"0001");
+
+    for (i=0; i<datalen; i++) {
+       buff[msize+(-datalen+i)*2]=hexchars[(data[i]&0xf0)>>4];
+       buff[msize+(-datalen+i)*2+1]=hexchars[data[i]&0xf];
+    }
+    
+    buff[msize-datalen*2-2]= '0';
+    buff[msize-datalen*2-1]= '0';
+    for (i=4; i<msize-datalen*2-2; i++)
+       buff[i]='f';
+
+    buff[msize]=0;
+
+    mpz_set_str(m, buff, 16);
+}
+
+static bool_t rsa_sign(void *sst, uint8_t *data, int32_t datalen,
+                      struct buffer_if *msg)
+{
+    struct rsapriv *st=sst;
+    MP_INT a, b, u, v, tmp, tmp2;
+    string_t signature = 0;
+    bool_t ok;
+
+    mpz_init(&a);
+    mpz_init(&b);
+
+    rsa_hash(&st->common,data,datalen);
+    /* Construct the message representative. */
+    emsa_pkcs1(&st->n, &a, st->common.hashbuf, st->common.hashi->hlen);
+
+    /*
+     * Produce an RSA signature (a^d mod n) using the Chinese
+     * Remainder Theorem. We compute:
+     * 
+     *   u = a^dp mod p    (== a^d mod p, since dp == d mod (p-1))
+     *   v = a^dq mod q    (== a^d mod q, similarly)
+     * 
+     * We also know w == iqmp * q, which has the property that w ==
+     * 0 mod q and w == 1 mod p. So (1-w) has the reverse property
+     * (congruent to 0 mod p and to 1 mod q). Hence we now compute
+     * 
+     *   b = w * u + (1-w) * v
+     *     = w * (u-v) + v
+     * 
+     * so that b is congruent to a^d both mod p and mod q. Hence b,
+     * reduced mod n, is the required signature.
+     */
+    mpz_init(&tmp);
+    mpz_init(&tmp2);
+    mpz_init(&u);
+    mpz_init(&v);
+
+    mpz_powm_sec(&u, &a, &st->dp, &st->p);
+    mpz_powm_sec(&v, &a, &st->dq, &st->q);
+    mpz_sub(&tmp, &u, &v);
+    mpz_mul(&tmp2, &tmp, &st->w);
+    mpz_add(&tmp, &tmp2, &v);
+    mpz_mod(&b, &tmp, &st->n);
+
+    mpz_clear(&tmp);
+    mpz_clear(&tmp2);
+    mpz_clear(&u);
+    mpz_clear(&v);
+
+    signature=write_mpstring(&b);
+
+    uint8_t *op = buf_append(msg,2);
+    if (!op) { ok=False; goto out; }
+    size_t l = strlen(signature);
+    assert(l < 65536);
+    put_uint16(op, l);
+    op = buf_append(msg,l);
+    if (!op) { ok=False; goto out; }
+    memcpy(op, signature, l);
+
+    ok = True;
+
+ out:
+    free(signature);
+    mpz_clear(&b);
+    mpz_clear(&a);
+    return ok;
+}
+
+static bool_t rsa_sig_unpick(void *sst, struct buffer_if *msg,
+                            struct alg_msg_data *sig)
+{
+    uint8_t *lp = buf_unprepend(msg, 2);
+    if (!lp) return False;
+    sig->len = get_uint16(lp);
+    sig->start = buf_unprepend(msg, sig->len);
+    if (!sig->start) return False;
+
+    /* In `rsa_sig_check' below, we assume that we can write a nul
+     * terminator following the signature.  Make sure there's enough space.
+     */
+    if (msg->start >= msg->base + msg->alloclen)
+       return False;
+
+    return True;
+}
+
+static sig_checksig_fn rsa_sig_check;
+static bool_t rsa_sig_check(void *sst, uint8_t *data, int32_t datalen,
+                           const struct alg_msg_data *sig)
+{
+    struct rsapub *st=sst;
+    MP_INT a, b, c;
+    bool_t ok;
+
+    mpz_init(&a);
+    mpz_init(&b);
+    mpz_init(&c);
+
+    rsa_hash(&st->common,data,datalen);
+    emsa_pkcs1(&st->n, &a, st->common.hashbuf, st->common.hashi->hlen);
+
+    /* Terminate signature with a '0' - already checked that this will fit */
+    int save = sig->start[sig->len];
+    sig->start[sig->len] = 0;
+    mpz_set_str(&b, sig->start, 16);
+    sig->start[sig->len] = save;
+
+    mpz_powm(&c, &b, &st->e, &st->n);
+
+    ok=(mpz_cmp(&a, &c)==0);
+
+    mpz_clear(&c);
+    mpz_clear(&b);
+    mpz_clear(&a);
+
+    return ok;
+}
+
+static list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context,
+                           list_t *args)
+{
+    struct rsapub *st;
+    item_t *i;
+    string_t e,n;
+
+    NEW(st);
+    st->cl.description="rsapub";
+    st->cl.type=CL_SIGPUBKEY;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.sethash=rsa_pub_sethash;
+    st->common.hashbuf=NULL;
+    st->ops.unpick=rsa_sig_unpick;
+    st->ops.check=rsa_sig_check;
+    st->loc=loc;
+
+    i=list_elem(args,0);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"rsa-public","first argument must be a string\n");
+       }
+       e=i->data.string;
+       if (mpz_init_set_str(&st->e,e,10)!=0) {
+           cfgfatal(i->loc,"rsa-public","encryption key \"%s\" is not a "
+                    "decimal number string\n",e);
+       }
+    } else {
+       cfgfatal(loc,"rsa-public","you must provide an encryption key\n");
+    }
+    if (mpz_sizeinbase(&st->e, 256) > RSA_MAX_MODBYTES) {
+       cfgfatal(loc, "rsa-public", "implausibly large public exponent\n");
+    }
+    
+    i=list_elem(args,1);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"rsa-public","second argument must be a string\n");
+       }
+       n=i->data.string;
+       if (mpz_init_set_str(&st->n,n,10)!=0) {
+           cfgfatal(i->loc,"rsa-public","modulus \"%s\" is not a decimal "
+                    "number string\n",n);
+       }
+    } else {
+       cfgfatal(loc,"rsa-public","you must provide a modulus\n");
+    }
+    if (mpz_sizeinbase(&st->n, 256) > RSA_MAX_MODBYTES) {
+       cfgfatal(loc, "rsa-public", "implausibly large modulus\n");
+    }
+    return new_closure(&st->cl);
+}
+
+static uint32_t keyfile_get_int(struct cloc loc, FILE *f)
+{
+    uint32_t r;
+    r=fgetc(f)<<24;
+    r|=fgetc(f)<<16;
+    r|=fgetc(f)<<8;
+    r|=fgetc(f);
+    cfgfile_postreadcheck(loc,f);
+    return r;
+}
+
+static uint16_t keyfile_get_short(struct cloc loc, FILE *f)
+{
+    uint16_t r;
+    r=fgetc(f)<<8;
+    r|=fgetc(f);
+    cfgfile_postreadcheck(loc,f);
+    return r;
+}
+
+static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context,
+                            list_t *args)
+{
+    struct rsapriv *st;
+    FILE *f;
+    cstring_t filename;
+    item_t *i;
+    long length;
+    uint8_t *b, *c;
+    int cipher_type;
+    MP_INT e,d,iqmp,tmp,tmp2,tmp3;
+    bool_t valid;
+
+    NEW(st);
+    st->cl.description="rsapriv";
+    st->cl.type=CL_SIGPRIVKEY;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.sethash=rsa_priv_sethash;
+    st->common.hashbuf=NULL;
+    st->ops.sign=rsa_sign;
+    st->loc=loc;
+
+    /* Argument is filename pointing to SSH1 private key file */
+    i=list_elem(args,0);
+    if (i) {
+       if (i->type!=t_string) {
+           cfgfatal(i->loc,"rsa-private","first argument must be a string\n");
+       }
+       filename=i->data.string;
+    } else {
+       filename=NULL; /* Make compiler happy */
+       cfgfatal(loc,"rsa-private","you must provide a filename\n");
+    }
+
+    f=fopen(filename,"rb");
+    if (!f) {
+       if (just_check_config) {
+           Message(M_WARNING,"rsa-private (%s:%d): cannot open keyfile "
+                   "\"%s\"; assuming it's valid while we check the "
+                   "rest of the configuration\n",loc.file,loc.line,filename);
+           goto assume_valid;
+       } else {
+           fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"",
+                        loc.file,loc.line,filename);
+       }
+    }
+
+    /* Check that the ID string is correct */
+    length=strlen(AUTHFILE_ID_STRING)+1;
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1 || memcmp(b,AUTHFILE_ID_STRING,length)!=0) {
+       cfgfatal_maybefile(f,loc,"rsa-private","failed to read magic ID"
+                          " string from SSH1 private keyfile \"%s\"\n",
+                          filename);
+    }
+    free(b);
+
+    cipher_type=fgetc(f);
+    keyfile_get_int(loc,f); /* "Reserved data" */
+    if (cipher_type != 0) {
+       cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n");
+    }
+
+    /* Read the public key */
+    keyfile_get_int(loc,f); /* Not sure what this is */
+    length=(keyfile_get_short(loc,f)+7)/8;
+    if (length>RSA_MAX_MODBYTES) {
+       cfgfatal(loc,"rsa-private","implausible length %ld for modulus\n",
+                length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f) != 1) {
+       cfgfatal_maybefile(f,loc,"rsa-private","error reading modulus\n");
+    }
+    mpz_init(&st->n);
+    read_mpbin(&st->n,b,length);
+    free(b);
+    length=(keyfile_get_short(loc,f)+7)/8;
+    if (length>RSA_MAX_MODBYTES) {
+       cfgfatal(loc,"rsa-private","implausible length %ld for e\n",length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1) {
+       cfgfatal_maybefile(f,loc,"rsa-private","error reading e\n");
+    }
+    mpz_init(&e);
+    read_mpbin(&e,b,length);
+    free(b);
+    
+    length=keyfile_get_int(loc,f);
+    if (length>1024) {
+       cfgfatal(loc,"rsa-private","implausibly long (%ld) key comment\n",
+                length);
+    }
+    c=safe_malloc(length+1,"rsapriv_apply");
+    if (fread(c,length,1,f)!=1) {
+       cfgfatal_maybefile(f,loc,"rsa-private","error reading key comment\n");
+    }
+    c[length]=0;
+
+    /* Check that the next two pairs of characters are identical - the
+       keyfile is not encrypted, so they should be */
+
+    if (keyfile_get_short(loc,f) != keyfile_get_short(loc,f)) {
+       cfgfatal(loc,"rsa-private","corrupt keyfile\n");
+    }
+
+    /* Read d */
+    length=(keyfile_get_short(loc,f)+7)/8;
+    if (length>RSA_MAX_MODBYTES) {
+       cfgfatal(loc,"rsa-private","implausibly long (%ld) decryption key\n",
+                length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1) {
+       cfgfatal_maybefile(f,loc,"rsa-private",
+                          "error reading decryption key\n");
+    }
+    mpz_init(&d);
+    read_mpbin(&d,b,length);
+    free(b);
+    /* Read iqmp (inverse of q mod p) */
+    length=(keyfile_get_short(loc,f)+7)/8;
+    if (length>RSA_MAX_MODBYTES) {
+       cfgfatal(loc,"rsa-private","implausibly long (%ld)"
+                " iqmp auxiliary value\n", length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1) {
+       cfgfatal_maybefile(f,loc,"rsa-private",
+                          "error reading decryption key\n");
+    }
+    mpz_init(&iqmp);
+    read_mpbin(&iqmp,b,length);
+    free(b);
+    /* Read q (the smaller of the two primes) */
+    length=(keyfile_get_short(loc,f)+7)/8;
+    if (length>RSA_MAX_MODBYTES) {
+       cfgfatal(loc,"rsa-private","implausibly long (%ld) q value\n",
+                length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1) {
+       cfgfatal_maybefile(f,loc,"rsa-private",
+                          "error reading q value\n");
+    }
+    mpz_init(&st->q);
+    read_mpbin(&st->q,b,length);
+    free(b);
+    /* Read p (the larger of the two primes) */
+    length=(keyfile_get_short(loc,f)+7)/8;
+    if (length>RSA_MAX_MODBYTES) {
+       cfgfatal(loc,"rsa-private","implausibly long (%ld) p value\n",
+                length);
+    }
+    b=safe_malloc(length,"rsapriv_apply");
+    if (fread(b,length,1,f)!=1) {
+       cfgfatal_maybefile(f,loc,"rsa-private",
+                          "error reading p value\n");
+    }
+    mpz_init(&st->p);
+    read_mpbin(&st->p,b,length);
+    free(b);
+    
+    if (fclose(f)!=0) {
+       fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line);
+    }
+
+    /*
+     * Now verify the validity of the key, and set up the auxiliary
+     * values for fast CRT signing.
+     */
+    valid=False;
+    i=list_elem(args,1);
+    mpz_init(&tmp);
+    mpz_init(&tmp2);
+    mpz_init(&tmp3);
+    if (i && i->type==t_bool && i->data.bool==False) {
+       Message(M_INFO,"rsa-private (%s:%d): skipping RSA key validity "
+               "check\n",loc.file,loc.line);
+    } else {
+       /* Verify that p*q is equal to n. */
+       mpz_mul(&tmp, &st->p, &st->q);
+       if (mpz_cmp(&tmp, &st->n) != 0)
+           goto done_checks;
+
+       /*
+        * Verify that d*e is congruent to 1 mod (p-1), and mod
+        * (q-1). This is equivalent to it being congruent to 1 mod
+        * lambda(n) = lcm(p-1,q-1).  The usual `textbook' condition,
+        * that d e == 1 (mod (p-1)(q-1)) is sufficient, but not
+        * actually necessary.
+        */
+       mpz_mul(&tmp, &d, &e);
+       mpz_sub_ui(&tmp2, &st->p, 1);
+       mpz_mod(&tmp3, &tmp, &tmp2);
+       if (mpz_cmp_si(&tmp3, 1) != 0)
+           goto done_checks;
+       mpz_sub_ui(&tmp2, &st->q, 1);
+       mpz_mod(&tmp3, &tmp, &tmp2);
+       if (mpz_cmp_si(&tmp3, 1) != 0)
+           goto done_checks;
+
+       /* Verify that q*iqmp is congruent to 1 mod p. */
+       mpz_mul(&tmp, &st->q, &iqmp);
+       mpz_mod(&tmp2, &tmp, &st->p);
+       if (mpz_cmp_si(&tmp2, 1) != 0)
+           goto done_checks;
+    }
+    /* Now we know the key is valid (or we don't care). */
+    valid = True;
+    
+    /*
+     * Now we compute auxiliary values dp, dq and w to allow us
+     * to use the CRT optimisation when signing.
+     * 
+     *   dp == d mod (p-1)      so that a^dp == a^d mod p, for all a
+     *   dq == d mod (q-1)      similarly mod q
+     *   w == iqmp * q          so that w == 0 mod q, and w == 1 mod p
+     */
+    mpz_init(&st->dp);
+    mpz_init(&st->dq);
+    mpz_init(&st->w);
+    mpz_sub_ui(&tmp, &st->p, 1);
+    mpz_mod(&st->dp, &d, &tmp);
+    mpz_sub_ui(&tmp, &st->q, 1);
+    mpz_mod(&st->dq, &d, &tmp);
+    mpz_mul(&st->w, &iqmp, &st->q);
+    
+done_checks:
+    if (!valid) {
+       cfgfatal(loc,"rsa-private","file \"%s\" does not contain a "
+                "valid RSA key!\n",filename);
+    }
+    mpz_clear(&tmp);
+    mpz_clear(&tmp2);
+    mpz_clear(&tmp3);
+
+    free(c);
+    mpz_clear(&e);
+    mpz_clear(&d);
+    mpz_clear(&iqmp);
+
+assume_valid:
+    return new_closure(&st->cl);
+}
+
+void rsa_module(dict_t *dict)
+{
+    add_closure(dict,"rsa-private",rsapriv_apply);
+    add_closure(dict,"rsa-public",rsapub_apply);
+}
diff --git a/secnet-wireshark.lua b/secnet-wireshark.lua
new file mode 100644 (file)
index 0000000..62739bc
--- /dev/null
@@ -0,0 +1,883 @@
+--- -*-lua-*-
+---
+--- This file is part of secnet.
+--- See README for full list of copyright holders.
+---
+--- secnet is free software; you can redistribute it and/or modify it
+--- under the terms of the GNU General Public License as published by
+--- the Free Software Foundation; either version d of the License, or
+--- (at your option) any later version.
+---
+--- secnet 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.  See the GNU
+--- General Public License for more details.
+---
+--- You should have received a copy of the GNU General Public License
+--- version 3 along with secnet; if not, see
+--- https://www.gnu.org/licenses/gpl.html.
+
+local secnet = Proto("secnet", "Secnet VPN")
+
+-----------------------------------------------------------------------------
+--- Session tracking.
+---
+--- This is the hardest part of the dissector.
+
+-- Timelines.  A timeline associates pieces of information with times T.
+
+local function tl_new()
+  -- Return a fresh shiny timeline.
+
+  return { }
+end
+
+local function tl__find(tl, t)
+  -- Find and return the earliest association in TL not earlier than T.  If
+  -- there is no such entry, return nil.
+
+  local lo = 1
+  local hi = #tl + 1
+
+  -- Plain old binary search.  The active interval is half-open, [lo, hi).
+  while true do
+    local w = hi - lo
+    if w == 0 then return nil end
+    local mid = lo + math.floor(w/2)
+    local tv = tl[mid]
+    if tv.t > t then hi = mid
+    elseif tv.t == t or w == 1 then return tv
+    else lo = mid
+    end
+  end
+end
+
+local function tl_find(tl, t)
+  -- Find and return the state of the timeline at time T, i.e., the earliest
+  -- value in TL not earlier than T.  If there is no such entry, return nil.
+
+  local tv = tl__find(tl, t)
+  if tv == nil then return nil else return tv.v end
+end
+
+local function tl_add(tl, t, v)
+  -- Associate the value V with time T in TL.
+
+  local tv = tl__find(tl, t)
+  if tv ~= nil and tv.t == t then
+    tv.v = v
+  else
+    -- Append the new item.  If necessary, sort the vector; we expect that
+    -- we'll see everything in the right order, so this won't be a problem.
+    local n = #tl
+    tl[n + 1] = { t = t, v = v }
+    if n > 0 and tl[n].t > t then
+      table.sort(tl, function (tv0, tv1) return tv0.t < tv1.t end)
+    end
+  end
+end
+
+local function dump_timeline(tl, cvt)
+  -- Dump a timeline TL, using the function CVT to convert each value to a
+  -- string.
+
+  for _, tv in ipairs(tl) do print("\t" .. tv.t .. ": " .. cvt(tv.v)) end
+end
+
+local function get_timeline_create(map, index)
+  -- If MAP[INDEX] exists, return it; otherwise set MAP[INDEX] to a fresh
+  -- timeline and return that.
+
+  local tl = map[index]
+  if tl == nil then tl = tl_new(); map[index] = tl end
+  return tl
+end
+
+local function lookup_timeline(map, index, t)
+  -- If it exists, MAP[INDEX] should be a timeline; find its state at time T.
+  -- Return nil if there's nothing there, or T is too early.
+
+  local tl = map[index]
+  if tl == nil then return nil
+  else return tl_find(tl, t)
+  end
+end
+
+-- The `SITEMAP' maps site names to little structures.
+--
+--   * `algs' is a map from peer site names to a timeline of structures
+--     described below.
+--
+--   * `index' is a map from site indices to a timeline of names, reflecting
+--     that, at some time T, this site thought that some index I referred to
+--     a peer site P.
+--
+-- The `algs' map contains the following slots, populated during .
+--
+--   * `xform' is a timeline of transform names.
+local SITEMAP = { }
+
+-- The `ADDRMAP' maps (IPv4 or IPv6) socket addresses in the form
+-- `[ADDR]:PORT' to a timeline of site names, populated based on claims made
+-- by senders about themselves.  The `GUESSMAP' is similar, but populated
+-- based on assertions about recipients.
+local ADDRMAP = { }
+local GUESSMAP = { }
+
+local function snd_sockname(st)
+  -- Return the sender's socket name as a thing which can be used as a table
+  -- index.
+
+  local pinfo = st.pinfo
+  return string.format("[%s]:%d", pinfo.net_src, pinfo.src_port)
+end
+
+local function rcv_sockname(st)
+  -- Return the recipient's socket name as a thing which can be used as a
+  -- table index.
+
+  local pinfo = st.pinfo
+  return string.format("[%s]:%d", pinfo.net_dst, pinfo.dst_port)
+end
+
+local function get_site_create(name)
+  -- If NAME refers to a known site, then return its information structure;
+  -- otherwise create a new one and return that.
+
+  local site = SITEMAP[name]
+  if site == nil then
+    site = { algs = { }, index = { } }
+    SITEMAP[name] = site
+  end
+  return site
+end
+
+local function notice_site_name(map, st, sock, name)
+  -- Record in MAP that the packet described in the state ST tells us that,
+  -- at that time, the site NAME appeared to be at address SOCK.
+
+  tl_add(get_timeline_create(map, sock), st.pinfo.rel_ts, name)
+end
+
+local function dump_algs(algs)
+  -- Dump the algorithms selection ALGS from a site structure.
+
+  return "xform=" .. algs.transform
+end
+
+local function dump_str(str) return str end
+
+local function dump_addrmap(what, map)
+  -- Dump MAP, which is an address map like `ADDRMAP' or `GUESSMAP'; WHAT is
+  -- a string describing which map it is.
+
+  print(what .. "...")
+  for addr, tl in pairs(map) do
+    print("  " .. addr)
+    dump_timeline(tl, dump_str)
+  end
+end
+
+local function dump_tracking_state()
+  -- Dump the entire tracking state to standard output.
+
+  dump_addrmap("Address map", ADDRMAP)
+  dump_addrmap("Guess map", GUESSMAP)
+  print("Site map...")
+  for name, site in pairs(SITEMAP) do
+    print("  " .. name)
+    print("    algs...")
+    for peer, tl in pairs(site.algs) do
+      print("      " .. peer)
+      dump_timeline(tl, dump_algs)
+    end
+    print("    index...")
+    for ix, tl in pairs(site.index) do
+      print("      " .. ix)
+      dump_timeline(tl, dump_str)
+    end
+  end
+end
+
+local function notice_sndname(st, name)
+  -- Record that sender of the packet described by state ST is called NAME.
+
+  st.sndname = name
+  notice_site_name(ADDRMAP, st, snd_sockname(st), name)
+end
+
+local function notice_rcvname(st, name)
+  -- Record that the sender of the packet described by ST thought that its
+  -- recipient was called NAME.
+
+  st.rcvname = name
+  notice_site_name(GUESSMAP, st, rcv_sockname(st), name)
+  if st.sndname ~= nil then
+    local site = get_site_create(st.sndname)
+    tl_add(get_timeline_create(site.index, st.sndix), st.pinfo.rel_ts, name)
+  end
+end
+
+-- Tables describing the kinds of algorithms which can be selected.
+local CAPTAB = {
+  [8] = { name = "serpent256cbc", kind = "transform",
+         desc = "Deprecated Serpent256-CBC transform" },
+  [9] = { name = "eaxserpent", kind = "transform",
+         desc = "Serpent256-EAX transform" },
+  [31] = { name = "mobile-priority", kind = "early",
+          desc = "Mobile site takes priority in case of MSG1 crossing" }
+}
+
+local function get_algname(kind, cap, dflt)
+  -- Fetch an algorithm of the given KIND, given its capability number CAP;
+  -- if CAP is nil, then return DFLT instead.
+
+  local name
+  if cap == nil then
+    name = dflt
+  else
+    local info = CAPTAB[cap]
+    if info ~= nil and info.kind == kind then name = info.name
+    else name = string.format("Unknown %s #%d", kind, cap)
+    end
+  end
+  return name
+end
+
+local function notice_alg_selection(st)
+  -- Record the algorithm selections declared in the packet described by ST.
+
+  local transform = get_algname("transform", st.transform, "serpent256cbc")
+  local site = get_site_create(st.sndname)
+  local peer = get_site_create(st.rcvname)
+  local now = st.pinfo.rel_ts
+  local algs = { transform = transform }
+  tl_add(get_timeline_create(site.algs, st.rcvname), now, algs)
+  tl_add(get_timeline_create(peer.algs, st.sndname), now, algs)
+end
+
+-----------------------------------------------------------------------------
+--- Protocol dissection primitives.
+
+local PF = { } -- The table of protocol fields, filled in later.
+local F = { } -- A table of field values, also filled in later.
+
+local function msgcode(major, minor)
+  -- Construct a Secnet message number according to the complicated rules.
+
+  local majlo = bit.band(major, 0x000f)
+  local majhi = bit.band(major, 0xfff0)
+  local minlo = bit.band(minor, 0x000f)
+  local minhi = bit.band(minor, 0xfff0)
+  return bit.bxor(bit.lshift(majlo,  0),
+                 bit.lshift(majlo,  8),
+                 bit.lshift(majlo, 16),
+                 bit.lshift(majlo, 24),
+                 bit.lshift(majhi,  4),
+                 bit.lshift(minlo,  4),
+                 bit.lshift(minlo, 28),
+                 bit.lshift(minhi, 16))
+end
+
+local function msgmajor(label)
+  -- Return the major message number from a LABEL.
+
+  local lo = bit.band(label, 0x000f)
+  local hi = bit.band(bit.rshift(label, 4), 0xfff0)
+  return bit.bxor(lo, bit.lshift(lo, 4), bit.lshift(lo, 12), hi)
+end
+
+local function msgminor(label)
+  -- Return the minor message number from a LABEL.
+
+  return bit.bxor(bit.lshift(bit.band(label, 0x00ff), 8),
+                 bit.band(bit.rshift(label, 4), 0x000f),
+                 bit.band(bit.rshift(label, 16), 0xfff0))
+end
+
+-- Main message-number table.
+local M = { NAK                = msgcode(     0, 0),
+           MSG0        = msgcode(0x2020, 0), -- !
+           MSG1        = msgcode(     1, 0),
+           MSG2        = msgcode(     2, 0),
+           MSG3        = msgcode(     3, 0),
+           MSG3BIS     = msgcode(     3, 1),
+           MSG4        = msgcode(     4, 0),
+           MSG5        = msgcode(     5, 0),
+           MSG6        = msgcode(     6, 0),
+           MSG7        = msgcode(     7, 0),
+           MSG8        = msgcode(     8, 0),
+           MSG9        = msgcode(     9, 0),
+           PROD        = msgcode(    10, 0)}
+
+-- The `dissect_*' functions follow a common protocol.  They parse a thing
+-- from a packet buffer BUF, of size SZ, starting from POS, and store
+-- interesting things in a given TREE; when they're done, they return the
+-- updated index where the next interesting thing might be, and maybe store
+-- interesting things in the state ST.  As a result, it's usually a simple
+-- matter to parse a packet by invoking the appropriate primitive dissectors
+-- in the right order.
+
+local function dissect_sequence(dissect, st, buf, tree, pos, sz)
+  -- Dissect pieces of the packed in BUF with each of the dissectors in the
+  -- list DISSECT in turn.
+
+  for _, d in ipairs(dissect) do pos = d(st, buf, tree, pos, sz) end
+  return pos
+end
+
+local function dissect_wtf(st, buf, tree, pos, sz)
+  -- If POS is not at the end of the buffer, note that there's unexpected
+  -- stuff in the packet.
+
+  if pos < sz then tree:add(PF["secnet.wtf"], buf(pos, sz - pos)) end
+  return sz
+end
+
+local dissect_caps
+do
+  -- This will be a list of the capability protocol field names, in the right
+  -- order.  We just have to figure out what that will be.
+  local caplist = { }
+
+  do
+    local caps = { }
+
+    -- Firstly, build, in `caps', a list of the capability names and their
+    -- numbers.
+    local i = 1
+    for j, cap in pairs(CAPTAB) do
+      caps[i] = { i = j, cap = cap.name }
+      i = i + 1
+    end
+
+    -- Sort the list.  Now they're in the right order.
+    table.sort(caps, function (v0, v1) return v0.i < v1.i end)
+
+    -- Finally, write the entries to `caplist', with the `user' entry at the
+    -- start and the `unassigned' entry at the end.
+    i = 1
+    caplist[i] = "secnet.cap.user"; i = i + 1
+    for _, v in ipairs(caps) do
+      caplist[i] = "secnet.cap." .. v.cap
+      i = i + 1
+    end
+    caplist[i] = "secnet.cap.unassigned"; i = i + 1
+  end
+
+  function dissect_caps(st, buf, tree, pos, sz)
+    -- Dissect a capabilities word.
+
+    if pos < sz then
+      local cap = tree:add(PF["secnet.cap"], buf(pos, 4))
+      for _, pf in ipairs(caplist) do cap:add(PF[pf], buf(pos, 4)) end
+      pos = pos + 4
+    end
+    return pos
+  end
+end
+
+local function dissect_mtu(st, buf, tree, pos, sz)
+  -- Dissect an MTU request.
+
+  if pos < sz then tree:add(PF["secnet.mtu"], buf(pos, 2)); pos = pos + 2 end
+  return pos
+end
+
+local function make_dissect_name_xinfo(label, dissect_xinfo, hook)
+  -- Return a dissector function for reading a name and extra information.
+  -- The function will dissect a subtree rooted at the protocol field LABEL;
+  -- it will dissect the extra information using the list DISSECT_XINFO
+  -- (processed using `dissect_sequence'); and finally, if the packet hasn't
+  -- been visited yet, it will call HOOK(ST, NAME), where NAME is the name
+  -- string extracted from the packet.
+
+  return function (st, buf, tree, pos, sz)
+
+    -- Find the length of the whole thing.
+    local len = buf(pos, 2):uint()
+
+    -- Make the subtree root.
+    local sub = tree:add(PF[label], buf(pos, len + 2))
+
+    -- Find the length of the name.  This is rather irritating: I'd like to
+    -- get Wireshark to do this, but it seems that `stringz' doesn't pay
+    -- attention to the buffer limits it's given.  So read the whole lot and
+    -- find the null by hand.
+    local name = buf(pos + 2, len):string()
+    local z, _ = string.find(name, "\0", 1, true)
+    if z == nil then
+      z = len
+    else
+      z = z - 1
+      name = string.sub(name, 1, z)
+    end
+
+    -- Fill in the subtree.
+    sub:add(PF["secnet.namex.len"], buf(pos, 2)); pos = pos + 2
+    sub:add(PF["secnet.namex.name"], buf(pos, z))
+    if z < len then
+      dissect_sequence(dissect_xinfo, st, buf, sub, pos + z + 1, pos + len)
+    end
+
+    -- Maybe call the hook.
+    if hook ~= nil and not st.pinfo.visited then hook(st, name) end
+
+    -- We're done.
+    return pos + len
+  end
+end
+
+local function dissect_sndnonce(st, buf, tree, pos, sz)
+  -- Dissect the sender's nonce.
+
+  tree:add(PF["secnet.kx.sndnonce"], buf(pos, 8)); pos = pos + 8
+  return pos
+end
+
+local function dissect_rcvnonce(st, buf, tree, pos, sz)
+  -- Dissect the recipient's nonce.
+
+  tree:add(PF["secnet.kx.rcvnonce"], buf(pos, 8)); pos = pos + 8
+  return pos
+end
+
+local function dissect_transform(st, buf, tree, pos, sz)
+  -- Dissect the selected transform.  Note this in the packet state for
+  -- later.
+
+  st.transform = buf(pos, 1):uint()
+  tree:add(PF["secnet.kx.transform"], buf(pos, 1)); pos = pos + 1
+  return pos
+end
+
+local function dissect_lenstr(st, buf, tree, label, pos, sz)
+  -- Dissect a simple string given its length.
+  local len = buf(pos, 2):uint()
+  local sub = tree:add(PF[label], buf(pos, len + 2))
+  sub:add(PF[label .. ".len"], buf(pos, 2)); pos = pos + 2
+  sub:add(PF[label .. ".text"], buf(pos, len)); pos = pos + len
+  return pos
+end
+
+local function dissect_dhval(st, buf, tree, pos, sz)
+  -- Dissect a Diffie--Hellman public value.
+
+  return dissect_lenstr(st, buf, tree, "secnet.kx.dhval", pos, sz)
+end
+
+local function dissect_sig(st, buf, tree, pos, sz)
+  -- Dissect a signature.
+
+  return dissect_lenstr(st, buf, tree, "secnet.kx.sig", pos, sz)
+end
+
+local function find_algs_lookup(map, sock, now, ix)
+  -- Utility for `find_algs': look SOCK up in the address map ADDR, to find a
+  -- site; find its peer with index IX; and return the algorithm selection
+  -- current between the pair at time NOW.  If the lookup fails, return nil.
+
+  local name = lookup_timeline(map, sock, now)
+  if name == nil then return nil end
+  local site = SITEMAP[name]
+  if site == nil then return nil end
+  local peername = lookup_timeline(site.index, ix, now)
+  if peername == nil then return nil end
+  return lookup_timeline(site.algs, peername, now)
+end
+
+local function find_algs(st)
+  -- Return the algorithm selection which applies to the packet described in
+  -- ST.
+
+  local now = st.pinfo.rel_ts
+  local sock = snd_sockname(st)
+  local algs = find_algs_lookup(ADDRMAP, sock, now, st.sndix)
+  if algs ~= nil then return algs
+  else return  find_algs_lookup(GUESSMAP, sock, now, st.rcvix)
+  end
+end
+
+-- Transform-specific dissectors...
+local dissect_ct = { }
+function dissect_ct.unknown(st, why, buf, tree, pos, sz)
+  tree:add(PF["secnet.ciphertext.unknown"], buf(pos, sz - pos),
+          "Ciphertext with unknown structure: " .. why)
+  return sz
+end
+function dissect_ct.serpent256cbc(st, buf, tree, pos, sz)
+  tree:add(PF["secnet.ciphertext.iv"], buf(pos, 4)); pos = pos + 4
+  tree:add(PF["secnet.ciphertext.payload"], buf(pos, sz - pos))
+  return sz
+end
+function dissect_ct.eaxserpent(st, buf, tree, pos, sz)
+  local len = sz - pos - 20
+  tree:add(PF["secnet.ciphertext.payload"], buf(pos, len)); pos = pos + len
+  tree:add(PF["secnet.ciphertext.tag"], buf(pos, 16)); pos = pos + 16
+  tree:add(PF["secnet.ciphertext.sequence"], buf(pos, 4)); pos = pos + 4
+  return pos
+end
+
+local function dissect_ciphertext(st, buf, tree, pos, sz)
+  -- Dissect a ciphertext.
+
+  local sub = tree:add(PF["secnet.ciphertext"], buf(pos, sz - pos))
+  local algs = find_algs(st)
+  local xform
+  if algs == nil then xform = nil else xform = algs.transform end
+  if xform == nil then
+    pos = dissect_ct.unknown(st, "unable to find negotiated transform",
+                            buf, sub, pos, sz)
+  else
+    local func = dissect_ct[xform]
+    if func == nil then
+      pos = dissect_ct.unknown(st, "unsupported transform " .. xform,
+                              buf, sub, pos, sz)
+    else
+      pos = func(st, buf, sub, pos, sz)
+    end
+  end
+  return pos
+end
+
+-----------------------------------------------------------------------------
+--- The protocol information table.
+
+local PKTINFO = {
+  -- This is the main table which describes the protocol.  The top level maps
+  -- message labels to structures:
+  --
+  --   * `label' is the category code's symbolic name;
+  --
+  --   * `info' is a prefix for the information column display; and
+  --
+  --   * `dissect' is a sequence of primitive dissectors to run in order to
+  --     parse the rest of the packet.
+
+  [M.NAK] = {
+    label = "NAK",
+    info = "Stimulate fresh key exchange",
+    dissect = { dissect_wtf }
+  },
+  [M.MSG0] = {
+    label = "MSG0",
+    info = "MSG0",
+    dissect = { dissect_ciphertext }
+  },
+  [M.MSG1] = {
+    label = "MSG1",
+    info = "MSG1",
+    dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
+                                       { dissect_caps, dissect_wtf },
+                                       notice_sndname),
+               make_dissect_name_xinfo("secnet.kx.rcvname",
+                                       { dissect_wtf },
+                                       notice_rcvname),
+               dissect_sndnonce,
+               dissect_wtf }
+  },
+  [M.MSG2] = {
+    label = "MSG2",
+    info = "MSG2",
+    dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
+                                       { dissect_caps, dissect_wtf },
+                                       notice_sndname),
+               make_dissect_name_xinfo("secnet.kx.rcvname",
+                                       { dissect_wtf },
+                                       notice_rcvname),
+               dissect_sndnonce, dissect_rcvnonce,
+               dissect_wtf }
+  },
+  [M.MSG3] = {
+    label = "MSG3",
+    info = "MSG3",
+    dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
+                                       { dissect_caps,
+                                         dissect_mtu,
+                                         dissect_wtf },
+                                       notice_sndname),
+               make_dissect_name_xinfo("secnet.kx.rcvname",
+                                       { dissect_wtf },
+                                       notice_rcvname),
+               dissect_sndnonce, dissect_rcvnonce,
+               dissect_wtf },
+    hook = notice_alg_selection
+  },
+  [M.MSG3BIS] = {
+    label = "MSG3BIS",
+    info = "MSG3BIS",
+    dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
+                                       { dissect_caps,
+                                         dissect_mtu,
+                                         dissect_wtf },
+                                       notice_sndname),
+               make_dissect_name_xinfo("secnet.kx.rcvname",
+                                       { dissect_wtf },
+                                       notice_rcvname),
+               dissect_sndnonce, dissect_rcvnonce,
+               dissect_transform,
+               dissect_dhval, dissect_sig,
+               dissect_wtf },
+    hook = notice_alg_selection
+  },
+  [M.MSG4] = {
+    label = "MSG4",
+    info = "MSG4",
+    dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
+                                       { dissect_caps,
+                                         dissect_mtu,
+                                         dissect_wtf },
+                                       notice_sndname),
+               make_dissect_name_xinfo("secnet.kx.rcvname",
+                                       { dissect_wtf },
+                                       notice_rcvname),
+               dissect_sndnonce, dissect_rcvnonce,
+               dissect_dhval, dissect_sig,
+               dissect_wtf }
+  },
+  [M.MSG5] = {
+    label = "MSG5",
+    info = "MSG5",
+    dissect = { dissect_ciphertext }
+  },
+  [M.MSG6] = {
+    label = "MSG6",
+    info = "MSG6",
+    dissect = { dissect_ciphertext }
+  },
+  [M.PROD] = {
+    label = "PROD",
+    info = "PROD",
+    dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
+                                       { dissect_caps,
+                                         dissect_wtf },
+                                       notice_sndname),
+               make_dissect_name_xinfo("secnet.kx.rcvname",
+                                       { dissect_wtf },
+                                       notice_rcvname),
+               dissect_wtf }
+  },
+}
+
+do
+  -- Work through the master table and build the `msgtab'' table, mapping
+  -- message codes to their symbolic names for presentation.
+  local msgtab = { }
+  for i, v in pairs(PKTINFO) do msgtab[i] = v.label end
+
+  local capmap = { transform = { }, early = { } }
+  for i, v in pairs(CAPTAB) do capmap[v.kind][i] = v.desc end
+
+  local ftab = {
+    -- The protocol fields.  This table maps the field names to structures
+    -- used to build the fields, which are then stored in `PF' (declared way
+    -- above):
+    --
+    --   * `name' is the field name to show in the dissector tree view;
+    --
+    --   * `type' is the field type;
+    --
+    --   * `base' is a tweak describing how the field should be formatted;
+    --
+    --   * `mask' is used to single out a piece of a larger bitfield;
+    --
+    --   * `tab' names a mapping table used to convert numerical values to
+    --     symbolic names; and
+    --
+    --   * `hook' is a hook function to run the first time we see a packet,
+    --     to keep track of things.
+
+    ["secnet.hdr"] = {
+      name = "Common message header", type = ftypes.NONE
+    },
+    ["secnet.hdr.rcvix"] = {
+      name = "Recipient's site index for sender",
+      type = ftypes.UINT32, base = base.DEC
+    },
+    ["secnet.hdr.sndix"] = {
+      name = "Sender's site index for recipient",
+      type = ftypes.UINT32, base = base.DEC
+    },
+    ["secnet.hdr.label"] = {
+      name = "Message label", type = ftypes.UINT32,
+      base = base.HEX, tab = msgtab
+    },
+    ["secnet.kx.sndname"] = {
+      name = "Sender's site name and extended information",
+      type = ftypes.NONE
+    },
+    ["secnet.kx.rcvname"] = {
+      name = "Recipient's site name and extended information",
+      type = ftypes.NONE
+    },
+    ["secnet.namex.len"] = {
+      name = "Name/extended info length",
+      type = ftypes.UINT16, base = base.DEC
+    },
+    ["secnet.namex.name"] = {
+      name = "Site name", type = ftypes.STRING,
+      field = true, base = base.ASCII,
+    },
+    ["secnet.cap"] = {
+      name = "Advertised capability bits",
+      type = ftypes.UINT32, base = base.HEX
+    },
+    ["secnet.cap.user"] = {
+      name = "User-assigned capability bits",
+      type = ftypes.UINT32, mask = 0x000000ff, base = base.HEX
+    },
+    ["secnet.mtu"] = {
+      name = "Sender's requested MTU", type = ftypes.UINT16, base = base.DEC
+    },
+    ["secnet.kx.sndnonce"] = {
+      name = "Sender's nonce", type = ftypes.BYTES, base = base.SPACE
+    },
+    ["secnet.kx.rcvnonce"] = {
+      name = "Recipient's nonce", type = ftypes.BYTES, base = base.SPACE
+    },
+    ["secnet.kx.transform"] = {
+      name = "Selected bulk-crypto transform", type = ftypes.UINT8,
+      base = base.DEC, tab = capmap.transform
+    },
+    ["secnet.kx.dhval"] = {
+      name = "Sender's public Diffie--Hellman value", type = ftypes.NONE
+    },
+    ["secnet.kx.dhval.len"] = {
+      name = "Sender's public Diffie--Hellman length",
+      type = ftypes.UINT16, base = base.DEC
+    },
+    ["secnet.kx.dhval.text"] = {
+      name = "Sender's public Diffie--Hellman text", type = ftypes.STRING,
+      base = base.ASCII
+    },
+    ["secnet.kx.sig"] = {
+      name = "Sender's signature", type = ftypes.NONE
+    },
+    ["secnet.kx.sig.len"] = {
+      name = "Sender's signature length",
+      type = ftypes.UINT16, base = base.DEC
+    },
+    ["secnet.kx.sig.text"] = {
+      name = "Sender's signature text", type = ftypes.STRING,
+      base = base.ASCII
+    },
+    ["secnet.ciphertext"] = {
+      name = "Encrypted data", type = ftypes.NONE
+    },
+    ["secnet.ciphertext.unknown"] = {
+      name = "Ciphertext with unknown structure",
+      type = ftypes.BYTES, base = base.SPACE
+    },
+    ["secnet.ciphertext.iv"] = {
+      name = "Initialization vector", type = ftypes.BYTES, base = base.SPACE
+    },
+    ["secnet.ciphertext.sequence"] = {
+      name = "Sequence number", type = ftypes.UINT32, base = base.DEC
+    },
+    ["secnet.ciphertext.payload"] = {
+      name = "Encrypted payload", type = ftypes.BYTES, base = base.SPACE
+    },
+    ["secnet.ciphertext.tag"] = {
+      name = "Authentication tag", type = ftypes.BYTES, base = base.SPACE
+    },
+    ["secnet.wtf"] = {
+      name = "Unexpected trailing data",
+      type = ftypes.BYTES, base = base.SPACE
+    }
+  }
+
+  -- Add the remaining capability fields.  Calculate the unassigned mask
+  -- based on the assigned bits.
+  local unasgn = 0x7fff7f00
+  for i, v in pairs(CAPTAB) do
+    local flag = bit.lshift(1, i)
+    ftab["secnet.cap." .. v.name] = {
+      name = v.desc, type = ftypes.BOOLEAN,
+      mask = flag, base = 32
+    }
+    unasgn = bit.band(unasgn, bit.bnot(flag))
+  end
+  ftab["secnet.cap.unassigned"] = {
+    name = "Unassigned capability bits",
+    type = ftypes.UINT32, mask = unasgn, base = base.HEX
+  }
+
+  -- Convert this table into the protocol fields, and populate `PF'.
+  local ff = { }
+  local i = 1
+
+  -- Figure out whether we can use `none' fields (see below).
+  local use_none_p = rawget(ProtoField, 'none') ~= nil
+  for abbr, args in pairs(ftab) do
+
+    -- An annoying hack.  Older versions of Wireshark don't allow setting
+    -- fields with type `none', which is a shame because they're ideal as
+    -- internal tree nodes.
+    ty = args.type
+    b = args.base
+    if ty == ftypes.NONE then
+      if use_none_p then
+       b = base.NONE
+      else
+       ty = ftypes.BYTES
+       b = base.SPACE
+      end
+    end
+
+    -- Go make the field.
+    local f = ProtoField.new(args.name, abbr, ty,
+                            args.tab, b, args.mask, args.descr)
+    PF[abbr] = f
+    ff[i] = f; i = i + 1
+  end
+  secnet.fields = PF
+
+  -- Make readable fields corresponding to especially interesting protocol
+  -- fields.
+  for abbr, args in pairs(ftab) do
+    if args.field then F[abbr] = Field.new(abbr) end
+  end
+end
+
+-----------------------------------------------------------------------------
+--- The main dissector.
+
+function secnet.dissector(buf, pinfo, tree)
+
+  -- Fill in the obvious stuff.
+  pinfo.cols.protocol = "Secnet"
+
+  local sz = buf:reported_length_remaining()
+  local sub = tree:add(secnet, buf(0, sz), "Secnet packet")
+  local p = 12
+
+  -- Decode the message header.
+  hdr = sub:add(PF["secnet.hdr"], buf(0, 12))
+  local rcvix = buf(0, 4):uint(); hdr:add(PF["secnet.hdr.rcvix"], buf(0, 4))
+  local sndix = buf(4, 4):uint(); hdr:add(PF["secnet.hdr.sndix"], buf(4, 4))
+  local label = buf(8, 4):uint()
+  hdr:add(PF["secnet.hdr.label"], buf(8, 4), label,
+         string.format("Message label (major = 0x%04x, minor = 0x%04x)",
+                       msgmajor(label), msgminor(label)))
+  local st = { pinfo = pinfo, label = label, rcvix = rcvix, sndix = sndix  }
+  local info = PKTINFO[label]
+
+  -- Dispatch using the master protocol table.
+  if info == nil then
+    pinfo.cols.info = string.format("Unknown message label 0x%08x", label)
+  else
+    pinfo.cols.info = info.info
+    p = dissect_sequence(info.dissect, st, buf, sub, p, sz)
+  end
+
+  -- Invoke the hook if necessary.
+  if not pinfo.visited and info.hook ~= nil then info.hook(st) end
+
+  -- Return the final position we reached.
+  return p
+end
+
+-- We're done.  Register the dissector.
+DissectorTable.get("udp.port"):add(410, secnet)
+
+-------- That's all, folks --------------------------------------------------
diff --git a/secnet.8 b/secnet.8
new file mode 100644 (file)
index 0000000..670bff7
--- /dev/null
+++ b/secnet.8
@@ -0,0 +1,858 @@
+.\" Man page for secnet.
+.\"
+.\" See the secnet.git README, or the Debian copyright file, for full
+.\" list of copyright holders.
+.\"
+.\" secnet is free software; you can redistribute it and/or modify it
+.\" under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 3 of the License, or
+.\" (at your option) any later version.
+.\" 
+.\" secnet 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.  See the GNU
+.\" General Public License for more details.
+.\" 
+.\" You should have received a copy of the GNU General Public License
+.\" version 3 along with secnet; if not, see
+.\" https://www.gnu.org/licenses/gpl.html.
+.TH secnet 8
+
+.SH NAME
+secnet \- VPN router daemon
+
+.SH SYNOPSIS
+\fBsecnet\fR [\fIOPTIONS\fR]
+
+.SH DESCRIPTION
+\fBsecnet\fR allows virtual private networks to be constructed
+spanning multiple separate sites.
+
+.SH OPTIONS
+.TP
+.B --verbose\fR, \fB-v
+Enable extra diagnostics.
+.TP
+.B --nowarnings\fR, \fB-w
+Suppress warnings.
+.TP
+.B --help
+Display usage message.
+.TP
+.B --version
+Display version string.
+.TP
+.B --nodetach\fR, \fB-n
+Don't go into background.
+The default behaviour is to become a daemon during startup.
+.TP
+.B --silent\fR, \fB--quiet\fR, \fB-f
+Suppress error messages.
+.TP
+.B --debug\fR, \fB-d
+Enable debug messages.
+.TP
+.B --config\fR, \fB-c \fIPATH
+Specify configuration file.
+The default is \fI/etc/secnet/secnet.conf\fR.
+.TP
+.B --just-check-config\fR, \fB-j
+Check configuration and exit.
+.TP
+.B --sites-key\fR, \fB-s \fIKEY
+Configuration file key defining active sites.
+The default is \fBsites\fR.
+
+.SH "CAPABILITY NEGOTIATION"
+Sites negotiate with each other during key exchange
+in order to determine which cryptographic algorithms and other features
+\(en termed
+.I capabilities
+\(en
+they each support.
+Capabilities are assigned small integer numbers.
+In many cases,
+capability numbers can be assigned in the configuration file,
+as described below;
+but secnet's default assignments will often be satisfactory.
+.PP
+Capability numbers between 0 and 7 inclusive
+are reserved for local use:
+secnet will never make use of them without explicit configuration.
+This may be useful to migrate from one set of parameters
+for a particular cryptographic algorithm
+to different, incompatible, parameters for the same algorithm.
+Other capability numbers are assigned by default
+by various kinds of closures.
+See the descriptions below for details.
+.PP
+It is essential that a capability number mean the same thing
+to each of a pair of peers.
+It's possible to configure a site
+so that it uses different capability numbers for the same feature
+when it communicates with different peer sites,
+but this is likely to be more confusing than useful.
+
+.SH "CONFIGURATION FILE"
+.SS Overview
+The default configuration file is \fI/etc/secnet/secnet.conf\fR.
+This can be overridden with the \fB--config\fR option.
+.PP
+The configuration file defines a dictionary (a mapping from keys to
+values) of configuration information for secnet.
+It is recursive in nature, i.e. values may themselves include dictionaries.
+Any node in the nested structure thus defined can be identified by a
+\fIpath\fR, which is the sequence of keys necessary to reach it from
+the root, separated by "/" characters.
+See \fBPaths\fR below for how this is used.
+.PP
+Furthermore, when a key is looked up in a dictionary, if it cannot be
+found, it is sought in the parent dictionary, and so on back to the
+root.
+For instance, each \fIsite\fR must contain the \fBresolver\fR key, but
+in a typical configuration there is no value in having different
+resolvers for each site.
+Therefore \fBresolver\fR is defined at the root and thus automatically
+incorporated into all sites.
+.SS Whitespace
+Whitespace, including newlines, is ignored except to the extent that
+it bounds other symbols.
+.PP
+Comment begin with "#" and continues to the end of the line.
+Comments are ignored.
+.SS Inclusion
+A file may be recursively included into the configuration file using a
+line of the form:
+.IP
+\fBinclude \fIPATH
+.PP
+This is handled at a higher level than the main parser and so
+precludes the possibility of using the string \fBinclude\fR for any
+other purpose.
+.\" check if this is true.  it's probably a bug!
+.SS Assignments
+The configuration file contains one or more assigments.
+Each assignment is written:
+.IP
+\fIkey\fR [\fB=\fR] \fIlist\fR\fB;\fR
+.PP
+i.e. the equals sign is optional.
+The semicolon is mandatory in all contexts.
+.PP
+Keys start with a letter or "_" and continue with any numbers of
+letters, digits, "_" and "-".
+.PP
+Each \fIkey\fR is a list of one or more \fIvalues\fR, separated by commas.
+Possible values types are \fIboolean\fR, \fIstring\fR, \fInumber\fR,
+\fIdictionary\fR, \fIpath\fR and \fIclosure evaluation\fR.
+.\" This man page draws a distinction between a closure (the thing
+.\" evaluated) and a closure evaluation (the closure plus is
+.\" arguments).
+.SS "Strings"
+Strings are contained within "double quotes".
+There is (currently) no escape syntax and no way to include quotes
+inside strings.
+.PP
+Example:
+.nf
+        filename "/var/log/secnet";
+.fi
+.SS "Numbers"
+Numbers are encoded in decimal and do not include a sign.
+Numbers must lie in the range 0 to 4294967295.
+.PP
+Example:
+.nf
+        mtu 1400;
+.fi
+.SS "Dictionaries"
+.\" In conffile.y dictionaries can be preceded by a search path, but
+.\" this is not implemented elsewhere, so not documented here.
+Dictionaries consist of one or more assignments, in the same syntax as
+given above, enclosed in "{" and "}".
+.PP
+Example:
+.nf
+        system {
+                userid "secnet";
+                pidfile "/var/run/secnet.pid";
+        };
+.fi
+.SS "Paths"
+Paths allow a key already defined in the configuration to be aliased.
+.PP
+Paths consist of a sequence of keys separated by "/".
+If the path starts with a "/" then it is an \fIabsolute path\fR and
+the search starts at the root of the configuration.
+Otherwise it is a \fIrelative path\fR and starts in the containing
+dictionary or in any of its parents, down to and including the root.
+If there is more than one match, the one furthest from the root "wins".
+.PP
+The value of a path is the list assigned to the key it refers to.
+Lists are flattened; for example if a key is defined as a list of two
+paths, and each of those refers to a list of two integers, the
+original key is therefore defined to be a list of four integers, not
+a list consisting of two lists.
+.PP
+It is not possible to refer to a \fIlater\fR key using a path.
+.PP
+Example:
+.nf
+        vpn {
+          test {
+            kakajou vpn-data/test/kakajou/kakajou;
+            araminta vpn-data/test/araminta/araminta;
+            deodand vpn-data/test/deodand/deodand;
+            all-sites kakajou,araminta,deodand;
+          };
+        };
+        all-sites vpn/test/all-sites;
+.fi
+.PP
+Here, each of \fBvpn/test/kakajou\fR, \fBvpn/test/araminta\fR and
+\fBvpn/test/deodand\fR are defined as aliases to values defined
+elsewhere.
+\fBvpn/tests/all-sites\fR is defined as the list of all three of those
+values, and \fBall-sites\fR is then defined to be an alias for that.
+.SS "Booleans"
+The (single-element) paths \fBfalse\fR, \fBno\fR and \fBnowise\fR are
+predefined and refer to a boolean false value.
+Similarly \fBtrue\fR, \fByes\fR and \fBverily\fR point at a boolean
+true value.
+.PP
+In all six cases, variants with just the first letter capitalized, and
+with all letters capitalized, are also provided.
+.PP
+Example:
+.nf
+        random randomfile("/dev/urandom",no);
+.fi
+.SS "Closure Evaluation"
+Closure evaluation uses the following syntax:
+.IP
+\fICLOSURE \fB( \fIARGUMENTS \fB)
+.PP
+\fICLOSURE\fR may be a path referring to a closure, or may itself be a
+closure evaluation.
+.PP
+\fIARGUMENTS\fR is a list of zero or more values, separated by commas.
+As a shortcut, if the arguments consist of a single dictionary, the
+parentheses may be ommitted:
+.IP
+\fICLOSURE \fB{ \fR... \fB}
+.PP
+Example:
+.nf
+        sites map(site, vpn/test/all-sites);
+.fi
+.PP
+When a closure is evaluated it returns a value (a list, much as above)
+and may also have side effects (which may be immediate or may be
+deferred to some later phase of execution).
+A list of built-in closures is given below.
+.SS "Mandatory Keys"
+Two keys are mandatory.
+\fBsystem\fR must be a dictionary in which the following keys can be
+looked up:
+.TP
+.B log
+A \fIlog closure\fR; see the \fBlogfile\fR documentation below.
+The destination for log messages.
+Mandatory.
+.TP
+.B userid
+A string.
+The userid to run as after dropping privilege.
+Optional.
+.TP
+.B pidfile
+A string.
+The path to write a pidfile.
+Optional.
+.PP
+\fBsites\fR should be a list of \fIsite closures\fR; see the \fBsite\fR documentation below.
+This defines the collection of tunnel endpoints that \fBsecnet\fR will
+communicate with.
+.PP
+Recall the recursive lookup logic described in \fBOverview\fR above:
+if (for instance) \fBlog\fR is defined in the top level dictionary but
+not in \fBsystem\fR, it will nevertheless be found when looked up in
+the latter.
+
+.SH CLOSURES
+\fBsecnet\fR contains a collection of built-in closures
+with names (i.e. single-element paths) given below.
+.PP
+Most of them return anonymous closures of various types,
+which are described contextually.
+
+.SS adns
+\fBadns(\fIDICT\fB)\fR => \fIresolver closure\fR
+.TP
+.I DICT
+This either be empty or contain the single key \fBconfig\fR, with a
+string value giving configuration to supply to ADNS.
+This might be read from a file using \fBreadfile\fR.
+.PP
+A \fIresolver closure\fR is a means of converting hostnames into
+network addresses.
+
+.SS diffie-hellman
+.PP
+\fBdiffie-hellman(\fIMODULUS\fB, \fIGENERATOR\fR[\fB, \fICHECK\fR]\fB)\fR => \fIdh closure\fR
+.TP
+.I MODULUS
+String.
+The prime modulus \fIp\fR in hex.
+.TP
+.I GENERATOR
+String.
+The generator \fIg\fR in hex.
+.TP
+.I CHECK
+Boolean.
+If \fBtrue\fR (the default) then check if \fIp\fR is prime.
+.PP
+A \fIdh closure\fR defines a group to be used for key exchange.
+
+.SS logfile
+\fBlogfile(\fIDICT\fB)\fR => \fIlog closure\fR
+.PP
+Valid keys in the \fIDICT\fR argument are:
+.TP
+.B filename
+The path to log to.
+.TP
+.B class
+A list of strings defining which classes of message to log.
+The possible message classes are \fBdebug-config\fR,
+\fBdebug-phase\fR, \fBdebug\fR, \fBinfo\fR, \fBnotice\fR,
+\fBwarning\fR, \fBerror\fR, \fBsecurity\fR and \fBfatal\fR.
+.IP
+\fBall-debug\fR is the union of all the \fBdebug\fR... classes.
+\fBdefault\fR is equivalent to \fBwarning, error, security, fatal\fR.
+\fBverbose\fR is equivalent to \fBinfo, notice, warning, error,
+security, fatal\fR.
+\fBquiet\fR is equivalent to \fBfatal\fR.
+.PP
+A \fIlog closure\fR is a means of saving log messages.
+See also \fBsyslog\fR below.
+
+.SS makelist
+\fBmakelist(\fIDICT\fB)\fR => \fILIST\fR
+.PP
+Returns the (flattened) list of values from the dictionary, discarding
+the keys.
+
+.SS map
+\fBmap(\fICLOSURE\fB, \fIINPUT\fR...\fB)\fR => \fILIST\fR
+.PP
+Applies \fICLOSURE\fR to all its additional input arguments and
+returns the resulting list.
+
+.SS md5
+\fBmd5\fR is a \fIhash closure\fR implementing the MD5 algorithm.
+
+.SS null-netlink
+\fBnull-netlink(\fIDICT\fB)\fR => \fInetlink closure\fR
+.br
+\fBnull-netlink(\fIDICT\fB)\fR => \fIpure closure\fR
+.\" TODO pure closure is what it's called internally but this is a
+.\" very opaque name to use in docs
+.PP
+Valid keys in the \fIDICT\fR argument are:
+.TP
+.B name
+String.
+The name for the netlink device.
+The default is \fBnull-netlink\fR.
+.TP
+.B networks
+List of strings.
+The networks on the host side of the netlink device.
+.TP
+.B remote-networks
+List of strings.
+Networks that may be claimed by remote sites using this netlink device.
+.TP
+.B secnet-address
+String.
+IP address of this netlink.
+Incompatible with \fBptp-address\fR.
+.TP
+.B ptp-address
+String.
+IP address of the other end of a point-to-point link.
+Incompatible with \fBsecnet-address\fR.
+.TP
+.B mtu
+Number.
+The MTU of the netlink device.
+The default is 1000.
+.PP
+If \fBptp-address\fR is used then the result is a \fInetlink closure\fR.
+This can be used directly with the \fBlink\fR key in the \fBsites\fR
+closure (see below).
+.PP
+If \fBsecnet-address\fR is used then the result is a \fIpure
+closure\fR.
+This must be evaluated to yield a \fInetlink closure\fR, using a
+dictionary argument with the following keys:
+.TP
+.B routes
+String list.
+networks reachable via this tunnel, in \fIaddress\fB/\fIbits\fR format.
+.TP
+.B options
+String list.
+A list of options:
+.RS
+.TP
+.B allow-route
+Allow packets received via this tunnel to be routed down other tunnels
+(without this option only packets from the host will be routed).
+.TP
+.B soft
+Remove these routes from the host routing table when the link quality
+is 0.
+.RE
+.TP
+.B mtu
+Number.
+Default MTU over this link.
+The default is inherited from the \fIpure closure\fR.
+.TP
+.B priority
+Number.
+The priority of this link.
+Higher values beat lower values.
+The default is 0.
+
+.\" TODO ptp-address turns up in sites.conf, but why?  I think this
+.\" is a bug in make-secnet-sites; it is not used by
+ \" netlink_inst_create.
+
+.PP
+A \fInetlink closure\fR is a virtual IP link, and is supplied to the
+\fBlink\fR key of a \fIsite\fR closure.
+.PP
+The netlink created by \fBnull-netlink\fR has no connection to the
+host.
+See \fBtun\fR and \fBuserv-ipif\fR below for more useful alternatives.
+
+
+
+.SS randomfile
+\fBrandomfile(\fIFILENAME\fR[\fB, \fIBLOCKING\fR]\fB)\fR => \fIrandomsource closure\fR
+.TP
+.I FILENAME
+String.
+Path to random device, e.g. \fI/dev/urandom\fR.
+.TP
+.I BLOCKING
+Boolean.
+\fBTrue\fR if this is a blocking device and \fBfalse\fR otherwise (the default).
+Blocking device support is not implemented so this must always be
+\fBFalse\fR or absent.
+.PP
+A \fIrandomsource closure\fR is a source of random numbers.
+
+.SS readfile
+\fBreadfile(\fIPATH\fB)\fR => \fISTRING\fR
+.PP
+Read the contents of the file \fIPATH\fR (a string) and return it as a string.
+
+.SS eax-serpent
+\fBeax-serpent(\fIDICT\fB)\fR => \fItransform closure\fR
+.PP
+Valid keys in the \fIDICT\fR argument are:
+.TP
+.B max-sequence-skew
+The maximum acceptable difference between the sequence number in a
+received, decrypted message and the previous one.
+The default is 10.
+It may be necessary to increase this is if connectivity is poor.
+.TP
+.B tag-length-bytes
+The length of the message authentication tag.  The default is 16,
+for a 128-bit tag length.  It must be no longer than the Serpent
+blocksize, 16.  Must be have the same value at both ends.
+.TP
+.B padding-rounding
+Messages are padded to a multiple of this many bytes.  This
+serves to obscure the exact length of messages.  The default is 16,
+.TP
+.B capab-num
+The capability number to use when advertising this
+transform.  The default for serpent-eax is 9.
+.PP
+A \fItransform closure\fR is a reversible means of transforming
+messages for transmission over a (presumably) insecure network.
+It is responsible for both confidentiality and integrity.
+
+.SS serpent256-cbc
+\fBserpent256-cbc(\fIDICT\fB)\fR => \fItransform closure\fR
+.PP
+This transform
+is deprecated as its security properties are poor; it should be
+specified only alongside a better transform such as eax-serpent.
+.PP
+Valid keys in the \fIDICT\fR argument are:
+.TP
+.B capab-num
+As above.  The default for serpent256-cbc is 8.
+.TP
+.B max-sequence-skew
+As above.
+.PP
+Note that this uses a big-endian variant of the Serpent block cipher
+(which is not compatible with most other Serpent implementations).
+.SS rsa-private
+\fBrsa-private(\fIPATH\fB\fR[, \fICHECK\fR]\fB)\fR => \fIrsaprivkey closure\fR
+.TP
+.I PATH
+String.
+The path to a file containing an RSA private key in SSH format
+(version 1).
+There must be no passphrase.
+.TP
+.I CHECK
+Boolean.
+If \fBtrue\fR (the default) then check that the key is valid.
+
+.SS rsa-public
+\fBrsa-public(\fIKEY\fB, \fIMODULUS\fB)\fR => \fIrsapubkey closure\fR
+.TP
+.I KEY
+String.
+The public key exponent (\fIe\fR), in decimal.
+.TP
+.I MODULUS
+String.
+The modulus (\fIn\fR), in decimal.
+
+.SS sha1
+\fBsha1\fR is a \fIhash closure\fR implementing the SHA-1 algorithm.
+
+.SS site
+\fBsite(\fIDICT\fB)\fR => \fIsite closure\fR
+.PP
+Valid keys in the \fIDICT\fR argument are:
+.TP
+.B local-name
+String.
+The site's name for itself.
+.TP
+.B name
+String.
+The name of the site's peer.
+.TP
+.B link
+A \fInetlink closure\fR.
+.TP
+.B comm
+A \fIcomm closure\fR.
+.TP
+.B resolver
+A \fIresolver closure\fR.
+.TP
+.B random
+A \fIrandomsource closure\fR.
+.TP
+.B local-key
+An \fIrsaprivkey closure\fR.
+The key used to prove our identity to the peer.
+.TP
+.B address
+String.
+The DNS name of the peer.
+Optional, but if it is missing then it will not be possible to
+initiate new connections to the peer.
+.TP
+.B port
+Number.
+The port to contact the peer.
+.TP
+.B key
+An \fIrsapubkey closure\fR.
+The key used to verify the peer's identity.
+.TP
+.B transform
+One or more \fItransform closures\fR.
+Used to protect packets exchanged with the peer.  These should
+all have distinct \fBcapab-num\fR values, and the same \fBcapab-num\fR
+value should have the same (or a compatible) meaning at both
+ends.  The list should be in order of preference, most preferred
+first.  (The end which sends MSG1,MSG3 ends up choosing; the ordering
+at the other end is irrelevant.)
+.TP
+.B dh
+A \fIdh closure\fR.
+The group to use in key exchange.
+.TP
+.B hash
+The hash function used during setup.
+.\" TODO clarify what we actually use it for!
+.TP
+.B key-lifetime
+Number.
+The maximum lifetime of a session key in milliseconds.
+The default is one hour.
+.TP
+.B setup-retries
+Number.
+The maximum number of times a key negotiation packet will be
+transmitted before giving up.
+The default is 5.
+.TP
+.B setup-timeout
+Number.
+The time between retransmissions of key negotiation packets, in milliseconds.
+The default is one second.
+.TP
+.B wait-time
+Number.
+The time to wait after a failed key setup before making another
+attempt, in milliseconds.
+The default is 20s.
+.TP
+.B renegotiate-time
+Number.
+The time after which a new session key will be negotiated, \fIif\fR
+there is traffic on the link, in milliseconds.
+It must not be greater than the \fBkey-lifetime\fR.
+The default 5 minutes less than the key lifetime, unless the lifetime
+is less than 10 minutes in which case the default is half the
+lifetime.
+.TP
+.B keepalive
+Boolean.
+If \fBtrue\fR then attempt to always maintain a live session key.
+Not implemented.
+.TP
+.B log-events
+String list.
+Types of event to log for this site.
+.RS
+.TP
+.B unexpected
+Unexpected key setup packets (including late retransmissions).
+.TP
+.B setup-init
+Start of attempt to setup a session key.
+.TP
+.B setup-timeout
+Failure of attempt to setup a session key, through timeout.
+.TP
+.B activate-key
+Activation of a new session key.
+.TP
+.B timeout-key
+Deletion of current session key through age.
+.TP
+.B security
+Anything potentially suspicious.
+.TP
+.B state-change
+Steps in the key setup protocol.
+.TP
+.B packet-drop
+Whenever we throw away an outgoing packet.
+.TP
+.B dump-packets
+Every key setup packet we see.
+.TP
+.B errors
+Failure of name resolution, internal errors.
+.TP
+.B all
+Everything (too much!)
+.RE
+.PP
+A \fIsite closure\fR defines one site to communicate with.
+\fBsecnet\fR expects the (root) key \fBsite\fR to be a list of site
+closures.
+
+.SS sysbuffer
+\fBsysbuffer(\fR[\fISIZE\fR[\fB, \fIOPTIONS\fR]]\fB)\fR => \fIbuffer closure\fR
+.TP
+.I SIZE
+Number.
+The size of the buffer in bytes.
+This must be between 64 and 131072.
+The default is 4096.
+.TP
+.I OPTIONS
+Dictionary.
+Optional and presently unused.
+.\" lockdown is accepted but ignored.
+.PP
+A \fIbuffer closure\fR is a means of buffering packets to send or that
+have been received.
+
+.SS syslog
+\fBsyslog(\fIDICT\fB)\fR => \fIlog closure\fR
+.PP
+Valid keys in the \fIDICT\fR argument are:
+.TP
+.B ident
+String.
+The ident string to pass to \fBopenlog\fR(3); this value will appear
+in each message.
+.TP
+.B facility
+String.
+The facility to log as.
+The possible values are \fBauthpriv\fR, \fBcron\fR, \fBdaemon\fR,
+\fBkern\fR, \fBlocal0\fR-\fB7\fR, \fBlpr\fR, \fBmail\fR, \fBnews\fR,
+\fBsyslog\fR, \fBuser\fR and \fBuucp\fR.
+.PP
+See also \fBlogfile\fR above.
+
+.SS tun
+\fBtun(\fIDICT\fB)\fR => \fInetlink closure\fR
+.br
+\fBtun(\fIDICT\fB)\fR => \fIpure closure\fR
+.PP
+Valid keys in the \fIDICT\fR argument are those documented for
+\fBnull-netlink\fR above, plus:
+.TP
+.B flavour
+String.
+The type of TUN interface to use.
+Possible values are \fBlinux\fR, \fBbsd\fR, \fBstreams\fR and \fBguess\fR.
+The default is \fBguess\fR.
+.TP
+.B device
+String.
+The path to the TUN/TAP device file.
+The default is \fI/dev/net/tun\fR for the \fBlinux\fR flavour and
+\fI/dev/tun\fR for the others.
+.TP
+.B interface
+String.
+The interface to use.
+The default is to pick one automatically.
+This cannot be used with the \fBstreams\fR flavour.
+.TP
+.B local-address
+String.
+IP address of the host's tunnel interface.
+.\" README says this belongs to netlink-null but actually it's
+ \" duplicated between slip & tun
+.TP
+.B ifconfig-path
+String.
+The name of the \fBifconfig\fR command.
+The default is simply "ifconfig".
+.TP
+.B route-path
+String.
+The name of the \fBroute\fR command.
+The default is simply "route".
+.TP
+.B ifconfig-type
+String.
+The syntax expected by the \fBifconfig\fR command.
+Possible values are \fBlinux\fR, \fBbsd\fR, \fBioctl\fR,
+\fBsolaris-2.5\fR and \fBguess\fR.
+The default is \fBguess\fR.
+.TP
+.B route-type
+String.
+The syntax expected by the \fBifconfig\fR command.
+Possible values are \fBlinux\fR, \fBbsd\fR, \fBioctl\fR,
+\fBsolaris-2.5\fR and \fBguess\fR.
+The default is \fBguess\fR.
+.TP
+.B buffer
+A \fIbuffer closure\fR to use for packets transferred from the host to secnet.
+The buffer size must be at least 60 greater than the MTU.
+.\" TODO rumour has is that buffers are sometimes shareable between
+.\" netlink devices - document that if the conditions are reasonable
+.\" ones.
+.PP
+The \fBifconfig-type\fR and \fBroute-type\fR values determine how
+those commands are executed.
+If they are set to \fBioctl\fR then low-level system calls are used
+directly instead of invoking the commands.
+.PP
+The netlink created by \fBtun\fR uses the \fBtun\fR device to
+communicate with the host kernel.
+
+.SS udp
+\fBudp(\fIDICT\fB)\fR => \fIcomm closure\fR
+.PP
+Valid keys in the \fIDICT\fR argument are:
+.TP
+.B address
+String.
+The IP address to bind on.
+The default is 0.0.0.0, i.e. "any".
+.TP
+.B port
+Number.
+The port number to bind to.
+The default is 0, i.e. the OS will choose one.
+It is suggested that any given VPN agree a common port number.
+.TP
+.B buffer
+A \fIbuffer closure\fR.
+See the \fBsysbuffer\fR closure above.
+.TP
+.B authbind
+String.
+The path to a helper program to bind the socket.
+Optional.
+.IP
+The program will be invoked with the address and port number as its
+arguments, and with the socket to bind as file descriptor 0.
+It should either bind the socket as requested, or exit with nonzero
+status.
+.PP
+A \fIcomm closure\fR is a means of sending and receiving messages via
+a network.
+It does not provide confidentiality, reliablity or availability.
+
+.SS userv-ipif
+\fBuserv-ipif(\fIDICT\fB)\fR => \fInetlink closure\fR
+.br
+\fBuserv-ipif(\fIDICT\fB)\fR => \fIpure closure\fR
+.PP
+Valid keys in the \fIDICT\fR argument are those documented for
+\fBnull-netlink\fR above, plus:
+.TP
+.B local-address
+String.
+IP address of the host's SLIP interface.
+.\" README says this belongs to netlink-null but actually it's
+ \" duplicated between SLIP & tun
+.TP
+.B userv-path
+String.
+Where to find \fBuserv\fR(1).
+The default is \fB"userv"\fR.
+.TP
+.B service-user
+String.
+The name of the user that owns the service.
+The default is \fB"root"\fR.
+.TP
+.B service-name
+String.
+The name of the service to request.
+The default is \fB"ipif"\fR.
+.TP
+.B buffer
+A \fIbuffer closure\fR to use for packets transferred from the host to secnet.
+.PP
+The netlink created by \fBuserv-ipif\fR invokes the specified \fBuserv\fR service with pipes connected to its standard input and output.
+It uses SLIP to communicate with the host kernel via these pipes.
+
+.SH FILES
+.TP
+.I /etc/secnet/secnet.conf
+Configuration file.
+
+.SH "SEE ALSO"
+\fBuserv\fR(1)
diff --git a/secnet.c b/secnet.c
new file mode 100644 (file)
index 0000000..c14a835
--- /dev/null
+++ b/secnet.c
@@ -0,0 +1,533 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "util.h"
+#include "conffile.h"
+#include "process.h"
+
+#if __APPLE__
+/* apple's poll() does not work on char devs */
+# define USE_SELECT 1
+#endif
+
+/* XXX should be from autoconf */
+static const char *configfile="/etc/secnet/secnet.conf";
+static const char *sites_key="sites";
+bool_t just_check_config=False;
+static char *userid=NULL;
+static uid_t uid=0;
+static gid_t gid;
+bool_t background=True;
+static char *pidfile=NULL;
+bool_t require_root_privileges=False;
+cstring_t require_root_privileges_explanation=NULL;
+
+static pid_t secnet_pid;
+
+/* Structures dealing with poll() call */
+struct poll_interest {
+    beforepoll_fn *before; /* 0 if deregistered and waiting to be deleted */
+    afterpoll_fn *after;
+    void *state;
+    int32_t nfds;
+    cstring_t desc;
+    LIST_ENTRY(poll_interest) entry;
+};
+static LIST_HEAD(, poll_interest) reg = LIST_HEAD_INITIALIZER(&reg);
+
+static bool_t interest_isregistered(const struct poll_interest *i)
+{
+    return !!i->before;
+}
+
+static bool_t finished=False;
+
+/* Parse the command line options */
+static void parse_options(int argc, char **argv)
+{
+    int c;
+
+    while (True) {
+       int option_index = 0;
+       static struct option long_options[] = {
+           {"verbose", 0, 0, 'v'},
+           {"nowarnings", 0, 0, 'w'},
+           {"help", 0, 0, 2},
+           {"version", 0, 0, 1},
+           {"nodetach", 0, 0, 'n'},
+           {"managed", 0, 0, 'm'},
+           {"silent", 0, 0, 'f'},
+           {"quiet", 0, 0, 'f'},
+           {"debug", 0, 0, 'd'},
+           {"config", 1, 0, 'c'},
+           {"just-check-config", 0, 0, 'j'},
+           {"sites-key", 1, 0, 's'},
+           {0,0,0,0}
+       };
+
+       c=getopt_long(argc, argv, "vwdnjc:ft:s:m",
+                     long_options, &option_index);
+       if (c==-1)
+           break;
+
+       switch(c) {
+       case 2:
+           /* Help */
+           printf("Usage: secnet [OPTION]...\n\n"
+                  "  -f, --silent, --quiet   suppress error messages\n"
+                  "  -w, --nowarnings        suppress warnings\n"
+                  "  -v, --verbose           output extra diagnostics\n"
+                  "  -c, --config=filename   specify a configuration file\n"
+                  "  -j, --just-check-config stop after reading "
+                  "configuration file\n"
+                  "  -s, --sites-key=name    configuration key that "
+                  "specifies active sites\n"
+                  "  -n, --nodetach          do not run in background\n"
+                  "  -m, --managed           running under a supervisor\n"
+                  "  -d, --debug             output debug messages\n"
+                  "      --help              display this help and exit\n"
+                  "      --version           output version information "
+                  "and exit\n"
+               );
+           exit(0);
+           break;
+      
+       case 1:
+           /* Version */
+           printf("%s\n",version);
+           exit(0);
+           break;
+
+       case 'd':
+           message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE|M_DEBUG;
+           /* fall through */
+       case 'v':
+           message_level|=M_INFO|M_NOTICE|M_WARNING|M_ERR|M_SECURITY|
+               M_FATAL;
+           break;
+
+       case 'w':
+           message_level&=(~M_WARNING);
+           break;
+
+       case 'f':
+           message_level=M_FATAL;
+           break;
+
+       case 'n':
+           background=False;
+           break;
+
+       case 'm':
+           secnet_is_daemon=True;
+           break;
+
+       case 'c':
+           if (optarg)
+               configfile=safe_strdup(optarg,"config_filename");
+           else
+               fatal("secnet: no config filename specified");
+           break;
+
+       case 'j':
+           just_check_config=True;
+           break;
+
+       case 's':
+           if (optarg)
+               sites_key=safe_strdup(optarg,"sites-key");
+           else
+               fatal("secnet: no sites key specified");
+           break;
+
+       case '?':
+           exit(1);
+           break;
+
+       default:
+           Message(M_ERR,"secnet: Unknown getopt code %c\n",c);
+       }
+    }
+
+    if (argc-optind != 0) {
+       Message(M_ERR,"secnet: You gave extra command line parameters, "
+               "which were ignored.\n");
+    }
+}
+
+static void setup(dict_t *config)
+{
+    list_t *l;
+    item_t *site;
+    dict_t *system;
+    struct passwd *pw;
+    struct cloc loc;
+    int i;
+
+    l=dict_lookup(config,"system");
+
+    if (!l || list_elem(l,0)->type!=t_dict) {
+       fatal("configuration does not include a \"system\" dictionary");
+    }
+    system=list_elem(l,0)->data.dict;
+    loc=list_elem(l,0)->loc;
+
+    /* Arrange systemwide log facility */
+    l=dict_lookup(system,"log");
+    if (!l) {
+       fatal("configuration does not include a system/log facility");
+    }
+    system_log=init_log(l);
+
+    /* Who are we supposed to run as? */
+    userid=dict_read_string(system,"userid",False,"system",loc);
+    if (userid) {
+       if (!(pw=getpwnam(userid)))
+           fatal("userid \"%s\" not found",userid);
+       uid=pw->pw_uid;
+       gid=pw->pw_gid;
+    }
+
+    /* Pidfile name */
+    pidfile=dict_read_string(system,"pidfile",False,"system",loc);
+
+    /* Check whether we need root privileges */
+    if (require_root_privileges && uid!=0) {
+       fatal("the configured feature \"%s\" requires "
+             "that secnet retain root privileges while running.",
+             require_root_privileges_explanation);
+    }
+
+    /* Go along site list, starting sites */
+    l=dict_lookup(config,sites_key);
+    if (!l) {
+       Message(M_WARNING,"secnet: configuration key \"%s\" is missing; no "
+               "remote sites are defined\n",sites_key);
+    } else {
+       i=0;
+       while ((site=list_elem(l, i++))) {
+           struct site_if *s;
+           if (site->type!=t_closure) {
+               cfgfatal(site->loc,"system","non-closure in site list");
+           }
+           if (site->data.closure->type!=CL_SITE) {
+               cfgfatal(site->loc,"system","non-site closure in site list");
+           }
+           s=site->data.closure->interface;
+           s->control(s->st,True);
+       }
+    }
+}
+
+struct poll_interest *register_for_poll(void *st, beforepoll_fn *before,
+                      afterpoll_fn *after, cstring_t desc)
+{
+    struct poll_interest *i;
+
+    NEW(i);
+    i->before=before;
+    i->after=after;
+    i->state=st;
+    i->nfds=0;
+    i->desc=desc;
+    LIST_INSERT_HEAD(&reg, i, entry);
+    return i;
+}
+
+void deregister_for_poll(struct poll_interest *i)
+{
+    /* We cannot simply throw this away because we're reentrantly
+     * inside the main loop, which needs to remember which range of
+     * fds corresponds to this now-obsolete interest */
+    i->before=0;
+}
+
+static void system_phase_hook(void *sst, uint32_t newphase)
+{
+    if (newphase==PHASE_SHUTDOWN && pidfile) {
+       /* Try to unlink the pidfile; don't care if it fails */
+       unlink(pidfile);
+    }
+}
+
+#if USE_SELECT
+static int fakepoll(struct pollfd *fds, int nfds, int timeout) {
+    fd_set infds[1], outfds[1];
+    int maxfd = -1, i, rc;
+    struct timeval tvtimeout;
+    FD_ZERO(infds);
+    FD_ZERO(outfds);
+    for(i = 0; i < nfds; ++i) {
+       if(fds[i].events & POLLIN)
+           FD_SET(fds[i].fd, infds);
+       if(fds[i].events & POLLOUT)
+           FD_SET(fds[i].fd, outfds);
+       if(fds[i].fd > maxfd)
+           maxfd = fds[i].fd;
+    }
+    if(timeout != -1) {
+       tvtimeout.tv_sec = timeout / 1000;
+       tvtimeout.tv_usec = 1000 * (timeout % 1000);
+    }
+    rc = select(maxfd + 1, infds, outfds, NULL, 
+               timeout == -1 ? NULL : &tvtimeout);
+    if(rc >= 0) {
+       for(i = 0; i < nfds; ++i) {
+           int revents = 0;
+           if(FD_ISSET(fds[i].fd, infds))
+               revents |= POLLIN;
+           if(FD_ISSET(fds[i].fd, outfds))
+               revents |= POLLOUT;
+           fds[i].revents = revents;
+       }
+    }
+    return rc;
+}
+#endif
+
+struct timeval tv_now_global;
+uint64_t now_global;
+
+static void run(void)
+{
+    struct poll_interest *i, *itmp;
+    int rv, nfds, idx;
+    int timeout;
+    struct pollfd *fds=0;
+    int allocdfds=0, shortfall=0;
+
+    Message(M_NOTICE,"%s [%d]: starting\n",version,secnet_pid);
+
+    do {
+       if (gettimeofday(&tv_now_global, NULL)!=0) {
+           fatal_perror("main loop: gettimeofday");
+       }
+       now_global=((uint64_t)tv_now_global.tv_sec*(uint64_t)1000)+
+                  ((uint64_t)tv_now_global.tv_usec/(uint64_t)1000);
+       idx=0;
+       LIST_FOREACH(i, &reg, entry) {
+           int check;
+           if (interest_isregistered(i)) {
+               for (check=0; check<i->nfds; check++) {
+                   if(fds[idx+check].revents & POLLNVAL) {
+                       fatal("run: poll (%s#%d) set POLLNVAL", i->desc, check);
+                   }
+               }
+               i->after(i->state, fds+idx, i->nfds);
+           }
+           idx+=i->nfds;
+       }
+       if (shortfall) {
+           allocdfds *= 2;
+           allocdfds += shortfall;
+           REALLOC_ARY(fds,allocdfds);
+       }
+       shortfall=0;
+       idx=0;
+       timeout=-1;
+       LIST_FOREACH_SAFE(i, &reg, entry, itmp) {
+           int remain=allocdfds-idx;
+           nfds=remain;
+           if (interest_isregistered(i)) {
+               rv=i->before(i->state, fds+idx, &nfds, &timeout);
+               if (rv!=0) {
+                   if (rv!=ERANGE)
+                       fatal("run: beforepoll_fn (%s) returns %d",i->desc,rv);
+                   assert(nfds < INT_MAX/4 - shortfall);
+                   shortfall += nfds-remain;
+                   nfds=0;
+                   timeout=0;
+               }
+           } else {
+               nfds=0;
+           }
+           if (timeout<-1) {
+               fatal("run: beforepoll_fn (%s) set timeout to %d",
+                     i->desc,timeout);
+           }
+           if (!interest_isregistered(i)) {
+               /* check this here, rather than earlier, so that we
+                  handle the case where i->before() calls deregister */
+               LIST_REMOVE(i, entry);
+               free(i);
+               continue;
+           }
+           idx+=nfds;
+           i->nfds=nfds;
+       }
+       do {
+           if (finished) break;
+#if USE_SELECT
+           rv=fakepoll(fds, idx, timeout);
+#else
+           rv=poll(fds, idx, timeout);
+#endif
+           if (rv<0) {
+               if (errno!=EINTR) {
+                   fatal_perror("run: poll");
+               }
+           }
+       } while (rv<0);
+    } while (!finished);
+    free(fds);
+}
+
+bool_t will_droppriv(void)
+{
+    assert(current_phase >= PHASE_SETUP);
+    return !!uid;
+}
+
+/* Surrender privileges, if necessary */
+static void droppriv(void)
+{
+    if (userid) {
+       if (setgid(gid)!=0)
+           fatal_perror("can't set gid to %ld",(long)gid);
+       if (initgroups(userid, gid) < 0)
+           fatal_perror("initgroups"); 
+       if (setuid(uid)!=0) {
+           fatal_perror("can't set uid to \"%s\"",userid);
+       }
+       assert(getuid() == uid);
+       assert(geteuid() == uid);
+       assert(getgid() == gid);
+       assert(getegid() == gid);
+    }
+}
+
+/* Become a daemon, if necessary */
+static void become_daemon(void)
+{
+    FILE *pf=NULL;
+    pid_t p;
+    int errfds[2];
+
+    add_hook(PHASE_SHUTDOWN,system_phase_hook,NULL);
+
+    /* We only want to become a daemon if we are not one
+     already */
+    if (background && !secnet_is_daemon) {
+       p=fork();
+       if (p>0) {
+           /* Parent process - just exit */
+           _exit(0);
+       } else if (p==0) {
+           /* Child process - all done, just carry on */
+           secnet_is_daemon=True;
+           if (setsid() < 0)
+               fatal_perror("setsid");
+       } else {
+           /* Error */
+           fatal_perror("cannot fork");
+           exit(1);
+       }
+    }
+    if (secnet_is_daemon) {
+       /* stderr etc are redirected to the system/log facility */
+       pipe_cloexec(errfds);
+       if (dup2(errfds[1],0) < 0
+           || dup2(errfds[1],1) < 0
+           || dup2(errfds[1],2) < 0)
+           fatal_perror("can't dup2 pipe");
+       if (close(errfds[1]) < 0)
+           fatal_perror("can't close redundant pipe endpoint");
+       log_from_fd(errfds[0],"stderr",system_log);
+    }
+    secnet_pid=getpid();
+    
+    /* Now we can write the pidfile */
+    if (pidfile) {
+       pf=fopen(pidfile,"w");
+       if (!pf) {
+           fatal_perror("cannot open pidfile \"%s\"",pidfile);
+       }
+       if (fprintf(pf,"%ld\n",(long)secnet_pid) < 0
+           || fclose(pf) < 0)
+           fatal_perror("cannot write to pidfile \"%s\"",pidfile);
+    }
+}
+
+static signal_notify_fn finish,ignore_hup;
+static void finish(void *st, int signum)
+{
+    finished=True;
+    Message(M_NOTICE,"%s [%d]: received %s\n",version,secnet_pid,(string_t)st);
+}
+static void ignore_hup(void *st, int signum)
+{
+    Message(M_INFO,"%s [%d]: received SIGHUP\n",version,secnet_pid);
+    return;
+}
+
+int main(int argc, char **argv)
+{
+    dict_t *config;
+
+    phase_hooks_init();
+
+    enter_phase(PHASE_GETOPTS);
+    parse_options(argc,argv);
+
+    enter_phase(PHASE_READCONFIG);
+    config=read_conffile(configfile);
+
+    enter_phase(PHASE_SETUP);
+    setup(config);
+
+    if (just_check_config) {
+       Message(M_INFO,"configuration file check complete\n");
+       exit(0);
+    }
+
+    enter_phase(PHASE_DAEMONIZE);
+    become_daemon();
+    
+    enter_phase(PHASE_GETRESOURCES);
+    /* Appropriate phase hooks will have been run */
+    
+    enter_phase(PHASE_DROPPRIV);
+    droppriv();
+
+    start_signal_handling();
+    request_signal_notification(SIGTERM,finish,safe_strdup("SIGTERM","run"));
+    if (!background) request_signal_notification(SIGINT,finish,
+                                                safe_strdup("SIGINT","run"));
+    request_signal_notification(SIGHUP,ignore_hup,NULL);
+    enter_phase(PHASE_RUN);
+    run();
+
+    enter_phase(PHASE_SHUTDOWN);
+    Message(M_NOTICE,"%s [%d]: finished\n",version,secnet_pid);
+
+    return 0;
+}
diff --git a/secnet.h b/secnet.h
new file mode 100644 (file)
index 0000000..7da7c2d
--- /dev/null
+++ b/secnet.h
@@ -0,0 +1,751 @@
+/* Core interface of secnet, to be used by all modules */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef secnet_h
+#define secnet_h
+
+#define ADNS_FEATURE_MANYAF
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <fnmatch.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <bsd/sys/queue.h>
+
+#define MAX_PEER_ADDRS 5
+/* send at most this many copies; honour at most that many addresses */
+
+#define MAX_NAK_MSG 80
+
+struct hash_if;
+struct comm_if;
+struct comm_addr;
+struct priomsg;
+
+typedef char *string_t;
+typedef const char *cstring_t;
+
+#define False (_Bool)0
+#define True  (_Bool)1
+typedef _Bool bool_t;
+
+union iaddr {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+    struct sockaddr_in6 sin6;
+#endif
+};
+
+#define ASSERT(x) do { if (!(x)) { fatal("assertion failed line %d file " \
+                                        __FILE__,__LINE__); } } while(0)
+
+/* from version.c */
+
+extern char version[];
+
+/* from logmsg.c */
+extern uint32_t message_level;
+extern bool_t secnet_is_daemon;
+extern struct log_if *system_log;
+
+/* from process.c */
+extern void start_signal_handling(void);
+
+void afterfork(void);
+/* Must be called before exec in every child made after
+   start_signal_handling.  Safe to call in earlier children too. */
+
+void childpersist_closefd_hook(void *fd_p, uint32_t newphase);
+/* Convenience hook function for use with add_hook PHASE_CHILDPERSIST.
+   With `int fd' in your state struct, pass fd_p=&fd.  The hook checks
+   whether fd>=0, so you can use it for an fd which is only sometimes
+   open.  This function will set fd to -1, so it is idempotent. */
+
+/***** CONFIGURATION support *****/
+
+extern bool_t just_check_config; /* If True then we're going to exit after
+                                   reading the configuration file */
+extern bool_t background; /* If True then we'll eventually run as a daemon */
+
+typedef struct dict dict_t;        /* Configuration dictionary */
+typedef struct closure closure_t;
+typedef struct item item_t;
+typedef struct list list_t;        /* A list of items */
+
+/* Configuration file location, for error-reporting */
+struct cloc {
+    cstring_t file;
+    int line;
+};
+
+/* Modules export closures, which can be invoked from the configuration file.
+   "Invoking" a closure usually returns another closure (of a different
+   type), but can actually return any configuration object. */
+typedef list_t *(apply_fn)(closure_t *self, struct cloc loc,
+                          dict_t *context, list_t *data);
+struct closure {
+    cstring_t description; /* For debugging */
+    uint32_t type; /* Central registry... */
+    apply_fn *apply;
+    void *interface; /* Interface for use inside secnet; depends on type */
+};
+
+enum types { t_null, t_bool, t_string, t_number, t_dict, t_closure };
+struct item {
+    enum types type;
+    union {
+       bool_t bool;
+       string_t string;
+       uint32_t number;
+       dict_t *dict;
+       closure_t *closure;
+    } data;
+    struct cloc loc;
+};
+
+/* Note that it is unwise to use this structure directly; use the list
+   manipulation functions instead. */
+struct list {
+    item_t *item;
+    struct list *next;
+};
+
+/* In the following two lookup functions, NULL means 'not found' */
+/* Lookup a value in the specified dictionary, or its parents */
+extern list_t *dict_lookup(dict_t *dict, cstring_t key);
+/* Lookup a value in just the specified dictionary */
+extern list_t *dict_lookup_primitive(dict_t *dict, cstring_t key);
+/* Add a value to the specified dictionary */
+extern void dict_add(dict_t *dict, cstring_t key, list_t *val);
+/* Obtain an array of keys in the dictionary. malloced; caller frees */
+extern cstring_t *dict_keys(dict_t *dict);
+
+/* List-manipulation functions */
+extern list_t *list_new(void);
+extern int32_t list_length(const list_t *a);
+extern list_t *list_append(list_t *a, item_t *i);
+extern list_t *list_append_list(list_t *a, list_t *b);
+/* Returns an item from the list (index starts at 0), or NULL */
+extern item_t *list_elem(list_t *l, int32_t index);
+
+/* Convenience functions */
+extern list_t *new_closure(closure_t *cl);
+extern void add_closure(dict_t *dict, cstring_t name, apply_fn apply);
+extern void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type,
+                       bool_t fail_if_invalid, cstring_t desc,
+                       struct cloc loc);
+extern item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required,
+                             cstring_t desc, struct cloc loc);
+extern string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required,
+                                cstring_t desc, struct cloc loc);
+extern uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required,
+                                cstring_t desc, struct cloc loc,
+                                uint32_t def);
+  /* return value can safely be assigned to int32_t */
+extern bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required,
+                            cstring_t desc, struct cloc loc, bool_t def);
+extern dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required,
+                       cstring_t desc, struct cloc loc);
+const char **dict_read_string_array(dict_t *dict, cstring_t key,
+                                   bool_t required, cstring_t desc,
+                                   struct cloc loc, const char *const *def);
+  /* Return value is a NULL-terminated array obtained from malloc;
+   * Individual string values are still owned by config file machinery
+   * and must not be modified or freed.  Returns NULL if key not
+   * found. */
+
+struct flagstr {
+    cstring_t name;
+    uint32_t value;
+};
+extern uint32_t string_to_word(cstring_t s, struct cloc loc,
+                              struct flagstr *f, cstring_t desc);
+extern uint32_t string_list_to_word(list_t *l, struct flagstr *f,
+                                   cstring_t desc);
+
+/***** END of configuration support *****/
+
+/***** UTILITY functions *****/
+
+extern char *safe_strdup(const char *string, const char *message);
+extern void *safe_malloc(size_t size, const char *message);
+extern void *safe_malloc_ary(size_t size, size_t count, const char *message);
+extern void *safe_realloc_ary(void *p, size_t size, size_t count,
+                             const char *message);
+
+#define NEW(p)                                 \
+    ((p)=safe_malloc(sizeof(*(p)),             \
+                    __FILE__ ":" #p))
+#define NEW_ARY(p,count)                                       \
+    ((p)=safe_malloc_ary(sizeof(*(p)),(count),                 \
+                        __FILE__ ":" #p "[" #count "]"))
+#define REALLOC_ARY(p,count)                                   \
+    ((p)=safe_realloc_ary((p),sizeof(*(p)),(count),            \
+                         __FILE__ ":" #p "[" #count "]"))
+
+void setcloexec(int fd); /* cannot fail */
+void setnonblock(int fd); /* cannot fail */
+void pipe_cloexec(int fd[2]); /* pipe(), setcloexec() twice; cannot fail */
+
+extern int sys_cmd(const char *file, const char *argc, ...);
+
+extern uint64_t now_global;
+extern struct timeval tv_now_global;
+
+static const uint64_t       *const now    = &now_global;
+static const struct timeval *const tv_now = &tv_now_global;
+
+/* "now" is current program time, in milliseconds. It is derived
+   from tv_now. Both are provided by the event loop. */
+
+/***** END of utility functions *****/
+
+/***** START of max_start_pad handling *****/
+
+extern int32_t site_max_start_pad, transform_max_start_pad,
+    comm_max_start_pad;
+
+void update_max_start_pad(int32_t *our_module_global, int32_t our_instance);
+int32_t calculate_max_start_pad(void);
+
+/***** END of max_start_pad handling *****/
+
+/***** SCHEDULING support */
+
+/* If nfds_io is insufficient for your needs, set it to the required
+   number and return ERANGE. timeout is in milliseconds; if it is too
+   high then lower it. It starts at -1 (==infinite). */
+/* Note that beforepoll_fn may NOT do anything which might change the
+   fds or timeouts wanted by other registered poll loop loopers.
+   Callers should make sure of this by not making any calls into other
+   modules from the beforepoll_fn; the easiest way to ensure this is
+   for beforepoll_fn to only retreive information and not take any
+   action.
+ */
+typedef int beforepoll_fn(void *st, struct pollfd *fds, int *nfds_io,
+                         int *timeout_io);
+typedef void afterpoll_fn(void *st, struct pollfd *fds, int nfds);
+  /* If beforepoll_fn returned ERANGE, afterpoll_fn gets nfds==0.
+     afterpoll_fn never gets !!(fds[].revents & POLLNVAL) - such
+     a report is detected as a fatal error by the event loop. */
+
+/* void BEFOREPOLL_WANT_FDS(int want);
+ *   Expects: int *nfds_io;
+ *   Can perform non-local exit.
+ * Checks whether there is space for want fds.  If so, sets *nfds_io.
+ * If not, sets *nfds_io and returns. */
+#define BEFOREPOLL_WANT_FDS(want) do{                          \
+    if (*nfds_io<(want)) { *nfds_io=(want); return ERANGE; }   \
+    *nfds_io=(want);                                           \
+  }while(0)
+
+/* Register interest in the main loop of the program. Before a call
+   to poll() your supplied beforepoll function will be called. After
+   the call to poll() the supplied afterpoll function will be called. */
+struct poll_interest *register_for_poll(void *st, beforepoll_fn *before,
+                             afterpoll_fn *after, cstring_t desc);
+void deregister_for_poll(struct poll_interest *i);
+
+/***** END of scheduling support */
+
+/***** PROGRAM LIFETIME support */
+
+/* The secnet program goes through a number of phases in its lifetime.
+   Module code may arrange to be called just as various phases are
+   entered.
+   Remember to update the table in util.c if changing the set of
+   phases. */
+
+enum phase {
+    PHASE_INIT,
+    PHASE_GETOPTS,             /* Process command-line arguments */
+    PHASE_READCONFIG,          /* Parse and process configuration file */
+    PHASE_SETUP,               /* Process information in configuration */
+    PHASE_DAEMONIZE,           /* Become a daemon (if necessary) */
+    PHASE_GETRESOURCES,        /* Obtain all external resources */
+    PHASE_DROPPRIV,            /* Last chance for privileged operations */
+    PHASE_RUN,
+    PHASE_SHUTDOWN,            /* About to die; delete key material, etc. */
+    PHASE_CHILDPERSIST,        /* Forked long-term child: close fds, etc. */
+    /* Keep this last: */
+    NR_PHASES,
+};
+
+/* Each module should, in its CHILDPERSIST hooks, close all fds which
+   constitute ownership of important operating system resources, or
+   which are used for IPC with other processes who want to get the
+   usual disconnection effects if the main secnet process dies.
+   CHILDPERSIST hooks are not run if the child is going to exec;
+   so fds such as described above should be CLOEXEC too. */
+
+typedef void hook_fn(void *self, uint32_t newphase);
+bool_t add_hook(uint32_t phase, hook_fn *f, void *state);
+bool_t remove_hook(uint32_t phase, hook_fn *f, void *state);
+
+extern uint32_t current_phase;
+extern void enter_phase(uint32_t new_phase);
+
+void phase_hooks_init(void); /* for main() only */
+void clear_phase_hooks(uint32_t phase); /* for afterfork() */
+
+/* Some features (like netlink 'soft' routes) require that secnet
+   retain root privileges.  They should indicate that here when
+   appropriate. */
+extern bool_t require_root_privileges;
+extern cstring_t require_root_privileges_explanation;
+
+/* Some modules may want to know whether secnet is going to drop
+   privilege, so that they know whether to do privsep.  Call only
+   in phases SETUP and later. */
+bool_t will_droppriv(void);
+
+/***** END of program lifetime support *****/
+
+/***** MODULE support *****/
+
+/* Module initialisation function type - modules export one function of
+   this type which is called to initialise them. For dynamically loaded
+   modules it's called "secnet_module". */
+typedef void init_module(dict_t *dict);
+
+extern void init_builtin_modules(dict_t *dict);
+
+extern init_module resolver_module;
+extern init_module random_module;
+extern init_module udp_module;
+extern init_module polypath_module;
+extern init_module util_module;
+extern init_module site_module;
+extern init_module transform_eax_module;
+extern init_module transform_cbcmac_module;
+extern init_module netlink_module;
+extern init_module rsa_module;
+extern init_module dh_module;
+extern init_module md5_module;
+extern init_module slip_module;
+extern init_module tun_module;
+extern init_module sha1_module;
+extern init_module log_module;
+
+/***** END of module support *****/
+
+/***** CLOSURE TYPES and interface definitions *****/
+
+#define CL_PURE         0
+#define CL_RESOLVER     1
+#define CL_RANDOMSRC    2
+#define CL_SIGPUBKEY    3
+#define CL_SIGPRIVKEY   4
+#define CL_COMM         5
+#define CL_IPIF         6
+#define CL_LOG          7
+#define CL_SITE         8
+#define CL_TRANSFORM    9
+#define CL_DH          11
+#define CL_HASH        12
+#define CL_BUFFER      13
+#define CL_NETLINK     14
+
+struct buffer_if;
+
+struct alg_msg_data {
+    uint8_t *start;
+    int32_t len;
+};
+
+/* PURE closure requires no interface */
+
+/* RESOLVER interface */
+
+/* Answers to queries are delivered to a function of this
+   type. 'address' will be NULL if there was a problem with the query. It
+   will be freed once resolve_answer_fn returns.  naddrs is the actual
+   size of the array at addrs; was_naddrs is the number of addresses
+   actually found in the DNS, which may be bigger if addrs is equal
+   to MAX_PEER_ADDRS (ie there were too many). */
+typedef void resolve_answer_fn(void *st, const struct comm_addr *addrs,
+                              int naddrs, int was_naddrs,
+                              const char *name, const char *failwhy);
+  /* name is the same ptr as passed to request, so its lifetime must
+   * be suitable*/
+typedef bool_t resolve_request_fn(void *st, cstring_t name,
+                                 int remoteport, struct comm_if *comm,
+                                 resolve_answer_fn *cb, void *cst);
+struct resolver_if {
+    void *st;
+    resolve_request_fn *request;
+};
+
+/* RANDOMSRC interface */
+
+/* Return some random data. Cannot fail. */
+typedef void random_fn(void *st, int32_t bytes, uint8_t *buff);
+
+struct random_if {
+    void *st;
+    bool_t blocking;
+    random_fn *generate;
+};
+
+/* SIGPUBKEY interface */
+
+typedef void sig_sethash_fn(void *st, struct hash_if *hash);
+typedef bool_t sig_unpick_fn(void *sst, struct buffer_if *msg,
+                            struct alg_msg_data *sig);
+typedef bool_t sig_checksig_fn(void *st, uint8_t *data, int32_t datalen,
+                              const struct alg_msg_data *sig);
+struct sigpubkey_if {
+    void *st;
+    sig_sethash_fn *sethash; /* must be called before check, if non-0 */
+    sig_unpick_fn *unpick;
+    sig_checksig_fn *check;
+};
+
+/* SIGPRIVKEY interface */
+
+/* Appends the signature to msg.
+ * Can fail and returnn False, eg if the buffer is too small. */
+typedef bool_t sig_makesig_fn(void *st, uint8_t *data, int32_t datalen,
+                             struct buffer_if *msg);
+struct sigprivkey_if {
+    void *st;
+    sig_sethash_fn *sethash; /* must be called before sign, if non-0 */
+    sig_makesig_fn *sign;
+};
+
+/* COMM interface */
+
+struct comm_addr {
+    /* This struct is pure data; in particular comm's clients may
+       freely copy it. */
+    struct comm_if *comm;
+    union iaddr ia;
+    int ix; /* see comment `Re comm_addr.ix' in udp.c */
+};
+
+struct comm_clientinfo; /* private for comm */
+
+typedef struct comm_clientinfo *comm_clientinfo_fn(void *state, dict_t*,
+                                                  struct cloc cloc);
+/* A comm client may call this during configuration, and then pass
+ * the resulting comm_clientinfo* to some or all sendmsg calls.
+ * The semantics depend on the dict and defined by the comm, and
+ * should be documented in README. */
+
+enum {
+    comm_notify_whynot_general,
+    comm_notify_whynot_unpick,
+    comm_notify_whynot_name_local,
+    comm_notify_whynot_name_remote,
+};
+
+/* Return True if the packet was processed, and shouldn't be passed to
+   any other potential receivers. (buf is freed iff True returned.) */
+typedef bool_t comm_notify_fn(void *state, struct buffer_if *buf,
+                             const struct comm_addr *source,
+                             struct priomsg *whynot);
+typedef void comm_request_notify_fn(void *commst, void *nst,
+                                   comm_notify_fn *fn);
+typedef void comm_release_notify_fn(void *commst, void *nst,
+                                   comm_notify_fn *fn);
+typedef bool_t comm_sendmsg_fn(void *commst, struct buffer_if *buf,
+                              const struct comm_addr *dest,
+                              struct comm_clientinfo* /* 0 OK */);
+  /* Only returns false if (we know that) the local network
+   * environment is such that this address cannot work; transient
+   * or unknown/unexpected failures return true. */
+typedef const char *comm_addr_to_string_fn(void *commst,
+                                          const struct comm_addr *ca);
+        /* Returned string is in a static buffer. */
+struct comm_if {
+    void *st;
+    comm_clientinfo_fn *clientinfo;
+    comm_request_notify_fn *request_notify;
+    comm_release_notify_fn *release_notify;
+    comm_sendmsg_fn *sendmsg;
+    comm_addr_to_string_fn *addr_to_string;
+};
+
+bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib,
+                  bool_t ignoreport);
+
+static inline const char *comm_addr_to_string(const struct comm_addr *ca)
+{
+    return ca->comm->addr_to_string(ca->comm->st, ca);
+}
+
+static inline bool_t comm_addr_equal(const struct comm_addr *a,
+                                    const struct comm_addr *b)
+{
+    return a->comm==b->comm && iaddr_equal(&a->ia,&b->ia,False);
+}
+
+/* LOG interface */
+
+#define LOG_MESSAGE_BUFLEN 1023
+
+typedef void log_msg_fn(void *st, int class, const char *message, ...);
+typedef void log_vmsg_fn(void *st, int class, const char *message,
+                        va_list args);
+struct log_if {
+    void *st;
+    log_vmsg_fn *vlogfn; /* printf format checking.  Use [v]slilog instead */
+    char buff[LOG_MESSAGE_BUFLEN+1];
+};
+/* (convenience functions, defined in util.c) */
+extern void slilog(struct log_if *lf, int class, const char *message, ...)
+FORMAT(printf,3,4);
+extern void vslilog(struct log_if *lf, int class, const char *message, va_list)
+FORMAT(printf,3,0);
+
+/* Versions which take (parts of) (multiple) messages, using \n to
+ * distinguish one message from another. */
+extern void slilog_part(struct log_if *lf, int class, const char *message, ...)
+FORMAT(printf,3,4);
+extern void vslilog_part(struct log_if *lf, int class, const char *message,
+                        va_list) FORMAT(printf,3,0);
+
+/* SITE interface */
+
+/* Pretty much a placeholder; allows starting and stopping of processing,
+   key expiry, etc. */
+typedef void site_control_fn(void *st, bool_t run);
+typedef uint32_t site_status_fn(void *st);
+struct site_if {
+    void *st;
+    site_control_fn *control;
+    site_status_fn *status;
+};
+
+/* TRANSFORM interface */
+
+/* A reversable transformation. Transforms buffer in-place; may add
+   data to start or end. (Reverse transformations decrease
+   length, of course.)  Transformations may be key-dependent, in which
+   case key material is passed in at initialisation time. They may
+   also depend on internal factors (eg. time) and keep internal
+   state. A struct transform_if only represents a particular type of
+   transformation; instances of the transformation (eg. with
+   particular key material) have a different C type. The same
+   secret key will be used in opposite directions between a pair of
+   secnets; one of these pairs will get direction==False, the other True. */
+
+typedef struct transform_inst_if *transform_createinstance_fn(void *st);
+typedef bool_t transform_setkey_fn(void *st, uint8_t *key, int32_t keylen,
+                                  bool_t direction);
+typedef bool_t transform_valid_fn(void *st); /* 0: no key; 1: ok */
+typedef void transform_delkey_fn(void *st);
+typedef void transform_destroyinstance_fn(void *st);
+
+typedef enum {
+    transform_apply_ok       = 0, /* all is well (everyone may assume==0) */
+    transform_apply_err      = 1, /* any other problem */
+    transform_apply_seqrange = 2,
+        /* message decrypted but sequence number was out of recent range */
+    transform_apply_seqdupe  = 3,
+        /* message decrypted but was dupe of recent packet */
+} transform_apply_return;
+
+static inline bool_t
+transform_apply_return_badseq(transform_apply_return problem) {
+    return problem == transform_apply_seqrange ||
+          problem == transform_apply_seqdupe;
+}
+
+typedef transform_apply_return transform_apply_fn(void *st,
+        struct buffer_if *buf, const char **errmsg);
+
+struct transform_inst_if {
+    void *st;
+    transform_setkey_fn *setkey;
+    transform_valid_fn *valid;
+    transform_delkey_fn *delkey;
+    transform_apply_fn *forwards;
+    transform_apply_fn *reverse;
+    transform_destroyinstance_fn *destroy;
+};
+
+struct transform_if {
+    void *st;
+    int capab_bit;
+    int32_t keylen; /* <<< INT_MAX */
+    transform_createinstance_fn *create;
+};
+
+/* NETLINK interface */
+
+/* Used by netlink to deliver to site, and by site to deliver to
+   netlink.  cid is the client identifier returned by
+   netlink_regnets_fn.  If buf has size 0 then the function is just
+   being called for its site-effects (eg. making the site code attempt
+   to bring up a network link) */
+typedef void netlink_deliver_fn(void *st, struct buffer_if *buf);
+/* site code can tell netlink when outgoing packets will be dropped,
+   so netlink can generate appropriate ICMP and make routing decisions */
+#define LINK_QUALITY_UNUSED 0   /* This link is unused, do not make this netlink */
+#define LINK_QUALITY_DOWN 1   /* No chance of a packet being delivered right away*/
+#define LINK_QUALITY_DOWN_STALE_ADDRESS 2 /* Link down, old address information */
+#define LINK_QUALITY_DOWN_CURRENT_ADDRESS 3 /* Link down, current address information */
+#define LINK_QUALITY_UP 4     /* Link active */
+#define MAXIMUM_LINK_QUALITY 3
+typedef void netlink_link_quality_fn(void *st, uint32_t quality);
+typedef void netlink_register_fn(void *st, netlink_deliver_fn *deliver,
+                                void *dst, uint32_t *localmtu_r /* NULL ok */);
+typedef void netlink_output_config_fn(void *st, struct buffer_if *buf);
+typedef bool_t netlink_check_config_fn(void *st, struct buffer_if *buf);
+typedef void netlink_set_mtu_fn(void *st, int32_t new_mtu);
+struct netlink_if {
+    void *st;
+    netlink_register_fn *reg;
+    netlink_deliver_fn *deliver;
+    netlink_link_quality_fn *set_quality;
+    netlink_set_mtu_fn *set_mtu;
+};
+
+/* DH interface */
+
+/* Returns public key as a malloced hex string */
+typedef string_t dh_makepublic_fn(void *st, uint8_t *secret,
+                                 int32_t secretlen);
+/* Fills buffer (up to buflen) with shared secret */
+typedef void dh_makeshared_fn(void *st, uint8_t *secret,
+                             int32_t secretlen, cstring_t rempublic,
+                             uint8_t *sharedsecret, int32_t buflen);
+struct dh_if {
+    void *st;
+    int32_t len; /* Approximate size of modulus in bytes */
+    int32_t ceil_len; /* Number of bytes just sufficient to contain modulus */
+    dh_makepublic_fn *makepublic;
+    dh_makeshared_fn *makeshared;
+};
+
+/* HASH interface */
+
+typedef void hash_init_fn(void *st /* slen bytes alloc'd by caller */);
+typedef void hash_update_fn(void *st, const void *buf, int32_t len);
+typedef void hash_final_fn(void *st, uint8_t *digest /* hlen bytes */);
+struct hash_if {
+    int32_t slen; /* State length in bytes */
+    int32_t hlen; /* Hash output length in bytes */
+    hash_init_fn *init;
+    hash_update_fn *update;
+    hash_final_fn *final;
+};
+
+/* BUFFER interface */
+
+struct buffer_if {
+    bool_t free;
+    cstring_t owner; /* Set to constant string */
+    struct cloc loc; /* Where we were defined */
+    uint8_t *base;
+    uint8_t *start;
+    int32_t size; /* Size of buffer contents */
+    int32_t alloclen; /* Total length allocated at base */
+};
+
+/***** LOG functions *****/
+
+#define M_DEBUG_CONFIG 0x001
+#define M_DEBUG_PHASE  0x002
+#define M_DEBUG        0x004
+#define M_INFO        0x008
+#define M_NOTICE       0x010
+#define M_WARNING      0x020
+#define M_ERR         0x040
+#define M_SECURITY     0x080
+#define M_FATAL               0x100
+
+/* The fatal() family of functions require messages that do not end in '\n' */
+extern NORETURN(fatal(const char *message, ...)) FORMAT(printf,1,2);
+extern NORETURN(fatal_perror(const char *message, ...)) FORMAT(printf,1,2);
+extern NORETURN(fatal_status(int status, const char *message, ...))
+       FORMAT(printf,2,3);
+extern NORETURN(fatal_perror_status(int status, const char *message, ...))
+       FORMAT(printf,2,3);
+
+/* Convenient nonfatal logging.  Requires message that does not end in '\n'.
+ * If class contains M_FATAL, exits (after entering PHASE_SHUTDOWN).
+ * lg, errnoval and loc may sensibly be 0.  desc must NOT be 0.
+ * lg_[v]perror save and restore errno. */
+void lg_vperror(struct log_if *lg, const char *desc, struct cloc *loc,
+               int class, int errnoval, const char *fmt, va_list al)
+    FORMAT(printf,6,0);
+void lg_perror(struct log_if *lg, const char *desc, struct cloc *loc,
+              int class, int errnoval, const char *fmt, ...)
+    FORMAT(printf,6,7);
+void lg_exitstatus(struct log_if *lg, const char *desc, struct cloc *loc,
+                  int class, int status, const char *progname);
+
+/* The cfgfatal() family of functions require messages that end in '\n' */
+extern NORETURN(cfgfatal(struct cloc loc, cstring_t facility,
+                        const char *message, ...)) FORMAT(printf,3,4);
+extern void cfgfile_postreadcheck(struct cloc loc, FILE *f);
+extern NORETURN(vcfgfatal_maybefile(FILE *maybe_f, struct cloc loc,
+                                   cstring_t facility, const char *message,
+                                   va_list))
+    FORMAT(printf,4,0);
+extern NORETURN(cfgfatal_maybefile(FILE *maybe_f, struct cloc loc,
+                                  cstring_t facility,
+                                  const char *message, ...))
+    FORMAT(printf,4,5);
+
+extern void Message(uint32_t class, const char *message, ...)
+    FORMAT(printf,2,3);
+extern void log_from_fd(int fd, cstring_t prefix, struct log_if *log);
+
+/***** END of log functions *****/
+
+#define STRING2(x) #x
+#define STRING(x) STRING2(x)
+
+#define FILLZERO(obj) (memset(&(obj),0,sizeof((obj))))
+#define ARRAY_SIZE(ary) (sizeof((ary))/sizeof((ary)[0]))
+
+/*
+ * void COPY_OBJ(  OBJECT& dst, const OBJECT& src);
+ * void COPY_ARRAY(OBJECT *dst, const OBJECT *src, INTEGER count);
+ *   // Typesafe: we check that the type OBJECT is the same in both cases.
+ *   // It is OK to use COPY_OBJ on an array object, provided dst is
+ *   // _actually_ the whole array object and not decayed into a
+ *   // pointer (e.g. a formal parameter).
+ */
+#define COPY_OBJ(dst,src) \
+    (&(dst)==&(src), memcpy(&(dst),&(src),sizeof((dst))))
+#define COPY_ARRAY(dst,src,count) \
+    (&(dst)[0]==&(src)[0], memcpy((dst),(src),sizeof((dst)[0])*(count)))
+
+#endif /* secnet_h */
diff --git a/serpent.c b/serpent.c
new file mode 100644 (file)
index 0000000..a8bfe2a
--- /dev/null
+++ b/serpent.c
@@ -0,0 +1,398 @@
+/*
+ * serpent.c: Implementation of the Serpent block cipher
+ */
+/*
+ * This file is Free Software.  It has been modified to as part of its
+ * incorporation into secnet.
+ *
+ * Copyright 1998      Ross Anderson, Eli Biham, Lars Knudsen
+ * Copyright 1995-2001 Stephen Early <steve@greenend.org.uk>
+ * Copyright 2011-2013 Ian Jackson
+ *
+ * For more information about Serpent see
+ * http://www.cl.cam.ac.uk/users/rja14/serpent.html
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include <stdint.h>
+
+#include "hexdebug.h"
+#include "serpent.h"
+#include "serpentsboxes.h"
+
+#ifdef SERPENT_BIGENDIAN
+
+#define GETPUT_CP(bytenum) \
+    (((basep) + (lenbytes) - (offset) - 4)[(bytenum)])
+
+#define SERPENT_DECORATE(func) serpentbe_##func
+
+#else /* !defined(SERPENT_BIGENDIAN) */
+
+#define GETPUT_CP(bytenum) \
+    (((basep) + (offset))[3-(bytenum)])
+
+#define SERPENT_DECORATE(func) serpent_##func
+
+#endif /* !defined(SERPENT_BIGENDIAN) */
+
+#if 0
+
+#include <stdio.h>
+
+static void SERP_DEBUG(const char *str1,
+                      const void *ary, int sz,
+                      const char *str2)
+{
+    fprintf(stderr,"%s",str1);
+    hexdebug(stderr,ary,sz);
+    fprintf(stderr,"%s",str2);
+}
+
+#else
+
+#define SERP_DEBUG(str1,aryv,sz,str2) /*empty*/
+
+#endif
+
+
+static uint32_t serpent_get_32bit(const uint8_t *basep,
+                                 int lenbytes, int offset)
+{
+    return (((uint32_t)GETPUT_CP(0) << 24) |
+           ((uint32_t)GETPUT_CP(1) << 16) |
+           ((uint32_t)GETPUT_CP(2) << +8) |
+           ((uint32_t)GETPUT_CP(3)));
+}
+
+static void serpent_put_32bit(uint8_t *basep, int lenbytes, int offset, uint32_t value)
+{
+    GETPUT_CP(0) = (char)((value) >> 24);
+    GETPUT_CP(1) = (char)((value) >> 16);
+    GETPUT_CP(2) = (char)((value) >> 8);
+    GETPUT_CP(3) = (char)(value);
+}
+
+void SERPENT_DECORATE(makekey)(struct keyInstance *key, int keyLen,
+           const uint8_t *keyMaterial)
+{
+    int i;
+    uint32_t j;
+    uint32_t w[132],k[132];
+
+    SERP_DEBUG("SERPENT makekey ",keyMaterial,keyLen/8,"\n");
+
+    for(i=0; i<keyLen/32; i++)
+       w[i]=serpent_get_32bit(keyMaterial, keyLen/8, i*4);
+    if(keyLen<256)
+       w[i]=(serpent_get_32bit(keyMaterial, keyLen/8, i*4)
+              & ((1L<<((keyLen&31)))-1)) | (1L<<((keyLen&31)));
+    for(i++; i<8; i++)
+       w[i]=0;
+    for(i=8; i<16; i++)
+       w[i]=ROL(w[i-8]^w[i-5]^w[i-3]^w[i-1]^PHI^(i-8),11);
+    for(i=0; i<8; i++)
+       w[i]=w[i+8];
+    for(i=8; i<132; i++)
+       w[i]=ROL(w[i-8]^w[i-5]^w[i-3]^w[i-1]^PHI^i,11);
+
+    RND03(w[  0], w[  1], w[  2], w[  3], k[  0], k[  1], k[  2], k[  3]);
+    RND02(w[  4], w[  5], w[  6], w[  7], k[  4], k[  5], k[  6], k[  7]);
+    RND01(w[  8], w[  9], w[ 10], w[ 11], k[  8], k[  9], k[ 10], k[ 11]);
+    RND00(w[ 12], w[ 13], w[ 14], w[ 15], k[ 12], k[ 13], k[ 14], k[ 15]);
+    RND31(w[ 16], w[ 17], w[ 18], w[ 19], k[ 16], k[ 17], k[ 18], k[ 19]);
+    RND30(w[ 20], w[ 21], w[ 22], w[ 23], k[ 20], k[ 21], k[ 22], k[ 23]);
+    RND29(w[ 24], w[ 25], w[ 26], w[ 27], k[ 24], k[ 25], k[ 26], k[ 27]);
+    RND28(w[ 28], w[ 29], w[ 30], w[ 31], k[ 28], k[ 29], k[ 30], k[ 31]);
+    RND27(w[ 32], w[ 33], w[ 34], w[ 35], k[ 32], k[ 33], k[ 34], k[ 35]);
+    RND26(w[ 36], w[ 37], w[ 38], w[ 39], k[ 36], k[ 37], k[ 38], k[ 39]);
+    RND25(w[ 40], w[ 41], w[ 42], w[ 43], k[ 40], k[ 41], k[ 42], k[ 43]);
+    RND24(w[ 44], w[ 45], w[ 46], w[ 47], k[ 44], k[ 45], k[ 46], k[ 47]);
+    RND23(w[ 48], w[ 49], w[ 50], w[ 51], k[ 48], k[ 49], k[ 50], k[ 51]);
+    RND22(w[ 52], w[ 53], w[ 54], w[ 55], k[ 52], k[ 53], k[ 54], k[ 55]);
+    RND21(w[ 56], w[ 57], w[ 58], w[ 59], k[ 56], k[ 57], k[ 58], k[ 59]);
+    RND20(w[ 60], w[ 61], w[ 62], w[ 63], k[ 60], k[ 61], k[ 62], k[ 63]);
+    RND19(w[ 64], w[ 65], w[ 66], w[ 67], k[ 64], k[ 65], k[ 66], k[ 67]);
+    RND18(w[ 68], w[ 69], w[ 70], w[ 71], k[ 68], k[ 69], k[ 70], k[ 71]);
+    RND17(w[ 72], w[ 73], w[ 74], w[ 75], k[ 72], k[ 73], k[ 74], k[ 75]);
+    RND16(w[ 76], w[ 77], w[ 78], w[ 79], k[ 76], k[ 77], k[ 78], k[ 79]);
+    RND15(w[ 80], w[ 81], w[ 82], w[ 83], k[ 80], k[ 81], k[ 82], k[ 83]);
+    RND14(w[ 84], w[ 85], w[ 86], w[ 87], k[ 84], k[ 85], k[ 86], k[ 87]);
+    RND13(w[ 88], w[ 89], w[ 90], w[ 91], k[ 88], k[ 89], k[ 90], k[ 91]);
+    RND12(w[ 92], w[ 93], w[ 94], w[ 95], k[ 92], k[ 93], k[ 94], k[ 95]);
+    RND11(w[ 96], w[ 97], w[ 98], w[ 99], k[ 96], k[ 97], k[ 98], k[ 99]);
+    RND10(w[100], w[101], w[102], w[103], k[100], k[101], k[102], k[103]);
+    RND09(w[104], w[105], w[106], w[107], k[104], k[105], k[106], k[107]);
+    RND08(w[108], w[109], w[110], w[111], k[108], k[109], k[110], k[111]);
+    RND07(w[112], w[113], w[114], w[115], k[112], k[113], k[114], k[115]);
+    RND06(w[116], w[117], w[118], w[119], k[116], k[117], k[118], k[119]);
+    RND05(w[120], w[121], w[122], w[123], k[120], k[121], k[122], k[123]);
+    RND04(w[124], w[125], w[126], w[127], k[124], k[125], k[126], k[127]);
+    RND03(w[128], w[129], w[130], w[131], k[128], k[129], k[130], k[131]);
+
+    for(i=0; i<=32; i++)
+       for(j=0; j<4; j++)
+           key->subkeys[i][j] = k[4*i+j];
+}
+
+void SERPENT_DECORATE(encrypt)(struct keyInstance *key,
+                    const uint8_t plaintext[16], 
+                    uint8_t ciphertext[16])
+{
+    register uint32_t x0, x1, x2, x3;
+    register uint32_t y0, y1, y2, y3;
+
+    SERP_DEBUG("SERPENT encrypt ",plaintext,16," ->");
+
+    x0=serpent_get_32bit(plaintext,16,+0);
+    x1=serpent_get_32bit(plaintext,16,+4);
+    x2=serpent_get_32bit(plaintext,16,+8);
+    x3=serpent_get_32bit(plaintext,16,12);
+
+    /* Start to encrypt the plaintext x */
+    keying(x0, x1, x2, x3, key->subkeys[ 0]);
+    RND00(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 1]);
+    RND01(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 2]);
+    RND02(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 3]);
+    RND03(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 4]);
+    RND04(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 5]);
+    RND05(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 6]);
+    RND06(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 7]);
+    RND07(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 8]);
+    RND08(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[ 9]);
+    RND09(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[10]);
+    RND10(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[11]);
+    RND11(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[12]);
+    RND12(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[13]);
+    RND13(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[14]);
+    RND14(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[15]);
+    RND15(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[16]);
+    RND16(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[17]);
+    RND17(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[18]);
+    RND18(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[19]);
+    RND19(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[20]);
+    RND20(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[21]);
+    RND21(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[22]);
+    RND22(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[23]);
+    RND23(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[24]);
+    RND24(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[25]);
+    RND25(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[26]);
+    RND26(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[27]);
+    RND27(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[28]);
+    RND28(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[29]);
+    RND29(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[30]);
+    RND30(x0, x1, x2, x3, y0, y1, y2, y3);
+    transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    keying(x0, x1, x2, x3, key->subkeys[31]);
+    RND31(x0, x1, x2, x3, y0, y1, y2, y3);
+    x0 = y0; x1 = y1; x2 = y2; x3 = y3;
+    keying(x0, x1, x2, x3, key->subkeys[32]);
+    /* The ciphertext is now in x */
+
+    serpent_put_32bit(ciphertext,16,+0, x0);
+    serpent_put_32bit(ciphertext,16,+4, x1);
+    serpent_put_32bit(ciphertext,16,+8, x2);
+    serpent_put_32bit(ciphertext,16,12, x3);
+
+    SERP_DEBUG(" ",ciphertext,16,"\n");
+}
+
+void SERPENT_DECORATE(decrypt)(struct keyInstance *key,
+                    const uint8_t ciphertext[16],
+                    uint8_t plaintext[16])
+{
+    register uint32_t x0, x1, x2, x3;
+    register uint32_t y0, y1, y2, y3;
+
+    SERP_DEBUG("SERPENT decrypt ",ciphertext,16," ->");
+
+    x0=serpent_get_32bit(ciphertext,16,+0);
+    x1=serpent_get_32bit(ciphertext,16,+4);
+    x2=serpent_get_32bit(ciphertext,16,+8);
+    x3=serpent_get_32bit(ciphertext,16,12);
+
+    /* Start to decrypt the ciphertext x */
+    keying(x0, x1, x2, x3, key->subkeys[32]);
+    InvRND31(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[31]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND30(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[30]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND29(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[29]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND28(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[28]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND27(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[27]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND26(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[26]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND25(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[25]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND24(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[24]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND23(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[23]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND22(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[22]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND21(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[21]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND20(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[20]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND19(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[19]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND18(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[18]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND17(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[17]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND16(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[16]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND15(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[15]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND14(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[14]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND13(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[13]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND12(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[12]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND11(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[11]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND10(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[10]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND09(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 9]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND08(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 8]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND07(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 7]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND06(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 6]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND05(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 5]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND04(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 4]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND03(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 3]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND02(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 2]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND01(x0, x1, x2, x3, y0, y1, y2, y3);
+    keying(y0, y1, y2, y3, key->subkeys[ 1]);
+    inv_transform(y0, y1, y2, y3, x0, x1, x2, x3);
+    InvRND00(x0, x1, x2, x3, y0, y1, y2, y3);
+    x0 = y0; x1 = y1; x2 = y2; x3 = y3;
+    keying(x0, x1, x2, x3, key->subkeys[ 0]);
+    /* The plaintext is now in x */
+
+    serpent_put_32bit(plaintext,16,+0, x0);
+    serpent_put_32bit(plaintext,16,+4, x1);
+    serpent_put_32bit(plaintext,16,+8, x2);
+    serpent_put_32bit(plaintext,16,12, x3);
+
+    SERP_DEBUG(" ",plaintext,16,"\n");
+}
diff --git a/serpent.h b/serpent.h
new file mode 100644 (file)
index 0000000..857b1b9
--- /dev/null
+++ b/serpent.h
@@ -0,0 +1,25 @@
+#ifndef serpent_h
+#define serpent_h
+
+struct keyInstance {
+      uint32_t key[8];             /* The key in binary */
+      uint32_t subkeys[33][4]; /* Serpent subkeys */
+};
+
+/*  Function protoypes  */
+void serpent_makekey(struct keyInstance *key, int keyLen,
+                    const uint8_t *keyMaterial);
+void serpentbe_makekey(struct keyInstance *key, int keyLen,
+                    const uint8_t *keyMaterial);
+
+void serpent_encrypt(struct keyInstance *key, const uint8_t plaintext[16],
+                    uint8_t ciphertext[16]);
+void serpentbe_encrypt(struct keyInstance *key, const uint8_t plaintext[16],
+                    uint8_t ciphertext[16]);
+
+void serpent_decrypt(struct keyInstance *key, const uint8_t ciphertext[16],
+                    uint8_t plaintext[16]);
+void serpentbe_decrypt(struct keyInstance *key, const uint8_t ciphertext[16],
+                    uint8_t plaintext[16]);
+
+#endif /* serpent_h */
diff --git a/serpentbe.c b/serpentbe.c
new file mode 100644 (file)
index 0000000..758d5c9
--- /dev/null
@@ -0,0 +1,2 @@
+#define SERPENT_BIGENDIAN
+#include "serpent.c"
diff --git a/serpentsboxes.h b/serpentsboxes.h
new file mode 100644 (file)
index 0000000..c264d63
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * serpentsboxes.h: S-boxes; internal to Serpent implementation.
+ */
+/*
+ * This file is Free Software.  It is now being distributed with
+ * secnet.
+ *
+ * Copyright 1998      Ross Anderson, Eli Biham, Lars Knudsen
+ * Copyright 1995-2001 Stephen Early <steve@greenend.org.uk>
+ * Copyright 2011-2013 Ian Jackson
+ *
+ * For more information about Serpent see
+ * http://www.cl.cam.ac.uk/users/rja14/serpent.html
+ *
+ * You may redistribute secnet as a whole and/or modify it under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3, or (at your option) any
+ * later version.
+ *
+ * You may redistribute this file and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This software 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.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+
+/* S0:   3  8 15  1 10  6  5 11 14 13  4  2  7  0  9 12 */
+
+/* depth = 5,7,4,2, Total gates=18 */
+#define RND00(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t05, t06, t07, t08, t09, t11, t12, t13, t14, t15, t17, t01;\
+       t01 = b   ^ c  ; \
+       t02 = a   | d  ; \
+       t03 = a   ^ b  ; \
+       z   = t02 ^ t01; \
+       t05 = c   | z  ; \
+       t06 = a   ^ d  ; \
+       t07 = b   | c  ; \
+       t08 = d   & t05; \
+       t09 = t03 & t07; \
+       y   = t09 ^ t08; \
+       t11 = t09 & y  ; \
+       t12 = c   ^ d  ; \
+       t13 = t07 ^ t11; \
+       t14 = b   & t06; \
+       t15 = t06 ^ t13; \
+       w   =     ~ t15; \
+       t17 = w   ^ t14; \
+       x   = t12 ^ t17; }
+
+/* InvS0:  13  3 11  0 10  6  5 12  1 14  4  7 15  9  8  2 */
+
+/* depth = 8,4,3,6, Total gates=19 */
+#define InvRND00(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t12, t13, t14, t15, t17, t18, t01;\
+       t01 = c   ^ d  ; \
+       t02 = a   | b  ; \
+       t03 = b   | c  ; \
+       t04 = c   & t01; \
+       t05 = t02 ^ t01; \
+       t06 = a   | t04; \
+       y   =     ~ t05; \
+       t08 = b   ^ d  ; \
+       t09 = t03 & t08; \
+       t10 = d   | y  ; \
+       x   = t09 ^ t06; \
+       t12 = a   | t05; \
+       t13 = x   ^ t12; \
+       t14 = t03 ^ t10; \
+       t15 = a   ^ c  ; \
+       z   = t14 ^ t13; \
+       t17 = t05 & t13; \
+       t18 = t14 | t17; \
+       w   = t15 ^ t18; }
+
+/* S1:  15 12  2  7  9  0  5 10  1 11 14  8  6 13  3  4 */
+
+/* depth = 10,7,3,5, Total gates=18 */
+#define RND01(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t10, t11, t12, t13, t16, t17, t01;\
+       t01 = a   | d  ; \
+       t02 = c   ^ d  ; \
+       t03 =     ~ b  ; \
+       t04 = a   ^ c  ; \
+       t05 = a   | t03; \
+       t06 = d   & t04; \
+       t07 = t01 & t02; \
+       t08 = b   | t06; \
+       y   = t02 ^ t05; \
+       t10 = t07 ^ t08; \
+       t11 = t01 ^ t10; \
+       t12 = y   ^ t11; \
+       t13 = b   & d  ; \
+       z   =     ~ t10; \
+       x   = t13 ^ t12; \
+       t16 = t10 | x  ; \
+       t17 = t05 & t16; \
+       w   = c   ^ t17; }
+
+/* InvS1:   5  8  2 14 15  6 12  3 11  4  7  9  1 13 10  0 */
+
+/* depth = 7,4,5,3, Total gates=18 */
+#define InvRND01(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t14, t15, t17, t01;\
+       t01 = a   ^ b  ; \
+       t02 = b   | d  ; \
+       t03 = a   & c  ; \
+       t04 = c   ^ t02; \
+       t05 = a   | t04; \
+       t06 = t01 & t05; \
+       t07 = d   | t03; \
+       t08 = b   ^ t06; \
+       t09 = t07 ^ t06; \
+       t10 = t04 | t03; \
+       t11 = d   & t08; \
+       y   =     ~ t09; \
+       x   = t10 ^ t11; \
+       t14 = a   | y  ; \
+       t15 = t06 ^ x  ; \
+       z   = t01 ^ t04; \
+       t17 = c   ^ t15; \
+       w   = t14 ^ t17; }
+
+/* S2:   8  6  7  9  3 12 10 15 13  1 14  4  0 11  5  2 */
+
+/* depth = 3,8,11,7, Total gates=16 */
+#define RND02(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t05, t06, t07, t08, t09, t10, t12, t13, t14, t01;\
+       t01 = a   | c  ; \
+       t02 = a   ^ b  ; \
+       t03 = d   ^ t01; \
+       w   = t02 ^ t03; \
+       t05 = c   ^ w  ; \
+       t06 = b   ^ t05; \
+       t07 = b   | t05; \
+       t08 = t01 & t06; \
+       t09 = t03 ^ t07; \
+       t10 = t02 | t09; \
+       x   = t10 ^ t08; \
+       t12 = a   | d  ; \
+       t13 = t09 ^ x  ; \
+       t14 = b   ^ t13; \
+       z   =     ~ t09; \
+       y   = t12 ^ t14; }
+
+/* InvS2:  12  9 15  4 11 14  1  2  0  3  6 13  5  8 10  7 */
+
+/* depth = 3,6,8,3, Total gates=18 */
+#define InvRND02(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t12, t15, t16, t17, t01;\
+       t01 = a   ^ d  ; \
+       t02 = c   ^ d  ; \
+       t03 = a   & c  ; \
+       t04 = b   | t02; \
+       w   = t01 ^ t04; \
+       t06 = a   | c  ; \
+       t07 = d   | w  ; \
+       t08 =     ~ d  ; \
+       t09 = b   & t06; \
+       t10 = t08 | t03; \
+       t11 = b   & t07; \
+       t12 = t06 & t02; \
+       z   = t09 ^ t10; \
+       x   = t12 ^ t11; \
+       t15 = c   & z  ; \
+       t16 = w   ^ x  ; \
+       t17 = t10 ^ t15; \
+       y   = t16 ^ t17; }
+
+/* S3:   0 15 11  8 12  9  6  3 13  1  2  4 10  7  5 14 */
+
+/* depth = 8,3,5,5, Total gates=18 */
+#define RND03(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t13, t14, t15, t01;\
+       t01 = a   ^ c  ; \
+       t02 = a   | d  ; \
+       t03 = a   & d  ; \
+       t04 = t01 & t02; \
+       t05 = b   | t03; \
+       t06 = a   & b  ; \
+       t07 = d   ^ t04; \
+       t08 = c   | t06; \
+       t09 = b   ^ t07; \
+       t10 = d   & t05; \
+       t11 = t02 ^ t10; \
+       z   = t08 ^ t09; \
+       t13 = d   | z  ; \
+       t14 = a   | t07; \
+       t15 = b   & t13; \
+       y   = t08 ^ t11; \
+       w   = t14 ^ t15; \
+       x   = t05 ^ t04; }
+
+/* InvS3:   0  9 10  7 11 14  6 13  3  5 12  2  4  8 15  1 */
+
+/* depth = 3,6,4,4, Total gates=17 */
+#define InvRND03(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t09, t11, t12, t13, t14, t16, t01;\
+       t01 = c   | d  ; \
+       t02 = a   | d  ; \
+       t03 = c   ^ t02; \
+       t04 = b   ^ t02; \
+       t05 = a   ^ d  ; \
+       t06 = t04 & t03; \
+       t07 = b   & t01; \
+       y   = t05 ^ t06; \
+       t09 = a   ^ t03; \
+       w   = t07 ^ t03; \
+       t11 = w   | t05; \
+       t12 = t09 & t11; \
+       t13 = a   & y  ; \
+       t14 = t01 ^ t05; \
+       x   = b   ^ t12; \
+       t16 = b   | t13; \
+       z   = t14 ^ t16; }
+
+/* S4:   1 15  8  3 12  0 11  6  2  5  4 10  9 14  7 13 */
+
+/* depth = 6,7,5,3, Total gates=19 */
+#define RND04(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t12, t13, t14, t15, t16, t01;\
+       t01 = a   | b  ; \
+       t02 = b   | c  ; \
+       t03 = a   ^ t02; \
+       t04 = b   ^ d  ; \
+       t05 = d   | t03; \
+       t06 = d   & t01; \
+       z   = t03 ^ t06; \
+       t08 = z   & t04; \
+       t09 = t04 & t05; \
+       t10 = c   ^ t06; \
+       t11 = b   & c  ; \
+       t12 = t04 ^ t08; \
+       t13 = t11 | t03; \
+       t14 = t10 ^ t09; \
+       t15 = a   & t05; \
+       t16 = t11 | t12; \
+       y   = t13 ^ t08; \
+       x   = t15 ^ t16; \
+       w   =     ~ t14; }
+
+/* InvS4:   5  0  8  3 10  9  7 14  2 12 11  6  4 15 13  1 */
+
+/* depth = 6,4,7,3, Total gates=17 */
+#define InvRND04(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t09, t10, t11, t12, t13, t15, t01;\
+       t01 = b   | d  ; \
+       t02 = c   | d  ; \
+       t03 = a   & t01; \
+       t04 = b   ^ t02; \
+       t05 = c   ^ d  ; \
+       t06 =     ~ t03; \
+       t07 = a   & t04; \
+       x   = t05 ^ t07; \
+       t09 = x   | t06; \
+       t10 = a   ^ t07; \
+       t11 = t01 ^ t09; \
+       t12 = d   ^ t04; \
+       t13 = c   | t10; \
+       z   = t03 ^ t12; \
+       t15 = a   ^ t04; \
+       y   = t11 ^ t13; \
+       w   = t15 ^ t09; }
+
+/* S5:  15  5  2 11  4 10  9 12  0  3 14  8 13  6  7  1 */
+
+/* depth = 4,6,8,6, Total gates=17 */
+#define RND05(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t14, t01;\
+       t01 = b   ^ d  ; \
+       t02 = b   | d  ; \
+       t03 = a   & t01; \
+       t04 = c   ^ t02; \
+       t05 = t03 ^ t04; \
+       w   =     ~ t05; \
+       t07 = a   ^ t01; \
+       t08 = d   | w  ; \
+       t09 = b   | t05; \
+       t10 = d   ^ t08; \
+       t11 = b   | t07; \
+       t12 = t03 | w  ; \
+       t13 = t07 | t10; \
+       t14 = t01 ^ t11; \
+       y   = t09 ^ t13; \
+       x   = t07 ^ t08; \
+       z   = t12 ^ t14; }
+
+/* InvS5:   8 15  2  9  4  1 13 14 11  6  5  3  7 12 10  0 */
+
+/* depth = 4,6,9,7, Total gates=17 */
+#define InvRND05(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t12, t13, t15, t16, t01;\
+       t01 = a   & d  ; \
+       t02 = c   ^ t01; \
+       t03 = a   ^ d  ; \
+       t04 = b   & t02; \
+       t05 = a   & c  ; \
+       w   = t03 ^ t04; \
+       t07 = a   & w  ; \
+       t08 = t01 ^ w  ; \
+       t09 = b   | t05; \
+       t10 =     ~ b  ; \
+       x   = t08 ^ t09; \
+       t12 = t10 | t07; \
+       t13 = w   | x  ; \
+       z   = t02 ^ t12; \
+       t15 = t02 ^ t13; \
+       t16 = b   ^ d  ; \
+       y   = t16 ^ t15; }
+
+/* S6:   7  2 12  5  8  4  6 11 14  9  1 15 13  3 10  0 */
+
+/* depth = 8,3,6,3, Total gates=19 */
+#define RND06(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t15, t17, t18, t01;\
+       t01 = a   & d  ; \
+       t02 = b   ^ c  ; \
+       t03 = a   ^ d  ; \
+       t04 = t01 ^ t02; \
+       t05 = b   | c  ; \
+       x   =     ~ t04; \
+       t07 = t03 & t05; \
+       t08 = b   & x  ; \
+       t09 = a   | c  ; \
+       t10 = t07 ^ t08; \
+       t11 = b   | d  ; \
+       t12 = c   ^ t11; \
+       t13 = t09 ^ t10; \
+       y   =     ~ t13; \
+       t15 = x   & t03; \
+       z   = t12 ^ t07; \
+       t17 = a   ^ b  ; \
+       t18 = y   ^ t15; \
+       w   = t17 ^ t18; }
+
+/* InvS6:  15 10  1 13  5  3  6  0  4  9 14  7  2 12  8 11 */
+
+/* depth = 5,3,8,6, Total gates=19 */
+#define InvRND06(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t12, t13, t14, t15, t16, t17, t01;\
+       t01 = a   ^ c  ; \
+       t02 =     ~ c  ; \
+       t03 = b   & t01; \
+       t04 = b   | t02; \
+       t05 = d   | t03; \
+       t06 = b   ^ d  ; \
+       t07 = a   & t04; \
+       t08 = a   | t02; \
+       t09 = t07 ^ t05; \
+       x   = t06 ^ t08; \
+       w   =     ~ t09; \
+       t12 = b   & w  ; \
+       t13 = t01 & t05; \
+       t14 = t01 ^ t12; \
+       t15 = t07 ^ t13; \
+       t16 = d   | t02; \
+       t17 = a   ^ x  ; \
+       z   = t17 ^ t15; \
+       y   = t16 ^ t14; }
+
+/* S7:   1 13 15  0 14  8  2 11  7  4 12 10  9  3  5  6 */
+
+/* depth = 10,7,10,4, Total gates=19 */
+#define RND07(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t13, t14, t15, t16, t17, t01;\
+       t01 = a   & c  ; \
+       t02 =     ~ d  ; \
+       t03 = a   & t02; \
+       t04 = b   | t01; \
+       t05 = a   & b  ; \
+       t06 = c   ^ t04; \
+       z   = t03 ^ t06; \
+       t08 = c   | z  ; \
+       t09 = d   | t05; \
+       t10 = a   ^ t08; \
+       t11 = t04 & z  ; \
+       x   = t09 ^ t10; \
+       t13 = b   ^ x  ; \
+       t14 = t01 ^ x  ; \
+       t15 = c   ^ t05; \
+       t16 = t11 | t13; \
+       t17 = t02 | t14; \
+       w   = t15 ^ t17; \
+       y   = a   ^ t16; }
+
+/* InvS7:   3  0  6 13  9 14 15  8  5 12 11  7 10  1  4  2 */
+
+/* depth = 9,7,3,3, Total gates=18 */
+#define InvRND07(a,b,c,d,w,x,y,z) \
+       { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t13, t14, t15, t16, t01;\
+       t01 = a   & b  ; \
+       t02 = a   | b  ; \
+       t03 = c   | t01; \
+       t04 = d   & t02; \
+       z   = t03 ^ t04; \
+       t06 = b   ^ t04; \
+       t07 = d   ^ z  ; \
+       t08 =     ~ t07; \
+       t09 = t06 | t08; \
+       t10 = b   ^ d  ; \
+       t11 = a   | d  ; \
+       x   = a   ^ t09; \
+       t13 = c   ^ t06; \
+       t14 = c   & t11; \
+       t15 = d   | x  ; \
+       t16 = t01 | t10; \
+       w   = t13 ^ t15; \
+       y   = t14 ^ t16; }
+
+#define RND08(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h)
+#define RND09(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h)
+#define RND10(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h)
+#define RND11(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h)
+#define RND12(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h)
+#define RND13(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h)
+#define RND14(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h)
+#define RND15(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h)
+#define RND16(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h)
+#define RND17(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h)
+#define RND18(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h)
+#define RND19(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h)
+#define RND20(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h)
+#define RND21(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h)
+#define RND22(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h)
+#define RND23(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h)
+#define RND24(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h)
+#define RND25(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h)
+#define RND26(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h)
+#define RND27(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h)
+#define RND28(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h)
+#define RND29(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h)
+#define RND30(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h)
+#define RND31(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h)
+
+#define InvRND08(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h)
+#define InvRND09(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h)
+#define InvRND10(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h)
+#define InvRND11(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h)
+#define InvRND12(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h)
+#define InvRND13(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h)
+#define InvRND14(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h)
+#define InvRND15(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h)
+#define InvRND16(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h)
+#define InvRND17(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h)
+#define InvRND18(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h)
+#define InvRND19(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h)
+#define InvRND20(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h)
+#define InvRND21(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h)
+#define InvRND22(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h)
+#define InvRND23(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h)
+#define InvRND24(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h)
+#define InvRND25(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h)
+#define InvRND26(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h)
+#define InvRND27(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h)
+#define InvRND28(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h)
+#define InvRND29(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h)
+#define InvRND30(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h)
+#define InvRND31(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h)
+
+/* Linear transformations and key mixing: */
+
+#define ROL(x,n) ((((unsigned long)(x))<<(n))| \
+                  (((unsigned long)(x))>>(32-(n))))
+#define ROR(x,n) ((((unsigned long)(x))<<(32-(n)))| \
+                  (((unsigned long)(x))>>(n)))
+
+#define transform(x0, x1, x2, x3, y0, y1, y2, y3) \
+      y0 = ROL(x0, 13); \
+      y2 = ROL(x2, 3); \
+      y1 = x1 ^ y0 ^ y2; \
+      y3 = x3 ^ y2 ^ ((unsigned long)y0)<<3; \
+      y1 = ROL(y1, 1); \
+      y3 = ROL(y3, 7); \
+      y0 = y0 ^ y1 ^ y3; \
+      y2 = y2 ^ y3 ^ ((unsigned long)y1<<7); \
+      y0 = ROL(y0, 5); \
+      y2 = ROL(y2, 22)
+
+#define inv_transform(x0, x1, x2, x3, y0, y1, y2, y3) \
+      y2 = ROR(x2, 22);\
+      y0 = ROR(x0, 5); \
+      y2 = y2 ^ x3 ^ ((unsigned long)x1<<7); \
+      y0 = y0 ^ x1 ^ x3; \
+      y3 = ROR(x3, 7); \
+      y1 = ROR(x1, 1); \
+      y3 = y3 ^ y2 ^ ((unsigned long)y0)<<3; \
+      y1 = y1 ^ y0 ^ y2; \
+      y2 = ROR(y2, 3); \
+      y0 = ROR(y0, 13)
+
+#define keying(x0, x1, x2, x3, subkey) \
+                         x0^=subkey[0];x1^=subkey[1]; \
+                         x2^=subkey[2];x3^=subkey[3]
+
+/* PHI: Constant used in the key schedule */
+#define PHI 0x9e3779b9L
diff --git a/setup.mac b/setup.mac
new file mode 100755 (executable)
index 0000000..a7a7359
--- /dev/null
+++ b/setup.mac
@@ -0,0 +1,83 @@
+#!/bin/bash
+#
+# Richard Kettlewell 2011-06-18
+#
+# This file is part of secnet.
+# See README for full list of copyright holders.
+#
+# secnet is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# secnet 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.  See the GNU
+# General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# version 3 along with secnet; if not, see
+# https://www.gnu.org/licenses/gpl.html.
+#
+set -e
+
+group=${group:-secnet}
+user=${user:-secnet}
+
+# pick ID1 ID2 ... IDn
+# Echoes an ID matching none of ID1..IDn
+pick() {
+  local n
+  n=250                                # better not choose 0!
+  while :; do
+    ok=true
+    for k in "$@"; do
+      if [ $n = $k ]; then
+        ok=false
+        break
+      fi
+    done
+    if $ok; then
+      echo $n
+      return
+    fi
+    n=$((1+$n))
+  done
+}
+
+if dscl . -read /Groups/$group >/dev/null 2>&1; then
+  :
+else
+  gids=$(dscl . -list /Groups PrimaryGroupID|awk '{print $2}')
+  gid=$(pick $gids)
+  dscl . -create /Groups/$group
+  dscl . -create /Groups/$group PrimaryGroupID $gid
+  dscl . -create /Groups/$group Password \*
+fi
+
+if dscl . -read /Users/$user >/dev/null 2>&1; then
+  :
+else
+  uids=$(dscl . -list /Users UniqueID|awk '{print $2}')
+  uid=$(pick $uids)
+  gid=$(dscl . -read /Groups/$group PrimaryGroupID | awk '{print $2}')
+  dscl . -create /Users/$user
+  dscl . -create /Users/$user UniqueID $uid
+  dscl . -create /Users/$user UserShell /usr/bin/false
+  dscl . -create /Users/$user RealName 'secnet'
+  dscl . -create /Users/$user NFSHomeDirectory /var/empty
+  dscl . -create /Users/$user PrimaryGroupID $gid
+  dscl . -create /Users/$user Password \*
+fi
+
+cp uk.org.greenend.secnet.plist /Library/LaunchDaemons/.
+launchctl load /Library/LaunchDaemons
+echo "To start secnet:"
+echo "  sudo launchctl start uk.org.greenend.secnet"
+echo
+echo "To stop secnet:"
+echo "  sudo launchctl stop uk.org.greenend.secnet"
+echo
+echo "To uninstall:"
+echo "  sudo launchctl unload /Library/LaunchDaemons/uk.org.greenend.secnet.plist"
+echo "  sudo rm -f /Library/LaunchDaemons/uk.org.greenend.secnet.plist"
diff --git a/sha1.c b/sha1.c
new file mode 100644 (file)
index 0000000..f442e08
--- /dev/null
+++ b/sha1.c
@@ -0,0 +1,350 @@
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+[I interpet this as a blanket permision -iwj.]
+
+Note: parts of this file have been removed or modified to work in secnet.
+Instead of using this file in new projects, I suggest you use the
+unmodified version. SDE.
+
+-----------------
+Modified 7/98 
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+       void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+       void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it.  This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to 
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.  
+
+-----------------
+(Further modifications as part of secnet.  See git history for full details.
+ - Ian Jackson et al)
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define SHA1HANDSOFF  */
+
+#include "secnet.h"
+#include "util.h"
+#include <stdio.h>
+#include <string.h>
+
+#define SHA1HANDSOFF
+
+#if 0
+#ifndef  i386   /* For ALPHA  (SAK) */
+#define LITTLE_ENDIAN 
+typedef          long int int64;
+typedef unsigned long int uint64;
+typedef          int int32;
+typedef unsigned int uint32;
+#else  /*i386*/
+#define LITTLE_ENDIAN 
+typedef          long long int int64;
+typedef unsigned long long int uint64;
+typedef          long int int32;
+typedef unsigned long int uint32;
+#endif /*i386*/
+#endif /* 0 */
+
+/* Get types and defines from the secnet configuration */
+/* typedef int64_t int64; */
+typedef uint64_t uint64;
+/* typedef int32_t int32; */
+typedef uint32_t uint32;
+
+/* #include <process.h> */     /* prototype for exit() - JHB */
+/* Using return() instead of exit() - SWR */
+
+typedef struct {
+    uint32 state[5];
+    uint32 count[2];
+    unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(uint32 state[5], unsigned char const buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, unsigned char const * data, uint32 len);
+/* JHB */
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+#ifdef VERBOSE  /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg){
+  printf("%s (%d,%d) %x %x %x %x %x\n",
+        msg,
+        context->count[0], context->count[1], 
+        context->state[0],
+        context->state[1],
+        context->state[2],
+        context->state[3],
+        context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(uint32 state[5], unsigned char const buffer[64])
+{
+uint32 a, b, c, d, e;
+typedef union {
+    unsigned char c[64];
+    uint32 l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+static unsigned char workspace[64];
+    block = (CHAR64LONG16*)workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16*)buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, unsigned char const* data, uint32 len)      /*
+JHB */
+{
+uint32 i, j;   /* JHB */
+
+#ifdef VERBOSE
+    SHAPrintContext(context, "before");
+#endif
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+    SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+uint32 i;      /* JHB */
+unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1Update(context, (unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        SHA1Update(context, (unsigned char *)"\0", 1);
+    }
+    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform()
+*/
+    for (i = 0; i < 20; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = 0;     /* JHB */
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(finalcount, 0, 8);  /* SWR */
+#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
+    SHA1Transform(context->state, context->buffer);
+#endif
+}
+  
+/*************************************************************/
+
+/* Everything below here is the interface to secnet */
+static void sha1_init(void *sst)
+{
+    SHA1_CTX *ctx=sst;
+
+    SHA1Init(ctx);
+}
+
+static void sha1_update(void *sst, const void *buf, int32_t len)
+{
+    SHA1_CTX *ctx=sst;
+
+    SHA1Update(ctx,buf,len);
+}
+
+static void sha1_final(void *sst, uint8_t *digest)
+{
+    SHA1_CTX *ctx=sst;
+
+    SHA1Final(digest,ctx);
+}
+
+struct sha1 {
+    closure_t cl;
+    struct hash_if ops;
+};
+
+void sha1_module(dict_t *dict)
+{
+    struct sha1 *st;
+    cstring_t testinput="abcdbcdecdefdefgefghfghigh"
+       "ijhijkijkljklmklmnlmnomnopnopq";
+    uint8_t expected[20]=
+    { 0x84,0x98,0x3e,0x44,
+      0x1c,0x3b,0xd2,0x6e,
+      0xba,0xae,0x4a,0xa1,
+      0xf9,0x51,0x29,0xe5,
+      0xe5,0x46,0x70,0xf1};
+    uint8_t digest[20];
+    int i;
+
+    NEW(st);
+    st->cl.description="sha1";
+    st->cl.type=CL_HASH;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.hlen=20;
+    st->ops.slen=sizeof(SHA1_CTX);
+    st->ops.init=sha1_init;
+    st->ops.update=sha1_update;
+    st->ops.final=sha1_final;
+
+    dict_add(dict,"sha1",new_closure(&st->cl));
+
+    hash_hash(&st->ops,testinput,strlen(testinput),digest);
+    for (i=0; i<20; i++) {
+       if (digest[i]!=expected[i]) {
+           fatal("sha1 module failed self-test");
+       }
+    }
+}
diff --git a/sha512.c b/sha512.c
new file mode 100644 (file)
index 0000000..5c0b2ed
--- /dev/null
+++ b/sha512.c
@@ -0,0 +1,619 @@
+/* sha512.c - Functions to compute SHA512 and SHA384 message digest of files or
+   memory blocks according to the NIST specification FIPS-180-2.
+
+   Copyright (C) 2005, 2006, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   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.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by David Madore, considerably copypasting from
+   Scott G. Miller's sha1.c
+*/
+
+#include <config.h>
+
+#include "sha512.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) (n)
+#else
+# define SWAP(n) \
+    u64or (u64or (u64or (u64shl (n, 56),                                \
+                         u64shl (u64and (n, u64lo (0x0000ff00)), 40)),  \
+                  u64or (u64shl (u64and (n, u64lo (0x00ff0000)), 24),   \
+                         u64shl (u64and (n, u64lo (0xff000000)),  8))), \
+           u64or (u64or (u64and (u64shr (n,  8), u64lo (0xff000000)),   \
+                         u64and (u64shr (n, 24), u64lo (0x00ff0000))),  \
+                  u64or (u64and (u64shr (n, 40), u64lo (0x0000ff00)),   \
+                         u64shr (n, 56))))
+#endif
+
+#define BLOCKSIZE 32768
+#if BLOCKSIZE % 128 != 0
+# error "invalid BLOCKSIZE"
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+   128-byte boundary.  */
+static const unsigned char fillbuf[128] = { 0x80, 0 /* , 0, 0, ...  */ };
+
+
+/*
+  Takes a pointer to a 512 bit block of data (eight 64 bit ints) and
+  intializes it to the start constants of the SHA512 algorithm.  This
+  must be called before using hash in the call to sha512_hash
+*/
+void
+sha512_init_ctx (struct sha512_ctx *ctx)
+{
+  ctx->state[0] = u64hilo (0x6a09e667, 0xf3bcc908);
+  ctx->state[1] = u64hilo (0xbb67ae85, 0x84caa73b);
+  ctx->state[2] = u64hilo (0x3c6ef372, 0xfe94f82b);
+  ctx->state[3] = u64hilo (0xa54ff53a, 0x5f1d36f1);
+  ctx->state[4] = u64hilo (0x510e527f, 0xade682d1);
+  ctx->state[5] = u64hilo (0x9b05688c, 0x2b3e6c1f);
+  ctx->state[6] = u64hilo (0x1f83d9ab, 0xfb41bd6b);
+  ctx->state[7] = u64hilo (0x5be0cd19, 0x137e2179);
+
+  ctx->total[0] = ctx->total[1] = u64lo (0);
+  ctx->buflen = 0;
+}
+
+void
+sha384_init_ctx (struct sha512_ctx *ctx)
+{
+  ctx->state[0] = u64hilo (0xcbbb9d5d, 0xc1059ed8);
+  ctx->state[1] = u64hilo (0x629a292a, 0x367cd507);
+  ctx->state[2] = u64hilo (0x9159015a, 0x3070dd17);
+  ctx->state[3] = u64hilo (0x152fecd8, 0xf70e5939);
+  ctx->state[4] = u64hilo (0x67332667, 0xffc00b31);
+  ctx->state[5] = u64hilo (0x8eb44a87, 0x68581511);
+  ctx->state[6] = u64hilo (0xdb0c2e0d, 0x64f98fa7);
+  ctx->state[7] = u64hilo (0x47b5481d, 0xbefa4fa4);
+
+  ctx->total[0] = ctx->total[1] = u64lo (0);
+  ctx->buflen = 0;
+}
+
+/* Copy the value from V into the memory location pointed to by *CP,
+   If your architecture allows unaligned access, this is equivalent to
+   * (__typeof__ (v) *) cp = v  */
+static inline void
+set_uint64 (char *cp, u64 v)
+{
+  memcpy (cp, &v, sizeof v);
+}
+
+/* Put result from CTX in first 64 bytes following RESBUF.
+   The result must be in little endian byte order.  */
+void *
+sha512_read_ctx (const struct sha512_ctx *ctx, void *resbuf)
+{
+  int i;
+  char *r = resbuf;
+
+  for (i = 0; i < 8; i++)
+    set_uint64 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i]));
+
+  return resbuf;
+}
+
+void *
+sha384_read_ctx (const struct sha512_ctx *ctx, void *resbuf)
+{
+  int i;
+  char *r = resbuf;
+
+  for (i = 0; i < 6; i++)
+    set_uint64 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i]));
+
+  return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.  */
+static void
+sha512_conclude_ctx (struct sha512_ctx *ctx)
+{
+  /* Take yet unprocessed bytes into account.  */
+  size_t bytes = ctx->buflen;
+  size_t size = (bytes < 112) ? 128 / 8 : 128 * 2 / 8;
+
+  /* Now count remaining bytes.  */
+  ctx->total[0] = u64plus (ctx->total[0], u64lo (bytes));
+  if (u64lt (ctx->total[0], u64lo (bytes)))
+    ctx->total[1] = u64plus (ctx->total[1], u64lo (1));
+
+  /* Put the 128-bit file length in *bits* at the end of the buffer.
+     Use set_uint64 rather than a simple assignment, to avoid risk of
+     unaligned access.  */
+  set_uint64 ((char *) &ctx->buffer[size - 2],
+              SWAP (u64or (u64shl (ctx->total[1], 3),
+                           u64shr (ctx->total[0], 61))));
+  set_uint64 ((char *) &ctx->buffer[size - 1],
+              SWAP (u64shl (ctx->total[0], 3)));
+
+  memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes);
+
+  /* Process last bytes.  */
+  sha512_process_block (ctx->buffer, size * 8, ctx);
+}
+
+void *
+sha512_finish_ctx (struct sha512_ctx *ctx, void *resbuf)
+{
+  sha512_conclude_ctx (ctx);
+  return sha512_read_ctx (ctx, resbuf);
+}
+
+void *
+sha384_finish_ctx (struct sha512_ctx *ctx, void *resbuf)
+{
+  sha512_conclude_ctx (ctx);
+  return sha384_read_ctx (ctx, resbuf);
+}
+
+/* Compute SHA512 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 64 bytes
+   beginning at RESBLOCK.  */
+int
+sha512_stream (FILE *stream, void *resblock)
+{
+  struct sha512_ctx ctx;
+  size_t sum;
+
+  char *buffer = malloc (BLOCKSIZE + 72);
+  if (!buffer)
+    return 1;
+
+  /* Initialize the computation context.  */
+  sha512_init_ctx (&ctx);
+
+  /* Iterate over full file contents.  */
+  while (1)
+    {
+      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
+         computation function processes the whole buffer so that with the
+         next round of the loop another block can be read.  */
+      size_t n;
+      sum = 0;
+
+      /* Read block.  Take care for partial reads.  */
+      while (1)
+        {
+          n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+          sum += n;
+
+          if (sum == BLOCKSIZE)
+            break;
+
+          if (n == 0)
+            {
+              /* Check for the error flag IFF N == 0, so that we don't
+                 exit the loop after a partial read due to e.g., EAGAIN
+                 or EWOULDBLOCK.  */
+              if (ferror (stream))
+                {
+                  free (buffer);
+                  return 1;
+                }
+              goto process_partial_block;
+            }
+
+          /* We've read at least one byte, so ignore errors.  But always
+             check for EOF, since feof may be true even though N > 0.
+             Otherwise, we could end up calling fread after EOF.  */
+          if (feof (stream))
+            goto process_partial_block;
+        }
+
+      /* Process buffer with BLOCKSIZE bytes.  Note that
+                        BLOCKSIZE % 128 == 0
+       */
+      sha512_process_block (buffer, BLOCKSIZE, &ctx);
+    }
+
+ process_partial_block:;
+
+  /* Process any remaining bytes.  */
+  if (sum > 0)
+    sha512_process_bytes (buffer, sum, &ctx);
+
+  /* Construct result in desired memory.  */
+  sha512_finish_ctx (&ctx, resblock);
+  free (buffer);
+  return 0;
+}
+
+/* FIXME: Avoid code duplication */
+int
+sha384_stream (FILE *stream, void *resblock)
+{
+  struct sha512_ctx ctx;
+  size_t sum;
+
+  char *buffer = malloc (BLOCKSIZE + 72);
+  if (!buffer)
+    return 1;
+
+  /* Initialize the computation context.  */
+  sha384_init_ctx (&ctx);
+
+  /* Iterate over full file contents.  */
+  while (1)
+    {
+      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
+         computation function processes the whole buffer so that with the
+         next round of the loop another block can be read.  */
+      size_t n;
+      sum = 0;
+
+      /* Read block.  Take care for partial reads.  */
+      while (1)
+        {
+          n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+          sum += n;
+
+          if (sum == BLOCKSIZE)
+            break;
+
+          if (n == 0)
+            {
+              /* Check for the error flag IFF N == 0, so that we don't
+                 exit the loop after a partial read due to e.g., EAGAIN
+                 or EWOULDBLOCK.  */
+              if (ferror (stream))
+                {
+                  free (buffer);
+                  return 1;
+                }
+              goto process_partial_block;
+            }
+
+          /* We've read at least one byte, so ignore errors.  But always
+             check for EOF, since feof may be true even though N > 0.
+             Otherwise, we could end up calling fread after EOF.  */
+          if (feof (stream))
+            goto process_partial_block;
+        }
+
+      /* Process buffer with BLOCKSIZE bytes.  Note that
+                        BLOCKSIZE % 128 == 0
+       */
+      sha512_process_block (buffer, BLOCKSIZE, &ctx);
+    }
+
+ process_partial_block:;
+
+  /* Process any remaining bytes.  */
+  if (sum > 0)
+    sha512_process_bytes (buffer, sum, &ctx);
+
+  /* Construct result in desired memory.  */
+  sha384_finish_ctx (&ctx, resblock);
+  free (buffer);
+  return 0;
+}
+
+/* Compute SHA512 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+void *
+sha512_buffer (const char *buffer, size_t len, void *resblock)
+{
+  struct sha512_ctx ctx;
+
+  /* Initialize the computation context.  */
+  sha512_init_ctx (&ctx);
+
+  /* Process whole buffer but last len % 128 bytes.  */
+  sha512_process_bytes (buffer, len, &ctx);
+
+  /* Put result in desired memory area.  */
+  return sha512_finish_ctx (&ctx, resblock);
+}
+
+void *
+sha384_buffer (const char *buffer, size_t len, void *resblock)
+{
+  struct sha512_ctx ctx;
+
+  /* Initialize the computation context.  */
+  sha384_init_ctx (&ctx);
+
+  /* Process whole buffer but last len % 128 bytes.  */
+  sha512_process_bytes (buffer, len, &ctx);
+
+  /* Put result in desired memory area.  */
+  return sha384_finish_ctx (&ctx, resblock);
+}
+
+void
+sha512_process_bytes (const void *buffer, size_t len, struct sha512_ctx *ctx)
+{
+  /* When we already have some bits in our internal buffer concatenate
+     both inputs first.  */
+  if (ctx->buflen != 0)
+    {
+      size_t left_over = ctx->buflen;
+      size_t add = 256 - left_over > len ? len : 256 - left_over;
+
+      memcpy (&((char *) ctx->buffer)[left_over], buffer, add);
+      ctx->buflen += add;
+
+      if (ctx->buflen > 128)
+        {
+          sha512_process_block (ctx->buffer, ctx->buflen & ~127, ctx);
+
+          ctx->buflen &= 127;
+          /* The regions in the following copy operation cannot overlap.  */
+          memcpy (ctx->buffer,
+                  &((char *) ctx->buffer)[(left_over + add) & ~127],
+                  ctx->buflen);
+        }
+
+      buffer = (const char *) buffer + add;
+      len -= add;
+    }
+
+  /* Process available complete blocks.  */
+  if (len >= 128)
+    {
+#if !_STRING_ARCH_unaligned
+# define alignof(type) offsetof (struct { char c; type x; }, x)
+# define UNALIGNED_P(p) (((size_t) p) % alignof (u64) != 0)
+      if (UNALIGNED_P (buffer))
+        while (len > 128)
+          {
+            sha512_process_block (memcpy (ctx->buffer, buffer, 128), 128, ctx);
+            buffer = (const char *) buffer + 128;
+            len -= 128;
+          }
+      else
+#endif
+        {
+          sha512_process_block (buffer, len & ~127, ctx);
+          buffer = (const char *) buffer + (len & ~127);
+          len &= 127;
+        }
+    }
+
+  /* Move remaining bytes in internal buffer.  */
+  if (len > 0)
+    {
+      size_t left_over = ctx->buflen;
+
+      memcpy (&((char *) ctx->buffer)[left_over], buffer, len);
+      left_over += len;
+      if (left_over >= 128)
+        {
+          sha512_process_block (ctx->buffer, 128, ctx);
+          left_over -= 128;
+          memcpy (ctx->buffer, &ctx->buffer[16], left_over);
+        }
+      ctx->buflen = left_over;
+    }
+}
+
+/* --- Code below is the primary difference between sha1.c and sha512.c --- */
+
+/* SHA512 round constants */
+#define K(I) sha512_round_constants[I]
+static u64 const sha512_round_constants[80] = {
+  u64init (0x428a2f98, 0xd728ae22), u64init (0x71374491, 0x23ef65cd),
+  u64init (0xb5c0fbcf, 0xec4d3b2f), u64init (0xe9b5dba5, 0x8189dbbc),
+  u64init (0x3956c25b, 0xf348b538), u64init (0x59f111f1, 0xb605d019),
+  u64init (0x923f82a4, 0xaf194f9b), u64init (0xab1c5ed5, 0xda6d8118),
+  u64init (0xd807aa98, 0xa3030242), u64init (0x12835b01, 0x45706fbe),
+  u64init (0x243185be, 0x4ee4b28c), u64init (0x550c7dc3, 0xd5ffb4e2),
+  u64init (0x72be5d74, 0xf27b896f), u64init (0x80deb1fe, 0x3b1696b1),
+  u64init (0x9bdc06a7, 0x25c71235), u64init (0xc19bf174, 0xcf692694),
+  u64init (0xe49b69c1, 0x9ef14ad2), u64init (0xefbe4786, 0x384f25e3),
+  u64init (0x0fc19dc6, 0x8b8cd5b5), u64init (0x240ca1cc, 0x77ac9c65),
+  u64init (0x2de92c6f, 0x592b0275), u64init (0x4a7484aa, 0x6ea6e483),
+  u64init (0x5cb0a9dc, 0xbd41fbd4), u64init (0x76f988da, 0x831153b5),
+  u64init (0x983e5152, 0xee66dfab), u64init (0xa831c66d, 0x2db43210),
+  u64init (0xb00327c8, 0x98fb213f), u64init (0xbf597fc7, 0xbeef0ee4),
+  u64init (0xc6e00bf3, 0x3da88fc2), u64init (0xd5a79147, 0x930aa725),
+  u64init (0x06ca6351, 0xe003826f), u64init (0x14292967, 0x0a0e6e70),
+  u64init (0x27b70a85, 0x46d22ffc), u64init (0x2e1b2138, 0x5c26c926),
+  u64init (0x4d2c6dfc, 0x5ac42aed), u64init (0x53380d13, 0x9d95b3df),
+  u64init (0x650a7354, 0x8baf63de), u64init (0x766a0abb, 0x3c77b2a8),
+  u64init (0x81c2c92e, 0x47edaee6), u64init (0x92722c85, 0x1482353b),
+  u64init (0xa2bfe8a1, 0x4cf10364), u64init (0xa81a664b, 0xbc423001),
+  u64init (0xc24b8b70, 0xd0f89791), u64init (0xc76c51a3, 0x0654be30),
+  u64init (0xd192e819, 0xd6ef5218), u64init (0xd6990624, 0x5565a910),
+  u64init (0xf40e3585, 0x5771202a), u64init (0x106aa070, 0x32bbd1b8),
+  u64init (0x19a4c116, 0xb8d2d0c8), u64init (0x1e376c08, 0x5141ab53),
+  u64init (0x2748774c, 0xdf8eeb99), u64init (0x34b0bcb5, 0xe19b48a8),
+  u64init (0x391c0cb3, 0xc5c95a63), u64init (0x4ed8aa4a, 0xe3418acb),
+  u64init (0x5b9cca4f, 0x7763e373), u64init (0x682e6ff3, 0xd6b2b8a3),
+  u64init (0x748f82ee, 0x5defb2fc), u64init (0x78a5636f, 0x43172f60),
+  u64init (0x84c87814, 0xa1f0ab72), u64init (0x8cc70208, 0x1a6439ec),
+  u64init (0x90befffa, 0x23631e28), u64init (0xa4506ceb, 0xde82bde9),
+  u64init (0xbef9a3f7, 0xb2c67915), u64init (0xc67178f2, 0xe372532b),
+  u64init (0xca273ece, 0xea26619c), u64init (0xd186b8c7, 0x21c0c207),
+  u64init (0xeada7dd6, 0xcde0eb1e), u64init (0xf57d4f7f, 0xee6ed178),
+  u64init (0x06f067aa, 0x72176fba), u64init (0x0a637dc5, 0xa2c898a6),
+  u64init (0x113f9804, 0xbef90dae), u64init (0x1b710b35, 0x131c471b),
+  u64init (0x28db77f5, 0x23047d84), u64init (0x32caab7b, 0x40c72493),
+  u64init (0x3c9ebe0a, 0x15c9bebc), u64init (0x431d67c4, 0x9c100d4c),
+  u64init (0x4cc5d4be, 0xcb3e42b6), u64init (0x597f299c, 0xfc657e2a),
+  u64init (0x5fcb6fab, 0x3ad6faec), u64init (0x6c44198c, 0x4a475817),
+};
+
+/* Round functions.  */
+#define F2(A, B, C) u64or (u64and (A, B), u64and (C, u64or (A, B)))
+#define F1(E, F, G) u64xor (G, u64and (E, u64xor (F, G)))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 128 == 0.
+   Most of this code comes from GnuPG's cipher/sha1.c.  */
+
+void
+sha512_process_block (const void *buffer, size_t len, struct sha512_ctx *ctx)
+{
+  u64 const *words = buffer;
+  u64 const *endp = words + len / sizeof (u64);
+  u64 x[16];
+  u64 a = ctx->state[0];
+  u64 b = ctx->state[1];
+  u64 c = ctx->state[2];
+  u64 d = ctx->state[3];
+  u64 e = ctx->state[4];
+  u64 f = ctx->state[5];
+  u64 g = ctx->state[6];
+  u64 h = ctx->state[7];
+
+  /* First increment the byte count.  FIPS PUB 180-2 specifies the possible
+     length of the file up to 2^128 bits.  Here we only compute the
+     number of bytes.  Do a double word increment.  */
+  ctx->total[0] = u64plus (ctx->total[0], u64lo (len));
+  if (u64lt (ctx->total[0], u64lo (len)))
+    ctx->total[1] = u64plus (ctx->total[1], u64lo (1));
+
+#define S0(x) u64xor (u64rol(x, 63), u64xor (u64rol (x, 56), u64shr (x, 7)))
+#define S1(x) u64xor (u64rol (x, 45), u64xor (u64rol (x, 3), u64shr (x, 6)))
+#define SS0(x) u64xor (u64rol (x, 36), u64xor (u64rol (x, 30), u64rol (x, 25)))
+#define SS1(x) u64xor (u64rol(x, 50), u64xor (u64rol (x, 46), u64rol (x, 23)))
+
+#define M(I) (x[(I) & 15]                                                 \
+              = u64plus (x[(I) & 15],                                     \
+                         u64plus (S1 (x[((I) - 2) & 15]),                 \
+                                  u64plus (x[((I) - 7) & 15],             \
+                                           S0 (x[((I) - 15) & 15])))))
+
+#define R(A, B, C, D, E, F, G, H, K, M)                                   \
+  do                                                                      \
+    {                                                                     \
+      u64 t0 = u64plus (SS0 (A), F2 (A, B, C));                           \
+      u64 t1 =                                                            \
+        u64plus (H, u64plus (SS1 (E),                                     \
+                             u64plus (F1 (E, F, G), u64plus (K, M))));    \
+      D = u64plus (D, t1);                                                \
+      H = u64plus (t0, t1);                                               \
+    }                                                                     \
+  while (0)
+
+  while (words < endp)
+    {
+      int t;
+      /* FIXME: see sha1.c for a better implementation.  */
+      for (t = 0; t < 16; t++)
+        {
+          x[t] = SWAP (*words);
+          words++;
+        }
+
+      R( a, b, c, d, e, f, g, h, K( 0), x[ 0] );
+      R( h, a, b, c, d, e, f, g, K( 1), x[ 1] );
+      R( g, h, a, b, c, d, e, f, K( 2), x[ 2] );
+      R( f, g, h, a, b, c, d, e, K( 3), x[ 3] );
+      R( e, f, g, h, a, b, c, d, K( 4), x[ 4] );
+      R( d, e, f, g, h, a, b, c, K( 5), x[ 5] );
+      R( c, d, e, f, g, h, a, b, K( 6), x[ 6] );
+      R( b, c, d, e, f, g, h, a, K( 7), x[ 7] );
+      R( a, b, c, d, e, f, g, h, K( 8), x[ 8] );
+      R( h, a, b, c, d, e, f, g, K( 9), x[ 9] );
+      R( g, h, a, b, c, d, e, f, K(10), x[10] );
+      R( f, g, h, a, b, c, d, e, K(11), x[11] );
+      R( e, f, g, h, a, b, c, d, K(12), x[12] );
+      R( d, e, f, g, h, a, b, c, K(13), x[13] );
+      R( c, d, e, f, g, h, a, b, K(14), x[14] );
+      R( b, c, d, e, f, g, h, a, K(15), x[15] );
+      R( a, b, c, d, e, f, g, h, K(16), M(16) );
+      R( h, a, b, c, d, e, f, g, K(17), M(17) );
+      R( g, h, a, b, c, d, e, f, K(18), M(18) );
+      R( f, g, h, a, b, c, d, e, K(19), M(19) );
+      R( e, f, g, h, a, b, c, d, K(20), M(20) );
+      R( d, e, f, g, h, a, b, c, K(21), M(21) );
+      R( c, d, e, f, g, h, a, b, K(22), M(22) );
+      R( b, c, d, e, f, g, h, a, K(23), M(23) );
+      R( a, b, c, d, e, f, g, h, K(24), M(24) );
+      R( h, a, b, c, d, e, f, g, K(25), M(25) );
+      R( g, h, a, b, c, d, e, f, K(26), M(26) );
+      R( f, g, h, a, b, c, d, e, K(27), M(27) );
+      R( e, f, g, h, a, b, c, d, K(28), M(28) );
+      R( d, e, f, g, h, a, b, c, K(29), M(29) );
+      R( c, d, e, f, g, h, a, b, K(30), M(30) );
+      R( b, c, d, e, f, g, h, a, K(31), M(31) );
+      R( a, b, c, d, e, f, g, h, K(32), M(32) );
+      R( h, a, b, c, d, e, f, g, K(33), M(33) );
+      R( g, h, a, b, c, d, e, f, K(34), M(34) );
+      R( f, g, h, a, b, c, d, e, K(35), M(35) );
+      R( e, f, g, h, a, b, c, d, K(36), M(36) );
+      R( d, e, f, g, h, a, b, c, K(37), M(37) );
+      R( c, d, e, f, g, h, a, b, K(38), M(38) );
+      R( b, c, d, e, f, g, h, a, K(39), M(39) );
+      R( a, b, c, d, e, f, g, h, K(40), M(40) );
+      R( h, a, b, c, d, e, f, g, K(41), M(41) );
+      R( g, h, a, b, c, d, e, f, K(42), M(42) );
+      R( f, g, h, a, b, c, d, e, K(43), M(43) );
+      R( e, f, g, h, a, b, c, d, K(44), M(44) );
+      R( d, e, f, g, h, a, b, c, K(45), M(45) );
+      R( c, d, e, f, g, h, a, b, K(46), M(46) );
+      R( b, c, d, e, f, g, h, a, K(47), M(47) );
+      R( a, b, c, d, e, f, g, h, K(48), M(48) );
+      R( h, a, b, c, d, e, f, g, K(49), M(49) );
+      R( g, h, a, b, c, d, e, f, K(50), M(50) );
+      R( f, g, h, a, b, c, d, e, K(51), M(51) );
+      R( e, f, g, h, a, b, c, d, K(52), M(52) );
+      R( d, e, f, g, h, a, b, c, K(53), M(53) );
+      R( c, d, e, f, g, h, a, b, K(54), M(54) );
+      R( b, c, d, e, f, g, h, a, K(55), M(55) );
+      R( a, b, c, d, e, f, g, h, K(56), M(56) );
+      R( h, a, b, c, d, e, f, g, K(57), M(57) );
+      R( g, h, a, b, c, d, e, f, K(58), M(58) );
+      R( f, g, h, a, b, c, d, e, K(59), M(59) );
+      R( e, f, g, h, a, b, c, d, K(60), M(60) );
+      R( d, e, f, g, h, a, b, c, K(61), M(61) );
+      R( c, d, e, f, g, h, a, b, K(62), M(62) );
+      R( b, c, d, e, f, g, h, a, K(63), M(63) );
+      R( a, b, c, d, e, f, g, h, K(64), M(64) );
+      R( h, a, b, c, d, e, f, g, K(65), M(65) );
+      R( g, h, a, b, c, d, e, f, K(66), M(66) );
+      R( f, g, h, a, b, c, d, e, K(67), M(67) );
+      R( e, f, g, h, a, b, c, d, K(68), M(68) );
+      R( d, e, f, g, h, a, b, c, K(69), M(69) );
+      R( c, d, e, f, g, h, a, b, K(70), M(70) );
+      R( b, c, d, e, f, g, h, a, K(71), M(71) );
+      R( a, b, c, d, e, f, g, h, K(72), M(72) );
+      R( h, a, b, c, d, e, f, g, K(73), M(73) );
+      R( g, h, a, b, c, d, e, f, K(74), M(74) );
+      R( f, g, h, a, b, c, d, e, K(75), M(75) );
+      R( e, f, g, h, a, b, c, d, K(76), M(76) );
+      R( d, e, f, g, h, a, b, c, K(77), M(77) );
+      R( c, d, e, f, g, h, a, b, K(78), M(78) );
+      R( b, c, d, e, f, g, h, a, K(79), M(79) );
+
+      a = ctx->state[0] = u64plus (ctx->state[0], a);
+      b = ctx->state[1] = u64plus (ctx->state[1], b);
+      c = ctx->state[2] = u64plus (ctx->state[2], c);
+      d = ctx->state[3] = u64plus (ctx->state[3], d);
+      e = ctx->state[4] = u64plus (ctx->state[4], e);
+      f = ctx->state[5] = u64plus (ctx->state[5], f);
+      g = ctx->state[6] = u64plus (ctx->state[6], g);
+      h = ctx->state[7] = u64plus (ctx->state[7], h);
+    }
+}
diff --git a/sha512.h b/sha512.h
new file mode 100644 (file)
index 0000000..ea85194
--- /dev/null
+++ b/sha512.h
@@ -0,0 +1,95 @@
+/* Declarations of functions and data types used for SHA512 and SHA384 sum
+   library functions.
+   Copyright (C) 2005, 2006, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   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.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef SHA512_H
+# define SHA512_H 1
+
+# include <stdio.h>
+
+# include "u64.h"
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+/* Structure to save state of computation between the single steps.  */
+struct sha512_ctx
+{
+  u64 state[8];
+
+  u64 total[2];
+  size_t buflen;
+  u64 buffer[32];
+};
+
+enum { SHA384_DIGEST_SIZE = 384 / 8 };
+enum { SHA512_DIGEST_SIZE = 512 / 8 };
+
+/* Initialize structure containing state of computation. */
+extern void sha512_init_ctx (struct sha512_ctx *ctx);
+extern void sha384_init_ctx (struct sha512_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is necessary that LEN is a multiple of 128!!! */
+extern void sha512_process_block (const void *buffer, size_t len,
+                                  struct sha512_ctx *ctx);
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 128.  */
+extern void sha512_process_bytes (const void *buffer, size_t len,
+                                  struct sha512_ctx *ctx);
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 64 (48) bytes following RESBUF.  The result is always in little
+   endian byte order, so that a byte-wise output yields to the wanted
+   ASCII representation of the message digest.  */
+extern void *sha512_finish_ctx (struct sha512_ctx *ctx, void *resbuf);
+extern void *sha384_finish_ctx (struct sha512_ctx *ctx, void *resbuf);
+
+
+/* Put result from CTX in first 64 (48) bytes following RESBUF.  The result is
+   always in little endian byte order, so that a byte-wise output yields
+   to the wanted ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *sha512_read_ctx (const struct sha512_ctx *ctx, void *resbuf);
+extern void *sha384_read_ctx (const struct sha512_ctx *ctx, void *resbuf);
+
+
+/* Compute SHA512 (SHA384) message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 64 (48) bytes
+   beginning at RESBLOCK.  */
+extern int sha512_stream (FILE *stream, void *resblock);
+extern int sha384_stream (FILE *stream, void *resblock);
+
+/* Compute SHA512 (SHA384) message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *sha512_buffer (const char *buffer, size_t len, void *resblock);
+extern void *sha384_buffer (const char *buffer, size_t len, void *resblock);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
diff --git a/site.c b/site.c
new file mode 100644 (file)
index 0000000..3796889
--- /dev/null
+++ b/site.c
@@ -0,0 +1,2605 @@
+/* site.c - manage communication with a remote network site */
+
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+/* The 'site' code doesn't know anything about the structure of the
+   packets it's transmitting.  In fact, under the new netlink
+   configuration scheme it doesn't need to know anything at all about
+   IP addresses, except how to contact its peer.  This means it could
+   potentially be used to tunnel other protocols too (IPv6, IPX, plain
+   old Ethernet frames) if appropriate netlink code can be written
+   (and that ought not to be too hard, eg. using the TUN/TAP device to
+   pretend to be an Ethernet interface).  */
+
+/* At some point in the future the netlink code will be asked for
+   configuration information to go in the PING/PONG packets at the end
+   of the key exchange. */
+
+#include "secnet.h"
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <sys/socket.h>
+
+#include <sys/mman.h>
+#include "util.h"
+#include "unaligned.h"
+#include "magic.h"
+
+#define SETUP_BUFFER_LEN 2048
+
+#define DEFAULT_KEY_LIFETIME                  (3600*1000) /* [ms] */
+#define DEFAULT_KEY_RENEGOTIATE_GAP           (5*60*1000) /* [ms] */
+#define DEFAULT_SETUP_RETRIES 5
+#define DEFAULT_SETUP_RETRY_INTERVAL             (2*1000) /* [ms] */
+#define DEFAULT_WAIT_TIME                       (20*1000) /* [ms] */
+
+#define DEFAULT_MOBILE_KEY_LIFETIME      (2*24*3600*1000) /* [ms] */
+#define DEFAULT_MOBILE_KEY_RENEGOTIATE_GAP (12*3600*1000) /* [ms] */
+#define DEFAULT_MOBILE_SETUP_RETRIES 30
+#define DEFAULT_MOBILE_SETUP_RETRY_INTERVAL      (1*1000) /* [ms] */
+#define DEFAULT_MOBILE_WAIT_TIME                (10*1000) /* [ms] */
+
+#define DEFAULT_MOBILE_PEER_EXPIRY            (2*60)      /* [s] */
+
+/* Each site can be in one of several possible states. */
+
+/* States:
+   SITE_STOP         - nothing is allowed to happen; tunnel is down;
+                       all session keys have been erased
+     -> SITE_RUN upon external instruction
+   SITE_RUN          - site up, maybe with valid key
+     -> SITE_RESOLVE upon outgoing packet and no valid key
+         we start name resolution for the other end of the tunnel
+     -> SITE_SENTMSG2 upon valid incoming message 1 and suitable time
+         we send an appropriate message 2
+   SITE_RESOLVE      - waiting for name resolution
+     -> SITE_SENTMSG1 upon successful resolution
+         we send an appropriate message 1
+     -> SITE_SENTMSG2 upon valid incoming message 1 (then abort resolution)
+         we abort resolution and 
+     -> SITE_WAIT on timeout or resolution failure
+   SITE_SENTMSG1
+     -> SITE_SENTMSG2 upon valid incoming message 1 from higher priority end
+     -> SITE_SENTMSG3 upon valid incoming message 2
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG2
+     -> SITE_SENTMSG4 upon valid incoming message 3
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG3
+     -> SITE_SENTMSG5 upon valid incoming message 4
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG4
+     -> SITE_RUN upon valid incoming message 5
+     -> SITE_WAIT on timeout
+   SITE_SENTMSG5
+     -> SITE_RUN upon valid incoming message 6
+     -> SITE_WAIT on timeout
+   SITE_WAIT         - failed to establish key; do nothing for a while
+     -> SITE_RUN on timeout
+   */
+
+#define SITE_STOP     0
+#define SITE_RUN      1
+#define SITE_RESOLVE  2
+#define SITE_SENTMSG1 3
+#define SITE_SENTMSG2 4
+#define SITE_SENTMSG3 5
+#define SITE_SENTMSG4 6
+#define SITE_SENTMSG5 7
+#define SITE_WAIT     8
+
+#define CASES_MSG3_KNOWN LABEL_MSG3: case LABEL_MSG3BIS
+
+struct msg;
+
+int32_t site_max_start_pad = 4*4;
+
+static cstring_t state_name(uint32_t state)
+{
+    switch (state) {
+    case 0: return "STOP";
+    case 1: return "RUN";
+    case 2: return "RESOLVE";
+    case 3: return "SENTMSG1";
+    case 4: return "SENTMSG2";
+    case 5: return "SENTMSG3";
+    case 6: return "SENTMSG4";
+    case 7: return "SENTMSG5";
+    case 8: return "WAIT";
+    default: return "*bad state*";
+    }
+}
+
+#define NONCELEN 8
+
+#define LOG_UNEXPECTED    0x00000001
+#define LOG_SETUP_INIT    0x00000002
+#define LOG_SETUP_TIMEOUT 0x00000004
+#define LOG_ACTIVATE_KEY  0x00000008
+#define LOG_TIMEOUT_KEY   0x00000010
+#define LOG_SEC           0x00000020
+#define LOG_STATE         0x00000040
+#define LOG_DROP          0x00000080
+#define LOG_DUMP          0x00000100
+#define LOG_ERROR         0x00000400
+#define LOG_PEER_ADDRS    0x00000800
+
+static struct flagstr log_event_table[]={
+    { "unexpected", LOG_UNEXPECTED },
+    { "setup-init", LOG_SETUP_INIT },
+    { "setup-timeout", LOG_SETUP_TIMEOUT },
+    { "activate-key", LOG_ACTIVATE_KEY },
+    { "timeout-key", LOG_TIMEOUT_KEY },
+    { "security", LOG_SEC },
+    { "state-change", LOG_STATE },
+    { "packet-drop", LOG_DROP },
+    { "dump-packets", LOG_DUMP },
+    { "errors", LOG_ERROR },
+    { "peer-addrs", LOG_PEER_ADDRS },
+    { "default", LOG_SETUP_INIT|LOG_SETUP_TIMEOUT|
+      LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SEC|LOG_ERROR },
+    { "all", 0xffffffff },
+    { NULL, 0 }
+};
+
+
+/***** TRANSPORT PEERS declarations *****/
+
+/* Details of "mobile peer" semantics:
+
+   - We use the same data structure for the different configurations,
+     but manage it with different algorithms.
+   
+   - We record up to mobile_peers_max peer address/port numbers
+     ("peers") for key setup, and separately up to mobile_peers_max
+     for data transfer.
+
+   - In general, we make a new set of addrs (see below) when we start
+     a new key exchange; the key setup addrs become the data transport
+     addrs when key setup complets.
+
+   If our peer is mobile:
+
+   - We send to all recent addresses of incoming packets, plus
+     initially all configured addresses (which we also expire).
+
+   - So, we record addrs of good incoming packets, as follows:
+      1. expire any peers last seen >120s ("mobile-peer-expiry") ago
+      2. add the peer of the just received packet to the applicable list
+         (possibly evicting the oldest entries to make room)
+     NB that we do not expire peers until an incoming packet arrives.
+
+   - If the peer has a configured address or name, we record them the
+     same way, but only as a result of our own initiation of key
+     setup.  (We might evict some incoming packet addrs to make room.)
+
+   - The default number of addrs to keep is 3, or 4 if we have a
+     configured name or address.  That's space for two configured
+     addresses (one IPv6 and one IPv4), plus two received addresses.
+
+   - Outgoing packets are sent to every recorded address in the
+     applicable list.  Any unsupported[1] addresses are deleted from
+     the list right away.  (This should only happen to configured
+     addresses, of course, but there is no need to check that.)
+
+   - When we successfully complete a key setup, we merge the key setup
+     peers into the data transfer peers.
+
+   [1] An unsupported address is one for whose AF we don't have a
+     socket (perhaps because we got EAFNOSUPPORT or some such) or for
+     which sendto gives ENETUNREACH.
+
+   If neither end is mobile:
+
+   - When peer initiated the key exchange, we use the incoming packet
+     address.
+
+   - When we initiate the key exchange, we try configured addresses
+     until we get one which isn't unsupported then fixate on that.
+
+   - When we complete a key setup, we replace the data transport peers
+     with those from the key setup.
+
+   If we are mobile:
+
+   - We can't tell when local network setup changes so we can't cache
+     the unsupported addrs and completely remove the spurious calls to
+     sendto, but we can optimise things a bit by deprioritising addrs
+     which seem to be unsupported.
+
+   - Use only configured addresses.  (Except, that if our peer
+     initiated a key exchange we use the incoming packet address until
+     our name resolution completes.)
+
+   - When we send a packet, try each address in turn; if addr
+     supported, put that address to the end of the list for future
+     packets, and go onto the next address.
+
+   - When we complete a key setup, we replace the data transport peers
+     with those from the key setup.
+
+   */
+
+typedef struct {
+    struct timeval last;
+    struct comm_addr addr;
+} transport_peer;
+
+typedef struct {
+/* configuration information */
+/* runtime information */
+    int npeers;
+    transport_peer peers[MAX_PEER_ADDRS];
+} transport_peers;
+
+/* Basic operations on transport peer address sets */
+static void transport_peers_clear(struct site *st, transport_peers *peers);
+static int transport_peers_valid(transport_peers *peers);
+static void transport_peers_copy(struct site *st, transport_peers *dst,
+                                const transport_peers *src);
+
+/* Record address of incoming setup packet; resp. data packet. */
+static void transport_setup_msgok(struct site *st, const struct comm_addr *a);
+static void transport_data_msgok(struct site *st, const struct comm_addr *a);
+
+/* Initialise the setup addresses.  Called before we send the first
+ * packet in a key exchange.  If we are the initiator, as a result of
+ * resolve completing (or being determined not to be relevant) or an
+ * incoming PROD; if we are the responder, as a result of the MSG1. */
+static bool_t transport_compute_setupinit_peers(struct site *st,
+        const struct comm_addr *configured_addrs /* 0 if none or not found */,
+        int n_configured_addrs /* 0 if none or not found */,
+        const struct comm_addr *incoming_packet_addr /* 0 if none */);
+
+/* Called if we are the responder in a key setup, when the resolve
+ * completes.  transport_compute_setupinit_peers will hvae been called
+ * earlier.  If _complete is called, we are still doing the key setup
+ * (and we should use the new values for both the rest of the key
+ * setup and the ongoing data exchange); if _tardy is called, the key
+ * setup is done (either completed or not) and only the data peers are
+ * relevant */
+static void transport_resolve_complete(struct site *st,
+        const struct comm_addr *addrs, int naddrs);
+static void transport_resolve_complete_tardy(struct site *st,
+        const struct comm_addr *addrs, int naddrs);
+
+static void transport_xmit(struct site *st, transport_peers *peers,
+                          struct buffer_if *buf, bool_t candebug);
+
+ /***** END of transport peers declarations *****/
+
+
+struct data_key {
+    struct transform_inst_if *transform;
+    uint64_t key_timeout; /* End of life of current key */
+    uint32_t remote_session_id;
+};
+
+struct site {
+    closure_t cl;
+    struct site_if ops;
+/* configuration information */
+    string_t localname;
+    string_t remotename;
+    bool_t keepalive;
+    bool_t local_mobile, peer_mobile; /* Mobile client support */
+    int32_t transport_peers_max;
+    string_t tunname; /* localname<->remotename by default, used in logs */
+    cstring_t *addresses; /* DNS name or address(es) for bootstrapping, optional */
+    int remoteport; /* Port for bootstrapping, optional */
+    uint32_t mtu_target;
+    struct netlink_if *netlink;
+    struct comm_if **comms;
+    struct comm_clientinfo **commclientinfos;
+    int ncomms;
+    struct resolver_if *resolver;
+    struct log_if *log;
+    struct random_if *random;
+    struct sigprivkey_if *privkey;
+    struct sigpubkey_if *pubkey;
+    struct transform_if **transforms;
+    int ntransforms;
+    struct dh_if *dh;
+
+    uint32_t index; /* Index of this site */
+    uint32_t early_capabilities;
+    uint32_t local_capabilities;
+    int32_t setup_retries; /* How many times to send setup packets */
+    int32_t setup_retry_interval; /* Initial timeout for setup packets */
+    int32_t wait_timeout_mean; /* How long to wait if setup unsuccessful */
+    int32_t mobile_peer_expiry; /* How long to remember 2ary addresses */
+    int32_t key_lifetime; /* How long a key lasts once set up */
+    int32_t key_renegotiate_time; /* If we see traffic (or a keepalive)
+                                     after this time, initiate a new
+                                     key exchange */
+
+    bool_t our_name_later; /* our name > peer name */
+    uint32_t log_events;
+
+/* runtime information */
+    uint32_t state;
+    uint64_t now; /* Most recently seen time */
+    bool_t allow_send_prod;
+    bool_t msg1_crossed_logged;
+    int resolving_count;
+    int resolving_n_results_all;
+    int resolving_n_results_stored;
+    struct comm_addr resolving_results[MAX_PEER_ADDRS];
+
+    /* The currently established session */
+    struct data_key current;
+    struct data_key auxiliary_key;
+    bool_t auxiliary_is_new;
+    uint64_t renegotiate_key_time; /* When we can negotiate a new key */
+    uint64_t auxiliary_renegotiate_key_time;
+    transport_peers peers; /* Current address(es) of peer for data traffic */
+
+    /* The current key setup protocol exchange.  We can only be
+       involved in one of these at a time.  There's a potential for
+       denial of service here (the attacker keeps sending a setup
+       packet; we keep trying to continue the exchange, and have to
+       timeout before we can listen for another setup packet); perhaps
+       we should keep a list of 'bad' sources for setup packets. */
+    uint32_t remote_capabilities;
+    uint16_t remote_adv_mtu;
+    struct transform_if *chosen_transform;
+    uint32_t setup_session_id;
+    transport_peers setup_peers;
+    uint8_t localN[NONCELEN]; /* Nonces for key exchange */
+    uint8_t remoteN[NONCELEN];
+    struct buffer_if buffer; /* Current outgoing key exchange packet */
+    struct buffer_if scratch;
+    int32_t retries; /* Number of retries remaining */
+    uint64_t timeout; /* Timeout for current state */
+    uint8_t *dhsecret;
+    uint8_t *sharedsecret;
+    uint32_t sharedsecretlen, sharedsecretallocd;
+    struct transform_inst_if *new_transform; /* For key setup/verify */
+};
+
+static uint32_t event_log_priority(struct site *st, uint32_t event)
+{
+    if (!(event&st->log_events))
+       return 0;
+    switch(event) {
+    case LOG_UNEXPECTED:    return M_INFO;
+    case LOG_SETUP_INIT:    return M_INFO;
+    case LOG_SETUP_TIMEOUT: return M_NOTICE;
+    case LOG_ACTIVATE_KEY:  return M_INFO;
+    case LOG_TIMEOUT_KEY:   return M_INFO;
+    case LOG_SEC:           return M_SECURITY;
+    case LOG_STATE:         return M_DEBUG;
+    case LOG_DROP:          return M_DEBUG;
+    case LOG_DUMP:          return M_DEBUG;
+    case LOG_ERROR:         return M_ERR;
+    case LOG_PEER_ADDRS:    return M_DEBUG;
+    default:                return M_ERR;
+    }
+}
+
+static uint32_t slog_start(struct site *st, uint32_t event)
+{
+    uint32_t class=event_log_priority(st, event);
+    if (class) {
+       slilog_part(st->log,class,"%s: ",st->tunname);
+    }
+    return class;
+}
+
+static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap)
+FORMAT(printf,3,0);
+static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap)
+{
+    uint32_t class;
+
+    class=slog_start(st,event);
+    if (class) {
+       vslilog_part(st->log,class,msg,ap);
+       slilog_part(st->log,class,"\n");
+    }
+}
+
+static void slog(struct site *st, uint32_t event, cstring_t msg, ...)
+FORMAT(printf,3,4);
+static void slog(struct site *st, uint32_t event, cstring_t msg, ...)
+{
+    va_list ap;
+    va_start(ap,msg);
+    vslog(st,event,msg,ap);
+    va_end(ap);
+}
+
+static void logtimeout(struct site *st, const char *fmt, ...)
+FORMAT(printf,2,3);
+static void logtimeout(struct site *st, const char *fmt, ...)
+{
+    uint32_t class=event_log_priority(st,LOG_SETUP_TIMEOUT);
+    if (!class)
+       return;
+
+    va_list ap;
+    va_start(ap,fmt);
+
+    slilog_part(st->log,class,"%s: ",st->tunname);
+    vslilog_part(st->log,class,fmt,ap);
+
+    const char *delim;
+    int i;
+    for (i=0, delim=" (tried ";
+        i<st->setup_peers.npeers;
+        i++, delim=", ") {
+       transport_peer *peer=&st->setup_peers.peers[i];
+       const char *s=comm_addr_to_string(&peer->addr);
+       slilog_part(st->log,class,"%s%s",delim,s);
+    }
+
+    slilog_part(st->log,class,")\n");
+    va_end(ap);
+}
+
+static void set_link_quality(struct site *st);
+static void delete_keys(struct site *st, cstring_t reason, uint32_t loglevel);
+static void delete_one_key(struct site *st, struct data_key *key,
+                          const char *reason /* may be 0 meaning don't log*/,
+                          const char *which /* ignored if !reasonn */,
+                          uint32_t loglevel /* ignored if !reasonn */);
+static bool_t initiate_key_setup(struct site *st, cstring_t reason,
+                                const struct comm_addr *prod_hint);
+static void enter_state_run(struct site *st);
+static bool_t enter_state_resolve(struct site *st);
+static void decrement_resolving_count(struct site *st, int by);
+static bool_t enter_new_state(struct site *st,uint32_t next,
+                             const struct msg *prompt
+                             /* may be 0 for SENTMSG1 */);
+static void enter_state_wait(struct site *st);
+static void activate_new_key(struct site *st);
+
+static bool_t is_transform_valid(struct transform_inst_if *transform)
+{
+    return transform && transform->valid(transform->st);
+}
+
+static bool_t current_valid(struct site *st)
+{
+    return is_transform_valid(st->current.transform);
+}
+
+#define DEFINE_CALL_TRANSFORM(fwdrev)                                  \
+static transform_apply_return                                           \
+call_transform_##fwdrev(struct site *st,                               \
+                                  struct transform_inst_if *transform, \
+                                  struct buffer_if *buf,               \
+                                  const char **errmsg)                 \
+{                                                                      \
+    if (!is_transform_valid(transform)) {                              \
+       *errmsg="transform not set up";                                 \
+       return transform_apply_err;                                     \
+    }                                                                  \
+    return transform->fwdrev(transform->st,buf,errmsg);                        \
+}
+
+DEFINE_CALL_TRANSFORM(forwards)
+DEFINE_CALL_TRANSFORM(reverse)
+
+static void dispose_transform(struct transform_inst_if **transform_var)
+{
+    struct transform_inst_if *transform=*transform_var;
+    if (transform) {
+       transform->delkey(transform->st);
+       transform->destroy(transform->st);
+    }
+    *transform_var = 0;
+}    
+
+#define CHECK_AVAIL(b,l) do { if ((b)->size<(l)) return False; } while(0)
+#define CHECK_EMPTY(b) do { if ((b)->size!=0) return False; } while(0)
+#define CHECK_TYPE(b,t) do { uint32_t type; \
+    CHECK_AVAIL((b),4); \
+    type=buf_unprepend_uint32((b)); \
+    if (type!=(t)) return False; } while(0)
+
+static _Bool type_is_msg34(uint32_t type)
+{
+    switch (type) {
+       case CASES_MSG3_KNOWN: case LABEL_MSG4: return True;
+       default: return False;
+    }
+}
+
+struct parsedname {
+    int32_t len;
+    uint8_t *name;
+    struct buffer_if extrainfo;
+};
+
+struct msg {
+    uint8_t *hashstart;
+    uint32_t dest;
+    uint32_t source;
+    struct parsedname remote;
+    struct parsedname local;
+    uint32_t remote_capabilities;
+    uint16_t remote_mtu;
+    int capab_transformnum;
+    uint8_t *nR;
+    uint8_t *nL;
+    int32_t pklen;
+    char *pk;
+    int32_t hashlen;
+    struct alg_msg_data sig;
+};
+
+static int32_t wait_timeout(struct site *st) {
+    int32_t t = st->wait_timeout_mean;
+    int8_t factor;
+    if (t < INT_MAX/2) {
+       st->random->generate(st->random->st,sizeof(factor),&factor);
+       t += (t / 256) * factor;
+    }
+    return t;
+}
+
+static _Bool set_new_transform(struct site *st, char *pk)
+{
+    _Bool ok;
+
+    /* Make room for the shared key */
+    st->sharedsecretlen=st->chosen_transform->keylen?:st->dh->ceil_len;
+    assert(st->sharedsecretlen);
+    if (st->sharedsecretlen > st->sharedsecretallocd) {
+       st->sharedsecretallocd=st->sharedsecretlen;
+       st->sharedsecret=safe_realloc_ary(st->sharedsecret,1,
+                                         st->sharedsecretallocd,
+                                         "site:sharedsecret");
+    }
+
+    /* Generate the shared key */
+    st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->len,pk,
+                      st->sharedsecret,st->sharedsecretlen);
+
+    /* Set up the transform */
+    struct transform_if *generator=st->chosen_transform;
+    struct transform_inst_if *generated=generator->create(generator->st);
+    ok = generated->setkey(generated->st,st->sharedsecret,
+                          st->sharedsecretlen,st->our_name_later);
+
+    dispose_transform(&st->new_transform);
+    if (!ok) return False;
+    st->new_transform=generated;
+
+    slog(st,LOG_SETUP_INIT,"key exchange negotiated transform"
+        " %d (capabilities ours=%#"PRIx32" theirs=%#"PRIx32")",
+        st->chosen_transform->capab_bit,
+        st->local_capabilities, st->remote_capabilities);
+    return True;
+}
+
+struct xinfoadd {
+    int32_t lenpos, afternul;
+};
+static void append_string_xinfo_start(struct buffer_if *buf,
+                                     struct xinfoadd *xia,
+                                     const char *str)
+    /* Helps construct one of the names with additional info as found
+     * in MSG1..4.  Call this function first, then append all the
+     * desired extra info (not including the nul byte) to the buffer,
+     * then call append_string_xinfo_done. */
+{
+    xia->lenpos = buf->size;
+    buf_append_string(buf,str);
+    buf_append_uint8(buf,0);
+    xia->afternul = buf->size;
+}
+static void append_string_xinfo_done(struct buffer_if *buf,
+                                    struct xinfoadd *xia)
+{
+    /* we just need to adjust the string length */
+    if (buf->size == xia->afternul) {
+       /* no extra info, strip the nul too */
+       buf_unappend_uint8(buf);
+    } else {
+       put_uint16(buf->start+xia->lenpos, buf->size-(xia->lenpos+2));
+    }
+}
+
+/* Build any of msg1 to msg4. msg5 and msg6 are built from the inside
+   out using a transform of config data supplied by netlink */
+static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what,
+                          const struct msg *prompt
+                          /* may be 0 for MSG1 */)
+{
+    string_t dhpub;
+    unsigned minor;
+
+    st->retries=st->setup_retries;
+    BUF_ALLOC(&st->buffer,what);
+    buffer_init(&st->buffer,0);
+    buf_append_uint32(&st->buffer,
+       (type==LABEL_MSG1?0:st->setup_session_id));
+    buf_append_uint32(&st->buffer,st->index);
+    buf_append_uint32(&st->buffer,type);
+
+    struct xinfoadd xia;
+    append_string_xinfo_start(&st->buffer,&xia,st->localname);
+    if ((st->local_capabilities & st->early_capabilities) ||
+       (type != LABEL_MSG1)) {
+       buf_append_uint32(&st->buffer,st->local_capabilities);
+    }
+    if (type_is_msg34(type)) {
+       buf_append_uint16(&st->buffer,st->mtu_target);
+    }
+    append_string_xinfo_done(&st->buffer,&xia);
+
+    buf_append_string(&st->buffer,st->remotename);
+    BUF_ADD_OBJ(append,&st->buffer,st->localN);
+    if (type==LABEL_MSG1) return True;
+    BUF_ADD_OBJ(append,&st->buffer,st->remoteN);
+    if (type==LABEL_MSG2) return True;
+
+    if (hacky_par_mid_failnow()) return False;
+
+    if (MSGMAJOR(type) == 3) do {
+       minor = MSGMINOR(type);
+       if (minor < 1) break;
+       buf_append_uint8(&st->buffer,st->chosen_transform->capab_bit);
+    } while (0);
+
+    dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len);
+    buf_append_string(&st->buffer,dhpub);
+    free(dhpub);
+
+    bool_t ok=st->privkey->sign(st->privkey->st,
+                               st->buffer.start,
+                               st->buffer.size,
+                               &st->buffer);
+    if (!ok) goto fail;
+    return True;
+
+ fail:
+    return False;
+}
+
+static bool_t unpick_name(struct buffer_if *msg, struct parsedname *nm)
+{
+    CHECK_AVAIL(msg,2);
+    nm->len=buf_unprepend_uint16(msg);
+    CHECK_AVAIL(msg,nm->len);
+    nm->name=buf_unprepend(msg,nm->len);
+    uint8_t *nul=memchr(nm->name,0,nm->len);
+    if (!nul) {
+       buffer_readonly_view(&nm->extrainfo,0,0);
+    } else {
+       buffer_readonly_view(&nm->extrainfo, nul+1, msg->start-(nul+1));
+       nm->len=nul-nm->name;
+    }
+    return True;
+}
+
+static bool_t unpick_msg(struct site *st, uint32_t type,
+                        struct buffer_if *msg, struct msg *m)
+{
+    unsigned minor;
+
+    m->capab_transformnum=-1;
+    m->hashstart=msg->start;
+    CHECK_AVAIL(msg,4);
+    m->dest=buf_unprepend_uint32(msg);
+    CHECK_AVAIL(msg,4);
+    m->source=buf_unprepend_uint32(msg);
+    CHECK_TYPE(msg,type);
+    if (!unpick_name(msg,&m->remote)) return False;
+    m->remote_capabilities=0;
+    m->remote_mtu=0;
+    if (m->remote.extrainfo.size) {
+       CHECK_AVAIL(&m->remote.extrainfo,4);
+       m->remote_capabilities=buf_unprepend_uint32(&m->remote.extrainfo);
+    }
+    if (type_is_msg34(type) && m->remote.extrainfo.size) {
+       CHECK_AVAIL(&m->remote.extrainfo,2);
+       m->remote_mtu=buf_unprepend_uint16(&m->remote.extrainfo);
+    }
+    if (!unpick_name(msg,&m->local)) return False;
+    if (type==LABEL_PROD) {
+       CHECK_EMPTY(msg);
+       return True;
+    }
+    CHECK_AVAIL(msg,NONCELEN);
+    m->nR=buf_unprepend(msg,NONCELEN);
+    if (type==LABEL_MSG1) {
+       CHECK_EMPTY(msg);
+       return True;
+    }
+    CHECK_AVAIL(msg,NONCELEN);
+    m->nL=buf_unprepend(msg,NONCELEN);
+    if (type==LABEL_MSG2) {
+       CHECK_EMPTY(msg);
+       return True;
+    }
+    if (MSGMAJOR(type) == 3) do {
+       minor = MSGMINOR(type);
+#define MAYBE_READ_CAP(minminor, kind, dflt) do {                      \
+    if (minor < (minminor))                                            \
+       m->capab_##kind##num = (dflt);                                  \
+    else {                                                             \
+       CHECK_AVAIL(msg, 1);                                            \
+       m->capab_##kind##num = buf_unprepend_uint8(msg);                \
+    }                                                                  \
+} while (0)
+       MAYBE_READ_CAP(1, transform, CAPAB_BIT_ANCIENTTRANSFORM);
+#undef MAYBE_READ_CAP
+    } while (0);
+    CHECK_AVAIL(msg,2);
+    m->pklen=buf_unprepend_uint16(msg);
+    CHECK_AVAIL(msg,m->pklen);
+    m->pk=buf_unprepend(msg,m->pklen);
+    m->hashlen=msg->start-m->hashstart;
+
+    if (!st->pubkey->unpick(st->pubkey->st,msg,&m->sig)) {
+       return False;
+    }
+
+    CHECK_EMPTY(msg);
+
+    return True;
+}
+
+static bool_t name_matches(const struct parsedname *nm, const char *expected)
+{
+    int expected_len=strlen(expected);
+    return
+       nm->len == expected_len &&
+       !memcmp(nm->name, expected, expected_len);
+}    
+
+static bool_t check_msg(struct site *st, uint32_t type, struct msg *m,
+                       cstring_t *error)
+{
+    if (type==LABEL_MSG1) return True;
+
+    /* Check that the site names and our nonce have been sent
+       back correctly, and then store our peer's nonce. */ 
+    if (!name_matches(&m->remote,st->remotename)) {
+       *error="wrong remote site name";
+       return False;
+    }
+    if (!name_matches(&m->local,st->localname)) {
+       *error="wrong local site name";
+       return False;
+    }
+    if (memcmp(m->nL,st->localN,NONCELEN)!=0) {
+       *error="wrong locally-generated nonce";
+       return False;
+    }
+    if (type==LABEL_MSG2) return True;
+    if (!consttime_memeq(m->nR,st->remoteN,NONCELEN)) {
+       *error="wrong remotely-generated nonce";
+       return False;
+    }
+    /* MSG3 has complicated rules about capabilities, which are
+     * handled in process_msg3. */
+    if (MSGMAJOR(type) == 3) return True;
+    if (m->remote_capabilities!=st->remote_capabilities) {
+       *error="remote capabilities changed";
+       return False;
+    }
+    if (type==LABEL_MSG4) return True;
+    *error="unknown message type";
+    return False;
+}
+
+static bool_t kex_init(struct site *st)
+{
+    st->random->generate(st->random->st,NONCELEN,st->localN);
+    return True;
+}
+
+static bool_t generate_msg1(struct site *st, const struct msg *prompt_maybe_0)
+{
+    return
+       generate_msg(st,LABEL_MSG1,"site:MSG1",prompt_maybe_0);
+}
+
+static bool_t process_msg1(struct site *st, struct buffer_if *msg1,
+                          const struct comm_addr *src,
+                          const struct msg *m)
+{
+    /* We've already determined we're in an appropriate state to
+       process an incoming MSG1, and that the MSG1 has correct values
+       of A and B. */
+
+    st->setup_session_id=m->source;
+    st->remote_capabilities=m->remote_capabilities;
+    memcpy(st->remoteN,m->nR,NONCELEN);
+    return True;
+}
+
+static bool_t generate_msg2(struct site *st,
+                           const struct msg *prompt_may_be_null)
+{
+    return
+       generate_msg(st,LABEL_MSG2,"site:MSG2",prompt_may_be_null);
+}
+
+static bool_t process_msg2(struct site *st, struct buffer_if *msg2,
+                          const struct comm_addr *src,
+                          struct msg *m /* returned */)
+{
+    cstring_t err;
+
+    if (!unpick_msg(st,LABEL_MSG2,msg2,m)) return False;
+    if (!check_msg(st,LABEL_MSG2,m,&err)) {
+       slog(st,LOG_SEC,"msg2: %s",err);
+       return False;
+    }
+    st->setup_session_id=m->source;
+    st->remote_capabilities=m->remote_capabilities;
+
+    /* Select the transform to use */
+
+    uint32_t remote_crypto_caps = st->remote_capabilities & CAPAB_TRANSFORM_MASK;
+    if (!remote_crypto_caps)
+       /* old secnets only had this one transform */
+       remote_crypto_caps = 1UL << CAPAB_BIT_ANCIENTTRANSFORM;
+
+#define CHOOSE_CRYPTO(kind, whats) do {                                        \
+    struct kind##_if *iface;                                           \
+    uint32_t bit, ours = 0;                                            \
+    int i;                                                             \
+    for (i= 0; i < st->n##kind##s; i++) {                              \
+       iface=st->kind##s[i];                                           \
+       bit = 1UL << iface->capab_bit;                                  \
+       if (bit & remote_crypto_caps) goto kind##_found;                \
+       ours |= bit;                                                    \
+    }                                                                  \
+    slog(st,LOG_ERROR,"no " whats " in common"                         \
+        " (us %#"PRIx32"; them: %#"PRIx32")",                          \
+        st->local_capabilities & ours, remote_crypto_caps);            \
+    return False;                                                      \
+kind##_found:                                                          \
+    st->chosen_##kind = iface;                                         \
+} while (0)
+
+    CHOOSE_CRYPTO(transform, "transforms");
+
+#undef CHOOSE_CRYPTO
+
+    memcpy(st->remoteN,m->nR,NONCELEN);
+    return True;
+}
+
+static bool_t generate_msg3(struct site *st, const struct msg *prompt)
+{
+    /* Now we have our nonce and their nonce. Think of a secret key,
+       and create message number 3. */
+    st->random->generate(st->random->st,st->dh->len,st->dhsecret);
+    return generate_msg(st,
+                       (st->remote_capabilities & CAPAB_TRANSFORM_MASK)
+                       ? LABEL_MSG3BIS
+                       : LABEL_MSG3,
+                       "site:MSG3",prompt);
+}
+
+static bool_t process_msg3_msg4(struct site *st, struct msg *m)
+{
+    /* Check signature and store g^x mod m */
+    if (!st->pubkey->check(st->pubkey->st,
+                          m->hashstart,m->hashlen,
+                          &m->sig)) {
+       slog(st,LOG_SEC,"msg3/msg4 signature failed check!");
+       return False;
+    }
+
+    st->remote_adv_mtu=m->remote_mtu;
+
+    return True;
+}
+
+static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
+                          const struct comm_addr *src, uint32_t msgtype,
+                          struct msg *m /* returned */)
+{
+    cstring_t err;
+
+    switch (msgtype) {
+       case CASES_MSG3_KNOWN: break;
+       default: assert(0);
+    }
+
+    if (!unpick_msg(st,msgtype,msg3,m)) return False;
+    if (!check_msg(st,msgtype,m,&err)) {
+       slog(st,LOG_SEC,"msg3: %s",err);
+       return False;
+    }
+    uint32_t capab_adv_late = m->remote_capabilities
+       & ~st->remote_capabilities & st->early_capabilities;
+    if (capab_adv_late) {
+       slog(st,LOG_SEC,"msg3 impermissibly adds early capability flag(s)"
+            " %#"PRIx32" (was %#"PRIx32", now %#"PRIx32")",
+            capab_adv_late, st->remote_capabilities, m->remote_capabilities);
+       return False;
+    }
+
+#define CHOSE_CRYPTO(kind, what) do {                                  \
+    struct kind##_if *iface;                                           \
+    int i;                                                             \
+    for (i=0; i<st->n##kind##s; i++) {                                 \
+       iface=st->kind##s[i];                                           \
+       if (iface->capab_bit == m->capab_##kind##num)                   \
+           goto kind##_found;                                          \
+    }                                                                  \
+    slog(st,LOG_SEC,"peer chose unknown-to-us " what " %d!",           \
+        m->capab_##kind##num);                                                 \
+    return False;                                                      \
+kind##_found:                                                          \
+    st->chosen_##kind=iface;                                           \
+} while (0)
+
+    CHOSE_CRYPTO(transform, "transform");
+
+#undef CHOSE_CRYPTO
+
+    if (!process_msg3_msg4(st,m))
+       return False;
+
+    /* Update our idea of the remote site's capabilities, now that we've
+     * verified that its message was authentic.
+     *
+     * Our previous idea of the remote site's capabilities came from the
+     * unauthenticated MSG1.  We've already checked that this new message
+     * doesn't change any of the bits we relied upon in the past, but it may
+     * also have set additional capability bits.  We simply throw those away
+     * now, and use the authentic capabilities from this MSG3. */
+    st->remote_capabilities=m->remote_capabilities;
+
+    /* Terminate their DH public key with a '0' */
+    m->pk[m->pklen]=0;
+    /* Invent our DH secret key */
+    st->random->generate(st->random->st,st->dh->len,st->dhsecret);
+
+    /* Generate the shared key and set up the transform */
+    if (!set_new_transform(st,m->pk)) return False;
+
+    return True;
+}
+
+static bool_t generate_msg4(struct site *st, const struct msg *prompt)
+{
+    /* We have both nonces, their public key and our private key. Generate
+       our public key, sign it and send it to them. */
+    return generate_msg(st,LABEL_MSG4,"site:MSG4",prompt);
+}
+
+static bool_t process_msg4(struct site *st, struct buffer_if *msg4,
+                          const struct comm_addr *src,
+                          struct msg *m /* returned */)
+{
+    cstring_t err;
+
+    if (!unpick_msg(st,LABEL_MSG4,msg4,m)) return False;
+    if (!check_msg(st,LABEL_MSG4,m,&err)) {
+       slog(st,LOG_SEC,"msg4: %s",err);
+       return False;
+    }
+    
+    if (!process_msg3_msg4(st,m))
+       return False;
+
+    /* Terminate their DH public key with a '0' */
+    m->pk[m->pklen]=0;
+
+    /* Generate the shared key and set up the transform */
+    if (!set_new_transform(st,m->pk)) return False;
+
+    return True;
+}
+
+struct msg0 {
+    uint32_t dest;
+    uint32_t source;
+    uint32_t type;
+};
+
+static bool_t unpick_msg0(struct site *st, struct buffer_if *msg0,
+                         struct msg0 *m)
+{
+    CHECK_AVAIL(msg0,4);
+    m->dest=buf_unprepend_uint32(msg0);
+    CHECK_AVAIL(msg0,4);
+    m->source=buf_unprepend_uint32(msg0);
+    CHECK_AVAIL(msg0,4);
+    m->type=buf_unprepend_uint32(msg0);
+    return True;
+    /* Leaves transformed part of buffer untouched */
+}
+
+static bool_t generate_msg5(struct site *st, const struct msg *prompt)
+{
+    cstring_t transform_err;
+
+    BUF_ALLOC(&st->buffer,"site:MSG5");
+    /* We are going to add four words to the message */
+    buffer_init(&st->buffer,calculate_max_start_pad());
+    /* Give the netlink code an opportunity to put its own stuff in the
+       message (configuration information, etc.) */
+    buf_prepend_uint32(&st->buffer,LABEL_MSG5);
+    if (call_transform_forwards(st,st->new_transform,
+                               &st->buffer,&transform_err))
+       return False;
+    buf_prepend_uint32(&st->buffer,LABEL_MSG5);
+    buf_prepend_uint32(&st->buffer,st->index);
+    buf_prepend_uint32(&st->buffer,st->setup_session_id);
+
+    st->retries=st->setup_retries;
+    return True;
+}
+
+static bool_t process_msg5(struct site *st, struct buffer_if *msg5,
+                          const struct comm_addr *src,
+                          struct transform_inst_if *transform)
+{
+    struct msg0 m;
+    cstring_t transform_err;
+
+    if (!unpick_msg0(st,msg5,&m)) return False;
+
+    if (call_transform_reverse(st,transform,msg5,&transform_err)) {
+       /* There's a problem */
+       slog(st,LOG_SEC,"process_msg5: transform: %s",transform_err);
+       return False;
+    }
+    /* Buffer should now contain untransformed PING packet data */
+    CHECK_AVAIL(msg5,4);
+    if (buf_unprepend_uint32(msg5)!=LABEL_MSG5) {
+       slog(st,LOG_SEC,"MSG5/PING packet contained wrong label");
+       return False;
+    }
+    /* Older versions of secnet used to write some config data here
+     * which we ignore.  So we don't CHECK_EMPTY */
+    return True;
+}
+
+static void create_msg6(struct site *st, struct transform_inst_if *transform,
+                       uint32_t session_id)
+{
+    cstring_t transform_err;
+
+    BUF_ALLOC(&st->buffer,"site:MSG6");
+    /* We are going to add four words to the message */
+    buffer_init(&st->buffer,calculate_max_start_pad());
+    /* Give the netlink code an opportunity to put its own stuff in the
+       message (configuration information, etc.) */
+    buf_prepend_uint32(&st->buffer,LABEL_MSG6);
+    transform_apply_return problem =
+       call_transform_forwards(st,transform,
+                               &st->buffer,&transform_err);
+    assert(!problem);
+    buf_prepend_uint32(&st->buffer,LABEL_MSG6);
+    buf_prepend_uint32(&st->buffer,st->index);
+    buf_prepend_uint32(&st->buffer,session_id);
+}
+
+static bool_t generate_msg6(struct site *st, const struct msg *prompt)
+{
+    if (!is_transform_valid(st->new_transform))
+       return False;
+    create_msg6(st,st->new_transform,st->setup_session_id);
+    st->retries=1; /* Peer will retransmit MSG5 if this packet gets lost */
+    return True;
+}
+
+static bool_t process_msg6(struct site *st, struct buffer_if *msg6,
+                          const struct comm_addr *src)
+{
+    struct msg0 m;
+    cstring_t transform_err;
+
+    if (!unpick_msg0(st,msg6,&m)) return False;
+
+    if (call_transform_reverse(st,st->new_transform,msg6,&transform_err)) {
+       /* There's a problem */
+       slog(st,LOG_SEC,"process_msg6: transform: %s",transform_err);
+       return False;
+    }
+    /* Buffer should now contain untransformed PING packet data */
+    CHECK_AVAIL(msg6,4);
+    if (buf_unprepend_uint32(msg6)!=LABEL_MSG6) {
+       slog(st,LOG_SEC,"MSG6/PONG packet contained invalid data");
+       return False;
+    }
+    /* Older versions of secnet used to write some config data here
+     * which we ignore.  So we don't CHECK_EMPTY */
+    return True;
+}
+
+static transform_apply_return
+decrypt_msg0(struct site *st, struct buffer_if *msg0,
+                          const struct comm_addr *src)
+{
+    cstring_t transform_err, auxkey_err, newkey_err="n/a";
+    struct msg0 m;
+    transform_apply_return problem;
+
+    if (!unpick_msg0(st,msg0,&m)) return False;
+
+    /* Keep a copy so we can try decrypting it with multiple keys */
+    buffer_copy(&st->scratch, msg0);
+
+    problem = call_transform_reverse(st,st->current.transform,
+                                    msg0,&transform_err);
+    if (!problem) {
+       if (!st->auxiliary_is_new)
+           delete_one_key(st,&st->auxiliary_key,
+                          "peer has used new key","auxiliary key",LOG_SEC);
+       return 0;
+    }
+    if (transform_apply_return_badseq(problem))
+       goto badseq;
+
+    buffer_copy(msg0, &st->scratch);
+    problem = call_transform_reverse(st,st->auxiliary_key.transform,
+                                    msg0,&auxkey_err);
+    if (!problem) {
+       slog(st,LOG_DROP,"processing packet which uses auxiliary key");
+       if (st->auxiliary_is_new) {
+           /* We previously timed out in state SENTMSG5 but it turns
+            * out that our peer did in fact get our MSG5 and is
+            * using the new key.  So we should switch to it too. */
+           /* This is a bit like activate_new_key. */
+           struct data_key t;
+           t=st->current;
+           st->current=st->auxiliary_key;
+           st->auxiliary_key=t;
+
+           delete_one_key(st,&st->auxiliary_key,"peer has used new key",
+                          "previous key",LOG_SEC);
+           st->auxiliary_is_new=0;
+           st->renegotiate_key_time=st->auxiliary_renegotiate_key_time;
+       }
+       return 0;
+    }
+    if (transform_apply_return_badseq(problem))
+       goto badseq;
+
+    if (st->state==SITE_SENTMSG5) {
+       buffer_copy(msg0, &st->scratch);
+       problem = call_transform_reverse(st,st->new_transform,
+                                        msg0,&newkey_err);
+       if (!problem) {
+           /* It looks like we didn't get the peer's MSG6 */
+           /* This is like a cut-down enter_new_state(SITE_RUN) */
+           slog(st,LOG_STATE,"will enter state RUN (MSG0 with new key)");
+           BUF_FREE(&st->buffer);
+           st->timeout=0;
+           activate_new_key(st);
+           return 0; /* do process the data in this packet */
+       }
+       if (transform_apply_return_badseq(problem))
+           goto badseq;
+    }
+
+    slog(st,LOG_SEC,"transform: %s (aux: %s, new: %s)",
+        transform_err,auxkey_err,newkey_err);
+    initiate_key_setup(st,"incoming message would not decrypt",0);
+    send_nak(src,m.dest,m.source,m.type,msg0,"message would not decrypt");
+    assert(problem);
+    return problem;
+
+ badseq:
+    slog(st,LOG_DROP,"transform: %s (bad seq.)",transform_err);
+    assert(problem);
+    return problem;
+}
+
+static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
+                          const struct comm_addr *src)
+{
+    uint32_t type;
+    transform_apply_return problem;
+
+    problem = decrypt_msg0(st,msg0,src);
+    if (problem==transform_apply_seqdupe) {
+       /* We recently received another copy of this packet, maybe due
+        * to polypath.  That's not a problem; indeed, for the
+        * purposes of transport address management it is a success.
+        * But we don't want to process the packet. */
+       transport_data_msgok(st,src);
+       return False;
+    }
+    if (problem)
+       return False;
+
+    CHECK_AVAIL(msg0,4);
+    type=buf_unprepend_uint32(msg0);
+    switch(type) {
+    case LABEL_MSG7:
+       /* We must forget about the current session. */
+       delete_keys(st,"request from peer",LOG_SEC);
+       /* probably, the peer is shutting down, and this is going to fail,
+        * but we need to be trying to bring the link up again */
+       if (st->keepalive)
+           initiate_key_setup(st,"peer requested key teardown",0);
+       return True;
+    case LABEL_MSG9:
+       /* Deliver to netlink layer */
+       st->netlink->deliver(st->netlink->st,msg0);
+       transport_data_msgok(st,src);
+       /* See whether we should start negotiating a new key */
+       if (st->now > st->renegotiate_key_time)
+           initiate_key_setup(st,"incoming packet in renegotiation window",0);
+       return True;
+    default:
+       slog(st,LOG_SEC,"incoming encrypted message of type %08x "
+            "(unknown)",type);
+       break;
+    }
+    return False;
+}
+
+static void dump_packet(struct site *st, struct buffer_if *buf,
+                       const struct comm_addr *addr, bool_t incoming,
+                       bool_t ok)
+{
+    uint32_t dest=get_uint32(buf->start);
+    uint32_t source=get_uint32(buf->start+4);
+    uint32_t msgtype=get_uint32(buf->start+8);
+
+    if (st->log_events & LOG_DUMP)
+       slilog(st->log,M_DEBUG,"%s: %s: %08x<-%08x: %08x: %s%s",
+              st->tunname,incoming?"incoming":"outgoing",
+              dest,source,msgtype,comm_addr_to_string(addr),
+              ok?"":" - fail");
+}
+
+static bool_t comm_addr_sendmsg(struct site *st,
+                               const struct comm_addr *dest,
+                               struct buffer_if *buf)
+{
+    int i;
+    struct comm_clientinfo *commclientinfo = 0;
+
+    for (i=0; i < st->ncomms; i++) {
+       if (st->comms[i] == dest->comm) {
+           commclientinfo = st->commclientinfos[i];
+           break;
+       }
+    }
+    return dest->comm->sendmsg(dest->comm->st, buf, dest, commclientinfo);
+}
+
+static uint32_t site_status(void *st)
+{
+    return 0;
+}
+
+static bool_t send_msg(struct site *st)
+{
+    if (st->retries>0) {
+       transport_xmit(st, &st->setup_peers, &st->buffer, True);
+       st->timeout=st->now+st->setup_retry_interval;
+       st->retries--;
+       return True;
+    } else if (st->state==SITE_SENTMSG5) {
+       logtimeout(st,"timed out sending MSG5, stashing new key");
+       /* We stash the key we have produced, in case it turns out that
+        * our peer did see our MSG5 after all and starts using it. */
+       /* This is a bit like some of activate_new_key */
+       struct transform_inst_if *t;
+       t=st->auxiliary_key.transform;
+       st->auxiliary_key.transform=st->new_transform;
+       st->new_transform=t;
+       dispose_transform(&st->new_transform);
+
+       st->auxiliary_is_new=1;
+       st->auxiliary_key.key_timeout=st->now+st->key_lifetime;
+       st->auxiliary_renegotiate_key_time=st->now+st->key_renegotiate_time;
+       st->auxiliary_key.remote_session_id=st->setup_session_id;
+
+       enter_state_wait(st);
+       return False;
+    } else {
+       logtimeout(st,"timed out sending key setup packet "
+           "(in state %s)",state_name(st->state));
+       enter_state_wait(st);
+       return False;
+    }
+}
+
+static void site_resolve_callback(void *sst, const struct comm_addr *addrs,
+                                 int stored_naddrs, int all_naddrs,
+                                 const char *address, const char *failwhy)
+{
+    struct site *st=sst;
+
+    if (!stored_naddrs) {
+       slog(st,LOG_ERROR,"resolution of %s failed: %s",address,failwhy);
+    } else {
+       slog(st,LOG_PEER_ADDRS,"resolution of %s completed, %d addrs, eg: %s",
+            address, all_naddrs, comm_addr_to_string(&addrs[0]));;
+
+       int space=st->transport_peers_max-st->resolving_n_results_stored;
+       int n_tocopy=MIN(stored_naddrs,space);
+       COPY_ARRAY(st->resolving_results + st->resolving_n_results_stored,
+                  addrs,
+                  n_tocopy);
+       st->resolving_n_results_stored += n_tocopy;
+       st->resolving_n_results_all += all_naddrs;
+    }
+
+    decrement_resolving_count(st,1);
+}
+
+static void decrement_resolving_count(struct site *st, int by)
+{
+    assert(st->resolving_count>0);
+    st->resolving_count-=by;
+
+    if (st->resolving_count)
+       return;
+
+    /* OK, we are done with them all.  Handle combined results. */
+
+    const struct comm_addr *addrs=st->resolving_results;
+    int naddrs=st->resolving_n_results_stored;
+    assert(naddrs<=st->transport_peers_max);
+
+    if (naddrs) {
+       if (naddrs != st->resolving_n_results_all) {
+           slog(st,LOG_SETUP_INIT,"resolution of supplied addresses/names"
+                " yielded too many results (%d > %d), some ignored",
+                st->resolving_n_results_all, naddrs);
+       }
+       slog(st,LOG_STATE,"resolution completed, %d addrs, eg: %s",
+            naddrs, iaddr_to_string(&addrs[0].ia));;
+    }
+
+    switch (st->state) {
+    case SITE_RESOLVE:
+        if (transport_compute_setupinit_peers(st,addrs,naddrs,0)) {
+           enter_new_state(st,SITE_SENTMSG1,0);
+       } else {
+           /* Can't figure out who to try to to talk to */
+           slog(st,LOG_SETUP_INIT,
+                "key exchange failed: cannot find peer address");
+           enter_state_run(st);
+       }
+       break;
+    case SITE_SENTMSG1: case SITE_SENTMSG2:
+    case SITE_SENTMSG3: case SITE_SENTMSG4:
+    case SITE_SENTMSG5:
+       if (naddrs) {
+           /* We start using the address immediately for data too.
+            * It's best to store it in st->peers now because we might
+            * go via SENTMSG5, WAIT, and a MSG0, straight into using
+            * the new key (without updating the data peer addrs). */
+           transport_resolve_complete(st,addrs,naddrs);
+       } else if (st->local_mobile) {
+           /* We can't let this rest because we may have a peer
+            * address which will break in the future. */
+           slog(st,LOG_SETUP_INIT,"resolution failed: "
+                "abandoning key exchange");
+           enter_state_wait(st);
+       } else {
+           slog(st,LOG_SETUP_INIT,"resolution failed: "
+                " continuing to use source address of peer's packets"
+                " for key exchange and ultimately data");
+       }
+       break;
+    case SITE_RUN:
+       if (naddrs) {
+           slog(st,LOG_SETUP_INIT,"resolution completed tardily,"
+                " updating peer address(es)");
+           transport_resolve_complete_tardy(st,addrs,naddrs);
+       } else if (st->local_mobile) {
+           /* Not very good.  We should queue (another) renegotiation
+            * so that we can update the peer address. */
+           st->key_renegotiate_time=st->now+wait_timeout(st);
+       } else {
+           slog(st,LOG_SETUP_INIT,"resolution failed: "
+                " continuing to use source address of peer's packets");
+       }
+       break;
+    case SITE_WAIT:
+    case SITE_STOP:
+       /* oh well */
+       break;
+    }
+}
+
+static bool_t initiate_key_setup(struct site *st, cstring_t reason,
+                                const struct comm_addr *prod_hint)
+{
+    /* Reentrancy hazard: can call enter_new_state/enter_state_* */
+    if (st->state!=SITE_RUN) return False;
+    slog(st,LOG_SETUP_INIT,"initiating key exchange (%s)",reason);
+    if (st->addresses) {
+       slog(st,LOG_SETUP_INIT,"resolving peer address(es)");
+       return enter_state_resolve(st);
+    } else if (transport_compute_setupinit_peers(st,0,0,prod_hint)) {
+       return enter_new_state(st,SITE_SENTMSG1,0);
+    }
+    slog(st,LOG_SETUP_INIT,"key exchange failed: no address for peer");
+    return False;
+}
+
+static void activate_new_key(struct site *st)
+{
+    struct transform_inst_if *t;
+
+    /* We have three transform instances, which we swap between old,
+       active and setup */
+    t=st->auxiliary_key.transform;
+    st->auxiliary_key.transform=st->current.transform;
+    st->current.transform=st->new_transform;
+    st->new_transform=t;
+    dispose_transform(&st->new_transform);
+
+    st->timeout=0;
+    st->auxiliary_is_new=0;
+    st->auxiliary_key.key_timeout=st->current.key_timeout;
+    st->current.key_timeout=st->now+st->key_lifetime;
+    st->renegotiate_key_time=st->now+st->key_renegotiate_time;
+    transport_peers_copy(st,&st->peers,&st->setup_peers);
+    st->current.remote_session_id=st->setup_session_id;
+
+    /* Compute the inter-site MTU.  This is min( our_mtu, their_mtu ).
+     * But their mtu be unspecified, in which case we just use ours. */
+    uint32_t intersite_mtu=
+       MIN(st->mtu_target, st->remote_adv_mtu ?: ~(uint32_t)0);
+    st->netlink->set_mtu(st->netlink->st,intersite_mtu);
+
+    slog(st,LOG_ACTIVATE_KEY,"new key activated"
+        " (mtu ours=%"PRId32" theirs=%"PRId32" intersite=%"PRId32")",
+        st->mtu_target, st->remote_adv_mtu, intersite_mtu);
+    enter_state_run(st);
+}
+
+static void delete_one_key(struct site *st, struct data_key *key,
+                          cstring_t reason, cstring_t which, uint32_t loglevel)
+{
+    if (!is_transform_valid(key->transform)) return;
+    if (reason) slog(st,loglevel,"%s deleted (%s)",which,reason);
+    dispose_transform(&key->transform);
+    key->key_timeout=0;
+}
+
+static void delete_keys(struct site *st, cstring_t reason, uint32_t loglevel)
+{
+    if (current_valid(st)) {
+       slog(st,loglevel,"session closed (%s)",reason);
+
+       delete_one_key(st,&st->current,0,0,0);
+       set_link_quality(st);
+    }
+    delete_one_key(st,&st->auxiliary_key,0,0,0);
+}
+
+static void state_assert(struct site *st, bool_t ok)
+{
+    if (!ok) fatal("site:state_assert");
+}
+
+static void enter_state_stop(struct site *st)
+{
+    st->state=SITE_STOP;
+    st->timeout=0;
+    delete_keys(st,"entering state STOP",LOG_TIMEOUT_KEY);
+    dispose_transform(&st->new_transform);
+}
+
+static void set_link_quality(struct site *st)
+{
+    uint32_t quality;
+    if (current_valid(st))
+       quality=LINK_QUALITY_UP;
+    else if (st->state==SITE_WAIT || st->state==SITE_STOP)
+       quality=LINK_QUALITY_DOWN;
+    else if (st->addresses)
+       quality=LINK_QUALITY_DOWN_CURRENT_ADDRESS;
+    else if (transport_peers_valid(&st->peers))
+       quality=LINK_QUALITY_DOWN_STALE_ADDRESS;
+    else
+       quality=LINK_QUALITY_DOWN;
+
+    st->netlink->set_quality(st->netlink->st,quality);
+}
+
+static void enter_state_run(struct site *st)
+{
+    slog(st,LOG_STATE,"entering state RUN%s",
+        current_valid(st) ? " (keyed)" : " (unkeyed)");
+    st->state=SITE_RUN;
+    st->timeout=0;
+
+    st->setup_session_id=0;
+    transport_peers_clear(st,&st->setup_peers);
+    FILLZERO(st->localN);
+    FILLZERO(st->remoteN);
+    dispose_transform(&st->new_transform);
+    memset(st->dhsecret,0,st->dh->len);
+    if (st->sharedsecret) memset(st->sharedsecret,0,st->sharedsecretlen);
+    set_link_quality(st);
+
+    if (st->keepalive && !current_valid(st))
+       initiate_key_setup(st, "keepalive", 0);
+}
+
+static bool_t ensure_resolving(struct site *st)
+{
+    /* Reentrancy hazard: may call site_resolve_callback and hence
+     * enter_new_state, enter_state_* and generate_msg*. */
+    if (st->resolving_count)
+        return True;
+
+    assert(st->addresses);
+
+    /* resolver->request might reentrantly call site_resolve_callback
+     * which will decrement st->resolving, so we need to increment it
+     * twice beforehand to prevent decrement from thinking we're
+     * finished, and decrement it ourselves.  Alternatively if
+     * everything fails then there are no callbacks due and we simply
+     * set it to 0 and return false.. */
+    st->resolving_n_results_stored=0;
+    st->resolving_n_results_all=0;
+    st->resolving_count+=2;
+    const char **addrp=st->addresses;
+    const char *address;
+    bool_t anyok=False;
+    for (; (address=*addrp++); ) {
+       bool_t ok = st->resolver->request(st->resolver->st,address,
+                                         st->remoteport,st->comms[0],
+                                         site_resolve_callback,st);
+       if (ok)
+           st->resolving_count++;
+       anyok|=ok;
+    }
+    if (!anyok) {
+       st->resolving_count=0;
+       return False;
+    }
+    decrement_resolving_count(st,2);
+    return True;
+}
+
+static bool_t enter_state_resolve(struct site *st)
+{
+    /* Reentrancy hazard!  See ensure_resolving. */
+    state_assert(st,st->state==SITE_RUN);
+    slog(st,LOG_STATE,"entering state RESOLVE");
+    st->state=SITE_RESOLVE;
+    return ensure_resolving(st);
+}
+
+static bool_t enter_new_state(struct site *st, uint32_t next,
+                             const struct msg *prompt
+                             /* may be 0 for SENTMSG1 */)
+{
+    bool_t (*gen)(struct site *st, const struct msg *prompt);
+    int r;
+
+    slog(st,LOG_STATE,"entering state %s",state_name(next));
+    switch(next) {
+    case SITE_SENTMSG1:
+       state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE);
+       if (!kex_init(st)) return False;
+       gen=generate_msg1;
+       st->msg1_crossed_logged = False;
+       break;
+    case SITE_SENTMSG2:
+       state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE ||
+                    st->state==SITE_SENTMSG1 || st->state==SITE_WAIT);
+       if (!kex_init(st)) return False;
+       gen=generate_msg2;
+       break;
+    case SITE_SENTMSG3:
+       state_assert(st,st->state==SITE_SENTMSG1);
+       BUF_FREE(&st->buffer);
+       gen=generate_msg3;
+       break;
+    case SITE_SENTMSG4:
+       state_assert(st,st->state==SITE_SENTMSG2);
+       BUF_FREE(&st->buffer);
+       gen=generate_msg4;
+       break;
+    case SITE_SENTMSG5:
+       state_assert(st,st->state==SITE_SENTMSG3);
+       BUF_FREE(&st->buffer);
+       gen=generate_msg5;
+       break;
+    case SITE_RUN:
+       state_assert(st,st->state==SITE_SENTMSG4);
+       BUF_FREE(&st->buffer);
+       gen=generate_msg6;
+       break;
+    default:
+       gen=NULL;
+       fatal("enter_new_state(%s): invalid new state",state_name(next));
+       break;
+    }
+
+    if (hacky_par_start_failnow()) return False;
+
+    r= gen(st,prompt) && send_msg(st);
+
+    hacky_par_end(&r,
+                 st->setup_retries, st->setup_retry_interval,
+                 send_msg, st);
+    
+    if (r) {
+       st->state=next;
+       if (next==SITE_RUN) {
+           BUF_FREE(&st->buffer); /* Never reused */
+           st->timeout=0; /* Never retransmit */
+           activate_new_key(st);
+       }
+       return True;
+    }
+    slog(st,LOG_ERROR,"error entering state %s",state_name(next));
+    st->buffer.free=False; /* Unconditionally use the buffer; it may be
+                             in either state, and enter_state_wait() will
+                             do a BUF_FREE() */
+    enter_state_wait(st);
+    return False;
+}
+
+/* msg7 tells our peer that we're about to forget our key */
+static bool_t send_msg7(struct site *st, cstring_t reason)
+{
+    cstring_t transform_err;
+
+    if (current_valid(st) && st->buffer.free
+       && transport_peers_valid(&st->peers)) {
+       BUF_ALLOC(&st->buffer,"site:MSG7");
+       buffer_init(&st->buffer,calculate_max_start_pad());
+       buf_append_uint32(&st->buffer,LABEL_MSG7);
+       buf_append_string(&st->buffer,reason);
+       if (call_transform_forwards(st, st->current.transform,
+                                   &st->buffer, &transform_err))
+           goto free_out;
+       buf_prepend_uint32(&st->buffer,LABEL_MSG0);
+       buf_prepend_uint32(&st->buffer,st->index);
+       buf_prepend_uint32(&st->buffer,st->current.remote_session_id);
+       transport_xmit(st,&st->peers,&st->buffer,True);
+       BUF_FREE(&st->buffer);
+    free_out:
+       return True;
+    }
+    return False;
+}
+
+/* We go into this state if our peer becomes uncommunicative. Similar to
+   the "stop" state, we forget all session keys for a while, before
+   re-entering the "run" state. */
+static void enter_state_wait(struct site *st)
+{
+    slog(st,LOG_STATE,"entering state WAIT");
+    st->timeout=st->now+wait_timeout(st);
+    st->state=SITE_WAIT;
+    set_link_quality(st);
+    BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */
+    /* XXX Erase keys etc. */
+}
+
+static void generate_prod(struct site *st, struct buffer_if *buf)
+{
+    buffer_init(buf,0);
+    buf_append_uint32(buf,0);
+    buf_append_uint32(buf,0);
+    buf_append_uint32(buf,LABEL_PROD);
+    buf_append_string(buf,st->localname);
+    buf_append_string(buf,st->remotename);
+}
+
+static void generate_send_prod(struct site *st,
+                              const struct comm_addr *source)
+{
+    if (!st->allow_send_prod) return; /* too soon */
+    if (!(st->state==SITE_RUN || st->state==SITE_RESOLVE ||
+         st->state==SITE_WAIT)) return; /* we'd ignore peer's MSG1 */
+
+    slog(st,LOG_SETUP_INIT,"prodding peer for key exchange");
+    st->allow_send_prod=0;
+    generate_prod(st,&st->scratch);
+    bool_t ok = comm_addr_sendmsg(st, source, &st->scratch);
+    dump_packet(st,&st->scratch,source,False,ok);
+}
+
+static inline void site_settimeout(uint64_t timeout, int *timeout_io)
+{
+    if (timeout) {
+       int64_t offset=timeout-*now;
+       if (offset<0) offset=0;
+       if (offset>INT_MAX) offset=INT_MAX;
+       if (*timeout_io<0 || offset<*timeout_io)
+           *timeout_io=offset;
+    }
+}
+
+static int site_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                          int *timeout_io)
+{
+    struct site *st=sst;
+
+    BEFOREPOLL_WANT_FDS(0); /* We don't use any file descriptors */
+    st->now=*now;
+
+    /* Work out when our next timeout is. The earlier of 'timeout' or
+       'current.key_timeout'. A stored value of '0' indicates no timeout
+       active. */
+    site_settimeout(st->timeout, timeout_io);
+    site_settimeout(st->current.key_timeout, timeout_io);
+    site_settimeout(st->auxiliary_key.key_timeout, timeout_io);
+
+    return 0; /* success */
+}
+
+static void check_expiry(struct site *st, struct data_key *key,
+                        const char *which)
+{
+    if (key->key_timeout && *now>key->key_timeout) {
+       delete_one_key(st,key,"maximum life exceeded",which,LOG_TIMEOUT_KEY);
+    }
+}
+
+/* NB site_afterpoll will be called before site_beforepoll is ever called */
+static void site_afterpoll(void *sst, struct pollfd *fds, int nfds)
+{
+    struct site *st=sst;
+
+    st->now=*now;
+    if (st->timeout && *now>st->timeout) {
+       st->timeout=0;
+       if (st->state>=SITE_SENTMSG1 && st->state<=SITE_SENTMSG5) {
+           if (!hacky_par_start_failnow())
+               send_msg(st);
+       } else if (st->state==SITE_WAIT) {
+           enter_state_run(st);
+       } else {
+           slog(st,LOG_ERROR,"site_afterpoll: unexpected timeout, state=%d",
+                st->state);
+       }
+    }
+    check_expiry(st,&st->current,"current key");
+    check_expiry(st,&st->auxiliary_key,"auxiliary key");
+}
+
+/* This function is called by the netlink device to deliver packets
+   intended for the remote network. The packet is in "raw" wire
+   format, but is guaranteed to be word-aligned. */
+static void site_outgoing(void *sst, struct buffer_if *buf)
+{
+    struct site *st=sst;
+    cstring_t transform_err;
+    
+    if (st->state==SITE_STOP) {
+       BUF_FREE(buf);
+       return;
+    }
+
+    st->allow_send_prod=1;
+
+    /* In all other states we consider delivering the packet if we have
+       a valid key and a valid address to send it to. */
+    if (current_valid(st) && transport_peers_valid(&st->peers)) {
+       /* Transform it and send it */
+       if (buf->size>0) {
+           buf_prepend_uint32(buf,LABEL_MSG9);
+           if (call_transform_forwards(st, st->current.transform,
+                                       buf, &transform_err))
+               goto free_out;
+           buf_prepend_uint32(buf,LABEL_MSG0);
+           buf_prepend_uint32(buf,st->index);
+           buf_prepend_uint32(buf,st->current.remote_session_id);
+           transport_xmit(st,&st->peers,buf,False);
+       }
+    free_out:
+       BUF_FREE(buf);
+       return;
+    }
+
+    slog(st,LOG_DROP,"discarding outgoing packet of size %d",buf->size);
+    BUF_FREE(buf);
+    initiate_key_setup(st,"outgoing packet",0);
+}
+
+static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in,
+                          uint32_t type, struct msg *m,
+                          struct priomsg *whynot)
+    /* For packets which are identified by the local and remote names.
+     * If it has our name and our peer's name in it it's for us. */
+{
+    struct buffer_if buf[1];
+    buffer_readonly_clone(buf,buf_in);
+
+    if (!unpick_msg(st,type,buf,m)) {
+       priomsg_update_fixed(whynot, comm_notify_whynot_unpick, "malformed");
+       return False;
+    }
+#define NAME_MATCHES(lr)                                               \
+    if (!name_matches(&m->lr, st->lr##name)) {                         \
+       if (priomsg_update_fixed(whynot, comm_notify_whynot_name_##lr,  \
+                                 "unknown " #lr " name: ")) {          \
+            truncmsg_add_packet_string(&whynot->m, m->lr.len, m->lr.name); \
+        }                                                              \
+        return False;                                                  \
+    }
+    NAME_MATCHES(remote);
+    NAME_MATCHES(local );
+#undef NAME_MATCHES
+
+    return True;
+}
+
+static bool_t we_have_priority(struct site *st, const struct msg *m) {
+    if (st->local_capabilities & m->remote_capabilities &
+       CAPAB_PRIORITY_MOBILE) {
+       if (st->local_mobile) return True;
+       if (st-> peer_mobile) return False;
+    }
+    return st->our_name_later;
+}
+
+static bool_t setup_late_msg_ok(struct site *st, 
+                               const struct buffer_if *buf_in,
+                               uint32_t msgtype,
+                               const struct comm_addr *source,
+                               struct msg *m /* returned */) {
+    /* For setup packets which seem from their type like they are
+     * late.  Maybe they came via a different path.  All we do is make
+     * a note of the sending address, iff they look like they are part
+     * of the current key setup attempt. */
+    if (!named_for_us(st,buf_in,msgtype,m,0))
+       /* named_for_us calls unpick_msg which gets the nonces */
+       return False;
+    if (!consttime_memeq(m->nR,st->remoteN,NONCELEN) ||
+       !consttime_memeq(m->nL,st->localN, NONCELEN))
+       /* spoof ?  from stale run ?  who knows */
+       return False;
+    transport_setup_msgok(st,source);
+    return True;
+}
+
+/* This function is called by the communication device to deliver
+   packets from our peers.
+   It should return True if the packet is recognised as being for
+   this current site instance (and should therefore not be processed
+   by other sites), even if the packet was otherwise ignored. */
+static bool_t site_incoming(void *sst, struct buffer_if *buf,
+                           const struct comm_addr *source,
+                           struct priomsg *whynot)
+{
+    struct site *st=sst;
+
+    if (buf->size < 12) return False;
+
+    uint32_t dest=get_uint32(buf->start);
+    uint32_t msgtype=get_uint32(buf->start+8);
+    struct msg msg;
+      /* initialised by named_for_us, or process_msgN for N!=1 */
+
+    if (msgtype==LABEL_MSG1) {
+       if (!named_for_us(st,buf,msgtype,&msg,whynot))
+           return False;
+       /* It's a MSG1 addressed to us. Decide what to do about it. */
+       dump_packet(st,buf,source,True,True);
+       if (st->state==SITE_RUN || st->state==SITE_RESOLVE ||
+           st->state==SITE_WAIT) {
+           /* We should definitely process it */
+           transport_compute_setupinit_peers(st,0,0,source);
+           if (process_msg1(st,buf,source,&msg)) {
+               slog(st,LOG_SETUP_INIT,"key setup initiated by peer");
+               bool_t entered=enter_new_state(st,SITE_SENTMSG2,&msg);
+               if (entered && st->addresses && st->local_mobile)
+                   /* We must do this as the very last thing, because
+                      the resolver callback might reenter us. */
+                   ensure_resolving(st);
+           } else {
+               slog(st,LOG_ERROR,"failed to process incoming msg1");
+           }
+           BUF_FREE(buf);
+           return True;
+       } else if (st->state==SITE_SENTMSG1) {
+           /* We've just sent a message 1! They may have crossed on
+              the wire. If we have priority then we ignore the
+              incoming one, otherwise we process it as usual. */
+           if (we_have_priority(st,&msg)) {
+               BUF_FREE(buf);
+               if (!st->msg1_crossed_logged++)
+                   slog(st,LOG_SETUP_INIT,"crossed msg1s; we are higher "
+                        "priority => ignore incoming msg1");
+               return True;
+           } else {
+               slog(st,LOG_SETUP_INIT,"crossed msg1s; we are lower "
+                    "priority => use incoming msg1");
+               if (process_msg1(st,buf,source,&msg)) {
+                   BUF_FREE(&st->buffer); /* Free our old message 1 */
+                   transport_setup_msgok(st,source);
+                   enter_new_state(st,SITE_SENTMSG2,&msg);
+               } else {
+                   slog(st,LOG_ERROR,"failed to process an incoming "
+                        "crossed msg1 (we have low priority)");
+               }
+               BUF_FREE(buf);
+               return True;
+           }
+       } else if (st->state==SITE_SENTMSG2 ||
+                  st->state==SITE_SENTMSG4) {
+           if (consttime_memeq(msg.nR,st->remoteN,NONCELEN)) {
+               /* We are ahead in the protocol, but that msg1 had the
+                * peer's nonce so presumably it is from this key
+                * exchange run, via a slower route */
+               transport_setup_msgok(st,source);
+           } else {
+               slog(st,LOG_UNEXPECTED,"competing incoming message 1");
+           }
+           BUF_FREE(buf);
+           return True;
+       }
+       /* The message 1 was received at an unexpected stage of the
+          key setup.  Well, they lost the race. */
+       slog(st,LOG_UNEXPECTED,"unexpected incoming message 1");
+       BUF_FREE(buf);
+       return True;
+    }
+    if (msgtype==LABEL_PROD) {
+       if (!named_for_us(st,buf,msgtype,&msg,whynot))
+           return False;
+       dump_packet(st,buf,source,True,True);
+       if (st->state!=SITE_RUN) {
+           slog(st,LOG_DROP,"ignoring PROD when not in state RUN");
+       } else if (current_valid(st)) {
+           slog(st,LOG_DROP,"ignoring PROD when we think we have a key");
+       } else {
+           initiate_key_setup(st,"peer sent PROD packet",source);
+       }
+       BUF_FREE(buf);
+       return True;
+    }
+    if (dest==st->index) {
+       /* Explicitly addressed to us */
+       if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True,True);
+       switch (msgtype) {
+       case LABEL_NAK:
+           /* If the source is our current peer then initiate a key setup,
+              because our peer's forgotten the key */
+           if (get_uint32(buf->start+4)==st->current.remote_session_id) {
+               bool_t initiated;
+               initiated = initiate_key_setup(st,"received a NAK",source);
+               if (!initiated) generate_send_prod(st,source);
+           } else {
+               slog(st,LOG_SEC,"bad incoming NAK");
+           }
+           break;
+       case LABEL_MSG0:
+           process_msg0(st,buf,source);
+           break;
+       case LABEL_MSG1:
+           /* Setup packet: should not have been explicitly addressed
+              to us */
+           slog(st,LOG_SEC,"incoming explicitly addressed msg1");
+           break;
+       case LABEL_MSG2:
+           /* Setup packet: expected only in state SENTMSG1 */
+           if (st->state!=SITE_SENTMSG1) {
+               if ((st->state==SITE_SENTMSG3 ||
+                    st->state==SITE_SENTMSG5) &&
+                   setup_late_msg_ok(st,buf,msgtype,source,&msg))
+                   break;
+               slog(st,LOG_UNEXPECTED,"unexpected MSG2");
+           } else if (process_msg2(st,buf,source,&msg)) {
+               transport_setup_msgok(st,source);
+               enter_new_state(st,SITE_SENTMSG3,&msg);
+           } else {
+               slog(st,LOG_SEC,"invalid MSG2");
+           }
+           break;
+       case CASES_MSG3_KNOWN:
+           /* Setup packet: expected only in state SENTMSG2 */
+           if (st->state!=SITE_SENTMSG2) {
+               if ((st->state==SITE_SENTMSG4) &&
+                   setup_late_msg_ok(st,buf,msgtype,source,&msg))
+                   break;
+               slog(st,LOG_UNEXPECTED,"unexpected MSG3");
+           } else if (process_msg3(st,buf,source,msgtype,&msg)) {
+               transport_setup_msgok(st,source);
+               enter_new_state(st,SITE_SENTMSG4,&msg);
+           } else {
+               slog(st,LOG_SEC,"invalid MSG3");
+           }
+           break;
+       case LABEL_MSG4:
+           /* Setup packet: expected only in state SENTMSG3 */
+           if (st->state!=SITE_SENTMSG3) {
+               if ((st->state==SITE_SENTMSG5) &&
+                   setup_late_msg_ok(st,buf,msgtype,source,&msg))
+                   break;
+               slog(st,LOG_UNEXPECTED,"unexpected MSG4");
+           } else if (process_msg4(st,buf,source,&msg)) {
+               transport_setup_msgok(st,source);
+               enter_new_state(st,SITE_SENTMSG5,&msg);
+           } else {
+               slog(st,LOG_SEC,"invalid MSG4");
+           }
+           break;
+       case LABEL_MSG5:
+           /* Setup packet: expected only in state SENTMSG4 */
+           /* (may turn up in state RUN if our return MSG6 was lost
+              and the new key has already been activated. In that
+              case we discard it. The peer will realise that we
+              are using the new key when they see our data packets.
+              Until then the peer's data packets to us get discarded. */
+           if (st->state==SITE_SENTMSG4) {
+               if (process_msg5(st,buf,source,st->new_transform)) {
+                   transport_setup_msgok(st,source);
+                   enter_new_state(st,SITE_RUN,&msg);
+               } else {
+                   slog(st,LOG_SEC,"invalid MSG5");
+               }
+           } else if (st->state==SITE_RUN) {
+               if (process_msg5(st,buf,source,st->current.transform)) {
+                   slog(st,LOG_DROP,"got MSG5, retransmitting MSG6");
+                   transport_setup_msgok(st,source);
+                   create_msg6(st,st->current.transform,
+                               st->current.remote_session_id);
+                   transport_xmit(st,&st->peers,&st->buffer,True);
+                   BUF_FREE(&st->buffer);
+               } else {
+                   slog(st,LOG_SEC,"invalid MSG5 (in state RUN)");
+               }
+           } else {
+               slog(st,LOG_UNEXPECTED,"unexpected MSG5");
+           }
+           break;
+       case LABEL_MSG6:
+           /* Setup packet: expected only in state SENTMSG5 */
+           if (st->state!=SITE_SENTMSG5) {
+               slog(st,LOG_UNEXPECTED,"unexpected MSG6");
+           } else if (process_msg6(st,buf,source)) {
+               BUF_FREE(&st->buffer); /* Free message 5 */
+               transport_setup_msgok(st,source);
+               activate_new_key(st);
+           } else {
+               slog(st,LOG_SEC,"invalid MSG6");
+           }
+           break;
+       default:
+           slog(st,LOG_SEC,"received message of unknown type 0x%08x",
+                msgtype);
+           break;
+       }
+       BUF_FREE(buf);
+       return True;
+    }
+
+    priomsg_update_fixed(whynot, comm_notify_whynot_general,
+                        "not MSG1 or PROD; unknown dest index");
+    return False;
+}
+
+static void site_control(void *vst, bool_t run)
+{
+    struct site *st=vst;
+    if (run) enter_state_run(st);
+    else enter_state_stop(st);
+}
+
+static void site_phase_hook(void *sst, uint32_t newphase)
+{
+    struct site *st=sst;
+
+    /* The program is shutting down; tell our peer */
+    send_msg7(st,"shutting down");
+}
+
+static void site_childpersist_clearkeys(void *sst, uint32_t newphase)
+{
+    struct site *st=sst;
+    dispose_transform(&st->current.transform);
+    dispose_transform(&st->auxiliary_key.transform);
+    dispose_transform(&st->new_transform);
+    /* Not much point overwiting the signing key, since we loaded it
+       from disk, and it is only valid prospectively if at all,
+       anyway. */
+    /* XXX it would be best to overwrite the DH state, because that
+       _is_ relevant to forward secrecy.  However we have no
+       convenient interface for doing that and in practice gmp has
+       probably dribbled droppings all over the malloc arena.  A good
+       way to fix this would be to have a privsep child for asymmetric
+       crypto operations, but that's a task for another day. */
+}
+
+static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
+                         list_t *args)
+{
+    static uint32_t index_sequence;
+    struct site *st;
+    item_t *item;
+    dict_t *dict;
+    int i;
+
+    NEW(st);
+
+    st->cl.description="site";
+    st->cl.type=CL_SITE;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.control=site_control;
+    st->ops.status=site_status;
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"site","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+    st->localname=dict_read_string(dict, "local-name", True, "site", loc);
+    st->remotename=dict_read_string(dict, "name", True, "site", loc);
+
+    st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False);
+
+    st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False);
+    st->local_mobile=
+       dict_read_bool(dict,"local-mobile",False,"site",loc,False);
+
+    /* Sanity check (which also allows the 'sites' file to include
+       site() closures for all sites including our own): refuse to
+       talk to ourselves */
+    if (strcmp(st->localname,st->remotename)==0) {
+       Message(M_DEBUG,"site %s: local-name==name -> ignoring this site\n",
+               st->localname);
+       if (st->peer_mobile != st->local_mobile)
+           cfgfatal(loc,"site","site %s's peer-mobile=%d"
+                   " but our local-mobile=%d\n",
+                   st->localname, st->peer_mobile, st->local_mobile);
+       free(st);
+       return NULL;
+    }
+    if (st->peer_mobile && st->local_mobile) {
+       Message(M_WARNING,"site %s: site is mobile but so are we"
+               " -> ignoring this site\n", st->remotename);
+       free(st);
+       return NULL;
+    }
+
+    assert(index_sequence < 0xffffffffUL);
+    st->index = ++index_sequence;
+    st->local_capabilities = 0;
+    st->early_capabilities = CAPAB_PRIORITY_MOBILE;
+    st->netlink=find_cl_if(dict,"link",CL_NETLINK,True,"site",loc);
+
+#define GET_CLOSURE_LIST(dictkey,things,nthings,CL_TYPE) do{           \
+    list_t *things##_cfg=dict_lookup(dict,dictkey);                    \
+    if (!things##_cfg)                                                 \
+       cfgfatal(loc,"site","closure list \"%s\" not found\n",dictkey); \
+    st->nthings=list_length(things##_cfg);                             \
+    NEW_ARY(st->things,st->nthings);                                   \
+    assert(st->nthings);                                               \
+    for (i=0; i<st->nthings; i++) {                                    \
+       item_t *item=list_elem(things##_cfg,i);                         \
+       if (item->type!=t_closure)                                      \
+           cfgfatal(loc,"site","%s is not a closure\n",dictkey);       \
+       closure_t *cl=item->data.closure;                               \
+       if (cl->type!=CL_TYPE)                                          \
+           cfgfatal(loc,"site","%s closure wrong type\n",dictkey);     \
+       st->things[i]=cl->interface;                                    \
+    }                                                                  \
+}while(0)
+
+    GET_CLOSURE_LIST("comm",comms,ncomms,CL_COMM);
+
+    NEW_ARY(st->commclientinfos, st->ncomms);
+    dict_t *comminfo = dict_read_dict(dict,"comm-info",False,"site",loc);
+    for (i=0; i<st->ncomms; i++) {
+       st->commclientinfos[i] =
+           !comminfo ? 0 :
+           st->comms[i]->clientinfo(st->comms[i],comminfo,loc);
+    }
+
+    st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc);
+    st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc);
+    st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc);
+
+    st->privkey=find_cl_if(dict,"local-key",CL_SIGPRIVKEY,True,"site",loc);
+    st->addresses=dict_read_string_array(dict,"address",False,"site",loc,0);
+    if (st->addresses)
+       st->remoteport=dict_read_number(dict,"port",True,"site",loc,0);
+    else st->remoteport=0;
+    st->pubkey=find_cl_if(dict,"key",CL_SIGPUBKEY,True,"site",loc);
+
+    GET_CLOSURE_LIST("transform",transforms,ntransforms,CL_TRANSFORM);
+
+    st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc);
+
+    if (st->privkey->sethash || st->pubkey->sethash) {
+       struct hash_if *hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
+       if (st->privkey->sethash) st->privkey->sethash(st->privkey->st,hash);
+       if (st->pubkey->sethash) st->pubkey->sethash(st->pubkey->st,hash);
+    }
+
+#define DEFAULT(D) (st->peer_mobile || st->local_mobile        \
+                    ? DEFAULT_MOBILE_##D : DEFAULT_##D)
+#define CFG_NUMBER(k,D) dict_read_number(dict,(k),False,"site",loc,DEFAULT(D));
+
+    st->key_lifetime=         CFG_NUMBER("key-lifetime",  KEY_LIFETIME);
+    st->setup_retries=        CFG_NUMBER("setup-retries", SETUP_RETRIES);
+    st->setup_retry_interval= CFG_NUMBER("setup-timeout", SETUP_RETRY_INTERVAL);
+    st->wait_timeout_mean=    CFG_NUMBER("wait-time",     WAIT_TIME);
+    st->mtu_target= dict_read_number(dict,"mtu-target",False,"site",loc,0);
+
+    st->mobile_peer_expiry= dict_read_number(
+       dict,"mobile-peer-expiry",False,"site",loc,DEFAULT_MOBILE_PEER_EXPIRY);
+
+    const char *peerskey= st->peer_mobile
+       ? "mobile-peers-max" : "static-peers-max";
+    st->transport_peers_max= dict_read_number(
+       dict,peerskey,False,"site",loc, st->addresses ? 4 : 3);
+    if (st->transport_peers_max<1 ||
+       st->transport_peers_max>MAX_PEER_ADDRS) {
+       cfgfatal(loc,"site", "%s must be in range 1.."
+                STRING(MAX_PEER_ADDRS) "\n", peerskey);
+    }
+
+    if (st->key_lifetime < DEFAULT(KEY_RENEGOTIATE_GAP)*2)
+       st->key_renegotiate_time=st->key_lifetime/2;
+    else
+       st->key_renegotiate_time=st->key_lifetime-DEFAULT(KEY_RENEGOTIATE_GAP);
+    st->key_renegotiate_time=dict_read_number(
+       dict,"renegotiate-time",False,"site",loc,st->key_renegotiate_time);
+    if (st->key_renegotiate_time > st->key_lifetime) {
+       cfgfatal(loc,"site",
+                "renegotiate-time must be less than key-lifetime\n");
+    }
+
+    st->log_events=string_list_to_word(dict_lookup(dict,"log-events"),
+                                      log_event_table,"site");
+
+    st->resolving_count=0;
+    st->allow_send_prod=0;
+
+    st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5,
+                           "site_apply");
+    sprintf(st->tunname,"%s<->%s",st->localname,st->remotename);
+
+    /* The information we expect to see in incoming messages of type 1 */
+    /* fixme: lots of unchecked overflows here, but the results are only
+       corrupted packets rather than undefined behaviour */
+    st->our_name_later=(strcmp(st->localname,st->remotename)>0);
+
+    buffer_new(&st->buffer,SETUP_BUFFER_LEN);
+
+    buffer_new(&st->scratch,SETUP_BUFFER_LEN);
+    BUF_ALLOC(&st->scratch,"site:scratch");
+
+    /* We are interested in poll(), but only for timeouts. We don't have
+       any fds of our own. */
+    register_for_poll(st, site_beforepoll, site_afterpoll, "site");
+    st->timeout=0;
+
+    st->remote_capabilities=0;
+    st->chosen_transform=0;
+    st->current.key_timeout=0;
+    st->auxiliary_key.key_timeout=0;
+    transport_peers_clear(st,&st->peers);
+    transport_peers_clear(st,&st->setup_peers);
+    /* XXX mlock these */
+    st->dhsecret=safe_malloc(st->dh->len,"site:dhsecret");
+    st->sharedsecretlen=st->sharedsecretallocd=0;
+    st->sharedsecret=0;
+
+#define SET_CAPBIT(bit) do {                                           \
+    uint32_t capflag = 1UL << (bit);                                   \
+    if (st->local_capabilities & capflag)                              \
+       slog(st,LOG_ERROR,"capability bit"                              \
+            " %d (%#"PRIx32") reused", (bit), capflag);                \
+    st->local_capabilities |= capflag;                                 \
+} while (0)
+
+    for (i=0; i<st->ntransforms; i++)
+       SET_CAPBIT(st->transforms[i]->capab_bit);
+
+#undef SET_CAPBIT
+
+    if (st->local_mobile || st->peer_mobile)
+       st->local_capabilities |= CAPAB_PRIORITY_MOBILE;
+
+    /* We need to register the remote networks with the netlink device */
+    uint32_t netlink_mtu; /* local virtual interface mtu */
+    st->netlink->reg(st->netlink->st, site_outgoing, st, &netlink_mtu);
+    if (!st->mtu_target)
+       st->mtu_target=netlink_mtu;
+    
+    for (i=0; i<st->ncomms; i++)
+       st->comms[i]->request_notify(st->comms[i]->st, st, site_incoming);
+
+    st->current.transform=0;
+    st->auxiliary_key.transform=0;
+    st->new_transform=0;
+    st->auxiliary_is_new=0;
+
+    enter_state_stop(st);
+
+    add_hook(PHASE_SHUTDOWN,site_phase_hook,st);
+    add_hook(PHASE_CHILDPERSIST,site_childpersist_clearkeys,st);
+
+    return new_closure(&st->cl);
+}
+
+void site_module(dict_t *dict)
+{
+    add_closure(dict,"site",site_apply);
+}
+
+
+/***** TRANSPORT PEERS definitions *****/
+
+static void transport_peers_debug(struct site *st, transport_peers *dst,
+                                 const char *didwhat,
+                                 int nargs, const struct comm_addr *args,
+                                 size_t stride) {
+    int i;
+    char *argp;
+
+    if (!(st->log_events & LOG_PEER_ADDRS))
+       return; /* an optimisation */
+
+    slog(st, LOG_PEER_ADDRS, "peers (%s) %s nargs=%d => npeers=%d",
+        (dst==&st->peers ? "data" :
+         dst==&st->setup_peers ? "setup" : "UNKNOWN"),
+        didwhat, nargs, dst->npeers);
+
+    for (i=0, argp=(void*)args;
+        i<nargs;
+        i++, (argp+=stride?stride:sizeof(*args))) {
+       const struct comm_addr *ca=(void*)argp;
+       slog(st, LOG_PEER_ADDRS, " args: addrs[%d]=%s",
+            i, comm_addr_to_string(ca));
+    }
+    for (i=0; i<dst->npeers; i++) {
+       struct timeval diff;
+       timersub(tv_now,&dst->peers[i].last,&diff);
+       const struct comm_addr *ca=&dst->peers[i].addr;
+       slog(st, LOG_PEER_ADDRS, " peers: addrs[%d]=%s T-%ld.%06ld",
+            i, comm_addr_to_string(ca),
+            (unsigned long)diff.tv_sec, (unsigned long)diff.tv_usec);
+    }
+}
+
+static void transport_peers_expire(struct site *st, transport_peers *peers) {
+    /* peers must be sorted first */
+    int previous_peers=peers->npeers;
+    struct timeval oldest;
+    oldest.tv_sec  = tv_now->tv_sec - st->mobile_peer_expiry;
+    oldest.tv_usec = tv_now->tv_usec;
+    while (peers->npeers>1 &&
+          timercmp(&peers->peers[peers->npeers-1].last, &oldest, <))
+       peers->npeers--;
+    if (peers->npeers != previous_peers)
+       transport_peers_debug(st,peers,"expire", 0,0,0);
+}
+
+static bool_t transport_peer_record_one(struct site *st, transport_peers *peers,
+                                       const struct comm_addr *ca,
+                                       const struct timeval *tv) {
+    /* returns false if output is full */
+    int search;
+
+    if (peers->npeers >= st->transport_peers_max)
+       return 0;
+
+    for (search=0; search<peers->npeers; search++)
+       if (comm_addr_equal(&peers->peers[search].addr, ca))
+           return 1;
+
+    peers->peers[peers->npeers].addr = *ca;
+    peers->peers[peers->npeers].last = *tv;
+    peers->npeers++;
+    return 1;
+}
+
+static void transport_record_peers(struct site *st, transport_peers *peers,
+                                  const struct comm_addr *addrs, int naddrs,
+                                  const char *m) {
+    /* We add addrs into peers.  The new entries end up at the front
+     * and displace entries towards the end (perhaps even off the
+     * end).  Any existing matching entries are moved up to the front.
+     *
+     * Caller must first call transport_peers_expire. */
+
+    if (naddrs==1) {
+       /* avoids debug for uninteresting updates */
+       int i;
+       for (i=0; i<peers->npeers; i++) {
+           if (comm_addr_equal(&addrs[0], &peers->peers[i].addr)) {
+               memmove(peers->peers+1, peers->peers,
+                       sizeof(peers->peers[0]) * i);
+               peers->peers[0].addr = addrs[0];
+               peers->peers[0].last = *tv_now;
+               return;
+           }
+       }
+    }
+
+    int old_npeers=peers->npeers;
+    transport_peer old_peers[old_npeers];
+    COPY_ARRAY(old_peers,peers->peers,old_npeers);
+
+    peers->npeers=0;
+    int i;
+    for (i=0; i<naddrs; i++) {
+       if (!transport_peer_record_one(st,peers, &addrs[i], tv_now))
+           break;
+    }
+    for (i=0; i<old_npeers; i++) {
+       const transport_peer *old=&old_peers[i];
+       if (!transport_peer_record_one(st,peers, &old->addr, &old->last))
+           break;
+    }
+
+    transport_peers_debug(st,peers,m, naddrs,addrs,0);
+}
+
+static void transport_expire_record_peers(struct site *st,
+                                         transport_peers *peers,
+                                         const struct comm_addr *addrs,
+                                         int naddrs, const char *m) {
+    /* Convenience function */
+    transport_peers_expire(st,peers);
+    transport_record_peers(st,peers,addrs,naddrs,m);
+}
+
+static bool_t transport_compute_setupinit_peers(struct site *st,
+        const struct comm_addr *configured_addrs /* 0 if none or not found */,
+        int n_configured_addrs /* 0 if none or not found */,
+        const struct comm_addr *incoming_packet_addr /* 0 if none */) {
+    if (!n_configured_addrs && !incoming_packet_addr &&
+       !transport_peers_valid(&st->peers))
+       return False;
+
+    slog(st,LOG_SETUP_INIT,
+        "using: %d configured addr(s);%s %d old peer addrs(es)",
+        n_configured_addrs,
+        incoming_packet_addr ? " incoming packet address;" : "",
+        st->peers.npeers);
+
+    /* Non-mobile peers try addresses until one is plausible.  The
+     * effect is that this code always tries first the configured
+     * address if supplied, or otherwise the address of the incoming
+     * PROD, or finally the existing data peer if one exists; this is
+     * as desired. */
+
+    transport_peers_copy(st,&st->setup_peers,&st->peers);
+    transport_peers_expire(st,&st->setup_peers);
+
+    if (incoming_packet_addr)
+       transport_record_peers(st,&st->setup_peers,
+                              incoming_packet_addr,1, "incoming");
+
+    if (n_configured_addrs)
+       transport_record_peers(st,&st->setup_peers,
+                             configured_addrs,n_configured_addrs, "setupinit");
+
+    assert(transport_peers_valid(&st->setup_peers));
+    return True;
+}
+
+static void transport_setup_msgok(struct site *st, const struct comm_addr *a) {
+    if (st->peer_mobile)
+       transport_expire_record_peers(st,&st->setup_peers,a,1,"setupmsg");
+}
+static void transport_data_msgok(struct site *st, const struct comm_addr *a) {
+    if (st->peer_mobile)
+       transport_expire_record_peers(st,&st->peers,a,1,"datamsg");
+}
+
+static int transport_peers_valid(transport_peers *peers) {
+    return peers->npeers;
+}
+static void transport_peers_clear(struct site *st, transport_peers *peers) {
+    peers->npeers= 0;
+    transport_peers_debug(st,peers,"clear",0,0,0);
+}
+static void transport_peers_copy(struct site *st, transport_peers *dst,
+                                const transport_peers *src) {
+    dst->npeers=src->npeers;
+    COPY_ARRAY(dst->peers, src->peers, dst->npeers);
+    transport_peers_debug(st,dst,"copy",
+                         src->npeers, &src->peers->addr, sizeof(*src->peers));
+}
+
+static void transport_resolve_complete(struct site *st,
+                                      const struct comm_addr *addrs,
+                                      int naddrs) {
+    transport_expire_record_peers(st,&st->peers,addrs,naddrs,
+                                 "resolved data");
+    transport_expire_record_peers(st,&st->setup_peers,addrs,naddrs,
+                                 "resolved setup");
+}
+
+static void transport_resolve_complete_tardy(struct site *st,
+                                            const struct comm_addr *addrs,
+                                            int naddrs) {
+    transport_expire_record_peers(st,&st->peers,addrs,naddrs,
+                                 "resolved tardily");
+}
+
+static void transport_peers__copy_by_mask(transport_peer *out, int *nout_io,
+                                         unsigned mask,
+                                         const transport_peers *inp) {
+    /* out and in->peers may be the same region, or nonoverlapping */
+    const transport_peer *in=inp->peers;
+    int slot;
+    for (slot=0; slot<inp->npeers; slot++) {
+       if (!(mask & (1U << slot)))
+           continue;
+       if (!(out==in && slot==*nout_io))
+           COPY_OBJ(out[*nout_io], in[slot]);
+       (*nout_io)++;
+    }
+}
+
+void transport_xmit(struct site *st, transport_peers *peers,
+                   struct buffer_if *buf, bool_t candebug) {
+    int slot;
+    transport_peers_expire(st, peers);
+    unsigned failed=0; /* bitmask */
+    assert(MAX_PEER_ADDRS < sizeof(unsigned)*CHAR_BIT);
+
+    int nfailed=0;
+    for (slot=0; slot<peers->npeers; slot++) {
+       transport_peer *peer=&peers->peers[slot];
+       bool_t ok = comm_addr_sendmsg(st, &peer->addr, buf);
+       if (candebug)
+           dump_packet(st, buf, &peer->addr, False, ok);
+       if (!ok) {
+           failed |= 1U << slot;
+           nfailed++;
+       }
+       if (ok && !st->peer_mobile)
+           break;
+    }
+    /* Now we need to demote/delete failing addrs: if we are mobile we
+     * merely demote them; otherwise we delete them. */
+    if (st->local_mobile) {
+       unsigned expected = ((1U << nfailed)-1) << (peers->npeers-nfailed);
+       /* `expected' has all the failures at the end already */
+       if (failed != expected) {
+           int fslot=0;
+           transport_peer failedpeers[nfailed];
+           transport_peers__copy_by_mask(failedpeers, &fslot, failed,peers);
+           assert(fslot == nfailed);
+           int wslot=0;
+           transport_peers__copy_by_mask(peers->peers,&wslot,~failed,peers);
+           assert(wslot+nfailed == peers->npeers);
+           COPY_ARRAY(peers->peers+wslot, failedpeers, nfailed);
+           transport_peers_debug(st,peers,"mobile failure reorder",0,0,0);
+       }
+    } else {
+       if (failed && peers->npeers > 1) {
+           int wslot=0;
+           transport_peers__copy_by_mask(peers->peers,&wslot,~failed,peers);
+           peers->npeers=wslot;
+           transport_peers_debug(st,peers,"non-mobile failure cleanup",0,0,0);
+       }
+    }
+}
+
+/***** END of transport peers declarations *****/
diff --git a/slip.c b/slip.c
new file mode 100644 (file)
index 0000000..6631fae
--- /dev/null
+++ b/slip.c
@@ -0,0 +1,474 @@
+/* When dealing with SLIP (to a pty, or ipif) we have separate rx, tx
+   and client buffers.  When receiving we may read() any amount, not
+   just whole packets.  When transmitting we need to bytestuff anyway,
+   and may be part-way through receiving. */
+
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include "util.h"
+#include "netlink.h"
+#include "process.h"
+#include "unaligned.h"
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#define SLIP_END    192
+#define SLIP_ESC    219
+#define SLIP_ESCEND 220
+#define SLIP_ESCESC 221
+
+struct slip {
+    struct netlink nl;
+    struct buffer_if *buff; /* We unstuff received packets into here
+                              and send them to the netlink code. */
+    bool_t pending_esc;
+    bool_t ignoring_packet; /* If this packet was corrupt or overlong,
+                              we ignore everything up to the next END */
+    netlink_deliver_fn *netlink_to_tunnel;
+};
+
+/* Generic SLIP mangling code */
+
+static void slip_write(int fd, const uint8_t *p, size_t l)
+{
+    while (l) {
+       ssize_t written=write(fd,p,l);
+       if (written<0) {
+           if (errno==EINTR) {
+               continue;
+           } else if (iswouldblock(errno)) {
+               lg_perror(0,"slip",0,M_ERR,errno,"write() (packet(s) lost)");
+               return;
+           } else {
+               fatal_perror("slip_stuff: write()");
+           }
+       }
+       assert(written>0);
+       assert((size_t)written<=l);
+       p+=written;
+       l-=written;
+    }
+}
+
+static void slip_stuff(struct slip *st, struct buffer_if *buf, int fd)
+{
+    uint8_t txbuf[DEFAULT_BUFSIZE];
+    uint8_t *i;
+    int32_t j=0;
+
+    BUF_ASSERT_USED(buf);
+
+    /* There's probably a much more efficient way of implementing this */
+    txbuf[j++]=SLIP_END;
+    for (i=buf->start; i<(buf->start+buf->size); i++) {
+       switch (*i) {
+       case SLIP_END:
+           txbuf[j++]=SLIP_ESC;
+           txbuf[j++]=SLIP_ESCEND;
+           break;
+       case SLIP_ESC:
+           txbuf[j++]=SLIP_ESC;
+           txbuf[j++]=SLIP_ESCESC;
+           break;
+       default:
+           txbuf[j++]=*i;
+           break;
+       }
+       if ((j+2)>DEFAULT_BUFSIZE) {
+           slip_write(fd,txbuf,j);
+           j=0;
+       }
+    }
+    txbuf[j++]=SLIP_END;
+    slip_write(fd,txbuf,j);
+    BUF_FREE(buf);
+}
+
+static void slip_unstuff(struct slip *st, uint8_t *buf, uint32_t l)
+{
+    uint32_t i;
+
+    BUF_ASSERT_USED(st->buff);
+    for (i=0; i<l; i++) {
+       int outputchr;
+       enum { OUTPUT_END = 256, OUTPUT_NOTHING = 257 };
+
+       if (!st->buff->size)
+           buffer_init(st->buff,calculate_max_start_pad());
+
+       if (st->pending_esc) {
+           st->pending_esc=False;
+           switch(buf[i]) {
+           case SLIP_ESCEND:
+               outputchr=SLIP_END;
+               break;
+           case SLIP_ESCESC:
+               outputchr=SLIP_ESC;
+               break;
+           default:
+               if (!st->ignoring_packet) {
+                   Message(M_WARNING, "userv_afterpoll: bad SLIP escape"
+                           " character, dropping packet\n");
+               }
+               st->ignoring_packet=True;
+               outputchr=OUTPUT_NOTHING;
+               break;
+           }
+       } else {
+           switch (buf[i]) {
+           case SLIP_END:
+               outputchr=OUTPUT_END;
+               break;
+           case SLIP_ESC:
+               st->pending_esc=True;
+               outputchr=OUTPUT_NOTHING;
+               break;
+           default:
+               outputchr=buf[i];
+               break;
+           }
+       }
+
+       if (st->ignoring_packet) {
+           if (outputchr == OUTPUT_END) {
+               st->ignoring_packet=False;
+               st->buff->size=0;
+           }
+       } else {
+           if (outputchr == OUTPUT_END) {
+               if (st->buff->size>0) {
+                   st->netlink_to_tunnel(&st->nl,st->buff);
+                   BUF_ALLOC(st->buff,"userv_afterpoll");
+               }
+               st->buff->size=0;
+           } else if (outputchr != OUTPUT_NOTHING) {
+               if (buf_remaining_space(st->buff)) {
+                   buf_append_uint8(st->buff,outputchr);
+               } else {
+                   Message(M_WARNING, "userv_afterpoll: dropping overlong"
+                           " SLIP packet\n");
+                   st->ignoring_packet=True;
+               }
+           }
+       }
+    }
+}
+
+static void slip_init(struct slip *st, struct cloc loc, dict_t *dict,
+                     cstring_t name, netlink_deliver_fn *to_host)
+{
+    st->netlink_to_tunnel=
+       netlink_init(&st->nl,st,loc,dict,
+                    "netlink-userv-ipif",NULL,to_host);
+    st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"name",loc);
+    BUF_ALLOC(st->buff,"slip_init");
+    st->pending_esc=False;
+    st->ignoring_packet=False;
+}
+
+/* Connection to the kernel through userv-ipif */
+
+struct userv {
+    struct slip slip;
+    int txfd; /* We transmit to userv */
+    int rxfd; /* We receive from userv */
+    cstring_t userv_path;
+    cstring_t service_user;
+    cstring_t service_name;
+    pid_t pid;
+    bool_t expecting_userv_exit;
+};
+
+static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                           int *timeout_io)
+{
+    struct userv *st=sst;
+
+    if (st->rxfd!=-1) {
+       BEFOREPOLL_WANT_FDS(2);
+       fds[0].fd=st->txfd;
+       fds[0].events=0; /* Might want to pick up POLLOUT sometime */
+       fds[1].fd=st->rxfd;
+       fds[1].events=POLLIN;
+    } else {
+       BEFOREPOLL_WANT_FDS(0);
+    }
+    return 0;
+}
+
+static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds)
+{
+    struct userv *st=sst;
+    uint8_t rxbuf[DEFAULT_BUFSIZE];
+    int l;
+
+    if (nfds==0) return;
+
+    if (fds[1].revents&POLLERR) {
+       Message(M_ERR,"%s: userv_afterpoll: POLLERR!\n",st->slip.nl.name);
+    }
+    if (fds[1].revents&POLLIN) {
+       l=read(st->rxfd,rxbuf,DEFAULT_BUFSIZE);
+       if (l<0) {
+           if (errno!=EINTR && !iswouldblock(errno))
+               fatal_perror("%s: userv_afterpoll: read(rxfd)",
+                            st->slip.nl.name);
+       } else if (l==0) {
+           fatal("%s: userv_afterpoll: read(rxfd)=0; userv gone away?",
+                 st->slip.nl.name);
+       } else slip_unstuff(&st->slip,rxbuf,l);
+    }
+}
+
+/* Send buf to the kernel. Free buf before returning. */
+static void userv_deliver_to_kernel(void *sst, struct buffer_if *buf)
+{
+    struct userv *st=sst;
+
+    if (buf->size > st->slip.nl.mtu) {
+       Message(M_ERR,"%s: packet of size %"PRIu32" exceeds mtu %"PRIu32":"
+               " cannot be injected into kernel, dropped\n",
+               st->slip.nl.name, buf->size, st->slip.nl.mtu);
+       BUF_FREE(buf);
+       return;
+    }
+
+    slip_stuff(&st->slip,buf,st->txfd);
+}
+
+static void userv_userv_callback(void *sst, pid_t pid, int status)
+{
+    struct userv *st=sst;
+
+    if (pid!=st->pid) {
+       Message(M_WARNING,"userv_callback called unexpectedly with pid %d "
+               "(expected %d)\n",pid,st->pid);
+       return;
+    }
+    if (!(st->expecting_userv_exit &&
+         (!status ||
+          (WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)))) {
+       lg_exitstatus(0,st->slip.nl.name,0,
+                     st->expecting_userv_exit ? M_WARNING : M_FATAL,
+                     status,"userv");
+    }
+    st->pid=0;
+}
+
+struct userv_entry_rec {
+    cstring_t path;
+    const char **argv;
+    int in;
+    int out;
+    /* XXX perhaps we should collect and log stderr? */
+};
+
+static void userv_entry(void *sst)
+{
+    struct userv_entry_rec *st=sst;
+
+    dup2(st->in,0);
+    dup2(st->out,1);
+
+    setsid();
+    execvp(st->path,(char *const*)st->argv);
+    perror("userv-entry: execvp()");
+    exit(1);
+}
+
+static void userv_invoke_userv(struct userv *st)
+{
+    struct userv_entry_rec er[1];
+    int c_stdin[2];
+    int c_stdout[2];
+    string_t nets;
+    string_t s;
+    struct netlink_client *r;
+    struct ipset *allnets;
+    struct subnet_list *snets;
+    int i, nread;
+    uint8_t confirm;
+
+    if (st->pid) {
+       fatal("userv_invoke_userv: already running");
+    }
+
+    /* This is where we actually invoke userv - all the networks we'll
+       be using should already have been registered. */
+
+    char addrs[512];
+    snprintf(addrs,sizeof(addrs),"%s,%s,%d,slip",
+            ipaddr_to_string(st->slip.nl.local_address),
+            ipaddr_to_string(st->slip.nl.secnet_address),st->slip.nl.mtu);
+
+    allnets=ipset_new();
+    for (r=st->slip.nl.clients; r; r=r->next) {
+       if (r->link_quality > LINK_QUALITY_UNUSED) {
+           struct ipset *nan;
+           r->kup=True;
+           nan=ipset_union(allnets,r->networks);
+           ipset_free(allnets);
+           allnets=nan;
+       }
+    }
+    snets=ipset_to_subnet_list(allnets);
+    ipset_free(allnets);
+    nets=safe_malloc(20*snets->entries,"userv_invoke_userv:nets");
+    *nets=0;
+    for (i=0; i<snets->entries; i++) {
+       s=subnet_to_string(snets->list[i]);
+       strcat(nets,s);
+       strcat(nets,",");
+    }
+    nets[strlen(nets)-1]=0;
+    subnet_list_free(snets);
+
+    Message(M_INFO,"%s: about to invoke: %s %s %s %s %s\n",st->slip.nl.name,
+           st->userv_path,st->service_user,st->service_name,addrs,nets);
+
+    st->slip.pending_esc=False;
+
+    /* Invoke userv */
+    pipe_cloexec(c_stdin);
+    pipe_cloexec(c_stdout);
+    st->txfd=c_stdin[1];
+    st->rxfd=c_stdout[0];
+
+    er->in=c_stdin[0];
+    er->out=c_stdout[1];
+    /* The arguments are:
+       userv
+       service-user
+       service-name
+       local-addr,secnet-addr,mtu,protocol
+       route1,route2,... */
+    const char *er_argv[6];
+    er->argv=er_argv;
+    er->argv[0]=st->userv_path;
+    er->argv[1]=st->service_user;
+    er->argv[2]=st->service_name;
+    er->argv[3]=addrs;
+    er->argv[4]=nets;
+    er->argv[5]=NULL;
+    er->path=st->userv_path;
+
+    st->pid=makesubproc(userv_entry, userv_userv_callback,
+                       er, st, st->slip.nl.name);
+    close(er->in);
+    close(er->out);
+    free(nets);
+    Message(M_INFO,"%s: userv-ipif pid is %d\n",st->slip.nl.name,st->pid);
+    /* Read a single character from the pipe to confirm userv-ipif is
+       running. If we get a SIGCHLD at this point then we'll get EINTR. */
+    if ((nread=read(st->rxfd,&confirm,1))!=1) {
+       if (errno==EINTR) {
+           Message(M_WARNING,"%s: read of confirmation byte was "
+                   "interrupted\n",st->slip.nl.name);
+       } else {
+           if (nread<0) {
+               fatal_perror("%s: error reading confirmation byte",
+                            st->slip.nl.name);
+           } else {
+               fatal("%s: unexpected EOF instead of confirmation byte"
+                     " - userv ipif failed?", st->slip.nl.name);
+           }
+       }
+    } else {
+       if (confirm!=SLIP_END) {
+           fatal("%s: bad confirmation byte %d from userv-ipif",
+                 st->slip.nl.name,confirm);
+       }
+    }
+    setnonblock(st->txfd);
+    setnonblock(st->rxfd);
+
+    add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->txfd);
+    add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->rxfd);
+}
+
+static void userv_kill_userv(struct userv *st)
+{
+    if (st->pid) {
+       kill(-st->pid,SIGTERM);
+       st->expecting_userv_exit=True;
+    }
+}
+
+static void userv_phase_hook(void *sst, uint32_t newphase)
+{
+    struct userv *st=sst;
+    /* We must wait until signal processing has started before forking
+       userv */
+    if (newphase==PHASE_RUN) {
+       userv_invoke_userv(st);
+       /* Register for poll() */
+       register_for_poll(st, userv_beforepoll, userv_afterpoll,
+                         st->slip.nl.name);
+    }
+    if (newphase==PHASE_SHUTDOWN) {
+       userv_kill_userv(st);
+    }
+}
+
+static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context,
+                          list_t *args)
+{
+    struct userv *st;
+    item_t *item;
+    dict_t *dict;
+
+    NEW(st);
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+
+    slip_init(&st->slip,loc,dict,"netlink-userv-ipif",
+             userv_deliver_to_kernel);
+
+    st->userv_path=dict_read_string(dict,"userv-path",False,"userv-netlink",
+                                   loc);
+    st->service_user=dict_read_string(dict,"service-user",False,
+                                     "userv-netlink",loc);
+    st->service_name=dict_read_string(dict,"service-name",False,
+                                     "userv-netlink",loc);
+    if (!st->userv_path) st->userv_path="userv";
+    if (!st->service_user) st->service_user="root";
+    if (!st->service_name) st->service_name="ipif";
+    st->rxfd=-1; st->txfd=-1;
+    st->pid=0;
+    st->expecting_userv_exit=False;
+    add_hook(PHASE_RUN,userv_phase_hook,st);
+    add_hook(PHASE_SHUTDOWN,userv_phase_hook,st);
+
+    return new_closure(&st->slip.nl.cl);
+}
+
+void slip_module(dict_t *dict)
+{
+    add_closure(dict,"userv-ipif",userv_apply);
+}
diff --git a/stamp-h.in b/stamp-h.in
new file mode 100644 (file)
index 0000000..9788f70
--- /dev/null
@@ -0,0 +1 @@
+timestamp
diff --git a/stest/Dir.sd.mk b/stest/Dir.sd.mk
new file mode 100644 (file)
index 0000000..d819170
--- /dev/null
@@ -0,0 +1,38 @@
+
+&TARGETS += & udp-preload.so
+
+&DEPS += & udp-preload.so
+&DEPS += &^ common.tcl
+&DEPS += secnet
+&DEPS += test-example/sites.conf
+&DEPS += test-example/inside.key
+&DEPS += test-example/outside.key
+
+&:include test-common.sd.mk
+
+&OBJECTS += & udp-preload.o
+
+$(&OBJECTS) : ALL_CFLAGS += -D_REENTRANT -fPIC -Wno-unused-result
+
+&udp-preload.so: $(&OBJECTS)
+       $(CC) -shared -Wl,-soname,$@.1 $^ -o $@ -ldl
+
+# These test scripts use little cpu but contain sleeps etc.  So when
+# there are several, we are going to want to run *loads* in parallel.
+#
+# Ideally we would do something like "every one of these counts for a
+# tenth of a job" but make can't do that.  So bodge it: we treat all the
+# tests as a single job, and disconnect the parent's jobserver.
+#
+# make.info says $(MAKE) causes special handling of the rule but only
+# if it's written literally like that in the rule, hence this
+# indirection.  We need no squash MAKEFLAGS and MFLAGS too.
+# MAKELEVEL seems like it will be fine to pass on.
+
+MAKE_NOTSPECIAL:=$(MAKE)
+
+&check:: $(&DEPS)
+       env -u MAKEFLAGS -u MFLAGS \
+       $(MAKE_NOTSPECIAL) -f main.mk -j$(shell nproc || echo 1)0 &check-real
+
+&:include subdirmk/cdeps.sd.mk
diff --git a/stest/common.tcl b/stest/common.tcl
new file mode 100644 (file)
index 0000000..0c1202c
--- /dev/null
@@ -0,0 +1,220 @@
+source test-common.tcl
+
+package require Tclx
+
+load chiark_tcl_hbytes-1.so
+load chiark_tcl_dgram-1.so
+
+set netlink(inside) {
+    local-address "172.18.232.9";
+    secnet-address "172.18.232.10";
+    remote-networks "172.18.232.0/28";
+}
+set netlink(outside) {
+    local-address "172.18.232.1";
+    secnet-address "172.18.232.2";
+    remote-networks "172.18.232.0/28";
+}
+
+set ports(inside) {16913 16910}
+set ports(outside) 16900
+
+set extra(inside) {
+    local-mobile True;
+    mtu-target 1260;
+}
+set extra(outside) {}
+
+proc mkconf {location site} {
+    global tmp
+    global builddir
+    global netlink
+    global ports
+    global extra
+    global netlinkfh
+    set pipefp $tmp/$site.netlink
+    foreach tr {t r} {
+       file delete $pipefp.$tr
+       exec mkfifo -m600 $pipefp.$tr
+       set netlinkfh($site.$tr) [set fh [open $pipefp.$tr r+]]
+       fconfigure $fh -blocking 0 -buffering none -translation binary
+    }
+    fileevent $netlinkfh($site.r) readable \
+       [list netlink-readable $location $site]
+    set fakeuf $tmp/$site.fake-userv
+    set fakeuh [open $fakeuf w 0755]
+    puts $fakeuh "#!/bin/sh
+set -e
+exec 3<&0
+cat <&3 3<&- >$pipefp.r &
+exec 3<>$pipefp.t
+exec <$pipefp.t
+exec 3<&-
+exec cat
+"
+    close $fakeuh
+    set cfg "
+       netlink userv-ipif {
+           name \"netlink\";
+            userv-path \"$fakeuf\";
+       $netlink($site)
+           mtu 1400;
+           buffer sysbuffer(2048);
+           interface \"secnet-test-[string range $site 0 0]\";
+        };
+        comm
+"
+    set delim {}
+    foreach port $ports($site) {
+       append cfg "$delim
+           udp {
+                port $port;
+                address \"::1\", \"127.0.0.1\";
+               buffer sysbuffer(4096);
+           }
+       "
+        set delim ,
+    }
+    append cfg ";
+       local-name \"test-example/$location/$site\";
+       local-key rsa-private(\"$builddir/test-example/$site.key\");
+"
+    append cfg $extra($site)
+    append cfg {
+       log logfile {
+           filename "/dev/tty";
+           class "info","notice","warning","error","security","fatal";
+       };
+       system {
+       };
+       resolver adns {
+       };
+       log-events "all";
+       random randomfile("/dev/urandom",no);
+       transform eax-serpent { }, serpent256-cbc { };
+    }
+
+    set f [open $builddir/test-example/sites.conf r]
+    set sites [read $f]
+    close $f
+    append cfg $sites
+    append cfg {
+       sites map(site,all-sites);
+    }
+    return $cfg
+}
+
+proc spawn-secnet {location site} {
+    global tmp
+    global builddir
+    global netlinkfh
+    upvar #0 pids($site) pid
+    set cf $tmp/$site.conf
+    set ch [open $cf w]
+    puts $ch [mkconf $location $site]
+    close $ch
+    set argl [list $builddir/secnet -dvnc $cf]
+    set pid [fork]
+    if {!$pid} {
+       execl [lindex $argl 0] [lrange $argl 1 end]
+    }
+    puts -nonewline $netlinkfh($site.t) [hbytes h2raw c0]
+}
+
+proc netlink-readable {location site} {
+    global ok
+    upvar #0 netlinkfh($site.r) fh
+    read $fh; # empty the buffer
+    switch -exact $site {
+       inside {
+           puts OK
+           set ok 1; # what a bodge
+           return
+       }
+       outside {
+           error "inside rx'd!"
+       }
+    }
+}
+
+proc bgerror {message} {
+    global errorInfo errorCode
+    catch {
+       puts stderr "
+----------------------------------------
+$errorInfo
+
+$errorCode
+$message
+----------------------------------------
+    "
+    }
+    exit 1
+}
+
+proc sendpkt {} {
+    global netlinkfh
+    set p {
+        4500 0054 ed9d 4000 4001 24da ac12 e809
+        ac12 e802 0800 1de4 2d96 0001 f1d4 a05d
+        0000 0000 507f 0b00 0000 0000 1011 1213
+        1415 1617 1819 1a1b 1c1d 1e1f 2021 2223
+        2425 2627 2829 2a2b 2c2d 2e2f 3031 3233
+        3435 3637
+    }
+    puts -nonewline $netlinkfh(inside.t) \
+       [hbytes h2raw c0[join $p ""]c0]
+}
+
+set socktmp $tmp/s
+exec mkdir -p -m700 $socktmp
+regsub {^(?!/)} $socktmp {./} socktmp ;# dgram-socket wants ./ or /
+
+proc prefix_preload {lib} { prefix_some_path LD_PRELOAD $lib }
+
+set env(UDP_PRELOAD_DIR) $socktmp
+prefix_preload $builddir/stest/udp-preload.so
+
+proc udp-proxy {} {
+    global socktmp udpsock
+    set u $socktmp/udp
+    file delete $u
+    regsub {^(?!/)} $u {./} u
+    set udpsock [dgram-socket create $u]
+    dgram-socket on-receive $udpsock udp-relay
+}
+
+proc udp-relay {data src sock args} {
+    global udpsock socktmp
+    set headerlen [expr {52+1}]
+    set orgsrc $src
+
+    set dst [hbytes range $data 0 $headerlen]
+    regsub {(?:00)*$} $dst {} dst
+    set dst [hbytes h2raw $dst]
+
+    hbytes overwrite data 0 [hbytes zeroes $headerlen]
+    regsub {.*/} $src {} src
+    set srch [hbytes raw2h $src]
+    hbytes append srch 00
+    if {[catch {
+       if {[regexp {[^.,:0-9a-f]} $dst c]} { error "bad dst" }
+       if {[hbytes length $srch] > $headerlen} { error "src addr too long" }
+       hbytes overwrite data 0 $srch
+       dgram-socket transmit $udpsock $data $socktmp/$dst
+    } emsg]} {
+       puts stderr "$orgsrc -> $dst: $emsg"
+    }
+}
+
+proc test-kex {} {
+    udp-proxy
+    spawn-secnet in inside
+    spawn-secnet out outside
+
+    after 500 sendpkt
+    after 1000 sendpkt
+    after 5000 timed-out
+
+    vwait ok
+}
diff --git a/stest/t-basic-kex b/stest/t-basic-kex
new file mode 100755 (executable)
index 0000000..0952d5f
--- /dev/null
@@ -0,0 +1,5 @@
+#! /usr/bin/tclsh
+
+source stest/common.tcl
+
+test-kex
diff --git a/stest/t-dyni-kex b/stest/t-dyni-kex
new file mode 100755 (executable)
index 0000000..4bc0087
--- /dev/null
@@ -0,0 +1,7 @@
+#! /usr/bin/tclsh
+
+# Dyamic key rollover config on inside only
+
+source stest/common.tcl
+
+test-kex
diff --git a/stest/udp-preload.c b/stest/udp-preload.c
new file mode 100644 (file)
index 0000000..b5897bb
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ *  udp-preload.c - testing mock library for secnet udp
+ *  This file is part of secnet.
+ *
+ *  Copyright (C) 1998,2003-2004,2012,2017,2019 Ian Jackson
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3, or (at your option)
+ *  any later version.
+ * 
+ *  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.  See the
+ *  GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#define _GNU_SOURCE
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1)
+#define STDERRSTR_STRING(m) write(2,m,strlen(m))
+
+typedef void anyfn_type(void);
+
+static anyfn_type *find_any(const char *name) {
+  static const char *dlerr;
+  anyfn_type *kv;
+
+  kv= dlsym(RTLD_NEXT,name); if (kv) return kv;
+  dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason";
+  STDERRSTR_CONST("udp-preload: error finding original version of ");
+  STDERRSTR_STRING(name);
+  STDERRSTR_CONST(": ");
+  STDERRSTR_STRING(dlerr);
+  STDERRSTR_STRING("\n");
+  errno= ENOSYS;
+  return 0;
+}
+
+#define socket_args int domain, int type, int protocol
+#define close_args  int fd
+#define bind_args   int fd, const struct sockaddr *addr, socklen_t addrlen
+#define sendto_args int fd, const void *buf, size_t len, int flags, \
+                    const struct sockaddr *addr, socklen_t addrlen
+#define recvfrom_args  int fd, void *buf, size_t len, int flags, \
+                       struct sockaddr *addr, socklen_t *addrlen
+#define setsockopt_args  int fd, int level, int optname, \
+                         const void *optval, socklen_t optlen
+#define getsockname_args int fd, struct sockaddr *addr, socklen_t *addrlen
+#define WRAPS(X)                                               \
+    X(socket,     int,     (domain,type,protocol))             \
+    X(close,      int,     (fd))                               \
+    X(bind,       int,     (fd,addr,addrlen))                  \
+    X(sendto,     ssize_t, (fd,buf,len,flags,addr,addrlen))    \
+    X(recvfrom,   ssize_t, (fd,buf,len,flags,addr,addrlen))    \
+    X(setsockopt, int,     (fd,level,optname,optval,optlen))   \
+    X(getsockname,int,     (fd,addr,addrlen))
+
+#define DEF_OLD(fn,rt,args)                            \
+  typedef rt fn##_fn_type(fn##_args);                  \
+  static fn##_fn_type find_##fn, *old_##fn=find_##fn;  \
+  static rt find_##fn(fn##_args) {                     \
+    anyfn_type *anyfn;                                 \
+    anyfn= find_any(#fn); if (!anyfn) return -1;       \
+    old_##fn= (fn##_fn_type*)anyfn;                    \
+    return old_##fn args;                              \
+  }
+
+WRAPS(DEF_OLD)
+
+#define WRAP(fn) int fn(fn##_args)
+#define TWRAP(fn) fn(fn##_args)
+
+typedef struct{
+    int af;
+} fdinfo;
+static fdinfo **table;
+static int tablesz;
+
+static fdinfo *lookup(int fd) {
+    if (fd<0 || fd>=tablesz) return 0;
+    return table[fd];
+}
+
+#define ADDRPORTSTRLEN (INET6_ADDRSTRLEN+1+5) /* not including nul */
+
+static int addrport2str(char buf[ADDRPORTSTRLEN+1],
+                       const struct sockaddr *addr, socklen_t addrlen) {
+    const void *addrv=addr;
+    const void *iav;
+    const struct sockaddr_in  *sin;
+    const struct sockaddr_in6 *sin6;
+    uint16_t port;
+    socklen_t el;
+    switch (addr->sa_family) {
+    case AF_INET:  sin =addrv; el=sizeof(*sin ); iav=&sin ->sin_addr ; port=sin ->sin_port ; break;
+    case AF_INET6: sin6=addrv; el=sizeof(*sin6); iav=&sin6->sin6_addr; port=sin6->sin6_port; break;
+    default: errno=ESRCH; return -1;
+    }
+//fprintf(stderr,"af=%lu el=%lu addrlen=%lu\n",
+//     (unsigned long)addr->sa_family,
+//     (unsigned long)el,
+//     (unsigned long)addrlen);
+    if (addrlen!=el) { errno=EINVAL; return -1; }
+    char *p=buf;
+    if (!inet_ntop(addr->sa_family,iav,p,INET6_ADDRSTRLEN)) return -1;
+    p+=strlen(p);
+    sprintf(p,",%u",(unsigned)ntohs(port));
+    return 0;
+}
+
+static int str2addrport(char *str,
+                       struct sockaddr *addr, socklen_t *addrlen) {
+    union {
+       struct sockaddr_in  sin;
+       struct sockaddr_in6 sin6;
+    } si;
+
+    memset(&si,0,sizeof(si));
+
+    int af;
+    void *iav;
+    uint16_t *portp;
+    socklen_t al;
+    switch (str[strcspn(str,".:")]) {
+    case '.': af=AF_INET ; iav=&si.sin .sin_addr ; al=sizeof(si.sin ); portp=&si.sin .sin_port ; break;
+    case ':': af=AF_INET6; iav=&si.sin6.sin6_addr; al=sizeof(si.sin6); portp=&si.sin6.sin6_port; break;
+    default: errno=ESRCH; return -1;
+    }
+    si.sin.sin_family=af;
+
+    char *comma=strchr(str,',');
+    if (!comma) { errno=ESRCH; return -1; }
+    *comma++=0;
+    int r=inet_pton(af,str,iav);
+//fprintf(stderr,"inet_pton(%d,\"%s\",)=%d\n",af,str,r);
+    if (r<0) return -1;
+    if (r==0) { errno=ENOTTY; return -1; }
+
+    char *ep;
+    errno=0;
+    unsigned long port=strtoul(comma,&ep,10);
+    if (ep==comma || *ep || errno || port>65536) { errno=ESRCH; return -1; }
+    *portp= htons(port);
+
+    if (addr) memcpy(addr,&si, *addrlen<al ? *addrlen : al);
+    *addrlen=al;
+    return 0;
+}
+
+static int sun_prep(struct sockaddr_un *sun, const char *leaf) {
+    const char *dir=getenv("UDP_PRELOAD_DIR");
+    if (!dir) { errno=ECHILD; return 0; }
+
+    memset(sun,0,sizeof(*sun));
+    sun->sun_family=AF_UNIX;
+    size_t dl = strlen(dir);
+    if (dl + 1 + strlen(leaf) + 1 > sizeof(sun->sun_path)) {
+       errno=ENAMETOOLONG; return -1;
+    }
+    strcpy(sun->sun_path,dir);
+    sun->sun_path[dl]='/';
+    strcpy(sun->sun_path+dl+1,leaf);
+    return 0;
+}
+
+WRAP(socket) {
+    if (!((domain==AF_INET || domain==AF_INET6) &&
+         type==SOCK_DGRAM))
+       return old_socket(domain,type,protocol);
+    int fd=socket(AF_UNIX,SOCK_DGRAM,0);
+    if (fd<0) return fd;
+    if (fd>=tablesz) {
+       int newsz=(fd+1)*2;
+       table=realloc(table,newsz*sizeof(*table));
+       if (!table) goto fail;
+       while (tablesz<newsz) table[tablesz++]=0;
+    }
+    free(table[fd]);
+    table[fd]=malloc(sizeof(*table[fd]));
+    if (!table[fd]) goto fail;
+    table[fd]->af=domain;
+    return fd;
+
+ fail:
+    close(fd);
+    return -1;
+}
+
+WRAP(close) {
+    if (fd>=0 && fd<tablesz) {
+       free(table[fd]);
+       table[fd]=0;
+    }
+    return old_close(fd);
+}
+
+WRAP(bind) {
+    fdinfo *ent=lookup(fd);
+    if (!ent) return old_bind(fd,addr,addrlen);
+    struct sockaddr_un sun;
+    char tbuf[ADDRPORTSTRLEN+1];
+    if (addrport2str(tbuf,addr,addrlen)) return -1;
+    if (sun_prep(&sun,tbuf)) return -1;
+//fprintf(stderr,"binding %s\n",sun.sun_path);
+    if (unlink(sun.sun_path) && errno!=ENOENT) return -1;
+    return old_bind(fd,(const void*)&sun,sizeof(sun));
+}
+
+WRAP(setsockopt) {
+    fdinfo *ent=lookup(fd);
+    if (!ent) return old_setsockopt(fd,level,optname,optval,optlen);
+    if (ent->af==AF_INET6 && level==IPPROTO_IPV6 && optname==IPV6_V6ONLY
+       && optlen==sizeof(int) && *(int*)optval==1) {
+       return 0;
+    }
+    errno=ENOTTY;
+    return -1;
+}
+
+WRAP(getsockname) {
+    fdinfo *ent=lookup(fd);
+    if (!ent) return old_getsockname(fd,addr,addrlen);
+    struct sockaddr_un sun;
+    socklen_t sunlen=sizeof(sun);
+    if (old_getsockname(fd,(void*)&sun,&sunlen)) return -1;
+    if (sun.sun_family!=AF_UNIX || sunlen>sizeof(sun)) {
+//fprintf(stderr,"old_getsockname af=%lu sunlen=%lu\n",
+//     (unsigned long)sun.sun_family,
+//     (unsigned long)sunlen);
+       errno=EDOM; return -1;
+    }
+    char *slash=strrchr(sun.sun_path,'/');
+    if (str2addrport(slash ? slash+1 : sun.sun_path,
+                    addr,addrlen)) return -1;
+    return 0;
+}
+
+ssize_t TWRAP(sendto) {
+    fdinfo *ent=lookup(fd);
+    if (!ent) return old_sendto(fd,buf,len,flags,addr,addrlen);
+
+    if (flags) { errno=ENOEXEC; return -1; }
+
+    const char *leaf=getenv("UDP_PRELOAD_SERVER");
+    if (!leaf) leaf="udp";
+    if (strlen(leaf) > ADDRPORTSTRLEN) { errno=ENAMETOOLONG; return -1; }
+    struct sockaddr_un sun;
+    if (sun_prep(&sun,leaf)) return -1;
+
+    char tbuf[ADDRPORTSTRLEN+1];
+    memset(tbuf,0,sizeof(tbuf));
+    if (addrport2str(tbuf,addr,addrlen)) return -1;
+
+    struct iovec iov[2];
+    iov[0].iov_base=tbuf;
+    iov[0].iov_len=sizeof(tbuf);
+    iov[1].iov_base=(void*)buf;
+    iov[1].iov_len=len;
+    
+    struct msghdr m;
+    memset(&m,0,sizeof(m));
+    m.msg_name=&sun;
+    m.msg_namelen=sizeof(sun);
+    m.msg_iov=iov;
+    m.msg_iovlen=2;
+
+    return sendmsg(fd,&m,0);
+}
+
+ssize_t TWRAP(recvfrom) {
+    fdinfo *ent=lookup(fd);
+    if (!ent) return old_recvfrom(fd,buf,len,flags,addr,addrlen);
+
+//fprintf(stderr,"recvfrom %d len=%lu flags=%d al=%lu\n",
+//     fd, (unsigned long)len, flags, (unsigned long)*addrlen);
+
+    if (flags) { errno=ENOEXEC; return -1; }
+
+    char tbuf[ADDRPORTSTRLEN+1];
+
+    struct iovec iov[2];
+    iov[0].iov_base=tbuf;
+    iov[0].iov_len=sizeof(tbuf);
+    iov[1].iov_base=buf;
+    iov[1].iov_len=len;
+
+    struct msghdr m;
+    memset(&m,0,sizeof(m));
+    m.msg_iov=iov;
+    m.msg_iovlen=2;
+
+    ssize_t rr=recvmsg(fd,&m,0);
+    if (rr==-1) return rr;
+    if ((size_t)rr<sizeof(tbuf)) { errno=ENXIO; return -1; }
+    if (tbuf[ADDRPORTSTRLEN]) { errno=E2BIG; return -1; }
+    if (str2addrport(tbuf,addr,addrlen)) {
+       fprintf(stderr, "recvfrom str2addrport `%s' %s\n",tbuf,
+               strerror(errno));
+       return -1;
+    }
+
+    rr -= sizeof(tbuf);
+//fprintf(stderr,"recvfrom %s %lu ok\n",tbuf,(unsigned long)rr);
+    return rr;
+}
diff --git a/subdirmk/.gitignore b/subdirmk/.gitignore
new file mode 100644 (file)
index 0000000..0b5bd46
--- /dev/null
@@ -0,0 +1,9 @@
+# subdirmk - subdirmk/.gitignore
+#  Copyright 2019 Mark Wooding
+#  Copyright 2019 Ian Jackson
+# SPDX-License-Identifier: LGPL-2.0-or-later
+# There is NO WARRANTY.
+
+#----- subdirmk-generated -----
+/regen.mk
+/usual.mk
diff --git a/subdirmk/DEVELOPER-CERTIFICATE b/subdirmk/DEVELOPER-CERTIFICATE
new file mode 100644 (file)
index 0000000..912d22e
--- /dev/null
@@ -0,0 +1,38 @@
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+1 Letterman Drive
+Suite D4700
+San Francisco, CA, 94129
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+    have the right to submit it under the open source license
+    indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+    of my knowledge, is covered under an appropriate open source
+    license and I have the right under that license to submit that
+    work with modifications, whether created in whole or in part
+    by me, under the same open source license (unless I am
+    permitted to submit under a different license), as indicated
+    in the file; or
+
+(c) The contribution was provided directly to me by some other
+    person who certified (a), (b) or (c) and I have not modified
+    it.
+
+(d) I understand and agree that this project and the contribution
+    are public and that a record of the contribution (including all
+    personal information I submit with it, including my sign-off) is
+    maintained indefinitely and may be redistributed consistent with
+    this project or the open source license(s) involved.
+
similarity index 100%
rename from LGPL-2
rename to subdirmk/LGPL-2
diff --git a/subdirmk/README b/subdirmk/README
new file mode 100644 (file)
index 0000000..3d18dc2
--- /dev/null
@@ -0,0 +1,551 @@
+subdirmk - assistance for non-recursive use of make
+===================================================
+
+Introduction
+------------
+
+Peter Miller's 1997 essay _Recursive Make Considered Harmful_
+persuasively argues that it is better to arrange to have a single
+make invocation with the project's complete dependency tree, rather
+than the currently conventional `$(MAKE) -C subdirectory' approach.
+
+However, actually writing a project's build system in a non-recursive
+style is not very ergonomic.  The main difficulties are:
+  - constantly having to write out long file and directory names
+  - the lack of a per-directory make variable namespace means
+    long make variables (or namespace clashes)
+  - it is difficult to arrange that one can cd to a subdirectory
+    and say `make all' and have something reasonable happen
+    (to wit, build an appropriate subset)
+
+`subdirmk' is an attempt to solve these problems (and it also slightly
+alleviates some of the boilerplate needed to support out-of-tree
+builds well, and helps a bit with metaprogramming and rule writing).
+
+Basic approach
+--------------
+
+The developer is expected to write a makefile fragment, in each
+relevant subdirectory, called `Dir.sd.mk'.
+
+These fragments may contain ordinary make language.  Unqualified
+filenames are relative to the build toplevel, and all commands all run
+there.
+
+However, the sigil & is treated specially.  By and large, it refers to
+`the build directory corresponding to this .sd.mk file', etc.
+There are a variety of convenient constructions.
+
+The result is that to a large extent, the Dir.sd.mk has an easy way
+to namespace its "local" make variables, and an easy way to refer to
+its "local" filenames (and filenames in general).
+
+The Dir.sd.mk's are filtered, fed through autoconf in the usual way
+(for @..@-substitutions) and included by one autogenerated toplevel
+makefile.
+
+So all of the input is combined and passed to one make invocation.
+(A corollary is that there is no enforcement of the namespacing:
+discipline is required to prefix relevant variable names with &, etc.)
+
+Each subdirectory is also provided with an autogenerated `Makefile'
+which exists purely to capture ordinary make invocations and arrange
+for something suitable to happen.
+
+Where there are dependencies between subdirectories, each Dir.sd.mk
+can simply refer to files in other subdirectories directly.
+
+Substitution syntax
+-------------------
+
+In general & expands to the subdirectory name when used for a
+filename, and to the subdirectory name with / replaced with _ for
+variable names.  (If your variables start with capital letters and
+your filenames with lowercase.  Otherwise, use &/ or &_.)
+
+Note that & is processed *even in makefile comments*.  The substitutor
+does not understand make syntax, or shell syntax, at all.  However,
+the substitution rules are chosen to work well with constructs which
+are common in makefiles.
+
+In the notation below, we suppose that the substitution is being in
+done in a subdirectory sub/dir of the source tree.  In the RH column
+we describe the expansion at the top level, which is often a special
+case (in general in variable names we call that TOP rather than the
+empty string).
+
+&CAPS          =>      sub_dir_CAPS                    or TOP_CAPS
+&lc            =>      sub/dir/lc                      or lc
+       Here CAPS is any ASCII letter A-Z and lc is a-z.
+       The assumption is that filenames are usually lowercase and
+       variables usually uppercase.  Otherwise, use another syntax:
+
+&/             =>      sub/dir/                        or nothing
+&_             =>      sub_dir_                        or TOP_
+&.             =>      sub/dir                         or .
+       (This implies that `&./' works roughly like `&/', although
+       it can produce a needless `./')
+
+&=             =>      sub_dir                         or TOP
+
+&^lc           =>      ${top_srcdir}/sub/dir/lc
+&^/            =>      ${top_srcdir}/sub/dir/
+&^.            =>      ${top_srcdir}/sub/dir
+
+&~lc           =>      ${top_srcdir}/lc
+&~/            =>      ${top_srcdir}/
+&~.            =>      ${top_srcdir}
+
+In general:
+    ^   pathname of this subdirectory in source tree
+    ~   pathname of top level of source tree
+    /  terminates the path escape } needed if next is
+    _   terminates the var escape  } not letter or space)
+    .   terminates path escape giving dir name (excluding /)
+    =  terminates var escape giving only prefix part (rarely needed)
+  lwsp  starts multi-word processing (see below)
+
+So pathname syntax is a subset of:
+    '&' [ '^' | '~' ] [ lc | '/' | '.' ]
+
+&&             =>      &&              for convenience in shell runes
+
+&\&            =>      &               general escaping mechanism
+&\$            =>      $               provided for $-doubling regimes
+&\NEWLINE                              eats the newline and vanishes
+
+&$VARIABLE     =>      ${sub_dir_VARIABLE}     or ${TOP_VARIABLE}
+       VARIABLE is ASCII starting with a letter and matching \w+
+
+& thing thing... &
+&^ thing thing... &
+&~ thing thing... &
+       Convenience syntax for prefixing multiple filenames.
+       Introduced by & followed by lwsp where lc could go.
+       Each lwsp-separated non-ws word is prefixed by &/ etc.
+        etc. respectively.  No other & escapes are recognised.
+       This processing continues until & preceded by lwsp,
+       or until EOL (the end of the line), or \ then EOL.
+
+&:<directive> <args>....
+       recognised at start of line only (possibly after lwsp)
+
+&:include filename             filename should usually be [&]foo.sd.mk
+&:-include filename            tolerate nonexistent file
+       RHS is &-expanded but filenames are relative to the top
+       srcdir.  This implies that unqualified names are like &~/
+       whereas &/ is like &^/.  &^ and &~ do not work here because
+       they expand to constructions involving literally
+       `$(top_srcdir)', but the RHS is not make-expanded.
+
+&!<lwsp>       disables & until EOL (and then disappears)
+
+&#     delete everything to end of line
+       (useful if the RHS contains unrecognised & constructions)
+
+&TARGETS_things
+       Handled specially.  If mentioned at the start of a line
+       (possibly following whitespace), declares that this
+       subdir ought to have a target `things'.  The rule will be
+               &/things:: $(&TARGETS_things)
+
+       You may extend it by adding more :: rules for the target,
+       but the preferred style is to do things like this:
+               &TARGETS_check += & test-passed.stamp
+
+       It is important to mention &TARGETS_things at least once in
+       the context of each applicable directory, because doing so
+       arranges that the *parent* will also have a `things' target
+       which recursively implies this directory's `things'.
+
+       Must be spelled exactly &TARGETS_things.  &_TARGETS_things,
+       for example, is not magic.  To make the target exist
+       without providing any prerequisites for it, write a line
+       containing just `&TARGETS_things +='.
+
+       `all' is extra special: every directory has an `all'
+       target, which corresponds to &TARGETS.
+
+&:warn [!]WARNTAG ...
+       Suppress (with !) or re-enable (without !) warnings tagged
+       WARNTAG (see section `Warnings', below).  The suppression list
+       is reset at the start of processing in each subdirectory.
+       Warnings that appear at the end of processing are controlled
+       by the final warning state after processing all the toplevel
+       input files (including Final.sd.mk).
+
+&:local+global [!][&]VARIABLE ...
+       Suppresses any warnings relating to forthcoming mentions
+       to VARIABLE or &VARIABLE, as applicable.  Scope ends at
+       the end of the current directory's Suffix.sd.mk.
+       Prefixing with ! removes [&]VARIABLE from the suppresion list.
+
+&:changequote NEWQUOTE
+       changes the escape sequence from & to literally NEWQUOTE
+       NEWQUOTE may be any series of of non-whitespace characters,
+       and is terminated by EOL or lwsp.  The whole line is
+       discarded.
+
+       After this, write NEWQUOTE instead of &, everywhere.
+       The effect is unscoped and lasts until the next setting,
+       or until the end of the current directory's Suffix.sd.mk.
+       It takes effect on &:include'd files too, so maybe set
+       it back before using &:include.
+
+       Notably
+               NEWQUOTENEWQUOTE        => NEWQUOTENEWQUOTE
+               NEWQUOTE\NEWQUOTE       => NEWQUOTE
+               NEWQUOTE\$              => $
+               NEWQUOTE:changequote &  set escape back to &
+
+
+Dollar doubling and macro assistance
+------------------------------------
+
+&$+            Starts dollar-doubling
+&$-            Stops dollar-doubling
+       Both are idempotent and local to the file or context.
+
+This is useful both for make macrology involving $(eval ...), and
+possibly for helping write complicated recipes involving shell
+variables, inline Perl code, etc.
+
+Sometimes we will show $'s being doubled inside another construct.
+This means the content of the construct is $-doubled: $-doubling is
+locally enabled, and restored afterwards.
+
+&:macro NAME   =>      define NAME
+STUFF $ THINGS ..      STUFF $$ THINGS
+&:endm         ..      endef
+       NAME is processed for &
+
+&${..$..} =>   ${eval ${call ..$$..}}
+       (matches { } pairs to find the end)
+       content is $-doubled (unless it contains &$- to turn that off)
+
+Together &:macro and &${...} provide a more reasonable macro facility
+than raw make.  They solve the problem that make expansions cannot
+directly generate multiple rules, variable, etc.; instead, `$(eval )'
+must be used, but that re-expands the argument, meaning that all the
+literal text must be $-doubled.  This applies to the macro text and to
+the arguments.  Also `$(eval $(call ...))' is an unfortunate syntax.
+Hence &:macro and &${...}.
+
+While dollar-doubling:
+- - - - - - - - - - -
+
+$      =>      $$      including $'s produced by other
+                        &-expansions not mentioned here
+
+&\$    =>      $
+&$(    =>      ${      (expands to { } so it is useable for shell too)
+&$NN   =>      ${NN}   where N are digits
+
+A few contexts do not support $-doubling, such as directive arguments
+or places where this might imply $-quadrupling.  (There is no way to
+get $-quadrupling.)
+
+
+Invocation, "recursive" per-directory targets
+---------------------------------------------
+
+Arrangements are made so that when you run `make foo' in a
+subdirectory, it is like running the whole toplevel makefile, from the
+toplevel, as `make subdir/foo'.  If `subdir/foo' is a file that might
+be built, that builds it.
+
+But `foo' can also be a conventional target like `all'.
+
+Each subdirectory has its own `all' target.  For example a
+subdirectory `src' has a target `src/all'.  The rules for these are
+automatically generated from the settings of the per-directory
+&TARGETS variables.  &TARGETS is magic in this way.  (In
+src/Dir.sd.mk, &TARGETS of course refers to a make variable called
+src_TARGETS.)
+
+The `all' target in a parent directory is taken to imply the `all'
+targets in all of its subdirectories, recursively.  And in the
+autogenerated stub Makefiles, `all' is the default target.  So if you
+just type `make' in the toplevel, you are asking for `&all'
+(<subdir>/all) for every directory in the project.
+
+In a parallel build, the rules for all these various subdirectory
+targets may be in run in parallel: there is only one `make' invocation
+at a time.  There is no sequencing between subdirectories, only been
+individual targets (as specified according to their dependencies).
+
+You can define other per-directory recursive targets too: set the
+variable &TARGETS_zonk, or whatever (being sure to write &TARGETS_zonk
+at the start of a line).  This will create a src/zonk target (for
+appropriate value of src/).  Unlike `all', these other targets only
+exist in areas of the project where at least something mentions them.
+So for example, if &TARGETS_zonk is set in src but not lib, `make
+zonk' in lib will fail.  If you want to make a target exist
+everywhere, += it with nothing in Prefix.sd.mk or Suffix.sd.mk (see
+below).
+
+Prefix.sd.mk, Suffix.sd.mk, Final.sd.mk, inclusion
+--------------------------------------------------
+
+The files Prefix.sd.mk and Suffix.sd.mk in the toplevel of the source
+are automatically processed before and after each individual
+directory's Dir.sd.mk, and the &-substituted contents therefore
+appear once for each subdirectory.
+
+This lets you do per-directory boilerplate.  Some useful boilerplate
+is already provided in subdirmk, for you to reference like this:
+  &:include subdirmk/cdeps.sd.mk
+  &:include subdirmk/clean.sd.mk
+For example you could put that in Suffix.sd.mk.
+
+The top-level Dir.sd.mk is the first makefile included after the
+autogenerated `main.mk' which merely has some basic settings and
+includes.  So if you want to get in early and set global variables,
+put them near the top of Dir.sd.mk.
+
+The file Final.sd.mk in the toplevel directory is processed and
+the result included after all the other files.  Its subdirmk
+filtering context inherits warning suppressions from the toplevel's
+Dir.sd.mk etc., but not anything else.
+
+subdirmk's filter script itself sets (only) these variables:
+  top_srcdir
+  abs_top_srcdir
+  SUBDIRMK_MAKEFILES
+  MAKEFILE_TEMPLATES
+You are likely to want to define $(PWD), and shorter names for
+top_srdir and abs_top_srcdir (we suggest $(src) and $(abs_src)).
+
+Warnings
+--------
+
+subdirmk's `generate' program, which does the acual &-substitution,
+can produce some warnings about your .sd.mk files.  These can be
+suppressed with the &:warn directive.  The warning tags are:
+
+    local+global
+       The same VARNAME was used both with and without an & prefix.
+       This can be confusing.  Also, if you avoid this then you will
+       get a warning iff you accidentally leave off a needed &.
+       The generation of this warning depends on scanning your
+       makefile for things that look like variable references, which
+       subdirmk does not do completely perfectly.  Exciting make
+       syntax may evade this warning, or require suppressions.
+       (You can suppress this warning for a particular VARNAME with
+       the &:local+global directive.)
+
+    single-char-var
+       A variable expansion like $FBAR.  make's expansion rules
+       interpret this as $(F)BAR.  It's normally better to write
+       it this way, at least if the variable expansion is followed
+       by more letters.  Note that &$FOO works differently to
+       raw make: it expands to ${sub_dir_FOO}.
+
+    broken-var-ref
+        An attempt at variable expansion looking like $&...
+       You probably expected this to mean $(TOP_F)BAR but it
+       expands to $TOP_FBAR which make thinks means $(T)OP_FBAR.
+
+    unknown-warning
+       &:warn was used to try to enable a warning that this version
+       of subdirmk does not understand.  (Note that an attempt to
+       *dis*able an unknown warning is only reported if some other
+       warning was issued which might have been disabled.)
+
+
+Guides, hints, and further explanations
+=======================================
+
+Incorporating this into your project
+------------------------------------
+
+Use `git-subtree' to merge the subdirmk/ directory.  You may find it
+useful to symlink the DEVELOPER-CERTIFICATE file (git can store
+symlinks as symlinks - just `git add' the link).  And you probably
+want to mention the situation in your top-level COPYING and HACKING.
+
+Symlink autogen.sh into your project toplevel.
+
+In your configure.ac, say
+
+  m4_include([subdirmk/subdirmk.ac])
+  SUBDIRMK_SUBDIRS([...list of subdirectories in relative syntax...])
+
+Write a Dir.sd.mk in each directory.  See the substitution syntax
+reference, above, and the example/ directory here.  The toplevel
+Dir.sd.mk should probably contain:
+
+  include subdirmk/usual.mk
+  include subdirmk/regen.mk
+
+Write a Suffix.sd.mk in the toplevel, if you want.  It should probably
+have:
+
+  &:include subdirmk/cdeps.sd.mk
+  &:include subdirmk/clean.sd.mk
+
+
+Hints
+-----
+
+You can convert your project incrementally.  Start with the top-level
+Makefile.in and rename it to Dir.sd.mk, and add the appropriate
+stuff to configure.ac, and fix everything up.  Leave the existing
+$(MAKE) -C for your existing subdirectories alone.  Then you can
+convert individual subdirectories, or classes of subdirectories, at
+your leisure.  (You must be /sure/ that each recursive (non-subdirmk)
+subdirectory will be entered only once at a time, but your existing
+recursive make descent system should already do that or you already
+have concurrency bugs.)
+
+Aside from this, be very wary of any invocation of $(MAKE) anywhere.
+This is a frequent source of concurrency bugs in recursive make build
+systems.  When combined with nonrecursive make it's all in the same
+directory and there is nothing stopping the different invocations
+ending up trying to make the same targets at the same time. That
+causes hideous racy lossage.  There are ways to get this to work
+reliably but it is advanced stuff.
+
+If you make syntax errors, or certain kinds of other errors, in your
+makefiles, you may find that just `make' is broken now and cannot get
+far enough to regenerate a working set of makefiles.  If this happens
+just rerun ./config.status by hand.
+
+If you go back and forth between different versions of your code you
+can sometimes find that `make' complains that one of your Dir.sd.mk
+files is missing: typically, if iot was used and therefore a
+dependency in some other version of your code.  If you run `make
+clean' (or `make realclean') these dependencies are suppressed, which
+will clear up the problem.
+
+
+Global definitions
+------------------
+
+If want to set global variables, such as CC, that should only be done
+once.  You can put them in your top-level Dir.sd.mk, or a separate
+file you `include' and declare using SUBDIRMK_MAKEFILES.
+
+If you need different settings of variables like CC for different
+subdirectories, you should probably do that with target-specific
+variable settings.  See the info node `(make) Target-specific'.
+
+
+Directory templates `.sd.mk' vs plain autoconf templates `.mk.in'
+--------------------------------------------------------------------
+
+There are two kinds of template files.
+
+ Filename                 .sd.mk                  .mk.in
+
+ Processed by             &-substitution,         autoconf only
+                          then autoconf
+
+ Instantiated             Usu. once per subdir    Once only
+
+ Need to be mentioned     No, but Dir.sd.mk       All not in subdirmk/
+ in configure.ac?         via SUBDIRMK_SUBDIRS    via SUBDIRMK_MAKEFILES
+
+ How to include           `&:include foo.sd.mk'   `include foo.mk'
+                         in all relevant .sd.mk  in only one
+                          (but not needed for     Dir.sd.mk
+                           Prefix, Suffix, Final)
+
+If you `include subdirmk/regen.mk', dependency management and
+automatic regeneration for all of this template substitution, and for
+config.status etc. is done for you.
+
+
+Tables of file reference syntaxes
+---------------------------------
+
+In a nonrecursive makefile supporting out of tree builds there are
+three separate important distinctions between different file
+locations:
+
+ (i) In the build tree, or in the source tree ?
+
+ (ii) In (or relative to) the subdirectory to which this Dir.sd.mk
+     relates, or relative to the project's top level ?
+
+ (iii) Absolute or relative pathname ?  Usually relative pathnames
+     suffice.  Where an absolute pathname is needed, it can be built
+     out of &/ and an appropriate make variable such as $(PWD).
+
+Path construction &-expansions are built from the following:
+
+                      Relative paths in...
+                      build     source
+                                                       
+  This directory      &         &^
+  Top level           .         &~
+
+In more detail, with all the various options laid out:
+
+      Recommended     Relative paths in...   Absolute paths in...
+             for      build     source       build         source
+                                                       
+  This       lc       &file     &^file       $(PWD)/&file  $(abs_src)/&file
+  directory  any      &/file    &^/file      $(PWD)/&/file $(abs_src)/&/file
+             several  & f g h   &^ f g h     $(addprefix...)
+                                             
+  Top        lc       file      &~file
+  level      any      file      &~/file      $(PWD)/file   $(abs_src)/file
+             .mk.in   file      $(src)/file  $(PWD)/file   $(abs_src)/file
+             several  f g h     &~ f g h     $(addprefix...)
+
+(This assumes you have appropriate make variables src, PWD and
+abs_src.)
+
+
+Subdirectory and variable naming
+--------------------------------
+
+The simple variable decoration scheme does not enforce a strict
+namespace distinction between parts of variable names which come from
+subdirectory names, and parts that mean something else.
+
+So it is a good idea to be a bit careful with your directory naming.
+`TOP', names that contain `_', and names that are similar to parts of
+make variables (whether conventional ones, or ones used in your
+project) are best avoided.
+
+If you name your variables in ALL CAPS and your subdirectories in
+lower case with `-' rather than `_', there will be no confusion.
+
+
+Legal information
+=================
+
+subdirmk is
+ Copyright 2019 Mark Wooding
+ Copyright 2019 Ian Jackson
+
+    subdirmk and its example is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This 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.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library as the file LGPL-2.
+    If not, see https://www.gnu.org/.
+
+Individual files generally contain the following tag in the copyright
+notice, instead of the full licence grant text:
+  SPDX-License-Identifier: LGPL-2.0-or-later
+As is conventional, this should be read as a licence grant.
+
+Contributions are accepted based on the git commit Signed-off-by
+convention, by which the contributors' certify their contributions
+according to the Developer Certificate of Origin version 1.1 - see
+the file DEVELOPER-CERTIFICATE.
+
+Where subdirmk is used by and incorporated into another project (eg
+via git subtree), the directory subdirmk/ is under GNU LGPL-2.0+, and
+the rest of the project are under that other project's licence(s).
+(The project's overall licence must be compatible with LGPL-2.0+.)
diff --git a/subdirmk/autogen.sh b/subdirmk/autogen.sh
new file mode 100755 (executable)
index 0000000..528cfbd
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+# subdirmk, autogen.sh (conventional autoconf invocation script)
+#  Copyright 2019 Ian Jackson
+# SPDX-License-Identifier: LGPL-2.0-or-later
+# There is NO WARRANTY.
+set -e
+cd ${0%/*}
+autoconf
similarity index 100%
rename from cdeps.sd.mk
rename to subdirmk/cdeps.sd.mk
similarity index 100%
rename from clean.sd.mk
rename to subdirmk/clean.sd.mk
similarity index 100%
rename from example/Dir.sd.mk
rename to subdirmk/example/Dir.sd.mk
similarity index 100%
rename from example/LGPL-2
rename to subdirmk/example/LGPL-2
similarity index 100%
rename from example/src/toy.c
rename to subdirmk/example/src/toy.c
similarity index 100%
rename from example/subdirmk
rename to subdirmk/example/subdirmk
similarity index 100%
rename from generate
rename to subdirmk/generate
similarity index 100%
rename from regen.mk.in
rename to subdirmk/regen.mk.in
similarity index 100%
rename from subdirmk.ac
rename to subdirmk/subdirmk.ac
similarity index 100%
rename from tests/.gitignore
rename to subdirmk/tests/.gitignore
similarity index 100%
rename from tests/check
rename to subdirmk/tests/check
similarity index 100%
rename from tests/tests.mk
rename to subdirmk/tests/tests.mk
similarity index 100%
rename from usual.mk.in
rename to subdirmk/usual.mk.in
diff --git a/test-common.sd.mk b/test-common.sd.mk
new file mode 100644 (file)
index 0000000..023e41c
--- /dev/null
@@ -0,0 +1,30 @@
+
+include common.make
+
+&TESTSCRIPTS ?= $(shell echo &^/t-*[0-9a-z])
+&TESTNAMES := $(patsubst t-%,%,$(notdir $(&TESTSCRIPTS)))
+
+&DEPS += $(src)/test-common.tcl
+&DEPS += common.make
+&DEPS += $(src)/test-common.sd.mk
+&DEPS += &/Dir.mk
+
+&check-real: $(foreach t,$(&TESTNAMES),&d-$t/ok)
+
+CHECK_SILENT ?= @
+
+&d-%/ok: &^/t-% $(&DEPS)
+       $(CHECK_SILENT) rm -rf &d-$*; mkdir &d-$*
+       $(CHECK_SILENT) export SECNET_TEST_BUILDDIR=$(topbuilddir); \
+        export PYTHONBYTECODEBASE=/dev/null; \
+        cd $(src) && \
+        &/t-$* >$(topbuilddir)/&/d-$*/log 2>&\&1 \
+        || { cat $(topbuilddir)/&/d-$*/log >&\&2; false; }
+       $(CHECK_SILENT) printf "&/$* "
+       $(CHECK_SILENT) touch $@
+
+&CLEAN += & *.so
+
+&clean::
+       $(RM) -rf & tmp
+       $(RM) -rf & d-*
diff --git a/test-common.tcl b/test-common.tcl
new file mode 100644 (file)
index 0000000..162ad3a
--- /dev/null
@@ -0,0 +1,23 @@
+
+proc prefix_some_path {pathvar entry} {
+    global env
+    set l {}
+    catch { set l [split $env($pathvar) :] }
+    set l [concat [list $entry] $l]
+    set env($pathvar) [join $l :]
+}
+
+if {![catch {
+    set builddir $env(SECNET_TEST_BUILDDIR)
+}]} {} else {
+    set builddir .
+}
+
+if {![catch {
+    set tmp $env(AUTOPKGTEST_ARTIACTS)
+}]} {} elseif {![catch {
+    set tmp $env(AUTOPKGTEST_TMP)
+}]} {} elseif {[regsub {^(?:\./)?([sm]test)/t-} $argv0 {\1/d-} tmp]} {
+    set tmp $builddir/$tmp
+    file mkdir $tmp
+}
diff --git a/test-example/Dir.sd.mk b/test-example/Dir.sd.mk
new file mode 100644 (file)
index 0000000..30813b2
--- /dev/null
@@ -0,0 +1,11 @@
+&TARGETS += & sites.conf inside.key outside.key
+
+include common.make
+
+&/%.key: &^/%.key.b64
+       base64 -d <$< >$@.new && mv -f $@.new $@
+
+&sites.conf: $(src)/make-secnet-sites &^/sites &/Dir.mk
+       $(src)/make-secnet-sites &^/sites $@
+
+&CLEAN += *.new
diff --git a/test-example/README b/test-example/README
new file mode 100644 (file)
index 0000000..72f98ab
--- /dev/null
@@ -0,0 +1,50 @@
+This directory contains some files useful for ad-hoc tests.
+With these it is possible to run a test of secnet on a Linux host
+even if that Linux host does not have another working network connection.
+
+The keys here are (obviously) public.  They were generated like this:
+ ssh-keygen -C inside@example.com -f test-example/inside.key -t rsa1 -b 1024
+ ssh-keygen -C outside@example.com -f test-example/outside.key -t rsa1 -b 1024
+ # edit sites to paste {inside,outside}.key.pub into pubkey lines
+ base64 <inside.key >inside.key.b64
+ base64 <outside.key >outside.key.b64
+
+To run the test:
+ Run the makefile:
+  make -C test-example/
+ In one window, as root
+  ./secnet -dvnc test-example/inside.conf
+ And in another
+  ./secnet -dvnc test-example/outside.conf
+ Then in a third
+  ping -I secnet-test-i 172.18.232.2 
+For running under valgrind memcheck, do something like this:
+  valgrind --num-callers=40 --gen-suppressions=yes --db-attach=yes \
+    --leak-check=full --suppressions=test-example/memcheck.suppressions \
+    ./secnet -dvnc test-example/outside.conf
+NB that --num-callers is needed as secnet's stack can be deep.
+
+The config file outside-unshare.conf can be used on Linux in
+conjunction with test-example/fake-userv and a built checkout of
+userv-utils.git to run the "outside" copy of secnet in a new "network
+namespace".
+
+
+
+Everything in this directory is part of secnet.  See README (in the
+directory above) for full list of copyright holders.
+
+secnet is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+secnet 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.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+version 3 along with secnet; if not, see
+https://www.gnu.org/licenses/gpl.html.
diff --git a/test-example/bogus-setup-request.c b/test-example/bogus-setup-request.c
new file mode 100644 (file)
index 0000000..c9cfc3c
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+   test-example/bogus-setup-request 127.0.0.1 19098 test-example/inside/inside 127.0.0.1 16096 test-example/outside/outside
+  */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+                                /*
+ | 00000  00 00 00 00 00 00 00 01  01 01 01 01 00 1a 74 65  ........ ......te |
+          ~~~~~~~~~~~ ~~~~~~~~~~~  ~~~~~~~~~~~ ~~~~~|~~~~~
+          sessionid   sender's     type         sender's
+          zero in     index        fixed for     name
+          msg1                     msg1
+
+ | 00010  73 74 2d 65 78 61 6d 70  6c 65 2f 69 6e 73 69 64  st-examp le/insid |
+ | 00020  65 2f 69 6e 73 69 64 65  00 1c 74 65 73 74 2d 65  e/inside ..test-e |
+                                   ~~~~~|~~~~~~~~~~~~~~~~~
+                                    recipient's name
+                                  
+ | 00030  78 61 6d 70 6c 65 2f 6f  75 74 73 69 64 65 2f 6f  xample/o utside/o |
+ | 00040  75 74 73 69 64 65 8d f0  3f 35 d6 c8 1f c0        utside.. ?5....   |
+          ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
+                           sender's nonce
+                                  */
+
+typedef struct {
+    const char *name;
+    union {
+       struct sockaddr sa;
+       struct sockaddr_in sin;
+    };
+} Ep;
+
+static void endaddr(Ep *ep, char **argv, int base) {
+    int r;
+    ep->sin.sin_family=AF_INET;
+    r=inet_aton(argv[base],&ep->sin.sin_addr); assert(r);
+    ep->sin.sin_port=htons(atoi(argv[base+1]));
+    ep->name=argv[base+2];
+}
+
+static void endname(uint8_t **msgp, const Ep *ep) {
+    int l=strlen(ep->name); assert(l<=65535);
+    *(*msgp)++ = l>>8;
+    *(*msgp)++ = l;
+    memcpy(*msgp, ep->name, l);
+    *msgp += l;
+}
+
+static Ep us, them;
+
+int main(int argc, char **argv) {
+    int r;
+
+    assert(argc==7);
+
+    endaddr(&us,argv,1);
+    endaddr(&them,argv,4);
+
+    static const uint8_t mprefix[]={
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x01,
+       0x01, 0x01, 0x01, 0x01,
+    };
+    static const uint8_t msuffix[]={
+       /* our nonce, fixed he he */
+       0x8d, 0xf0, 0x3f, 0x35, 0xd6, 0xc8, 0x1f, 0xc0
+    };
+    int msglen= (sizeof(mprefix) +
+                2+strlen(us.name) +
+                2+strlen(them.name) +
+                sizeof(msuffix));
+    uint8_t msg[msglen];
+    uint8_t *msgp=msg;
+
+#define PREFIXSUFFIX(prefixsuffix) do {                        \
+    memcpy(msgp,prefixsuffix,sizeof(prefixsuffix));    \
+    msgp += sizeof(prefixsuffix);                      \
+  }while(0)
+
+    PREFIXSUFFIX(mprefix);
+    
+    endname(&msgp,&us);
+    endname(&msgp,&them);
+
+    PREFIXSUFFIX(msuffix);
+
+    assert(msgp == msg+msglen);
+
+    struct protoent *proto=getprotobyname("udp");
+    int fd=socket(AF_INET, SOCK_DGRAM, proto->p_proto);
+    r=bind(fd,&us.sa,sizeof(us.sin)); if (r) { perror("bind us2"); exit(1); }
+
+    for (;;) {
+       r=sendto(fd,msg,msglen,0,&them.sa,sizeof(them.sin));
+       if (r < 0) perror("sendto");
+
+       r=getchar();
+       if (r==EOF) {
+           if (ferror(stdin)) { perror("getchar"); exit(1); }
+           break;
+       }
+       if (r!='\n')
+           break;
+    }
+    exit(0);
+}
diff --git a/test-example/common.conf b/test-example/common.conf
new file mode 100644 (file)
index 0000000..8fefad6
--- /dev/null
@@ -0,0 +1,14 @@
+log logfile {
+       filename "/dev/tty";
+       class "info","notice","warning","error","security","fatal";
+};
+system {
+       userid "secnet";
+};
+resolver adns {
+};
+log-events "all";
+random randomfile("/dev/urandom",no);
+transform eax-serpent { }, serpent256-cbc { };
+include test-example/sites.conf
+sites map(site,vpn/test-example/all-sites);
diff --git a/test-example/fake-userv b/test-example/fake-userv
new file mode 100755 (executable)
index 0000000..6f5da40
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -e
+echo >&2 "$0: invoked as $0 $*"
+shift
+shift
+exec 3<&0 4>&1 5>&2 >&2 </dev/null
+exec xterm -T netns -e unshare -n -- sh -xc '
+  ../userv-utils.git/ipif/service \* -- "$@" <&3 >&4 2>&5 &
+  sleep 0.1
+  env - bash -i
+' x "$@"
diff --git a/test-example/inside-polypath.conf b/test-example/inside-polypath.conf
new file mode 100644 (file)
index 0000000..5e66e01
--- /dev/null
@@ -0,0 +1,20 @@
+comm polypath {
+       buffer sysbuffer(4096);
+       monitor-command "./polypath-interface-monitor-linux";
+       interfaces "!secnet-test*";
+       permit-loopback True;
+};
+netlink tun {
+       name "netlink-tun"; # Printed in log messages from this netlink
+       local-address "172.18.232.9";
+       secnet-address "172.18.232.10";
+       remote-networks "172.18.232.0/28";
+       mtu 1400;
+       buffer sysbuffer(2048);
+       interface "secnet-test-i";
+};
+local-name "test-example/inside/inside";
+local-key rsa-private("test-example/inside.key");
+local-mobile True;
+mtu-target 1260;
+include test-example/common.conf
diff --git a/test-example/inside.conf b/test-example/inside.conf
new file mode 100644 (file)
index 0000000..060a0bf
--- /dev/null
@@ -0,0 +1,21 @@
+netlink tun {
+       name "netlink-tun"; # Printed in log messages from this netlink
+       local-address "172.18.232.9";
+       secnet-address "172.18.232.10";
+       remote-networks "172.18.232.0/28";
+       mtu 1400;
+       buffer sysbuffer(2048);
+       interface "secnet-test-i";
+};
+comm udp {
+       port 16913;
+       buffer sysbuffer(4096);
+}, udp {
+       port 16910;
+       buffer sysbuffer(4096);
+};
+local-name "test-example/inside/inside";
+local-key rsa-private("test-example/inside.key");
+local-mobile True;
+mtu-target 1260;
+include test-example/common.conf
diff --git a/test-example/inside.key.b64 b/test-example/inside.key.b64
new file mode 100644 (file)
index 0000000..d384c7c
--- /dev/null
@@ -0,0 +1,10 @@
+U1NIIFBSSVZBVEUgS0VZIEZJTEUgRk9STUFUIDEuMQoAAAAAAAAAAAQABAC5N9rmU46hhdLO1FVh
+Efkc9cq+x/UdC/a+nt0yM4HswxfChfJpcHq008Hkd4KOqRZORG7N5Q8fKPpkrnt3T3qSDX4P5HOW
+5Q+2Qc82h1hO4mDbHo2xqmp4hv/88fHgPQTW9MffriDFs24HTt7uOqvx5LNtdmrw5ws6cXuyLwan
+lQARAQABAAAAEmluc2lkZUBleGFtcGxlLmNvbe8z7zMD/1/rgT3PAAq+V1ItvJmsySoqUHlE7LfC
+PmKxuzQIYLzQvDlNTSE10xZapAtBqSdggeC+p/ORMKeefS4u/lnnmz2tW9TlbtwWfj5Bwm/ftUZR
+8BhelZQn5+/vTv1jLZ9dibLhemd20XxpMRIoOg+1w4xfbh1DoJbqs8OCCPPnNVJxAf9h3Hq0x84a
+P0JOgyFFNatWcRKVJxapseeZPnpIAnkaDZ0KirE1RZFkHbfL4HFL3kI3MI657rE7rSC2yakvJtX9
+AgDD98/vGKw19bSdM8dHbocQQdDmn3SG5U9psbkvNQh06seKNL9QOeH2iHqjzBXmwTjPiWphdsVP
+dFOBy1VE52YPAgDx9QU0xrSytFrjcqlP/FICaBiuJ9g0t4RbYBcm2iZaXLwXLDTX91arNJJrzblX
+9yMkHDBDw9j1nKXnig+8dtwbAAAAAA==
diff --git a/test-example/inside.key.pub b/test-example/inside.key.pub
new file mode 100644 (file)
index 0000000..85fea76
--- /dev/null
@@ -0,0 +1 @@
+1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com
diff --git a/test-example/memcheck.suppressions b/test-example/memcheck.suppressions
new file mode 100644 (file)
index 0000000..42080da
--- /dev/null
@@ -0,0 +1,14 @@
+{
+   secnet_read_conffile
+   Memcheck:Leak
+   ...
+   fun:read_conffile
+   fun:main
+}
+{
+   secnet_enter_phase
+   Memcheck:Leak
+   ...
+   fun:enter_phase
+   fun:main
+}
diff --git a/test-example/outside-random.conf b/test-example/outside-random.conf
new file mode 100644 (file)
index 0000000..5c23920
--- /dev/null
@@ -0,0 +1,16 @@
+netlink userv-ipif {
+       name "netlink-ipif"; # Printed in log messages from this netlink
+       local-address "172.18.232.1";
+       secnet-address "172.18.232.2";
+       remote-networks "172.18.232.0/28";
+       mtu 1400;
+       buffer sysbuffer(2048);
+       userv-path "test-example/random-fake-userv";
+};
+comm udp {
+       port 16900;
+       buffer sysbuffer(4096);
+};
+local-name "test-example/outside/outside";
+local-key rsa-private("test-example/outside.key");
+include test-example/common.conf
diff --git a/test-example/outside-unshare.conf b/test-example/outside-unshare.conf
new file mode 100644 (file)
index 0000000..2811962
--- /dev/null
@@ -0,0 +1,16 @@
+netlink userv-ipif {
+       name "netlink-ipif"; # Printed in log messages from this netlink
+       local-address "172.18.232.1";
+       secnet-address "172.18.232.2";
+       remote-networks "172.18.232.0/28";
+       mtu 1400;
+       buffer sysbuffer(2048);
+       userv-path "test-example/fake-userv";
+};
+comm udp {
+       port 16900;
+       buffer sysbuffer(4096);
+};
+local-name "test-example/outside/outside";
+local-key rsa-private("test-example/outside.key");
+include test-example/common.conf
diff --git a/test-example/outside.conf b/test-example/outside.conf
new file mode 100644 (file)
index 0000000..8944130
--- /dev/null
@@ -0,0 +1,16 @@
+netlink tun {
+       name "netlink-tun"; # Printed in log messages from this netlink
+       local-address "172.18.232.1";
+       secnet-address "172.18.232.2";
+       remote-networks "172.18.232.0/28";
+       mtu 1400;
+       buffer sysbuffer(2048);
+       interface "secnet-test-o";
+};
+comm udp {
+       port 16900;
+       buffer sysbuffer(4096);
+};
+local-name "test-example/outside/outside";
+local-key rsa-private("test-example/outside.key");
+include test-example/common.conf
diff --git a/test-example/outside.key.b64 b/test-example/outside.key.b64
new file mode 100644 (file)
index 0000000..f8ed4aa
--- /dev/null
@@ -0,0 +1,10 @@
+U1NIIFBSSVZBVEUgS0VZIEZJTEUgRk9STUFUIDEuMQoAAAAAAAAAAAQABAC4D2q3B/nZUjsGMX72
+5FrgEB1y0uYS732QF/NXOEs9FA8/xmM68NF8JRfCctlCm9kQ9t/0xW+wOQTNg0BFIdgbZjXIwXLy
+K9rreM1G1BsTjROtiz1UyjZMpo3Z89SWjtYCVN/UldRhakw/o0vrEKkZDTxiryhhYCGDUkONNsa5
+1QARAQABAAAAE291dHNpZGVAZXhhbXBsZS5jb23IlsiWA/9AO6kbPN5VmBvfGnDbim+oWBde1fjS
+zN895Q3X915Sb2iu8fX5QMdqkqtLAbeORkMnZ3BaxHgowI1Lhy1rstbuiUcd3WWB6xUDcQll85Cy
++2IFfvFDKH7HsrzxgWx9M23WewlTje2NmVF0Y3xR39w2jUCLuEcyaWdPPQiLTucCgQH/axUYwPI5
+6QEKPiONve88GpehGCjereP5EjWTJomjQI+brOhnPckiWLwXXtWZoa894jpbVT8BtHNdDUg2gPFV
+pwIA0FQowUgwxCnCoNZe/v/K5zwP3ar8OPoBV2c8rnEuZ2sR0AdLcBpaCpOQf7LKk9p+GUOHlMJy
+hkrz7tAitvXEdQIA4i2dOA/PVYD6ZCZrwY5SToBmVtOzt2TVdhLbB/XDJ91ydl0uDdyN0Sn/Dyx+
+I55YwyhLA8zNV8mL4ZQS8OLz4QAAAAA=
diff --git a/test-example/outside.key.pub b/test-example/outside.key.pub
new file mode 100644 (file)
index 0000000..5e4cc0f
--- /dev/null
@@ -0,0 +1 @@
+1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com
diff --git a/test-example/random-fake-userv b/test-example/random-fake-userv
new file mode 100755 (executable)
index 0000000..cccd22c
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/perl -w
+
+use strict;
+use POSIX;
+
+open R, '/dev/urandom' or die $!;
+
+system 'cat >/dev/null &';
+
+sub randbytes ($) {
+    my ($count) = @_;
+    my $s;
+    my $r = read R, $s, $count;
+    die $! unless $r==$count;
+    return $s;
+}
+
+sub randbyteval () {
+    my $b = randbytes 1;
+    my ($r) = unpack 'C', $b;
+    return $r;
+}
+
+sub randvalue ($$) {
+    my ($min,$maxplus1) = @_;
+    my $b = randbyteval;
+    return floor(($b/256.0) * ($maxplus1-$min)) + $min;
+}
+
+for (;;) {
+    my $lenbits = randvalue 0,14;
+    my $len= (randbyteval << 8) | randbyteval;
+    $len &= (1 << $lenbits)-1;
+    my $data = randbytes $len;
+    if (randbyteval >= 0x80) {
+       $data =~ s{[\xc0\xdb]}{
+            $& eq "\xc0" ? "\xcb\xdc" :
+            $& eq "\xdb" ? "\xcb\xdd" :
+            die
+        }ge;
+    }
+    print "\xc0";
+    print $data;
+    STDOUT->flush;
+}
diff --git a/test-example/sites b/test-example/sites
new file mode 100644 (file)
index 0000000..93d536a
--- /dev/null
@@ -0,0 +1,23 @@
+vpn test-example
+contact devnull@example.com
+dh 8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3 2
+hash sha1
+key-lifetime 72000000
+restrict-nets 172.18.232.0/28
+setup-timeout 2000
+setup-retries 5
+
+location out root
+site outside
+  networks 172.18.232.0/29
+  peer 172.18.232.1
+  address [::1] 16900
+  pubkey 1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com
+
+location in root
+site inside
+  networks 172.18.232.8/29
+  peer 172.18.232.9
+  address [127.0.0.1] 16910
+  mobile True
+  pubkey 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com
diff --git a/transform-cbcmac.c b/transform-cbcmac.c
new file mode 100644 (file)
index 0000000..b481346
--- /dev/null
@@ -0,0 +1,381 @@
+/* Transform module - bulk data transformation */
+
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+/* For now it's hard-coded to do sequence
+   number/pkcs5/serpent-cbcmac/serpent with a 256 bit key for each
+   instance of serpent. We also require key material for the IVs for
+   cbcmac and cbc. Hack: we're not using full 128-bit IVs, we're just
+   using 32 bits and encrypting to get the full IV to save space in
+   the packets sent over the wire. */
+
+#include <stdio.h>
+#include <string.h>
+#include "secnet.h"
+#include "util.h"
+#include "serpent.h"
+#include "unaligned.h"
+#include "hexdebug.h"
+
+/* Required key length in bytes */
+#define REQUIRED_KEYLEN ((512+64+32)/8)
+
+#include "transform-common.h"
+
+struct transform_params {
+    SEQNUM_PARAMS_FIELDS;
+};
+
+struct transform {
+    closure_t cl;
+    struct transform_if ops;
+    struct transform_params p;
+};
+
+struct transform_inst {
+    struct transform_inst_if ops;
+    struct transform_params p;
+    struct keyInstance cryptkey;
+    struct keyInstance mackey;
+    uint32_t cryptiv;
+    uint32_t maciv;
+    SEQNUM_KEYED_FIELDS;
+};
+
+#define PKCS5_MASK 15
+
+static bool_t transform_setkey(void *sst, uint8_t *key, int32_t keylen,
+                              bool_t direction)
+{
+    struct transform_inst *ti=sst;
+
+    if (keylen<REQUIRED_KEYLEN) {
+       Message(M_ERR,"transform_create: insufficient key material supplied "
+               "(need %d bytes, got %d)\n",REQUIRED_KEYLEN,keylen);
+       return False;
+    }
+
+#if 0
+    {
+       printf("Setting key to: ");
+       hexdebug(stdout,key,keylen);
+       printf("\n");
+    }
+#endif /* 0 */
+
+    serpentbe_makekey(&ti->cryptkey,256,key);
+    serpentbe_makekey(&ti->mackey,256,key+32);
+    ti->cryptiv=get_uint32(key+64);
+    ti->maciv=get_uint32(key+68);
+    uint32_t firstseq=get_uint32(key+72);
+    SEQNUM_KEYED_INIT(firstseq,firstseq);
+
+    return True;
+}
+
+TRANSFORM_VALID;
+
+static void transform_delkey(void *sst)
+{
+    struct transform_inst *ti=sst;
+
+    FILLZERO(ti->cryptkey);
+    FILLZERO(ti->mackey);
+    ti->keyed=False;
+}
+
+static transform_apply_return transform_forward(void *sst,
+            struct buffer_if *buf, const char **errmsg)
+{
+    struct transform_inst *ti=sst;
+    uint8_t *padp;
+    int padlen;
+    uint8_t iv[16];
+    uint8_t macplain[16];
+    uint8_t macacc[16];
+    uint8_t *p, *n;
+    int i;
+
+    KEYED_CHECK;
+
+    /* Sequence number */
+    buf_prepend_uint32(buf,ti->sendseq);
+    ti->sendseq++;
+
+    /* PKCS5, stolen from IWJ */
+                                    /* eg with blocksize=4 mask=3 mask+2=5   */
+                                    /* msgsize    20    21    22    23   24  */
+    padlen= PKCS5_MASK-buf->size;   /*           -17   -18   -19   -16  -17  */
+    padlen &= PKCS5_MASK;           /*             3     2     1     0    3  */
+    padlen++;                       /*             4     3     2     1    4  */
+
+    padp=buf_append(buf,padlen);
+    memset(padp,padlen,padlen);
+
+    /* Serpent-CBCMAC. We expand the IV from 32-bit to 128-bit using
+       one encryption. Then we do the MAC and append the result. We don't
+       bother sending the IV - it's the same each time. (If we wanted to send
+       it we've have to add 16 bytes to each message, not 4, so that the
+       message stays a multiple of 16 bytes long.) */
+    FILLZERO(iv);
+    put_uint32(iv, ti->maciv);
+    serpentbe_encrypt(&ti->mackey,iv,macacc);
+
+    /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted
+       block encrypted once again. */
+    for (n=buf->start; n<buf->start+buf->size; n+=16)
+    {
+       for (i = 0; i < 16; i++)
+           macplain[i] = macacc[i] ^ n[i];
+       serpentbe_encrypt(&ti->mackey,macplain,macacc);
+    }
+    serpentbe_encrypt(&ti->mackey,macacc,macacc);
+    BUF_ADD_BYTES(append,buf,macacc,16);
+
+    /* Serpent-CBC. We expand the ID as for CBCMAC, do the encryption,
+       and prepend the IV before increasing it. */
+    FILLZERO(iv);
+    put_uint32(iv, ti->cryptiv);
+    serpentbe_encrypt(&ti->cryptkey,iv,iv);
+
+    /* CBC: each block is XORed with the previous encrypted block (or the IV)
+       before being encrypted. */
+    p=iv;
+
+    for (n=buf->start; n<buf->start+buf->size; n+=16)
+    {
+       for (i = 0; i < 16; i++)
+           n[i] ^= p[i];
+       serpentbe_encrypt(&ti->cryptkey,n,n);
+       p=n;
+    }
+
+    buf_prepend_uint32(buf,ti->cryptiv);
+    ti->cryptiv++;
+    return 0;
+}
+
+static transform_apply_return transform_reverse(void *sst,
+                struct buffer_if *buf, const char **errmsg)
+{
+    struct transform_inst *ti=sst;
+    uint8_t *padp;
+    int padlen;
+    int i;
+    uint32_t seqnum;
+    uint8_t iv[16];
+    uint8_t pct[16];
+    uint8_t macplain[16];
+    uint8_t macacc[16];
+    uint8_t *n;
+    uint8_t *macexpected;
+
+    KEYED_CHECK;
+
+    if (buf->size < 4 + 16 + 16) {
+       *errmsg="msg too short";
+       return transform_apply_err;
+    }
+
+    /* CBC */
+    FILLZERO(iv);
+    {
+       uint32_t ivword = buf_unprepend_uint32(buf);
+       put_uint32(iv, ivword);
+    }
+    /* Assert bufsize is multiple of blocksize */
+    if (buf->size&0xf) {
+       *errmsg="msg not multiple of cipher blocksize";
+       return transform_apply_err;
+    }
+    serpentbe_encrypt(&ti->cryptkey,iv,iv);
+    for (n=buf->start; n<buf->start+buf->size; n+=16)
+    {
+       for (i = 0; i < 16; i++)
+           pct[i] = n[i];
+       serpentbe_decrypt(&ti->cryptkey,n,n);
+       for (i = 0; i < 16; i++)
+           n[i] ^= iv[i];
+       COPY_OBJ(iv, pct);
+    }
+
+    /* CBCMAC */
+    macexpected=buf_unappend(buf,16);
+    FILLZERO(iv);
+    put_uint32(iv, ti->maciv);
+    serpentbe_encrypt(&ti->mackey,iv,macacc);
+
+    /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted
+       block encrypted once again. */
+    for (n=buf->start; n<buf->start+buf->size; n+=16)
+    {
+       for (i = 0; i < 16; i++)
+           macplain[i] = macacc[i] ^ n[i];
+       serpentbe_encrypt(&ti->mackey,macplain,macacc);
+    }
+    serpentbe_encrypt(&ti->mackey,macacc,macacc);
+    if (!consttime_memeq(macexpected,macacc,16)) {
+       *errmsg="invalid MAC";
+       return transform_apply_err;
+    }
+
+    /* PKCS5, stolen from IWJ */
+
+    padp=buf_unappend(buf,1);
+    padlen=*padp;
+    if (!padlen || (padlen > PKCS5_MASK+1)) {
+       *errmsg="pkcs5: invalid length";
+       return transform_apply_err;
+    }
+
+    buf_unappend(buf,padlen-1);
+
+    /* Sequence number must be within max_skew of lastrecvseq; lastrecvseq
+       is only allowed to increase. */
+    seqnum=buf_unprepend_uint32(buf);
+    SEQNUM_CHECK(seqnum, &ti->p);
+    
+    return 0;
+}
+
+TRANSFORM_DESTROY;
+
+static struct transform_inst_if *transform_create(void *sst)
+{
+    struct transform *st=sst;
+
+    TRANSFORM_CREATE_CORE;
+
+    ti->p=st->p;
+
+    return &ti->ops;
+}
+
+static list_t *transform_apply(closure_t *self, struct cloc loc,
+                              dict_t *context, list_t *args)
+{
+    struct transform *st;
+    item_t *item;
+    dict_t *dict;
+
+    NEW(st);
+    st->cl.description="serpent-cbc256";
+    st->cl.type=CL_TRANSFORM;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    update_max_start_pad(&transform_max_start_pad, 28);
+        /* 4byte seqnum, 16byte pad, 4byte MACIV, 4byte IV */
+
+    /* We need 256*2 bits for serpent keys, 32 bits for CBC-IV and 32 bits
+       for CBCMAC-IV, and 32 bits for init sequence number */
+    st->ops.keylen=REQUIRED_KEYLEN;
+    st->ops.create=transform_create;
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"serpent256-cbc","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+
+    SEQNUM_PARAMS_INIT(dict,&st->p,"serpent-cbc256",loc);
+
+    SET_CAPAB_BIT(CAPAB_BIT_SERPENT256CBC);
+
+    return new_closure(&st->cl);
+}
+
+void transform_cbcmac_module(dict_t *dict)
+{
+    struct keyInstance k;
+    uint8_t data[32];
+    uint8_t plaintext[16];
+    uint8_t ciphertext[16];
+
+    /*
+     * Serpent self-test.
+     * 
+     * This test pattern was taken directly from the Serpent test
+     * vectors, which results in a big-endian Serpent which is not
+     * compatible with other implementations.
+     */
+
+    /* Serpent self-test */
+    memcpy(data,
+           "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff"
+           "\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00",
+           32);
+    serpentbe_makekey(&k,256,data);
+
+    memcpy(plaintext,
+           "\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10",
+           16);
+    serpentbe_encrypt(&k,plaintext,ciphertext);
+
+    if (memcmp(ciphertext, "\xca\x7f\xa1\x93\xe3\xeb\x9e\x99"
+               "\xbd\x87\xe3\xaf\x3c\x9a\xdf\x93", 16)) {
+       fatal("transform_module: serpent failed self-test (encrypt)");
+    }
+    serpentbe_decrypt(&k,ciphertext,plaintext);
+    if (memcmp(plaintext, "\x01\x23\x45\x67\x89\xab\xcd\xef"
+               "\xfe\xdc\xba\x98\x76\x54\x32\x10", 16)) {
+       fatal("transform_module: serpent failed self-test (decrypt)");
+    }
+
+    add_closure(dict,"serpent256-cbc",transform_apply);
+
+#ifdef TEST_WHOLE_TRANSFORM
+    {
+       struct transform *tr;
+       void *ti;
+       struct buffer_if buf;
+       const char text[] = "This is a piece of test text.";
+       char keymaterial[76] =
+           "Seventy-six bytes i"
+           "n four rows of 19; "
+           "this looks almost l"
+           "ike a poem but not.";
+       const char *errmsg;
+       int i;
+
+       NEW(tr);
+       tr->max_seq_skew = 20;
+       ti = transform_create(tr);
+
+       transform_setkey(ti, keymaterial, 76);
+
+        buf.base = malloc(4096);
+       buffer_init(&buf, 2048);
+       BUF_ADD_OBJ(append, buf, text, sizeof(text));
+       if (transform_forward(ti, &buf, &errmsg)) {
+           fatal("transform_forward test: %s", errmsg);
+       }
+       printf("transformed text is:\n");
+       for (i = 0; i < buf.size; i++)
+           printf("%02x%c", buf.start[i],
+                  (i%16==15 || i==buf.size-1 ? '\n' : ' '));
+       if (transform_reverse(ti, &buf, &errmsg)) {
+           fatal("transform_reverse test: %s", errmsg);
+       }
+       printf("transform reversal worked OK\n");
+    }
+#endif
+}
diff --git a/transform-common.h b/transform-common.h
new file mode 100644 (file)
index 0000000..4351ce8
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef TRANSFORM_COMMON_H
+#define TRANSFORM_COMMON_H
+
+#include "magic.h"
+
+#define KEYED_CHECK do{                                \
+       if (!ti->keyed) {                       \
+           *errmsg="transform unkeyed";        \
+           return transform_apply_err;         \
+       }                                       \
+    }while(0)
+
+#define RECVBITMAP_SIZE 32
+typedef uint32_t recvbitmap_type;
+
+#define SEQNUM_CHECK(seqnum, p) do{                            \
+       uint32_t skew=seqnum-ti->lastrecvseq;                   \
+       if (skew<0x8fffffff) {                                  \
+           /* Ok */                                            \
+           ti->lastrecvseq=seqnum;                             \
+           if (skew < RECVBITMAP_SIZE)                         \
+                ti->recvbitmap <<= skew;                       \
+            else                                               \
+                ti->recvbitmap=0;                              \
+            skew=0;                                            \
+       } else if ((0-skew)<(p)->max_seq_skew) {                \
+           /* Ok */                                            \
+       } else {                                                \
+           /* Too much skew */                                 \
+           *errmsg="seqnum: too much skew";                    \
+           return transform_apply_seqrange;                    \
+       }                                                       \
+       if ((p)->dedupe) {                                      \
+           recvbitmap_type recvbit=(uint32_t)1 << skew;        \
+           if (ti->recvbitmap & recvbit) {                     \
+               *errmsg="seqnum: duplicate";                    \
+               return transform_apply_seqdupe;                 \
+           }                                                   \
+           ti->recvbitmap |= recvbit;                          \
+       }                                                       \
+    }while(0)
+
+#define SEQNUM_KEYED_FIELDS                                            \
+    uint32_t sendseq;                                                  \
+    uint32_t lastrecvseq;                                              \
+    recvbitmap_type recvbitmap; /* 1<<0 is lastrecvseq (i.e., most recent) */ \
+    bool_t keyed
+
+#define SEQNUM_KEYED_INIT(initlastrecvseq,initsendseq) \
+    (ti->lastrecvseq=(initlastrecvseq),                        \
+     ti->sendseq=(initsendseq),                                \
+     ti->recvbitmap=0,                                 \
+     ti->keyed=True)
+
+#define TRANSFORM_VALID                                \
+    static bool_t transform_valid(void *sst)   \
+    {                                          \
+       struct transform_inst *ti=sst;          \
+                                               \
+       return ti->keyed;                       \
+    }
+
+#define TRANSFORM_DESTROY                              \
+    static void transform_destroy(void *sst)           \
+    {                                                  \
+       struct transform_inst *st=sst;                  \
+                                                       \
+       FILLZERO(*st); /* Destroy key material */       \
+       free(st);                                       \
+    }
+
+#define SET_CAPAB_BIT(def) do{                                         \
+        st->ops.capab_bit=dict_read_number(dict, "capab-num",          \
+                                     False, "transform", loc, (def));  \
+        if (st->ops.capab_bit > CAPAB_BIT_MAX)                         \
+           cfgfatal(loc,"transform","capab-num out of range 0..%d\n",  \
+                    CAPAB_BIT_MAX);                                    \
+    }while(0)
+
+#define TRANSFORM_CREATE_CORE                          \
+       struct transform_inst *ti;                      \
+       NEW(ti);                                        \
+       /* mlock XXX */                                 \
+       ti->ops.st=ti;                                  \
+       ti->ops.setkey=transform_setkey;                \
+       ti->ops.valid=transform_valid;                  \
+       ti->ops.delkey=transform_delkey;                \
+       ti->ops.forwards=transform_forward;             \
+       ti->ops.reverse=transform_reverse;              \
+       ti->ops.destroy=transform_destroy;              \
+       ti->keyed=False;
+
+#define SEQNUM_PARAMS_FIELDS                   \
+    uint32_t max_seq_skew;                     \
+    bool_t dedupe;
+
+#define SEQNUM_PARAMS_INIT(dict,p,desc,loc)                            \
+    (p)->max_seq_skew=dict_read_number((dict), "max-sequence-skew",    \
+                                       False, (desc), (loc), 10);      \
+    bool_t can_dedupe=(p)->max_seq_skew < RECVBITMAP_SIZE;             \
+    (p)->dedupe=dict_read_bool((dict), "dedupe",                       \
+                              False,(desc),(loc), can_dedupe);         \
+    if ((p)->dedupe && !can_dedupe)                                    \
+       cfgfatal(loc,"transform",                                       \
+                 "cannot dedupe with max-sequence-skew>=32");          \
+    else (void)0
+
+#endif /*TRANSFORM_COMMON_H*/
diff --git a/transform-eax.c b/transform-eax.c
new file mode 100644 (file)
index 0000000..04cd0e6
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * eax-transform.c: EAX-Serpent bulk data transformation
+ */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+/*
+ * We use EAX with the following parameters:
+ *
+ *   Plaintext:
+ *      Concatenation of:
+ *        Data packet as supplied to us
+ *        Zero or more zero bytes ignored by receiver } padding
+ *        One byte padding length                     }
+ *      This is a bit like PKCS#5.  It helps disguise message lengths.
+ *      It also provides a further room for future expansion.  When
+ *      transmitting we pad the message to the next multiple of
+ *      a configurable rounding factor, 16 bytes by default.
+ *
+ *   Transmitted message:
+ *      Concatenation of:
+ *        EAX ciphertext
+ *        32-bit sequence number (initially zero)
+ *      The sequence number allows us to discard far-too-old
+ *      packets.
+ *
+ *   Nonce:
+ *      Concatenation of:
+ *        32-bit sequence number (big endian)
+ *               initial value comes from SHA-512 hash (see below)
+ *        1 byte: 0x01 if sender has setup priority, 0x00 if it doesn't
+ *               (ie, the direction of data flow)
+ *
+ *   Header: None
+ *
+ *   Tag length:
+ *      16 bytes (128 bits) by default
+ *
+ *   Key:
+ *      The first 32 bytes of the SHA-512 hash of the shared secret
+ *      from the DH key exchange (the latter being expressed as
+ *      the shortest possible big-endian octet string).
+ *
+ * The bytes [32,40> of the hash of the shared secret are used for
+ * initial sequence numbers: [32,36> for those sent by the end without
+ * setup priority, [36,40> for those for the other end.
+ *
+ */
+
+#include "secnet.h"
+#include "unaligned.h"
+#include "util.h"
+#include "serpent.h"
+#include "sha512.h"
+#include "transform-common.h"
+#include "hexdebug.h"
+
+#define BLOCK_SIZE 16
+#define SEQLEN 4
+
+struct transform_params {
+    SEQNUM_PARAMS_FIELDS;
+    uint32_t tag_length, padding_mask;
+};
+
+struct transform {
+    closure_t cl;
+    struct transform_if ops;
+    struct transform_params p;
+};
+
+struct transform_inst {
+    struct transform_inst_if ops;
+    struct transform_params p;
+    /* remaining valid iff keyed */
+    unsigned direction:1;
+    SEQNUM_KEYED_FIELDS;
+    struct keyInstance key;
+    uint8_t info_b[BLOCK_SIZE], info_p[BLOCK_SIZE];
+};
+
+static void block_encrypt(struct transform_inst *transform_inst,
+                         uint8_t dst[BLOCK_SIZE],
+                         const uint8_t src[BLOCK_SIZE])
+{
+    serpent_encrypt(&transform_inst->key, src, dst);
+}
+
+#define INFO                    struct transform_inst *transform_inst
+#define I                       transform_inst
+#define EAX_ENTRYPOINT_DECL     static
+#define BLOCK_ENCRYPT(dst,src)  block_encrypt(transform_inst,dst,src)
+#define INFO_B                  (transform_inst->info_b)
+#define INFO_P                  (transform_inst->info_p)
+
+#include "eax.c"
+
+#if 0
+
+#define TEAX_DEBUG(ary,sz) teax_debug(__func__,__LINE__,#ary,#sz,ary,sz)
+static void teax_debug(const char *func, int line,
+                      const char *aryp, const char *szp,
+                      const void *ary, size_t sz)
+{
+    fprintf(stderr,"TEAX %s:%-3d %10s %15s : ", func,line,aryp,szp);
+    hexdebug(stderr,ary,sz);
+    fprintf(stderr,"\n");
+}
+
+#else
+
+#define TEAX_DEBUG(ary,sz) /* empty */
+
+#endif
+
+static bool_t transform_setkey(void *sst, uint8_t *key, int32_t keylen,
+                              bool_t direction)
+{
+    struct transform_inst *ti=sst;
+    struct sha512_ctx hash_ctx;
+    uint8_t hash_out[64];
+
+    TEAX_DEBUG(key,keylen);
+
+    sha512_init_ctx(&hash_ctx);
+    sha512_process_bytes(key, keylen, &hash_ctx);
+    sha512_finish_ctx(&hash_ctx, hash_out);
+
+    TEAX_DEBUG(hash_out,32);
+    TEAX_DEBUG(hash_out+32,8);
+
+    ti->direction=direction;
+    serpent_makekey(&ti->key, 32*8, hash_out);
+    eax_setup(ti);
+    SEQNUM_KEYED_INIT(get_uint32(hash_out+32+!direction*4),
+                     get_uint32(hash_out+32+direction*4));
+
+    return True;
+}
+
+TRANSFORM_VALID;
+
+TRANSFORM_DESTROY;
+
+static void transform_delkey(void *sst)
+{
+    struct transform_inst *ti=sst;
+
+    FILLZERO(ti->key);
+    FILLZERO(ti->info_b);
+    FILLZERO(ti->info_p);
+    ti->keyed=False;
+}
+
+static transform_apply_return transform_forward(void *sst,
+        struct buffer_if *buf, const char **errmsg)
+{
+    struct transform_inst *ti=sst;
+
+    KEYED_CHECK;
+    
+    size_t padlen = ti->p.padding_mask - buf->size;
+    padlen &= ti->p.padding_mask;
+    padlen++;
+
+    uint8_t *pad = buf_append(buf,padlen);
+    memset(pad, 0, padlen-1);
+    pad[padlen-1] = padlen;
+
+    uint8_t nonce[SEQLEN+1];
+    put_uint32(nonce,ti->sendseq);
+    nonce[SEQLEN] = ti->direction;
+
+    TEAX_DEBUG(nonce,sizeof(nonce));
+    TEAX_DEBUG(buf->start,buf->size);
+
+    assert(buf_append(buf,ti->p.tag_length));
+    eax_encrypt(ti, nonce,sizeof(nonce), 0,0,
+               buf->start,buf->size-ti->p.tag_length,
+               ti->p.tag_length, buf->start);
+
+    TEAX_DEBUG(buf->start,buf->size);
+
+    BUF_ADD_BYTES(append,buf,nonce,SEQLEN);
+
+    TEAX_DEBUG(nonce,SEQLEN);
+
+    ti->sendseq++;
+
+    return 0;
+}
+
+static transform_apply_return transform_reverse(void *sst,
+        struct buffer_if *buf, const char **errmsg)
+{
+    struct transform_inst *ti=sst;
+
+    KEYED_CHECK;
+
+    TEAX_DEBUG(buf->start,buf->size);
+
+    uint8_t nonce[SEQLEN+1];
+    const uint8_t *seqp = buf_unappend(buf,SEQLEN);
+    if (!seqp) goto too_short;
+
+    TEAX_DEBUG(seqp,SEQLEN);
+
+    uint32_t seqnum = get_uint32(seqp);
+
+    memcpy(nonce,seqp,SEQLEN);
+    nonce[4] = !ti->direction;
+
+    TEAX_DEBUG(nonce,sizeof(nonce));
+    TEAX_DEBUG(buf->start,buf->size);
+
+    bool_t ok = eax_decrypt(ti, nonce,sizeof(nonce), 0,0, buf->start,buf->size,
+                           ti->p.tag_length, buf->start);
+    if (!ok) {
+       TEAX_DEBUG(0,0);
+       *errmsg="EAX decryption failed";
+       return transform_apply_err;
+    }
+    assert(buf->size >= (int)ti->p.tag_length);
+    buf->size -= ti->p.tag_length;
+
+    TEAX_DEBUG(buf->start,buf->size);
+
+    const uint8_t *padp = buf_unappend(buf,1);
+    if (!padp) goto too_short;
+
+    TEAX_DEBUG(padp,1);
+
+    size_t padlen = *padp;
+    if (!buf_unappend(buf,padlen-1)) goto too_short;
+
+    SEQNUM_CHECK(seqnum, &ti->p);
+
+    TEAX_DEBUG(buf->start,buf->size);
+
+    return 0;
+
+ too_short:
+    *errmsg="ciphertext or plaintext too short";
+    return transform_apply_err;
+}
+
+static struct transform_inst_if *transform_create(void *sst)
+{
+    struct transform *st=sst;
+
+    TRANSFORM_CREATE_CORE;
+
+    ti->p=st->p;
+
+    return &ti->ops;
+}
+
+static list_t *transform_apply(closure_t *self, struct cloc loc,
+                              dict_t *context, list_t *args)
+{
+    struct transform *st;
+    item_t *item;
+    dict_t *dict;
+
+    NEW(st);
+    st->cl.description="eax-serpent";
+    st->cl.type=CL_TRANSFORM;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"eax-serpent","parameter must be a dictionary\n");
+    dict=item->data.dict;
+
+    SET_CAPAB_BIT(CAPAB_BIT_EAXSERPENT);
+
+    SEQNUM_PARAMS_INIT(dict,&st->p,"eax-serpent",loc);
+
+    st->p.tag_length=dict_read_number(dict, "tag-length-bytes",
+                                     False, "eax-serpent", loc, 128/8);
+    if (st->p.tag_length<1 || st->p.tag_length>BLOCK_SIZE)
+       cfgfatal(loc,"eax-serpent","tag-length-bytes out of range 0..%d\n",
+                BLOCK_SIZE);
+
+    uint32_t padding_round=dict_read_number(dict, "padding-rounding",
+                                           False, "eax-serpent", loc, 16);
+    if (padding_round & (padding_round-1))
+       cfgfatal(loc,"eax-serpent","padding-round not a power of two\n");
+    if (padding_round > 255)
+       cfgfatal(loc,"eax-serpent","padding-round must be 1..128\n");
+    if (padding_round == 0)
+       padding_round = 1;
+    st->p.padding_mask = padding_round-1;
+
+    update_max_start_pad(&transform_max_start_pad, 0);
+
+    st->ops.keylen=0;
+    st->ops.create=transform_create;
+
+    return new_closure(&st->cl);
+}
+
+void transform_eax_module(dict_t *dict)
+{
+    add_closure(dict,"eax-serpent",transform_apply);
+}
diff --git a/tun.c b/tun.c
new file mode 100644 (file)
index 0000000..3ba62b3
--- /dev/null
+++ b/tun.c
@@ -0,0 +1,650 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include "util.h"
+#include "netlink.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#ifdef HAVE_LINUX_IF_TUN_H
+#include <linux/if_tun.h>
+#define LINUX_TUN_SUPPORTED
+#endif
+#endif
+
+#ifdef HAVE_NET_ROUTE_H
+#include <net/route.h>
+#endif
+
+#if defined(HAVE_STROPTS_H) && defined(HAVE_SYS_SOCKIO_H) && \
+defined(HAVE_NET_IF_TUN_H)
+#define HAVE_TUN_STREAMS
+#endif
+
+#ifdef HAVE_TUN_STREAMS
+#include <stropts.h>
+#include <sys/sockio.h>
+#include <net/if_tun.h>
+#endif
+
+#define TUN_FLAVOUR_GUESS   0
+#define TUN_FLAVOUR_BSD     1
+#define TUN_FLAVOUR_LINUX   2
+#define TUN_FLAVOUR_STREAMS 3
+
+static struct flagstr flavours[]={
+    {"guess", TUN_FLAVOUR_GUESS},
+    {"bsd", TUN_FLAVOUR_BSD},
+    {"BSD", TUN_FLAVOUR_BSD},
+    {"linux", TUN_FLAVOUR_LINUX},
+    {"streams", TUN_FLAVOUR_STREAMS},
+    {"STREAMS", TUN_FLAVOUR_STREAMS},
+    {NULL, 0}
+};
+
+#define TUN_CONFIG_GUESS      0
+#define TUN_CONFIG_IOCTL      1
+#define TUN_CONFIG_BSD        2
+#define TUN_CONFIG_LINUX      3
+#define TUN_CONFIG_SOLARIS25  4
+
+static struct flagstr config_types[]={
+    {"guess", TUN_CONFIG_GUESS},
+    {"ioctl", TUN_CONFIG_IOCTL},
+    {"bsd", TUN_CONFIG_BSD},
+    {"BSD", TUN_CONFIG_BSD},
+    {"linux", TUN_CONFIG_LINUX},
+    {"solaris-2.5", TUN_CONFIG_SOLARIS25},
+    {NULL, 0}
+};
+
+/* Connection to the kernel through the universal TUN/TAP driver */
+
+struct tun {
+    struct netlink nl;
+    int fd;
+    cstring_t device_path;
+    cstring_t ip_path;
+    string_t interface_name;
+    cstring_t ifconfig_path;
+    uint32_t ifconfig_type;
+    cstring_t route_path;
+    uint32_t route_type;
+    uint32_t tun_flavour;
+    bool_t search_for_if; /* Applies to tun-BSD only */
+    struct buffer_if *buff; /* We receive packets into here
+                              and send them to the netlink code. */
+    netlink_deliver_fn *netlink_to_tunnel;
+};
+
+static cstring_t tun_flavour_str(uint32_t flavour)
+{
+    switch (flavour) {
+    case TUN_FLAVOUR_GUESS: return "guess";
+    case TUN_FLAVOUR_BSD: return "BSD";
+    case TUN_FLAVOUR_LINUX: return "linux";
+    case TUN_FLAVOUR_STREAMS: return "STREAMS";
+    default: return "unknown";
+    }
+}
+
+static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                         int *timeout_io)
+{
+    struct tun *st=sst;
+    BEFOREPOLL_WANT_FDS(1);
+    fds[0].fd=st->fd;
+    fds[0].events=POLLIN;
+    return 0;
+}
+
+static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds)
+{
+    struct tun *st=sst;
+    int l;
+
+    if (nfds==0) return;
+    if (fds[0].revents&POLLERR) {
+       printf("tun_afterpoll: hup!\n");
+    }
+    if (fds[0].revents&POLLIN) {
+       BUF_ALLOC(st->buff,"tun_afterpoll");
+       buffer_init(st->buff,calculate_max_start_pad());
+       l=read(st->fd, st->buff->start, buf_remaining_space(st->buff));
+       if (l<0) {
+           if (errno==EINTR || iswouldblock(errno)) return;
+           fatal_perror("tun_afterpoll: read()");
+       }
+       if (l==0) {
+           fatal("tun_afterpoll: read()=0; device gone away?");
+       }
+       if (l>0) {
+           st->buff->size=l;
+           st->netlink_to_tunnel(&st->nl,st->buff);
+           BUF_ASSERT_FREE(st->buff);
+       }
+    }
+}
+
+static void tun_deliver_to_kernel(void *sst, struct buffer_if *buf)
+{
+    struct tun *st=sst;
+    ssize_t rc;
+
+    BUF_ASSERT_USED(buf);
+    
+    /* Log errors, so we can tell what's going on, but only once a
+       minute, so we don't flood the logs.  Short writes count as
+       errors. */
+    rc = write(st->fd,buf->start,buf->size);
+    if(rc != buf->size) {
+       static struct timeval last_report;
+       if(tv_now_global.tv_sec >= last_report.tv_sec + 60) {
+           if(rc < 0)
+               Message(M_WARNING,
+                       "failed to deliver packet to tun device: %s\n",
+                       strerror(errno));
+           else
+               Message(M_WARNING,
+                       "truncated packet delivered to tun device\n");
+           last_report = tv_now_global;
+       }
+    }
+    BUF_FREE(buf);
+}
+
+static bool_t tun_set_route(void *sst, struct netlink_client *routes)
+{
+    struct tun *st=sst;
+    string_t network, mask, secnetaddr;
+    struct subnet_list *nets;
+    int32_t i;
+    int fd=-1;
+    bool_t up;
+
+    if (routes->options & OPT_SOFTROUTE)
+        up = routes->up;
+    else
+        up = routes->link_quality > LINK_QUALITY_UNUSED;
+
+    if (up == routes->kup) return False;
+    if (st->route_type==TUN_CONFIG_IOCTL) {
+       if (st->tun_flavour==TUN_FLAVOUR_STREAMS) {
+           fd=open(st->ip_path,O_RDWR);
+           if (fd<0) {
+               fatal_perror("tun_set_route: can't open %s",st->ip_path);
+           }
+       } else {
+           fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+           if (fd<0) {
+               fatal_perror("tun_set_route: socket()");
+           }
+       }
+    }
+    nets=routes->subnets;
+    secnetaddr=ipaddr_to_string(st->nl.secnet_address);
+    for (i=0; i<nets->entries; i++) {
+       network=ipaddr_to_string(nets->list[i].prefix);
+       mask=ipaddr_to_string(nets->list[i].mask);
+       Message(M_INFO,"%s: %s route %s/%d %s kernel routing table\n",
+               st->nl.name,up?"adding":"deleting",network,
+               nets->list[i].len,up?"to":"from");
+       switch (st->route_type) {
+       case TUN_CONFIG_LINUX:
+           sys_cmd(st->route_path,"route",up?"add":"del",
+                   "-net",network,"netmask",mask,
+                   "gw",secnetaddr,(char *)0);
+           break;
+       case TUN_CONFIG_BSD:
+           sys_cmd(st->route_path,"route",up?"add":"del",
+                   "-net",network,secnetaddr,mask,(char *)0);
+           break;
+       case TUN_CONFIG_SOLARIS25:
+           sys_cmd(st->route_path,"route",up?"add":"del",
+                   network,secnetaddr,(char *)0);
+           break;
+       case TUN_CONFIG_IOCTL:
+       {
+         /* darwin rtentry has a different format, use /sbin/route instead */
+#if HAVE_NET_ROUTE_H && ! __APPLE__
+           struct rtentry rt;
+           struct sockaddr_in *sa;
+           int action;
+           
+           FILLZERO(rt);
+           sa=(struct sockaddr_in *)&rt.rt_dst;
+           sa->sin_family=AF_INET;
+           sa->sin_addr.s_addr=htonl(nets->list[i].prefix);
+           sa=(struct sockaddr_in *)&rt.rt_genmask;
+           sa->sin_family=AF_INET;
+           sa->sin_addr.s_addr=htonl(nets->list[i].mask);
+           sa=(struct sockaddr_in *)&rt.rt_gateway;
+           sa->sin_family=AF_INET;
+           sa->sin_addr.s_addr=htonl(st->nl.secnet_address);
+           rt.rt_flags=RTF_UP|RTF_GATEWAY;
+           action=up?SIOCADDRT:SIOCDELRT;
+           if (ioctl(fd,action,&rt)<0) {
+               fatal_perror("tun_set_route: ioctl()");
+           }
+#else
+           fatal("tun_set_route: ioctl method not supported");
+#endif
+       }
+       break;
+       default:
+           fatal("tun_set_route: unsupported route command type");
+           break;
+       }
+    }
+    if (fd >= 0) {
+       close(fd);
+    }
+    routes->kup=up;
+    return True;
+}
+
+static void tun_phase_hook(void *sst, uint32_t newphase)
+{
+    struct tun *st=sst;
+    string_t hostaddr,secnetaddr;
+    char mtu[6];
+    struct netlink_client *r;
+
+    if (st->tun_flavour==TUN_FLAVOUR_BSD) {
+       if (st->search_for_if) {
+           string_t dname;
+           int i;
+
+           dname=safe_malloc(strlen(st->device_path)+4,"tun_old_apply");
+           st->interface_name=safe_malloc(8,"tun_phase_hook");
+       
+           for (i=0; i<255; i++) {
+               sprintf(dname,"%s%d",st->device_path,i);
+               if ((st->fd=open(dname,O_RDWR))>0) {
+                   sprintf(st->interface_name,"tun%d",i);
+                   Message(M_INFO,"%s: allocated network interface %s "
+                           "through %s\n",st->nl.name,st->interface_name,
+                           dname);
+                   break;
+               }
+           }
+           if (st->fd==-1) {
+               fatal("%s: unable to open any TUN device (%s...)",
+                     st->nl.name,st->device_path);
+           }
+       } else {
+           st->fd=open(st->device_path,O_RDWR);
+           if (st->fd==-1) {
+               fatal_perror("%s: unable to open TUN device file %s",
+                            st->nl.name,st->device_path);
+           }
+       }
+    } else if (st->tun_flavour==TUN_FLAVOUR_LINUX) {
+#ifdef LINUX_TUN_SUPPORTED
+       struct ifreq ifr;
+
+       /* New TUN interface: open the device, then do ioctl TUNSETIFF
+          to set or find out the network interface name. */
+       st->fd=open(st->device_path,O_RDWR);
+       if (st->fd==-1) {
+           fatal_perror("%s: can't open device file %s",st->nl.name,
+                        st->device_path);
+       }
+       FILLZERO(ifr);
+       ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Just send/receive IP packets,
+                                               no extra headers */
+       if (st->interface_name)
+           strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+       if (ioctl(st->fd,TUNSETIFF,&ifr)<0) {
+           fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name);
+       }
+       if (!st->interface_name) {
+           st->interface_name=safe_malloc(strlen(ifr.ifr_name)+1,"tun_apply");
+           strcpy(st->interface_name,ifr.ifr_name);
+           Message(M_INFO,"%s: allocated network interface %s\n",st->nl.name,
+                   st->interface_name);
+       }
+#else
+       fatal("tun_phase_hook: TUN_FLAVOUR_LINUX unexpected");
+#endif /* LINUX_TUN_SUPPORTED */
+    } else if (st->tun_flavour==TUN_FLAVOUR_STREAMS) {
+#ifdef HAVE_TUN_STREAMS
+       int tun_fd, if_fd, ppa=-1, ip_fd;
+
+       if ((ip_fd=open(st->ip_path, O_RDWR)) < 0) {
+           fatal_perror("%s: can't open %s",st->nl.name,st->ip_path);
+       }
+       if ((tun_fd=open(st->device_path,O_RDWR)) < 0) {
+           fatal_perror("%s: can't open %s",st->nl.name,st->device_path);
+       }
+       if ((ppa=ioctl(tun_fd,TUNNEWPPA,ppa)) < 0) {
+           fatal_perror("%s: can't assign new interface");
+       }
+       if ((if_fd=open(st->device_path,O_RDWR)) < 0) {
+           fatal_perror("%s: can't open %s (2)",st->nl.name,st->device_path);
+       }
+       if (ioctl(if_fd,I_PUSH,"ip") < 0) {
+           fatal_perror("%s: can't push IP module",st->nl.name);
+       }
+       if (ioctl(if_fd,IF_UNITSEL,(char *)&ppa) < 0) {
+           fatal_perror("%s: can't set ppa %d",st->nl.name,ppa);
+       }
+       if (ioctl(ip_fd, I_LINK, if_fd) < 0) {
+           fatal_perror("%s: can't link TUN device to IP",st->nl.name);
+       }
+       st->interface_name=safe_malloc(10,"tun_apply");
+       sprintf(st->interface_name,"tun%d",ppa);
+       st->fd=tun_fd;
+       setcloexec(if_ifd);
+       setcloexec(ip_ifd);
+#else
+       fatal("tun_phase_hook: TUN_FLAVOUR_STREAMS unexpected");
+#endif /* HAVE_TUN_STREAMS */
+    } else {
+       fatal("tun_phase_hook: unknown flavour of TUN");
+    }
+    /* All the networks we'll be using have been registered. Invoke ifconfig
+       to set the TUN device's address, and route to add routes to all
+       our networks. */
+
+    setcloexec(st->fd);
+    setnonblock(st->fd);
+
+    hostaddr=ipaddr_to_string(st->nl.local_address);
+    secnetaddr=ipaddr_to_string(st->nl.secnet_address);
+    snprintf(mtu,sizeof(mtu),"%d",st->nl.mtu);
+    mtu[5]=0;
+
+    switch (st->ifconfig_type) {
+    case TUN_CONFIG_LINUX:
+       sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name,
+               hostaddr,"netmask","255.255.255.255","-broadcast",
+               "-multicast",
+               "pointopoint",secnetaddr,"mtu",mtu,"up",(char *)0);
+       break;
+    case TUN_CONFIG_BSD:
+       sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name,
+               hostaddr,"netmask","255.255.255.255",
+               secnetaddr,"mtu",mtu,"up",(char *)0);
+       break;
+    case TUN_CONFIG_SOLARIS25:
+       sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name,
+               hostaddr,secnetaddr,"mtu",mtu,"up",(char *)0);
+       break;
+    case TUN_CONFIG_IOCTL:
+#if HAVE_NET_IF_H && ! __APPLE__
+    {
+       int fd;
+       struct ifreq ifr;
+       struct sockaddr_in *sa;
+       fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+       /* Interface address */
+       strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+       sa=(struct sockaddr_in *)&ifr.ifr_addr;
+       FILLZERO(*sa);
+       sa->sin_family=AF_INET;
+       sa->sin_addr.s_addr=htonl(st->nl.local_address);
+       if (ioctl(fd,SIOCSIFADDR, &ifr)!=0) {
+           fatal_perror("tun_apply: SIOCSIFADDR");
+       }
+#ifdef SIOCSIFNETMASK
+       /* Netmask */
+       strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+       sa=(struct sockaddr_in *)&ifr.ifr_netmask;
+       FILLZERO(*sa);
+       sa->sin_family=AF_INET;
+       sa->sin_addr.s_addr=htonl(0xffffffff);
+       if (ioctl(fd,SIOCSIFNETMASK, &ifr)!=0) {
+           fatal_perror("tun_apply: SIOCSIFNETMASK");
+       }
+#endif
+       /* Destination address (point-to-point) */
+       strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+       sa=(struct sockaddr_in *)&ifr.ifr_dstaddr;
+       FILLZERO(*sa);
+       sa->sin_family=AF_INET;
+       sa->sin_addr.s_addr=htonl(st->nl.secnet_address);
+       if (ioctl(fd,SIOCSIFDSTADDR, &ifr)!=0) {
+           fatal_perror("tun_apply: SIOCSIFDSTADDR");
+       }
+       /* MTU */
+       strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+       ifr.ifr_mtu=st->nl.mtu;
+       if (ioctl(fd,SIOCSIFMTU, &ifr)!=0) {
+           fatal_perror("tun_apply: SIOCSIFMTU");
+       }
+       /* Flags */
+       strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+       ifr.ifr_flags=IFF_UP|IFF_POINTOPOINT|IFF_RUNNING|IFF_NOARP;
+       if (ioctl(fd,SIOCSIFFLAGS, &ifr)!=0) {
+           fatal_perror("tun_apply: SIOCSIFFLAGS");
+       }
+
+       close(fd);
+    }
+#else
+    fatal("tun_apply: ifconfig by ioctl() not supported");
+#endif /* HAVE_NET_IF_H */
+    break;
+    default:
+       fatal("tun_apply: unsupported ifconfig method");
+       break;
+    }
+       
+    for (r=st->nl.clients; r; r=r->next) {
+       tun_set_route(st,r);
+    }
+
+    add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->fd);
+
+    /* Register for poll() */
+    register_for_poll(st, tun_beforepoll, tun_afterpoll, st->nl.name);
+}
+
+static list_t *tun_create(closure_t *self, struct cloc loc, dict_t *context,
+                         list_t *args,uint32_t default_flavour)
+{
+    struct tun *st;
+    item_t *item;
+    dict_t *dict;
+    string_t flavour,type;
+
+    NEW(st);
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"tun","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+
+    st->netlink_to_tunnel=
+       netlink_init(&st->nl,st,loc,dict,
+                    "netlink-tun",tun_set_route,tun_deliver_to_kernel);
+
+    flavour=dict_read_string(dict,"flavour",False,"tun-netlink",loc);
+    if (flavour)
+       st->tun_flavour=string_to_word(flavour,loc,flavours,"tun-flavour");
+    else
+       st->tun_flavour=default_flavour;
+
+    st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+    st->ip_path=dict_read_string(dict,"ip-path",False,"tun-netlink",loc);
+    st->interface_name=dict_read_string(dict,"interface",False,
+                                       "tun-netlink",loc);
+    st->search_for_if=dict_read_bool(dict,"interface-search",False,
+                                    "tun-netlink",loc,st->device_path==NULL);
+
+    type=dict_read_string(dict,"ifconfig-type",False,"tun-netlink",loc);
+    if (type) st->ifconfig_type=string_to_word(type,loc,config_types,
+                                              "ifconfig-type");
+    else st->ifconfig_type=TUN_CONFIG_GUESS;
+    st->ifconfig_path=dict_read_string(dict,"ifconfig-path",False,
+                                      "tun-netlink",loc);
+
+    type=dict_read_string(dict,"route-type",False,"tun-netlink",loc);
+    if (type) st->route_type=string_to_word(type,loc,config_types,
+                                           "route-type");
+    else st->route_type=TUN_CONFIG_GUESS;
+    st->route_path=dict_read_string(dict,"route-path",False,"tun-netlink",loc);
+
+    st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
+
+    if (st->tun_flavour==TUN_FLAVOUR_GUESS) {
+       /* If we haven't been told what type of TUN we're using, take
+          a guess based on the system details. */
+       struct utsname u;
+       if (uname(&u)<0) {
+           fatal_perror("tun_create: uname");
+       }
+       if (strcmp(u.sysname,"Linux")==0) {
+           st->tun_flavour=TUN_FLAVOUR_LINUX;
+       } else if (strcmp(u.sysname,"SunOS")==0) {
+           st->tun_flavour=TUN_FLAVOUR_STREAMS;
+       } else if (strcmp(u.sysname,"FreeBSD")==0
+                  || strcmp(u.sysname,"Darwin")==0) {
+           st->tun_flavour=TUN_FLAVOUR_BSD;
+       }
+    }
+    if (st->tun_flavour==TUN_FLAVOUR_GUESS) {
+       cfgfatal(loc,"tun","cannot guess which type of TUN is in use; "
+                "specify the flavour explicitly\n");
+    }
+
+    if (st->ifconfig_type==TUN_CONFIG_GUESS) {
+       switch (st->tun_flavour) {
+       case TUN_FLAVOUR_LINUX:
+           st->ifconfig_type=TUN_CONFIG_IOCTL;
+           break;
+       case TUN_FLAVOUR_BSD:
+#if __linux__
+           /* XXX on Linux we still want TUN_CONFIG_IOCTL.  Perhaps we can
+              use this on BSD too. */
+           st->ifconfig_type=TUN_CONFIG_IOCTL;
+#else    
+           st->ifconfig_type=TUN_CONFIG_BSD;
+#endif
+           break;
+       case TUN_FLAVOUR_STREAMS:
+           st->ifconfig_type=TUN_CONFIG_BSD;
+           break;
+       }
+    }
+    if (st->route_type==TUN_CONFIG_GUESS)
+       st->route_type=st->ifconfig_type;
+
+    if (st->ifconfig_type==TUN_CONFIG_GUESS) {
+       cfgfatal(loc,"tun","cannot guess which ifconfig method to use\n");
+    }
+    if (st->route_type==TUN_CONFIG_GUESS) {
+       cfgfatal(loc,"tun","cannot guess which route method to use\n");
+    }
+
+    if (st->ifconfig_type==TUN_CONFIG_IOCTL && st->ifconfig_path) {
+       cfgfatal(loc,"tun","ifconfig-type \"ioctl\" is incompatible with "
+                "ifconfig-path\n");
+    }
+    if (st->route_type==TUN_CONFIG_IOCTL && st->route_path) {
+       cfgfatal(loc,"tun","route-type \"ioctl\" is incompatible with "
+                "route-path\n");
+    }
+
+    Message(M_DEBUG_CONFIG,"%s: tun flavour %s\n",st->nl.name,
+           tun_flavour_str(st->tun_flavour));
+    switch (st->tun_flavour) {
+    case TUN_FLAVOUR_BSD:
+       if (!st->device_path) st->device_path="/dev/tun";
+       break;
+    case TUN_FLAVOUR_LINUX:
+       if (!st->device_path) st->device_path="/dev/net/tun";
+       break;
+    case TUN_FLAVOUR_STREAMS:
+       if (!st->device_path) st->device_path="/dev/tun";
+       if (st->interface_name) cfgfatal(loc,"tun","interface name cannot "
+                                        "be specified with STREAMS TUN\n");
+       break;
+    }
+    
+    if (!st->ip_path) st->ip_path="/dev/ip";
+    if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
+    if (!st->route_path) st->route_path="route";
+
+#ifndef HAVE_TUN_STREAMS
+    if (st->tun_flavour==TUN_FLAVOUR_STREAMS) {
+       cfgfatal(loc,"tun","TUN flavour STREAMS unsupported in this build "
+                "of secnet\n");
+    }
+#endif
+#ifndef LINUX_TUN_SUPPORTED
+    if (st->tun_flavour==TUN_FLAVOUR_LINUX) {
+       cfgfatal(loc,"tun","TUN flavour LINUX unsupported in this build "
+                "of secnet\n");
+    }
+#endif
+
+    /* Old TUN interface: the network interface name depends on which
+       /dev/tunX file we open. If 'interface-search' is set to true, treat
+       'device' as the prefix and try numbers from 0--255. If it's set
+       to false, treat 'device' as the whole name, and require than an
+       appropriate interface name be specified. */
+    if (st->tun_flavour==TUN_FLAVOUR_BSD) {
+       if (st->search_for_if && st->interface_name) {
+           cfgfatal(loc,"tun","you may not specify an interface name "
+                    "in interface-search mode\n");
+       }
+       if (!st->search_for_if && !st->interface_name) {
+           cfgfatal(loc,"tun","you must specify an interface name "
+                    "when you explicitly specify a TUN device file\n");
+       }
+    }
+
+    add_hook(PHASE_GETRESOURCES,tun_phase_hook,st);
+
+    return new_closure(&st->nl.cl);
+}
+
+static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context,
+                        list_t *args)
+{
+    return tun_create(self,loc,context,args,TUN_FLAVOUR_GUESS);
+}
+
+static list_t *tun_bsd_apply(closure_t *self, struct cloc loc, dict_t *context,
+                            list_t *args)
+{
+    Message(M_WARNING,"(%s,%d): obsolete use of tun-old; replace with tun "
+           "and specify flavour \"bsd\".\n",loc.file,loc.line);
+    return tun_create(self,loc,context,args,TUN_FLAVOUR_BSD);
+}
+
+void tun_module(dict_t *dict)
+{
+    add_closure(dict,"tun",tun_apply);
+    add_closure(dict,"tun-old",tun_bsd_apply);
+}
diff --git a/u64.h b/u64.h
new file mode 100644 (file)
index 0000000..0d35c55
--- /dev/null
+++ b/u64.h
@@ -0,0 +1,159 @@
+/* uint64_t-like operations that work even on hosts lacking uint64_t
+
+   Copyright (C) 2006, 2009, 2010 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   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.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Return X rotated left by N bits, where 0 < N < 64.  */
+#define u64rol(x, n) u64or (u64shl (x, n), u64shr (x, 64 - n))
+
+#ifdef UINT64_MAX
+
+/* Native implementations are trivial.  See below for comments on what
+   these operations do.  */
+typedef uint64_t u64;
+# define u64hilo(hi, lo) ((u64) (((u64) (hi) << 32) + (lo)))
+# define u64init(hi, lo) u64hilo (hi, lo)
+# define u64lo(x) ((u64) (x))
+# define u64lt(x, y) ((x) < (y))
+# define u64and(x, y) ((x) & (y))
+# define u64or(x, y) ((x) | (y))
+# define u64xor(x, y) ((x) ^ (y))
+# define u64plus(x, y) ((x) + (y))
+# define u64shl(x, n) ((x) << (n))
+# define u64shr(x, n) ((x) >> (n))
+
+#else
+
+/* u64 is a 64-bit unsigned integer value.
+   u64init (HI, LO), is like u64hilo (HI, LO), but for use in
+   initializer contexts.  */
+# ifdef WORDS_BIGENDIAN
+typedef struct { uint32_t hi, lo; } u64;
+#  define u64init(hi, lo) { hi, lo }
+# else
+typedef struct { uint32_t lo, hi; } u64;
+#  define u64init(hi, lo) { lo, hi }
+# endif
+
+/* Given the high and low-order 32-bit quantities HI and LO, return a u64
+   value representing (HI << 32) + LO.  */
+static inline u64
+u64hilo (uint32_t hi, uint32_t lo)
+{
+  u64 r;
+  r.hi = hi;
+  r.lo = lo;
+  return r;
+}
+
+/* Return a u64 value representing LO.  */
+static inline u64
+u64lo (uint32_t lo)
+{
+  u64 r;
+  r.hi = 0;
+  r.lo = lo;
+  return r;
+}
+
+/* Return X < Y.  */
+static inline int
+u64lt (u64 x, u64 y)
+{
+  return x.hi < y.hi || (x.hi == y.hi && x.lo < y.lo);
+}
+
+/* Return X & Y.  */
+static inline u64
+u64and (u64 x, u64 y)
+{
+  u64 r;
+  r.hi = x.hi & y.hi;
+  r.lo = x.lo & y.lo;
+  return r;
+}
+
+/* Return X | Y.  */
+static inline u64
+u64or (u64 x, u64 y)
+{
+  u64 r;
+  r.hi = x.hi | y.hi;
+  r.lo = x.lo | y.lo;
+  return r;
+}
+
+/* Return X ^ Y.  */
+static inline u64
+u64xor (u64 x, u64 y)
+{
+  u64 r;
+  r.hi = x.hi ^ y.hi;
+  r.lo = x.lo ^ y.lo;
+  return r;
+}
+
+/* Return X + Y.  */
+static inline u64
+u64plus (u64 x, u64 y)
+{
+  u64 r;
+  r.lo = x.lo + y.lo;
+  r.hi = x.hi + y.hi + (r.lo < x.lo);
+  return r;
+}
+
+/* Return X << N.  */
+static inline u64
+u64shl (u64 x, int n)
+{
+  u64 r;
+  if (n < 32)
+    {
+      r.hi = (x.hi << n) | (x.lo >> (32 - n));
+      r.lo = x.lo << n;
+    }
+  else
+    {
+      r.hi = x.lo << (n - 32);
+      r.lo = 0;
+    }
+  return r;
+}
+
+/* Return X >> N.  */
+static inline u64
+u64shr (u64 x, int n)
+{
+  u64 r;
+  if (n < 32)
+    {
+      r.hi = x.hi >> n;
+      r.lo = (x.hi << (32 - n)) | (x.lo >> n);
+    }
+  else
+    {
+      r.hi = 0;
+      r.lo = x.hi >> (n - 32);
+    }
+  return r;
+}
+
+#endif
diff --git a/udp.c b/udp.c
new file mode 100644 (file)
index 0000000..d1ada01
--- /dev/null
+++ b/udp.c
@@ -0,0 +1,516 @@
+/* UDP send/receive module for secnet */
+
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+/* This module enables sites to communicate by sending UDP
+ * packets. When an instance of the module is created we can
+ * optionally bind to a particular local IP address (not implemented
+ * yet).
+ *
+ * Packets are offered to registered receivers in turn. Once one
+ * accepts it, it isn't offered to any more. */
+
+#include "secnet.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "util.h"
+#include "magic.h"
+#include "unaligned.h"
+#include "ipaddr.h"
+#include "magic.h"
+#include "comm-common.h"
+
+static comm_sendmsg_fn udp_sendmsg;
+
+struct udp {
+    struct udpcommon uc;
+    struct udpsocks socks;
+    bool_t addr_configured;
+    unsigned counter;
+};
+
+/*
+ * Re comm_addr.ix: This field allows us to note in the comm_addr
+ * which socket an incoming packet was received on.  This is required
+ * for conveniently logging the actual source of a packet.  But the ix
+ * does not formally form part of the address: it is not used when
+ * sending, nor when comparing two comm_addrs.
+ *
+ * The special value -1 means that the comm_addr was constructed by
+ * another module in secnet (eg the resolver), rather than being a
+ * description of the source of an incoming packet.
+ */
+
+static const char *udp_addr_to_string(void *commst, const struct comm_addr *ca)
+{
+    struct udp *st=commst;
+    struct udpsocks *socks=&st->socks;
+    static char sbuf[100];
+    int ix=ca->ix>=0 ? ca->ix : 0;
+
+    assert(ix>=0 && ix<socks->n_socks);
+    snprintf(sbuf, sizeof(sbuf), "udp#%u@l%d:%s%s-%s",
+            st->counter, st->uc.cc.loc.line,
+            iaddr_to_string(&socks->socks[ix].addr),
+            ca->ix<0 && socks->n_socks>1 ? "&" : "",
+            iaddr_to_string(&ca->ia));
+    return sbuf;
+}
+
+static int udp_socks_beforepoll(void *state, struct pollfd *fds, int *nfds_io,
+                               int *timeout_io)
+{
+    struct udpsocks *socks=state;
+    int i;
+    BEFOREPOLL_WANT_FDS(socks->n_socks);
+    for (i=0; i<socks->n_socks; i++) {
+       fds[i].fd=socks->socks[i].fd;
+       fds[i].events=POLLIN;
+    }
+    return 0;
+}
+
+const char *af_name(int af)
+{
+    switch (af) {
+    case AF_INET6: return "IPv6";
+    case AF_INET:  return "IPv4";
+    case 0:        return "(any)";
+    default: abort();
+    }
+}
+
+void udp_sock_experienced(struct log_if *lg, struct udpcommon *uc,
+                         struct udpsocks *socks, struct udpsock *us,
+                         const union iaddr *dest, int af,
+                         int r, int errnoval)
+{
+    bool_t success=r>=0;
+    if (us->experienced[!!dest][af][success]++)
+       return;
+    lg_perror(lg, uc->cc.cl.description, &uc->cc.loc,
+             success ? M_INFO : M_WARNING,
+             success ? 0 : errnoval,
+             "%s %s experiencing some %s %s%s%s%s%s%s",
+             socks->desc,iaddr_to_string(&us->addr),
+             success?"success":"trouble",
+             dest?"transmitting":"receiving",
+             af?" ":"", af?af_name(af):"",
+             dest?" (to ":"",
+             dest?iaddr_to_string(dest):"",
+             dest?")":"");
+}
+
+static void udp_socks_afterpoll(void *state, struct pollfd *fds, int nfds)
+{
+    struct udpsocks *socks=state;
+    struct udpcommon *uc=socks->uc;
+    union iaddr from;
+    socklen_t fromlen;
+    bool_t done;
+    int rv;
+    int i;
+
+    struct commcommon *cc=&uc->cc;
+
+    for (i=0; i<socks->n_socks; i++) {
+       struct udpsock *us=&socks->socks[i];
+       if (i>=nfds) continue;
+       if (!(fds[i].revents & POLLIN)) continue;
+       assert(fds[i].fd == us->fd);
+       int fd=us->fd;
+       do {
+           fromlen=sizeof(from);
+           BUF_ASSERT_FREE(cc->rbuf);
+           BUF_ALLOC(cc->rbuf,"udp_afterpoll");
+           buffer_init(cc->rbuf,calculate_max_start_pad());
+           rv=recvfrom(fd, cc->rbuf->start,
+                       buf_remaining_space(cc->rbuf),
+                       0, &from.sa, &fromlen);
+           if (rv>0) {
+               cc->rbuf->size=rv;
+               if (uc->use_proxy) {
+                   /* Check that the packet came from our poxy server;
+                      we shouldn't be contacted directly by anybody else
+                      (since they can trivially forge source addresses) */
+                   if (!iaddr_equal(&from,&uc->proxy,False)) {
+                       Message(M_INFO,"udp: received packet that's not "
+                               "from the proxy\n");
+                       BUF_FREE(cc->rbuf);
+                       continue;
+                   }
+                   /* proxy protocol supports ipv4 transport only */
+                   from.sa.sa_family=AF_INET;
+                   BUF_GET_BYTES(unprepend,cc->rbuf,&from.sin.sin_addr,4);
+                   buf_unprepend(cc->rbuf,2);
+                   BUF_GET_BYTES(unprepend,cc->rbuf,&from.sin.sin_port,2);
+               }
+               struct comm_addr ca;
+               ca.comm=&cc->ops;
+               ca.ia=from;
+               ca.ix=i;
+               done=comm_notify(cc, cc->rbuf, &ca);
+               if (done) {
+                   udp_sock_experienced(0,uc,socks,us,0,
+                                        from.sa.sa_family,0,0);
+               } else {
+                   uint32_t msgtype;
+                   if (cc->rbuf->size>12 /* prevents traffic amplification */
+                       && ((msgtype=get_uint32(cc->rbuf->start+8))
+                           != LABEL_NAK)) {
+                       uint32_t source,dest;
+                       /* Manufacture and send NAK packet */
+                       source=get_uint32(cc->rbuf->start); /* Us */
+                       dest=get_uint32(cc->rbuf->start+4); /* Them */
+                       send_nak(&ca,source,dest,msgtype,cc->rbuf,
+                                priomsg_getmessage(&cc->why_unwanted,
+                                                   "unwanted"));
+                   }
+                   BUF_FREE(cc->rbuf);
+               }
+               BUF_ASSERT_FREE(cc->rbuf);
+           } else { /* rv<=0 */
+               if (errno!=EINTR && !iswouldblock(errno))
+                   udp_sock_experienced(0,uc,socks,us, 0,0, rv,errno);
+               BUF_FREE(cc->rbuf);
+           }
+       } while (rv>=0);
+    }
+}
+
+static bool_t udp_sendmsg(void *commst, struct buffer_if *buf,
+                         const struct comm_addr *dest,
+                         struct comm_clientinfo *clientinfo)
+{
+    struct udp *st=commst;
+    struct udpcommon *uc=&st->uc;
+    struct udpsocks *socks=&st->socks;
+    uint8_t *sa;
+
+    if (uc->use_proxy) {
+       struct udpsock *us=&socks->socks[0];
+       sa=buf_prepend(buf,8);
+       if (dest->ia.sa.sa_family != AF_INET) {
+           Message(M_INFO,
+               "udp: proxy means dropping outgoing non-IPv4 packet to %s\n",
+                   iaddr_to_string(&dest->ia));
+           return False;
+       }
+       memcpy(sa,&dest->ia.sin.sin_addr,4);
+       memset(sa+4,0,4);
+       memcpy(sa+6,&dest->ia.sin.sin_port,2);
+       int r=sendto(us->fd,sa,buf->size+8,0,&uc->proxy.sa,
+              iaddr_socklen(&uc->proxy));
+       udp_sock_experienced(0,uc,socks,us, &dest->ia,0, r,errno);
+       buf_unprepend(buf,8);
+    } else {
+       int i,r;
+       bool_t allunsupported=True;
+       int af=dest->ia.sa.sa_family;
+       for (i=0; i<socks->n_socks; i++) {
+           struct udpsock *us=&socks->socks[i];
+           if (us->addr.sa.sa_family != af)
+               /* no point even trying */
+               continue;
+           r=sendto(us->fd, buf->start, buf->size, 0,
+                    &dest->ia.sa, iaddr_socklen(&dest->ia));
+           udp_sock_experienced(0,uc,socks,us, &dest->ia,af, r,errno);
+           if (r>=0) return True;
+           if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH))
+               /* who knows what that error means? */
+               allunsupported=False;
+       }
+       return !allunsupported; /* see doc for comm_sendmsg_fn in secnet.h */
+    }
+
+    return True;
+}
+
+void udp_destroy_socket(struct udpcommon *uc, struct udpsock *us)
+{
+    if (us->fd>=0) {
+       close(us->fd);
+       us->fd=-1;
+    }
+}
+
+#define FAIL_LG 0, cc->cl.description, &cc->loc, failmsgclass
+#define FAIL(...) do{                                          \
+        lg_perror(FAIL_LG,errno,__VA_ARGS__);  \
+       goto failed;                                            \
+    }while(0)
+
+static bool_t record_socket_gotaddr(struct udpcommon *uc, struct udpsock *us,
+                                   int failmsgclass)
+{
+    struct commcommon *cc=&uc->cc;
+    socklen_t salen=sizeof(us->addr);
+    int r=getsockname(us->fd,&us->addr.sa,&salen);
+    if (r) FAIL("getsockname()");
+    if ((size_t)salen>sizeof(us->addr)) /* cast squashes clang warning */
+      { errno=0; FAIL("getsockname() length"); }
+    return True;
+
+ failed:
+    return False;
+}
+
+bool_t udp_import_socket(struct udpcommon *uc, struct udpsock *us,
+                        int failmsgclass, int fd)
+{
+    FILLZERO(us->experienced);
+    us->fd=fd;
+    return record_socket_gotaddr(uc,us,failmsgclass);
+}
+
+bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us,
+                      int failmsgclass)
+{
+    const union iaddr *addr=&us->addr;
+    struct commcommon *cc=&uc->cc;
+    us->fd=-1;
+
+    FILLZERO(us->experienced);
+    us->fd=socket(addr->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
+    if (us->fd<0) FAIL("socket");
+    setnonblock(us->fd);
+    setcloexec(us->fd);
+#ifdef CONFIG_IPV6
+    if (addr->sa.sa_family==AF_INET6) {
+       int r;
+       int optval=1;
+       socklen_t optlen=sizeof(optval);
+       r=setsockopt(us->fd,IPPROTO_IPV6,IPV6_V6ONLY,&optval,optlen);
+       if (r) FAIL("setsockopt(,IPV6_V6ONLY,&1,)");
+    }
+#endif
+
+    if (uc->authbind) {
+       pid_t c;
+       int status;
+       char desc[200];
+       snprintf(desc,sizeof(desc),"authbind for %s: %s",
+                iaddr_to_string(addr), uc->authbind);
+
+       /* XXX this fork() and waitpid() business needs to be hidden
+          in some system-specific library functions. */
+       c=fork();
+       if (c==-1)
+           FAIL("fork() for authbind");
+       if (c==0) {
+           char *argv[5], addrstr[33], portstr[5];
+           const char *addrfam;
+           int port;
+           afterfork();
+           switch (addr->sa.sa_family) {
+           case AF_INET:
+               sprintf(addrstr,"%08lX",(long)addr->sin.sin_addr.s_addr);
+               port=addr->sin.sin_port;
+               addrfam=NULL;
+               break;
+#ifdef CONFIG_IPV6
+           case AF_INET6: {
+               int i;
+               for (i=0; i<16; i++)
+                   sprintf(addrstr+i*2,"%02X",addr->sin6.sin6_addr.s6_addr[i]);
+               port=addr->sin6.sin6_port;
+               addrfam="6";
+               break;
+           }
+#endif /*CONFIG_IPV6*/
+           default:
+               fatal("udp (%s:%d): unsupported address family for authbind",
+                     cc->loc.file,cc->loc.line);
+           }
+           sprintf(portstr,"%04X",port);
+           argv[0]=uc->authbind;
+           argv[1]=addrstr;
+           argv[2]=portstr;
+           argv[3]=(char*)addrfam;
+           argv[4]=NULL;
+           dup2(us->fd,0);
+           execvp(uc->authbind,argv);
+           _exit(255);
+       }
+       while (waitpid(c,&status,0)==-1) {
+           if (errno==EINTR) continue;
+           FAIL("waitpid for authbind");
+       }
+       if (status) {
+           if (WIFEXITED(status) && WEXITSTATUS(status)<127) {
+               int es=WEXITSTATUS(status);
+               lg_perror(FAIL_LG,es,
+                         "%s exited with error exit status %d;"
+                         " indicates error",desc,es);
+           } else {
+               lg_exitstatus(FAIL_LG,status,desc);
+           }
+           goto failed;
+       }
+    } else {
+       if (bind(us->fd, &addr->sa, iaddr_socklen(addr))!=0)
+           FAIL("bind (%s)",iaddr_to_string(addr));
+    }
+
+    bool_t ok=record_socket_gotaddr(uc,us,failmsgclass);
+    if (!ok) goto failed;
+
+    return True;
+
+failed:
+    udp_destroy_socket(uc,us);
+    return False;
+}
+
+#undef FAIL
+
+void udp_socks_register(struct udpcommon *uc, struct udpsocks *socks,
+                       const char *desc)
+{
+    socks->uc=uc;
+    socks->desc=desc;
+    socks->interest=
+       register_for_poll(socks,udp_socks_beforepoll,udp_socks_afterpoll,"udp");
+}
+
+void udp_socks_deregister(struct udpcommon *uc, struct udpsocks *socks)
+{
+    socks->uc=uc;
+    deregister_for_poll(socks->interest);
+}
+
+void udp_socks_childpersist(struct udpcommon *uc, struct udpsocks *socks)
+{
+    int i;
+    for (i=0; i<socks->n_socks; i++)
+       udp_destroy_socket(uc,&socks->socks[i]);
+}
+
+static void udp_childpersist_hook(void *sst, uint32_t new_phase)
+{
+    struct udp *st=sst;
+    udp_socks_childpersist(&st->uc,&st->socks);
+}
+
+static void udp_phase_hook(void *sst, uint32_t new_phase)
+{
+    struct udp *st=sst;
+    struct udpsocks *socks=&st->socks;
+    struct udpcommon *uc=&st->uc;
+    int i;
+    bool_t anydone=0;
+
+    for (i=0; i<socks->n_socks; i++) {
+       bool_t required=st->addr_configured
+           || (!anydone && i==socks->n_socks-1);
+       anydone += udp_make_socket(uc,&socks->socks[i],
+                                  required ? M_FATAL : M_WARNING);
+    }
+
+    udp_socks_register(uc,socks, uc->use_proxy ? "proxy" : "socket");
+
+    add_hook(PHASE_CHILDPERSIST,udp_childpersist_hook,st);
+}
+
+static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context,
+                        list_t *args)
+{
+    static unsigned counter;
+
+    struct udp *st;
+    list_t *caddrl;
+    list_t *l;
+    uint32_t a;
+    int i;
+
+    COMM_APPLY(st,&st->uc.cc,udp_,"udp",loc);
+    COMM_APPLY_STANDARD(st,&st->uc.cc,"udp",args);
+    UDP_APPLY_STANDARD(st,&st->uc,"udp");
+
+    struct udpcommon *uc=&st->uc;
+    struct udpsocks *socks=&st->socks;
+    struct commcommon *cc=&uc->cc;
+
+    st->counter=counter++;
+
+    union iaddr defaultaddrs[] = {
+#ifdef CONFIG_IPV6
+       { .sin6 = { .sin6_family=AF_INET6,
+                   .sin6_port=htons(uc->port),
+                   .sin6_addr=IN6ADDR_ANY_INIT } },
+#endif
+       { .sin = { .sin_family=AF_INET,
+                  .sin_port=htons(uc->port),
+                  .sin_addr= { .s_addr=INADDR_ANY } } }
+    };
+
+    caddrl=dict_lookup(d,"address");
+    st->addr_configured=!!caddrl;
+    socks->n_socks=st->addr_configured ? list_length(caddrl)
+       : (int)ARRAY_SIZE(defaultaddrs);
+    if (socks->n_socks<=0 || socks->n_socks>UDP_MAX_SOCKETS)
+       cfgfatal(cc->loc,"udp","`address' must be 1..%d addresses",
+                UDP_MAX_SOCKETS);
+
+    for (i=0; i<socks->n_socks; i++) {
+       struct udpsock *us=&socks->socks[i];
+       if (!st->addr_configured) {
+           us->addr=defaultaddrs[i];
+       } else {
+           string_item_to_iaddr(list_elem(caddrl,i),uc->port,&us->addr,"udp");
+       }
+       us->fd=-1;
+    }
+
+    l=dict_lookup(d,"proxy");
+    if (l) {
+       uc->use_proxy=True;
+       uc->proxy.sa.sa_family=AF_INET;
+       item=list_elem(l,0);
+       if (!item || item->type!=t_string) {
+           cfgfatal(cc->loc,"udp","proxy must supply ""addr"",port\n");
+       }
+       a=string_item_to_ipaddr(item,"proxy");
+       uc->proxy.sin.sin_addr.s_addr=htonl(a);
+       item=list_elem(l,1);
+       if (!item || item->type!=t_number) {
+           cfgfatal(cc->loc,"udp","proxy must supply ""addr"",port\n");
+       }
+       uc->proxy.sin.sin_port=htons(item->data.number);
+    }
+
+    update_max_start_pad(&comm_max_start_pad, uc->use_proxy ? 8 : 0);
+
+    add_hook(PHASE_GETRESOURCES,udp_phase_hook,st);
+
+    return new_closure(&cc->cl);
+}
+
+void udp_module(dict_t *dict)
+{
+    add_closure(dict,"udp",udp_apply);
+}
diff --git a/uk.org.greenend.secnet.plist b/uk.org.greenend.secnet.plist
new file mode 100644 (file)
index 0000000..f462c03
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<!--
+ This file is part of secnet.
+ See README for full list of copyright holders.
+
+ secnet is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ secnet 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.  See the GNU
+ General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ version 3 along with secnet; if not, see
+ https://www.gnu.org/licenses/gpl.html.
+  -->
+<dict>
+       <key>EnvironmentVariables</key>
+       <dict>
+               <key>LANG</key>
+               <string>en_GB.UTF-8</string>
+               <key>LC_ALL</key>
+               <string>en_GB.UTF-8</string>
+       </dict>
+       <key>Label</key>
+       <string>uk.org.greenend.secnet</string>
+       <key>ProgramArguments</key>
+       <array>
+               <string>/usr/local/sbin/secnet</string>
+               <string>-m</string>
+       </array>
+       <key>WorkingDirectory</key>
+       <string>/</string>
+       <key>RunAtLoad</key>
+       <true/>
+</dict>
+</plist>
diff --git a/unaligned.h b/unaligned.h
new file mode 100644 (file)
index 0000000..2c48d0e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef unaligned_h
+#define unaligned_h
+
+#include <stdint.h>
+#include "util.h"
+
+/* Parts of the secnet key-exchange protocol require access to
+   unaligned big-endian quantities in buffers. These macros provide
+   convenient access, even on architectures that don't support unaligned
+   accesses. */
+
+#define put_uint32(a,v) do { (a)[0]=(v)>>24; (a)[1]=((v)&0xff0000)>>16; \
+(a)[2]=((v)&0xff00)>>8; (a)[3]=(v)&0xff; } while(0)
+
+#define put_uint16(a,v) do {(a)[0]=((v)&0xff00)>>8; (a)[1]=(v)&0xff;} while(0)
+
+#define put_uint8(a,v) do {(a)[0]=((v)&0xff);} while(0)
+
+#define get_uint32(a)                                  \
+  (((uint32_t)(a)[0]<<24) | ((uint32_t)(a)[1]<<16) |   \
+   ((uint32_t)(a)[2]<<8)  |  (uint32_t)(a)[3])
+
+#define get_uint16(a) (((uint16_t)(a)[0]<<8)|(uint16_t)(a)[1])
+
+#define get_uint8(a) (((uint8_t)(a)[0]))
+
+#define UNALIGNED_DEF_FORTYPE(type,appre)                              \
+static inline void buf_##appre##_##type(struct buffer_if *buf, type##_t v) \
+{                                                                      \
+    uint8_t *c=buf_##appre(buf,sizeof(type##_t));                      \
+    put_##type(c,v);                                                   \
+}                                                                      \
+static inline type##_t buf_un##appre##_##type(struct buffer_if *buf)   \
+{                                                                      \
+    const uint8_t *c=buf_un##appre(buf,sizeof(type##_t));              \
+    return get_##type(c);                                              \
+}
+
+UNALIGNED_DEF_FORTYPE(uint32,append)
+UNALIGNED_DEF_FORTYPE(uint16,append)
+UNALIGNED_DEF_FORTYPE(uint8,append)
+UNALIGNED_DEF_FORTYPE(uint32,prepend)
+UNALIGNED_DEF_FORTYPE(uint16,prepend)
+UNALIGNED_DEF_FORTYPE(uint8,prepend)
+
+#endif /* unaligned_h */
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..977e131
--- /dev/null
+++ b/util.c
@@ -0,0 +1,807 @@
+/*
+ * util.c
+ * - output and logging support
+ * - program lifetime support
+ * - IP address and subnet munging routines
+ * - MPI convenience functions
+ */
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <assert.h>
+#include <sys/wait.h>
+#include <adns.h>
+#include "util.h"
+#include "unaligned.h"
+#include "magic.h"
+#include "ipaddr.h"
+
+#define MIN_BUFFER_SIZE 64
+#define DEFAULT_BUFFER_SIZE 4096
+#define MAX_BUFFER_SIZE 131072
+
+static const char *hexdigits="0123456789abcdef";
+
+uint32_t current_phase=0;
+
+struct phase_hook {
+    hook_fn *fn;
+    void *state;
+    LIST_ENTRY(phase_hook) entry;
+};
+
+static LIST_HEAD(, phase_hook) hooks[NR_PHASES];
+
+char *safe_strdup(const char *s, const char *message)
+{
+    char *d;
+    d=strdup(s);
+    if (!d) {
+       fatal_perror("%s",message);
+    }
+    return d;
+}
+
+void *safe_malloc(size_t size, const char *message)
+{
+    void *r;
+    if (!size)
+       return 0;
+    r=malloc(size);
+    if (!r) {
+       fatal_perror("%s",message);
+    }
+    return r;
+}
+void *safe_realloc_ary(void *p, size_t size, size_t count,
+                      const char *message) {
+    if (count >= INT_MAX/size) {
+       fatal("array allocation overflow: %s", message);
+    }
+    assert(size && count);
+    p = realloc(p, size*count);
+    if (!p)
+       fatal_perror("%s", message);
+    return p;
+}
+
+void *safe_malloc_ary(size_t size, size_t count, const char *message) {
+    if (!size || !count)
+       return 0;
+    return safe_realloc_ary(0,size,count,message);
+}
+
+void hex_encode(const uint8_t *bin, int binsize, char *buff)
+{
+    int i;
+
+    for (i=0; i<binsize; i++) {
+       buff[i*2]=hexdigits[(bin[i] & 0xf0) >> 4];
+       buff[i*2+1]=hexdigits[(bin[i] & 0xf)];
+    }
+    buff[binsize*2]=0;
+}
+
+string_t hex_encode_alloc(const uint8_t *bin, int binsize)
+{
+    char *buff;
+
+    buff=safe_malloc(hex_encode_size(binsize),"hex_encode");
+    hex_encode(bin,binsize,buff);
+    return buff;
+}
+
+static uint8_t hexval(uint8_t c)
+{
+    switch (c) {
+    case '0': return 0;
+    case '1': return 1;
+    case '2': return 2;
+    case '3': return 3;
+    case '4': return 4;
+    case '5': return 5;
+    case '6': return 6;
+    case '7': return 7;
+    case '8': return 8;
+    case '9': return 9;
+    case 'a': return 10;
+    case 'A': return 10;
+    case 'b': return 11;
+    case 'B': return 11;
+    case 'c': return 12;
+    case 'C': return 12;
+    case 'd': return 13;
+    case 'D': return 13;
+    case 'e': return 14;
+    case 'E': return 14;
+    case 'f': return 15;
+    case 'F': return 15;
+    }
+    return -1;
+}
+
+bool_t hex_decode(uint8_t *buffer, int32_t buflen, int32_t *outlen,
+                 cstring_t hb, bool_t allow_odd_nibble)
+{
+    int i = 0, j = 0, l = strlen(hb), hi, lo;
+    bool_t ok = False;
+
+    if (!l || !buflen) { ok = !l; goto done; }
+    if (l&1) {
+       /* The number starts with a half-byte */
+       if (!allow_odd_nibble) goto done;
+       lo = hexval(hb[j++]); if (lo < 0) goto done;
+       buffer[i++] = lo;
+    }
+    for (; hb[j] && i < buflen; i++) {
+       hi = hexval(hb[j++]);
+       lo = hexval(hb[j++]);
+       if (hi < 0 || lo < 0) goto done;
+       buffer[i] = (hi << 4) | lo;
+    }
+    ok = !hb[j];
+done:
+    *outlen = i;
+    return ok;
+}
+
+void read_mpbin(MP_INT *a, uint8_t *bin, int binsize)
+{
+    char *buff = hex_encode_alloc(bin, binsize);
+    mpz_set_str(a, buff, 16);
+    free(buff);
+}
+
+char *write_mpstring(MP_INT *a)
+{
+    char *buff;
+
+    buff=safe_malloc(mpz_sizeinbase(a,16)+2,"write_mpstring");
+    mpz_get_str(buff, 16, a);
+    return buff;
+}
+
+int32_t write_mpbin(MP_INT *a, uint8_t *buffer, int32_t buflen)
+{
+    char *hb = write_mpstring(a);
+    int32_t len;
+    hex_decode(buffer, buflen, &len, hb, True);
+    free(hb);
+    return len;
+}
+
+#define DEFINE_SETFDFLAG(fn,FL,FLAG)                                   \
+void fn(int fd) {                                                      \
+    int r=fcntl(fd, F_GET##FL);                                                \
+    if (r<0) fatal_perror("fcntl(,F_GET" #FL ") failed");              \
+    r=fcntl(fd, F_SET##FL, r|FLAG);                                    \
+    if (r<0) fatal_perror("fcntl(,F_SET" #FL ",|" #FLAG ") failed");   \
+}
+
+DEFINE_SETFDFLAG(setcloexec,FD,FD_CLOEXEC);
+DEFINE_SETFDFLAG(setnonblock,FL,O_NONBLOCK);
+
+void pipe_cloexec(int fd[2]) {
+    int r=pipe(fd);
+    if (r) fatal_perror("pipe");
+    setcloexec(fd[0]);
+    setcloexec(fd[1]);
+}
+
+static const char *phases[NR_PHASES]={
+    "PHASE_INIT",
+    "PHASE_GETOPTS",
+    "PHASE_READCONFIG",
+    "PHASE_SETUP",
+    "PHASE_DAEMONIZE",
+    "PHASE_GETRESOURCES",
+    "PHASE_DROPPRIV",
+    "PHASE_RUN",
+    "PHASE_SHUTDOWN",
+    "PHASE_CHILDPERSIST"
+};
+
+void enter_phase(uint32_t new_phase)
+{
+    struct phase_hook *i;
+
+    if (!LIST_EMPTY(&hooks[new_phase]))
+       Message(M_DEBUG_PHASE,"Running hooks for %s...\n", phases[new_phase]);
+    current_phase=new_phase;
+
+    LIST_FOREACH(i, &hooks[new_phase], entry)
+       i->fn(i->state, new_phase);
+    Message(M_DEBUG_PHASE,"Now in %s\n",phases[new_phase]);
+}
+
+void phase_hooks_init(void)
+{
+    int i;
+    for (i=0; i<NR_PHASES; i++)
+       LIST_INIT(&hooks[i]);
+}
+
+void clear_phase_hooks(uint32_t phase)
+{
+    struct phase_hook *h, *htmp;
+    LIST_FOREACH_SAFE(h, &hooks[phase], entry, htmp)
+       free(h);
+    LIST_INIT(&hooks[phase]);
+}
+
+bool_t add_hook(uint32_t phase, hook_fn *fn, void *state)
+{
+    struct phase_hook *h;
+
+    NEW(h);
+    h->fn=fn;
+    h->state=state;
+    LIST_INSERT_HEAD(&hooks[phase],h,entry);
+    return True;
+}
+
+bool_t remove_hook(uint32_t phase, hook_fn *fn, void *state)
+{
+    fatal("remove_hook: not implemented");
+
+    return False;
+}
+
+void vslilog(struct log_if *lf, int priority, const char *message, va_list ap)
+{
+    lf->vlogfn(lf->st,priority,message,ap);
+}
+
+void slilog(struct log_if *lf, int priority, const char *message, ...)
+{
+    va_list ap;
+    
+    va_start(ap,message);
+    vslilog(lf,priority,message,ap);
+    va_end(ap);
+}
+
+struct buffer {
+    closure_t cl;
+    struct buffer_if ops;
+};
+
+void buffer_assert_free(struct buffer_if *buffer, cstring_t file,
+                       int line)
+{
+    if (!buffer->free) {
+       fprintf(stderr,"secnet: BUF_ASSERT_FREE, %s line %d, owned by %s",
+               file,line,buffer->owner);
+       assert(!"buffer_assert_free failure");
+    }
+}
+
+void buffer_assert_used(struct buffer_if *buffer, cstring_t file,
+                       int line)
+{
+    if (buffer->free) {
+       fprintf(stderr,"secnet: BUF_ASSERT_USED, %s line %d, last owned by %s",
+               file,line,buffer->owner);
+       assert(!"buffer_assert_used failure");
+    }
+}
+
+void buffer_init(struct buffer_if *buffer, int32_t max_start_pad)
+{
+    assert(max_start_pad<=buffer->alloclen);
+    buffer->start=buffer->base+max_start_pad;
+    buffer->size=0;
+}
+
+void buffer_destroy(struct buffer_if *buf)
+{
+    BUF_ASSERT_FREE(buf);
+    free(buf->base);
+    buf->start=buf->base=0;
+    buf->size=buf->alloclen=0;
+}
+
+void *buf_append(struct buffer_if *buf, int32_t amount) {
+    void *p;
+    assert(amount <= buf_remaining_space(buf));
+    p=buf->start + buf->size;
+    buf->size+=amount;
+    return p;
+}
+
+void *buf_prepend(struct buffer_if *buf, int32_t amount) {
+    assert(amount <= buf->start - buf->base);
+    buf->size+=amount;
+    return buf->start-=amount;
+}
+
+void *buf_unappend(struct buffer_if *buf, int32_t amount) {
+    if (buf->size < amount) return 0;
+    return buf->start+(buf->size-=amount);
+}
+
+void *buf_unprepend(struct buffer_if *buf, int32_t amount) {
+    void *p;
+    if (buf->size < amount) return 0;
+    p=buf->start;
+    buf->start+=amount;
+    buf->size-=amount;
+    return p;
+}
+
+void buf_append_string(struct buffer_if *buf, cstring_t s)
+{
+    size_t len;
+
+    len=strlen(s);
+    /* fixme: if string is longer than 65535, result is a corrupted packet */
+    buf_append_uint16(buf,len);
+    BUF_ADD_BYTES(append,buf,s,len);
+}
+
+void truncmsg_add_string(struct buffer_if *buf, cstring_t s)
+{
+    int32_t l = MIN((int32_t)strlen(s), buf_remaining_space(buf));
+    BUF_ADD_BYTES(append, buf, s, l);
+}
+void truncmsg_add_packet_string(struct buffer_if *buf, int32_t l,
+                               const uint8_t *s)
+{
+    char c;
+    while (l-- > 0) {
+       c = *s++;
+       if (c >= ' ' && c <= 126 && c != '\\' && c != '"' && c != '\'') {
+           if (!buf_remaining_space(buf)) break;
+           buf->start[buf->size++] = c;
+           continue;
+       }
+       char quoted[5];
+       quoted[0] = '\\';
+       quoted[2] = 0;
+       switch (c) {
+       case '\n': quoted[1] = 'n'; break;
+       case '\r': quoted[1] = 'r'; break;
+       case '\t': quoted[1] = 't'; break;
+       case '\\': case '"': case '\'': quoted[1] = c; break;
+       default: sprintf(quoted, "\\x%02x", (unsigned)c);
+       }
+       truncmsg_add_string(buf, quoted);
+    }
+}
+const char *truncmsg_terminate(const struct buffer_if *buf)
+{
+    if (buf_remaining_space(buf)) {
+       buf->start[buf->size] = 0;
+    } else {
+       assert(buf->size >= 4);
+       strcpy(buf->start + buf->size - 4, "...");
+    }
+    return buf->start;
+}
+
+void priomsg_new(struct priomsg *pm, int32_t maxlen)
+{
+    buffer_new(&pm->m, maxlen);
+    pm->prio = INT_MIN;
+}
+void priomsg_reset(struct priomsg *pm)
+{
+    buffer_init(&pm->m, 0);
+    pm->prio = INT_MIN;
+}
+bool_t priomsg_update_p(struct priomsg *pm, int prio)
+{
+    if (!pm) return False;
+    if (prio <= pm->prio) return False;
+    buffer_init(&pm->m, 0);
+    pm->prio = prio;
+    return True;
+}
+const char *priomsg_getmessage(const struct priomsg *pm, const char *defmsg)
+{
+    if (pm->prio >= INT_MIN)
+       return truncmsg_terminate(&pm->m);
+    else
+       return defmsg;
+}
+
+bool_t priomsg_update_fixed(struct priomsg *pm, int prio, const char *m) {
+    if (!priomsg_update_p(pm, prio)) return False;
+    truncmsg_add_string(&pm->m, m);
+    return True;
+}
+
+void buffer_new(struct buffer_if *buf, int32_t len)
+{
+    buf->free=True;
+    buf->owner=NULL;
+    buf->loc.file=NULL;
+    buf->loc.line=0;
+    buf->size=0;
+    buf->alloclen=len;
+    buf->start=NULL;
+    buf->base=safe_malloc(len,"buffer_new");
+}
+
+void buffer_readonly_view(struct buffer_if *buf, const void *data, int32_t len)
+{
+    buf->free=False;
+    buf->owner="READONLY";
+    buf->loc.file=NULL;
+    buf->loc.line=0;
+    buf->size=buf->alloclen=len;
+    buf->base=buf->start=(uint8_t*)data;
+}
+
+void buffer_readonly_clone(struct buffer_if *out, const struct buffer_if *in)
+{
+    buffer_readonly_view(out,in->start,in->size);
+}
+
+void buffer_copy(struct buffer_if *dst, const struct buffer_if *src)
+{
+    if (dst->alloclen < src->alloclen) {
+       dst->base=realloc(dst->base,src->alloclen);
+       if (!dst->base) fatal_perror("buffer_copy");
+       dst->alloclen = src->alloclen;
+    }
+    dst->start = dst->base + (src->start - src->base);
+    dst->size = src->size;
+    memcpy(dst->start, src->start, dst->size);
+}
+
+static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context,
+                           list_t *args)
+{
+    struct buffer *st;
+    item_t *item;
+    dict_t *dict;
+    bool_t lockdown=False;
+    uint32_t len=DEFAULT_BUFFER_SIZE;
+    
+    NEW(st);
+    st->cl.description="buffer";
+    st->cl.type=CL_BUFFER;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+
+    /* First argument, if present, is buffer length */
+    item=list_elem(args,0);
+    if (item) {
+       if (item->type!=t_number) {
+           cfgfatal(st->ops.loc,"buffer","first parameter must be a "
+                    "number (buffer size)\n");
+       }
+       len=item->data.number;
+       if (len<MIN_BUFFER_SIZE) {
+           cfgfatal(st->ops.loc,"buffer","ludicrously small buffer size\n");
+       }
+       if (len>MAX_BUFFER_SIZE) {
+           cfgfatal(st->ops.loc,"buffer","ludicrously large buffer size\n");
+       }
+    }
+    /* Second argument, if present, is a dictionary */
+    item=list_elem(args,1);
+    if (item) {
+       if (item->type!=t_dict) {
+           cfgfatal(st->ops.loc,"buffer","second parameter must be a "
+                    "dictionary\n");
+       }
+       dict=item->data.dict;
+       lockdown=dict_read_bool(dict,"lockdown",False,"buffer",st->ops.loc,
+                               False);
+    }
+
+    buffer_new(&st->ops,len);
+    if (lockdown) {
+       /* XXX mlock the buffer if possible */
+    }
+    
+    return new_closure(&st->cl);
+}
+
+void send_nak(const struct comm_addr *dest, uint32_t our_index,
+             uint32_t their_index, uint32_t msgtype,
+             struct buffer_if *buf, const char *logwhy)
+{
+    buffer_init(buf,calculate_max_start_pad());
+    buf_append_uint32(buf,their_index);
+    buf_append_uint32(buf,our_index);
+    buf_append_uint32(buf,LABEL_NAK);
+    if (logwhy)
+       Message(M_INFO,"%s: sending NAK for"
+               " %08"PRIx32" %08"PRIx32"<-%08"PRIx32":"
+               " %s\n",
+               comm_addr_to_string(dest),
+               msgtype, our_index, their_index, logwhy);
+    dest->comm->sendmsg(dest->comm->st, buf, dest, 0);
+}
+
+int consttime_memeq(const void *s1in, const void *s2in, size_t n)
+{
+    const uint8_t *s1=s1in, *s2=s2in;
+    register volatile uint8_t accumulator=0;
+
+    while (n-- > 0) {
+       accumulator |= (*s1++ ^ *s2++);
+    }
+    accumulator |= accumulator >> 4; /* constant-time             */
+    accumulator |= accumulator >> 2; /*  boolean canonicalisation */
+    accumulator |= accumulator >> 1;
+    accumulator &= 1;
+    accumulator ^= 1;
+    return accumulator;
+}
+
+void hash_hash(const struct hash_if *hashi, const void *msg, int32_t len,
+              uint8_t *digest) {
+    uint8_t hst[hashi->slen];
+    hashi->init(hst);
+    hashi->update(hst,msg,len);
+    hashi->final(hst,digest);
+}
+
+void util_module(dict_t *dict)
+{
+    add_closure(dict,"sysbuffer",buffer_apply);
+}
+
+void update_max_start_pad(int32_t *our_module_global, int32_t our_instance)
+{
+    if (*our_module_global < our_instance)
+       *our_module_global=our_instance;
+}
+
+int32_t        transform_max_start_pad, comm_max_start_pad;
+
+int32_t calculate_max_start_pad(void)
+{
+    return
+       site_max_start_pad +
+       transform_max_start_pad +
+       comm_max_start_pad;
+}
+
+void vslilog_part(struct log_if *lf, int priority, const char *message, va_list ap)
+{
+    char *buff=lf->buff;
+    size_t bp;
+    char *nlp;
+
+    bp=strlen(buff);
+    assert(bp < LOG_MESSAGE_BUFLEN);
+    vsnprintf(buff+bp,LOG_MESSAGE_BUFLEN-bp,message,ap);
+    buff[LOG_MESSAGE_BUFLEN-1] = '\n';
+    buff[LOG_MESSAGE_BUFLEN] = '\0';
+    /* Each line is sent separately */
+    while ((nlp=strchr(buff,'\n'))) {
+       *nlp=0;
+       slilog(lf,priority,"%s",buff);
+       memmove(buff,nlp+1,strlen(nlp+1)+1);
+    }
+}
+
+extern void slilog_part(struct log_if *lf, int priority, const char *message, ...)
+{
+    va_list ap;
+    va_start(ap,message);
+    vslilog_part(lf,priority,message,ap);
+    va_end(ap);
+}
+
+void string_item_to_iaddr(const item_t *item, uint16_t port, union iaddr *ia,
+                         const char *desc)
+{
+#ifndef CONFIG_IPV6
+
+    ia->sin.sin_family=AF_INET;
+    ia->sin.sin_addr.s_addr=htonl(string_item_to_ipaddr(item,desc));
+    ia->sin.sin_port=htons(port);
+
+#else /* CONFIG_IPV6 => we have adns_text2addr */
+
+    if (item->type!=t_string)
+       cfgfatal(item->loc,desc,"expecting a string IP (v4 or v6) address\n");
+    socklen_t salen=sizeof(*ia);
+    int r=adns_text2addr(item->data.string, port,
+                        adns_qf_addrlit_ipv4_quadonly,
+                        &ia->sa, &salen);
+    assert(r!=ENOSPC);
+    if (r) cfgfatal(item->loc,desc,"invalid IP (v4 or v6) address: %s\n",
+                   strerror(r));
+
+#endif /* CONFIG_IPV6 */
+}
+
+#define IADDR_NBUFS 8
+
+const char *iaddr_to_string(const union iaddr *ia)
+{
+#ifndef CONFIG_IPV6
+
+    SBUF_DEFINE(IADDR_NBUFS, 100);
+
+    assert(ia->sa.sa_family == AF_INET);
+
+    snprintf(SBUF, sizeof(SBUF), "[%s]:%d",
+            inet_ntoa(ia->sin.sin_addr),
+            ntohs(ia->sin.sin_port));
+
+#else /* CONFIG_IPV6 => we have adns_addr2text */
+
+    SBUF_DEFINE(IADDR_NBUFS, 1+ADNS_ADDR2TEXT_BUFLEN+20);
+
+    int port;
+
+    char *addrbuf = SBUF;
+    *addrbuf++ = '[';
+    int addrbuflen = ADNS_ADDR2TEXT_BUFLEN;
+
+    int r = adns_addr2text(&ia->sa, 0, addrbuf, &addrbuflen, &port);
+    if (r) {
+       const char fmt[]= "bad addr, error: %.*s";
+       sprintf(addrbuf, fmt,
+               (int)(ADNS_ADDR2TEXT_BUFLEN - sizeof(fmt)) /* underestimate */,
+               strerror(r));
+    }
+
+    char *portbuf = addrbuf;
+    int addrl = strlen(addrbuf);
+    portbuf += addrl;
+
+    snprintf(portbuf, sizeof(SBUF)-addrl, "]:%d", port);
+
+#endif /* CONFIG_IPV6 */
+
+    return SBUF;
+}
+
+bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib,
+                  bool_t ignoreport)
+{
+    if (ia->sa.sa_family != ib->sa.sa_family)
+       return 0;
+    switch (ia->sa.sa_family) {
+    case AF_INET:
+       return ia->sin.sin_addr.s_addr == ib->sin.sin_addr.s_addr
+           && (ignoreport ||
+              ia->sin.sin_port        == ib->sin.sin_port);
+#ifdef CONFIG_IPV6
+    case AF_INET6:
+       return !memcmp(&ia->sin6.sin6_addr, &ib->sin6.sin6_addr, 16)
+          &&  ia->sin6.sin6_scope_id  == ib->sin6.sin6_scope_id
+           && (ignoreport ||
+              ia->sin6.sin6_port      == ib->sin6.sin6_port)
+           /* we ignore the flowinfo field */;
+#endif /* CONFIG_IPV6 */
+    default:
+       abort();
+    }
+}
+
+int iaddr_socklen(const union iaddr *ia)
+{
+    switch (ia->sa.sa_family) {
+    case AF_INET:  return sizeof(ia->sin);
+#ifdef CONFIG_IPV6
+    case AF_INET6: return sizeof(ia->sin6);
+#endif /* CONFIG_IPV6 */
+    default:       abort();
+    }
+}
+
+const char *pollbadbit(int revents)
+{
+#define BADBIT(b) \
+    if ((revents & b)) return #b
+    BADBIT(POLLERR);
+    BADBIT(POLLHUP);
+    /* POLLNVAL is handled by the event loop - see afterpoll_fn comment */
+#undef BADBIT
+    return 0;
+}
+
+enum async_linebuf_result
+async_linebuf_read(struct pollfd *pfd, struct buffer_if *buf,
+                  const char **emsg_out)
+{
+    int revents=pfd->revents;
+
+#define BAD(m) do{ *emsg_out=(m); return async_linebuf_broken; }while(0)
+
+    const char *badbit=pollbadbit(revents);
+    if (badbit) BAD(badbit);
+
+    if (!(revents & POLLIN))
+       return async_linebuf_nothing;
+
+    /*
+     * Data structure: A line which has been returned to the user is
+     * stored in buf at base before start.  But we retain the usual
+     * buffer meaning of size.  So:
+     *
+     *   | returned :    | input read,   |    unused    |
+     *   |  to user : \0 |  awaiting     |     buffer   |
+     *   |          :    |  processing   |      space   |
+     *   |          :    |               |              |
+     *   ^base           ^start          ^start+size    ^base+alloclen
+     */
+
+    BUF_ASSERT_USED(buf);
+
+    /* firstly, eat any previous */
+    if (buf->start != buf->base) {
+       memmove(buf->base,buf->start,buf->size);
+       buf->start=buf->base;
+    }
+
+    uint8_t *searched=buf->base;
+
+    /*
+     * During the workings here we do not use start.  We set start
+     * when we return some actual data.  So we have this:
+     *
+     *   | searched     | read, might   |  unused      |
+     *   |  for \n      |  contain \n   |   buffer     |
+     *   |  none found  |  but not \0   |    space     |
+     *   |              |               |              |
+     *   ^base          ^searched       ^base+size     ^base+alloclen
+     *  [^start]                        ^dataend
+     *
+     */
+    for (;;) {
+       uint8_t *dataend=buf->base+buf->size;
+       char *newline=memchr(searched,'\n',dataend-searched);
+       if (newline) {
+           *newline=0;
+           buf->start=newline+1;
+           buf->size=dataend-buf->start;
+           return async_linebuf_ok;
+       }
+       searched=dataend;
+       ssize_t space=(buf->base+buf->alloclen)-dataend;
+       if (!space) BAD("input line too long");
+       ssize_t r=read(pfd->fd,searched,space);
+       if (r==0) {
+           *searched=0;
+           *emsg_out=buf->size?"no newline at eof":0;
+           buf->start=searched+1;
+           buf->size=0;
+           return async_linebuf_eof;
+       }
+       if (r<0) {
+           if (errno==EINTR)
+               continue;
+           if (iswouldblock(errno))
+               return async_linebuf_nothing;
+           BAD(strerror(errno));
+       }
+       assert(r<=space);
+       if (memchr(searched,0,r)) BAD("nul in input data");
+       buf->size+=r;
+    }
+
+#undef BAD
+}
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..a2ef828
--- /dev/null
+++ b/util.h
@@ -0,0 +1,268 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * secnet 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.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#ifndef util_h
+#define util_h
+
+#include "secnet.h"
+#include <gmp.h>
+
+#include "hackypar.h"
+
+#define BUF_ASSERT_FREE(buf) do { buffer_assert_free((buf), \
+                                                    __FILE__,__LINE__); } \
+while(0)
+#define BUF_ASSERT_USED(buf) do { buffer_assert_used((buf), \
+                                                    __FILE__,__LINE__); } \
+while(0)
+#define BUF_ALLOC(buf,own) do { buffer_assert_free((buf),__FILE__,__LINE__); \
+        (buf)->free=False; (buf)->owner=(own); (buf)->start=(buf)->base; \
+        (buf)->size=0; } while(0)
+#define BUF_FREE(buf) do { (buf)->free=True; } while(0)
+
+extern void buffer_assert_free(struct buffer_if *buffer, cstring_t file,
+                              int line);
+extern void buffer_assert_used(struct buffer_if *buffer, cstring_t file,
+                              int line);
+extern void buffer_new(struct buffer_if *buffer, int32_t len);
+extern void buffer_init(struct buffer_if *buffer, int32_t max_start_pad);
+extern void buffer_destroy(struct buffer_if *buffer);
+extern void buffer_copy(struct buffer_if *dst, const struct buffer_if *src);
+extern void *buf_append(struct buffer_if *buf, int32_t amount);
+extern void *buf_prepend(struct buffer_if *buf, int32_t amount);
+extern void *buf_unappend(struct buffer_if *buf, int32_t amount);
+extern void *buf_unprepend(struct buffer_if *buf, int32_t amount);
+
+/* These construct a message in a buffer, truncating if necessary.
+ * _string is only safe for trusted input and *not* UTF-8 (sorry).
+ * _packet_string is safe for any input, including untrusted.
+ * _terminate arranges for the buffer to be null-terminated (and
+ * maybe for a trailing `...' to indicate truncation), and returns
+ * a pointer to the null-terminated string. */
+void truncmsg_add_string(struct buffer_if *buf, cstring_t s);
+void truncmsg_add_packet_string(struct buffer_if*, int32_t, const uint8_t*);
+const char *truncmsg_terminate(const struct buffer_if *buf);
+
+
+struct priomsg {
+    /* U: uninitialised
+     * F: initialised but free (no memory allocated), no leak if discarded
+     * Z: contains no message yet
+     * M: contains some message; you may call truncmsg_add_*
+     */
+    int prio;
+    struct buffer_if m;
+};
+
+void priomsg_new(struct priomsg *pm, int32_t maxlen);          /* UF -> Z */
+void priomsg_destroy(struct priomsg *pm, int32_t maxlen);     /* FZM -> F */
+void priomsg_reset(struct priomsg *pm);                       /* FZM -> Z */
+bool_t priomsg_update_p(struct priomsg *pm, int prio);         /* ZM -> M */
+  /* returns true iff message of priority prio ought to be added,
+   * caller should then call truncmsg_add_*.
+   * pm may be NULL, in which case it just returns false */
+const char *priomsg_getmessage(const struct priomsg *pm, const char *defmsg);
+  /* return value is null-terminated, valid until next call
+   * or until defmsg is no longer valid                                ZM */
+
+bool_t priomsg_update_fixed(struct priomsg *pm, int prio, const char *m);
+  /* convenience combination of _update_p and truncmsg_add_string */
+
+/*
+ * void BUF_ADD_BYTES(append,    struct buffer_if*, const void*, int32_t size);
+ * void BUF_ADD_BYTES(prepend,   struct buffer_if*, const void*, int32_t size);
+ * void BUF_GET_BYTES(unappend,  struct buffer_if*,       void*, int32_t size);
+ * void BUF_GET_BYTES(unprepend, struct buffer_if*,       void*, int32_t size);
+ *     // all of these evaluate size twice
+ *
+ * void BUF_ADD_OBJ(append,    struct_buffer_if*, const OBJECT& something);
+ * void BUF_ADD_OBJ(prepend,   struct_buffer_if*, const OBJECT& something);
+ * void BUF_GET_OBJ(unappend,  struct_buffer_if*,       OBJECT& something);
+ * void BUF_GET_OBJ(unprepend, struct_buffer_if*,       OBJECT& something);
+ */
+#define BUF_ADD_BYTES(appendprepend, bufp, datap, size)                        \
+    (buf_un##appendprepend /* ensures we have correct direction */,    \
+     memcpy(buf_##appendprepend((bufp),(size)),(datap),(size)))
+#define BUF_ADD_OBJ(appendprepend, bufp, obj) \
+    BUF_ADD_BYTES(appendprepend,(bufp),&(obj),sizeof((obj)))
+#define BUF_GET_BYTES(unappendunprepend, bufp, datap, size)            \
+    (BUF_GET__DOESNOTEXIST__buf_un##unappendunprepend,                 \
+     memcpy((datap),buf_##unappendunprepend((bufp),(size)),(size)))
+#define BUF_GET_OBJ(unappendunprepend, bufp, obj) \
+    BUF_ADD_BYTES(unappendunprepend,&(obj),(bufp),sizeof((obj)))
+#define BUF_GET__DOESNOTEXIST__buf_ununappend  0
+#define BUF_GET__DOESNOTEXIST__buf_ununprepend 0
+
+static inline int32_t buf_remaining_space(const struct buffer_if *buf)
+{
+    return (buf->base + buf->alloclen) - (buf->start + buf->size);
+}
+
+extern void buffer_readonly_view(struct buffer_if *n, const void*, int32_t len);
+extern void buffer_readonly_clone(struct buffer_if *n, const struct buffer_if*);
+  /* Caller must only use unappend, unprepend et al. on n.
+   * New buffer state (in n) before this can be undefined.  After use,
+   * it must NOT be freed. */
+
+extern void buf_append_string(struct buffer_if *buf, cstring_t s);
+  /* Append a two-byte length and the string to the buffer. Length is in
+   * network byte order. */
+
+static inline int hex_encode_size(int binsize) { return binsize*2 + 1; }
+extern void hex_encode(const uint8_t *bin, int binsize, char *buf);
+  /* Convert a byte array to hex into a supplied buffer. */
+extern string_t hex_encode_alloc(const uint8_t *bin, int binsize);
+  /* Returns the result in a freshly allocated string. */
+
+extern bool_t hex_decode(uint8_t *buffer, int32_t buflen, int32_t *outlen,
+                        cstring_t hb, bool_t allow_odd_nibble);
+  /* Convert a hex string to binary, storing the result in buffer.  If
+   * allow_odd_nibble then it is acceptable if the input is an odd number of
+   * digits, and an additional leading zero digit is assumed; otherwise, this
+   * is not acceptable and the conversion fails.
+   *
+   * The input is processed left to right until it is consumed, the buffer is
+   * full, or an error is encountered in the input.  The length of output
+   * produced is stored in *outlen.  Returns true if the entire input was
+   * processed without error; otherwise false. */
+
+extern void read_mpbin(MP_INT *a, uint8_t *bin, int binsize);
+  /* Convert a buffer into its MP_INT representation */
+
+extern char *write_mpstring(MP_INT *a);
+  /* Convert a MP_INT into a hex string */
+
+extern int32_t write_mpbin(MP_INT *a, uint8_t *buffer, int32_t buflen);
+  /* Convert a MP_INT into a buffer; return length; truncate if necessary */
+
+extern struct log_if *init_log(list_t *loglist);
+
+extern void send_nak(const struct comm_addr *dest, uint32_t our_index,
+                    uint32_t their_index, uint32_t msgtype,
+                    struct buffer_if *buf, const char *logwhy);
+
+extern int consttime_memeq(const void *s1, const void *s2, size_t n);
+
+void hash_hash(const struct hash_if *hashi, const void *msg, int32_t len,
+              uint8_t *digest /* hi->hlen bytes */);
+
+const char *iaddr_to_string(const union iaddr *ia);
+int iaddr_socklen(const union iaddr *ia);
+
+void string_item_to_iaddr(const item_t *item, uint16_t port, union iaddr *ia,
+                         const char *desc);
+
+
+/*
+ * SBUF_DEFINE(int nbufs, size_t size);
+ *   // Generates a number of definitions and statements organising
+ *   // nbufs rotating char[size] buffers such that subsequent code
+ *   // may refer to:
+ * char *const SBUF;
+ */
+#define SBUF_DEFINE(nbufs, size)                       \
+    static int static_bufs__bufnum;                    \
+    static char static_bufs__bufs[(nbufs)][(size)];    \
+    static_bufs__bufnum++;                             \
+    static_bufs__bufnum %= (nbufs);                    \
+    static_bufs__bufs[static_bufs__bufnum]
+#define SBUF (static_bufs__bufs[static_bufs__bufnum])
+
+/*----- line-buffered asynch input -----*/
+
+enum async_linebuf_result {
+    async_linebuf_nothing,
+    async_linebuf_ok,
+    async_linebuf_eof,
+    async_linebuf_broken,
+};
+
+const char *pollbadbit(int revents); /* returns 0, or bad bit description */
+
+enum async_linebuf_result
+async_linebuf_read(struct pollfd *pfd, struct buffer_if *buf,
+                  const char **emsg_out);
+   /* Implements reading whole lines, asynchronously.  Use like
+    * this:
+    *   - set up the fd, which should be readable, O_NONBLOCK
+    *   - set up and initialise buffer, which should be big enough
+    *     for one line plus its trailing newline, and be empty
+    *     with start==base
+    *   - in your beforepoll_fn, be interested in POLLIN
+    *   - in your afterpoll_fn, repeatedly call this function
+    *     until it doesn't return `nothing'
+    *   - after you're done, simply close fd and free or reset buf
+    * State on return from async_linebuf_read depends on return value:
+    *
+    *   async_linebuf_nothing:
+    *
+    *      No complete lines available right now.  You should return
+    *      from afterpoll.  buf should be left untouched until the
+    *      next call to async_linebuf_read.
+    *
+    *   async_linebuf_ok:
+    *
+    *      buf->base contains a input line as a nul-terminated string
+    *      (\n replaced by \0); *emsg_out==0.  You must call
+    *      async_linebuf_read again before returning from afterpoll.
+    *
+    *   async_linebuf_eof:
+    *
+    *      EOF on stream.  buf->base contains any partial
+    *      (non-newline-terminated) line; *emsg_out!=0 iff there was
+    *      such a partial line.  You can call async_linebuf_read again
+    *      if you like but it will probably just return eof again.
+    *
+    *   broken:
+    *
+    *      Fatal problem (might be overly long lines, nuls in input
+    *      data, bad bits in pfd->revents, errors from read, etc.)
+    *
+    *      *emsg_out is the error message describing the problem;
+    *      this message might be stored in buf, might be from
+    *      strerror, or might be a constant.
+    *
+    *      You must not call async_linebuf_read again.  buf contents
+    *      is undefined: it is only safe to reset or free.
+    *
+    * While using this function, do not look at buf->start or ->size
+    * or anything after the first '\0' in buf.
+    *
+    * If you decide to stop reading with async_linebuf_read that's
+    * fine and you can reset or free buf, but you risk missing some
+    * read-but-not-reported data.
+    */
+
+/*----- some handy macros -----*/
+
+#define MINMAX(ae,be,op) ({                    \
+       typeof((ae)) a=(ae);                    \
+       typeof((be)) b=(be);                    \
+       a op b ? a : b;                         \
+    })
+#define MAX(a,b) MINMAX((a),(b),>)
+#define MIN(a,b) MINMAX((a),(b),<)
+
+#define MAX_RAW(a,b) ((a)>(b)?(a):(b))
+#define MIN_RAW(a,b) ((a)<(b)?(a):(b))
+
+static inline bool_t iswouldblock(int e)
+    { return e==EWOULDBLOCK || e==EAGAIN; }
+
+#endif /* util_h */