--- /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
+Socket6bed4 README
+==================
+
+Socket6bed4 is a Java package that introduces an IPv6 tunnel as a socket subclass.
+There is no need to configure anything, just adapt your datagram factory to the
+new tunnel and you will be able to use IPv6.
+
+
+Purpose
+-------
+
+Java is used in many places, including some that will remain IPv4-only for years
+to come. The 6bed4 tunnel is a zero-config tunnel that eliminates dependency on
+your ISP so you can assume that IPv6 is always available. Or better even, you
+can distribute Java software that is IPv6-only by using 6bed4 as a fallback for
+users that don't have native IPv6 yet.
+
+
+About 6bed4
+-----------
+
+The design of 6bed4 is intended to support zero-config operation. You are welcome
+to setup your private 6bed4 server though; it will still be able to communicate
+with the rest of the IPv6 world, native as well as 6bed4.
+
+The design of 6bed4 also explicitly supports peer-to-peer communication. It will
+attempt to setup direct links between clients, which are therefore referred to as
+peers instead of clients. The tunnel server is a fallback service, and will only
+be used in practice if a symmetric NAT is used anywhere between the peers. This
+means that 6bed4 is suitable for realtime media streaming between directly
+connected peers. The disadvantages of IPv4 are not transferred to IPv6 through
+this tunnel mechanism!
+
+Until 6bed4 is a formal IETF standard, something that we are indeed working
+towards, there is a serious chance that the addresses used will change in the
+future. This means that you should keep an eye on such changes and keep the
+software using 6bed4 up to date. To help you with that, announcements of
+such an infrastructural nature are sent to an extremely low-traffic list,
+
+https://lists.sourceforge.net/lists/listinfo/tun6bed4-infra
+
+More information about the 6bed4 tunnel is concentrated at
+
+http://devel.0cpm.org/6bed4
+
+Specifically for this module, you will find information on
+
+http://devel.0cpm.org/6bed4/java-socket6bed4
+
+
+Using Socket6bed4 on a peer or client
+-------------------------------------
+
+We prefer to speak of 6bed4 endpoints as peers, rather than clients. This is to
+emphasise that they are normally connected to each other, instead of to an
+intermediate server.
+
+Peer use of 6bed4 is straightforward. You should adapt the code that generates
+your DatagramSocket to create an instance of nl.openfortress.DatagramSocket6bed4
+instead. That's all. If you are already using a factory, you have to do this in
+one place only.
+
+If you have access to a native IPv6 address, you probably want to prefer using
+that. We will not enforce that however -- as we can imagine that you would want
+to run native IPv6 and 6bed4 in parallel, to approach other 6bed4 peers in
+parallel with yours.
+
+
+Using Socket6bed4 on a server
+-----------------------------
+
+Every run of Java will normally create a different IPv6 address. This address
+contains the external IPv4 address and UDP port for use with the server. These
+will change every restart of the Socket6bed4 stack, so a server-side address
+would not be fixed. This may present a problem; let us know if this is important
+for you.
+
+
+Feedback
+--------
+
+We would like to hear what you use Socket6bed4 for.
+
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>socket6bed4</groupId>
+ <artifactId>nl.openfortress.socket6bed4</artifactId>
+ <version>0.0</version>
+ <packaging>jar</packaging>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+
+<!--
+ <execution>
+ <id>jar-with-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>net.AbstractSIPlet</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+-->
+
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+<!--
+ <source>1.6</source>
+ <target>1.6</target>
+-->
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9</version>
+<!--
+ <configuration>
+ ...
+ </configuration>
+-->
+ </plugin>
+ </plugins>
+ </reporting>
+
+
+</project>
--- /dev/null
+package nl.openfortress.socket6bed4;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import java.util.Hashtable;
+
+
+/** The ConnectionPool links IPv4 sockets for 6bed4 with the implementations
+ * of IPv6 sockets. Each IPv4 socket may have multiple IPv6 sockets, as
+ * a result of these variation factors:
+ * - the router supplies a range of IPv6 addresses
+ * - the IPv6 DatagramSockets can listen to multiple ports
+ * - the IPv6 DatagramSockets could receive from multiple ports
+ *
+ * Incoming traffic is mapped based on an InetSocketAddress; if an exact
+ * match is not available, then the zero address is tried instead.
+ *
+ * The sender information is stored in a Datagrampacket's SocketAddress.
+ */
+public class ConnectionPool {
+
+ protected final static byte serveripbytes[] = { (byte) 145, (byte) 136, 0, 1 };
+ protected final static int serverport = 25788;
+ protected InetSocketAddress default_server;
+ protected Hashtable<InetSocketAddress,DatagramSocket6bed4> clients;
+ protected Hashtable<InetSocketAddress,ServerNode > servers;
+ protected Hashtable<Inet6Address,InetSocketAddress > addressmap;
+
+
+ /** The connection pool is normally a shared resource,
+ * although private instances are certainly possible.
+ */
+ private static ConnectionPool pool;
+
+ /** Create the shared connection pool when loading this class.
+ */
+ static {
+ try {
+ pool = new ConnectionPool ();
+ } catch (SocketException se) {
+ throw new RuntimeException ("Failed to create initial default ConnectionPool");
+ }
+ }
+
+ /** Return the connection pool intended to be shared.
+ */
+ public static ConnectionPool getSharedConnectionPool () {
+ return pool;
+ }
+
+
+ /** Start using a server, possibly introducing it.
+ * Return the ServerNode that is herewith opened.
+ */
+ public ServerNode openServer (InetSocketAddress isa)
+ throws UnknownHostException, SocketException {
+ if (!(isa.getAddress () instanceof Inet4Address)) {
+ throw new UnknownHostException ("openServer() must use an IPv4 address");
+ }
+ synchronized (servers) {
+ ServerNode srv = servers.get (isa);
+ if (srv == null) {
+ srv = new ServerNode (isa);
+ servers.put (isa, srv);
+ srv.useMore ();
+ addressmap.put (srv.getShared6bed4Address (), isa); //TODO:BOOTSTRAP LOCKUP -- NO SHARED ADDRESS UNTIL MAINTAINER STARTS
+ } else {
+ srv.useMore ();
+ }
+ return srv;
+ }
+ }
+
+ /** Stop using a server, possibly removing it.
+ */
+ public void closeServer (InetSocketAddress isa)
+ throws UnknownHostException {
+ if (!(isa.getAddress () instanceof Inet4Address)) {
+ throw new UnknownHostException ("closeServer() must use an IPv4 address");
+ }
+ synchronized (servers) {
+ ServerNode srv = servers.get (isa);
+ if (srv == null) {
+ throw new UnknownHostException ("Cannot close a new server");
+ }
+ if (srv.useLess ()) {
+ servers.remove (isa);
+ addressmap.remove (srv.getShared6bed4Address ());
+ }
+ }
+ }
+
+ /** Return the default 6bed4 tunnel server.
+ */
+ public InetSocketAddress getDefaultServer () {
+ return default_server;
+ }
+
+ /** Return the ServerNode for the default 6bed4 tunnel server.
+ */
+ public ServerNode getDefaultServerNode () {
+ return servers.get (default_server);
+ }
+
+ /** Return the ServerNode for a given InetSocketAddress,
+ * holding the server's IPv4 address and UDP port.
+ * Returns null if not found.
+ * TODO: WHAT IS THE PURPOSE HERE?!?
+ */
+ public ServerNode getServerNode (InetSocketAddress isa) {
+ synchronized (servers) {
+ return servers.get (isa);
+ }
+ }
+
+ /** Returns the ServerNode for a given InetAddress, which
+ * is then assumed to match an Inet6Address in the
+ * hash tables.
+ * Returns null if not found.
+ */
+ public ServerNode getServerNode (InetAddress ia) {
+ InetSocketAddress isa = addressmap.get (ia);
+ if (isa == null) {
+ return null;
+ }
+ return servers.get (isa);
+ }
+
+ /** Change the server that counts as the default tunnel server
+ * for future connection attempts. Existing connections are not
+ * influenced.
+ */
+ public void setDefaultServer (InetSocketAddress isa)
+ throws UnknownHostException, SocketException {
+ if (!(isa.getAddress () instanceof Inet4Address)) {
+ throw new UnknownHostException ("setDefaultServer() must use an IPv4 address");
+ }
+ try {
+ synchronized (servers) {
+ closeServer (default_server);
+ default_server = isa;
+ openServer (default_server);
+ }
+ } catch (UnknownHostException uhe) {
+ throw new RuntimeException ("UseCount error for default 6bed4 tunnel server");
+ }
+ }
+
+ /** Construct a ServerPool with nothing but the default server.
+ * There are no clients yet, of course.
+ */
+ public ConnectionPool ()
+ throws SocketException {
+ clients = new Hashtable<InetSocketAddress,DatagramSocket6bed4> ();
+ servers = new Hashtable<InetSocketAddress,ServerNode > ();
+ addressmap = new Hashtable<Inet6Address,InetSocketAddress > ();
+ try {
+ default_server = new InetSocketAddress (InetAddress.getByAddress (serveripbytes), serverport);
+ } catch (UnknownHostException uhe) {
+ throw new RuntimeException ("Failed to parse IPv4 address of 6bed4 default server");
+ }
+ try {
+ openServer (default_server);
+ } catch (UnknownHostException uhe) {
+ throw new RuntimeException ("Failed to initialize default 6bed4 tunnel server");
+ }
+ }
+
+}
+
--- /dev/null
+package nl.openfortress.socket6bed4;
+
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.DatagramSocketImpl;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.io.IOException;
+
+import java.util.Arrays;
+import java.nio.ByteBuffer;
+
+
+/** Socket6bed4 describes sockets that are run over IPv6. This means
+ * that a remote IPv6 address can be contacted when there are only
+ * IPv4 addresses available locally.
+ *
+ * A Socket6bed4 can switch between IPv4 transports; it can either send
+ * to a 6bed4 server, or directly to the targeted peer. Either path is
+ * acceptable as. The Socket can arrange this automatically, using
+ * peering attempts through Neighbor Discovery.
+ *
+ * Perhaps it's a bit silly to make a 6bed4 DatagramSocket a subclass
+ * of a plain DatagramSocket. It is very practical however; it means
+ * that these objects can be substituted anywhere, without question.
+ */
+public class DatagramSocket6bed4 extends DatagramSocket {
+
+ protected static DatagramSocket server_ipv4socket;
+ protected DatagramSocket ipv4socket;
+ protected InetSocketAddress cnx6sa = null;
+ protected InetSocketAddress my6sa = null;
+ protected ServerNode my6sn = null;
+ protected int ephemeral = 3210;
+ static final byte prephdr_payload [] = { 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, Utils.IPPROTO_UDP, 64 /*HopLimit*/ };
+ protected byte prephdr_udp [] = new byte [8];
+
+ /* Tell this connection to switch back to the default server,
+ * skipping any direct route.
+ */
+ public void useDefaultServer () {
+ ipv4socket = server_ipv4socket;
+ }
+
+ /* Set the IPv4 address and UDP port of the default server,
+ * as shared by all sockets. Note that sockets will not
+ * move over immediately. Invoke useDefaultServer() if this
+ * is needed.
+ */
+ public void setDefaultServer (Inet4Address server4, int port4)
+ throws SocketException {
+ DatagramSocket sox = new DatagramSocket ();
+ sox.connect (server4, port4);
+ server_ipv4socket = sox;
+ }
+
+
+ /** Return currect remote address */
+ public InetAddress getInetAddress () {
+ return (cnx6sa != null)? cnx6sa.getAddress (): null;
+ }
+
+ /** Return currect local port */
+ public int getPort () {
+ return (cnx6sa != null)? cnx6sa.getPort (): 0;
+ }
+
+ /** Return remote sock address */
+ public InetSocketAddress getRemoteSocketAddress () {
+ return cnx6sa;
+ }
+
+ /** Return local address */
+ public InetAddress getLocalAddress () {
+ return my6sa.getAddress ();
+ }
+
+ /** Return local port */
+ public int getLocalPort () {
+ return my6sa.getPort ();
+ }
+
+ /** Return bound local socket address */
+ public InetSocketAddress getLocalSocketAddress () {
+ return my6sa;
+ }
+
+ public void bind (int port)
+ throws SocketException {
+ //
+ // Verify if binding request is properly formed
+ if ((port <= 0) || (port > 65535)) {
+ throw new SocketException ("Binding is only possible to ports from 1 to 65535");
+ }
+ //TODO// Checks against Router Advertisement
+ //
+ // Perform the actual binding, and unbind old
+ ConnectionPool pool = ConnectionPool.getSharedConnectionPool ();
+ my6sn = pool.getDefaultServerNode ();
+ synchronized (this) {
+ my6sn.registerDatagramClient (port);
+ if (my6sa != null) {
+ my6sn.unregisterDatagramClient (my6sa.getPort ());
+ }
+ prephdr_udp [0] = (byte) (port >> 8);
+ prephdr_udp [1] = (byte) (port & 0x00ff);
+ my6sa = new InetSocketAddress (my6sn.getShared6bed4Address (), port);
+ }
+ }
+
+ /** Attempt to bind to an address and/or port, exercising
+ * 6bed4 constraints as they arise from the router advertisement.
+ */
+ public void bind (InetSocketAddress sa)
+ throws SocketException {
+ if (sa == null) {
+ //
+ // Iterate over ports until one is found to be free
+ for (int i=0; i < 4095; i++) {
+ ephemeral = (ephemeral + 1) & 0x0ffff;
+ try {
+ bind (ephemeral + 49152);
+ return; /* No complaints? Than break out of the loop */
+ } catch (SocketException se) {
+ /* Taken? Then continue trying */
+ }
+ }
+ throw new SocketException ("No ephemeral ports are free on the 6bed4 address");
+
+ } else {
+ bind (sa.getPort ());
+ }
+ }
+
+ /** Are we bound? */
+ public boolean isBound () {
+ return my6sa != null;
+ }
+
+ /** Connect to a remote IPv6 address and UDP port. */
+ public void connect (InetAddress address, int port) {
+ connect (new InetSocketAddress (address, port));
+ }
+ public void connect (InetSocketAddress addr) {
+ cnx6sa = (InetSocketAddress) addr;
+ prephdr_udp [2] = (byte) (cnx6sa.getPort () >> 8);
+ prephdr_udp [3] = (byte) (cnx6sa.getPort () & 0x00ff);
+
+ }
+
+ /** Disconnect from a remote IPv6 address and UDP port. */
+ public void disconnect () {
+ cnx6sa = null;
+ }
+
+ /** Are we connected? */
+ public boolean isConnected () {
+ return cnx6sa != null;
+ }
+
+
+ /** The interface for DatagramPackets conceals the UDP layer
+ * underlaying the actual data exchanged. For 6bed4 however,
+ * a few headers will have to be prefixed. These are the
+ * IPv6 header and UDP-over-IPv6 header. The underlying
+ * DatagramSocket for IPv4 will conceal the UDP-over-IPv4.
+ *
+ * Details about playful hints are described in the class
+ * NeighborCache. In general, the hint should only be given
+ * for packets that will be re-sent upon failure, and whose
+ * success of delivery can be reported back. This can then
+ * be used to setup direct connections to peers without
+ * explicit negotiation through Neighbor Discovery. You can
+ * safely set the playful flag on all similar traffic, as it
+ * will only influence on initial attempts at traffic. A good
+ * efficiency trade-off is to use playful hints only on
+ * initiating UDP messages, such as a SIP INVITE.
+ */
+ public void send_playful (DatagramPacket pkt6, boolean playful)
+ throws IOException {
+ int pkt6len = pkt6.getLength ();
+ ByteBuffer buf = ByteBuffer.allocate (Utils.OFS_UDP6_PLOAD + pkt6len);
+ buf.put (prephdr_payload);
+ buf.put ( my6sa.getAddress ().getAddress ());
+ Inet6Address remote;
+ if (cnx6sa != null) {
+ remote = (Inet6Address) cnx6sa.getAddress ();
+ } else {
+ remote = (Inet6Address) pkt6.getAddress ();
+ }
+ buf.put (remote.getAddress ());
+ buf.put (prephdr_udp);
+ buf.put (pkt6.getData (), pkt6.getOffset (), pkt6len);
+ byte[] tundata = buf.array ();
+ tundata [Utils.OFS_IP6_PLEN + 0] =
+ tundata [Utils.OFS_UDP6_PLEN + 0] = (byte) ((pkt6len + 8) >> 8);
+ tundata [Utils.OFS_IP6_PLEN + 1] =
+ tundata [Utils.OFS_UDP6_PLEN + 1] = (byte) ((pkt6len + 8) & 0x00ff);
+ if (cnx6sa == null) {
+ int rport = pkt6.getPort ();
+ tundata [Utils.OFS_UDP6_DSTPORT + 0] = (byte) (rport >> 8);
+ tundata [Utils.OFS_UDP6_DSTPORT + 1] = (byte) (rport & 0x00ff);
+ }
+ int csum = Utils.checksum_udpv6 (tundata);
+ tundata [Utils.OFS_UDP6_CSUM + 0] = (byte) (csum >> 8);
+ tundata [Utils.OFS_UDP6_CSUM + 1] = (byte) (csum & 0x00ff);
+ //TODO// Shared and locked send4 instead of local pkt4
+ DatagramPacket pkt4 = new DatagramPacket (tundata, 0, tundata.length);
+ pkt4.setSocketAddress (my6sn.lookup_neighbor ((Inet6Address) pkt6.getAddress (), playful));
+ my6sn.send (pkt4);
+ }
+
+
+ /** Receive a packet from the underlying IPv4 layer. As with
+ * sending, the UDP underneath the packet is hidden, but for
+ * 6bed4 the IPv6 and UDP-over-IPv6 headers must be stripped
+ * and interpreted. Only traffic destined for our own
+ * combination of IPv6 address and UDP-over-IPv6 port will be
+ * passed on to us.
+ *
+ * The hint returned indicates if the datagram was received
+ * directly from the peer. This information may be of varying
+ * use to implementations, but it would be specifically useful
+ * for confirmation of playfully sent packets, as described
+ * for the send_playful () method. To this end, the method
+ * acknowledge_playful () is used to complete the cycle of
+ * optimistic direct connections to peers.
+ */
+ public boolean receive_playful (DatagramPacket pkt6)
+ throws IOException {
+ byte msg[] = my6sn.receive_datagram (my6sa.getPort (), getSoTimeout ());
+ if (msg == null) {
+ throw new SocketTimeoutException ("No data available within timeout");
+ }
+ int len = Utils.fetch_net16 (msg, Utils.OFS_IP6_PLEN) + Utils.OFS_IP6_PLOAD;
+ if (len < Utils.OFS_UDP6_PLOAD) {
+ throw new IOException ("Datagram packet with silly short size received over 6bed4 tunnel");
+ }
+ if (Utils.checksum_udpv6 (msg, 0) != Utils.fetch_net16 (msg, Utils.OFS_UDP6_CSUM)) {
+ throw new IOException ("Datagram packet with faulty checksum received over 6bed4 tunnel");
+ }
+ if (Utils.fetch_net16 (msg, Utils.OFS_UDP6_PLEN) + Utils.OFS_IP6_PLOAD > len) {
+ throw new IOException ("Incomplete datagram received over 6bed4 tunnel");
+ }
+ pkt6.setAddress ((Inet6Address) InetAddress.getByAddress (Arrays.copyOfRange (msg, Utils.OFS_IP6_SRC, Utils.OFS_IP6_SRC + 16)));
+ pkt6.setPort (Utils.fetch_net16 (msg, Utils.OFS_UDP6_SRCPORT));
+ // PROBABLY FORMALLY CORRECT, BUT NOT HOW PEOPLE USE IT: pkt6.setData (msg, Utils.OFS_UDP6_PLOAD, len);
+ pkt6.setData (Arrays.copyOfRange (msg, Utils.OFS_UDP6_PLOAD, Utils.OFS_UDP6_PLOAD + len - 48), 0, len - 48);
+ //TODO// return ! pkt4.getAddress ().equals (ipv4socket.getInetAddress ());
+ return false;
+ }
+
+ /** In a playfully hinting exchange with send_playful() and
+ * receive_playful(), this is the final acknowledgement that
+ * should be called upon completion. This acknowledges a
+ * reliable direct connection to a peer without a need to
+ * go through explicit Neighbor Discovery. The pkt6
+ * parameter holds the remote peer's address.
+ *
+ * Note that invoking this function when receive_playful()
+ * returned false for the exchane is a damn lie, and may
+ * end up configuring the NeighborCache entry for this
+ * neighbor with a direct route that does not actually
+ * function. You will then end up sending messages into
+ * oblivia.
+ *
+ * Also note that plaful sends are an optimistic variation
+ * that is not strictly necessary; the NeighborCache has
+ * its own method builtin, based on Neighbor Discovery.
+ * If a direct link to a peer is possible at all, then
+ * this will find it. Only if you wish to use the
+ * optimistic variation to avoid these extra exchanges
+ * should you consider playful mode. After all this
+ * discouraging information it should also be noted that
+ * your network traffic will look extremely cool if it
+ * manages to get through directly to a 6bed4 peer without
+ * any explicit negotiation!
+ */
+ public void acknowledge_playful (Inet6Address ia6bed4) {
+ my6sn.acknowledge_playful (ia6bed4.getAddress(), 0);
+ }
+
+ /** The "standard" interface for sending bytes, overriding the
+ * parent function and not sending playful hints.
+ */
+ public void send (DatagramPacket pkt6)
+ throws IOException {
+ send_playful (pkt6, false);
+ }
+
+ /** The "standard" interface for receiving bytes, overriding the
+ * parent function and not supporting playful operation.
+ */
+ public void receive (DatagramPacket pkt6)
+ throws IOException {
+ /*(void)*/ receive_playful (pkt6);
+ }
+
+
+ /** Construct a new DatagramSocket6bed4 based on an underlying
+ * DatagramSocket for IPv4.
+ */
+ public DatagramSocket6bed4 (InetSocketAddress bindaddr)
+ throws SocketException {
+ super ();
+ bind (bindaddr);
+ }
+ public DatagramSocket6bed4 (int port, Inet6Address bindaddr)
+ throws SocketException {
+ this (new InetSocketAddress (bindaddr, port));
+ }
+ public DatagramSocket6bed4 (int port)
+ throws SocketException {
+ this (port, null /*TODO:INADDR_ANY*/);
+ }
+ public DatagramSocket6bed4 ()
+ throws SocketException {
+ super ();
+ bind ((InetSocketAddress) null);
+ }
+ public DatagramSocket6bed4 (DatagramSocketImpl impl)
+ throws SocketException {
+ throw new RuntimeException ("Cannot choose DatagramSocketImpl");
+ }
+
+
+}
+
--- /dev/null
+package nl.openfortress.socket6bed4;
+
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.UnknownHostException;
+
+
+/** The Inet6bed4Address class acts a bit like InetAddress and
+ * Inet6Address. It is not, however related to these classes;
+ * they are locked down by the Java language [1]. There are
+ * a few calls similar to those in Inet*Address that are also
+ * available here; however, an address created here will be an
+ * Inet6Address.
+ *
+ * [1] Inet6Address is a final class, and InetAddress has no
+ * constructors to call. Nasty.
+ */
+public final class Inet6bed4Address {
+
+ /** Returns the shared 6bed4 address for this host.
+ * Note that a 6bed4 address is available on any host that
+ * has an IPv4 address and supports IPv6. This is also the
+ * when a native IPv6 address exists. You should however
+ * prefer native IPv6 over 6bed4, except perhaps for the
+ * communication with 6bed4 peers.
+ */
+ public static Inet6Address getLocalHost ()
+ throws UnknownHostException {
+ return ConnectionPool.
+ getSharedConnectionPool ().
+ getDefaultServerNode ().
+ getShared6bed4Address ();
+ }
+
+ /** Test if a given InetAddress is a 6bed4 address.
+ * Note that this does not guarantee that it can be cast
+ * to an Inet6bed4Address.
+ * TODO: See notes for is6bed4Address(byte addr[])
+ */
+ public static boolean is6bed4Address (Inet6Address ia) {
+ if (! (ia instanceof Inet6Address)) {
+ return false;
+ }
+ //TODO// Quickfix to incorporate active servers, ideally also to be done for the byte[] version, and would ideally not be limited to the shared pool:
+ if (ConnectionPool.getSharedConnectionPool ().getServerNode (ia) != null) {
+ return true;
+ }
+ return is6bed4Address (ia.getAddress ());
+ }
+
+ /** Test if the given byte array represents a 6bed4 address.
+ * TODO: These addresses are subject to change until 6bed4
+ * is formalised as an RFC! Please subscribe to
+ * tun6bed4-infra for (only those) updates:
+ * https://lists.sourceforge.net/lists/listinfo/tun6bed4-infra
+ * The intention is to obtain a /16 for ten years to come,
+ * and recognise a 6bed4 address based on that.
+ */
+ public static boolean is6bed4Address (byte addr[]) {
+ if (addr.length != 16) {
+ return false;
+ }
+ //TODO// Take local 6bed4 serverpool into account
+ return (addr [0] == 0x20) && (addr [1] == 0x01) && (addr [2] == 0x06) && (addr [3] == 0x7c) && (addr [4] == 0x12) && (addr [5] == 0x7c);
+ }
+
+ /** Return an Inet6bed4Address based on a byte string.
+ * TODO: Perhaps try sharing with existing instances?
+ */
+ public static Inet6Address getByAddress (byte addr[]) {
+ //TODO// Instead of just checking length, eventually use is6bed4Address
+ if (addr.length != 16) {
+ throw new ClassCastException ("Byte array has wrong length");
+ }
+ if (!is6bed4Address (addr)) {
+ throw new ClassCastException ("Address is not a 6bed4 address");
+ }
+ try {
+ return (Inet6Address) InetAddress.getByAddress (addr);
+ } catch (UnknownHostException uhe) {
+ throw new RuntimeException ("Failed to parse IPv6 address bytes");
+ }
+ }
+
+ /** A private constructor avoids instantiation.
+ */
+ private Inet6bed4Address () {
+ ;
+ }
+
+}
+
--- /dev/null
+package nl.openfortress.socket6bed4;
+
+
+import java.util.Hashtable;
+import java.util.Queue;
+import java.util.ArrayDeque;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+
+import java.io.IOException;
+
+
+
+/* The Neighbor Cache is modelled in Java, because Android/Java won't
+ * give access to the network stack.
+ *
+ * The primary task of the cache is to map IPv6-addresses in byte [16]
+ * format to a destination, which is stored as a Inet4SocketAddress
+ * for direct use with DatagramPacket facilities in Java. A hash is
+ * used to gain fast access to the target object.
+ *
+ * To find mappings, the cache will send up to 3 Neighor Solicitation
+ * messages to the direct peer. During this period it will pass through
+ * states ATTEMPT1 to ATTEMPT3, and finally become FAILED. This is a sign
+ * that no further attempts need to be made until the expiration of the
+ * mapping, about 30 seconds after starting it. This process starts
+ * when an unknown peer is approached, or one that is STALE.
+ *
+ * If a Neighbor Advertisement comes in, this information is sent here
+ * for processing. The cache entry will usually go to REACHABLE in such
+ * situations.
+ *
+ * When traffic comes in with the direct address of this peer as its
+ * destination, then the cache is informed; if no mapping in REACHABLE
+ * state exists, then new attempts are started, so as to enable direct
+ * connectivity after the remote peer has opened a hole to us.
+ *
+ * The cache maintains a seperate timer queue for each state, and will
+ * act upon the objects if they expire. This makes the queue rather
+ * self-controlled.
+ *
+ * Before changing any piece of this code, please assure yourself that
+ * you understand each single synchronized section, and convince
+ * yourself that you understand the trick in NeighborQueue.dequeue().
+ * This is not just multitasking code; it may well be the most complex
+ * example of multitasking code that you ever came accross. Do not
+ * change the code unless you are absolutely certain that you have
+ * understood all the invariants that make this code work!
+ */
+
+class NeighborCache {
+
+ /* The states of neighbors in the cache.
+ */
+ public final static int ATTEMPT1 = 0; /* Discovery sequence */
+ public final static int ATTEMPT2 = 1;
+ public final static int ATTEMPT3 = 2;
+ public final static int FAILED = 3;
+ public final static int REACHABLE = 4; /* Confirmed in the last ~30s */
+ public final static int STALE = 5; /* Not used for up to ~30s */
+ public final static int NUM_NGB_STATES = 6; /* Count of normal states */
+ public final static int INTRANSIT = 7; /* Traveling between queues */
+
+
+ /* The timing spent in the various states of a cache entry. Stable states
+ * take up several seconds, while waiting for a response is faster.
+ * Note the remarks at NeighborQueue.dequeue() for an important constraint;
+ * specifically, STALE is spent in an attempt to recycle an entry as
+ * REACHABLE, so the time spent in STALE must not exceed the time spent
+ * in REACHABLE. This avoids having two entries for one neighbor in a
+ * queue, which would jeapourdise the timeout calculations. Note that
+ * this problem does not arise when an object occurs in multiple queues
+ * at once, because a mismatch with the queue's state means that those
+ * lingering neighbors are ignored as timeout sources.
+ *
+ * TODO: I found that Android can be pretty slow in responding over eth0,
+ * possibly worse over UMTS or (shiver) GPRS. So, it may be that the
+ * following timing must be slowed down, *or* that NgbAdv ought to be
+ * accepted in the STALE state.
+ */
+ final static int state_timing_millis [] = {
+ 50, 50, 50, 30000, /* Discovery -- a few ms between steps */
+ 27500, /* STALE -- 30 additional seconds */
+ 30000 /* REACHABLE -- 30 seconds of bliss */
+ };
+
+ /* The timer queues for neighbor discovery, each in its own thread */
+ NeighborQueue queues [];
+
+ /* The neighbor cache contains all elements, regardless of their state.
+ * This is needed to avoid doing double work. If a neighbor is considered
+ * to be available, then it should be in here. Once it stops being of
+ * interest (STALE or FAILED expires) it will be removed.
+ *
+ * Note that the key and value are both Neighbor objects. The only part
+ * of the Neighbor dictating equality (and the hashCode) is the 6-byte
+ * key holding the remote peer's IPv4 address and UDP port, so it is
+ * possible to put one object into the Hashtable as both key and value;
+ * and then to lookup an entry with a minimally setup Neighbor as key
+ * to find a more evolved value; the latter will be incorporated into
+ * the procedures of the Neighbor Cache, including the various queues.
+ */
+ private Hashtable <Integer, Neighbor> neighbor_cache;
+
+
+ /* The public server, used as default return value for queires after
+ * unsettled neighbor cache entries.
+ */
+ private InetSocketAddress public_server;
+
+
+ /* The socket used as uplink to the rest of the 6bed4-using World.
+ */
+ private DatagramSocket uplink;
+
+
+ /* The 6bed4 address as a 16-byte array; may also be used as an 8-byte prefix.
+ */
+ private byte address_6bed4 [];
+
+ /* The 6bed4 address as a 6-byte key.
+ */
+ private byte address_6bed4_key [];
+
+
+ /* Construct a neighbor cache with no entries but a lot of structure */
+ public NeighborCache (DatagramSocket ipv4_uplink, InetSocketAddress pubserver, byte link_address_6bed4 []) {
+ uplink = ipv4_uplink;
+ public_server = pubserver;
+ address_6bed4 = new byte [16];
+ Utils.memcp_address (address_6bed4, 0, link_address_6bed4, 0);
+ address_6bed4_key = new byte [6];
+ address2key (address_6bed4, 0, address_6bed4_key);
+ queues = new NeighborQueue [NUM_NGB_STATES];
+ for (int i=0; i < NUM_NGB_STATES; i++) {
+ queues [i] = new NeighborQueue (this, i);
+ }
+ neighbor_cache = new Hashtable <Integer,Neighbor> ();
+ }
+
+ /* Destroy a neighbor cache */
+ public void cleanup () {
+ for (int i=0; i < NUM_NGB_STATES; i++) {
+ queues [i].interrupt ();
+ }
+ }
+
+ /* Remove an expired entry from the neighbor cache.
+ */
+ public void remove_from_cache (Neighbor ngb) {
+ byte key [] = new byte [6];
+ socketaddress2key (ngb.target, key);
+ synchronized (neighbor_cache) {
+ neighbor_cache.remove (key);
+ }
+ }
+
+ /* Attempt to retrieve an entry from the neighbor cache. The resulting
+ * actions as well as the return value may differ, depending on the
+ * state of the cache entry. This function always returns immediately,
+ * with the tunnel server as a default response for instant connections,
+ * but the cache may asynchronously attempt to find a direct path to the
+ * neighbor.
+ *
+ * The "playful" argument is rather special. When set to true, it
+ * indicates that an initial experiment attempting to contact the
+ * neighbor directly is acceptable. This is usually the cae if the
+ * following conditions apply:
+ * 1. The message is resent upon failure
+ * 2. Success can be recognised and reported back here
+ * Setting this flag causes the first (and only the first) result
+ * from lookup_neighbor to point directly to the host; resends will
+ * be sent through the tunnel, while a few neighbor discoveries are
+ * still tried towards the remote peer, until something comes back
+ * directly to acknowledge contact. When something comes back over
+ * a direct route, this should be reported to the neighbor cache
+ * through received_peer_direct_acknowledgement() so it can
+ * learn that future traffic can be directly sent to the peer.
+ *
+ * A common case for which this holds is TCP connection setup:
+ * 1. Packets with SYN are sent repeatedly if need be
+ * 2. Packets with ACK stand out in a crowd
+ * So, when TCP packets with SYN are sent, their lookup_neighbor()
+ * invocation can set the playful flag; the first attempt will go
+ * directly to the peer and hopefully initiate direct contact; if
+ * not, future attempts are sent through the tunnel. When the
+ * remote peer accepts, it will return a packet with ACK set over
+ * the direct route, which is enough reason to signal success to
+ * the neighbor cache. Interestingly, the first reply message
+ * from a TCP server will generally hold the SYN flag as well, so
+ * if the remote 6bed4 stack is also playful about TCP, it is
+ * likely to send it back directly even if it received the opening
+ * SYN through the tunnel. This means that even a blocking NAT on
+ * the remote end will cause a new attempt on return. As usual,
+ * an ACK from client to server follows, which asserts to the
+ * server that it can talk directly (because, at that time, the
+ * client has picked up on the directly sent SYN+ACK).
+ *
+ * It is possible for TCP to initiate a connection from both ends
+ * at the same time. Effectively this splits the SYN+ACK into
+ * two messages, one with SYN and one with ACK. It may happen
+ * that direct contact is not correctly established if that is
+ * done, but the follow-up neighbor discovery that follow on a
+ * playful SYN that does not return an ACK over the direct route
+ * will establish NAT-passthrough if it is technically possible.
+ * A similar thing occurs when the initial SYN is lost for some
+ * reason.
+ */
+ public InetSocketAddress lookup_neighbor (byte addr [], int addrofs, boolean playful) {
+ if (!is6bed4 (addr, addrofs)) {
+ return public_server;
+ }
+ boolean puppy = false;
+ Neighbor ngb;
+ byte seeker_key [] = new byte [6];
+ address2key (addr, addrofs, seeker_key); //TODO// Only used for seeker creation?
+ int seeker_hash = key2hash (seeker_key);
+ synchronized (neighbor_cache) {
+ ngb = neighbor_cache.get (seeker_hash);
+ if (ngb == null) {
+ ngb = new Neighbor (seeker_key, playful); //TODO// Possibly seeker.clone ()
+ neighbor_cache.put (seeker_hash, ngb);
+ puppy = true;
+ }
+ }
+ switch (ngb.state) {
+ case ATTEMPT1:
+ case ATTEMPT2:
+ case ATTEMPT3:
+ case FAILED:
+ case INTRANSIT:
+ //
+ // No usable entry exists, but no new attempts are needed.
+ // Simply return the default router's address.
+ //
+ // If the new entry was just inserted into the neighbor
+ // cache for this thread, then enqueue into ATTEMPT1, part
+ // of which is sending a neighbor solicitation. However,
+ // if the request is made by a playful puppy, then skip
+ // sending the Neighbor Discovery upon insertion into the
+ // ATTEMPT1 queue; the 6bed4 stack will instead send the
+ // initial message directly to the peer. To that end, return
+ // the direct neighbor instead of the safe default of the
+ // public server. Note that a repeat of the packet will
+ // no longer count as a puppy, so this is done only once.
+ if (puppy) {
+ queues [ATTEMPT1].enqueue (ngb, playful);
+ if (playful) {
+ return ngb.target;
+ }
+ }
+ return public_server;
+ case STALE:
+ //
+ // The neighbor has been reached directly before; assume this
+ // is still possible (continue into REACHABLE) but also send a
+ // neighbor solicitation to keep the lines open.
+ solicit_neighbor (ngb);
+ case REACHABLE:
+ //
+ // The neighbor is known to be available for direct contact.
+ // There is no work to be done to that end.
+ return ngb.target;
+ default:
+ return public_server;
+ }
+ }
+
+ /* Send a Neighbor Solicitation to the peer. When this succeeds at
+ * penetrating to the remote peer's 6bed4 stack, then a Neighbor
+ * Advertisement will return, and be reported through the method
+ * NeighborCache.received_peer_direct_acknowledgement() so the
+ * cache can update its state, and support direct sending to the
+ * remote peer.
+ *
+ * Even though this kind of message may be sent after failed
+ * playful attempts, it still does not rule out the possibility
+ * of any such attempts returning, so the "playful" flag for
+ * the Neighbor solicited is not changed.
+ */
+ public void solicit_neighbor (Neighbor ngb) {
+ byte key [] = new byte [6];
+ socketaddress2key (ngb.target, key);
+ byte ngbsol [] = new byte [72];
+ //
+ // IPv6 header:
+ ngbsol [0] = 0x60;
+ ngbsol [1] = ngbsol [2] = ngbsol [3] = 0;
+ ngbsol [4] = 0; ngbsol [5] = 4 + 28;
+ ngbsol [6] = Utils.IPPROTO_ICMPV6;
+ ngbsol [7] = (byte) 255;
+ Utils.memcp_address (ngbsol, 8, address_6bed4, 0);
+ key2ipv6address (key, ngbsol, 24);
+ int ofs = 40;
+ // ICMPv6 header:
+ ngbsol [ofs++] = (byte) Utils.ND_NEIGHBOR_SOLICIT;
+ ngbsol [ofs++] = 0;
+ ofs += 2; // Checksum
+ ngbsol [ofs++] =
+ ngbsol [ofs++] =
+ ngbsol [ofs++] =
+ ngbsol [ofs++] = 0x00;
+ key2ipv6address (ngb.key, ngbsol, ofs);
+ ofs += 16;
+ // ICMPv6 Option: Source Link-Layer Address
+ ngbsol [ofs++] = 1; // SrcLLAddr
+ ngbsol [ofs++] = 1; // length is 1x 8 bytes
+ for (int i=0; i<6; i++) {
+ ngbsol [ofs++] = address_6bed4_key [i];
+ }
+ int csum = Utils.checksum_icmpv6 (ngbsol, 0);
+ ngbsol [42] = (byte) ((csum >> 8) & 0xff);
+ ngbsol [43] = (byte) ( csum & 0xff);
+ try {
+ DatagramPacket pkt = new DatagramPacket (ngbsol, ngbsol.length, ngb.target);
+ uplink.send (pkt);
+ } catch (IOException ioe) {
+ ;
+ }
+ }
+
+ /* The TunnelServer reports having received a neighbor advertisement
+ * by calling this function. This may signal the cache that the
+ * corresponding cache entry needs updating.
+ *
+ * The "playful" flag is used to indicate that the acknowledgement
+ * arrived as part of a playful attempt in lookup_neighbor(). This
+ * information is used to determine if resends have taken place
+ * through the tunnel server. If such resends were sent, then it
+ * is not safe anymore to assume that a one-to-one exchange has
+ * taken place, and it becomes necessary to rely on the generic
+ * mechanism of Neighbor Discovery to detect the ability for direct
+ * contact between peers.
+ */
+ public void received_peer_direct_acknowledgement (byte addr [], int addrofs, boolean playful) {
+ Neighbor ngb;
+ byte key [] = new byte [6];
+ address2key (addr, addrofs, key);
+ int seeker_hash = key2hash (key);
+ synchronized (neighbor_cache) {
+ ngb = neighbor_cache.get (seeker_hash);
+ }
+ if (ngb == null) {
+ return;
+ }
+ if (playful && !ngb.playful) {
+ return;
+ }
+ int nst = ngb.state;
+ switch (nst) {
+ case ATTEMPT1:
+ case ATTEMPT2:
+ case ATTEMPT3:
+ case STALE:
+ //
+ // There is an entry waiting to be resolved.
+ // Apply the lessons learnt to that entry.
+ // Be careful -- another thread may be trying
+ // the same, so we use the dequeue function
+ // that checks again before proceeding.
+ if (queues [nst].dequeue (ngb)) {
+ //
+ // Only one thread will continue here, having dequeued the neighbor
+ queues [REACHABLE].enqueue (ngb);
+ }
+ break;
+ case REACHABLE:
+ case FAILED:
+ //
+ // A persistent state has been reached, and any
+ // advertisements coming in have missed their
+ // window of opportunity. Note that this may be
+ // a sign of attempted abuse. So ignore it.
+ break;
+ case INTRANSIT:
+ //
+ // Some thread is already working on this neighbor, so drop
+ // this extra notification.
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ /* The Neighbor class represents individual entries in the NeighborCache.
+ * Each contains an IPv6 address as 16 bytes, its state, and a timeout for
+ * its current state queue.
+ *
+ * Most of this class is storage, and is publicly accessible by the
+ * other components in this class/file.
+ *
+ * Note that the Neighbor class serves in the hashtable as a key and value;
+ * but as a key, the only input used are the 6 bytes with the remote peer's
+ * IPv4 address and UDP port. This means that another object can easily be
+ * constructed as a search entry, to resolve into the full entry.
+ *
+ * TODO: Inner class -- does that mean that the context is copied into this object? That'd be wasteful!
+ */
+ static class Neighbor {
+ protected int state;
+ protected InetSocketAddress target;
+ protected long timeout;
+ byte key [];
+ boolean playful;
+
+ /* The hashCode for this object depends solely on the key value.
+ * Integrate all the bits of the key for optimal spreading. The
+ * hashCode may only differ if the equals() output below also
+ * differs.
+ */
+ public int hashCode () {
+ return key2hash (key);
+ }
+
+ /* The equals output determines that two Neighbor objects are
+ * the same if their key matches; that is, if their remote
+ * IPv4 address and UDP port is the same. It is guaranteed
+ * that the hashCode is the same for two equal objects; if two
+ * objects are unequal then their hashCode is *likely* to differ,
+ * but not guaranteed.
+ */
+ public boolean equals (Object oth) {
+ try {
+ Neighbor other = (Neighbor) oth;
+ for (int i=0; i<5; i++) {
+ if (this.key [i] != ((Neighbor) other).key [i]) {
+ return false;
+ }
+ }
+ return true;
+ } catch (Throwable thr) {
+ return false;
+ }
+ }
+
+ /* Construct a new Neighbor based on its 6-byte key */
+ public Neighbor (byte fromkey [], boolean create_playfully) {
+ key = new byte [6];
+ for (int i=0; i<6; i++) {
+ key [i] = fromkey [i];
+ }
+ state = NeighborCache.ATTEMPT1;
+ target = key2socketaddress (key);
+ playful = create_playfully;
+ }
+ }
+
+ /* The NeighborQueue inner class represents a timer queue. The objects in this
+ * queue each have their own timeouts, as specified by state_timing_millis.
+ * New entries are attached to the back, timeouts are picked up at the front.
+ *
+ * The queues have understanding of the various states, and how to handle
+ * expiration.
+ */
+ class NeighborQueue extends Thread {
+
+ private NeighborCache cache;
+ private int whoami;
+ private Queue <Neighbor> queue;
+
+ public NeighborQueue (NeighborCache owner, int mystate) {
+ cache = owner;
+ whoami = mystate;
+ queue = new ArrayDeque <Neighbor> ();
+ this.start ();
+ }
+
+ public void run () {
+ while (!isInterrupted ()) {
+ //
+ // First, fetch the head element (or wait for one to appear).
+ // Sample its timeout. Even if the head disappears later on,
+ // any further entries are certain to have a later timeout.
+ long timeout;
+ synchronized (queue) {
+ Neighbor head = null;
+ while (head == null) {
+ head = queue.peek ();
+ if (head == null) {
+ try {
+ queue.wait ();
+ } catch (InterruptedException ie) {
+ return;
+ }
+ }
+ }
+ if (head.state == whoami) {
+ timeout = 0;
+ } else {
+ timeout = head.timeout;
+ }
+ }
+ //
+ // Now wait until the timeout expires. This is not done in
+ // a synchronous fashion, so the queue can be changed as
+ // desired by other threads. Note that misplaced items
+ // in the queue will be removed by the code further down
+ // before this thread can find rest.
+ if (timeout != 0) {
+ try {
+ sleep (timeout - System.currentTimeMillis ());
+ } catch (InterruptedException ie) {
+ return;
+ }
+ }
+ //
+ // Now the time has come to remove the head elements that
+ // have expired. If nothing happened to the head of the
+ // queue, then this one should at least be removed, but
+ // following entries may just as well need removal. Do
+ // this in a queue-synchronous manner to stop other threads
+ // from altering the queue.
+ long now = System.currentTimeMillis ();
+ Queue <Neighbor> removed = new ArrayDeque <Neighbor> ();
+ synchronized (queue) {
+ Neighbor head = queue.peek ();
+ while ((head != null) && ((head.state != whoami) || (head.timeout <= now))) {
+ head = queue.remove ();
+ if (head.state == whoami) {
+ removed.offer (head);
+ } // else, misplaced item that can be silently dropped
+ head = queue.peek ();
+ }
+ }
+ //
+ // We now have a few elements in a temporary removed list;
+ // handle those by inserting them into their next lifecycle
+ // state (possibly meaning, destroy them).
+ switch (whoami) {
+ case ATTEMPT3:
+/* TODO: Why would we want to remove the remote socket address?
+ //
+ // Remove the remote socket address, and continue with
+ // promotion to the next lifecycle state
+ for (Neighbor ngb : removed) {
+ ngb.target = null;
+ }
+ */
+ case ATTEMPT1:
+ case ATTEMPT2:
+ case REACHABLE:
+ //
+ // Promote the neighbor to its next lifecycle state
+ for (Neighbor ngb : removed) {
+ queues [whoami + 1].enqueue (ngb);
+ }
+ break;
+ case FAILED:
+ case STALE:
+ //
+ // Remove the neighbor from the cache
+ for (Neighbor ngb : removed) {
+ cache.remove_from_cache (ngb);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Insert a new element into the queue. If this is the first
+ * entry, be sure to notify the thread that may be waiting for
+ * something to chew on.
+ * Note that an entry may be enqueued only once, use dequeue
+ * to remove it from a possible former queue.
+ */
+ public void enqueue (Neighbor ngb) {
+ enqueue (ngb, false);
+ }
+
+ /* Insert a new element into the queue. If this is the first
+ * entry, be sure to notify the thread that may be waiting for
+ * something to chew on.
+ * Note that an entry may be enqueued only once, use dequeue
+ * to remove it from a possible former queue.
+ * The flag "no_action" requests that the side-effects that
+ * may take place after inserting the element in the queue
+ * should be skipped.
+ */
+ public void enqueue (Neighbor ngb, boolean no_action) {
+ //
+ // Initialise the neighbor for this queue
+ ngb.state = whoami;
+ ngb.timeout = System.currentTimeMillis () + NeighborCache.state_timing_millis [whoami];
+ //
+ // Insert the Neighbor into the queue
+ synchronized (queue) {
+ boolean wasEmpty = queue.isEmpty ();
+ queue.offer (ngb);
+ if (wasEmpty) {
+ queue.notifyAll ();
+ }
+ }
+ //
+ // Skip the side-effect of insertion is so desired.
+ if (no_action) {
+ return;
+ }
+ //
+ // If this queue represents waiting for a reply to Neighbor
+ // Solicitation, then solicit the neighbor. This is not
+ // synchronous, because networking does not directly modify
+ // the queue, is slow and is asynchronous in nature anyway.
+ switch (whoami) {
+ case ATTEMPT1:
+ case ATTEMPT2:
+ case ATTEMPT3:
+ cache.solicit_neighbor (ngb);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Remove an element from the queue without awaiting timer
+ * expiration. This is only of interest when incoming traffic
+ * follows a direct route, while our previous attempts ran into
+ * FAILED because the remote didn't have its ports open for us yet.
+ * In addition, this may be used when a timer has gone STALE.
+ * Note that expiration timers on the queue can continue to run,
+ * they may simply find that the first element has not timed out
+ * and another cycle is needed for what is then the head.
+ * The state and timeout of the neighbor entry will not be modified
+ * by this call, but future enqueueing may do that nonetheless.
+ * The function returns success; that is, it tells the caller if
+ * the object was indeed found in the queue.
+ *
+ * *** Implementation note ***
+ *
+ * Dequeueing is expensive, as it involves going through the list
+ * of stored items. Keeping the structures up to date to speedup
+ * this operation is hardly more pleasant. So, we'll use a trick.
+ * We simply won't dequeue. This means that queues can hold items
+ * that should have been removed. As long as objects never cycle
+ * back to this queue before their timers in this queue have
+ * expired, the misplacement of a queue item could be directly
+ * inferred from the state, which does not match the queue's state.
+ * Such misplaced items can be silently removed. Doing this, the
+ * only remaining task for dequeue is to change the state in an
+ * atomic fashion -- that is, without risk of overlapping similar
+ * operations by other threads. As originally intended, there
+ * must be only one thread that receives true when removing an
+ * item from a queue that holds it. The loop that removes the
+ * queue elements from the head also changes to accommodate this
+ * behaviour.
+ */
+ public boolean dequeue (Neighbor ngb) {
+ boolean wasthere;
+ synchronized (queue) {
+ if (ngb.state != whoami) {
+ wasthere = false;
+ } else {
+ ngb.state = NeighborCache.INTRANSIT;
+ wasthere = true;
+ }
+ }
+ return wasthere;
+ }
+ }
+
+
+ /***
+ *** UTILITIES
+ ***/
+
+
+ /* Check if an address is a 6bed4 address.
+ */
+ public boolean is6bed4 (byte addr [], int addrofs) {
+ if (Utils.memdiff_halfaddr (addr, addrofs, address_6bed4, 0)) {
+ return false;
+ }
+ if ((addr [addrofs + 11] != (byte) 0xff) || (addr [addrofs + 12] != (byte) 0xfe)) {
+ return false;
+ }
+ //TODO// Possibly require that the port number is even
+ return true;
+ }
+
+ /* Map an address to a 6-byte key in the provided space. Assume that the
+ * address is known to be a 6bed4 address.
+ */
+ private static void address2key (byte addr [], int addrofs, byte key []) {
+ key [0] = (byte) (addr [addrofs + 8] ^ 0x02);
+ key [1] = addr [addrofs + 9];
+ key [2] = addr [addrofs + 10];
+ key [3] = addr [addrofs + 13];
+ key [4] = addr [addrofs + 14];
+ key [5] = addr [addrofs + 15];
+ }
+
+ /* Map an InetSocketAddress to a 6-byte key in the provided space.
+ */
+ private static void socketaddress2key (InetSocketAddress sa, byte key []) {
+ int port = sa.getPort ();
+ byte addr [] = sa.getAddress ().getAddress ();
+ key [0] = (byte) (port & 0xff);
+ key [1] = (byte) (port >> 8 );
+ key [2] = addr [0];
+ key [3] = addr [1];
+ key [4] = addr [2];
+ key [5] = addr [3];
+ }
+
+ /* Map a key to an IPv6 address, following the 6bed4 structures.
+ */
+ private void key2ipv6address (byte key [], byte addr [], int addrofs) {
+ for (int i=0; i<8; i++) {
+ addr [addrofs + i] = address_6bed4 [i];
+ }
+ addr [addrofs + 8] = (byte) (key [0] ^ 0x02);
+ addr [addrofs + 9] = key [1];
+ addr [addrofs + 10] = key [2];
+ addr [addrofs + 11] = (byte) 0xff;
+ addr [addrofs + 12] = (byte) 0xfe;
+ addr [addrofs + 13] = key [3];
+ addr [addrofs + 14] = key [4];
+ addr [addrofs + 15] = key [5];
+ }
+
+ /* Map a key to an InetSocketAddress.
+ */
+ private static InetSocketAddress key2socketaddress (byte key []) {
+ int port = (int) (key [0] & 0xff);
+ port = port | (((int) (key [1] << 8)) & 0xff00);
+ byte v4addr [] = new byte [4];
+ v4addr [0] = key [2];
+ v4addr [1] = key [3];
+ v4addr [2] = key [4];
+ v4addr [3] = key [5];
+ try {
+ Inet4Address remote = (Inet4Address) Inet4Address.getByAddress (v4addr);
+ return new InetSocketAddress (remote, port);
+ } catch (UnknownHostException uhe) {
+ throw new RuntimeException ("Internet Error", uhe);
+ }
+ }
+
+ private static int key2hash (byte key []) {
+ int retval;
+ retval = key [0] ^ key [3];
+ retval <<= 8;
+ retval += key [1] ^ key [4];
+ retval <<= 8;
+ retval += key [2] ^ key [5];
+ return retval;
+ }
+
+}
--- /dev/null
+package nl.openfortress.socket6bed4;
+
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.SocketException;
+import java.io.IOException;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+
+/** Each ServerNode instance serves a connection to a particular 6bed4
+ * tunnel server. This is reflected by its superclass, which is the
+ * principal address serviced -- the IPv4 address and UDP port for the
+ * server.
+ *
+ * As part of the service for a node, there is a neighbor cache. This
+ * may be compared to having a per-network neighbor cache as part of
+ * an operating system. Lookups may lead to more direct routes than
+ * would otherwise be possible.
+ *
+ * TODO: Hook up handle_4to6/handle_6to4 methods with receive/send.
+ * TODO: Handle IPv6 addr chg by throwing an IOException subclass.
+ * TODO: Establish NeighborCache with lladdr_6bed4, somehow.
+ */
+public class ServerNode
+extends DatagramSocket {
+
+ private int usecount = 0;
+ protected InetSocketAddress tunserver;
+ protected NeighborCache ngbcache;
+ protected Inet6Address sharedAddress;
+ private Maintainer maintainer;
+ private Worker worker;
+ private BlockingQueue<byte[]> udp_clients [];
+ static byte local_address [] = new byte [16];
+
+ /** Create a connection to a 6bed4 tunnel server, and keep it active.
+ */
+ public ServerNode (InetSocketAddress isa)
+ throws SocketException {
+ super ();
+ maintainer = new Maintainer (this);
+ worker = new Worker (this);
+ tunserver = isa;
+ udp_clients = new BlockingQueue [65536];
+ }
+
+ /** Create a connection to a 6bed4 tunnel server, and keep it active.
+ * This constructor binds locally to the given UDP port, if possible.
+ * This may provide a more predictable IPv6 address, but that depends
+ * on your networking setup. If you are behind NAT, and/or someone
+ * else is already using the port you will still have a different IPv6
+ * address provided by the 6bed4 server, because it "sees" you on
+ * another UDP port at the outside of NAT. NAT is NAsTy, we know.
+ * Things may improve when you setup port forwarding, but that would
+ * involve fixating the LAN host that can run this ServerNode at the
+ * given IPv6 address. And again, it would depend on your NAT.
+ */
+ public ServerNode (InetSocketAddress isa, int port)
+ throws SocketException {
+ super (port);
+ maintainer = new Maintainer (this);
+ worker = new Worker (this);
+ tunserver = isa;
+ udp_clients = new BlockingQueue [65536];
+ }
+
+
+ /** Teardown the connection to a 6bed4 tunnel server.
+ */
+ public void stop () {
+ worker.interrupt ();
+ maintainer.interrupt ();
+ if (ngbcache != null) {
+ ngbcache.cleanup ();
+ ngbcache = null; /* Intended exceptions on continued use */
+ }
+ }
+
+ /** Increment the use counter */
+ public synchronized void useMore () {
+ if (usecount++ == 0) {
+ maintainer.start ();
+ worker.start ();
+ }
+ }
+
+ /** Decrement the use counter, return if it is now useless */
+ public synchronized boolean useLess () {
+ if (--usecount == 0) {
+ maintainer.interrupt ();
+ worker.interrupt ();
+ return true;
+ }
+ return false;
+ }
+
+
+ /* Register a UDP client for a given port on the default
+ * IPv6 address as handed out by getDefault6bed4Address ().
+ * This is a claim to that port, and a SocketException is
+ * raised if it is not available.
+ */
+ public void registerDatagramClient (int port)
+ throws SocketException {
+ synchronized (udp_clients) {
+ if (udp_clients [port] != null) {
+ throw new SocketException ("port taken");
+ }
+ udp_clients [port] = new ArrayBlockingQueue<byte[]> (10);
+ }
+ }
+
+ /* Unregister a client from a port that it has claimed.
+ */
+ public void unregisterDatagramClient (int port)
+ throws SocketException {
+ synchronized (udp_clients) {
+ if (udp_clients [port] == null) {
+ throw new SocketException ("port not in use");
+ }
+ //TODO// possibly cleanup the Queue before letting go
+ udp_clients [port] = null;
+ }
+ }
+
+ /** Return the shared 6bed4 address, usable for anyone who
+ * wants to allocate ports for UDP/TCP through either a
+ * Socket6bed4 or a DatagramSocket6bed4. This routine will
+ * block until an address has been obtained.
+ *
+ * To allocate recipients on the shared address, use
+ * registerDatagramClient(port) and, hopefully one day,
+ * registerStreamClient(port). Raw access is not possible
+ * with this address; for that, use getUnique6bed4Address()
+ * as long as supply lasts.
+ */
+ public Inet6Address getShared6bed4Address () {
+ synchronized (maintainer) {
+ while (sharedAddress == null) {
+ try {
+ maintainer.wait ();
+ } catch (InterruptedException ie) {
+ return null;
+ }
+ }
+ }
+ return sharedAddress;
+ }
+
+ /** Return a unique 6bed4 address, which is available for
+ * raw communication over IPv6. Return null if no addresses
+ * are available anymore.
+ * Addresses returned from this function can be used as
+ * raw sockets. They share the communication channel setup
+ * for getShared6bed4Address() but the contents of the IPv6
+ * packet will not be implemented, but rather relayed
+ * directly to the provided BlockingQueue instance.
+ * TODO: Sending, NxtHdr, and how to deal with security.
+ * TODO: Implement. Consider security implications.
+ */
+ public synchronized Inet6Address getUnique6bed4Address (BlockingQueue<byte[]> recv_TODO_OR_KEEP_INTERNAL) {
+ //TODO// Register the address in the ConnectionPool so getServerNode() will work!
+ return null;
+ }
+
+ /** Pull an element from the BlockingQueue for the given
+ * Datagram port. Block if no input is available.
+ * The timeout value is in milli-seconds, zero
+ * representing infinity (just as with setSoTimeout).
+ */
+ public byte[] receive_datagram (int port, int timeout)
+ throws SocketException {
+ if (udp_clients [port] == null) {
+ throw new SocketException ("port is not allocated");
+ }
+ try {
+ if (timeout > 0) {
+ return udp_clients [port].poll (timeout, TimeUnit.MILLISECONDS);
+ } else {
+ return udp_clients [port].take ();
+ }
+ } catch (InterruptedException ie) {
+ return null;
+ }
+ }
+
+ /** Acknowledge a playful exchange to the Neighbor Cache.
+ * This is a complex protocol, involving an interaction
+ * with the application layer, to confirm initial messages
+ * that support resends. Please see the documentation in
+ * NeighborCache and DatagramSocket6bed4 for details.
+ */
+ public void acknowledge_playful (byte addr[], int ofs) {
+ if ((ngbcache != null) && (ofs + 16 <= addr.length)) {
+ ngbcache.received_peer_direct_acknowledgement (addr, ofs, true);
+ }
+ }
+
+ /** Lookup a neighbor. Forwards to NeighborCache.
+ */
+ public InetSocketAddress lookup_neighbor (Inet6Address ia, boolean playful) {
+ while (ngbcache == null) {
+ synchronized (maintainer) {
+ try {
+ maintainer.wait ();
+ } catch (InterruptedException ie) {
+ return null;
+ }
+ }
+ }
+ return ngbcache.lookup_neighbor (ia.getAddress (), 0, playful);
+ }
+
+
+ public void handle_4to6_nd (byte pkt [], int pktlen, SocketAddress src)
+ throws IOException, SocketException {
+ if (Utils.checksum_icmpv6 (pkt, 0) != Utils.fetch_net16 (pkt, Utils.OFS_ICMP6_CSUM)) {
+ // Checksum is off
+ return;
+ }
+ switch (pkt [Utils.OFS_ICMP6_TYPE]) {
+ //
+ // Handle Router Solicitation by dropping it -- this is a peer, not a router
+ case Utils.ND_ROUTER_SOLICIT:
+ return;
+ //
+ // Handle Router Advertisement as an addressing offering -- but validate the sender
+ case Utils.ND_ROUTER_ADVERT:
+ if (pktlen < 40 + 16 + 16) {
+ // Too short to contain IPv6, ICMPv6 RtrAdv and Prefix Option
+ return;
+ }
+ if ((pkt [Utils.OFS_ICMP6_DATA+1] & 0x80) != 0x00) {
+ // Indecent proposal to use DHCPv6 over 6bed4
+ return;
+ }
+ if (Utils.memdiff_addr (pkt, Utils.OFS_IP6_SRC, Utils.router_linklocal_address, 0)) {
+ // Sender is not 0xfe80::/128
+ return;
+ }
+ if (Utils.memdiff_halfaddr (pkt, Utils.OFS_IP6_DST, Utils.router_linklocal_address, 0)) {
+ // Receiver address is not 0xfe80::/64
+ return;
+ }
+ if ((pkt [Utils.OFS_IP6_DST + 11] != (byte) 0xff) || (pkt [Utils.OFS_IP6_DST + 12] != (byte) 0xfe)) {
+ // No MAC-based destination address ending in ....:..ff:fe..:....
+ return;
+ }
+ //TODO// Check if offered address looks like a multicast-address (MAC byte 0 is odd)
+ //TODO// Check Secure ND on incoming Router Advertisement?
+ //
+ // Having validated the Router Advertisement, process its contents
+ int destprefix_ofs = 0;
+ int rdofs = Utils.OFS_ICMP6_DATA + 12;
+ //TODO:+4_WRONG?// while (rdofs <= ntohs (v4v6plen) + 4) { ... }
+ while (rdofs + 4 < pktlen) {
+ if (pkt [rdofs + 1] == 0) {
+ return; /* zero length option */
+ }
+ if (pkt [rdofs + 0] != Utils.ND_OPT_PREFIX_INFORMATION) {
+ /* skip to next option */
+ } else if (pkt [rdofs + 1] != 4) {
+ return; /* bad length field */
+ } else if (rdofs + (pkt [rdofs + 1] << 3) > pktlen + 4) {
+ return; /* out of packet length */
+ } else if ((pkt [rdofs + 3] & (byte) 0xc0) != (byte) 0xc0) {
+ /* no on-link autoconfig prefix */
+ } else if (pkt [rdofs + 2] != 64) {
+ return;
+ } else {
+ destprefix_ofs = rdofs + 16;
+ }
+ rdofs += (pkt [rdofs + 1] << 3);
+ }
+ if (destprefix_ofs > 0) {
+ for (int i=0; i<8; i++) {
+ local_address [0 + i] = pkt [destprefix_ofs + i];
+ local_address [8 + i] = pkt [Utils.OFS_IP6_DST + 8 + i];
+ }
+ sharedAddress = (Inet6Address) InetAddress.getByAddress (local_address);
+ //TODO// syslog (LOG_INFO, "%s: Assigning address %s to tunnel\n", program, v6prefix);
+ // update_local_netconfig ();
+ if (ngbcache != null) {
+ ngbcache.cleanup ();
+ }
+ ngbcache = new NeighborCache (this, tunserver, local_address);
+ maintainer.have_local_address (true);
+ synchronized (maintainer) {
+ maintainer.notifyAll ();
+ }
+ // Log.i (TAG, "Assigned address to tunnel");
+ }
+ return;
+ //
+ // Neighbor Solicitation is an attempt to reach us peer-to-peer, and should be responded to
+ case Utils.ND_NEIGHBOR_SOLICIT:
+ if (pktlen < 24) {
+ // Too short to make sense
+ return;
+ }
+ if (Utils.memdiff_addr (pkt, Utils.OFS_ICMP6_NGBSOL_TARGET, local_address, 0)) {
+ // Neighbor Solicitation not aimed at me
+ return;
+ }
+ if ((ngbcache == null) || !ngbcache.is6bed4 (pkt, Utils.OFS_IP6_SRC)) {
+ // Source is not a 6bed4 address
+ return;
+ }
+ //
+ // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
+ // Not checked here: LLaddr in NgbSol -- simply send back to IPv6 src address
+ //
+ Utils.memcp_address (pkt, Utils.OFS_IP6_DST, pkt, Utils.OFS_IP6_SRC);
+ Utils.memcp_address (pkt, Utils.OFS_IP6_SRC, local_address, 0);
+ pkt [Utils.OFS_ICMP6_TYPE] = Utils.ND_NEIGHBOR_ADVERT;
+ pkt [Utils.OFS_IP6_PLEN + 0] = 0;
+ pkt [Utils.OFS_IP6_PLEN + 1] = 8 + 16;
+ pkt [Utils.OFS_ICMP6_NGBADV_FLAGS] = 0x60; // Solicited, Override
+ // Assume that Utils.OFS_ICMP6_NGBADV_TARGET == Utils.OFS_ICMP6_NGBSOL_TARGET
+ int csum = Utils.checksum_icmpv6 (pkt, 0);
+ pkt [Utils.OFS_ICMP6_CSUM + 0] = (byte) (csum >> 8 );
+ pkt [Utils.OFS_ICMP6_CSUM + 1] = (byte) (csum & 0xff);
+ DatagramPacket replypkt = new DatagramPacket (pkt, 0, 40 + 8 + 16, src);
+ super.send (replypkt);
+
+ //
+ // TODO:OLD Replicate the message over the tunnel link
+ //
+ // We should attach a Source Link-Layer Address, but
+ // we cannot automatically trust the one provided remotely.
+ // Also, we want to detect if routes differ, and handle it.
+ //
+ // 0. if no entry in the ngb.cache
+ // then use 6bed4 server in ND, initiate ngb.sol to src.ll
+ // impl: use 6bed4-server lladdr, set highest metric
+ // 1. if metric (ngb.cache) < metric (src.ll)
+ // then retain ngb.cache, send Redirect to source
+ // 2. if metric (ngb.cache) > metric (src.ll)
+ // then retain ngb.cache, initiate ngb.sol to src.ll
+ // 3. if metric (ngb.cache) == metric (src.ll)
+ // then retain ngb.cache
+ //
+ //TODO// Handle Utils.ND_NEIGHBOR_SOLICIT (handle_4to6_nd)
+ return;
+ //
+ // Neighbor Advertisement may be in response to our peer-to-peer search
+ case Utils.ND_NEIGHBOR_ADVERT:
+ //
+ // Process Neighbor Advertisement coming in over 6bed4
+ // First, make sure it is against an item in the ndqueue
+ //
+ // Validate the Neighbor Advertisement
+ if (pktlen < 64) {
+ // Packet too small to hold ICMPv6 Neighbor Advertisement
+ return;
+ }
+ if ((pkt [Utils.OFS_ICMP6_TYPE] != Utils.ND_NEIGHBOR_ADVERT) || (pkt [Utils.OFS_ICMP6_CODE] != 0)) {
+ // ICMPv6 Type or Code is wrong
+ return;
+ }
+ if ((ngbcache == null) || (!ngbcache.is6bed4 (pkt, Utils.OFS_IP6_SRC)) || (!ngbcache.is6bed4 (pkt, Utils.OFS_IP6_DST))) {
+ // Source or Destination IPv6 address is not a 6bed4 address
+ return;
+ }
+ if (Utils.memdiff_addr (pkt, Utils.OFS_IP6_SRC, pkt, Utils.OFS_ICMP6_NGBADV_TARGET)) {
+ // NgbAdv's Target Address does not match IPv6 source
+ return;
+ }
+ //
+ // Not checked here: IPv4/UDP source versus IPv6 source address (already done)
+ //
+ ngbcache.received_peer_direct_acknowledgement (pkt, Utils.OFS_ICMP6_NGBADV_TARGET, false);
+ return;
+ //
+ // Route Redirect messages are not supported in 6bed4 draft v01
+ case Utils.ND_REDIRECT:
+ return;
+ }
+ }
+
+ /* Forward an IPv6 packet, wrapped into UDP and IPv4 in the 6bed4
+ * way, as a pure IPv6 packet over the tunnel interface. This is
+ * normally a simple copying operation. One exception exists for
+ * TCP ACK packets; these may be in response to a "playful" TCP SYN
+ * packet that was sent directly to the IPv4 recipient. This is a
+ * piggyback ride of the opportunistic connection efforts on the
+ * 3-way handshake for TCP, without a need to modify the packets!
+ * The only thing needed to make that work is to report success
+ * back to the Neighbor Cache, in cases when TCP ACK comes back in
+ * directly from the remote peer.
+ *
+ * Note that nothing is stopping an ACK packet that is meaningful
+ * to us from also being a SYN packet that is meaningful to the
+ * remote peer. We will simply do our thing and forward any ACK
+ * to the most direct route we can imagine -- which may well be
+ * the sender, _especially_ since we opened our 6bed4 port to the
+ * remote peer when sending our playful initial TCP packet.
+ *
+ * Observing the traffic on the network, this may well look like
+ * magic! All you see is plain TCP traffic crossing over directly
+ * if it is possible --and bouncing one or two packets through the
+ * tunnel otherwise-- and especially in the case where it can work
+ * directly it will be a surprise. Servers are therefore strongly
+ * encouraged to setup port forwarding for their 6bed4 addresses,
+ * or just open a hole in full cone NAT/firewall setups. This will
+ * mean zero delay and zero bypasses for 6bed4 on the majority of
+ * TCP connection initiations between 6bed4 peers!
+ */
+ public void handle_4to6_plain (byte pkt [], int pktlen)
+ throws IOException {
+ //
+ // Check if the designated queue for this packet exists
+ int port = Utils.fetch_net16 (pkt, Utils.OFS_UDP6_DSTPORT);
+ //TODO// Handling for udp_clients: only for UDP-over-IPv6, only to the default address
+ BlockingQueue queue = udp_clients [port];
+ if (queue == null) {
+ return;
+ }
+ // The buffer is already full -- drop the oldest element
+ if (queue.remainingCapacity () == 0) {
+ /* (void) */ queue.poll ();
+ }
+ //
+ // If this is a successful peering attempt, that is, a tcpack packet, report that back
+ // Note that the UDP/IPv4 source has already been validated against the IPv6 source
+ boolean tcpack = (pktlen >= 40 + 20) && (pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_TCP) && ((pkt [Utils.OFS_TCP6_FLAGS] & Utils.TCP_FLAG_ACK) != 0x00);
+ if (tcpack) {
+ if ((ngbcache != null) && ngbcache.is6bed4 (pkt, Utils.OFS_IP6_SRC)) {
+ ngbcache.received_peer_direct_acknowledgement (pkt, Utils.OFS_IP6_SRC, true);
+ }
+ }
+ //
+ // Enqueue the packet in the designated client queue
+ //TODO// Select whether this is UDP/TCP/??? for the shared address, or raw for unique addresses!
+ queue.offer (pkt);
+ }
+
+ /** Handle a 6bed4 packet that is being stripped and passed on as
+ * an IPv6 packet. Returns true if the packet is suitable to be
+ * relayed as IPv6.
+ */
+ public void handle_4to6 (DatagramPacket datagram)
+ throws IOException {
+ byte pkt [] = datagram.getData ();
+ int pktlen = datagram.getLength ();
+
+ if (pktlen < 40) {
+ return;
+ }
+ if ((pkt [0] & (byte) 0xf0) != 0x60) {
+ return;
+ }
+ validate_originator (pkt, (InetSocketAddress) datagram.getSocketAddress ());
+ if ((pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_ICMPV6) && (pkt [Utils.OFS_ICMP6_TYPE] >= Utils.ND_LOWEST) && (pkt [Utils.OFS_ICMP6_TYPE] <= Utils.ND_HIGHEST)) {
+ //
+ // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
+ handle_4to6_nd (pkt, pktlen, datagram.getSocketAddress ());
+ return;
+ } else {
+ //
+ // Plain Unicast or Plain Multicast (both may enter)
+ handle_4to6_plain (pkt, pktlen);
+ return;
+ }
+ }
+
+ public void validate_originator (byte pkt [], InetSocketAddress originator)
+ throws IOException {
+/* TODO: validate originator address
+ if (tunserver.equals (originator)) {
+ return;
+ }
+ if (memdiff_halfaddr (pkt, Utils.OFS_IP6_SRC, router_linklocal, 0) && ((local_address == null) || memdiff_halfaddr (pkt, Utils.OFS_IP6_SRC, local_address, 8))) {
+ throw new IOException ("Incoming 6bed4 uses bad /64 prefix");
+ }
+ int port = (pkt [Utils.OFS_IP6_SRC + 8] ^ 0x02) & 0xff;
+ port = (port | (pkt [Utils.OFS_IP6_SRC + 9] << 8) & 0xffff;
+ if (originator.getPort () != port) {
+ throw new IOException ("Incoming 6bed4 uses ")
+ }
+*/
+ }
+
+ /* This routine passes IPv6 traffic from the tunnel interface on
+ * to the 6bed4 interface where it is wrapped into UDP and IPv4.
+ * The only concern for this is where to send it to -- should it
+ * be sent to the tunnel server, or directly to the peer? The
+ * Neighbor Cache is consulted for advise.
+ *
+ * A special flag exists to modify the behaviour of the response
+ * to this inquiry. This flag is used to signal that a first
+ * packet might be tried directly, which should be harmless if
+ * it fails and otherwise lead to optimistic connections if:
+ * 1. the packet will repeat upon failure, and
+ * 2. explicit acknowledgement can be reported to the cache
+ * This is the case with TCP connection setup; during a SYN,
+ * it is possible to be playful and try to send the first
+ * packet directly. A TCP ACK that returns directly from the
+ * sender indicates that return traffic is possible, which is
+ * then used to update the Neighbor Cache with positivism on
+ * the return route.
+ */
+ public void handle_6to4_plain_unicast (byte pkt [], int pktlen)
+ throws IOException {
+ InetSocketAddress target;
+ if ((ngbcache != null) && ngbcache.is6bed4 (pkt, 24)) {
+ boolean tcpsyn = (pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_TCP) && ((pkt [Utils.OFS_TCP6_FLAGS] & Utils.TCP_FLAG_SYN) != 0x00);
+ target = ngbcache.lookup_neighbor (pkt, 24, tcpsyn);
+ } else {
+ target = tunserver;
+ }
+ }
+
+ public void handle_6to4_nd (byte pkt [], int pktlen)
+ throws IOException {
+ throw new RuntimeException ("You should not be trying to send ND over the 6bed4 interface");
+/* TODO: Entire routine can probably be removed
+ switch (pkt [Utils.OFS_ICMP6_TYPE]) {
+ //
+ // Handle Router Solicitation by answering it with the local configuration
+ case Utils.ND_ROUTER_SOLICIT:
+ int ofs = Utils.OFS_ICMP6_TYPE;
+ pkt [ofs++] = Utils.ND_ROUTER_ADVERT; // type
+ pkt [ofs++] = 0; // code
+ ofs += 2; // checksum
+ pkt [ofs++] = 0; // hop limit -- unspecified
+ pkt [ofs++] = 0x18; // M=0, O=0, H=0, Prf=11=Low, Reserved=0
+ pkt [ofs++] = 0x00; // Lifetime
+ pkt [ofs++] = 0x00; // (cont)
+ for (int i=0; i<8; i++) {
+ pkt [ofs++] = 0; // Reachable time, Retrans timer
+ }
+ pkt [ofs-6] = (byte) 0x80; // Reachable time := 32s
+ pkt [ofs-2] = 0x01; // Retrans timer := 0.25s
+ // Start of Prefix Option
+ pkt [ofs++] = Utils.ND_OPT_PREFIX_INFORMATION;
+ pkt [ofs++] = 4; // Option length = 4 * 8 bytes
+ pkt [ofs++] = (byte) 128; // Announce a /64 prefix (TODO: Temporarily /128)
+ pkt [ofs++] = (byte) 0x80; // Link-local, No autoconfig, tunnel does the work
+ for (int i=0; i<8; i++) {
+ pkt [ofs++] = (byte) 0xff; // Valid / Preferred Lifetime: Infinite
+ }
+ for (int i=0; i<4; i++) {
+ pkt [ofs++] = 0; // Reserved
+ }
+ Utils.memcp_address (pkt, ofs, local_address, 0);
+ ofs += 16;
+ // End of Prefix Option
+ Utils.memcp_address (pkt, Utils.OFS_IP6_DST, pkt, Utils.OFS_IP6_SRC); // dst:=src
+ Utils.memcp_address (pkt, Utils.OFS_IP6_SRC, local_address, 0);
+ //TODO// Send packet back to IPv6 downlink
+ return;
+ //
+ // Handle Router Advertisement by dropping it -- Android is not setup a router
+ case Utils.ND_ROUTER_ADVERT:
+ return;
+ //
+ // Neighbor Solicitation is not normally sent by the phone due to its /128 on 6bed4
+ case Utils.ND_NEIGHBOR_SOLICIT:
+ return;
+ //
+ // Neighbor Advertisement is a response to a peer, and should be relayed
+ case Utils.ND_NEIGHBOR_ADVERT:
+ //TODO// Possibly arrange the peer's receiving address
+ handle_6to4_plain_unicast (pkt, pktlen);
+ return;
+ // Route Redirect messages are not supported in 6bed4 draft v01
+ case Utils.ND_REDIRECT:
+ return;
+ }
+*/
+ }
+
+ public void handle_6to4 (byte pkt [], int pktlen)
+ throws IOException {
+ if (pktlen < 41) {
+ return;
+ }
+ if ((pkt [0] & 0xf0) != 0x60) {
+ return;
+ }
+ if ((pkt [Utils.OFS_IP6_NXTHDR] == Utils.IPPROTO_ICMPV6) && (pkt [Utils.OFS_ICMP6_TYPE] >= Utils.ND_LOWEST) && (pkt [Utils.OFS_ICMP6_TYPE] <= Utils.ND_HIGHEST)) {
+ //
+ // Not Plain: Router Adv/Sol, Neighbor Adv/Sol, Redirect
+ handle_6to4_nd (pkt, pktlen);
+ } else if ((pkt [Utils.OFS_IP6_DST+0] != 0xff) && ((pkt [Utils.OFS_IP6_DST+8] & 0x01) == 0x00)) {
+ //
+ // Plain Unicast
+ pkt [Utils.OFS_IP6_HOPS]--;
+ if (pkt [Utils.OFS_IP6_HOPS] == 0) {
+ return;
+ }
+ handle_6to4_plain_unicast (pkt, pktlen);
+ } else {
+ //
+ // Plain Multicast
+ //TODO: Ignore Multicast for now...
+ }
+ }
+
+
+ /** The Worker inner class ensures that incoming traffic, and that
+ * includes 6bed4 tunnel management traffic, is processed as soon
+ * as it arrives. This means that applications need not actively
+ * read input to get it processed. Furthermore, this approach
+ * simplifies receiving all 6bed4 traffic on one IPv4/UDP socket
+ * and subsequently dispatching it to the various 6bed4 sockets
+ * connected to the same ServerNode, and possibly each applying
+ * their own idea of a socket timeout or other queueing disciplines.
+ * The expense (there always seems to be one) is having to queue
+ * received data between the ServerNode and the 6bed4 socket.
+ */
+ private class Worker extends Thread {
+
+ protected ServerNode uplink;
+
+ /** The thread logic for the Worker inner class comes down
+ * to reading input from the IPv4/UDP socket, seeing if it
+ * needs local handling as a 6bed4 management packet, and
+ * otherwise shipping it off to the queue of a 6bed4 socket
+ * that is read when the user of the DatagramSocket6bed4
+ * wants to grab its input. Note how this reflects the way
+ * that native UDP ports operate -- packets wait until they
+ * are picked up.
+ */
+ public void run () {
+ DatagramPacket dgram4 = null;
+ while (!isInterrupted ()) {
+ try {
+ dgram4 = new DatagramPacket (new byte [1280 + 28], 1280 + 28);
+ uplink.receive (dgram4);
+ handle_4to6 (dgram4);
+ } catch (IOException ioe) {
+ ; //TODO// What to do here? Not sure...
+ }
+ }
+ }
+
+ public Worker (ServerNode owner) {
+ this.uplink = owner;
+ }
+
+ }
+
+ /** The Maintainer inner class performs regular maintainence.
+ * One task is to acquire the router address by sending regular
+ * Router Solicitation messages to the 6bed4 tunnel server,
+ * and once it has an address it will regularly send keep-alives.
+ */
+ private class Maintainer extends Thread {
+
+ /* The time for the next scheduled maintenance: routersol or keepalive.
+ * The milliseconds are always 0 for maintenance tasks.
+ */
+ private long maintenance_time_millis;
+ private int maintenance_time_cycle = 0;
+ private int maintenance_time_cycle_max = 30;
+ private boolean have_lladdr = false;
+ private DatagramPacket keepalive_packet = null;
+ private DatagramSocket uplink;
+
+ /* Perform the initial Router Solicitation exchange with the public server.
+ */
+ public void solicit_router () {
+ if (uplink != null) {
+ try {
+ DatagramPacket rtrsol = new DatagramPacket (Utils.router_solicitation, Utils.router_solicitation.length, tunserver);
+ uplink.send (rtrsol);
+ } catch (IOException ioe) {
+ /* Network is probably down, so don't
+ * throw new RuntimeException ("Network failure", ioe);
+ */
+ }
+ }
+ }
+
+ /* Send a KeepAlive packet to the public server.
+ * Note, ideally, we would set a low-enough TTL to never reach it;
+ * after all, the only goal is to open /local/ firewalls and NAT.
+ * Java however, is not capable of setting TTL on unicast sockets.
+ */
+ public void keepalive () {
+ if ((keepalive_packet != null) && (uplink != null)) {
+ try {
+ uplink.send (keepalive_packet);
+ // Log.i (TAG, "Sent KeepAlive (empty UDP) to Tunnel Server");
+ } catch (IOException ioe) {
+ ; /* Network is probably down; order reconnect to tunnel server */
+ have_lladdr = false;
+ }
+ }
+ }
+
+ /* Perform regular maintenance tasks: KeepAlive, and requesting a local address.
+ */
+ public void regular_maintenance () {
+ if (!have_lladdr) {
+ solicit_router ();
+ maintenance_time_cycle <<= 1;
+ maintenance_time_cycle += 1;
+ if (maintenance_time_cycle > maintenance_time_cycle_max) {
+ maintenance_time_cycle = maintenance_time_cycle_max;
+ }
+ //TODO// syslog (LOG_INFO, "Sent Router Advertisement to Public 6bed4 Service, next attempt in %d seconds\n", maintenance_time_cycle);
+ // Log.i (TAG, "Sent Router Advertisement to Tunnel Server");
+ } else {
+ //TODO// syslog (LOG_INFO, "Sending a KeepAlive message (empty UDP) to the 6bed4 Router\n");
+ keepalive ();
+ if (have_lladdr) {
+ maintenance_time_cycle = maintenance_time_cycle_max;
+ } else {
+ maintenance_time_cycle = 1;
+ }
+ }
+ maintenance_time_millis = System.currentTimeMillis () + 1000 * (long) maintenance_time_cycle;
+ }
+
+ /* Run the regular maintenance thread. This involves sending KeepAlives
+ * and possibly requesting a local address through Router Solicitation.
+ */
+ public void run () {
+ try {
+ while (!isInterrupted ()) {
+ regular_maintenance ();
+ sleep (maintenance_time_millis - System.currentTimeMillis());
+ }
+ } catch (InterruptedException ie) {
+ return;
+ }
+ }
+
+ /* Tell the maintenance routine whether a local address has been setup.
+ * Until this is called, the maintenance will focus on getting one through
+ * regular Router Solicitation messages. It is possible to revert to this
+ * behaviour by setting the flag to false; this can be useful in case of
+ * changes, for instance resulting from an IPv4 address change.
+ */
+ public void have_local_address (boolean new_setting) {
+ have_lladdr = new_setting;
+ if (have_lladdr) {
+ maintenance_time_cycle = maintenance_time_cycle_max;
+ maintenance_time_millis = System.currentTimeMillis () + 1000 * maintenance_time_cycle;
+ }
+ }
+
+ /* See if a local address has been setup.
+ */
+ public boolean have_local_address () {
+ return have_lladdr;
+ }
+
+ /* Construct the Maintainer thread.
+ */
+ public Maintainer (DatagramSocket uplink) {
+ this.uplink = uplink;
+ }
+ }
+
+
+}
+
--- /dev/null
+package nl.openfortress.socket6bed4;
+
+import java.net.Socket;
+
+
+public class Socket6bed4
+extends Socket {
+
+ public Socket6bed4 () {
+ throw new UnsupportedOperationException ("Socket6bed4 cannot be implemented until a TCP implementation is written in Java");
+ }
+
+}
+
--- /dev/null
+package nl.openfortress.socket6bed4;
+
+
+class Utils {
+
+ final static byte IPPROTO_ICMPV6 = 58;
+ final static byte IPPROTO_UDP = 17;
+ final static byte IPPROTO_TCP = 6;
+
+ final static byte ND_ROUTER_SOLICIT = (byte) 133;
+ final static byte ND_ROUTER_ADVERT = (byte) 134;
+ final static byte ND_NEIGHBOR_SOLICIT = (byte) 135;
+ final static byte ND_NEIGHBOR_ADVERT = (byte) 136;
+ final static byte ND_REDIRECT = (byte) 137;
+ final static byte ND_LOWEST = (byte) 133;
+ final static byte ND_HIGHEST = (byte) 137;
+
+ final static byte ND_OPT_PREFIX_INFORMATION = 3;
+
+ final static int OFS_IP6_SRC = 8;
+ final static int OFS_IP6_DST = 24;
+ final static int OFS_IP6_PLEN = 4;
+ final static int OFS_IP6_NXTHDR = 6;
+ final static int OFS_IP6_HOPS = 7;
+ final static int OFS_IP6_PLOAD = 40;
+
+ final static int OFS_UDP6_SRCPORT = 40 + 0;
+ final static int OFS_UDP6_DSTPORT = 40 + 2;
+ final static int OFS_UDP6_CSUM = 40 + 6;
+ final static int OFS_UDP6_PLEN = 40 + 4;
+ final static int OFS_UDP6_PLOAD = 40 + 8;
+
+ final static int OFS_ICMP6_TYPE = 40 + 0;
+ final static int OFS_ICMP6_CODE = 40 + 1;
+ final static int OFS_ICMP6_CSUM = 40 + 2;
+ final static int OFS_ICMP6_DATA = 40 + 4;
+
+ final static int OFS_ICMP6_NGBSOL_TARGET = 40 + 8;
+ final static int OFS_ICMP6_NGBADV_TARGET = 40 + 8;
+ final static int OFS_ICMP6_NGBADV_FLAGS = 40 + 4;
+
+ final static int OFS_TCP6_FLAGS = 13;
+ final static int TCP_FLAG_SYN = 0x02;
+ final static int TCP_FLAG_ACK = 0x01;
+
+ final static byte router_solicitation [] = {
+ // IPv6 header
+ 0x60, 0x00, 0x00, 0x00,
+ 16 / 256, 16 % 256, IPPROTO_ICMPV6, (byte) 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // unspecd src
+ (byte) 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02, // all-rtr tgt
+ // ICMPv6 header: router solicitation
+ ND_ROUTER_SOLICIT, 0, 0x7a, (byte) 0xae, // Checksum courtesy of WireShark :)
+ // ICMPv6 body: reserved
+ 0, 0, 0, 0,
+ // ICMPv6 option: source link layer address 0x0001 (end-aligned)
+ 0x01, 0x01, 0, 0, 0, 0, 0x00, 0x01,
+ };
+
+ final static byte router_linklocal_address [] = { (byte)0xfe,(byte)0x80,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+
+ /* Retrieve an unsigned 16-bit value from a given index in a byte array and
+ * return it as an integer.
+ */
+ public static int fetch_net16 (byte pkt [], int ofs16) {
+ int retval = ((int) pkt [ofs16]) << 8 & 0xff00;
+ retval = retval + (((int) pkt [ofs16+1]) & 0xff);
+ return retval;
+ }
+
+ /* Calculate the ICMPv6 checksum field in a given IPv6 packet.
+ */
+ public static int checksum_icmpv6 (byte pkt []) {
+ return checksum_icmpv6 (pkt, 0);
+ }
+ public static int checksum_icmpv6 (byte pkt [], int pktofs) {
+ int plen = fetch_net16 (pkt, pktofs + OFS_IP6_PLEN);
+ int nxth = ((int) pkt [pktofs + 6]) & 0xff;
+ // Pseudo header is IPv6 src/dst (included with packet) and plen/nxth and zeroes:
+ int csum = plen + nxth;
+ for (int i=8; i < OFS_IP6_PLOAD + plen; i += 2) {
+ if (i != OFS_ICMP6_CSUM) {
+ // Skip current checksum value
+ csum += fetch_net16 (pkt, pktofs + i);
+ }
+ }
+ // No need to treat a trailing single byte: ICMPv6 has no odd packet lengths
+ csum = (csum & 0xffff) + (csum >> 16);
+ csum = (csum & 0xffff) + (csum >> 16);
+ csum = csum ^ 0xffff; // 1's complement limited to 16 bits
+ return csum;
+ }
+
+ /* Calculate the UDP checksum field in a given IPv6 packet.
+ */
+ public static int checksum_udpv6 (byte pkt []) {
+ return checksum_udpv6 (pkt, 0);
+ }
+ public static int checksum_udpv6 (byte pkt [], int pktofs) {
+ int udplen = fetch_net16 (pkt, pktofs + OFS_UDP6_PLEN);
+ int nxth = ((int) pkt [pktofs + 6]) & 0xff;
+ // Pseudo header is IPv6 src/dst (included with packet) and plen/nxth and zeroes:
+ int csum = udplen + nxth;
+ for (int i=8; i < OFS_IP6_PLOAD + udplen; i++) {
+ if ((i & 0x0001) == 0x0000) {
+ if (i != OFS_UDP6_CSUM + 0) {
+ csum += (((int) pkt [pktofs + i]) << 8) & 0xff00;
+ }
+ } else {
+ if (i != OFS_UDP6_CSUM + 1) {
+ csum += ((int) pkt [pktofs + i]) & 0x00ff;
+ }
+ }
+ }
+ csum = (csum & 0xffff) + (csum >> 16);
+ csum = (csum & 0xffff) + (csum >> 16);
+ csum = csum ^ 0xffff; // 1's complement limited to 16 bits
+ return csum;
+ }
+
+ public static void memcp_address (byte tgt [], int tgtofs, byte src [], int srcofs) {
+ for (int i=0; i<16; i++) {
+ tgt [tgtofs+i] = src [srcofs+i];
+ }
+ }
+
+ public static boolean memdiff_addr (byte one[], int oneofs, byte oth[], int othofs) {
+ for (int i=0; i<16; i++) {
+ if (one [oneofs + i] != oth [othofs + i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean memdiff_halfaddr (byte one[], int oneofs, byte oth[], int othofs) {
+ for (int i=0; i<8; i++) {
+ if (one [oneofs + i] != oth [othofs + i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+