--- /dev/null
+;;; 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)))
-# subdirmk - subdirmk/.gitignore
-# Copyright 2019 Mark Wooding
-# Copyright 2019 Ian Jackson
-# SPDX-License-Identifier: LGPL-2.0-or-later
-
-#----- 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
+
+/config.log
+/config.h
+/config.status
+/config.stamp
+/config.stamp.in
+Makefile
+/common.make
+/test-common.make
+
+msgcode-test
+msgcode-test.confirm
+
+autom4te.cache
+
+*~
+TAGS
+
+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
--- /dev/null
+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.
--- /dev/null
+ 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>.
--- /dev/null
+Stephen Early <steve@greenend.org.uk> - original author
+Ian Jackson <ijackson@chiark.greenend.org.uk> - current maintainer
+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
--- /dev/null
+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
--- /dev/null
+# 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.0
+
+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)
+
+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:=version.o
+TEST_OBJECTS:=
+endif
+
+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) -c $< -o $@
+
+all: $(TARGETS) check
+
+# Automatic remaking of configuration files, from autoconf documentation
+${srcdir}/configure: configure.in
+ cd ${srcdir} && autoconf
+
+# autoheader might not change config.h.in, so touch a stamp file.
+${srcdir}/config.h.in: config.stamp.in
+${srcdir}/config.stamp.in: configure.in
+ cd ${srcdir} && autoheader
+ echo timestamp > ${srcdir}/config.stamp.in
+
+config.h: config.stamp
+config.stamp: config.h.in config.status
+ ./config.status
+
+Makefile: Makefile.in config.status
+ ./config.status
+
+config.status: configure
+ ./config.status --recheck
+# End of config file remaking rules
+
+# C and header file dependency rules
+SOURCES:=$(OBJECTS:.o=.c) $(TEST_OBJECTS:.o=.c)
+DEPENDS:=$(OBJECTS:.o=.d) $(TEST_OBJECTS:.o=.d)
+
+-include *.d
+
+# 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) 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 $(shell sed -n 's#^ref: #.git/#p' .git/HEAD)
+secnet: $(wildcard .git/packed-refs)
+endif
+
+TESTDIRS=stest mtest
+
+FAST_CHECKS= eax-aes-test.confirm eax-serpent-test.confirm \
+ eax-serpentbe-test.confirm check-ipaddrset \
+ $(addprefix check-,$(TESTDIRS))
+
+CHECKS += $(FAST_CHECKS)
+CHECKS += msgcode-test.confirm
+
+check: $(CHECKS)
+
+recheck:
+ rm -f $(FAST_CHECKS)
+ rm -rf $(addsuffix /d-*, $(TESTDIRS))
+ $(MAKE) check
+
+version.c: Makefile
+ 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 $@
+
+msgcode-test: msgcode-test.o
+ $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $^
+
+msgcode-test.confirm: msgcode-test
+ ./msgcode-test
+ touch $@
+
+check-ipaddrset: ipaddrset-test.py ipaddrset.py ipaddrset-test.expected
+ $(srcdir)/ipaddrset-test.py >ipaddrset-test.new
+ diff -u $(srcdir)/ipaddrset-test.expected ipaddrset-test.new
+
+check-stest: secnet test-example/sites.conf
+ $(MAKE) -C stest check
+
+check-mtest: make-secnet-sites $(PYMODULES)
+ $(MAKE) -C mtest check
+
+test-example/sites.conf:
+ $(MAKE) -C test-example
+
+.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) install
+
+clean: $(addprefix clean-,$(TESTDIRS))
+ $(RM) -f *.o *.yy.[ch] *.tab.[ch] $(TARGETS) core version.c
+ $(RM) -f *.d *.pyc *~ eax-*-test.confirm eax-*-test
+ $(RM) -rf __pycache__
+ $(RM) -f msgcode-test.confirm msgcode-test
+
+$(addprefix clean-,$(TESTDIRS)): clean-%:
+ $(MAKE) -C $* clean
+
+realclean: clean
+ $(RM) -f *~ Makefile config.h *.d \
+ config.log config.status config.cache \
+ config.stamp Makefile.bak
+
+distclean: realclean
+
+# Release checklist:
+#
+# 0. Use this checklist from Makefile.in
+#
+# 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 ~
--- /dev/null
+* 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.
--- /dev/null
+* 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.
-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).
-
-Basic approach
---------------
+secnet - flexible VPN software
-The developer is expected to write a makefile fragment, in each
-relevant subdirectory, called `Subdir.sd.mk'.
+* Copying
-These fragments may contain ordinary make language.
+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-2017 Mark Wooding
+ Copyright 1995-2013 Simon Tatham
-However, the sigil & is treated specially. By and large, it refers to
-`the current directory'. There are a variety of convenient
-constructions.
+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).
-The result is that to a large extent, the Subdir.sd.mk has an easy way
-to namespace its "local" make variables, and an easy way to refer to
-its "local" filenames.
+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.
-The Subdir.sd.mk's are filtered, fed through autoconf in the usual way
-(for @..@-substitutions) and included by one autogenerated toplevel
-makefile.
+Example:
-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.)
+a=1;
+b=2;
+c={ d=3; e=a; };
+f={ a=4; g=c; };
-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 Subdir.sd.mk
-can simply refer to files in other subdirectories directly.
-
-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/Subdir.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: simply
-mention (usually, by setting) the variable &TARGETS_zonk, or whatever.
-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 mentioned in src but not lib, `make zonk' in
-lib will fail. If you want to make a target exist everywhere,
-mention its name in Perdir.sd.mk (see below).
-
-Perdir.sd.mk, inclusion
------------------------
-
-The file Perdir.sd.mk in the toplevel of the source is automatically
-processed after each individual directory's Subdir.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 Perdir.sd.mk.
-
-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 Subdir.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'.
-
-Subdirectory 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 Subdir.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 Subdir.sd.mk
- Subdir and Perdir)
-
-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.
-
-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.
-
-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 TOP_
-&/ => sub/dir/ or nothing
-&=_ => sub_dir or TOP
-&=/ => sub/dir or .
-&^ => $(top_srcdir)/sub/dir or $(top_srcdir)
-&~ => $(abs_top_srcdir)/sub/dir or $(abs_top_srcdir)
-
-&& => && for convenience in shell runes
-\& => & general escaping mechanism
-
-& thing thing... &
-& ^ thing thing... &
-& ~ thing thing... &
- Convenience syntax for prefixing multiple filenames.
- Introduced by & followed by lwsp (space or tab).
- Each lwsp-separated non-ws word is prefixed by &/ &^/ &~/
- 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)
- args are processed for &
-
-&:include filename filename should usually be foo.sd.mk
-&:-include filename tolerate nonexistent file
- filenames are relative to $(top_srcdir)
-
-&!<lwsp> disables & until EOL (and then disappears)
-
-&# delete everything to end of line
- (useful if the RHS contains unrecognised & constructions)
-
-&!STUFF
- changes the escape sequence from & to literally STUFF
- STUFF may be any series of of non-whitespace characters,
- and is terminated by EOL or lwsp. &!STUFF and the lwsp
- are discarded.
-
- After this, write STUFF instead of &, everywhere.
- The effect is global and lasts until the next setting.
- It takes effect on &:include'd files too, so maybe set
- it back before using &:include.
-
- Notably
- STUFFSTUFF => STUFFSTUFF
- \STUFF => STUFF
- STUFF!& set escape back to &
-
-&TARGETS_things
- Handled specially. If mentioned, 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. But mentioning &TARGETS_things in
- a #-comment *does* work because the & filter does not care
- about comments.
-
- `all' is extra special: every directory has an `all'
- target, which corresponds to &TARGETS.
-
-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.
-
-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.
-
-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 Subdir.sd.mk in each directory. The toplevel one should
-probably contain:
-
- include subdirmk/usual.mk
- include subdirmk/regen.mk
-
-Write a Perdir.sd.mk in the toplevel, if you want. It should probably
-have:
-
- &:include subdirmk/cdeps.sd.mk
- &:include subdirmk/clean.sd.mk
-
-
-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.
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+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
--- /dev/null
+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
+])
+
+
--- /dev/null
+# 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.])
+ ])
+])
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+# 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)
#!/bin/sh
-# subdirmk, autogen.sh (conventional autoconf invocation script)
-# Copyright 2019 Ian Jackson
-# SPDX-License-Identifier: LGPL-2.0-or-later
+#
+# 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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*/
--- /dev/null
+# 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@
+
+SHELL:=/bin/sh
+RM:=@RM@
+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 \
+ -MMD
--- /dev/null
+/* 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);
+}
--- /dev/null
+/*
+ * 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;
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+%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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/* config.h.in. Generated from configure.in 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 */
+
--- /dev/null
+#! /bin/sh
+# From configure.in 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
+RM
+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'
+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_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'
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; 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_RM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $RM in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_RM="$RM" # 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_RM="$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
+RM=$ac_cv_path_RM
+if test -n "$RM"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5
+$as_echo "$RM" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+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 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
+
+ac_config_files="$ac_config_files Makefile common.make test-common.make test-example/Makefile stest/Makefile mtest/Makefile"
+
+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
+_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" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "common.make") CONFIG_FILES="$CONFIG_FILES common.make" ;;
+ "test-common.make") CONFIG_FILES="$CONFIG_FILES test-common.make" ;;
+ "test-example/Makefile") CONFIG_FILES="$CONFIG_FILES test-example/Makefile" ;;
+ "stest/Makefile") CONFIG_FILES="$CONFIG_FILES stest/Makefile" ;;
+ "mtest/Makefile") CONFIG_FILES="$CONFIG_FILES mtest/Makefile" ;;
+ "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
+
+
+
+
+
--- /dev/null
+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)
+
+AC_INIT(secnet,0.1.18+,secnet@chiark.greenend.org.uk)
+AC_CONFIG_SRCDIR(secnet.c)
+AC_CONFIG_HEADER(config.h)
+
+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_PATH_PROG(RM,rm)
+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
+
+AC_OUTPUT(Makefile common.make test-common.make
+ test-example/Makefile stest/Makefile mtest/Makefile,
+ 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 */
+])
--- /dev/null
+secnet (0.5.1~) unstable; urgency=medium
+
+ *
+
+ --
+
+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
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+RUN_SECNET=no
--- /dev/null
+#! /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
--- /dev/null
+#!/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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+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
--- /dev/null
+/*
+ * 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"
--- /dev/null
+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
--- /dev/null
+#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"
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+# 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
--- /dev/null
+/* 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*/
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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*/
--- /dev/null
+#!/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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+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
--- /dev/null
+#!/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))
--- /dev/null
+"""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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/* 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 */
--- /dev/null
+#! /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 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
+ 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 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 c in self.children.values():
+ 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
+prefix=''
+
+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)
--- /dev/null
+/*
+ * 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");
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+
+VPATH:=@srcdir@
+srcdir:=@srcdir@
+topdir:=@top_srcdir@
+
+DEPS += $(topdir)/make-secnet-sites
+DEPS += $(topdir)/ipaddrset.py
+DEPS += $(srcdir)/common.tcl
+
+TESTDIR=mtest
+
+include ../test-common.make
+
+check: check-real
--- /dev/null
+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-output {expected got suffix} {
+ global seddery
+ global tmp
+ exec bash -c "
+ diff -u <($seddery mtest/$expected$suffix) \\
+ <($seddery $tmp/$got$suffix )
+ "
+}
+
+file mkdir $tmp/groupfiles
+
+set env(PYTHONHASHSEED) 0
+set env(PYTHONBYTECODEBASE) 0
+
+set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' }
+
+prefix_some_path PYTHONPATH .
--- /dev/null
+location outside Goutside
+restrict-nets 172.18.232.0/29
+
+location inside Ginside
+restrict-nets 172.18.232.8/29
--- /dev/null
+# secnet sites file autogenerated by make-secnet-sites version 0.1.18
+# Sun Oct 20 13:55:05 2019
+# Command line: ./make-secnet-sites test-example/sites ./mtest/d-basic/out.conf
+
+vpn-data {
+ test-example {
+ # restrict-nets "172.18.232.0/28"
+ hash sha1;
+ dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2");
+ setup-timeout 2000;
+ setup-retries 5;
+ # Contact email address: <devnull@example.com>
+ key-lifetime 72000000;
+
+ out {
+ outside {
+ name "test-example/out/outside";
+ key rsa-public("65537","129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941");
+ address "[::1]"; port 16900;
+ link netlink {
+ routes "172.18.232.0/29";
+ ptp-address "172.18.232.1";
+ };
+ };
+ };
+ in {
+ inside {
+ name "test-example/in/inside";
+ key rsa-public("65537","130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669");
+ mobile True;
+ address "[127.0.0.1]"; port 16910;
+ link netlink {
+ routes "172.18.232.8/29";
+ ptp-address "172.18.232.9";
+ };
+ };
+ };
+ };
+};
+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;
--- /dev/null
+# 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
--- /dev/null
+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
+
--- /dev/null
+#! /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' }
+exec bash -c "
+ diff -u <($seddery mtest/e-basic.conf) \\
+ <($seddery $tmp/out.conf )
+"
--- /dev/null
+#! /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"
+ }
+ }
+}
+
--- /dev/null
+/* 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#!/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 $!;
+}
--- /dev/null
+/* 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 */
+}
--- /dev/null
+/*
+ * 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(®istered,s->signum)) return;
+ sigaddset(®istered,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(®istered);
+ 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+--- -*-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 --------------------------------------------------
--- /dev/null
+.\" 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)
--- /dev/null
+/*
+ * 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(®);
+
+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(®, 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, ®, 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, ®, 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+#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 */
--- /dev/null
+#define SERPENT_BIGENDIAN
+#include "serpent.c"
--- /dev/null
+/*
+ * 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
--- /dev/null
+#!/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"
--- /dev/null
+/*
+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");
+ }
+ }
+}
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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 *****/
--- /dev/null
+/* 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);
+}
--- /dev/null
+
+VPATH:=@srcdir@
+srcdir:=@srcdir@
+topdir:=@top_srcdir@
+
+TARGETS += udp-preload.so
+
+DEPS += udp-preload.so
+DEPS += $(srcdir)/common.tcl
+DEPS += ../secnet
+DEPS += ../test-example/sites.conf
+
+TESTDIR=stest
+
+include ../test-common.make
+
+CFLAGS += -D_REENTRANT -fPIC
+
+udp-preload.so: udp-preload.o
+ $(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:
+ env -u MAKEFLAGS -u MFLAGS \
+ $(MAKE_NOTSPECIAL) -j$(shell nproc || 1)0 check-real
--- /dev/null
+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
+}
--- /dev/null
+#! /usr/bin/tclsh
+
+source stest/common.tcl
+
+test-kex
--- /dev/null
+#! /usr/bin/tclsh
+
+# Dyamic key rollover config on inside only
+
+source stest/common.tcl
+
+test-kex
--- /dev/null
+/*
+ * 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 char *sun_prep(struct sockaddr_un *sun) {
+ 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 + ADDRPORTSTRLEN + 1 > sizeof(sun->sun_path)) {
+ errno=ENAMETOOLONG; return 0;
+ }
+ strcpy(sun->sun_path,dir);
+ char *p=sun->sun_path+dl;
+ *p++='/';
+ return p;
+}
+
+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 *p=sun_prep(&sun);
+ if (addrport2str(p,addr,addrlen)) 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;
+ char *p=sun_prep(&sun);
+ strcpy(p,leaf);
+
+ 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;
+}
--- /dev/null
+# subdirmk - subdirmk/.gitignore
+# Copyright 2019 Mark Wooding
+# Copyright 2019 Ian Jackson
+# SPDX-License-Identifier: LGPL-2.0-or-later
+
+#----- subdirmk-generated -----
+/regen.mk
+/usual.mk
--- /dev/null
+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).
+
+Basic approach
+--------------
+
+The developer is expected to write a makefile fragment, in each
+relevant subdirectory, called `Subdir.sd.mk'.
+
+These fragments may contain ordinary make language.
+
+However, the sigil & is treated specially. By and large, it refers to
+`the current directory'. There are a variety of convenient
+constructions.
+
+The result is that to a large extent, the Subdir.sd.mk has an easy way
+to namespace its "local" make variables, and an easy way to refer to
+its "local" filenames.
+
+The Subdir.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 Subdir.sd.mk
+can simply refer to files in other subdirectories directly.
+
+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/Subdir.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: simply
+mention (usually, by setting) the variable &TARGETS_zonk, or whatever.
+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 mentioned in src but not lib, `make zonk' in
+lib will fail. If you want to make a target exist everywhere,
+mention its name in Perdir.sd.mk (see below).
+
+Perdir.sd.mk, inclusion
+-----------------------
+
+The file Perdir.sd.mk in the toplevel of the source is automatically
+processed after each individual directory's Subdir.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 Perdir.sd.mk.
+
+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 Subdir.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'.
+
+Subdirectory 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 Subdir.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 Subdir.sd.mk
+ Subdir and Perdir)
+
+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.
+
+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.
+
+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 TOP_
+&/ => sub/dir/ or nothing
+&=_ => sub_dir or TOP
+&=/ => sub/dir or .
+&^ => $(top_srcdir)/sub/dir or $(top_srcdir)
+&~ => $(abs_top_srcdir)/sub/dir or $(abs_top_srcdir)
+
+&& => && for convenience in shell runes
+\& => & general escaping mechanism
+
+& thing thing... &
+& ^ thing thing... &
+& ~ thing thing... &
+ Convenience syntax for prefixing multiple filenames.
+ Introduced by & followed by lwsp (space or tab).
+ Each lwsp-separated non-ws word is prefixed by &/ &^/ &~/
+ 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)
+ args are processed for &
+
+&:include filename filename should usually be foo.sd.mk
+&:-include filename tolerate nonexistent file
+ filenames are relative to $(top_srcdir)
+
+&!<lwsp> disables & until EOL (and then disappears)
+
+&# delete everything to end of line
+ (useful if the RHS contains unrecognised & constructions)
+
+&!STUFF
+ changes the escape sequence from & to literally STUFF
+ STUFF may be any series of of non-whitespace characters,
+ and is terminated by EOL or lwsp. &!STUFF and the lwsp
+ are discarded.
+
+ After this, write STUFF instead of &, everywhere.
+ The effect is global and lasts until the next setting.
+ It takes effect on &:include'd files too, so maybe set
+ it back before using &:include.
+
+ Notably
+ STUFFSTUFF => STUFFSTUFF
+ \STUFF => STUFF
+ STUFF!& set escape back to &
+
+&TARGETS_things
+ Handled specially. If mentioned, 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. But mentioning &TARGETS_things in
+ a #-comment *does* work because the & filter does not care
+ about comments.
+
+ `all' is extra special: every directory has an `all'
+ target, which corresponds to &TARGETS.
+
+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.
+
+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.
+
+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 Subdir.sd.mk in each directory. The toplevel one should
+probably contain:
+
+ include subdirmk/usual.mk
+ include subdirmk/regen.mk
+
+Write a Perdir.sd.mk in the toplevel, if you want. It should probably
+have:
+
+ &:include subdirmk/cdeps.sd.mk
+ &:include subdirmk/clean.sd.mk
+
+
+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+.)
--- /dev/null
+#!/bin/sh
+# subdirmk, autogen.sh (conventional autoconf invocation script)
+# Copyright 2019 Ian Jackson
+# SPDX-License-Identifier: LGPL-2.0-or-later
+set -e
+cd ${0%/*}
+autoconf
--- /dev/null
+
+include ../common.make
+
+TESTSCRIPTS ?= $(shell echo $(srcdir)/t-*[0-9a-z])
+TESTNAMES := $(patsubst t-%,%,$(notdir $(TESTSCRIPTS)))
+
+DEPS += $(topdir)/test-common.tcl
+DEPS += $(topdir)/common.make
+DEPS += $(topdir)/test-common.make
+DEPS += Makefile
+
+TARGETS += check
+
+export PYTHONBYTECODEBASE=/dev/null
+
+all: $(TARGETS)
+
+check-real: $(foreach t,$(TESTNAMES),d-$t/ok)
+
+d-%/ok: $(srcdir)/t-% $(DEPS)
+ @rm -rf d-$*; mkdir d-$*
+ @export SECNET_TEST_BUILDDIR=$(topbuilddir); \
+ cd $(topdir) && \
+ $(TESTDIR)/t-$* >$(topbuilddir)/$(TESTDIR)/d-$*/log 2>&1 \
+ || { cat $(topbuilddir)/$(TESTDIR)/d-$*/log >&2; false; }
+ @printf "$(TESTDIR)/$* "
+ @touch $@
+
+clean:
+ $(RM) -f *.o *.so
+ $(RM) -rf tmp
+ $(RM) -rf d-*
--- /dev/null
+
+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
+}
--- /dev/null
+TARGETS=sites.conf inside.key outside.key
+
+VPATH:=@srcdir@
+include ../common.make
+srcdir:=@srcdir@
+topdir:=@top_srcdir@
+
+all: $(TARGETS)
+
+%.key: %.key.b64
+ base64 -d <$< >$@.new && mv -f $@.new $@
+
+sites.conf: $(topdir)/make-secnet-sites $(srcdir)/sites Makefile
+ $(topdir)/make-secnet-sites $(srcdir)/sites sites.conf
+
+clean:
+ rm -f *~ ./#*# *.new $(TARGETS)
--- /dev/null
+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.
--- /dev/null
+/*
+ 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);
+}
--- /dev/null
+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);
--- /dev/null
+#!/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 "$@"
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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==
--- /dev/null
+1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com
--- /dev/null
+{
+ secnet_read_conffile
+ Memcheck:Leak
+ ...
+ fun:read_conffile
+ fun:main
+}
+{
+ secnet_enter_phase
+ Memcheck:Leak
+ ...
+ fun:enter_phase
+ fun:main
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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=
--- /dev/null
+1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com
--- /dev/null
+#!/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;
+}
--- /dev/null
+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
--- /dev/null
+/* 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
+}
--- /dev/null
+/*
+ * 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*/
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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);
+}
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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 */